Skip to content

Commit 92d6114

Browse files
fda0igcbot
authored andcommitted
Opaque pointer fixes in LegalizeFunctionSignatures, PromoteBools
In LegalizeFunctionSignatures don't call `getFunction()` which returns parent function. Add support for llvm15+ which works with opaque pointers and a legacy llvm 14 path. In PromoteBools: - Call `getType()` on load instruction - calling `getType()` on src returns an opaque pointer. - Use getValueType() in promoteGlobalVariable to work with opaque pointers.
1 parent acae2f8 commit 92d6114

File tree

5 files changed

+266
-5
lines changed

5 files changed

+266
-5
lines changed

IGC/AdaptorCommon/LegalizeFunctionSignatures.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,15 @@ inline StructType* PromotedStructValueType(const Module& M, const Argument* arg)
215215
return nullptr;
216216
}
217217

218+
inline StructType* StructTypeFromCallInstArg(const CallInst* callInst, unsigned argNo)
219+
{
220+
#if LLVM_VERSION_MAJOR >= 15
221+
return dyn_cast_or_null<StructType>(callInst->getParamStructRetType(argNo));
222+
#else
223+
return dyn_cast_or_null<StructType>(callInst->getAttributes().getParamStructRetType(argNo));
224+
#endif
225+
}
226+
218227
// BE does not handle struct load/store, so instead store each element of the struct value to the GEP of the struct pointer
219228
inline void StoreToStruct(IGCLLVM::IRBuilder<>& builder, Value* strVal, Value* strPtr)
220229
{
@@ -702,7 +711,7 @@ void LegalizeFunctionSignatures::FixCallInstruction(Module& M, CallInst* callIns
702711
}
703712
Type* retType =
704713
retTypeOption == ReturnOpt::RETURN_BY_REF ? Type::getVoidTy(callInst->getContext()) :
705-
retTypeOption == ReturnOpt::RETURN_STRUCT ? PromotedStructValueType(M, callInst->getFunction()->getArg(0)) :
714+
retTypeOption == ReturnOpt::RETURN_STRUCT ? StructTypeFromCallInstArg(callInst, 0) :
706715
retTypeOption == ReturnOpt::RETURN_LEGAL_INT ? LegalizedIntVectorType(M, callInst->getType()) :
707716
callInst->getType();
708717
newFnTy = FunctionType::get(retType, argTypes, false);

IGC/AdaptorOCL/preprocess_spvir/PromoteBools.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ Function* PromoteBools::promoteFunction(Function* function)
683683

684684
GlobalVariable* PromoteBools::promoteGlobalVariable(GlobalVariable* globalVariable)
685685
{
686-
if (!globalVariable || !typeNeedsPromotion(globalVariable->getType()))
686+
if (!globalVariable || !typeNeedsPromotion(globalVariable->getValueType()))
687687
{
688688
return globalVariable;
689689
}
@@ -1093,13 +1093,12 @@ LoadInst* PromoteBools::promoteLoad(LoadInst* load)
10931093
return nullptr;
10941094
}
10951095

1096-
auto src = load->getOperand(0);
1097-
1098-
if (!wasPromotedAnyOf(load->operands()) && !typeNeedsPromotion(src->getType()))
1096+
if (!wasPromotedAnyOf(load->operands()) && !typeNeedsPromotion(load->getType()))
10991097
{
11001098
return load;
11011099
}
11021100

1101+
auto src = load->getOperand(0);
11031102
auto newSrc = getOrCreatePromotedValue(src);
11041103
auto newType = getOrCreatePromotedType(load->getType());
11051104
auto newLoad = new LoadInst(
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
;=========================== begin_copyright_notice ============================
2+
;
3+
; Copyright (C) 2025 Intel Corporation
4+
;
5+
; SPDX-License-Identifier: MIT
6+
;
7+
;============================ end_copyright_notice =============================
8+
;
9+
; REQUIRES: llvm-15-plus
10+
; RUN: igc_opt --opaque-pointers --igc-legalize-function-signatures -S < %s 2>&1 | FileCheck %s
11+
; ------------------------------------------------
12+
; LegalizeFunctionSignatures
13+
; ------------------------------------------------
14+
15+
; Pseudo-code:
16+
; Before:
17+
; void foo() {
18+
; st_foo a;
19+
; void (*func)(st_foo*) = condition ? bar_0 : bar_1;
20+
; func(&a);
21+
; consume(&a);
22+
; }
23+
;
24+
; void bar_0(st_foo* out) {
25+
; *out = {10, 20};
26+
; }
27+
;
28+
; void bar_1(st_foo* out) {
29+
; *out = {1, 2};
30+
; }
31+
;
32+
; After:
33+
; void foo() {
34+
; st_foo a;
35+
; st_foo (*func)() = condition ? bar_0 : bar_1;
36+
; a = func();
37+
; consume(&a);
38+
; }
39+
; st_foo bar_0()
40+
; {
41+
; st_foo out = {10, 20};
42+
; return out;
43+
; }
44+
; st_foo bar_1()
45+
; {
46+
; st_foo out = {1, 2};
47+
; return out;
48+
; }
49+
50+
%struct._st_foo = type { i32, i32 }
51+
52+
define spir_kernel void @foo(i1 %condition) #1 {
53+
; CHECK: define spir_kernel void @foo(i8 %condition)
54+
; CHECK: [[CONDITION:%.*]] = trunc i8 %condition to i1
55+
%a = alloca %struct._st_foo
56+
; CHECK-NEXT: [[A:%.*]] = alloca %struct._st_foo, align 8
57+
%func_addr = select i1 %condition, ptr @bar_0, ptr @bar_1
58+
; CHECK-NEXT: [[FUNC_ADDR:%.*]] = select i1 [[CONDITION]], ptr @bar_0, ptr @bar_1
59+
call void %func_addr(ptr sret(%struct._st_foo) %a)
60+
; CHECK-NEXT: [[RET_BAR_0:%.*]] = call %struct._st_foo [[FUNC_ADDR]]()
61+
; CHECK-NEXT: [[M_0_PTR:%.*]] = getelementptr inbounds %struct._st_foo, ptr [[A]], i32 0, i32 0
62+
; CHECK-NEXT: [[M_0:%.*]] = extractvalue %struct._st_foo [[RET_BAR_0]], 0
63+
; CHECK-NEXT: store i32 [[M_0]], ptr [[M_0_PTR]], align 4
64+
; CHECK-NEXT: [[M_1_PTR:%.*]] = getelementptr inbounds %struct._st_foo, ptr [[A]], i32 0, i32 1
65+
; CHECK-NEXT: [[M_1:%.*]] = extractvalue %struct._st_foo [[RET_BAR_0]], 1
66+
; CHECK-NEXT: store i32 [[M_1]], ptr [[M_1_PTR]], align 4
67+
call void @consume(ptr %a)
68+
; CHECK-NEXT: call void @consume(ptr [[A]])
69+
ret void
70+
; CHECK-NEXT: ret void
71+
}
72+
; CHECK-NEXT: }
73+
74+
define linkonce_odr spir_func void @bar_0(ptr sret(%struct._st_foo) %out) {
75+
; CHECK: define linkonce_odr spir_func %struct._st_foo @bar_0()
76+
; CHECK-NEXT: [[OUT:%.*]] = alloca %struct._st_foo, align 8
77+
store %struct._st_foo { i32 10, i32 20 }, ptr %out, align 4
78+
; CHECK-NEXT: store %struct._st_foo { i32 10, i32 20 }, ptr [[OUT]], align 4
79+
ret void
80+
; CHECK-NEXT: [[M_0_PTR:%.*]] = getelementptr inbounds %struct._st_foo, ptr [[OUT]], i32 0, i32 0
81+
; CHECK-NEXT: [[M_0:%.*]] = load i32, ptr [[M_0_PTR]], align 4
82+
; CHECK-NEXT: [[R_0:%.*]] = insertvalue %struct._st_foo undef, i32 [[M_0]], 0
83+
; CHECK-NEXT: [[M_1_PTR:%.*]] = getelementptr inbounds %struct._st_foo, ptr [[OUT]], i32 0, i32 1
84+
; CHECK-NEXT: [[M_1:%.*]] = load i32, ptr [[M_1_PTR]], align 4
85+
; CHECK-NEXT: [[R_1:%.*]] = insertvalue %struct._st_foo [[R_0]], i32 [[M_1]], 1
86+
; CHECK-NEXT: ret %struct._st_foo [[R_1]]
87+
}
88+
; CHECK-NEXT: }
89+
90+
define linkonce_odr spir_func void @bar_1(ptr sret(%struct._st_foo) %out) {
91+
; CHECK: define linkonce_odr spir_func %struct._st_foo @bar_1()
92+
; CHECK-NEXT: [[OUT:%.*]] = alloca %struct._st_foo, align 8
93+
store %struct._st_foo { i32 1, i32 2 }, ptr %out, align 4
94+
; CHECK-NEXT: store %struct._st_foo { i32 1, i32 2 }, ptr [[OUT]], align 4
95+
ret void
96+
; CHECK-NEXT: [[M_0_PTR:%.*]] = getelementptr inbounds %struct._st_foo, ptr [[OUT]], i32 0, i32 0
97+
; CHECK-NEXT: [[M_0:%.*]] = load i32, ptr [[M_0_PTR]], align 4
98+
; CHECK-NEXT: [[R_0:%.*]] = insertvalue %struct._st_foo undef, i32 [[M_0]], 0
99+
; CHECK-NEXT: [[M_1_PTR:%.*]] = getelementptr inbounds %struct._st_foo, ptr [[OUT]], i32 0, i32 1
100+
; CHECK-NEXT: [[M_1:%.*]] = load i32, ptr [[M_1_PTR]], align 4
101+
; CHECK-NEXT: [[R_1:%.*]] = insertvalue %struct._st_foo [[R_0]], i32 [[M_1]], 1
102+
; CHECK-NEXT: ret %struct._st_foo [[R_1]]
103+
}
104+
; CHECK-NEXT: }
105+
106+
declare spir_func void @consume(ptr)
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
;=========================== begin_copyright_notice ============================
2+
;
3+
; Copyright (C) 2025 Intel Corporation
4+
;
5+
; SPDX-License-Identifier: MIT
6+
;
7+
;============================ end_copyright_notice =============================
8+
;
9+
; RUN: igc_opt --typed-pointers --igc-legalize-function-signatures -S < %s 2>&1 | FileCheck %s
10+
; ------------------------------------------------
11+
; LegalizeFunctionSignatures
12+
; ------------------------------------------------
13+
14+
; Pseudo-code:
15+
; Before:
16+
; void foo() {
17+
; st_foo a;
18+
; void (*func)(st_foo*) = condition ? bar_0 : bar_1;
19+
; func(&a);
20+
; consume(&a);
21+
; }
22+
;
23+
; void bar_0(st_foo* out) {
24+
; *out = {10, 20};
25+
; }
26+
;
27+
; void bar_1(st_foo* out) {
28+
; *out = {1, 2};
29+
; }
30+
;
31+
; After:
32+
; void foo() {
33+
; st_foo a;
34+
; st_foo (*func)() = condition ? bar_0 : bar_1;
35+
; a = func();
36+
; consume(&a);
37+
; }
38+
; st_foo bar_0()
39+
; {
40+
; st_foo out = {10, 20};
41+
; return out;
42+
; }
43+
; st_foo bar_1()
44+
; {
45+
; st_foo out = {1, 2};
46+
; return out;
47+
; }
48+
49+
%struct._st_foo = type { i32, i32 }
50+
51+
define spir_kernel void @foo(i1 %condition) #1 {
52+
; CHECK: define spir_kernel void @foo(i8 %condition)
53+
; CHECK: [[CONDITION:%.*]] = trunc i8 %condition to i1
54+
%a = alloca %struct._st_foo
55+
; CHECK-NEXT: [[A:%.*]] = alloca %struct._st_foo, align 8
56+
%func_addr = select i1 %condition, void (%struct._st_foo*)* @bar_0, void (%struct._st_foo*)* @bar_1
57+
; CHECK-NEXT: [[FUNC_ADDR_SELECT:%.*]] = select i1 [[CONDITION]], void (%struct._st_foo*)* bitcast (%struct._st_foo ()* @bar_0 to void (%struct._st_foo*)*), void (%struct._st_foo*)* bitcast (%struct._st_foo ()* @bar_1 to void (%struct._st_foo*)*)
58+
; CHECK-NEXT: [[FUNC_ADDR:%.*]] = bitcast void (%struct._st_foo*)* [[FUNC_ADDR_SELECT]] to %struct._st_foo ()*
59+
call void %func_addr(%struct._st_foo* sret(%struct._st_foo) %a)
60+
; CHECK-NEXT: [[RET_BAR_0:%.*]] = call %struct._st_foo [[FUNC_ADDR]]()
61+
; CHECK-NEXT: [[M_0_PTR:%.*]] = getelementptr inbounds %struct._st_foo, %struct._st_foo* [[A]], i32 0, i32 0
62+
; CHECK-NEXT: [[M_0:%.*]] = extractvalue %struct._st_foo [[RET_BAR_0]], 0
63+
; CHECK-NEXT: store i32 [[M_0]], i32* [[M_0_PTR]], align 4
64+
; CHECK-NEXT: [[M_1_PTR:%.*]] = getelementptr inbounds %struct._st_foo, %struct._st_foo* [[A]], i32 0, i32 1
65+
; CHECK-NEXT: [[M_1:%.*]] = extractvalue %struct._st_foo [[RET_BAR_0]], 1
66+
; CHECK-NEXT: store i32 [[M_1]], i32* [[M_1_PTR]], align 4
67+
call void @consume(%struct._st_foo* %a)
68+
; CHECK-NEXT: call void @consume(%struct._st_foo* [[A]])
69+
ret void
70+
; CHECK-NEXT: ret void
71+
}
72+
; CHECK-NEXT: }
73+
74+
define linkonce_odr spir_func void @bar_0(%struct._st_foo* sret(%struct._st_foo) %out) {
75+
; CHECK: define linkonce_odr spir_func %struct._st_foo @bar_0()
76+
; CHECK-NEXT: [[OUT:%.*]] = alloca %struct._st_foo, align 8
77+
store %struct._st_foo { i32 10, i32 20 }, %struct._st_foo* %out, align 4
78+
; CHECK-NEXT: store %struct._st_foo { i32 10, i32 20 }, %struct._st_foo* [[OUT]], align 4
79+
ret void
80+
; CHECK-NEXT: [[M_0_PTR:%.*]] = getelementptr inbounds %struct._st_foo, %struct._st_foo* [[OUT]], i32 0, i32 0
81+
; CHECK-NEXT: [[M_0:%.*]] = load i32, i32* [[M_0_PTR]], align 4
82+
; CHECK-NEXT: [[R_0:%.*]] = insertvalue %struct._st_foo undef, i32 [[M_0]], 0
83+
; CHECK-NEXT: [[M_1_PTR:%.*]] = getelementptr inbounds %struct._st_foo, %struct._st_foo* [[OUT]], i32 0, i32 1
84+
; CHECK-NEXT: [[M_1:%.*]] = load i32, i32* [[M_1_PTR]], align 4
85+
; CHECK-NEXT: [[R_1:%.*]] = insertvalue %struct._st_foo [[R_0]], i32 [[M_1]], 1
86+
; CHECK-NEXT: ret %struct._st_foo [[R_1]]
87+
}
88+
; CHECK-NEXT: }
89+
90+
define linkonce_odr spir_func void @bar_1(%struct._st_foo* sret(%struct._st_foo) %out) {
91+
; CHECK: define linkonce_odr spir_func %struct._st_foo @bar_1()
92+
; CHECK-NEXT: [[OUT:%.*]] = alloca %struct._st_foo, align 8
93+
store %struct._st_foo { i32 1, i32 2 }, %struct._st_foo* %out, align 4
94+
; CHECK-NEXT: store %struct._st_foo { i32 1, i32 2 }, %struct._st_foo* [[OUT]], align 4
95+
ret void
96+
; CHECK-NEXT: [[M_0_PTR:%.*]] = getelementptr inbounds %struct._st_foo, %struct._st_foo* [[OUT]], i32 0, i32 0
97+
; CHECK-NEXT: [[M_0:%.*]] = load i32, i32* [[M_0_PTR]], align 4
98+
; CHECK-NEXT: [[R_0:%.*]] = insertvalue %struct._st_foo undef, i32 [[M_0]], 0
99+
; CHECK-NEXT: [[M_1_PTR:%.*]] = getelementptr inbounds %struct._st_foo, %struct._st_foo* [[OUT]], i32 0, i32 1
100+
; CHECK-NEXT: [[M_1:%.*]] = load i32, i32* [[M_1_PTR]], align 4
101+
; CHECK-NEXT: [[R_1:%.*]] = insertvalue %struct._st_foo [[R_0]], i32 [[M_1]], 1
102+
; CHECK-NEXT: ret %struct._st_foo [[R_1]]
103+
}
104+
; CHECK-NEXT: }
105+
106+
declare spir_func void @consume(%struct._st_foo*)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
;=========================== begin_copyright_notice ============================
2+
;
3+
; Copyright (C) 2025 Intel Corporation
4+
;
5+
; SPDX-License-Identifier: MIT
6+
;
7+
;============================ end_copyright_notice =============================
8+
9+
; RUN: igc_opt --opaque-pointers -igc-promote-bools -S %s -o %t.ll
10+
; RUN: FileCheck %s --input-file=%t.ll
11+
12+
%struct = type { i8, i1 }
13+
14+
@global_scalar = internal addrspace(3) global i1 false
15+
@global_struct = internal addrspace(3) global %struct {i8 0, i1 true}
16+
17+
define void @main() {
18+
%1 = load i1, ptr addrspace(3) @global_scalar
19+
%2 = load %struct, ptr addrspace(3) @global_struct
20+
21+
store i1 false, ptr addrspace(3) @global_scalar
22+
store i1 true, ptr addrspace(3) @global_scalar
23+
store i1 %1, ptr addrspace(3) @global_scalar
24+
store %struct %2, ptr addrspace(3) @global_struct
25+
26+
ret void
27+
}
28+
29+
; CHECK: %struct = type { i8, i8 }
30+
31+
; CHECK: @global_scalar = internal addrspace(3) global i8 0
32+
; CHECK: @global_struct = internal addrspace(3) global %struct { i8 0, i8 1 }
33+
34+
; CHECK-LABEL: define void @main()
35+
; CHECK-NEXT: %1 = load i8, ptr addrspace(3) @global_scalar
36+
; CHECK-NEXT: %2 = load %struct, ptr addrspace(3) @global_struct
37+
38+
; CHECK-NEXT: store i8 0, ptr addrspace(3) @global_scalar
39+
; CHECK-NEXT: store i8 1, ptr addrspace(3) @global_scalar
40+
; CHECK-NEXT: store i8 %1, ptr addrspace(3) @global_scalar
41+
; CHECK-NEXT: store %struct %2, ptr addrspace(3) @global_struct

0 commit comments

Comments
 (0)