From: Ilia Diachkov Date: Thu, 5 May 2022 20:29:26 +0000 (+0300) Subject: [SPIRV] Add SPIR-V specific intrinsics, two passes and tests X-Git-Tag: upstream/15.0.7~8498 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=0098f2aebb43218f0040f0b739897b0bfe0e259e;p=platform%2Fupstream%2Fllvm.git [SPIRV] Add SPIR-V specific intrinsics, two passes and tests The patch adds SPIR-V specific intrinsics required to keep information critical to SPIR-V consistency (types, constants, etc.) during translation from IR to MIR. Two related passes (SPIRVEmitIntrinsics and SPIRVPreLegalizer) and several LIT tests (passed with this change) have also been added. It also fixes the issue with opaque pointers in SPIRVGlobalRegistry.cpp and the mismatch of the data layout between the SPIR-V backend and clang (Issue #55122). Differential Revision: https://reviews.llvm.org/D124416 Co-authored-by: Aleksandr Bezzubikov Co-authored-by: Michal Paszkowski Co-authored-by: Andrey Tretyakov Co-authored-by: Konrad Trifunovic --- diff --git a/llvm/include/llvm/IR/CMakeLists.txt b/llvm/include/llvm/IR/CMakeLists.txt index 0498fc2..872c400 100644 --- a/llvm/include/llvm/IR/CMakeLists.txt +++ b/llvm/include/llvm/IR/CMakeLists.txt @@ -14,6 +14,7 @@ tablegen(LLVM IntrinsicsNVPTX.h -gen-intrinsic-enums -intrinsic-prefix=nvvm) 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 index 72ff144..76ff481 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -2046,4 +2046,5 @@ include "llvm/IR/IntrinsicsBPF.td" 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 index 0000000..14c6285 --- /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 index 0c52fed..f19609e 100644 --- a/llvm/lib/Target/SPIRV/CMakeLists.txt +++ b/llvm/lib/Target/SPIRV/CMakeLists.txt @@ -15,6 +15,7 @@ add_public_tablegen_target(SPIRVCommonTableGen) add_llvm_target(SPIRVCodeGen SPIRVAsmPrinter.cpp SPIRVCallLowering.cpp + SPIRVEmitIntrinsics.cpp SPIRVGlobalRegistry.cpp SPIRVInstrInfo.cpp SPIRVInstructionSelector.cpp @@ -22,6 +23,7 @@ add_llvm_target(SPIRVCodeGen 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 index c574002..8da54a5 100644 --- a/llvm/lib/Target/SPIRV/SPIRV.h +++ b/llvm/lib/Target/SPIRV/SPIRV.h @@ -19,12 +19,16 @@ class SPIRVSubtarget; 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 index 0000000..4c5bad4 --- /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.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp index f1d9386..02a6905 100644 --- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp @@ -30,14 +30,14 @@ SPIRVType *SPIRVGlobalRegistry::assignTypeToVReg( 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 @@ Register SPIRVGlobalRegistry::buildGlobalVariable( 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 @@ SPIRVType *SPIRVGlobalRegistry::createSPIRVType(const Type *Ty, 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/SPIRVGlobalRegistry.h b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h index b6727a6..952ab4c 100644 --- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h +++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h @@ -58,7 +58,7 @@ public: // 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/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp index 367fca0..9294a60 100644 --- 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 @@ private: 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 @@ bool SPIRVInstructionSelector::selectOpUndef(Register ResVReg, .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 index fff277e..87f9e95 100644 --- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp @@ -264,7 +264,7 @@ static Register convertPtrToInt(Register Reg, LLT ConvTy, SPIRVType *SpirvType, 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 index 0000000..687f840 --- /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 index e705de5..f7c88a5 100644 --- a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp @@ -44,14 +44,12 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVTarget() { } 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 @@ public: void addISelPrepare() override; bool addIRTranslator() override; + void addPreLegalizeMachineIR() override; bool addLegalizeMachineIR() override; bool addRegBankSelect() override; bool addGlobalInstructionSelect() override; @@ -143,13 +142,20 @@ TargetPassConfig *SPIRVTargetMachine::createPassConfig(PassManagerBase &PM) { 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.cpp b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp index aa1933f..b92dc12 100644 --- 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 @@ SPIRV::MemorySemantics getMemSemanticsForStorageClass(SPIRV::StorageClass SC) { 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/lib/Target/SPIRV/SPIRVUtils.h b/llvm/lib/Target/SPIRV/SPIRVUtils.h index 3cb1db7..ffa82c9 100644 --- a/llvm/lib/Target/SPIRV/SPIRVUtils.h +++ b/llvm/lib/Target/SPIRV/SPIRVUtils.h @@ -66,4 +66,18 @@ llvm::SPIRV::StorageClass addressSpaceToStorageClass(unsigned AddrSpace); 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/test/CodeGen/SPIRV/branching/if-merging.ll b/llvm/test/CodeGen/SPIRV/branching/if-merging.ll new file mode 100644 index 0000000..e112982 --- /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 index 0000000..3fb8301 --- /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 index df61f2c..66b273d 100644 --- 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 @@ target triple = "spirv32-unknown-unknown" ; 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 index 0000000..90603b4 --- /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 index 0000000..e758117 --- /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 index 0000000..7ad22d8 --- /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 index 0000000..73ec5ca --- /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 index 0000000..bdf8284 --- /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 index 0000000..74f95e5 --- /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 index 0000000..bbc2e20 --- /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 index 0000000..55da211 --- /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 +}