Skip to content

Commit 01c7776

Browse files
ensure that OpPhi is consistent with respect to operand types
1 parent 8b4b7d2 commit 01c7776

File tree

4 files changed

+159
-6
lines changed

4 files changed

+159
-6
lines changed

llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ class SPIRVEmitIntrinsics
144144
Type *deduceFunParamElementType(Function *F, unsigned OpIdx);
145145
Type *deduceFunParamElementType(Function *F, unsigned OpIdx,
146146
std::unordered_set<Function *> &FVisited);
147+
void replaceWithPtrcasted(Instruction *CI, Type *NewElemTy, Type *KnownElemTy,
148+
CallInst *AssignCI);
147149

148150
public:
149151
static char ID;
@@ -475,10 +477,11 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
475477
if (DemangledName.length() > 0)
476478
DemangledName = SPIRV::lookupBuiltinNameHelper(DemangledName);
477479
auto AsArgIt = ResTypeByArg.find(DemangledName);
478-
if (AsArgIt != ResTypeByArg.end()) {
480+
if (AsArgIt != ResTypeByArg.end())
479481
Ty = deduceElementTypeHelper(CI->getArgOperand(AsArgIt->second),
480482
Visited, UnknownElemTypeI8);
481-
}
483+
else if (Type *KnownRetTy = GR->findDeducedElementType(CalledF))
484+
Ty = KnownRetTy;
482485
}
483486
}
484487

@@ -808,6 +811,7 @@ void SPIRVEmitIntrinsics::deduceOperandElementType(Instruction *I,
808811
CallInst *PtrCastI =
809812
B.CreateIntrinsic(Intrinsic::spv_ptrcast, {Types}, Args);
810813
I->setOperand(OpIt.second, PtrCastI);
814+
buildAssignPtr(B, KnownElemTy, PtrCastI);
811815
}
812816
}
813817
}
@@ -1706,6 +1710,26 @@ bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
17061710
return true;
17071711
}
17081712

