diff --git a/llvm/include/llvm/IR/CMakeLists.txt b/llvm/include/llvm/IR/CMakeLists.txt --- a/llvm/include/llvm/IR/CMakeLists.txt +++ b/llvm/include/llvm/IR/CMakeLists.txt @@ -14,6 +14,7 @@ tablegen(LLVM IntrinsicsPowerPC.h -gen-intrinsic-enums -intrinsic-prefix=ppc) tablegen(LLVM IntrinsicsR600.h -gen-intrinsic-enums -intrinsic-prefix=r600) tablegen(LLVM IntrinsicsRISCV.h -gen-intrinsic-enums -intrinsic-prefix=riscv) +tablegen(LLVM IntrinsicsSPIRV.h -gen-intrinsic-enums -intrinsic-prefix=spv) tablegen(LLVM IntrinsicsS390.h -gen-intrinsic-enums -intrinsic-prefix=s390) tablegen(LLVM IntrinsicsWebAssembly.h -gen-intrinsic-enums -intrinsic-prefix=wasm) tablegen(LLVM IntrinsicsX86.h -gen-intrinsic-enums -intrinsic-prefix=x86) diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -2046,4 +2046,5 @@ include "llvm/IR/IntrinsicsSystemZ.td" include "llvm/IR/IntrinsicsWebAssembly.td" include "llvm/IR/IntrinsicsRISCV.td" +include "llvm/IR/IntrinsicsSPIRV.td" include "llvm/IR/IntrinsicsVE.td" diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td @@ -0,0 +1,31 @@ +//===- IntrinsicsSPIRV.td - Defines SPIRV intrinsics -------*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines all of the SPIRV-specific intrinsics. +// +//===----------------------------------------------------------------------===// + +let TargetPrefix = "spv" in { + def int_spv_assign_type : Intrinsic<[], [llvm_any_ty, llvm_metadata_ty]>; + def int_spv_assign_name : Intrinsic<[], [llvm_any_ty, llvm_vararg_ty]>; + + def int_spv_track_constant : Intrinsic<[llvm_any_ty], [llvm_any_ty, llvm_metadata_ty]>; + def int_spv_init_global : Intrinsic<[], [llvm_any_ty, llvm_any_ty]>; + def int_spv_unref_global : Intrinsic<[], [llvm_any_ty]>; + + def int_spv_gep : Intrinsic<[llvm_anyptr_ty], [llvm_i1_ty, llvm_any_ty, llvm_vararg_ty], [ImmArg>]>; + def int_spv_load : Intrinsic<[llvm_i32_ty], [llvm_anyptr_ty, llvm_i16_ty, llvm_i8_ty], [ImmArg>, ImmArg>]>; + def int_spv_store : Intrinsic<[], [llvm_i32_ty, llvm_anyptr_ty, llvm_i16_ty, llvm_i8_ty], [ImmArg>, ImmArg>]>; + def int_spv_extractv : Intrinsic<[llvm_any_ty], [llvm_i32_ty, llvm_vararg_ty]>; + def int_spv_insertv : Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_any_ty, llvm_vararg_ty]>; + def int_spv_extractelt : Intrinsic<[llvm_any_ty], [llvm_any_ty, llvm_anyint_ty]>; + def int_spv_insertelt : Intrinsic<[llvm_any_ty], [llvm_any_ty, llvm_any_ty, llvm_anyint_ty]>; + def int_spv_const_composite : Intrinsic<[llvm_i32_ty], [llvm_vararg_ty]>; + def int_spv_bitcast : Intrinsic<[llvm_any_ty], [llvm_any_ty]>; + def int_spv_switch : Intrinsic<[], [llvm_any_ty, llvm_vararg_ty]>; +} diff --git a/llvm/lib/Target/SPIRV/CMakeLists.txt b/llvm/lib/Target/SPIRV/CMakeLists.txt --- a/llvm/lib/Target/SPIRV/CMakeLists.txt +++ b/llvm/lib/Target/SPIRV/CMakeLists.txt @@ -15,6 +15,7 @@ add_llvm_target(SPIRVCodeGen SPIRVAsmPrinter.cpp SPIRVCallLowering.cpp + SPIRVEmitIntrinsics.cpp SPIRVGlobalRegistry.cpp SPIRVInstrInfo.cpp SPIRVInstructionSelector.cpp @@ -22,6 +23,7 @@ SPIRVLegalizerInfo.cpp SPIRVMCInstLower.cpp SPIRVModuleAnalysis.cpp + SPIRVPreLegalizer.cpp SPIRVRegisterBankInfo.cpp SPIRVRegisterInfo.cpp SPIRVSubtarget.cpp diff --git a/llvm/lib/Target/SPIRV/SPIRV.h b/llvm/lib/Target/SPIRV/SPIRV.h --- a/llvm/lib/Target/SPIRV/SPIRV.h +++ b/llvm/lib/Target/SPIRV/SPIRV.h @@ -19,12 +19,16 @@ class InstructionSelector; class RegisterBankInfo; +FunctionPass *createSPIRVPreLegalizerPass(); +FunctionPass *createSPIRVEmitIntrinsicsPass(SPIRVTargetMachine *TM); InstructionSelector * createSPIRVInstructionSelector(const SPIRVTargetMachine &TM, const SPIRVSubtarget &Subtarget, const RegisterBankInfo &RBI); void initializeSPIRVModuleAnalysisPass(PassRegistry &); +void initializeSPIRVPreLegalizerPass(PassRegistry &); +void initializeSPIRVEmitIntrinsicsPass(PassRegistry &); } // namespace llvm #endif // LLVM_LIB_TARGET_SPIRV_SPIRV_H diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp @@ -0,0 +1,433 @@ +//===-- SPIRVEmitIntrinsics.cpp - emit SPIRV intrinsics ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// The pass emits SPIRV intrinsics keeping essential high-level information for +// the translation of LLVM IR to SPIR-V. +// +//===----------------------------------------------------------------------===// + +#include "SPIRV.h" +#include "SPIRVTargetMachine.h" +#include "SPIRVUtils.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/InstVisitor.h" +#include "llvm/IR/IntrinsicsSPIRV.h" + +#include + +// This pass performs the following transformation on LLVM IR level required +// for the following translation to SPIR-V: +// - replaces direct usages of aggregate constants with target-specific +// intrinsics; +// - replaces aggregates-related instructions (extract/insert, ld/st, etc) +// with a target-specific intrinsics; +// - emits intrinsics for the global variable initializers since IRTranslator +// doesn't handle them and it's not very convenient to translate them +// ourselves; +// - emits intrinsics to keep track of the string names assigned to the values; +// - emits intrinsics to keep track of constants (this is necessary to have an +// LLVM IR constant after the IRTranslation is completed) for their further +// deduplication; +// - emits intrinsics to keep track of original LLVM types of the values +// to be able to emit proper SPIR-V types eventually. +// +// TODO: consider removing spv.track.constant in favor of spv.assign.type. + +using namespace llvm; + +namespace llvm { +void initializeSPIRVEmitIntrinsicsPass(PassRegistry &); +} // namespace llvm + +namespace { +class SPIRVEmitIntrinsics + : public FunctionPass, + public InstVisitor { + SPIRVTargetMachine *TM = nullptr; + IRBuilder<> *IRB = nullptr; + Function *F = nullptr; + bool TrackConstants = true; + DenseMap AggrConsts; + DenseSet AggrStores; + void preprocessCompositeConstants(); + CallInst *buildIntrWithMD(Intrinsic::ID IntrID, ArrayRef Types, + Value *Arg, Value *Arg2) { + ConstantAsMetadata *CM = ValueAsMetadata::getConstant(Arg); + MDTuple *TyMD = MDNode::get(F->getContext(), CM); + MetadataAsValue *VMD = MetadataAsValue::get(F->getContext(), TyMD); + return IRB->CreateIntrinsic(IntrID, {Types}, {Arg2, VMD}); + } + void replaceMemInstrUses(Instruction *Old, Instruction *New); + void processInstrAfterVisit(Instruction *I); + void insertAssignTypeIntrs(Instruction *I); + void processGlobalValue(GlobalVariable &GV); + +public: + static char ID; + SPIRVEmitIntrinsics() : FunctionPass(ID) { + initializeSPIRVEmitIntrinsicsPass(*PassRegistry::getPassRegistry()); + } + SPIRVEmitIntrinsics(SPIRVTargetMachine *_TM) : FunctionPass(ID), TM(_TM) { + initializeSPIRVEmitIntrinsicsPass(*PassRegistry::getPassRegistry()); + } + Instruction *visitInstruction(Instruction &I) { return &I; } + Instruction *visitSwitchInst(SwitchInst &I); + Instruction *visitGetElementPtrInst(GetElementPtrInst &I); + Instruction *visitBitCastInst(BitCastInst &I); + Instruction *visitInsertElementInst(InsertElementInst &I); + Instruction *visitExtractElementInst(ExtractElementInst &I); + Instruction *visitInsertValueInst(InsertValueInst &I); + Instruction *visitExtractValueInst(ExtractValueInst &I); + Instruction *visitLoadInst(LoadInst &I); + Instruction *visitStoreInst(StoreInst &I); + Instruction *visitAllocaInst(AllocaInst &I); + bool runOnFunction(Function &F) override; +}; +} // namespace + +char SPIRVEmitIntrinsics::ID = 0; + +INITIALIZE_PASS(SPIRVEmitIntrinsics, "emit-intrinsics", "SPIRV emit intrinsics", + false, false) + +static inline bool isAssignTypeInstr(const Instruction *I) { + return isa(I) && + cast(I)->getIntrinsicID() == Intrinsic::spv_assign_type; +} + +static bool isMemInstrToReplace(Instruction *I) { + return isa(I) || isa(I) || isa(I) || + isa(I); +} + +static bool isAggrToReplace(const Value *V) { + return isa(V) || isa(V) || + (isa(V) && !V->getType()->isVectorTy()); +} + +static void setInsertPointSkippingPhis(IRBuilder<> &B, Instruction *I) { + if (isa(I)) + B.SetInsertPoint(I->getParent(), I->getParent()->getFirstInsertionPt()); + else + B.SetInsertPoint(I); +} + +static bool requireAssignType(Instruction *I) { + IntrinsicInst *Intr = dyn_cast(I); + if (Intr) { + switch (Intr->getIntrinsicID()) { + case Intrinsic::invariant_start: + case Intrinsic::invariant_end: + return false; + } + } + return true; +} + +void SPIRVEmitIntrinsics::replaceMemInstrUses(Instruction *Old, + Instruction *New) { + while (!Old->user_empty()) { + auto *U = Old->user_back(); + if (isMemInstrToReplace(U) || isa(U)) { + U->replaceUsesOfWith(Old, New); + } else if (isAssignTypeInstr(U)) { + IRB->SetInsertPoint(U); + SmallVector Args = {New, U->getOperand(1)}; + IRB->CreateIntrinsic(Intrinsic::spv_assign_type, {New->getType()}, Args); + U->eraseFromParent(); + } else { + llvm_unreachable("illegal aggregate intrinsic user"); + } + } + Old->eraseFromParent(); +} + +void SPIRVEmitIntrinsics::preprocessCompositeConstants() { + std::queue Worklist; + for (auto &I : instructions(F)) + Worklist.push(&I); + + while (!Worklist.empty()) { + auto *I = Worklist.front(); + assert(I); + bool KeepInst = false; + for (const auto &Op : I->operands()) { + auto BuildCompositeIntrinsic = [&KeepInst, &Worklist, &I, &Op, + this](Constant *AggrC, + ArrayRef Args) { + IRB->SetInsertPoint(I); + auto *CCI = + IRB->CreateIntrinsic(Intrinsic::spv_const_composite, {}, {Args}); + Worklist.push(CCI); + I->replaceUsesOfWith(Op, CCI); + KeepInst = true; + AggrConsts[CCI] = AggrC; + }; + + if (auto *AggrC = dyn_cast(Op)) { + SmallVector Args(AggrC->op_begin(), AggrC->op_end()); + BuildCompositeIntrinsic(AggrC, Args); + } else if (auto *AggrC = dyn_cast(Op)) { + SmallVector Args; + for (unsigned i = 0; i < AggrC->getNumElements(); ++i) + Args.push_back(AggrC->getElementAsConstant(i)); + BuildCompositeIntrinsic(AggrC, Args); + } else if (isa(Op) && + !Op->getType()->isVectorTy()) { + auto *AggrC = cast(Op); + SmallVector Args(AggrC->op_begin(), AggrC->op_end()); + BuildCompositeIntrinsic(AggrC, Args); + } + } + if (!KeepInst) + Worklist.pop(); + } +} + +Instruction *SPIRVEmitIntrinsics::visitSwitchInst(SwitchInst &I) { + SmallVector Args; + for (auto &Op : I.operands()) + if (Op.get()->getType()->isSized()) + Args.push_back(Op); + IRB->CreateIntrinsic(Intrinsic::spv_switch, {I.getOperand(0)->getType()}, + {Args}); + return &I; +} + +Instruction *SPIRVEmitIntrinsics::visitGetElementPtrInst(GetElementPtrInst &I) { + SmallVector Types = {I.getType(), I.getOperand(0)->getType()}; + SmallVector Args; + Args.push_back(IRB->getInt1(I.isInBounds())); + for (auto &Op : I.operands()) + Args.push_back(Op); + auto *NewI = IRB->CreateIntrinsic(Intrinsic::spv_gep, {Types}, {Args}); + I.replaceAllUsesWith(NewI); + I.eraseFromParent(); + return NewI; +} + +Instruction *SPIRVEmitIntrinsics::visitBitCastInst(BitCastInst &I) { + SmallVector Types = {I.getType(), I.getOperand(0)->getType()}; + SmallVector Args(I.op_begin(), I.op_end()); + auto *NewI = IRB->CreateIntrinsic(Intrinsic::spv_bitcast, {Types}, {Args}); + std::string InstName = I.hasName() ? I.getName().str() : ""; + I.replaceAllUsesWith(NewI); + I.eraseFromParent(); + NewI->setName(InstName); + return NewI; +} + +Instruction *SPIRVEmitIntrinsics::visitInsertElementInst(InsertElementInst &I) { + SmallVector Types = {I.getType(), I.getOperand(0)->getType(), + I.getOperand(1)->getType(), + I.getOperand(2)->getType()}; + SmallVector Args(I.op_begin(), I.op_end()); + auto *NewI = IRB->CreateIntrinsic(Intrinsic::spv_insertelt, {Types}, {Args}); + std::string InstName = I.hasName() ? I.getName().str() : ""; + I.replaceAllUsesWith(NewI); + I.eraseFromParent(); + NewI->setName(InstName); + return NewI; +} + +Instruction * +SPIRVEmitIntrinsics::visitExtractElementInst(ExtractElementInst &I) { + SmallVector Types = {I.getType(), I.getVectorOperandType(), + I.getIndexOperand()->getType()}; + SmallVector Args = {I.getVectorOperand(), I.getIndexOperand()}; + auto *NewI = IRB->CreateIntrinsic(Intrinsic::spv_extractelt, {Types}, {Args}); + std::string InstName = I.hasName() ? I.getName().str() : ""; + I.replaceAllUsesWith(NewI); + I.eraseFromParent(); + NewI->setName(InstName); + return NewI; +} + +Instruction *SPIRVEmitIntrinsics::visitInsertValueInst(InsertValueInst &I) { + SmallVector Types = {I.getInsertedValueOperand()->getType()}; + SmallVector Args; + for (auto &Op : I.operands()) + if (isa(Op)) + Args.push_back(UndefValue::get(IRB->getInt32Ty())); + else + Args.push_back(Op); + for (auto &Op : I.indices()) + Args.push_back(IRB->getInt32(Op)); + Instruction *NewI = + IRB->CreateIntrinsic(Intrinsic::spv_insertv, {Types}, {Args}); + replaceMemInstrUses(&I, NewI); + return NewI; +} + +Instruction *SPIRVEmitIntrinsics::visitExtractValueInst(ExtractValueInst &I) { + SmallVector Args; + for (auto &Op : I.operands()) + Args.push_back(Op); + for (auto &Op : I.indices()) + Args.push_back(IRB->getInt32(Op)); + auto *NewI = + IRB->CreateIntrinsic(Intrinsic::spv_extractv, {I.getType()}, {Args}); + I.replaceAllUsesWith(NewI); + I.eraseFromParent(); + return NewI; +} + +Instruction *SPIRVEmitIntrinsics::visitLoadInst(LoadInst &I) { + if (!I.getType()->isAggregateType()) + return &I; + TrackConstants = false; + const auto *TLI = TM->getSubtargetImpl()->getTargetLowering(); + MachineMemOperand::Flags Flags = + TLI->getLoadMemOperandFlags(I, F->getParent()->getDataLayout()); + auto *NewI = + IRB->CreateIntrinsic(Intrinsic::spv_load, {I.getOperand(0)->getType()}, + {I.getPointerOperand(), IRB->getInt16(Flags), + IRB->getInt8(I.getAlignment())}); + replaceMemInstrUses(&I, NewI); + return NewI; +} + +Instruction *SPIRVEmitIntrinsics::visitStoreInst(StoreInst &I) { + if (!AggrStores.contains(&I)) + return &I; + TrackConstants = false; + const auto *TLI = TM->getSubtargetImpl()->getTargetLowering(); + MachineMemOperand::Flags Flags = + TLI->getStoreMemOperandFlags(I, F->getParent()->getDataLayout()); + auto *PtrOp = I.getPointerOperand(); + auto *NewI = + IRB->CreateIntrinsic(Intrinsic::spv_store, {PtrOp->getType()}, + {I.getValueOperand(), PtrOp, IRB->getInt16(Flags), + IRB->getInt8(I.getAlignment())}); + I.eraseFromParent(); + return NewI; +} + +Instruction *SPIRVEmitIntrinsics::visitAllocaInst(AllocaInst &I) { + TrackConstants = false; + return &I; +} + +void SPIRVEmitIntrinsics::processGlobalValue(GlobalVariable &GV) { + // Skip special artifical variable llvm.global.annotations. + if (GV.getName() == "llvm.global.annotations") + return; + if (GV.hasInitializer() && !isa(GV.getInitializer())) { + Constant *Init = GV.getInitializer(); + Type *Ty = isAggrToReplace(Init) ? IRB->getInt32Ty() : Init->getType(); + Constant *Const = isAggrToReplace(Init) ? IRB->getInt32(1) : Init; + auto *InitInst = IRB->CreateIntrinsic(Intrinsic::spv_init_global, + {GV.getType(), Ty}, {&GV, Const}); + InitInst->setArgOperand(1, Init); + } + if ((!GV.hasInitializer() || isa(GV.getInitializer())) && + GV.getNumUses() == 0) + IRB->CreateIntrinsic(Intrinsic::spv_unref_global, GV.getType(), &GV); +} + +void SPIRVEmitIntrinsics::insertAssignTypeIntrs(Instruction *I) { + Type *Ty = I->getType(); + if (!Ty->isVoidTy() && requireAssignType(I)) { + setInsertPointSkippingPhis(*IRB, I->getNextNode()); + Type *TypeToAssign = Ty; + if (auto *II = dyn_cast(I)) { + if (II->getIntrinsicID() == Intrinsic::spv_const_composite) { + auto t = AggrConsts.find(II); + assert(t != AggrConsts.end()); + TypeToAssign = t->second->getType(); + } + } + Constant *Const = Constant::getNullValue(TypeToAssign); + buildIntrWithMD(Intrinsic::spv_assign_type, {Ty}, Const, I); + } + for (const auto &Op : I->operands()) { + if (isa(Op) || isa(Op) || + // Check GetElementPtrConstantExpr case. + (isa(Op) && isa(Op))) { + IRB->SetInsertPoint(I); + buildIntrWithMD(Intrinsic::spv_assign_type, {Op->getType()}, Op, Op); + } + } + // StoreInst's operand type can be changed in the next stage so we need to + // store it in the set. + if (isa(I) && + cast(I)->getValueOperand()->getType()->isAggregateType()) + AggrStores.insert(I); +} + +void SPIRVEmitIntrinsics::processInstrAfterVisit(Instruction *I) { + auto *II = dyn_cast(I); + if (II && II->getIntrinsicID() == Intrinsic::spv_const_composite && + TrackConstants) { + IRB->SetInsertPoint(I->getNextNode()); + Type *Ty = IRB->getInt32Ty(); + auto t = AggrConsts.find(I); + assert(t != AggrConsts.end()); + auto *NewOp = + buildIntrWithMD(Intrinsic::spv_track_constant, {Ty, Ty}, t->second, I); + I->replaceAllUsesWith(NewOp); + NewOp->setArgOperand(0, I); + } + for (const auto &Op : I->operands()) { + if ((isa(Op) && Op->getType()->isVectorTy()) || + isa(I) || isa(I)) + TrackConstants = false; + if (isa(Op) && TrackConstants) { + unsigned OpNo = Op.getOperandNo(); + if (II && ((II->getIntrinsicID() == Intrinsic::spv_gep && OpNo == 0) || + (II->paramHasAttr(OpNo, Attribute::ImmArg)))) + continue; + IRB->SetInsertPoint(I); + auto *NewOp = buildIntrWithMD(Intrinsic::spv_track_constant, + {Op->getType(), Op->getType()}, Op, Op); + I->setOperand(OpNo, NewOp); + } + } + if (I->hasName()) { + setInsertPointSkippingPhis(*IRB, I->getNextNode()); + std::vector Args = {I}; + addStringImm(I->getName(), *IRB, Args); + IRB->CreateIntrinsic(Intrinsic::spv_assign_name, {I->getType()}, Args); + } +} + +bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) { + if (Func.isDeclaration()) + return false; + F = &Func; + IRB = new IRBuilder<>(Func.getContext()); + AggrConsts.clear(); + AggrStores.clear(); + + IRB->SetInsertPoint(&Func.getEntryBlock().front()); + + for (auto &GV : Func.getParent()->globals()) + processGlobalValue(GV); + + preprocessCompositeConstants(); + SmallVector Worklist; + for (auto &I : instructions(Func)) + Worklist.push_back(&I); + + for (auto &I : Worklist) + insertAssignTypeIntrs(I); + + for (auto *I : Worklist) { + TrackConstants = true; + if (!I->getType()->isVoidTy() || isa(I)) + IRB->SetInsertPoint(I->getNextNode()); + I = visit(*I); + processInstrAfterVisit(I); + } + return true; +} + +FunctionPass *llvm::createSPIRVEmitIntrinsicsPass(SPIRVTargetMachine *TM) { + return new SPIRVEmitIntrinsics(TM); +} diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h --- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h +++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h @@ -58,7 +58,7 @@ // In cases where the SPIR-V type is already known, this function can be // used to map it to the given VReg via an ASSIGN_TYPE instruction. void assignSPIRVTypeToVReg(SPIRVType *Type, Register VReg, - MachineIRBuilder &MIRBuilder); + MachineFunction &MF); // Either generate a new OpTypeXXX instruction or return an existing one // corresponding to the given LLVM IR type. diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp --- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp @@ -30,14 +30,14 @@ SPIRVType *SpirvType = getOrCreateSPIRVType(Type, MIRBuilder, AccessQual, EmitIR); - assignSPIRVTypeToVReg(SpirvType, VReg, MIRBuilder); + assignSPIRVTypeToVReg(SpirvType, VReg, MIRBuilder.getMF()); return SpirvType; } void SPIRVGlobalRegistry::assignSPIRVTypeToVReg(SPIRVType *SpirvType, Register VReg, - MachineIRBuilder &MIRBuilder) { - VRegToTypeMap[&MIRBuilder.getMF()][VReg] = SpirvType; + MachineFunction &MF) { + VRegToTypeMap[&MF][VReg] = SpirvType; } static Register createTypeVReg(MachineIRBuilder &MIRBuilder) { @@ -191,7 +191,7 @@ if (Reg != ResVReg) { LLT RegLLTy = LLT::pointer(MRI->getType(ResVReg).getAddressSpace(), 32); MRI->setType(Reg, RegLLTy); - assignSPIRVTypeToVReg(BaseType, Reg, MIRBuilder); + assignSPIRVTypeToVReg(BaseType, Reg, MIRBuilder.getMF()); } // If it's a global variable with name, output OpName for it. @@ -283,16 +283,23 @@ return getOpTypeFunction(RetTy, ParamTypes, MIRBuilder); } if (auto PType = dyn_cast(Ty)) { - Type *ElemType = PType->getPointerElementType(); - - // Some OpenCL and SPIRV builtins like image2d_t are passed in as pointers, - // but should be treated as custom types like OpTypeImage. - assert(!isa(ElemType) && "Unsupported StructType pointer"); - - // Otherwise, treat it as a regular pointer type. + SPIRVType *SpvElementType; + // At the moment, all opaque pointers correspond to i8 element type. + // TODO: change the implementation once opaque pointers are supported + // in the SPIR-V specification. + if (PType->isOpaque()) { + SpvElementType = getOrCreateSPIRVIntegerType(8, MIRBuilder); + } else { + Type *ElemType = PType->getNonOpaquePointerElementType(); + // TODO: support OpenCL and SPIRV builtins like image2d_t that are passed + // as pointers, but should be treated as custom types like OpTypeImage. + assert(!isa(ElemType) && "Unsupported StructType pointer"); + + // Otherwise, treat it as a regular pointer type. + SpvElementType = getOrCreateSPIRVType( + ElemType, MIRBuilder, SPIRV::AccessQualifier::ReadWrite, EmitIR); + } auto SC = addressSpaceToStorageClass(PType->getAddressSpace()); - SPIRVType *SpvElementType = getOrCreateSPIRVType( - ElemType, MIRBuilder, SPIRV::AccessQualifier::ReadWrite, EmitIR); return getOpTypePointer(SC, SpvElementType, MIRBuilder); } llvm_unreachable("Unable to convert LLVM type to SPIRVType"); 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 @@ -24,6 +24,7 @@ #include "llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/IntrinsicsSPIRV.h" #include "llvm/Support/Debug.h" #define DEBUG_TYPE "spirv-isel" @@ -139,6 +140,16 @@ MachineInstr &I) const; bool selectIntrinsic(Register ResVReg, const SPIRVType *ResType, MachineInstr &I) const; + bool selectExtractVal(Register ResVReg, const SPIRVType *ResType, + MachineInstr &I) const; + bool selectInsertVal(Register ResVReg, const SPIRVType *ResType, + MachineInstr &I) const; + bool selectExtractElt(Register ResVReg, const SPIRVType *ResType, + MachineInstr &I) const; + bool selectInsertElt(Register ResVReg, const SPIRVType *ResType, + MachineInstr &I) const; + bool selectGEP(Register ResVReg, const SPIRVType *ResType, + MachineInstr &I) const; bool selectFrameIndex(Register ResVReg, const SPIRVType *ResType, MachineInstr &I) const; @@ -968,10 +979,179 @@ .constrainAllUses(TII, TRI, RBI); } +static bool isImm(const MachineOperand &MO, MachineRegisterInfo *MRI) { + assert(MO.isReg()); + const SPIRVType *TypeInst = MRI->getVRegDef(MO.getReg()); + if (TypeInst->getOpcode() != SPIRV::ASSIGN_TYPE) + return false; + assert(TypeInst->getOperand(1).isReg()); + MachineInstr *ImmInst = MRI->getVRegDef(TypeInst->getOperand(1).getReg()); + return ImmInst->getOpcode() == TargetOpcode::G_CONSTANT; +} + +static int64_t foldImm(const MachineOperand &MO, MachineRegisterInfo *MRI) { + const SPIRVType *TypeInst = MRI->getVRegDef(MO.getReg()); + MachineInstr *ImmInst = MRI->getVRegDef(TypeInst->getOperand(1).getReg()); + assert(ImmInst->getOpcode() == TargetOpcode::G_CONSTANT); + return ImmInst->getOperand(1).getCImm()->getZExtValue(); +} + +bool SPIRVInstructionSelector::selectInsertVal(Register ResVReg, + const SPIRVType *ResType, + MachineInstr &I) const { + MachineBasicBlock &BB = *I.getParent(); + return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpCompositeInsert)) + .addDef(ResVReg) + .addUse(GR.getSPIRVTypeID(ResType)) + // object to insert + .addUse(I.getOperand(3).getReg()) + // composite to insert into + .addUse(I.getOperand(2).getReg()) + // TODO: support arbitrary number of indices + .addImm(foldImm(I.getOperand(4), MRI)) + .constrainAllUses(TII, TRI, RBI); +} + +bool SPIRVInstructionSelector::selectExtractVal(Register ResVReg, + const SPIRVType *ResType, + MachineInstr &I) const { + MachineBasicBlock &BB = *I.getParent(); + return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpCompositeExtract)) + .addDef(ResVReg) + .addUse(GR.getSPIRVTypeID(ResType)) + .addUse(I.getOperand(2).getReg()) + // TODO: support arbitrary number of indices + .addImm(foldImm(I.getOperand(3), MRI)) + .constrainAllUses(TII, TRI, RBI); +} + +bool SPIRVInstructionSelector::selectInsertElt(Register ResVReg, + const SPIRVType *ResType, + MachineInstr &I) const { + if (isImm(I.getOperand(4), MRI)) + return selectInsertVal(ResVReg, ResType, I); + MachineBasicBlock &BB = *I.getParent(); + return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpVectorInsertDynamic)) + .addDef(ResVReg) + .addUse(GR.getSPIRVTypeID(ResType)) + .addUse(I.getOperand(2).getReg()) + .addUse(I.getOperand(3).getReg()) + .addUse(I.getOperand(4).getReg()) + .constrainAllUses(TII, TRI, RBI); +} + +bool SPIRVInstructionSelector::selectExtractElt(Register ResVReg, + const SPIRVType *ResType, + MachineInstr &I) const { + if (isImm(I.getOperand(3), MRI)) + return selectExtractVal(ResVReg, ResType, I); + MachineBasicBlock &BB = *I.getParent(); + return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpVectorExtractDynamic)) + .addDef(ResVReg) + .addUse(GR.getSPIRVTypeID(ResType)) + .addUse(I.getOperand(2).getReg()) + .addUse(I.getOperand(3).getReg()) + .constrainAllUses(TII, TRI, RBI); +} + +bool SPIRVInstructionSelector::selectGEP(Register ResVReg, + const SPIRVType *ResType, + MachineInstr &I) const { + // In general we should also support OpAccessChain instrs here (i.e. not + // PtrAccessChain) but SPIRV-LLVM Translator doesn't emit them at all and so + // do we to stay compliant with its test and more importantly consumers. + unsigned Opcode = I.getOperand(2).getImm() ? SPIRV::OpInBoundsPtrAccessChain + : SPIRV::OpPtrAccessChain; + auto Res = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Opcode)) + .addDef(ResVReg) + .addUse(GR.getSPIRVTypeID(ResType)) + // Object to get a pointer to. + .addUse(I.getOperand(3).getReg()); + // Adding indices. + for (unsigned i = 4; i < I.getNumExplicitOperands(); ++i) + Res.addUse(I.getOperand(i).getReg()); + return Res.constrainAllUses(TII, TRI, RBI); +} + bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg, const SPIRVType *ResType, MachineInstr &I) const { - llvm_unreachable("Intrinsic selection not implemented"); + MachineBasicBlock &BB = *I.getParent(); + 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()); + MachineInstr *Init = I.getNumExplicitOperands() > 2 + ? MRI->getVRegDef(I.getOperand(2).getReg()) + : nullptr; + assert(MI); + return selectGlobalValue(MI->getOperand(0).getReg(), *MI, Init); + } break; + case Intrinsic::spv_const_composite: { + // If no values are attached, the composite is null constant. + bool IsNull = I.getNumExplicitDefs() + 1 == I.getNumExplicitOperands(); + unsigned Opcode = + IsNull ? SPIRV::OpConstantNull : SPIRV::OpConstantComposite; + auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(Opcode)) + .addDef(ResVReg) + .addUse(GR.getSPIRVTypeID(ResType)); + // skip type MD node we already used when generated assign.type for this + if (!IsNull) { + for (unsigned i = I.getNumExplicitDefs() + 1; + i < I.getNumExplicitOperands(); ++i) { + MIB.addUse(I.getOperand(i).getReg()); + } + } + 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()); + for (unsigned i = I.getNumExplicitDefs() + 2; + i < I.getNumExplicitOperands(); ++i) { + 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) { + if (I.getOperand(i).isReg()) + MIB.addReg(I.getOperand(i).getReg()); + else if (I.getOperand(i).isCImm()) + addNumImm(I.getOperand(i).getCImm()->getValue(), MIB); + else if (I.getOperand(i).isMBB()) + MIB.addMBB(I.getOperand(i).getMBB()); + else + llvm_unreachable("Unexpected OpSwitch operand"); + } + return MIB.constrainAllUses(TII, TRI, RBI); + } break; + default: + llvm_unreachable("Intrinsic selection not implemented"); + } + return true; } bool SPIRVInstructionSelector::selectFrameIndex(Register ResVReg, diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp --- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp @@ -264,7 +264,7 @@ MachineRegisterInfo &MRI, SPIRVGlobalRegistry *GR) { Register ConvReg = MRI.createGenericVirtualRegister(ConvTy); - GR->assignSPIRVTypeToVReg(SpirvType, ConvReg, Helper.MIRBuilder); + GR->assignSPIRVTypeToVReg(SpirvType, ConvReg, Helper.MIRBuilder.getMF()); Helper.MIRBuilder.buildInstr(TargetOpcode::G_PTRTOINT) .addDef(ConvReg) .addUse(Reg); diff --git a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp @@ -0,0 +1,440 @@ +//===-- SPIRVPreLegalizer.cpp - prepare IR for legalization -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// The pass prepares IR for legalization: it assigns SPIR-V types to registers +// and removes intrinsics which holded these types during IR translation. +// Also it processes constants and registers them in GR to avoid duplication. +// +//===----------------------------------------------------------------------===// + +#include "SPIRV.h" +#include "SPIRVGlobalRegistry.h" +#include "SPIRVSubtarget.h" +#include "SPIRVUtils.h" +#include "llvm/ADT/PostOrderIterator.h" +#include "llvm/Analysis/OptimizationRemarkEmitter.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/IntrinsicsSPIRV.h" +#include "llvm/Target/TargetIntrinsicInfo.h" + +#define DEBUG_TYPE "spirv-prelegalizer" + +using namespace llvm; + +namespace { +class SPIRVPreLegalizer : public MachineFunctionPass { +public: + static char ID; + SPIRVPreLegalizer() : MachineFunctionPass(ID) { + initializeSPIRVPreLegalizerPass(*PassRegistry::getPassRegistry()); + } + bool runOnMachineFunction(MachineFunction &MF) override; +}; +} // namespace + +static bool isSpvIntrinsic(MachineInstr &MI, Intrinsic::ID IntrinsicID) { + if (MI.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS && + MI.getIntrinsicID() == IntrinsicID) + return true; + return false; +} + +static void foldConstantsIntoIntrinsics(MachineFunction &MF) { + SmallVector ToErase; + MachineRegisterInfo &MRI = MF.getRegInfo(); + const unsigned AssignNameOperandShift = 2; + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + if (!isSpvIntrinsic(MI, Intrinsic::spv_assign_name)) + continue; + unsigned NumOp = MI.getNumExplicitDefs() + AssignNameOperandShift; + while (MI.getOperand(NumOp).isReg()) { + MachineOperand &MOp = MI.getOperand(NumOp); + MachineInstr *ConstMI = MRI.getVRegDef(MOp.getReg()); + assert(ConstMI->getOpcode() == TargetOpcode::G_CONSTANT); + MI.removeOperand(NumOp); + MI.addOperand(MachineOperand::CreateImm( + ConstMI->getOperand(1).getCImm()->getZExtValue())); + if (MRI.use_empty(ConstMI->getOperand(0).getReg())) + ToErase.push_back(ConstMI); + } + } + } + for (MachineInstr *MI : ToErase) + MI->eraseFromParent(); +} + +static void insertBitcasts(MachineFunction &MF, SPIRVGlobalRegistry *GR, + MachineIRBuilder MIB) { + SmallVector ToErase; + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + if (!isSpvIntrinsic(MI, Intrinsic::spv_bitcast)) + continue; + assert(MI.getOperand(2).isReg()); + MIB.setInsertPt(*MI.getParent(), MI); + MIB.buildBitcast(MI.getOperand(0).getReg(), MI.getOperand(2).getReg()); + ToErase.push_back(&MI); + } + } + for (MachineInstr *MI : ToErase) + MI->eraseFromParent(); +} + +// Translating GV, IRTranslator sometimes generates following IR: +// %1 = G_GLOBAL_VALUE +// %2 = COPY %1 +// %3 = G_ADDRSPACE_CAST %2 +// New registers have no SPIRVType and no register class info. +// +// Set SPIRVType for GV, propagate it from GV to other instructions, +// also set register classes. +static SPIRVType *propagateSPIRVType(MachineInstr *MI, SPIRVGlobalRegistry *GR, + MachineRegisterInfo &MRI, + MachineIRBuilder &MIB) { + SPIRVType *SpirvTy = nullptr; + assert(MI && "Machine instr is expected"); + if (MI->getOperand(0).isReg()) { + Register Reg = MI->getOperand(0).getReg(); + SpirvTy = GR->getSPIRVTypeForVReg(Reg); + if (!SpirvTy) { + switch (MI->getOpcode()) { + case TargetOpcode::G_CONSTANT: { + MIB.setInsertPt(*MI->getParent(), MI); + Type *Ty = MI->getOperand(1).getCImm()->getType(); + SpirvTy = GR->getOrCreateSPIRVType(Ty, MIB); + break; + } + case TargetOpcode::G_GLOBAL_VALUE: { + MIB.setInsertPt(*MI->getParent(), MI); + Type *Ty = MI->getOperand(1).getGlobal()->getType(); + SpirvTy = GR->getOrCreateSPIRVType(Ty, MIB); + break; + } + case TargetOpcode::G_TRUNC: + case TargetOpcode::G_ADDRSPACE_CAST: + case TargetOpcode::COPY: { + MachineOperand &Op = MI->getOperand(1); + MachineInstr *Def = Op.isReg() ? MRI.getVRegDef(Op.getReg()) : nullptr; + if (Def) + SpirvTy = propagateSPIRVType(Def, GR, MRI, MIB); + break; + } + default: + break; + } + if (SpirvTy) + GR->assignSPIRVTypeToVReg(SpirvTy, Reg, MIB.getMF()); + if (!MRI.getRegClassOrNull(Reg)) + MRI.setRegClass(Reg, &SPIRV::IDRegClass); + } + } + return SpirvTy; +} + +// Insert ASSIGN_TYPE instuction between Reg and its definition, set NewReg as +// a dst of the definition, assign SPIRVType to both registers. If SpirvTy is +// provided, use it as SPIRVType in ASSIGN_TYPE, otherwise create it from Ty. +// TODO: maybe move to SPIRVUtils. +static Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy, + SPIRVGlobalRegistry *GR, + MachineIRBuilder &MIB, + MachineRegisterInfo &MRI) { + MachineInstr *Def = MRI.getVRegDef(Reg); + assert((Ty || SpirvTy) && "Either LLVM or SPIRV type is expected."); + MIB.setInsertPt(*Def->getParent(), + (Def->getNextNode() ? Def->getNextNode()->getIterator() + : Def->getParent()->end())); + Register NewReg = MRI.createGenericVirtualRegister(MRI.getType(Reg)); + if (auto *RC = MRI.getRegClassOrNull(Reg)) + MRI.setRegClass(NewReg, RC); + SpirvTy = SpirvTy ? SpirvTy : GR->getOrCreateSPIRVType(Ty, MIB); + GR->assignSPIRVTypeToVReg(SpirvTy, Reg, MIB.getMF()); + // This is to make it convenient for Legalizer to get the SPIRVType + // when processing the actual MI (i.e. not pseudo one). + GR->assignSPIRVTypeToVReg(SpirvTy, NewReg, MIB.getMF()); + MIB.buildInstr(SPIRV::ASSIGN_TYPE) + .addDef(Reg) + .addUse(NewReg) + .addUse(GR->getSPIRVTypeID(SpirvTy)); + Def->getOperand(0).setReg(NewReg); + MRI.setRegClass(Reg, &SPIRV::ANYIDRegClass); + return NewReg; +} + +static void generateAssignInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR, + MachineIRBuilder MIB) { + MachineRegisterInfo &MRI = MF.getRegInfo(); + SmallVector ToErase; + + for (MachineBasicBlock *MBB : post_order(&MF)) { + if (MBB->empty()) + continue; + + bool ReachedBegin = false; + for (auto MII = std::prev(MBB->end()), Begin = MBB->begin(); + !ReachedBegin;) { + MachineInstr &MI = *MII; + + if (isSpvIntrinsic(MI, Intrinsic::spv_assign_type)) { + Register Reg = MI.getOperand(1).getReg(); + Type *Ty = getMDOperandAsType(MI.getOperand(2).getMetadata(), 0); + MachineInstr *Def = MRI.getVRegDef(Reg); + assert(Def && "Expecting an instruction that defines the register"); + // G_GLOBAL_VALUE already has type info. + if (Def->getOpcode() != TargetOpcode::G_GLOBAL_VALUE) + insertAssignInstr(Reg, Ty, nullptr, GR, MIB, MF.getRegInfo()); + ToErase.push_back(&MI); + } else if (MI.getOpcode() == TargetOpcode::G_CONSTANT || + MI.getOpcode() == TargetOpcode::G_FCONSTANT || + MI.getOpcode() == TargetOpcode::G_BUILD_VECTOR) { + // %rc = G_CONSTANT ty Val + // ===> + // %cty = OpType* ty + // %rctmp = G_CONSTANT ty Val + // %rc = ASSIGN_TYPE %rctmp, %cty + Register Reg = MI.getOperand(0).getReg(); + if (MRI.hasOneUse(Reg)) { + MachineInstr &UseMI = *MRI.use_instr_begin(Reg); + if (isSpvIntrinsic(UseMI, Intrinsic::spv_assign_type) || + isSpvIntrinsic(UseMI, Intrinsic::spv_assign_name)) + continue; + } + Type *Ty = nullptr; + if (MI.getOpcode() == TargetOpcode::G_CONSTANT) + Ty = MI.getOperand(1).getCImm()->getType(); + else if (MI.getOpcode() == TargetOpcode::G_FCONSTANT) + Ty = MI.getOperand(1).getFPImm()->getType(); + else { + assert(MI.getOpcode() == TargetOpcode::G_BUILD_VECTOR); + Type *ElemTy = nullptr; + MachineInstr *ElemMI = MRI.getVRegDef(MI.getOperand(1).getReg()); + assert(ElemMI); + + if (ElemMI->getOpcode() == TargetOpcode::G_CONSTANT) + ElemTy = ElemMI->getOperand(1).getCImm()->getType(); + else if (ElemMI->getOpcode() == TargetOpcode::G_FCONSTANT) + ElemTy = ElemMI->getOperand(1).getFPImm()->getType(); + else + llvm_unreachable("Unexpected opcode"); + unsigned NumElts = + MI.getNumExplicitOperands() - MI.getNumExplicitDefs(); + Ty = VectorType::get(ElemTy, NumElts, false); + } + insertAssignInstr(Reg, Ty, nullptr, GR, MIB, MRI); + } else if (MI.getOpcode() == TargetOpcode::G_TRUNC || + MI.getOpcode() == TargetOpcode::G_GLOBAL_VALUE || + MI.getOpcode() == TargetOpcode::COPY || + MI.getOpcode() == TargetOpcode::G_ADDRSPACE_CAST) { + propagateSPIRVType(&MI, GR, MRI, MIB); + } + + if (MII == Begin) + ReachedBegin = true; + else + --MII; + } + } + for (MachineInstr *MI : ToErase) + MI->eraseFromParent(); +} + +static std::pair +createNewIdReg(Register ValReg, unsigned Opcode, MachineRegisterInfo &MRI, + const SPIRVGlobalRegistry &GR) { + LLT NewT = LLT::scalar(32); + SPIRVType *SpvType = GR.getSPIRVTypeForVReg(ValReg); + assert(SpvType && "VReg is expected to have SPIRV type"); + bool IsFloat = SpvType->getOpcode() == SPIRV::OpTypeFloat; + bool IsVectorFloat = + SpvType->getOpcode() == SPIRV::OpTypeVector && + GR.getSPIRVTypeForVReg(SpvType->getOperand(1).getReg())->getOpcode() == + SPIRV::OpTypeFloat; + IsFloat |= IsVectorFloat; + auto GetIdOp = IsFloat ? SPIRV::GET_fID : SPIRV::GET_ID; + auto DstClass = IsFloat ? &SPIRV::fIDRegClass : &SPIRV::IDRegClass; + if (MRI.getType(ValReg).isPointer()) { + NewT = LLT::pointer(0, 32); + GetIdOp = SPIRV::GET_pID; + DstClass = &SPIRV::pIDRegClass; + } else if (MRI.getType(ValReg).isVector()) { + NewT = LLT::fixed_vector(2, NewT); + GetIdOp = IsFloat ? SPIRV::GET_vfID : SPIRV::GET_vID; + DstClass = IsFloat ? &SPIRV::vfIDRegClass : &SPIRV::vIDRegClass; + } + Register IdReg = MRI.createGenericVirtualRegister(NewT); + MRI.setRegClass(IdReg, DstClass); + return {IdReg, GetIdOp}; +} + +static void processInstr(MachineInstr &MI, MachineIRBuilder &MIB, + MachineRegisterInfo &MRI, SPIRVGlobalRegistry *GR) { + unsigned Opc = MI.getOpcode(); + assert(MI.getNumDefs() > 0 && MRI.hasOneUse(MI.getOperand(0).getReg())); + MachineInstr &AssignTypeInst = + *(MRI.use_instr_begin(MI.getOperand(0).getReg())); + auto NewReg = createNewIdReg(MI.getOperand(0).getReg(), Opc, MRI, *GR).first; + AssignTypeInst.getOperand(1).setReg(NewReg); + MI.getOperand(0).setReg(NewReg); + MIB.setInsertPt(*MI.getParent(), + (MI.getNextNode() ? MI.getNextNode()->getIterator() + : MI.getParent()->end())); + for (auto &Op : MI.operands()) { + if (!Op.isReg() || Op.isDef()) + continue; + auto IdOpInfo = createNewIdReg(Op.getReg(), Opc, MRI, *GR); + MIB.buildInstr(IdOpInfo.second).addDef(IdOpInfo.first).addUse(Op.getReg()); + Op.setReg(IdOpInfo.first); + } +} + +// Defined in SPIRVLegalizerInfo.cpp. +extern bool isTypeFoldingSupported(unsigned Opcode); + +static void processInstrsWithTypeFolding(MachineFunction &MF, + SPIRVGlobalRegistry *GR, + MachineIRBuilder MIB) { + MachineRegisterInfo &MRI = MF.getRegInfo(); + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + if (isTypeFoldingSupported(MI.getOpcode())) + processInstr(MI, MIB, MRI, GR); + } + } +} + +static void processSwitches(MachineFunction &MF, SPIRVGlobalRegistry *GR, + MachineIRBuilder MIB) { + DenseMap> + SwitchRegToMBB; + DenseMap DefaultMBBs; + DenseSet SwitchRegs; + MachineRegisterInfo &MRI = MF.getRegInfo(); + // Before IRTranslator pass, spv_switch calls are inserted before each + // switch instruction. IRTranslator lowers switches to ICMP+CBr+Br triples. + // A switch with two cases may be translated to this MIR sequesnce: + // intrinsic(@llvm.spv.switch), %CmpReg, %Const0, %Const1 + // %Dst0 = G_ICMP intpred(eq), %CmpReg, %Const0 + // G_BRCOND %Dst0, %bb.2 + // G_BR %bb.5 + // bb.5.entry: + // %Dst1 = G_ICMP intpred(eq), %CmpReg, %Const1 + // G_BRCOND %Dst1, %bb.3 + // G_BR %bb.4 + // bb.2.sw.bb: + // ... + // bb.3.sw.bb1: + // ... + // bb.4.sw.epilog: + // ... + // Walk MIs and collect information about destination MBBs to update + // spv_switch call. We assume that all spv_switch precede corresponding ICMPs. + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + if (isSpvIntrinsic(MI, Intrinsic::spv_switch)) { + assert(MI.getOperand(1).isReg()); + Register Reg = MI.getOperand(1).getReg(); + SwitchRegs.insert(Reg); + // Set the first successor as default MBB to support empty switches. + DefaultMBBs[Reg] = *MBB.succ_begin(); + } + // Process only ICMPs that relate to spv_switches. + if (MI.getOpcode() == TargetOpcode::G_ICMP && MI.getOperand(2).isReg() && + SwitchRegs.contains(MI.getOperand(2).getReg())) { + assert(MI.getOperand(0).isReg() && MI.getOperand(1).isPredicate() && + MI.getOperand(3).isReg()); + Register Dst = MI.getOperand(0).getReg(); + // Set type info for destination register of switch's ICMP instruction. + if (GR->getSPIRVTypeForVReg(Dst) == nullptr) { + MIB.setInsertPt(*MI.getParent(), MI); + Type *LLVMTy = IntegerType::get(MF.getFunction().getContext(), 1); + SPIRVType *SpirvTy = GR->getOrCreateSPIRVType(LLVMTy, MIB); + MRI.setRegClass(Dst, &SPIRV::IDRegClass); + GR->assignSPIRVTypeToVReg(SpirvTy, Dst, MIB.getMF()); + } + Register CmpReg = MI.getOperand(2).getReg(); + MachineOperand &PredOp = MI.getOperand(1); + const auto CC = static_cast(PredOp.getPredicate()); + assert(CC == CmpInst::ICMP_EQ && MRI.hasOneUse(Dst) && + MRI.hasOneDef(CmpReg)); + uint64_t Val = getIConstVal(MI.getOperand(3).getReg(), &MRI); + MachineInstr *CBr = MRI.use_begin(Dst)->getParent(); + assert(CBr->getOpcode() == SPIRV::G_BRCOND && + CBr->getOperand(1).isMBB()); + SwitchRegToMBB[CmpReg][Val] = CBr->getOperand(1).getMBB(); + // The next MI is always BR to either the next case or the default. + MachineInstr *NextMI = CBr->getNextNode(); + assert(NextMI->getOpcode() == SPIRV::G_BR && + NextMI->getOperand(0).isMBB()); + MachineBasicBlock *NextMBB = NextMI->getOperand(0).getMBB(); + assert(NextMBB != nullptr); + // The default MBB is not started by ICMP with switch's cmp register. + if (NextMBB->front().getOpcode() != SPIRV::G_ICMP || + (NextMBB->front().getOperand(2).isReg() && + NextMBB->front().getOperand(2).getReg() != CmpReg)) + DefaultMBBs[CmpReg] = NextMBB; + } + } + } + // Modify spv_switch's operands by collected values. For the example above, + // the result will be like this: + // intrinsic(@llvm.spv.switch), %CmpReg, %bb.4, i32 0, %bb.2, i32 1, %bb.3 + // Note that ICMP+CBr+Br sequences are not removed, but ModuleAnalysis marks + // them as skipped and AsmPrinter does not output them. + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + if (!isSpvIntrinsic(MI, Intrinsic::spv_switch)) + continue; + assert(MI.getOperand(1).isReg()); + Register Reg = MI.getOperand(1).getReg(); + unsigned NumOp = MI.getNumExplicitOperands(); + SmallVector Vals; + SmallVector MBBs; + for (unsigned i = 2; i < NumOp; i++) { + Register CReg = MI.getOperand(i).getReg(); + uint64_t Val = getIConstVal(CReg, &MRI); + MachineInstr *ConstInstr = getDefInstrMaybeConstant(CReg, &MRI); + Vals.push_back(ConstInstr->getOperand(1).getCImm()); + MBBs.push_back(SwitchRegToMBB[Reg][Val]); + } + for (unsigned i = MI.getNumExplicitOperands() - 1; i > 1; i--) + MI.removeOperand(i); + MI.addOperand(MachineOperand::CreateMBB(DefaultMBBs[Reg])); + for (unsigned i = 0; i < Vals.size(); i++) { + MI.addOperand(MachineOperand::CreateCImm(Vals[i])); + MI.addOperand(MachineOperand::CreateMBB(MBBs[i])); + } + } + } +} + +bool SPIRVPreLegalizer::runOnMachineFunction(MachineFunction &MF) { + // Initialize the type registry. + const SPIRVSubtarget &ST = MF.getSubtarget(); + SPIRVGlobalRegistry *GR = ST.getSPIRVGlobalRegistry(); + GR->setCurrentFunc(MF); + MachineIRBuilder MIB(MF); + foldConstantsIntoIntrinsics(MF); + insertBitcasts(MF, GR, MIB); + generateAssignInstrs(MF, GR, MIB); + processInstrsWithTypeFolding(MF, GR, MIB); + processSwitches(MF, GR, MIB); + + return true; +} + +INITIALIZE_PASS(SPIRVPreLegalizer, DEBUG_TYPE, "SPIRV pre legalizer", false, + false) + +char SPIRVPreLegalizer::ID = 0; + +FunctionPass *llvm::createSPIRVPreLegalizerPass() { + return new SPIRVPreLegalizer(); +} diff --git a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp --- a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp @@ -44,14 +44,12 @@ } static std::string computeDataLayout(const Triple &TT) { - std::string DataLayout = "e-m:e"; - const auto Arch = TT.getArch(); if (Arch == Triple::spirv32) - DataLayout += "-p:32:32"; - else if (Arch == Triple::spirv64) - DataLayout += "-p:64:64"; - return DataLayout; + return "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-" + "v96:128-v192:256-v256:256-v512:512-v1024:1024"; + return "e-i64:64-v16:16-v24:32-v32:32-v48:64-" + "v96:128-v192:256-v256:256-v512:512-v1024:1024"; } static Reloc::Model getEffectiveRelocModel(Optional RM) { @@ -95,6 +93,7 @@ void addISelPrepare() override; bool addIRTranslator() override; + void addPreLegalizeMachineIR() override; bool addLegalizeMachineIR() override; bool addRegBankSelect() override; bool addGlobalInstructionSelect() override; @@ -143,13 +142,20 @@ void SPIRVPassConfig::addIRPasses() { TargetPassConfig::addIRPasses(); } -void SPIRVPassConfig::addISelPrepare() { TargetPassConfig::addISelPrepare(); } +void SPIRVPassConfig::addISelPrepare() { + addPass(createSPIRVEmitIntrinsicsPass(&getTM())); + TargetPassConfig::addISelPrepare(); +} bool SPIRVPassConfig::addIRTranslator() { addPass(new IRTranslator(getOptLevel())); return false; } +void SPIRVPassConfig::addPreLegalizeMachineIR() { + addPass(createSPIRVPreLegalizerPass()); +} + // Use a default legalizer. bool SPIRVPassConfig::addLegalizeMachineIR() { addPass(new Legalizer()); diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.h b/llvm/lib/Target/SPIRV/SPIRVUtils.h --- a/llvm/lib/Target/SPIRV/SPIRVUtils.h +++ b/llvm/lib/Target/SPIRV/SPIRVUtils.h @@ -66,4 +66,18 @@ llvm::SPIRV::MemorySemantics getMemSemanticsForStorageClass(llvm::SPIRV::StorageClass SC); + +// Find def instruction for the given ConstReg, walking through +// spv_track_constant and ASSIGN_TYPE instructions. Updates ConstReg by def +// of OpConstant instruction. +llvm::MachineInstr * +getDefInstrMaybeConstant(llvm::Register &ConstReg, + const llvm::MachineRegisterInfo *MRI); + +// Get constant integer value of the given ConstReg. +uint64_t getIConstVal(llvm::Register ConstReg, + const llvm::MachineRegisterInfo *MRI); + +// Get type of i-th operand of the metadata node. +llvm::Type *getMDOperandAsType(const llvm::MDNode *N, unsigned I); #endif // LLVM_LIB_TARGET_SPIRV_SPIRVUTILS_H diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp --- a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp @@ -18,6 +18,7 @@ #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/IR/IntrinsicsSPIRV.h" using namespace llvm; @@ -180,3 +181,27 @@ return SPIRV::MemorySemantics::None; } } + +MachineInstr *getDefInstrMaybeConstant(Register &ConstReg, + const MachineRegisterInfo *MRI) { + MachineInstr *ConstInstr = MRI->getVRegDef(ConstReg); + if (ConstInstr->getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS && + ConstInstr->getIntrinsicID() == Intrinsic::spv_track_constant) { + ConstReg = ConstInstr->getOperand(2).getReg(); + ConstInstr = MRI->getVRegDef(ConstReg); + } else if (ConstInstr->getOpcode() == SPIRV::ASSIGN_TYPE) { + ConstReg = ConstInstr->getOperand(1).getReg(); + ConstInstr = MRI->getVRegDef(ConstReg); + } + return ConstInstr; +} + +uint64_t getIConstVal(Register ConstReg, const MachineRegisterInfo *MRI) { + const MachineInstr *MI = getDefInstrMaybeConstant(ConstReg, MRI); + assert(MI && MI->getOpcode() == TargetOpcode::G_CONSTANT); + return MI->getOperand(1).getCImm()->getValue().getZExtValue(); +} + +Type *getMDOperandAsType(const MDNode *N, unsigned I) { + return cast(N->getOperand(I))->getType(); +} diff --git a/llvm/test/CodeGen/SPIRV/branching/if-merging.ll b/llvm/test/CodeGen/SPIRV/branching/if-merging.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/branching/if-merging.ll @@ -0,0 +1,53 @@ +; RUN: llc -O0 %s -o - | FileCheck %s + +; NOTE: This does not check for structured control-flow operations. + +target triple = "spirv32-unknown-unknown" + +; CHECK-DAG: OpName [[FOO:%.+]] "foo" +; CHECK-DAG: OpName [[BAR:%.+]] "bar" + +; CHECK-DAG: [[I32:%.+]] = OpTypeInt 32 +; CHECK-DAG: [[BOOL:%.+]] = OpTypeBool + +declare i32 @foo() +declare i32 @bar() + +define i32 @test_if(i32 %a, i32 %b) { +entry: + %cond = icmp eq i32 %a, %b + br i1 %cond, label %true_label, label %false_label + +true_label: + %v1 = call i32 @foo() + br label %merge_label + +false_label: + %v2 = call i32 @bar() + br label %merge_label + +merge_label: + %v = phi i32 [%v1, %true_label], [%v2, %false_label] + ret i32 %v +} + +; CHECK: OpFunction +; CHECK: [[A:%.+]] = OpFunctionParameter [[I32]] +; CHECK: [[B:%.+]] = OpFunctionParameter [[I32]] + +; CHECK: [[ENTRY:%.+]] = OpLabel +; CHECK: [[COND:%.+]] = OpIEqual [[BOOL]] [[A]] [[B]] +; CHECK: OpBranchConditional [[COND]] [[TRUE_LABEL:%.+]] [[FALSE_LABEL:%.+]] + +; CHECK: [[TRUE_LABEL]] = OpLabel +; CHECK: [[V1:%.+]] = OpFunctionCall [[I32]] [[FOO]] +; CHECK: OpBranch [[MERGE_LABEL:%.+]] + +; CHECK: [[FALSE_LABEL]] = OpLabel +; CHECK: [[V2:%.+]] = OpFunctionCall [[I32]] [[BAR]] +; CHECK: OpBranch [[MERGE_LABEL]] + +; CHECK: [[MERGE_LABEL]] = OpLabel +; CHECK-NEXT: [[V:%.+]] = OpPhi [[I32]] [[V1]] [[TRUE_LABEL]] [[V2]] [[FALSE_LABEL]] +; CHECK: OpReturnValue [[V]] +; CHECK-NEXT: OpFunctionEnd diff --git a/llvm/test/CodeGen/SPIRV/function/alloca-load-store.ll b/llvm/test/CodeGen/SPIRV/function/alloca-load-store.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/function/alloca-load-store.ll @@ -0,0 +1,63 @@ +; RUN: llc -O0 %s -o - | FileCheck %s + +target triple = "spirv32-unknown-unknown" + +; CHECK-DAG: OpName [[BAR:%.+]] "bar" +; CHECK-DAG: OpName [[FOO:%.+]] "foo" +; CHECK-DAG: OpName [[GOO:%.+]] "goo" + +; CHECK: [[INT:%.+]] = OpTypeInt 32 +; CHECK-DAG: [[STACK_PTR:%.+]] = OpTypePointer Function [[INT]] +; CHECK-DAG: [[GLOBAL_PTR:%.+]] = OpTypePointer CrossWorkgroup [[INT]] +; CHECK-DAG: [[FN1:%.+]] = OpTypeFunction [[INT]] [[INT]] +; CHECK-DAG: [[FN2:%.+]] = OpTypeFunction [[INT]] [[INT]] [[GLOBAL_PTR]] + +define i32 @bar(i32 %a) { + %p = alloca i32 + store i32 %a, i32* %p + %b = load i32, i32* %p + ret i32 %b +} + +; CHECK: [[BAR]] = OpFunction [[INT]] None [[FN1]] +; CHECK: [[A:%.+]] = OpFunctionParameter [[INT]] +; CHECK: OpLabel +; CHECK: [[P:%.+]] = OpVariable [[STACK_PTR]] Function +; CHECK: OpStore [[P]] [[A]] +; CHECK: [[B:%.+]] = OpLoad [[INT]] [[P]] +; CHECK: OpReturnValue [[B]] +; CHECK: OpFunctionEnd + + +define i32 @foo(i32 %a) { + %p = alloca i32 + store volatile i32 %a, i32* %p + %b = load volatile i32, i32* %p + ret i32 %b +} + +; CHECK: [[FOO]] = OpFunction [[INT]] None [[FN1]] +; CHECK: [[A:%.+]] = OpFunctionParameter [[INT]] +; CHECK: OpLabel +; CHECK: [[P:%.+]] = OpVariable [[STACK_PTR]] Function +; CHECK: OpStore [[P]] [[A]] Volatile +; CHECK: [[B:%.+]] = OpLoad [[INT]] [[P]] Volatile +; CHECK: OpReturnValue [[B]] +; CHECK: OpFunctionEnd + + +; Test load and store in global address space. +define i32 @goo(i32 %a, i32 addrspace(1)* %p) { + store i32 %a, i32 addrspace(1)* %p + %b = load i32, i32 addrspace(1)* %p + ret i32 %b +} + +; CHECK: [[GOO]] = OpFunction [[INT]] None [[FN2]] +; CHECK: [[A:%.+]] = OpFunctionParameter [[INT]] +; CHECK: [[P:%.+]] = OpFunctionParameter [[GLOBAL_PTR]] +; CHECK: OpLabel +; CHECK: OpStore [[P]] [[A]] +; CHECK: [[B:%.+]] = OpLoad [[INT]] [[P]] +; CHECK: OpReturnValue [[B]] +; CHECK: OpFunctionEnd diff --git a/llvm/test/CodeGen/SPIRV/function/trivial-function-with-attributes.ll b/llvm/test/CodeGen/SPIRV/function/trivial-function-with-attributes.ll --- a/llvm/test/CodeGen/SPIRV/function/trivial-function-with-attributes.ll +++ b/llvm/test/CodeGen/SPIRV/function/trivial-function-with-attributes.ll @@ -13,8 +13,6 @@ ; CHECK-DAG: OpName [[FN6:%.+]] "fn6" ; CHECK-DAG: OpName [[FN7:%.+]] "fn7" -; CHECK-NOT: DAG-FENCE - ; Types: ; CHECK: [[VOID:%.+]] = OpTypeVoid ; CHECK: [[FN:%.+]] = OpTypeFunction [[VOID]] diff --git a/llvm/test/CodeGen/SPIRV/instructions/atomic.ll b/llvm/test/CodeGen/SPIRV/instructions/atomic.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/instructions/atomic.ll @@ -0,0 +1,127 @@ +; RUN: llc %s -o - | FileCheck %s + +target triple = "spirv32-unknown-unknown" + +; CHECK-DAG: OpName [[ADD:%.*]] "test_add" +; CHECK-DAG: OpName [[SUB:%.*]] "test_sub" +; CHECK-DAG: OpName [[MIN:%.*]] "test_min" +; CHECK-DAG: OpName [[MAX:%.*]] "test_max" +; CHECK-DAG: OpName [[UMIN:%.*]] "test_umin" +; CHECK-DAG: OpName [[UMAX:%.*]] "test_umax" +; CHECK-DAG: OpName [[AND:%.*]] "test_and" +; CHECK-DAG: OpName [[OR:%.*]] "test_or" +; CHECK-DAG: OpName [[XOR:%.*]] "test_xor" + +; CHECK-DAG: [[I32Ty:%.*]] = OpTypeInt 32 0 +; Device scope is encoded with constant 1 +; CHECK-DAG: [[SCOPE:%.*]] = OpConstant [[I32Ty]] 1 +; "monotonic" maps to the relaxed memory semantics, encoded with constant 0 +; CHECK-DAG: [[RELAXED:%.*]] = OpConstantNull [[I32Ty]] + +; CHECK: [[ADD]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicIAdd [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_add(i32* %ptr, i32 %val) { + %r = atomicrmw add i32* %ptr, i32 %val monotonic + ret i32 %r +} + +; CHECK: [[SUB]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicISub [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_sub(i32* %ptr, i32 %val) { + %r = atomicrmw sub i32* %ptr, i32 %val monotonic + ret i32 %r +} + +; CHECK: [[MIN]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicSMin [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_min(i32* %ptr, i32 %val) { + %r = atomicrmw min i32* %ptr, i32 %val monotonic + ret i32 %r +} + +; CHECK: [[MAX]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicSMax [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_max(i32* %ptr, i32 %val) { + %r = atomicrmw max i32* %ptr, i32 %val monotonic + ret i32 %r +} + +; CHECK: [[UMIN]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicUMin [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_umin(i32* %ptr, i32 %val) { + %r = atomicrmw umin i32* %ptr, i32 %val monotonic + ret i32 %r +} + +; CHECK: [[UMAX]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicUMax [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_umax(i32* %ptr, i32 %val) { + %r = atomicrmw umax i32* %ptr, i32 %val monotonic + ret i32 %r +} + +; CHECK: [[AND]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicAnd [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_and(i32* %ptr, i32 %val) { + %r = atomicrmw and i32* %ptr, i32 %val monotonic + ret i32 %r +} + +; CHECK: [[OR]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicOr [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_or(i32* %ptr, i32 %val) { + %r = atomicrmw or i32* %ptr, i32 %val monotonic + ret i32 %r +} + +; CHECK: [[XOR]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicXor [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_xor(i32* %ptr, i32 %val) { + %r = atomicrmw xor i32* %ptr, i32 %val monotonic + ret i32 %r +} diff --git a/llvm/test/CodeGen/SPIRV/instructions/atomic_acqrel.ll b/llvm/test/CodeGen/SPIRV/instructions/atomic_acqrel.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/instructions/atomic_acqrel.ll @@ -0,0 +1,127 @@ +; RUN: llc %s -o - | FileCheck %s + +target triple = "spirv32-unknown-unknown" + +; CHECK-DAG: OpName [[ADD:%.*]] "test_add" +; CHECK-DAG: OpName [[SUB:%.*]] "test_sub" +; CHECK-DAG: OpName [[MIN:%.*]] "test_min" +; CHECK-DAG: OpName [[MAX:%.*]] "test_max" +; CHECK-DAG: OpName [[UMIN:%.*]] "test_umin" +; CHECK-DAG: OpName [[UMAX:%.*]] "test_umax" +; CHECK-DAG: OpName [[AND:%.*]] "test_and" +; CHECK-DAG: OpName [[OR:%.*]] "test_or" +; CHECK-DAG: OpName [[XOR:%.*]] "test_xor" + +; CHECK-DAG: [[I32Ty:%.*]] = OpTypeInt 32 0 +; Device scope is encoded with constant 1 +; CHECK-DAG: [[SCOPE:%.*]] = OpConstant [[I32Ty]] 1 +; "acq_rel" maps to the constant 8 +; CHECK-DAG: [[ACQREL:%.*]] = OpConstant [[I32Ty]] 8 + +; CHECK: [[ADD]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicIAdd [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_add(i32* %ptr, i32 %val) { + %r = atomicrmw add i32* %ptr, i32 %val acq_rel + ret i32 %r +} + +; CHECK: [[SUB]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicISub [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_sub(i32* %ptr, i32 %val) { + %r = atomicrmw sub i32* %ptr, i32 %val acq_rel + ret i32 %r +} + +; CHECK: [[MIN]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicSMin [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_min(i32* %ptr, i32 %val) { + %r = atomicrmw min i32* %ptr, i32 %val acq_rel + ret i32 %r +} + +; CHECK: [[MAX]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicSMax [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_max(i32* %ptr, i32 %val) { + %r = atomicrmw max i32* %ptr, i32 %val acq_rel + ret i32 %r +} + +; CHECK: [[UMIN]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicUMin [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_umin(i32* %ptr, i32 %val) { + %r = atomicrmw umin i32* %ptr, i32 %val acq_rel + ret i32 %r +} + +; CHECK: [[UMAX]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicUMax [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_umax(i32* %ptr, i32 %val) { + %r = atomicrmw umax i32* %ptr, i32 %val acq_rel + ret i32 %r +} + +; CHECK: [[AND]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicAnd [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_and(i32* %ptr, i32 %val) { + %r = atomicrmw and i32* %ptr, i32 %val acq_rel + ret i32 %r +} + +; CHECK: [[OR]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicOr [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_or(i32* %ptr, i32 %val) { + %r = atomicrmw or i32* %ptr, i32 %val acq_rel + ret i32 %r +} + +; CHECK: [[XOR]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicXor [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_xor(i32* %ptr, i32 %val) { + %r = atomicrmw xor i32* %ptr, i32 %val acq_rel + ret i32 %r +} diff --git a/llvm/test/CodeGen/SPIRV/instructions/atomic_seq.ll b/llvm/test/CodeGen/SPIRV/instructions/atomic_seq.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/instructions/atomic_seq.ll @@ -0,0 +1,127 @@ +; RUN: llc %s -o - | FileCheck %s + +target triple = "spirv32-unknown-unknown" + +; CHECK-DAG: OpName [[ADD:%.*]] "test_add" +; CHECK-DAG: OpName [[SUB:%.*]] "test_sub" +; CHECK-DAG: OpName [[MIN:%.*]] "test_min" +; CHECK-DAG: OpName [[MAX:%.*]] "test_max" +; CHECK-DAG: OpName [[UMIN:%.*]] "test_umin" +; CHECK-DAG: OpName [[UMAX:%.*]] "test_umax" +; CHECK-DAG: OpName [[AND:%.*]] "test_and" +; CHECK-DAG: OpName [[OR:%.*]] "test_or" +; CHECK-DAG: OpName [[XOR:%.*]] "test_xor" + +; CHECK-DAG: [[I32Ty:%.*]] = OpTypeInt 32 0 +; Device scope is encoded with constant 1 +; CHECK-DAG: [[SCOPE:%.*]] = OpConstant [[I32Ty]] 1 +; "sequentially consistent" maps to constant 16 +; CHECK-DAG: [[SEQ:%.*]] = OpConstant [[I32Ty]] 16 + +; CHECK: [[ADD]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicIAdd [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_add(i32* %ptr, i32 %val) { + %r = atomicrmw add i32* %ptr, i32 %val seq_cst + ret i32 %r +} + +; CHECK: [[SUB]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicISub [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_sub(i32* %ptr, i32 %val) { + %r = atomicrmw sub i32* %ptr, i32 %val seq_cst + ret i32 %r +} + +; CHECK: [[MIN]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicSMin [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_min(i32* %ptr, i32 %val) { + %r = atomicrmw min i32* %ptr, i32 %val seq_cst + ret i32 %r +} + +; CHECK: [[MAX]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicSMax [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_max(i32* %ptr, i32 %val) { + %r = atomicrmw max i32* %ptr, i32 %val seq_cst + ret i32 %r +} + +; CHECK: [[UMIN]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicUMin [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_umin(i32* %ptr, i32 %val) { + %r = atomicrmw umin i32* %ptr, i32 %val seq_cst + ret i32 %r +} + +; CHECK: [[UMAX]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicUMax [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_umax(i32* %ptr, i32 %val) { + %r = atomicrmw umax i32* %ptr, i32 %val seq_cst + ret i32 %r +} + +; CHECK: [[AND]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicAnd [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_and(i32* %ptr, i32 %val) { + %r = atomicrmw and i32* %ptr, i32 %val seq_cst + ret i32 %r +} + +; CHECK: [[OR]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicOr [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_or(i32* %ptr, i32 %val) { + %r = atomicrmw or i32* %ptr, i32 %val seq_cst + ret i32 %r +} + +; CHECK: [[XOR]] = OpFunction [[I32Ty]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpAtomicXor [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @test_xor(i32* %ptr, i32 %val) { + %r = atomicrmw xor i32* %ptr, i32 %val seq_cst + ret i32 %r +} diff --git a/llvm/test/CodeGen/SPIRV/instructions/fcmp.ll b/llvm/test/CodeGen/SPIRV/instructions/fcmp.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/instructions/fcmp.ll @@ -0,0 +1,369 @@ +; RUN: llc %s -o - | FileCheck %s + +target triple = "spirv32-unknown-unknown" + +; CHECK-DAG: OpName [[UEQ:%.*]] "test_ueq" +; CHECK-DAG: OpName [[OEQ:%.*]] "test_oeq" +; CHECK-DAG: OpName [[UNE:%.*]] "test_une" +; CHECK-DAG: OpName [[ONE:%.*]] "test_one" +; CHECK-DAG: OpName [[ULT:%.*]] "test_ult" +; CHECK-DAG: OpName [[OLT:%.*]] "test_olt" +; CHECK-DAG: OpName [[ULE:%.*]] "test_ule" +; CHECK-DAG: OpName [[OLE:%.*]] "test_ole" +; CHECK-DAG: OpName [[UGT:%.*]] "test_ugt" +; CHECK-DAG: OpName [[OGT:%.*]] "test_ogt" +; CHECK-DAG: OpName [[UGE:%.*]] "test_uge" +; CHECK-DAG: OpName [[OGE:%.*]] "test_oge" +; CHECK-DAG: OpName [[UNO:%.*]] "test_uno" +; CHECK-DAG: OpName [[ORD:%.*]] "test_ord" + +; CHECK-DAG: OpName [[v3UEQ:%.*]] "test_v3_ueq" +; CHECK-DAG: OpName [[v3OEQ:%.*]] "test_v3_oeq" +; CHECK-DAG: OpName [[v3UNE:%.*]] "test_v3_une" +; CHECK-DAG: OpName [[v3ONE:%.*]] "test_v3_one" +; CHECK-DAG: OpName [[v3ULT:%.*]] "test_v3_ult" +; CHECK-DAG: OpName [[v3OLT:%.*]] "test_v3_olt" +; CHECK-DAG: OpName [[v3ULE:%.*]] "test_v3_ule" +; CHECK-DAG: OpName [[v3OLE:%.*]] "test_v3_ole" +; CHECK-DAG: OpName [[v3UGT:%.*]] "test_v3_ugt" +; CHECK-DAG: OpName [[v3OGT:%.*]] "test_v3_ogt" +; CHECK-DAG: OpName [[v3UGE:%.*]] "test_v3_uge" +; CHECK-DAG: OpName [[v3OGE:%.*]] "test_v3_oge" +; CHECK-DAG: OpName [[v3UNO:%.*]] "test_v3_uno" +; CHECK-DAG: OpName [[v3ORD:%.*]] "test_v3_ord" + +; CHECK: [[UEQ]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFUnordEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_ueq(float %a, float %b) { + %r = fcmp ueq float %a, %b + ret i1 %r +} + +; CHECK: [[OEQ]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFOrdEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_oeq(float %a, float %b) { + %r = fcmp oeq float %a, %b + ret i1 %r +} + +; CHECK: [[UNE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFUnordNotEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_une(float %a, float %b) { + %r = fcmp une float %a, %b + ret i1 %r +} + +; CHECK: [[ONE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFOrdNotEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_one(float %a, float %b) { + %r = fcmp one float %a, %b + ret i1 %r +} + +; CHECK: [[ULT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFUnordLessThan {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_ult(float %a, float %b) { + %r = fcmp ult float %a, %b + ret i1 %r +} + +; CHECK: [[OLT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFOrdLessThan {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_olt(float %a, float %b) { + %r = fcmp olt float %a, %b + ret i1 %r +} + +; CHECK: [[ULE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFUnordLessThanEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_ule(float %a, float %b) { + %r = fcmp ule float %a, %b + ret i1 %r +} + +; CHECK: [[OLE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFOrdLessThanEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_ole(float %a, float %b) { + %r = fcmp ole float %a, %b + ret i1 %r +} + +; CHECK: [[UGT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFUnordGreaterThan {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_ugt(float %a, float %b) { + %r = fcmp ugt float %a, %b + ret i1 %r +} + +; CHECK: [[OGT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFOrdGreaterThan {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_ogt(float %a, float %b) { + %r = fcmp ogt float %a, %b + ret i1 %r +} + +; CHECK: [[UGE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFUnordGreaterThanEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_uge(float %a, float %b) { + %r = fcmp uge float %a, %b + ret i1 %r +} + +; CHECK: [[OGE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFOrdGreaterThanEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_oge(float %a, float %b) { + %r = fcmp oge float %a, %b + ret i1 %r +} + +; CHECK: [[ORD]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpOrdered {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_ord(float %a, float %b) { + %r = fcmp ord float %a, %b + ret i1 %r +} + +; CHECK: [[UNO]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpUnordered {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_uno(float %a, float %b) { + %r = fcmp uno float %a, %b + ret i1 %r +} + +; CHECK: [[v3UEQ]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFUnordEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_ueq(<3 x float> %a, <3 x float> %b) { + %r = fcmp ueq <3 x float> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3OEQ]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFOrdEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_oeq(<3 x float> %a, <3 x float> %b) { + %r = fcmp oeq <3 x float> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3UNE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFUnordNotEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_une(<3 x float> %a, <3 x float> %b) { + %r = fcmp une <3 x float> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3ONE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFOrdNotEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_one(<3 x float> %a, <3 x float> %b) { + %r = fcmp one <3 x float> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3ULT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFUnordLessThan {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_ult(<3 x float> %a, <3 x float> %b) { + %r = fcmp ult <3 x float> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3OLT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFOrdLessThan {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_olt(<3 x float> %a, <3 x float> %b) { + %r = fcmp olt <3 x float> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3ULE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFUnordLessThanEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_ule(<3 x float> %a, <3 x float> %b) { + %r = fcmp ule <3 x float> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3OLE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFOrdLessThanEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_ole(<3 x float> %a, <3 x float> %b) { + %r = fcmp ole <3 x float> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3UGT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFUnordGreaterThan {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_ugt(<3 x float> %a, <3 x float> %b) { + %r = fcmp ugt <3 x float> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3OGT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFOrdGreaterThan {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_ogt(<3 x float> %a, <3 x float> %b) { + %r = fcmp ogt <3 x float> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3UGE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFUnordGreaterThanEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_uge(<3 x float> %a, <3 x float> %b) { + %r = fcmp uge <3 x float> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3OGE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpFOrdGreaterThanEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_oge(<3 x float> %a, <3 x float> %b) { + %r = fcmp oge <3 x float> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3ORD]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpOrdered {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_ord(<3 x float> %a, <3 x float> %b) { + %r = fcmp ord <3 x float> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3UNO]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpUnordered {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_uno(<3 x float> %a, <3 x float> %b) { + %r = fcmp uno <3 x float> %a, %b + ret <3 x i1> %r +} diff --git a/llvm/test/CodeGen/SPIRV/instructions/float-casts.ll b/llvm/test/CodeGen/SPIRV/instructions/float-casts.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/instructions/float-casts.ll @@ -0,0 +1,213 @@ +; RUN: llc %s -o - | FileCheck %s + +target triple = "spirv32-unknown-unknown" + +; CHECK-DAG: OpName [[TRUNC32_16:%.*]] "f32tof16" +; CHECK-DAG: OpName [[EXT16_32:%.*]] "f16tof32" + +; CHECK-DAG: OpName [[TRUNC32_16v3:%.*]] "f32tof16v3" +; CHECK-DAG: OpName [[EXT16_32v3:%.*]] "f16tof32v3" + +; CHECK-DAG: OpName [[F32toS32:%.*]] "f32tos32" +; CHECK-DAG: OpName [[F32toS16:%.*]] "f32tos16" +; CHECK-DAG: OpName [[F32toS8:%.*]] "f32tos8" +; CHECK-DAG: OpName [[F16toS32:%.*]] "f16tos32" +; CHECK-DAG: OpName [[F16toS16:%.*]] "f16tos16" +; CHECK-DAG: OpName [[F16toS8:%.*]] "f16tos8" + +; CHECK-DAG: OpName [[F32toU32v2:%.*]] "f32tou32v2" +; CHECK-DAG: OpName [[F32toU16v2:%.*]] "f32tou16v2" +; CHECK-DAG: OpName [[F32toU8v2:%.*]] "f32tou8v2" +; CHECK-DAG: OpName [[F16toU32v2:%.*]] "f16tou32v2" +; CHECK-DAG: OpName [[F16toU16v2:%.*]] "f16tou16v2" +; CHECK-DAG: OpName [[F16toU8v2:%.*]] "f16tou8v2" + +; CHECK-DAG: [[F32:%.*]] = OpTypeFloat 32 +; CHECK-DAG: [[F16:%.*]] = OpTypeFloat 16 +; CHECK-DAG: [[F32v2:%.*]] = OpTypeVector [[F32]] 2 +; CHECK-DAG: [[F16v2:%.*]] = OpTypeVector [[F16]] 2 +; CHECK-DAG: [[F32v3:%.*]] = OpTypeVector [[F32]] 3 +; CHECK-DAG: [[F16v3:%.*]] = OpTypeVector [[F16]] 3 +; CHECK-DAG: [[U32:%.*]] = OpTypeInt 32 0 +; CHECK-DAG: [[U16:%.*]] = OpTypeInt 16 0 +; CHECK-DAG: [[U8:%.*]] = OpTypeInt 8 0 +; CHECK-DAG: [[U32v2:%.*]] = OpTypeVector [[U32]] 2 +; CHECK-DAG: [[U16v2:%.*]] = OpTypeVector [[U16]] 2 +; CHECK-DAG: [[U8v2:%.*]] = OpTypeVector [[U8]] 2 + + +; CHECK: [[TRUNC32_16]] = OpFunction [[F16]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F32]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpFConvert [[F16]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define half @f32tof16(float %a) { + %r = fptrunc float %a to half + ret half %r +} + +; CHECK: [[EXT16_32]] = OpFunction [[F32]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F16]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpFConvert [[F32]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define float @f16tof32(half %a) { + %r = fpext half %a to float + ret float %r +} + +; CHECK: [[TRUNC32_16v3]] = OpFunction [[F16v3]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F32v3]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpFConvert [[F16v3]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x half> @f32tof16v3(<3 x float> %a) { + %r = fptrunc <3 x float> %a to <3 x half> + ret <3 x half> %r +} + +; CHECK: [[EXT16_32v3]] = OpFunction [[F32v3]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F16v3]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpFConvert [[F32v3]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x float> @f16tof32v3(<3 x half> %a) { + %r = fpext <3 x half> %a to <3 x float> + ret <3 x float> %r +} + +; CHECK: [[F32toS32]] = OpFunction [[U32]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F32]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpConvertFToS [[U32]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @f32tos32(float %a) { + %r = fptosi float %a to i32 + ret i32 %r +} + +; CHECK: [[F32toS16]] = OpFunction [[U16]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F32]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpConvertFToS [[U16]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i16 @f32tos16(float %a) { + %r = fptosi float %a to i16 + ret i16 %r +} + +; CHECK: [[F32toS8]] = OpFunction [[U8]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F32]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpConvertFToS [[U8]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i8 @f32tos8(float %a) { + %r = fptosi float %a to i8 + ret i8 %r +} + +; CHECK: [[F16toS32]] = OpFunction [[U32]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F16]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpConvertFToS [[U32]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @f16tos32(half %a) { + %r = fptosi half %a to i32 + ret i32 %r +} + +; CHECK: [[F16toS16]] = OpFunction [[U16]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F16]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpConvertFToS [[U16]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i16 @f16tos16(half %a) { + %r = fptosi half %a to i16 + ret i16 %r +} + +; CHECK: [[F16toS8]] = OpFunction [[U8]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F16]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpConvertFToS [[U8]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i8 @f16tos8(half %a) { + %r = fptosi half %a to i8 + ret i8 %r +} + +; CHECK: [[F32toU32v2]] = OpFunction [[U32v2]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F32v2]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpConvertFToU [[U32v2]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <2 x i32> @f32tou32v2(<2 x float> %a) { + %r = fptoui <2 x float> %a to <2 x i32> + ret <2 x i32> %r +} + +; CHECK: [[F32toU16v2]] = OpFunction [[U16v2]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F32v2]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpConvertFToU [[U16v2]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <2 x i16> @f32tou16v2(<2 x float> %a) { + %r = fptoui <2 x float> %a to <2 x i16> + ret <2 x i16> %r +} + +; CHECK: [[F32toU8v2]] = OpFunction [[U8v2]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F32v2]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpConvertFToU [[U8v2]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <2 x i8> @f32tou8v2(<2 x float> %a) { + %r = fptoui <2 x float> %a to <2 x i8> + ret <2 x i8> %r +} + +; CHECK: [[F16toU32v2]] = OpFunction [[U32v2]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F16v2]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpConvertFToU [[U32v2]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <2 x i32> @f16tou32v2(<2 x half> %a) { + %r = fptoui <2 x half> %a to <2 x i32> + ret <2 x i32> %r +} + +; CHECK: [[F16toU16v2]] = OpFunction [[U16v2]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F16v2]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpConvertFToU [[U16v2]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <2 x i16> @f16tou16v2(<2 x half> %a) { + %r = fptoui <2 x half> %a to <2 x i16> + ret <2 x i16> %r +} + +; CHECK: [[F16toU8v2]] = OpFunction [[U8v2]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F16v2]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpConvertFToU [[U8v2]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <2 x i8> @f16tou8v2(<2 x half> %a) { + %r = fptoui <2 x half> %a to <2 x i8> + ret <2 x i8> %r +} diff --git a/llvm/test/CodeGen/SPIRV/instructions/icmp.ll b/llvm/test/CodeGen/SPIRV/instructions/icmp.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/instructions/icmp.ll @@ -0,0 +1,265 @@ +; RUN: llc %s -o - | FileCheck %s + +target triple = "spirv32-unknown-unknown" + +; CHECK-DAG: OpName [[EQ:%.*]] "test_eq" +; CHECK-DAG: OpName [[NE:%.*]] "test_ne" +; CHECK-DAG: OpName [[ULT:%.*]] "test_ult" +; CHECK-DAG: OpName [[SLT:%.*]] "test_slt" +; CHECK-DAG: OpName [[ULE:%.*]] "test_ule" +; CHECK-DAG: OpName [[SLE:%.*]] "test_sle" +; CHECK-DAG: OpName [[UGT:%.*]] "test_ugt" +; CHECK-DAG: OpName [[SGT:%.*]] "test_sgt" +; CHECK-DAG: OpName [[UGE:%.*]] "test_uge" +; CHECK-DAG: OpName [[SGE:%.*]] "test_sge" + +; CHECK-DAG: OpName [[v3EQ:%.*]] "test_v3_eq" +; CHECK-DAG: OpName [[v3NE:%.*]] "test_v3_ne" +; CHECK-DAG: OpName [[v3ULT:%.*]] "test_v3_ult" +; CHECK-DAG: OpName [[v3SLT:%.*]] "test_v3_slt" +; CHECK-DAG: OpName [[v3ULE:%.*]] "test_v3_ule" +; CHECK-DAG: OpName [[v3SLE:%.*]] "test_v3_sle" +; CHECK-DAG: OpName [[v3UGT:%.*]] "test_v3_ugt" +; CHECK-DAG: OpName [[v3SGT:%.*]] "test_v3_sgt" +; CHECK-DAG: OpName [[v3UGE:%.*]] "test_v3_uge" +; CHECK-DAG: OpName [[v3SGE:%.*]] "test_v3_sge" + +; CHECK: [[EQ]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpIEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_eq(i32 %a, i32 %b) { + %r = icmp eq i32 %a, %b + ret i1 %r +} + +; CHECK: [[NE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpINotEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_ne(i32 %a, i32 %b) { + %r = icmp ne i32 %a, %b + ret i1 %r +} + +; CHECK: [[SLT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpSLessThan {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_slt(i32 %a, i32 %b) { + %r = icmp slt i32 %a, %b + ret i1 %r +} + +; CHECK: [[ULT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpULessThan {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_ult(i32 %a, i32 %b) { + %r = icmp ult i32 %a, %b + ret i1 %r +} + +; CHECK: [[ULE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpULessThanEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_ule(i32 %a, i32 %b) { + %r = icmp ule i32 %a, %b + ret i1 %r +} + +; CHECK: [[SLE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpSLessThanEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_sle(i32 %a, i32 %b) { + %r = icmp sle i32 %a, %b + ret i1 %r +} + +; CHECK: [[UGT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpUGreaterThan {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_ugt(i32 %a, i32 %b) { + %r = icmp ugt i32 %a, %b + ret i1 %r +} + +; CHECK: [[SGT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpSGreaterThan {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_sgt(i32 %a, i32 %b) { + %r = icmp sgt i32 %a, %b + ret i1 %r +} + +; CHECK: [[UGE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpUGreaterThanEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_uge(i32 %a, i32 %b) { + %r = icmp uge i32 %a, %b + ret i1 %r +} + +; CHECK: [[SGE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpSGreaterThanEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_sge(i32 %a, i32 %b) { + %r = icmp sge i32 %a, %b + ret i1 %r +} + +; CHECK: [[v3EQ]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpIEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_eq(<3 x i32> %a, <3 x i32> %b) { + %r = icmp eq <3 x i32> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3NE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpINotEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_ne(<3 x i32> %a, <3 x i32> %b) { + %r = icmp ne <3 x i32> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3SLT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpSLessThan {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_slt(<3 x i32> %a, <3 x i32> %b) { + %r = icmp slt <3 x i32> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3ULT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpULessThan {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_ult(<3 x i32> %a, <3 x i32> %b) { + %r = icmp ult <3 x i32> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3ULE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpULessThanEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_ule(<3 x i32> %a, <3 x i32> %b) { + %r = icmp ule <3 x i32> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3SLE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpSLessThanEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_sle(<3 x i32> %a, <3 x i32> %b) { + %r = icmp sle <3 x i32> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3UGT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpUGreaterThan {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_ugt(<3 x i32> %a, <3 x i32> %b) { + %r = icmp ugt <3 x i32> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3SGT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpSGreaterThan {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_sgt(<3 x i32> %a, <3 x i32> %b) { + %r = icmp sgt <3 x i32> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3UGE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpUGreaterThanEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_uge(<3 x i32> %a, <3 x i32> %b) { + %r = icmp uge <3 x i32> %a, %b + ret <3 x i1> %r +} + +; CHECK: [[v3SGE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpSGreaterThanEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <3 x i1> @test_v3_sge(<3 x i32> %a, <3 x i32> %b) { + %r = icmp sge <3 x i32> %a, %b + ret <3 x i1> %r +} diff --git a/llvm/test/CodeGen/SPIRV/instructions/integer-casts.ll b/llvm/test/CodeGen/SPIRV/instructions/integer-casts.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/instructions/integer-casts.ll @@ -0,0 +1,231 @@ +; RUN: llc %s -o - | FileCheck %s + +target triple = "spirv32-unknown-unknown" + +; CHECK-DAG: OpName [[TRUNC32_16:%.*]] "i32toi16" +; CHECK-DAG: OpName [[TRUNC32_8:%.*]] "i32toi8" +; CHECK-DAG: OpName [[TRUNC16_8:%.*]] "i16toi8" +; CHECK-DAG: OpName [[SEXT8_32:%.*]] "s8tos32" +; CHECK-DAG: OpName [[SEXT8_16:%.*]] "s8tos16" +; CHECK-DAG: OpName [[SEXT16_32:%.*]] "s16tos32" +; CHECK-DAG: OpName [[ZEXT8_32:%.*]] "u8tou32" +; CHECK-DAG: OpName [[ZEXT8_16:%.*]] "u8tou16" +; CHECK-DAG: OpName [[ZEXT16_32:%.*]] "u16tou32" + +; CHECK-DAG: OpName [[TRUNC32_16v4:%.*]] "i32toi16v4" +; CHECK-DAG: OpName [[TRUNC32_8v4:%.*]] "i32toi8v4" +; CHECK-DAG: OpName [[TRUNC16_8v4:%.*]] "i16toi8v4" +; CHECK-DAG: OpName [[SEXT8_32v4:%.*]] "s8tos32v4" +; CHECK-DAG: OpName [[SEXT8_16v4:%.*]] "s8tos16v4" +; CHECK-DAG: OpName [[SEXT16_32v4:%.*]] "s16tos32v4" +; CHECK-DAG: OpName [[ZEXT8_32v4:%.*]] "u8tou32v4" +; CHECK-DAG: OpName [[ZEXT8_16v4:%.*]] "u8tou16v4" +; CHECK-DAG: OpName [[ZEXT16_32v4:%.*]] "u16tou32v4" + +; CHECK-DAG: [[U32:%.*]] = OpTypeInt 32 0 +; CHECK-DAG: [[U16:%.*]] = OpTypeInt 16 0 +; CHECK-DAG: [[U8:%.*]] = OpTypeInt 8 0 +; CHECK-DAG: [[U32v4:%.*]] = OpTypeVector [[U32]] 4 +; CHECK-DAG: [[U16v4:%.*]] = OpTypeVector [[U16]] 4 +; CHECK-DAG: [[U8v4:%.*]] = OpTypeVector [[U8]] 4 + + +; CHECK: [[TRUNC32_16]] = OpFunction [[U16]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U32]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpUConvert [[U16]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i16 @i32toi16(i32 %a) { + %r = trunc i32 %a to i16 + ret i16 %r +} + +; CHECK: [[TRUNC32_8]] = OpFunction [[U8]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U32]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpUConvert [[U8]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i8 @i32toi8(i32 %a) { + %r = trunc i32 %a to i8 + ret i8 %r +} + +; CHECK: [[TRUNC16_8]] = OpFunction [[U8]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U16]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpUConvert [[U8]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i8 @i16toi8(i16 %a) { + %r = trunc i16 %a to i8 + ret i8 %r +} + + +; CHECK: [[SEXT8_32]] = OpFunction [[U32]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U8]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpSConvert [[U32]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @s8tos32(i8 %a) { + %r = sext i8 %a to i32 + ret i32 %r +} + +; CHECK: [[SEXT8_16]] = OpFunction [[U16]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U8]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpSConvert [[U16]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i16 @s8tos16(i8 %a) { + %r = sext i8 %a to i16 + ret i16 %r +} + +; CHECK: [[SEXT16_32]] = OpFunction [[U32]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U16]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpSConvert [[U32]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @s16tos32(i16 %a) { + %r = sext i16 %a to i32 + ret i32 %r +} + +; CHECK: [[ZEXT8_32]] = OpFunction [[U32]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U8]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpUConvert [[U32]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @u8tou32(i8 %a) { + %r = zext i8 %a to i32 + ret i32 %r +} + +; CHECK: [[ZEXT8_16]] = OpFunction [[U16]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U8]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpUConvert [[U16]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i16 @u8tou16(i8 %a) { + %r = zext i8 %a to i16 + ret i16 %r +} + +; CHECK: [[ZEXT16_32]] = OpFunction [[U32]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U16]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpUConvert [[U32]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i32 @u16tou32(i16 %a) { + %r = zext i16 %a to i32 + ret i32 %r +} + +; CHECK: [[TRUNC32_16v4]] = OpFunction [[U16v4]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U32v4]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpUConvert [[U16v4]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <4 x i16> @i32toi16v4(<4 x i32> %a) { + %r = trunc <4 x i32> %a to <4 x i16> + ret <4 x i16> %r +} + +; CHECK: [[TRUNC32_8v4]] = OpFunction [[U8v4]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U32v4]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpUConvert [[U8v4]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <4 x i8> @i32toi8v4(<4 x i32> %a) { + %r = trunc <4 x i32> %a to <4 x i8> + ret <4 x i8> %r +} + +; CHECK: [[TRUNC16_8v4]] = OpFunction [[U8v4]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U16v4]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpUConvert [[U8v4]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <4 x i8> @i16toi8v4(<4 x i16> %a) { + %r = trunc <4 x i16> %a to <4 x i8> + ret <4 x i8> %r +} + + +; CHECK: [[SEXT8_32v4]] = OpFunction [[U32v4]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U8v4]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpSConvert [[U32v4]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <4 x i32> @s8tos32v4(<4 x i8> %a) { + %r = sext <4 x i8> %a to <4 x i32> + ret <4 x i32> %r +} + +; CHECK: [[SEXT8_16v4]] = OpFunction [[U16v4]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U8v4]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpSConvert [[U16v4]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <4 x i16> @s8tos16v4(<4 x i8> %a) { + %r = sext <4 x i8> %a to <4 x i16> + ret <4 x i16> %r +} + +; CHECK: [[SEXT16_32v4]] = OpFunction [[U32v4]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U16v4]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpSConvert [[U32v4]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <4 x i32> @s16tos32v4(<4 x i16> %a) { + %r = sext <4 x i16> %a to <4 x i32> + ret <4 x i32> %r +} + +; CHECK: [[ZEXT8_32v4]] = OpFunction [[U32v4]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U8v4]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpUConvert [[U32v4]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <4 x i32> @u8tou32v4(<4 x i8> %a) { + %r = zext <4 x i8> %a to <4 x i32> + ret <4 x i32> %r +} + +; CHECK: [[ZEXT8_16v4]] = OpFunction [[U16v4]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U8v4]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpUConvert [[U16v4]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <4 x i16> @u8tou16v4(<4 x i8> %a) { + %r = zext <4 x i8> %a to <4 x i16> + ret <4 x i16> %r +} + +; CHECK: [[ZEXT16_32v4]] = OpFunction [[U32v4]] +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U16v4]] +; CHECK: OpLabel +; CHECK: [[R:%.*]] = OpUConvert [[U32v4]] [[A]] +; CHECK: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define <4 x i32> @u16tou32v4(<4 x i16> %a) { + %r = zext <4 x i16> %a to <4 x i32> + ret <4 x i32> %r +} diff --git a/llvm/test/CodeGen/SPIRV/instructions/ptrcmp.ll b/llvm/test/CodeGen/SPIRV/instructions/ptrcmp.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/instructions/ptrcmp.ll @@ -0,0 +1,151 @@ +; RUN: llc %s -o - | FileCheck %s + +target triple = "spirv32-unknown-unknown" + +; CHECK-DAG: OpName [[EQ:%.*]] "test_eq" +; CHECK-DAG: OpName [[NE:%.*]] "test_ne" +; CHECK-DAG: OpName [[ULT:%.*]] "test_ult" +; CHECK-DAG: OpName [[SLT:%.*]] "test_slt" +; CHECK-DAG: OpName [[ULE:%.*]] "test_ule" +; CHECK-DAG: OpName [[SLE:%.*]] "test_sle" +; CHECK-DAG: OpName [[UGT:%.*]] "test_ugt" +; CHECK-DAG: OpName [[SGT:%.*]] "test_sgt" +; CHECK-DAG: OpName [[UGE:%.*]] "test_uge" +; CHECK-DAG: OpName [[SGE:%.*]] "test_sge" + +; FIXME: Translator uses OpIEqual/OpINotEqual for test_eq/test_ne cases +; CHECK: [[EQ]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpPtrEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_eq(i16* %a, i16* %b) { + %r = icmp eq i16* %a, %b + ret i1 %r +} + +; CHECK: [[NE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[R:%.*]] = OpPtrNotEqual {{%.+}} [[A]] [[B]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_ne(i16* %a, i16* %b) { + %r = icmp ne i16* %a, %b + ret i1 %r +} + +; CHECK: [[SLT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[AI:%.*]] = OpConvertPtrToU {{%.+}} [[A]] +; CHECK-NEXT: [[BI:%.*]] = OpConvertPtrToU {{%.+}} [[B]] +; CHECK: [[R:%.*]] = OpSLessThan {{%.+}} [[AI]] [[BI]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_slt(i16* %a, i16* %b) { + %r = icmp slt i16* %a, %b + ret i1 %r +} + +; CHECK: [[ULT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[AI:%.*]] = OpConvertPtrToU {{%.+}} [[A]] +; CHECK-NEXT: [[BI:%.*]] = OpConvertPtrToU {{%.+}} [[B]] +; CHECK: [[R:%.*]] = OpULessThan {{%.+}} [[AI]] [[BI]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_ult(i16* %a, i16* %b) { + %r = icmp ult i16* %a, %b + ret i1 %r +} + +; CHECK: [[ULE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[AI:%.*]] = OpConvertPtrToU {{%.+}} [[A]] +; CHECK-NEXT: [[BI:%.*]] = OpConvertPtrToU {{%.+}} [[B]] +; CHECK: [[R:%.*]] = OpULessThanEqual {{%.+}} [[AI]] [[BI]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_ule(i16* %a, i16* %b) { + %r = icmp ule i16* %a, %b + ret i1 %r +} + +; CHECK: [[SLE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[AI:%.*]] = OpConvertPtrToU {{%.+}} [[A]] +; CHECK-NEXT: [[BI:%.*]] = OpConvertPtrToU {{%.+}} [[B]] +; CHECK: [[R:%.*]] = OpSLessThanEqual {{%.+}} [[AI]] [[BI]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_sle(i16* %a, i16* %b) { + %r = icmp sle i16* %a, %b + ret i1 %r +} + +; CHECK: [[UGT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[AI:%.*]] = OpConvertPtrToU {{%.+}} [[A]] +; CHECK-NEXT: [[BI:%.*]] = OpConvertPtrToU {{%.+}} [[B]] +; CHECK: [[R:%.*]] = OpUGreaterThan {{%.+}} [[AI]] [[BI]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_ugt(i16* %a, i16* %b) { + %r = icmp ugt i16* %a, %b + ret i1 %r +} + +; CHECK: [[SGT]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[AI:%.*]] = OpConvertPtrToU {{%.+}} [[A]] +; CHECK-NEXT: [[BI:%.*]] = OpConvertPtrToU {{%.+}} [[B]] +; CHECK: [[R:%.*]] = OpSGreaterThan {{%.+}} [[AI]] [[BI]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_sgt(i16* %a, i16* %b) { + %r = icmp sgt i16* %a, %b + ret i1 %r +} + +; CHECK: [[UGE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[AI:%.*]] = OpConvertPtrToU {{%.+}} [[A]] +; CHECK-NEXT: [[BI:%.*]] = OpConvertPtrToU {{%.+}} [[B]] +; CHECK: [[R:%.*]] = OpUGreaterThanEqual {{%.+}} [[AI]] [[BI]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_uge(i16* %a, i16* %b) { + %r = icmp uge i16* %a, %b + ret i1 %r +} + +; CHECK: [[SGE]] = OpFunction +; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter +; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[AI:%.*]] = OpConvertPtrToU {{%.+}} [[A]] +; CHECK-NEXT: [[BI:%.*]] = OpConvertPtrToU {{%.+}} [[B]] +; CHECK: [[R:%.*]] = OpSGreaterThanEqual {{%.+}} [[AI]] [[BI]] +; CHECK-NEXT: OpReturnValue [[R]] +; CHECK-NEXT: OpFunctionEnd +define i1 @test_sge(i16* %a, i16* %b) { + %r = icmp sge i16* %a, %b + ret i1 %r +}