Implement SPV_KHR_poison_freeze extension (#198037)
Specification can be found here: https://github.com/KhronosGroup/SPIRV-Registry/blob/main/extensions/KHR/SPV_KHR_poison_freeze.asciidoc
This commit is contained in:
parent
34e7ecaf87
commit
b43dcbeffe
16 changed files with 447 additions and 54 deletions
|
|
@ -281,6 +281,8 @@ Below is a list of supported SPIR-V extensions, sorted alphabetically by their e
|
|||
- Adds cooperative matrix types and instructions for matrix multiply-add operations across invocations in a subgroup.
|
||||
* - ``SPV_KHR_vulkan_memory_model``
|
||||
- Adds the Vulkan memory model, which defines memory semantics for the Vulkan API.
|
||||
* - ``SPV_KHR_poison_freeze``
|
||||
- Adds instructions to represent a poison value and freeze. Also adds an execution mode to control poison behavior.
|
||||
|
||||
|
||||
SPIR-V representation in LLVM IR
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ let TargetPrefix = "spv" in {
|
|||
def int_spv_alloca : Intrinsic<[llvm_any_ty], [llvm_i32_ty], [ImmArg<ArgIndex<0>>]>;
|
||||
def int_spv_alloca_array : Intrinsic<[llvm_any_ty], [llvm_anyint_ty, llvm_i32_ty], [ImmArg<ArgIndex<1>>]>;
|
||||
def int_spv_undef : Intrinsic<[llvm_i32_ty], []>;
|
||||
def int_spv_poison : Intrinsic<[llvm_any_ty], [], [IntrNoMem]>;
|
||||
def int_spv_freeze : Intrinsic<[llvm_any_ty], [LLVMMatchType<0>]>;
|
||||
def int_spv_inline_asm : Intrinsic<[], [llvm_metadata_ty, llvm_metadata_ty, llvm_vararg_ty]>;
|
||||
def int_spv_named_boolean_spec_constant
|
||||
: Intrinsic<[llvm_i1_ty], [llvm_i32_ty, llvm_i1_ty, llvm_metadata_ty],
|
||||
|
|
|
|||
|
|
@ -543,15 +543,18 @@ void SPIRVAsmPrinter::outputExecutionMode(const Module &M) {
|
|||
NamedMDNode *Node = M.getNamedMetadata("spirv.ExecutionMode");
|
||||
if (Node) {
|
||||
for (unsigned i = 0; i < Node->getNumOperands(); i++) {
|
||||
const auto EM =
|
||||
cast<ConstantInt>(
|
||||
cast<ConstantAsMetadata>((Node->getOperand(i))->getOperand(1))
|
||||
->getValue())
|
||||
->getZExtValue();
|
||||
// Skip ArithmeticPoisonKHR to avoid a duplicate.
|
||||
if (EM == SPIRV::ExecutionMode::ArithmeticPoisonKHR)
|
||||
continue;
|
||||
// If SPV_KHR_float_controls2 is enabled and we find any of
|
||||
// FPFastMathDefault, ContractionOff or SignedZeroInfNanPreserve execution
|
||||
// modes, skip it, it'll be done somewhere else.
|
||||
if (ST->canUseExtension(SPIRV::Extension::SPV_KHR_float_controls2)) {
|
||||
const auto EM =
|
||||
cast<ConstantInt>(
|
||||
cast<ConstantAsMetadata>((Node->getOperand(i))->getOperand(1))
|
||||
->getValue())
|
||||
->getZExtValue();
|
||||
if (EM == SPIRV::ExecutionMode::FPFastMathDefault ||
|
||||
EM == SPIRV::ExecutionMode::ContractionOff ||
|
||||
EM == SPIRV::ExecutionMode::SignedZeroInfNanPreserve)
|
||||
|
|
@ -620,6 +623,18 @@ void SPIRVAsmPrinter::outputExecutionMode(const Module &M) {
|
|||
Inst.addOperand(MCOperand::createImm(TypeCode));
|
||||
outputMCInst(Inst);
|
||||
}
|
||||
// Per SPV_KHR_poison_freeze description of PoisonFreezeKHR "If declared,
|
||||
// all entry points must use the ArithmeticPoisonKHR execution mode".
|
||||
if (llvm::is_contained(MAI->Reqs.getMinimalCapabilities(),
|
||||
SPIRV::Capability::PoisonFreezeKHR)) {
|
||||
MCInst Inst;
|
||||
Inst.setOpcode(SPIRV::OpExecutionMode);
|
||||
Inst.addOperand(MCOperand::createReg(FReg));
|
||||
unsigned EM =
|
||||
static_cast<unsigned>(SPIRV::ExecutionMode::ArithmeticPoisonKHR);
|
||||
Inst.addOperand(MCOperand::createImm(EM));
|
||||
outputMCInst(Inst);
|
||||
}
|
||||
if (ST->isKernel() && !M.getNamedMetadata("spirv.ExecutionMode") &&
|
||||
!M.getNamedMetadata("opencl.enable.FP_CONTRACT")) {
|
||||
if (ST->canUseExtension(SPIRV::Extension::SPV_KHR_float_controls2)) {
|
||||
|
|
|
|||
|
|
@ -178,7 +178,9 @@ static const StringMap<SPIRV::Extension::Extension> SPIRVExtensionMap = {
|
|||
{"SPV_INTEL_unstructured_loop_controls",
|
||||
SPIRV::Extension::Extension::SPV_INTEL_unstructured_loop_controls},
|
||||
{"SPV_AMD_weak_linkage", SPIRV::Extension::Extension::SPV_AMD_weak_linkage},
|
||||
{"SPV_KHR_abort", SPIRV::Extension::Extension::SPV_KHR_abort}};
|
||||
{"SPV_KHR_abort", SPIRV::Extension::Extension::SPV_KHR_abort},
|
||||
{"SPV_KHR_poison_freeze",
|
||||
SPIRV::Extension::Extension::SPV_KHR_poison_freeze}};
|
||||
|
||||
bool SPIRVExtensionsParser::parse(cl::Option &O, StringRef ArgName,
|
||||
StringRef ArgValue, ExtensionSet &Vals) {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "llvm/IR/TypedPointerType.h"
|
||||
#include "llvm/IR/Value.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Transforms/Utils/Local.h"
|
||||
|
||||
#include <cassert>
|
||||
|
|
@ -57,6 +58,8 @@
|
|||
using namespace llvm;
|
||||
using namespace llvm::PatternMatch;
|
||||
|
||||
#define DEBUG_TYPE "spirv-emit-intrinsics"
|
||||
|
||||
static cl::opt<bool>
|
||||
SpirvEmitOpNames("spirv-emit-op-names",
|
||||
cl::desc("Emit OpName for all instructions"),
|
||||
|
|
@ -257,6 +260,7 @@ class SPIRVEmitIntrinsics
|
|||
|
||||
void preprocessCompositeConstants(IRBuilder<> &B);
|
||||
void preprocessUndefs(IRBuilder<> &B);
|
||||
void preprocessPoisons(IRBuilder<> &B);
|
||||
void simplifyNullAddrSpaceCasts();
|
||||
|
||||
Type *reconstructType(Value *Op, bool UnknownElemTypeI8,
|
||||
|
|
@ -447,6 +451,12 @@ static bool isAggrConstForceInt32(const Value *V) {
|
|||
isa<ConstantDataArray>(V) || IsAggrZero || IsUndefAggregate;
|
||||
}
|
||||
|
||||
static bool isSpvAggrPlaceholder(const Value *V) {
|
||||
return match(
|
||||
V,
|
||||
m_AnyIntrinsic<Intrinsic::spv_undef, Intrinsic::spv_const_composite>());
|
||||
}
|
||||
|
||||
static void setInsertPointSkippingPhis(IRBuilder<> &B, Instruction *I) {
|
||||
if (isa<PHINode>(I))
|
||||
B.SetInsertPoint(I->getParent()->getFirstNonPHIOrDbgOrAlloca());
|
||||
|
|
@ -555,13 +565,8 @@ Type *SPIRVEmitIntrinsics::reconstructType(Value *Op, bool UnknownElemTypeI8,
|
|||
Type *Ty = Op->getType();
|
||||
if (auto *OpI = dyn_cast<Instruction>(Op)) {
|
||||
Ty = restoreMutatedType(GR, OpI, Ty);
|
||||
// Aggregate undef/constant values are lowered to spv_undef/
|
||||
// spv_const_composite intrinsics whose return type is i32; recover the
|
||||
// original aggregate type so downstream pointee deduction sees it.
|
||||
if (match(OpI, m_CombineOr(m_Intrinsic<Intrinsic::spv_undef>(),
|
||||
m_Intrinsic<Intrinsic::spv_const_composite>())))
|
||||
if (auto It = AggrConstTypes.find(OpI); It != AggrConstTypes.end())
|
||||
Ty = It->second;
|
||||
if (auto It = AggrConstTypes.find(OpI); It != AggrConstTypes.end())
|
||||
Ty = It->second;
|
||||
}
|
||||
if (!isUntypedPointerTy(Ty))
|
||||
return Ty;
|
||||
|
|
@ -1605,26 +1610,30 @@ void SPIRVEmitIntrinsics::replaceMemInstrUses(Instruction *Old,
|
|||
}
|
||||
|
||||
void SPIRVEmitIntrinsics::preprocessUndefs(IRBuilder<> &B) {
|
||||
std::queue<Instruction *> Worklist;
|
||||
const SPIRVSubtarget *STI = TM.getSubtargetImpl(*CurrF);
|
||||
bool HasPoisonExt =
|
||||
STI->canUseExtension(SPIRV::Extension::SPV_KHR_poison_freeze);
|
||||
SmallVector<Instruction *, 16> Insts;
|
||||
for (auto &I : instructions(CurrF))
|
||||
Worklist.push(&I);
|
||||
Insts.push_back(&I);
|
||||
|
||||
while (!Worklist.empty()) {
|
||||
Instruction *I = Worklist.front();
|
||||
for (Instruction *I : Insts) {
|
||||
bool BPrepared = false;
|
||||
Worklist.pop();
|
||||
|
||||
for (auto &Op : I->operands()) {
|
||||
auto *AggrUndef = dyn_cast<UndefValue>(Op);
|
||||
if (!AggrUndef || !Op->getType()->isAggregateType())
|
||||
continue;
|
||||
if (HasPoisonExt && isa<PoisonValue>(AggrUndef))
|
||||
continue;
|
||||
if (isa<PoisonValue>(AggrUndef))
|
||||
LLVM_DEBUG(dbgs() << "SPV_KHR_poison_freeze is not enabled. Poison is "
|
||||
"lowered as undef\n");
|
||||
|
||||
if (!BPrepared) {
|
||||
setInsertPointSkippingPhis(B, I);
|
||||
BPrepared = true;
|
||||
}
|
||||
auto *IntrUndef = B.CreateIntrinsic(Intrinsic::spv_undef, {});
|
||||
Worklist.push(IntrUndef);
|
||||
I->replaceUsesOfWith(Op, IntrUndef);
|
||||
AggrConsts[IntrUndef] = AggrUndef;
|
||||
AggrConstTypes[IntrUndef] = AggrUndef->getType();
|
||||
|
|
@ -1632,6 +1641,47 @@ void SPIRVEmitIntrinsics::preprocessUndefs(IRBuilder<> &B) {
|
|||
}
|
||||
}
|
||||
|
||||
void SPIRVEmitIntrinsics::preprocessPoisons(IRBuilder<> &B) {
|
||||
const SPIRVSubtarget *STI = TM.getSubtargetImpl(*CurrF);
|
||||
if (!STI->canUseExtension(SPIRV::Extension::SPV_KHR_poison_freeze))
|
||||
return;
|
||||
|
||||
for (Instruction &I : instructions(CurrF)) {
|
||||
bool BPrepared = false;
|
||||
auto *Phi = dyn_cast<PHINode>(&I);
|
||||
|
||||
for (unsigned Idx = 0; Idx < I.getNumOperands(); ++Idx) {
|
||||
Value *Op = I.getOperand(Idx);
|
||||
auto *Poison = dyn_cast<PoisonValue>(Op);
|
||||
if (!Poison || Op->getType()->isMetadataTy())
|
||||
continue;
|
||||
|
||||
Type *OpTy = Op->getType();
|
||||
Value *Replacement = nullptr;
|
||||
if (OpTy->isAggregateType()) {
|
||||
if (!BPrepared) {
|
||||
setInsertPointSkippingPhis(B, &I);
|
||||
BPrepared = true;
|
||||
}
|
||||
auto *Call =
|
||||
B.CreateIntrinsic(Intrinsic::spv_poison, {B.getInt32Ty()}, {});
|
||||
AggrConsts[Call] = Poison;
|
||||
AggrConstTypes[Call] = OpTy;
|
||||
Replacement = Call;
|
||||
} else {
|
||||
if (Phi)
|
||||
B.SetInsertPoint(Phi->getIncomingBlock(Idx)->getTerminator());
|
||||
else if (!BPrepared) {
|
||||
setInsertPointSkippingPhis(B, &I);
|
||||
BPrepared = true;
|
||||
}
|
||||
Replacement = B.CreateIntrinsic(Intrinsic::spv_poison, {OpTy}, {});
|
||||
}
|
||||
I.setOperand(Idx, Replacement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Simplify addrspacecast(null) instructions to ConstantPointerNull of the
|
||||
// target type. Casting null always yields null, and this avoids SPIR-V
|
||||
// lowering issues where the null gets typed as an integer instead of a
|
||||
|
|
@ -1647,6 +1697,9 @@ void SPIRVEmitIntrinsics::simplifyNullAddrSpaceCasts() {
|
|||
}
|
||||
|
||||
void SPIRVEmitIntrinsics::preprocessCompositeConstants(IRBuilder<> &B) {
|
||||
const SPIRVSubtarget *STI = TM.getSubtargetImpl(*CurrF);
|
||||
bool HasPoisonExt =
|
||||
STI->canUseExtension(SPIRV::Extension::SPV_KHR_poison_freeze);
|
||||
std::queue<Instruction *> Worklist;
|
||||
for (auto &I : instructions(CurrF))
|
||||
Worklist.push(&I);
|
||||
|
|
@ -1676,6 +1729,13 @@ void SPIRVEmitIntrinsics::preprocessCompositeConstants(IRBuilder<> &B) {
|
|||
ResTy = Op->getType()->isVectorTy() ? COp->getType() : B.getInt32Ty();
|
||||
}
|
||||
if (AggrConst) {
|
||||
auto PrepareInsert = [&]() {
|
||||
if (BPrepared)
|
||||
return;
|
||||
IsPhi ? B.SetInsertPointPastAllocas(I->getParent()->getParent())
|
||||
: B.SetInsertPoint(I);
|
||||
BPrepared = true;
|
||||
};
|
||||
SmallVector<Value *> Args;
|
||||
if (auto *COp = dyn_cast<ConstantDataSequential>(Op))
|
||||
for (unsigned i = 0; i < COp->getNumElements(); ++i)
|
||||
|
|
@ -1688,13 +1748,22 @@ void SPIRVEmitIntrinsics::preprocessCompositeConstants(IRBuilder<> &B) {
|
|||
CE && CE->getOpcode() == Instruction::AddrSpaceCast &&
|
||||
isa<ConstantPointerNull>(CE->getOperand(0)))
|
||||
Op = ConstantPointerNull::get(cast<PointerType>(CE->getType()));
|
||||
if (HasPoisonExt && isa<PoisonValue>(Op)) {
|
||||
PrepareInsert();
|
||||
Type *PoisonTy = Op->getType();
|
||||
if (PoisonTy->isAggregateType()) {
|
||||
auto *Call = B.CreateIntrinsic(Intrinsic::spv_poison,
|
||||
{B.getInt32Ty()}, {});
|
||||
AggrConsts[Call] = cast<PoisonValue>(Op);
|
||||
AggrConstTypes[Call] = PoisonTy;
|
||||
Op = Call;
|
||||
} else {
|
||||
Op = B.CreateIntrinsic(Intrinsic::spv_poison, {PoisonTy}, {});
|
||||
}
|
||||
}
|
||||
Args.push_back(Op);
|
||||
}
|
||||
if (!BPrepared) {
|
||||
IsPhi ? B.SetInsertPointPastAllocas(I->getParent()->getParent())
|
||||
: B.SetInsertPoint(I);
|
||||
BPrepared = true;
|
||||
}
|
||||
PrepareInsert();
|
||||
auto *CI =
|
||||
B.CreateIntrinsic(Intrinsic::spv_const_composite, {ResTy}, {Args});
|
||||
Worklist.push(CI);
|
||||
|
|
@ -2068,17 +2137,11 @@ void SPIRVEmitIntrinsics::insertPtrCastOrAssignTypeInstr(Instruction *I,
|
|||
Value *Op = SI->getValueOperand();
|
||||
Value *Pointer = SI->getPointerOperand();
|
||||
Type *OpTy = Op->getType();
|
||||
if (auto *OpI = dyn_cast<Instruction>(Op))
|
||||
if (auto *OpI = dyn_cast<Instruction>(Op)) {
|
||||
OpTy = restoreMutatedType(GR, OpI, OpTy);
|
||||
// Aggregate undef/constant values were lowered by preprocessUndefs/
|
||||
// preprocessCompositeConstants to spv_undef/spv_const_composite calls
|
||||
// returning i32; recover the original aggregate type so the pointee type
|
||||
// assigned to the store's pointer operand reflects the value being stored.
|
||||
if (match(Op, m_CombineOr(m_Intrinsic<Intrinsic::spv_undef>(),
|
||||
m_Intrinsic<Intrinsic::spv_const_composite>())))
|
||||
if (auto It = AggrConstTypes.find(cast<Instruction>(Op));
|
||||
It != AggrConstTypes.end())
|
||||
if (auto It = AggrConstTypes.find(OpI); It != AggrConstTypes.end())
|
||||
OpTy = It->second;
|
||||
}
|
||||
if (OpTy == Op->getType())
|
||||
OpTy = deduceElementTypeByValueDeep(OpTy, Op, false);
|
||||
replacePointerOperandWithPtrCast(I, Pointer, OpTy, 1, B);
|
||||
|
|
@ -2457,24 +2520,23 @@ shouldEmitIntrinsicsForGlobalValue(const GlobalVariableUsers &GVUsers,
|
|||
|
||||
Value *SPIRVEmitIntrinsics::buildSpvUndefComposite(Type *AggrTy,
|
||||
IRBuilder<> &B) {
|
||||
auto MakeLeaf = [&](Type *ElemTy) -> Instruction * {
|
||||
auto *Leaf = B.CreateIntrinsic(Intrinsic::spv_undef, {});
|
||||
AggrConsts[Leaf] = PoisonValue::get(ElemTy);
|
||||
AggrConstTypes[Leaf] = ElemTy;
|
||||
return Leaf;
|
||||
};
|
||||
SmallVector<Value *, 4> Elems;
|
||||
if (auto *ArrTy = dyn_cast<ArrayType>(AggrTy)) {
|
||||
Type *ElemTy = ArrTy->getElementType();
|
||||
auto *UI = B.CreateIntrinsic(Intrinsic::spv_undef, {});
|
||||
AggrConsts[UI] = PoisonValue::get(ElemTy);
|
||||
AggrConstTypes[UI] = ElemTy;
|
||||
Elems.assign(ArrTy->getNumElements(), UI);
|
||||
Elems.assign(ArrTy->getNumElements(), MakeLeaf(ArrTy->getElementType()));
|
||||
} else {
|
||||
auto *StructTy = cast<StructType>(AggrTy);
|
||||
DenseMap<Type *, Instruction *> UndefByType;
|
||||
DenseMap<Type *, Instruction *> LeafByType;
|
||||
for (unsigned I = 0; I < StructTy->getNumElements(); ++I) {
|
||||
Type *ElemTy = StructTy->getContainedType(I);
|
||||
auto &Entry = UndefByType[ElemTy];
|
||||
if (!Entry) {
|
||||
Entry = B.CreateIntrinsic(Intrinsic::spv_undef, {});
|
||||
AggrConsts[Entry] = PoisonValue::get(ElemTy);
|
||||
AggrConstTypes[Entry] = ElemTy;
|
||||
}
|
||||
auto &Entry = LeafByType[ElemTy];
|
||||
if (!Entry)
|
||||
Entry = MakeLeaf(ElemTy);
|
||||
Elems.push_back(Entry);
|
||||
}
|
||||
}
|
||||
|
|
@ -2499,8 +2561,21 @@ void SPIRVEmitIntrinsics::processGlobalValue(GlobalVariable &GV,
|
|||
deduceElementTypeHelper(&GV, false);
|
||||
Init = GV.getInitializer();
|
||||
Value *InitOp = Init;
|
||||
if (isa<UndefValue>(Init) && Init->getType()->isAggregateType())
|
||||
InitOp = buildSpvUndefComposite(Init->getType(), B);
|
||||
if (isa<UndefValue>(Init) && Init->getType()->isAggregateType()) {
|
||||
const SPIRVSubtarget *STI = TM.getSubtargetImpl();
|
||||
bool UsePoison =
|
||||
isa<PoisonValue>(Init) &&
|
||||
STI->canUseExtension(SPIRV::Extension::SPV_KHR_poison_freeze);
|
||||
if (UsePoison) {
|
||||
auto *Call =
|
||||
B.CreateIntrinsic(Intrinsic::spv_poison, {B.getInt32Ty()}, {});
|
||||
AggrConsts[Call] = cast<PoisonValue>(Init);
|
||||
AggrConstTypes[Call] = Init->getType();
|
||||
InitOp = Call;
|
||||
} else {
|
||||
InitOp = buildSpvUndefComposite(Init->getType(), B);
|
||||
}
|
||||
}
|
||||
Type *Ty = isAggrConstForceInt32(Init) ? B.getInt32Ty() : Init->getType();
|
||||
Constant *Const = isAggrConstForceInt32(Init) ? B.getInt32(1) : Init;
|
||||
auto *InitInst = B.CreateIntrinsic(Intrinsic::spv_init_global,
|
||||
|
|
@ -2593,12 +2668,14 @@ void SPIRVEmitIntrinsics::insertAssignTypeIntrs(Instruction *I,
|
|||
setInsertPointAfterDef(B, I);
|
||||
Type *TypeToAssign = Ty;
|
||||
if (auto *II = dyn_cast<IntrinsicInst>(I)) {
|
||||
if (II->getIntrinsicID() == Intrinsic::spv_const_composite ||
|
||||
II->getIntrinsicID() == Intrinsic::spv_undef) {
|
||||
if (isSpvAggrPlaceholder(II)) {
|
||||
auto It = AggrConstTypes.find(II);
|
||||
if (It == AggrConstTypes.end())
|
||||
report_fatal_error("Unknown composite intrinsic type");
|
||||
TypeToAssign = It->second;
|
||||
} else if (II->getIntrinsicID() == Intrinsic::spv_poison) {
|
||||
if (auto It = AggrConstTypes.find(II); It != AggrConstTypes.end())
|
||||
TypeToAssign = It->second;
|
||||
}
|
||||
} else if (auto It = AggrConstTypes.find(I); It != AggrConstTypes.end())
|
||||
TypeToAssign = It->second;
|
||||
|
|
@ -3375,6 +3452,7 @@ bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
|
|||
processGlobalValue(GV, B);
|
||||
|
||||
preprocessUndefs(B);
|
||||
preprocessPoisons(B);
|
||||
simplifyNullAddrSpaceCasts();
|
||||
preprocessCompositeConstants(B);
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ bool SPIRVInstrInfo::isConstantInstr(const MachineInstr &MI) const {
|
|||
case SPIRV::OpSpecConstantCompositeContinuedINTEL:
|
||||
case SPIRV::OpSpecConstantOp:
|
||||
case SPIRV::OpUndef:
|
||||
case SPIRV::OpPoisonKHR:
|
||||
case SPIRV::OpConstantFunctionPointerINTEL:
|
||||
return true;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -98,6 +98,10 @@ def OpNop: SimpleOp<"OpNop", 0>;
|
|||
def OpUndef: Op<1, (outs ID:$res), (ins TYPE:$type), "$res = OpUndef $type">;
|
||||
def OpSizeOf: Op<321, (outs ID:$res), (ins TYPE:$ty, ID:$ptr), "$res = OpSizeOf $ty $ptr">;
|
||||
|
||||
// - SPV_KHR_poison_freeze : Poison values and freeze
|
||||
def OpPoisonKHR: Op<5158, (outs ID:$res), (ins TYPE:$type), "$res = OpPoisonKHR $type">;
|
||||
def OpFreezeKHR: Op<5159, (outs ID:$res), (ins TYPE:$type, ID:$val), "$res = OpFreezeKHR $type $val">;
|
||||
|
||||
// - SPV_KHR_expect_assume : Expect assume instructions
|
||||
def OpAssumeTrueKHR: Op<5630, (outs), (ins ID:$cond), "OpAssumeTrueKHR $cond">;
|
||||
def OpExpectKHR: Op<5631, (outs ID:$res), (ins TYPE:$ty, ID:$val, ID:$expected), "$res = OpExpectKHR $ty $val $expected">;
|
||||
|
|
|
|||
|
|
@ -623,7 +623,7 @@ static bool isConstReg(MachineRegisterInfo *MRI, MachineInstr *OpDef) {
|
|||
GIntrinsic *GIntr = cast<GIntrinsic>(MI);
|
||||
unsigned IntrID = GIntr->getIntrinsicID();
|
||||
if (IntrID != Intrinsic::spv_const_composite &&
|
||||
IntrID != Intrinsic::spv_undef)
|
||||
IntrID != Intrinsic::spv_undef && IntrID != Intrinsic::spv_poison)
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -650,6 +650,7 @@ static bool isConstReg(MachineRegisterInfo *MRI, MachineInstr *OpDef) {
|
|||
case SPIRV::OpConstantSampler:
|
||||
case SPIRV::OpConstantNull:
|
||||
case SPIRV::OpUndef:
|
||||
case SPIRV::OpPoisonKHR:
|
||||
case SPIRV::OpConstantFunctionPointerINTEL:
|
||||
continue;
|
||||
default:
|
||||
|
|
@ -3840,14 +3841,25 @@ bool SPIRVInstructionSelector::selectBitreverse(Register ResVReg,
|
|||
bool SPIRVInstructionSelector::selectFreeze(Register ResVReg,
|
||||
SPIRVTypeInst ResType,
|
||||
MachineInstr &I) const {
|
||||
assert(I.getOperand(0).isReg() && I.getOperand(1).isReg() &&
|
||||
"G_FREEZE must define and use a register");
|
||||
Register OpReg = I.getOperand(1).getReg();
|
||||
|
||||
// With SPV_KHR_poison_freeze, lower `freeze` to OpFreezeKHR.
|
||||
if (STI.canUseExtension(SPIRV::Extension::SPV_KHR_poison_freeze)) {
|
||||
BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpFreezeKHR))
|
||||
.addDef(ResVReg)
|
||||
.addUse(GR.getSPIRVTypeID(ResType))
|
||||
.addUse(OpReg)
|
||||
.constrainAllUses(TII, TRI, RBI);
|
||||
return true;
|
||||
}
|
||||
|
||||
// There is no way to implement `freeze` correctly without support on SPIR-V
|
||||
// standard side, but we may at least address a simple (static) case when
|
||||
// undef/poison value presence is obvious. The main benefit of even
|
||||
// incomplete `freeze` support is preventing of translation from crashing due
|
||||
// to lack of support on legalization and instruction selection steps.
|
||||
if (!I.getOperand(0).isReg() || !I.getOperand(1).isReg())
|
||||
return false;
|
||||
Register OpReg = I.getOperand(1).getReg();
|
||||
if (MachineInstr *Def = MRI->getVRegDef(OpReg)) {
|
||||
if (Def->getOpcode() == TargetOpcode::COPY)
|
||||
Def = MRI->getVRegDef(Def->getOperand(1).getReg());
|
||||
|
|
@ -3867,9 +3879,13 @@ bool SPIRVInstructionSelector::selectFreeze(Register ResVReg,
|
|||
unsigned DestOpCode;
|
||||
if (Reg.isValid()) {
|
||||
DestOpCode = SPIRV::OpConstantNull;
|
||||
LLVM_DEBUG(dbgs() << "SPV_KHR_poison_freeze is not enabled. freeze of a "
|
||||
"static undef/poison lowered to OpConstantNull\n");
|
||||
} else {
|
||||
DestOpCode = TargetOpcode::COPY;
|
||||
Reg = OpReg;
|
||||
LLVM_DEBUG(dbgs() << "SPV_KHR_poison_freeze is not enabled. freeze "
|
||||
"skipped, lowered as a copy of the operand\n");
|
||||
}
|
||||
BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(DestOpCode))
|
||||
.addDef(I.getOperand(0).getReg())
|
||||
|
|
@ -4718,6 +4734,19 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
|
|||
MIB.constrainAllUses(TII, TRI, RBI);
|
||||
return true;
|
||||
}
|
||||
case Intrinsic::spv_poison:
|
||||
BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpPoisonKHR))
|
||||
.addDef(ResVReg)
|
||||
.addUse(GR.getSPIRVTypeID(ResType))
|
||||
.constrainAllUses(TII, TRI, RBI);
|
||||
return true;
|
||||
case Intrinsic::spv_freeze:
|
||||
BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpFreezeKHR))
|
||||
.addDef(ResVReg)
|
||||
.addUse(GR.getSPIRVTypeID(ResType))
|
||||
.addUse(I.getOperand(2).getReg())
|
||||
.constrainAllUses(TII, TRI, RBI);
|
||||
return true;
|
||||
case Intrinsic::spv_named_boolean_spec_constant: {
|
||||
auto Opcode = I.getOperand(3).getImm() ? SPIRV::OpSpecConstantTrue
|
||||
: SPIRV::OpSpecConstantFalse;
|
||||
|
|
|
|||
|
|
@ -1981,6 +1981,15 @@ void addInstrRequirements(const MachineInstr &MI,
|
|||
Reqs.addExtension(SPIRV::Extension::SPV_KHR_abort);
|
||||
Reqs.addCapability(SPIRV::Capability::AbortKHR);
|
||||
break;
|
||||
case SPIRV::OpPoisonKHR:
|
||||
case SPIRV::OpFreezeKHR:
|
||||
if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_poison_freeze))
|
||||
report_fatal_error("OpPoisonKHR/OpFreezeKHR instruction requires the "
|
||||
"following SPIR-V extension: SPV_KHR_poison_freeze",
|
||||
false);
|
||||
Reqs.addExtension(SPIRV::Extension::SPV_KHR_poison_freeze);
|
||||
Reqs.addCapability(SPIRV::Capability::PoisonFreezeKHR);
|
||||
break;
|
||||
case SPIRV::OpFunctionPointerCallINTEL:
|
||||
if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers)) {
|
||||
Reqs.addExtension(SPIRV::Extension::SPV_INTEL_function_pointers);
|
||||
|
|
|
|||
|
|
@ -403,6 +403,7 @@ defm SPV_AMD_weak_linkage : ExtensionOperand<137, [EnvOpenCL]>;
|
|||
// Vulkan/Shader support requires emitting explicit layout decorations on the
|
||||
// Message Type, which is not yet implemented.
|
||||
defm SPV_KHR_abort : ExtensionOperand<138, [EnvOpenCL]>;
|
||||
defm SPV_KHR_poison_freeze : ExtensionOperand<139, [EnvVulkan, EnvOpenCL]>;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Multiclass used to define Capabilities enum values and at the same time
|
||||
|
|
@ -640,6 +641,7 @@ defm BlockingPipesALTERA : CapabilityOperand<5945, 0, 0, [SPV_ALTERA_blocking_pi
|
|||
defm ArbitraryPrecisionFixedPointALTERA : CapabilityOperand<5922, 0, 0, [SPV_ALTERA_arbitrary_precision_fixed_point], []>;
|
||||
defm ArbitraryPrecisionFloatingPointALTERA : CapabilityOperand<5845, 0, 0,[SPV_ALTERA_arbitrary_precision_floating_point], []>;
|
||||
defm UnstructuredLoopControlsINTEL : CapabilityOperand<5886, 0, 0, [SPV_INTEL_unstructured_loop_controls], []>;
|
||||
defm PoisonFreezeKHR : CapabilityOperand<5156, 0, 0, [SPV_KHR_poison_freeze], []>;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Multiclass used to define SourceLanguage enum values and at the same time
|
||||
|
|
@ -844,6 +846,7 @@ defm RoundingModeRTNINTEL : ExecutionModeOperand<5621, [RoundToInfinityINTEL]>;
|
|||
defm FloatingPointModeALTINTEL : ExecutionModeOperand<5622, [FloatingPointModeINTEL]>;
|
||||
defm FloatingPointModeIEEEINTEL : ExecutionModeOperand<5623, [FloatingPointModeINTEL]>;
|
||||
defm MaxWorkgroupSizeINTEL : ExecutionModeOperand<5893, [KernelAttributesINTEL]>;
|
||||
defm ArithmeticPoisonKHR : ExecutionModeOperand<5157, [PoisonFreezeKHR]>;
|
||||
// TODO-SPIRV: Add the following once they are used / tested.
|
||||
// defm MaxWorkDimINTEL : ExecutionModeOperand<5894, [KernelAttributesINTEL]>;
|
||||
// defm NoGlobalOffsetINTEL : ExecutionModeOperand<5895, [KernelAttributesINTEL]>;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_poison_freeze %s -o - | FileCheck %s
|
||||
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_poison_freeze %s -o - -filetype=obj | spirv-val %}
|
||||
|
||||
; The entry point already requests ArithmeticPoisonKHR through
|
||||
; !spirv.ExecutionMode. The capability-driven path must not add a second,
|
||||
; duplicate execution mode for it.
|
||||
|
||||
; CHECK-DAG: OpCapability PoisonFreezeKHR
|
||||
; CHECK-DAG: OpExtension "SPV_KHR_poison_freeze"
|
||||
; CHECK: OpEntryPoint Kernel %[[#ENTRY:]] "kernel_with_poison"
|
||||
; CHECK-COUNT-1: OpExecutionMode %[[#ENTRY]] ArithmeticPoisonKHR
|
||||
; CHECK-NOT: OpExecutionMode %[[#ENTRY]] ArithmeticPoisonKHR
|
||||
|
||||
define spir_kernel void @kernel_with_poison(ptr %dst) {
|
||||
store i32 poison, ptr %dst
|
||||
ret void
|
||||
}
|
||||
|
||||
!spirv.ExecutionMode = !{!0}
|
||||
!0 = !{ptr @kernel_with_poison, i32 5157}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_poison_freeze %s -o - | FileCheck %s
|
||||
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_poison_freeze %s -o - -filetype=obj | spirv-val %}
|
||||
|
||||
; CHECK-NOT: OpCapability PoisonFreezeKHR
|
||||
; CHECK-NOT: OpExtension "SPV_KHR_poison_freeze"
|
||||
; CHECK-NOT: ArithmeticPoisonKHR
|
||||
|
||||
define spir_kernel void @kernel_no_poison(ptr %dst, i32 %v) {
|
||||
store i32 %v, ptr %dst
|
||||
ret void
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_poison_freeze %s -o - | FileCheck %s
|
||||
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_poison_freeze %s -o - -filetype=obj | spirv-val %}
|
||||
|
||||
; Poison only appears in poison_helper, which is not an entry point and is not
|
||||
; reachable from one. The PoisonFreezeKHR capability is nonetheless module-wide,
|
||||
; so every entry point must carry the ArithmeticPoisonKHR execution mode, while
|
||||
; the non-entry helper must not.
|
||||
|
||||
; CHECK-DAG: OpCapability PoisonFreezeKHR
|
||||
; CHECK-DAG: OpExtension "SPV_KHR_poison_freeze"
|
||||
|
||||
; CHECK: OpEntryPoint Kernel %[[#ENTRY_A:]] "entry_a"
|
||||
; CHECK: OpEntryPoint Kernel %[[#ENTRY_B:]] "entry_b"
|
||||
; CHECK: OpExecutionMode %[[#ENTRY_A]] ArithmeticPoisonKHR
|
||||
; CHECK: OpExecutionMode %[[#ENTRY_B]] ArithmeticPoisonKHR
|
||||
; CHECK-NOT: OpExecutionMode %[[#]] ArithmeticPoisonKHR
|
||||
|
||||
define void @poison_helper(ptr %dst) {
|
||||
store i32 poison, ptr %dst
|
||||
ret void
|
||||
}
|
||||
|
||||
define spir_kernel void @entry_a(ptr %dst) {
|
||||
store i32 0, ptr %dst
|
||||
ret void
|
||||
}
|
||||
|
||||
define spir_kernel void @entry_b(ptr %dst) {
|
||||
store i32 0, ptr %dst
|
||||
ret void
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_poison_freeze %s -o - | FileCheck %s
|
||||
; RUN: llc -O0 -mtriple=spirv-vulkan-unknown --spirv-ext=+SPV_KHR_poison_freeze %s -o - | FileCheck %s
|
||||
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_poison_freeze %s -o - -filetype=obj | spirv-val %}
|
||||
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-unknown --spirv-ext=+SPV_KHR_poison_freeze %s -o - -filetype=obj | spirv-val %}
|
||||
|
||||
; CHECK-DAG: OpCapability PoisonFreezeKHR
|
||||
; CHECK-DAG: OpExtension "SPV_KHR_poison_freeze"
|
||||
|
||||
; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 0
|
||||
; CHECK-DAG: %[[#V4:]] = OpTypeVector %[[#I32]] 4
|
||||
|
||||
; CHECK-DAG: %[[#PI:]] = OpPoisonKHR %[[#I32]]
|
||||
; CHECK-DAG: %[[#PV:]] = OpPoisonKHR %[[#V4]]
|
||||
|
||||
; CHECK: %[[#FS:]] = OpFreezeKHR %[[#I32]] %[[#PI]]
|
||||
; CHECK: OpStore %[[#]] %[[#FS]]
|
||||
|
||||
; CHECK: %[[#FVP:]] = OpFreezeKHR %[[#V4]] %[[#PV]]
|
||||
; CHECK: OpStore %[[#]] %[[#FVP]]
|
||||
|
||||
; CHECK: %[[#FV:]] = OpFreezeKHR %[[#V4]] %[[#]]
|
||||
; CHECK: OpStore %[[#]] %[[#FV]]
|
||||
|
||||
define void @freeze_scalar_poison(ptr %dst) {
|
||||
%f = freeze i32 poison
|
||||
store i32 %f, ptr %dst
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @freeze_vector_poison(ptr %dst) {
|
||||
%f = freeze <4 x i32> poison
|
||||
store <4 x i32> %f, ptr %dst
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @freeze_vector(ptr %dst, <4 x i32> %v) {
|
||||
%f = freeze <4 x i32> %v
|
||||
store <4 x i32> %f, ptr %dst
|
||||
ret void
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_poison_freeze %s -o - | FileCheck %s
|
||||
; RUN: llc -O0 -mtriple=spirv-vulkan-unknown --spirv-ext=+SPV_KHR_poison_freeze %s -o - | FileCheck %s
|
||||
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_poison_freeze %s -o - -filetype=obj | spirv-val %}
|
||||
|
||||
; CHECK-DAG: OpCapability PoisonFreezeKHR
|
||||
; CHECK-DAG: OpExtension "SPV_KHR_poison_freeze"
|
||||
|
||||
; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 0
|
||||
; CHECK-DAG: %[[#ARR:]] = OpTypeArray %[[#I32]] %[[#]]
|
||||
|
||||
; CHECK-DAG: %[[#INNER:]] = OpTypeArray %[[#I32]] %[[#]]
|
||||
; CHECK-DAG: %[[#NEST:]] = OpTypeArray %[[#INNER]] %[[#]]
|
||||
|
||||
; CHECK-DAG: %[[#PI:]] = OpPoisonKHR %[[#I32]]
|
||||
; CHECK-DAG: %[[#C1:]] = OpConstant %[[#I32]] 1
|
||||
; CHECK-DAG: %[[#C3:]] = OpConstant %[[#I32]] 3
|
||||
; CHECK-DAG: %[[#C4:]] = OpConstant %[[#I32]] 4
|
||||
; CHECK-DAG: %[[#MIX:]] = OpConstantComposite %[[#ARR]] %[[#C1]] %[[#PI]] %[[#C3]] %[[#C4]]
|
||||
|
||||
; CHECK-DAG: %[[#PINNER:]] = OpPoisonKHR %[[#INNER]]
|
||||
; CHECK-DAG: %[[#PARTIAL:]] = OpConstantComposite %[[#INNER]] %[[#C1]] %[[#PI]]
|
||||
; CHECK-DAG: %[[#NESTMIX:]] = OpConstantComposite %[[#NEST]] %[[#PARTIAL]] %[[#PINNER]]
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: OpStore %[[#]] %[[#MIX]]
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: OpStore %[[#]] %[[#NESTMIX]]
|
||||
|
||||
%arr = type [4 x i32]
|
||||
%nest = type [2 x [2 x i32]]
|
||||
|
||||
@g_partial = global %arr [i32 1, i32 poison, i32 3, i32 4]
|
||||
|
||||
define void @poison_partial_composite(ptr %dst) {
|
||||
store %arr [i32 1, i32 poison, i32 3, i32 4], ptr %dst
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @poison_nested_composite(ptr %dst) {
|
||||
store %nest [[2 x i32] [i32 1, i32 poison], [2 x i32] poison], ptr %dst
|
||||
ret void
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_poison_freeze %s -o - | FileCheck %s --check-prefixes=CHECK,OCL
|
||||
; RUN: llc -O0 -mtriple=spirv-vulkan-unknown --spirv-ext=+SPV_KHR_poison_freeze %s -o - | FileCheck %s --check-prefixes=CHECK
|
||||
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_poison_freeze %s -o - -filetype=obj | spirv-val %}
|
||||
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-unknown --spirv-ext=+SPV_KHR_poison_freeze %s -o - -filetype=obj | spirv-val %}
|
||||
|
||||
; CHECK-DAG: OpCapability PoisonFreezeKHR
|
||||
; CHECK-DAG: OpExtension "SPV_KHR_poison_freeze"
|
||||
|
||||
; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 0
|
||||
; CHECK-DAG: %[[#V4:]] = OpTypeVector %[[#I32]] 4
|
||||
; CHECK-DAG: %[[#ARR:]] = OpTypeArray %[[#I32]] %[[#]]
|
||||
; CHECK-DAG: %[[#F32:]] = OpTypeFloat 32
|
||||
; CHECK-DAG: %[[#V4F:]] = OpTypeVector %[[#F32]] 4
|
||||
|
||||
; CHECK-DAG: %[[#PI:]] = OpPoisonKHR %[[#I32]]
|
||||
; CHECK-DAG: %[[#PV:]] = OpPoisonKHR %[[#V4]]
|
||||
; CHECK-DAG: %[[#PVF:]] = OpPoisonKHR %[[#V4F]]
|
||||
; CHECK-DAG: %[[#PF:]] = OpPoisonKHR %[[#F32]]
|
||||
; CHECK-DAG: %[[#PA:]] = OpPoisonKHR %[[#ARR]]
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: OpStore %[[#]] %[[#PI]]
|
||||
; CHECK: OpStore %[[#]] %[[#PI]]
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: OpStore %[[#]] %[[#PV]]
|
||||
; CHECK: OpStore %[[#]] %[[#PV]]
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: OpStore %[[#]] %[[#PA]]
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: OpVectorShuffle %[[#V4F]] %[[#]] %[[#PVF]]
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: OpSelect %[[#V4F]] %[[#]] %[[#]] %[[#PVF]]
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: OpFAdd %[[#F32]] %[[#]] %[[#PF]]
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: OpFAdd %[[#F32]] %[[#]] %[[#PF]]
|
||||
|
||||
; OpPhi survives only on the OpenCL flow. The Vulkan path structurizes the
|
||||
; CFG and folds phi(%v, poison) to %v before lowering.
|
||||
; OCL: OpFunction
|
||||
; OCL: OpPhi %[[#V4F]] %[[#]] %[[#]] %[[#PVF]] %[[#]]
|
||||
|
||||
%arr = type [4 x i32]
|
||||
|
||||
define void @poison_scalar(ptr %dst) {
|
||||
store i32 poison, ptr %dst
|
||||
store i32 poison, ptr %dst
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @poison_vector(ptr %dst) {
|
||||
store <4 x i32> poison, ptr %dst
|
||||
store <4 x i32> poison, ptr %dst
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @poison_aggregate(ptr %dst) {
|
||||
store %arr poison, ptr %dst
|
||||
ret void
|
||||
}
|
||||
|
||||
define <4 x float> @poison_shuffle(float %val) {
|
||||
%splat = insertelement <4 x float> poison, float %val, i32 0
|
||||
%bcast = shufflevector <4 x float> %splat, <4 x float> poison, <4 x i32> zeroinitializer
|
||||
ret <4 x float> %bcast
|
||||
}
|
||||
|
||||
define <4 x float> @poison_select(i1 %cond, <4 x float> %v) {
|
||||
%r = select i1 %cond, <4 x float> %v, <4 x float> poison
|
||||
ret <4 x float> %r
|
||||
}
|
||||
|
||||
define float @poison_fadd(float %x) {
|
||||
%r = fadd float %x, poison
|
||||
ret float %r
|
||||
}
|
||||
|
||||
; A direct call to the type-overloaded intrinsic must lower the same way.
|
||||
declare float @llvm.spv.poison.f32()
|
||||
|
||||
define float @poison_intrinsic_call(float %x) {
|
||||
%p = call float @llvm.spv.poison.f32()
|
||||
%r = fadd float %x, %p
|
||||
ret float %r
|
||||
}
|
||||
|
||||
define <4 x float> @poison_phi(i1 %c, <4 x float> %v) {
|
||||
entry:
|
||||
br i1 %c, label %a, label %b
|
||||
a:
|
||||
br label %j
|
||||
b:
|
||||
br label %j
|
||||
j:
|
||||
%p = phi <4 x float> [ %v, %a ], [ poison, %b ]
|
||||
ret <4 x float> %p
|
||||
}
|
||||
Loading…
Reference in a new issue