1713+
void SPIRVEmitIntrinsics::replaceWithPtrcasted(Instruction *CI, Type *NewElemTy,
1714+
Type *KnownElemTy,
1715+
CallInst *AssignCI) {
1716+
updateAssignType(AssignCI, CI, PoisonValue::get(NewElemTy));
1717+
IRBuilder<> B(CI->getContext());
1718+
B.SetInsertPoint(*CI->getInsertionPointAfterDef());
1719+
B.SetCurrentDebugLocation(CI->getDebugLoc());
1720+
Type *OpTy = CI->getType();
1721+
SmallVector<Type *, 2> Types = {OpTy, OpTy};
1722+
SmallVector<Value *, 2> Args = {CI, buildMD(PoisonValue::get(KnownElemTy)),
1723+
B.getInt32(getPointerAddressSpace(OpTy))};
1724+
CallInst *PtrCasted =
1725+
B.CreateIntrinsic(Intrinsic::spv_ptrcast, {Types}, Args);
1726+
SmallVector<User *> Users(CI->users());
1727+
for (auto *U : Users)
1728+
if (U != AssignCI && U != PtrCasted)
1729+
U->replaceUsesOfWith(CI, PtrCasted);
1730+
buildAssignPtr(B, KnownElemTy, PtrCasted);
1731+
}
1732+
17091733
// Try to deduce a better type for pointers to untyped ptr.
17101734
bool SPIRVEmitIntrinsics::postprocessTypes() {
17111735
bool Changed = false;
@@ -1717,6 +1741,18 @@ bool SPIRVEmitIntrinsics::postprocessTypes() {
17171741
Type *KnownTy = GR->findDeducedElementType(*IB);
17181742
if (!KnownTy || !AssignCI || !isa<Instruction>(AssignCI->getArgOperand(0)))
17191743
continue;
1744+
// Try to improve the type deduced after all Functions are processed.
1745+
if (auto *CI = dyn_cast<CallInst>(*IB)) {
1746+
if (Function *CalledF = CI->getCalledFunction()) {
1747+
Type *RetElemTy = GR->findDeducedElementType(CalledF);
1748+
// Fix inconsistency between known type and function's return type.
1749+
if (RetElemTy && RetElemTy != KnownTy) {
1750+
replaceWithPtrcasted(CI, RetElemTy, KnownTy, AssignCI);
1751+
Changed = true;
1752+
continue;
1753+
}
1754+
}
1755+
}
17201756
Instruction *I = cast<Instruction>(AssignCI->getArgOperand(0));
17211757
for (User *U : I->users()) {
17221758
Instruction *Inst = dyn_cast<Instruction>(U);

llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,17 @@ createNewIdReg(SPIRVType *SpvType, Register SrcReg, MachineRegisterInfo &MRI,
341341
return {Reg, GetIdOp};
342342
}
343343

344+
static void setInsertPtAfterDef(MachineIRBuilder &MIB, MachineInstr *Def) {
345+
MachineBasicBlock &MBB = *Def->getParent();
346+
MachineBasicBlock::iterator DefIt =
347+
Def->getNextNode() ? Def->getNextNode()->getIterator() : MBB.end();
348+
// Skip all the PHI and debug instructions.
349+
while (DefIt != MBB.end() &&
350+
(DefIt->isPHI() || DefIt->isDebugOrPseudoInstr()))
351+
DefIt = std::next(DefIt);
352+
MIB.setInsertPt(MBB, DefIt);
353+
}
354+
344355
// Insert ASSIGN_TYPE instuction between Reg and its definition, set NewReg as
345356
// a dst of the definition, assign SPIRVType to both registers. If SpvType is
346357
// provided, use it as SPIRVType in ASSIGN_TYPE, otherwise create it from Ty.
@@ -350,11 +361,9 @@ namespace llvm {
350361
Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpvType,
351362
SPIRVGlobalRegistry *GR, MachineIRBuilder &MIB,
352363
MachineRegisterInfo &MRI) {
353-
MachineInstr *Def = MRI.getVRegDef(Reg);
354364
assert((Ty || SpvType) && "Either LLVM or SPIRV type is expected.");
355-
MIB.setInsertPt(*Def->getParent(),
356-
(Def->getNextNode() ? Def->getNextNode()->getIterator()
357-
: Def->getParent()->end()));
365+
MachineInstr *Def = MRI.getVRegDef(Reg);
366+
setInsertPtAfterDef(MIB, Def);
358367
SpvType = SpvType ? SpvType : GR->getOrCreateSPIRVType(Ty, MIB);
359368
Register NewReg = MRI.createGenericVirtualRegister(MRI.getType(Reg));
360369
if (auto *RC = MRI.getRegClassOrNull(Reg)) {
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
; The goal of the test case is to ensure that OpPhi is consistent with respect to operand types.
2+
; -verify-machineinstrs is not available due to mutually exclusive requirements for G_BITCAST and G_PHI.
3+
4+
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
5+
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
6+
7+
; CHECK: %[[#Char:]] = OpTypeInt 8 0
8+
; CHECK: %[[#PtrChar:]] = OpTypePointer Function %[[#Char]]
9+
; CHECK: %[[#Int:]] = OpTypeInt 32 0
10+
; CHECK: %[[#PtrInt:]] = OpTypePointer Function %[[#Int]]
11+
; CHECK: %[[#R1:]] = OpFunctionCall %[[#PtrChar]] %[[#]]
12+
; CHECK: %[[#R2:]] = OpFunctionCall %[[#PtrInt]] %[[#]]
13+
; CHECK-DAG: %[[#Casted1:]] = OpBitcast %[[#PtrChar]] %[[#R2]]
14+
; CHECK-DAG: %[[#Casted2:]] = OpBitcast %[[#PtrChar]] %[[#R2]]
15+
; CHECK: OpBranchConditional
16+
; CHECK-DAG: OpPhi %[[#PtrChar]] %[[#R1]] %[[#]] %[[#Casted1]] %[[#]]
17+
; CHECK-DAG: OpPhi %[[#PtrChar]] %[[#R1]] %[[#]] %[[#Casted2]] %[[#]]
18+
19+
define void @f0(ptr %arg) {
20+
entry:
21+
ret void
22+
}
23+
24+
define ptr @f1() {
25+
entry:
26+
%p = alloca i8
27+
store i8 8, ptr %p
28+
ret ptr %p
29+
}
30+
31+
define ptr @f2() {
32+
entry:
33+
%p = alloca i32
34+
store i32 32, ptr %p
35+
ret ptr %p
36+
}
37+
38+
define ptr @foo(i1 %arg) {
39+
entry:
40+
%r1 = tail call ptr @f1()
41+
%r2 = tail call ptr @f2()
42+
br i1 %arg, label %l1, label %l2
43+
44+
l1:
45+
br label %exit
46+
47+
l2:
48+
br label %exit
49+
50+
exit:
51+
%ret = phi ptr [ %r1, %l1 ], [ %r2, %l2 ]
52+
%ret2 = phi ptr [ %r1, %l1 ], [ %r2, %l2 ]
53+
tail call void @f0(ptr %ret)
54+
ret ptr %ret2
55+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
; The goal of the test case is to ensure that OpPhi is consistent with respect to operand types.
2+
; -verify-machineinstrs is not available due to mutually exclusive requirements for G_BITCAST and G_PHI.
3+
4+
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
5+
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
6+
7+
; CHECK: %[[#Char:]] = OpTypeInt 8 0
8+
; CHECK: %[[#PtrChar:]] = OpTypePointer Function %[[#Char]]
9+
; CHECK: %[[#Int:]] = OpTypeInt 32 0
10+
; CHECK: %[[#PtrInt:]] = OpTypePointer Function %[[#Int]]
11+
; CHECK: %[[#R1:]] = OpFunctionCall %[[#PtrChar]] %[[#]]
12+
; CHECK: %[[#R2:]] = OpFunctionCall %[[#PtrInt]] %[[#]]
13+
; CHECK: %[[#Casted:]] = OpBitcast %[[#PtrChar]] %[[#R2]]
14+
; CHECK: OpPhi %[[#PtrChar]] %[[#R1]] %[[#]] %[[#Casted]] %[[#]]
15+
; CHECK: OpPhi %[[#PtrChar]] %[[#R1]] %[[#]] %[[#Casted]] %[[#]]
16+
17+
define ptr @foo(i1 %arg) {
18+
entry:
19+
%r1 = tail call ptr @f1()
20+
%r2 = tail call ptr @f2()
21+
br i1 %arg, label %l1, label %l2
22+
23+
l1:
24+
br label %exit
25+
26+
l2:
27+
br label %exit
28+
29+
exit:
30+
%ret = phi ptr [ %r1, %l1 ], [ %r2, %l2 ]
31+
%ret2 = phi ptr [ %r1, %l1 ], [ %r2, %l2 ]
32+
tail call void @f0(ptr %ret)
33+
ret ptr %ret2
34+
}
35+
36+
define void @f0(ptr %arg) {
37+
entry:
38+
ret void
39+
}
40+
41+
define ptr @f1() {
42+
entry:
43+
%p = alloca i8
44+
store i8 8, ptr %p
45+
ret ptr %p
46+
}
47+
48+
define ptr @f2() {
49+
entry:
50+
%p = alloca i32
51+
store i32 32, ptr %p
52+
ret ptr %p
53+
}

0 commit comments

Comments
 (0)