diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td --- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td +++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td @@ -31,4 +31,5 @@ def int_spv_cmpxchg : Intrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_vararg_ty]>; def int_spv_unreachable : Intrinsic<[], []>; def int_spv_alloca : Intrinsic<[llvm_any_ty], []>; + def int_spv_undef : Intrinsic<[llvm_i32_ty], []>; } diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp --- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp @@ -56,6 +56,7 @@ DenseMap AggrConsts; DenseSet AggrStores; void preprocessCompositeConstants(); + void preprocessUndefs(); CallInst *buildIntrWithMD(Intrinsic::ID IntrID, ArrayRef Types, Value *Arg, Value *Arg2) { ConstantAsMetadata *CM = ValueAsMetadata::getConstant(Arg); @@ -151,6 +152,29 @@ Old->eraseFromParent(); } +void SPIRVEmitIntrinsics::preprocessUndefs() { + std::queue Worklist; + for (auto &I : instructions(F)) + Worklist.push(&I); + + while (!Worklist.empty()) { + Instruction *I = Worklist.front(); + Worklist.pop(); + + for (auto &Op : I->operands()) { + auto *AggrUndef = dyn_cast(Op); + if (!AggrUndef || !Op->getType()->isAggregateType()) + continue; + + IRB->SetInsertPoint(I); + auto *IntrUndef = IRB->CreateIntrinsic(Intrinsic::spv_undef, {}, {}); + Worklist.push(IntrUndef); + I->replaceUsesOfWith(Op, IntrUndef); + AggrConsts[IntrUndef] = AggrUndef; + } + } +} + void SPIRVEmitIntrinsics::preprocessCompositeConstants() { std::queue Worklist; for (auto &I : instructions(F)) @@ -369,7 +393,8 @@ setInsertPointSkippingPhis(*IRB, I->getNextNode()); Type *TypeToAssign = Ty; if (auto *II = dyn_cast(I)) { - if (II->getIntrinsicID() == Intrinsic::spv_const_composite) { + if (II->getIntrinsicID() == Intrinsic::spv_const_composite || + II->getIntrinsicID() == Intrinsic::spv_undef) { auto t = AggrConsts.find(II); assert(t != AggrConsts.end()); TypeToAssign = t->second->getType(); @@ -453,6 +478,7 @@ for (auto &GV : Func.getParent()->globals()) processGlobalValue(GV); + preprocessUndefs(); preprocessCompositeConstants(); SmallVector Worklist; for (auto &I : instructions(Func)) diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp --- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp @@ -1316,25 +1316,18 @@ switch (I.getIntrinsicID()) { case Intrinsic::spv_load: return selectLoad(ResVReg, ResType, I); - break; case Intrinsic::spv_store: return selectStore(I); - break; case Intrinsic::spv_extractv: return selectExtractVal(ResVReg, ResType, I); - break; case Intrinsic::spv_insertv: return selectInsertVal(ResVReg, ResType, I); - break; case Intrinsic::spv_extractelt: return selectExtractElt(ResVReg, ResType, I); - break; case Intrinsic::spv_insertelt: return selectInsertElt(ResVReg, ResType, I); - break; case Intrinsic::spv_gep: return selectGEP(ResVReg, ResType, I); - break; case Intrinsic::spv_unref_global: case Intrinsic::spv_init_global: { MachineInstr *MI = MRI->getVRegDef(I.getOperand(1).getReg()); @@ -1343,7 +1336,13 @@ : nullptr; assert(MI); return selectGlobalValue(MI->getOperand(0).getReg(), *MI, Init); - } break; + } + case Intrinsic::spv_undef: { + auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpUndef)) + .addDef(ResVReg) + .addUse(GR.getSPIRVTypeID(ResType)); + return MIB.constrainAllUses(TII, TRI, RBI); + } case Intrinsic::spv_const_composite: { // If no values are attached, the composite is null constant. bool IsNull = I.getNumExplicitDefs() + 1 == I.getNumExplicitOperands(); @@ -1360,7 +1359,7 @@ } } return MIB.constrainAllUses(TII, TRI, RBI); - } break; + } case Intrinsic::spv_assign_name: { auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpName)); MIB.addUse(I.getOperand(I.getNumExplicitDefs() + 1).getReg()); @@ -1369,7 +1368,7 @@ MIB.addImm(I.getOperand(i).getImm()); } return MIB.constrainAllUses(TII, TRI, RBI); - } break; + } case Intrinsic::spv_switch: { auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpSwitch)); for (unsigned i = 1; i < I.getNumExplicitOperands(); ++i) { @@ -1383,16 +1382,14 @@ llvm_unreachable("Unexpected OpSwitch operand"); } return MIB.constrainAllUses(TII, TRI, RBI); - } break; + } case Intrinsic::spv_cmpxchg: return selectAtomicCmpXchg(ResVReg, ResType, I); - break; case Intrinsic::spv_unreachable: BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpUnreachable)); break; case Intrinsic::spv_alloca: return selectFrameIndex(ResVReg, ResType, I); - break; default: llvm_unreachable("Intrinsic selection not implemented"); } diff --git a/llvm/test/CodeGen/SPIRV/instructions/undef-nested-composite-store.ll b/llvm/test/CodeGen/SPIRV/instructions/undef-nested-composite-store.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/instructions/undef-nested-composite-store.ll @@ -0,0 +1,29 @@ +; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s + +; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 +; CHECK-DAG: %[[#I16:]] = OpTypeInt 16 +; CHECK-DAG: %[[#STRUCT:]] = OpTypeStruct %[[#I32]] %[[#I16]] +; CHECK-DAG: %[[#NESTED_STRUCT:]] = OpTypeStruct %[[#STRUCT]] %[[#I16]] +; CHECK-DAG: %[[#UNDEF:]] = OpUndef %[[#NESTED_STRUCT]] + +; CHECK: %[[#]] = OpFunction %[[#]] None %[[#]] +; CHECK-NEXT: %[[#PTR:]] = OpFunctionParameter %[[#]] +; CHECK-NEXT: %[[#]] = OpLabel +; CHECK-NEXT: OpStore %[[#PTR]] %[[#UNDEF]] Aligned 4 +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd + +%struct = type { + i32, + i16 +} + +%nested_struct = type { + %struct, + i16 +} + +define void @foo(ptr %ptr) { + store %nested_struct undef, ptr %ptr + ret void +} diff --git a/llvm/test/CodeGen/SPIRV/instructions/undef-simple-composite-store.ll b/llvm/test/CodeGen/SPIRV/instructions/undef-simple-composite-store.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/instructions/undef-simple-composite-store.ll @@ -0,0 +1,18 @@ +; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s + +; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 +; CHECK-DAG: %[[#I16:]] = OpTypeInt 16 +; CHECK-DAG: %[[#STRUCT:]] = OpTypeStruct %[[#I32]] %[[#I16]] +; CHECK-DAG: %[[#UNDEF:]] = OpUndef %[[#STRUCT]] + +; CHECK: %[[#]] = OpFunction %[[#]] None %[[#]] +; CHECK-NEXT: %[[#PTR:]] = OpFunctionParameter %[[#]] +; CHECK-NEXT: %[[#]] = OpLabel +; CHECK-NEXT: OpStore %[[#PTR]] %[[#UNDEF]] Aligned 4 +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd + +define void @foo(ptr %ptr) { + store { i32, i16 } undef, ptr %ptr + ret void +}