Fix constant folding in PredefinedConstantResolving

PredefinedConstantResolving pass caused type mismatch assertion
in tests while moving to opaque pointers. It happened when there was a
type difference between a global variable and it's load instruction
user. With typed pointers the pass was skipped in this scenario because
user of same global was bitcast and then it's user was load. What this
pass tried to do was doing RAUW operation on load to replace it with
global constant. This fix changes pass's behaviour by enabling constant
folding even when there is a type difference between load instruction
and global constant.

Example crashing ir:
```llvm
 @global = constant [3 x i64] [i64 16, i64 32, i64 64]
 define void @func(i64 %0) {
  %2 = load i64, ptr @global ; <-- crash
  ret void
 }
```
This commit is contained in:
Kreczko, Konrad
2025-08-20 13:46:48 +00:00
committed by igcbot
parent f7a18fd28f
commit 137cd1df57
2 changed files with 75 additions and 11 deletions

View File

@ -1,6 +1,6 @@
/*========================== begin_copyright_notice ============================
Copyright (C) 2017-2021 Intel Corporation
Copyright (C) 2017-2025 Intel Corporation
SPDX-License-Identifier: MIT
@ -14,6 +14,7 @@ SPDX-License-Identifier: MIT
#include <llvm/IR/Module.h>
#include <llvm/IR/Instructions.h>
#include <llvm/Pass.h>
#include <llvm/Analysis/ConstantFolding.h>
#include "common/LLVMWarningsPop.hpp"
using namespace llvm;
@ -39,7 +40,7 @@ ModulePass *IGC::createResolvePredefinedConstantPass() { return new PredefinedCo
char PredefinedConstantResolving::ID = 0;
#define PASS_FLAG "igc-predefined-constant-resolve"
#define PASS_DESC "Resolve compiler predefeind constants"
#define PASS_DESC "Resolve compiler predefined constants"
#define PASS_CFG_ONLY false
#define PASS_ANALYSIS false
namespace IGC {
@ -49,23 +50,24 @@ IGC_INITIALIZE_PASS_END(PredefinedConstantResolving, PASS_FLAG, PASS_DESC, PASS_
bool PredefinedConstantResolving::runOnModule(Module &M) {
bool Changed = false;
const DataLayout &DL = M.getDataLayout();
for (auto &GV : M.globals()) {
if (!GV.isConstant())
if (!GV.isConstant() || !GV.hasUniqueInitializer())
continue;
if (!GV.hasUniqueInitializer())
continue;
// We don't use fancy data structure to reduce the lookup overhead due to
// the current limited size of compiler pre-defined constants.
Constant *C = GV.getInitializer();
for (auto I = GV.user_begin(); I != GV.user_end(); /* empty */) {
// Ensure we understand how the predefined constant is used.
LoadInst *LI = dyn_cast<LoadInst>(*I++);
if (!LI)
continue;
LI->replaceAllUsesWith(C);
LI->eraseFromParent();
Changed = true;
if (Constant *Folded = ConstantFoldLoadFromConst(C, LI->getType(), DL)) {
LI->replaceAllUsesWith(Folded);
LI->eraseFromParent();
Changed = true;
}
}
}
return Changed;

View File

@ -0,0 +1,62 @@
;=========================== begin_copyright_notice ============================
;
; Copyright (C) 2025 Intel Corporation
;
; SPDX-License-Identifier: MIT
;
;============================ end_copyright_notice =============================
; REQUIRES: regkeys, llvm-16-plus
;
; RUN: igc_opt --regkey "EnableOpaquePointersBackend=1" --igc-predefined-constant-resolve 2>&1 -S < %s | FileCheck %s
; ------------------------------------------------
; PredefinedConstantResolving
; ------------------------------------------------
; This test checks whether the PredefinedConstantResolving pass correctly folds constants with matching or not matching types
@global_int = internal addrspace(1) constant i32 1337, align 4
@global_i8s_arr = internal addrspace(1) constant [8 x i8] [i8 1, i8 2, i8 3, i8 4, i8 5, i8 6, i8 7, i8 8], align 8
@global_i64s_arr = internal addrspace(1) constant [3 x i64] [i64 16, i64 32, i64 64], align 8
; CHECK-LABEL: @test_i64_from_i64_array(
; CHECK: store i64 16,
; CHECK-NOT: load
define spir_kernel void @test_i64_from_i64_array(ptr addrspace(1) %out) {
entry:
%local = load i64, ptr addrspace(1) @global_i64s_arr, align 8
store i64 %local, ptr addrspace(1) %out, align 8
ret void
}
; CHECK-LABEL: @test_direct_i32(
; CHECK: store i32 1337,
; CHECK-NOT: load
define spir_kernel void @test_direct_i32(ptr addrspace(1) %out) {
entry:
%local = load i32, ptr addrspace(1) @global_int
store i32 %local, ptr addrspace(1) %out, align 4
ret void
}
; CHECK-LABEL: @test_i64_from_i8_array(
; CHECK: store i64 578437695752307201,
; CHECK-NOT: load
define spir_kernel void @test_i64_from_i8_array(ptr addrspace(1) %out) {
entry:
%local = load i64, ptr addrspace(1) @global_i8s_arr, align 8
store i64 %local, ptr addrspace(1) %out, align 8
ret void
}
; CHECK-LABEL: @test_i8_from_i64_array(
; CHECK: store i8 16,
; CHECK-NOT: load
define spir_kernel void @test_i8_from_i64_array(ptr addrspace(1) %out) {
entry:
%local = load i8, ptr addrspace(1) @global_i64s_arr, align 1
store i8 %local, ptr addrspace(1) %out, align 1
ret void
}
attributes #0 = { convergent noinline nounwind optnone "less-precise-fpmad"="true" "visaStackCall" }