Skip to content
This repository was archived by the owner on Sep 15, 2025. It is now read-only.

Commit b8435e3

Browse files
[SPIR-V] Emit spv_undef intrinsic for aggregate undef operands
This change adds a new spv_undef intrinsic which is emitted in place of aggregate undef operands and later selected to single OpUndef SPIR-V instruction. The behavior matches that of Khronos SPIR-V Translator and should support nested aggregates. Differential Revision: https://reviews.llvm.org/D143107
1 parent ac35c1d commit b8435e3

File tree

5 files changed

+85
-14
lines changed

5 files changed

+85
-14
lines changed

llvm/include/llvm/IR/IntrinsicsSPIRV.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,5 @@ let TargetPrefix = "spv" in {
3131
def int_spv_cmpxchg : Intrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_vararg_ty]>;
3232
def int_spv_unreachable : Intrinsic<[], []>;
3333
def int_spv_alloca : Intrinsic<[llvm_any_ty], []>;
34+
def int_spv_undef : Intrinsic<[llvm_i32_ty], []>;
3435
}

llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class SPIRVEmitIntrinsics
5656
DenseMap<Instruction *, Constant *> AggrConsts;
5757
DenseSet<Instruction *> AggrStores;
5858
void preprocessCompositeConstants();
59+
void preprocessUndefs();
5960
CallInst *buildIntrWithMD(Intrinsic::ID IntrID, ArrayRef<Type *> Types,
6061
Value *Arg, Value *Arg2) {
6162
ConstantAsMetadata *CM = ValueAsMetadata::getConstant(Arg);
@@ -151,6 +152,29 @@ void SPIRVEmitIntrinsics::replaceMemInstrUses(Instruction *Old,
151152
Old->eraseFromParent();
152153
}
153154

155+
void SPIRVEmitIntrinsics::preprocessUndefs() {
156+
std::queue<Instruction *> Worklist;
157+
for (auto &I : instructions(F))
158+
Worklist.push(&I);
159+
160+
while (!Worklist.empty()) {
161+
Instruction *I = Worklist.front();
162+
Worklist.pop();
163+
164+
for (auto &Op : I->operands()) {
165+
auto *AggrUndef = dyn_cast<UndefValue>(Op);
166+
if (!AggrUndef || !Op->getType()->isAggregateType())
167+
continue;
168+
169+
IRB->SetInsertPoint(I);
170+
auto *IntrUndef = IRB->CreateIntrinsic(Intrinsic::spv_undef, {}, {});
171+
Worklist.push(IntrUndef);
172+
I->replaceUsesOfWith(Op, IntrUndef);
173+
AggrConsts[IntrUndef] = AggrUndef;
174+
}
175+
}
176+
}
177+
154178
void SPIRVEmitIntrinsics::preprocessCompositeConstants() {
155179
std::queue<Instruction *> Worklist;
156180
for (auto &I : instructions(F))
@@ -369,7 +393,8 @@ void SPIRVEmitIntrinsics::insertAssignTypeIntrs(Instruction *I) {
369393
setInsertPointSkippingPhis(*IRB, I->getNextNode());
370394
Type *TypeToAssign = Ty;
371395
if (auto *II = dyn_cast<IntrinsicInst>(I)) {
372-
if (II->getIntrinsicID() == Intrinsic::spv_const_composite) {
396+
if (II->getIntrinsicID() == Intrinsic::spv_const_composite ||
397+
II->getIntrinsicID() == Intrinsic::spv_undef) {
373398
auto t = AggrConsts.find(II);
374399
assert(t != AggrConsts.end());
375400
TypeToAssign = t->second->getType();
@@ -453,6 +478,7 @@ bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
453478
for (auto &GV : Func.getParent()->globals())
454479
processGlobalValue(GV);
455480

481+
preprocessUndefs();
456482
preprocessCompositeConstants();
457483
SmallVector<Instruction *> Worklist;
458484
for (auto &I : instructions(Func))

llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1316,25 +1316,18 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
13161316
switch (I.getIntrinsicID()) {
13171317
case Intrinsic::spv_load:
13181318
return selectLoad(ResVReg, ResType, I);
1319-
break;
13201319
case Intrinsic::spv_store:
13211320
return selectStore(I);
1322-
break;
13231321
case Intrinsic::spv_extractv:
13241322
return selectExtractVal(ResVReg, ResType, I);
1325-
break;
13261323
case Intrinsic::spv_insertv:
13271324
return selectInsertVal(ResVReg, ResType, I);
1328-
break;
13291325
case Intrinsic::spv_extractelt:
13301326
return selectExtractElt(ResVReg, ResType, I);
1331-
break;
13321327
case Intrinsic::spv_insertelt:
13331328
return selectInsertElt(ResVReg, ResType, I);
1334-
break;
13351329
case Intrinsic::spv_gep:
13361330
return selectGEP(ResVReg, ResType, I);
1337-
break;
13381331
case Intrinsic::spv_unref_global:
13391332
case Intrinsic::spv_init_global: {
13401333
MachineInstr *MI = MRI->getVRegDef(I.getOperand(1).getReg());
@@ -1343,7 +1336,13 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
13431336
: nullptr;
13441337
assert(MI);
13451338
return selectGlobalValue(MI->getOperand(0).getReg(), *MI, Init);
1346-
} break;
1339+
}
1340+
case Intrinsic::spv_undef: {
1341+
auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpUndef))
1342+
.addDef(ResVReg)
1343+
.addUse(GR.getSPIRVTypeID(ResType));
1344+
return MIB.constrainAllUses(TII, TRI, RBI);
1345+
}
13471346
case Intrinsic::spv_const_composite: {
13481347
// If no values are attached, the composite is null constant.
13491348
bool IsNull = I.getNumExplicitDefs() + 1 == I.getNumExplicitOperands();
@@ -1360,7 +1359,7 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
13601359
}
13611360
}
13621361
return MIB.constrainAllUses(TII, TRI, RBI);
1363-
} break;
1362+
}
13641363
case Intrinsic::spv_assign_name: {
13651364
auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpName));
13661365
MIB.addUse(I.getOperand(I.getNumExplicitDefs() + 1).getReg());
@@ -1369,7 +1368,7 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
13691368
MIB.addImm(I.getOperand(i).getImm());
13701369
}
13711370
return MIB.constrainAllUses(TII, TRI, RBI);
1372-
} break;
1371+
}
13731372
case Intrinsic::spv_switch: {
13741373
auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpSwitch));
13751374
for (unsigned i = 1; i < I.getNumExplicitOperands(); ++i) {
@@ -1383,16 +1382,14 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
13831382
llvm_unreachable("Unexpected OpSwitch operand");
13841383
}
13851384
return MIB.constrainAllUses(TII, TRI, RBI);
1386-
} break;
1385+
}
13871386
case Intrinsic::spv_cmpxchg:
13881387
return selectAtomicCmpXchg(ResVReg, ResType, I);
1389-
break;
13901388
case Intrinsic::spv_unreachable:
13911389
BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpUnreachable));
13921390
break;
13931391
case Intrinsic::spv_alloca:
13941392
return selectFrameIndex(ResVReg, ResType, I);
1395-
break;
13961393
default:
13971394
llvm_unreachable("Intrinsic selection not implemented");
13981395
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
2+
3+
; CHECK-DAG: %[[#I32:]] = OpTypeInt 32
4+
; CHECK-DAG: %[[#I16:]] = OpTypeInt 16
5+
; CHECK-DAG: %[[#STRUCT:]] = OpTypeStruct %[[#I32]] %[[#I16]]
6+
; CHECK-DAG: %[[#NESTED_STRUCT:]] = OpTypeStruct %[[#STRUCT]] %[[#I16]]
7+
; CHECK-DAG: %[[#UNDEF:]] = OpUndef %[[#NESTED_STRUCT]]
8+
9+
; CHECK: %[[#]] = OpFunction %[[#]] None %[[#]]
10+
; CHECK-NEXT: %[[#PTR:]] = OpFunctionParameter %[[#]]
11+
; CHECK-NEXT: %[[#]] = OpLabel
12+
; CHECK-NEXT: OpStore %[[#PTR]] %[[#UNDEF]] Aligned 4
13+
; CHECK-NEXT: OpReturn
14+
; CHECK-NEXT: OpFunctionEnd
15+
16+
%struct = type {
17+
i32,
18+
i16
19+
}
20+
21+
%nested_struct = type {
22+
%struct,
23+
i16
24+
}
25+
26+
define void @foo(ptr %ptr) {
27+
store %nested_struct undef, ptr %ptr
28+
ret void
29+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
2+
3+
; CHECK-DAG: %[[#I32:]] = OpTypeInt 32
4+
; CHECK-DAG: %[[#I16:]] = OpTypeInt 16
5+
; CHECK-DAG: %[[#STRUCT:]] = OpTypeStruct %[[#I32]] %[[#I16]]
6+
; CHECK-DAG: %[[#UNDEF:]] = OpUndef %[[#STRUCT]]
7+
8+
; CHECK: %[[#]] = OpFunction %[[#]] None %[[#]]
9+
; CHECK-NEXT: %[[#PTR:]] = OpFunctionParameter %[[#]]
10+
; CHECK-NEXT: %[[#]] = OpLabel
11+
; CHECK-NEXT: OpStore %[[#PTR]] %[[#UNDEF]] Aligned 4
12+
; CHECK-NEXT: OpReturn
13+
; CHECK-NEXT: OpFunctionEnd
14+
15+
define void @foo(ptr %ptr) {
16+
store { i32, i16 } undef, ptr %ptr
17+
ret void
18+
}

0 commit comments

Comments
 (0)