diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp index e5ba0201c0cc1..b111909fc25cc 100644 --- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp @@ -1154,10 +1154,63 @@ static unsigned getNumSizeComponents(SPIRVType *imgType) { return arrayed ? numComps + 1 : numComps; } +static bool builtinMayNeedPromotionToVec(uint32_t BuiltinNumber) { + switch (BuiltinNumber) { + case SPIRV::OpenCLExtInst::s_min: + case SPIRV::OpenCLExtInst::u_min: + case SPIRV::OpenCLExtInst::s_max: + case SPIRV::OpenCLExtInst::u_max: + case SPIRV::OpenCLExtInst::fmax: + case SPIRV::OpenCLExtInst::fmin: + case SPIRV::OpenCLExtInst::fmax_common: + case SPIRV::OpenCLExtInst::fmin_common: + case SPIRV::OpenCLExtInst::s_clamp: + case SPIRV::OpenCLExtInst::fclamp: + case SPIRV::OpenCLExtInst::u_clamp: + case SPIRV::OpenCLExtInst::mix: + case SPIRV::OpenCLExtInst::step: + case SPIRV::OpenCLExtInst::smoothstep: + return true; + default: + break; + } + return false; +} + //===----------------------------------------------------------------------===// // Implementation functions for each builtin group //===----------------------------------------------------------------------===// +static SmallVector +getBuiltinCallArguments(const SPIRV::IncomingCall *Call, uint32_t BuiltinNumber, + MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) { + + Register ReturnTypeId = GR->getSPIRVTypeID(Call->ReturnType); + unsigned ResultElementCount = + GR->getScalarOrVectorComponentCount(ReturnTypeId); + bool MayNeedPromotionToVec = + builtinMayNeedPromotionToVec(BuiltinNumber) && ResultElementCount > 1; + + if (!MayNeedPromotionToVec) + return {Call->Arguments.begin(), Call->Arguments.end()}; + + SmallVector Arguments; + for (Register Argument : Call->Arguments) { + Register VecArg = Argument; + SPIRVType *ArgumentType = GR->getSPIRVTypeForVReg(Argument); + if (ArgumentType != Call->ReturnType) { + VecArg = createVirtualRegister(Call->ReturnType, GR, MIRBuilder); + auto VecSplat = MIRBuilder.buildInstr(SPIRV::OpCompositeConstruct) + .addDef(VecArg) + .addUse(ReturnTypeId); + for (unsigned I = 0; I != ResultElementCount; ++I) + VecSplat.addUse(Argument); + } + Arguments.push_back(VecArg); + } + return Arguments; +} + static bool generateExtInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR, const CallBase &CB) { @@ -1179,16 +1232,21 @@ static bool generateExtInst(const SPIRV::IncomingCall *Call, : SPIRV::OpenCLExtInst::fmax; } + Register ReturnTypeId = GR->getSPIRVTypeID(Call->ReturnType); + SmallVector Arguments = + getBuiltinCallArguments(Call, Number, MIRBuilder, GR); + // Build extended instruction. auto MIB = MIRBuilder.buildInstr(SPIRV::OpExtInst) .addDef(Call->ReturnRegister) - .addUse(GR->getSPIRVTypeID(Call->ReturnType)) + .addUse(ReturnTypeId) .addImm(static_cast(SPIRV::InstructionSet::OpenCL_std)) .addImm(Number); - for (auto Argument : Call->Arguments) + for (Register Argument : Arguments) MIB.addUse(Argument); + MIB.getInstr()->copyIRFlags(CB); if (OrigNumber == SPIRV::OpenCLExtInst::fmin_common || OrigNumber == SPIRV::OpenCLExtInst::fmax_common) { diff --git a/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp b/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp index 1b95f09974c61..653c9ad53e888 100644 --- a/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp @@ -12,11 +12,10 @@ //===----------------------------------------------------------------------===// #include "SPIRV.h" -#include "llvm/Demangle/Demangle.h" +#include "llvm/IR/Constants.h" #include "llvm/IR/InstIterator.h" -#include "llvm/IR/InstVisitor.h" +#include "llvm/IR/Instructions.h" #include "llvm/IR/PassManager.h" -#include "llvm/Transforms/Utils/Cloning.h" #include @@ -25,9 +24,7 @@ using namespace llvm; namespace { -struct SPIRVRegularizer : public FunctionPass, InstVisitor { - DenseMap Old2NewFuncs; - +struct SPIRVRegularizer : public FunctionPass { public: static char ID; SPIRVRegularizer() : FunctionPass(ID) {} @@ -37,11 +34,8 @@ struct SPIRVRegularizer : public FunctionPass, InstVisitor { void getAnalysisUsage(AnalysisUsage &AU) const override { FunctionPass::getAnalysisUsage(AU); } - void visitCallInst(CallInst &CI); private: - void visitCallScalToVec(CallInst *CI, StringRef MangledName, - StringRef DemangledName); void runLowerConstExpr(Function &F); }; } // namespace @@ -157,98 +151,8 @@ void SPIRVRegularizer::runLowerConstExpr(Function &F) { } } -// It fixes calls to OCL builtins that accept vector arguments and one of them -// is actually a scalar splat. -void SPIRVRegularizer::visitCallInst(CallInst &CI) { - auto F = CI.getCalledFunction(); - if (!F) - return; - - auto MangledName = F->getName(); - char *NameStr = itaniumDemangle(F->getName().data()); - if (!NameStr) - return; - StringRef DemangledName(NameStr); - - // TODO: add support for other builtins. - if (DemangledName.starts_with("fmin") || DemangledName.starts_with("fmax") || - DemangledName.starts_with("min") || DemangledName.starts_with("max")) - visitCallScalToVec(&CI, MangledName, DemangledName); - free(NameStr); -} - -void SPIRVRegularizer::visitCallScalToVec(CallInst *CI, StringRef MangledName, - StringRef DemangledName) { - // Check if all arguments have the same type - it's simple case. - auto Uniform = true; - Type *Arg0Ty = CI->getOperand(0)->getType(); - auto IsArg0Vector = isa(Arg0Ty); - for (unsigned I = 1, E = CI->arg_size(); Uniform && (I != E); ++I) - Uniform = isa(CI->getOperand(I)->getType()) == IsArg0Vector; - if (Uniform) - return; - - auto *OldF = CI->getCalledFunction(); - Function *NewF = nullptr; - auto [It, Inserted] = Old2NewFuncs.try_emplace(OldF); - if (Inserted) { - AttributeList Attrs = CI->getCalledFunction()->getAttributes(); - SmallVector ArgTypes = {OldF->getArg(0)->getType(), Arg0Ty}; - auto *NewFTy = - FunctionType::get(OldF->getReturnType(), ArgTypes, OldF->isVarArg()); - NewF = Function::Create(NewFTy, OldF->getLinkage(), OldF->getName(), - *OldF->getParent()); - ValueToValueMapTy VMap; - auto NewFArgIt = NewF->arg_begin(); - for (auto &Arg : OldF->args()) { - auto ArgName = Arg.getName(); - NewFArgIt->setName(ArgName); - VMap[&Arg] = &(*NewFArgIt++); - } - SmallVector Returns; - CloneFunctionInto(NewF, OldF, VMap, - CloneFunctionChangeType::LocalChangesOnly, Returns); - NewF->setAttributes(Attrs); - It->second = NewF; - } else { - NewF = It->second; - } - assert(NewF); - - // This produces an instruction sequence that implements a splat of - // CI->getOperand(1) to a vector Arg0Ty. However, we use InsertElementInst - // and ShuffleVectorInst to generate the same code as the SPIR-V translator. - // For instance (transcoding/OpMin.ll), this call - // call spir_func <2 x i32> @_Z3minDv2_ii(<2 x i32> , i32 5) - // is translated to - // %8 = OpUndef %v2uint - // %14 = OpConstantComposite %v2uint %uint_1 %uint_10 - // ... - // %10 = OpCompositeInsert %v2uint %uint_5 %8 0 - // %11 = OpVectorShuffle %v2uint %10 %8 0 0 - // %call = OpExtInst %v2uint %1 s_min %14 %11 - auto ConstInt = ConstantInt::get(IntegerType::get(CI->getContext(), 32), 0); - PoisonValue *PVal = PoisonValue::get(Arg0Ty); - Instruction *Inst = InsertElementInst::Create( - PVal, CI->getOperand(1), ConstInt, "", CI->getIterator()); - ElementCount VecElemCount = cast(Arg0Ty)->getElementCount(); - Constant *ConstVec = ConstantVector::getSplat(VecElemCount, ConstInt); - Value *NewVec = - new ShuffleVectorInst(Inst, PVal, ConstVec, "", CI->getIterator()); - CI->setOperand(1, NewVec); - CI->replaceUsesOfWith(OldF, NewF); - CI->mutateFunctionType(NewF->getFunctionType()); -} - bool SPIRVRegularizer::runOnFunction(Function &F) { runLowerConstExpr(F); - visit(F); - for (auto &OldNew : Old2NewFuncs) { - Function *OldF = OldNew.first; - Function *NewF = OldNew.second; - NewF->takeName(OldF); - OldF->eraseFromParent(); - } return true; } diff --git a/llvm/test/CodeGen/SPIRV/transcoding/OpExtInst_vector_promotion.ll b/llvm/test/CodeGen/SPIRV/transcoding/OpExtInst_vector_promotion.ll new file mode 100644 index 0000000000000..b406f8b71f7e6 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/transcoding/OpExtInst_vector_promotion.ll @@ -0,0 +1,179 @@ +; RUN: llc -O0 -mtriple=spirv32-unknown-unknown < %s | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown < %s -filetype=obj | spirv-val %} +; +; Some OpenCL builtins have mixed vector-scalar variants, but OpExtInt only supports +; versions where all the arguments have the same type. +; +; We generate code, but it is invalid. +; We should generate vector versions for these cases. + +define spir_kernel void @S_MIN() { +; CHECK-LABEL: OpFunction %{{[0-9]+}} None %{{[0-9]+}} ; -- Begin function S_MIN +; CHECK-NEXT: OpLabel +; CHECK-NEXT: %[[VEC:[0-9]+]] = OpCompositeConstruct %[[VECTYPE:[0-9]+]] %[[SCALAR:[0-9]+]] %[[SCALAR]] +; CHECK-NEXT: %{{[0-9]+}} = OpExtInst %[[VECTYPE]] %{{[0-9]+}} s_min %{{[0-9]+}} %[[VEC]] +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd +; CHECK-NEXT: ; -- End function +entry: + %call = tail call spir_func <2 x i32> @_Z3minDv2_ii(<2 x i32> , i32 5) + ret void +} + +define spir_kernel void @U_MIN() { +; CHECK-LABEL: OpFunction %{{[0-9]+}} None %{{[0-9]+}} ; -- Begin function U_MIN +; CHECK-NEXT: OpLabel +; CHECK-NEXT: %[[VEC:[0-9]+]] = OpCompositeConstruct %[[VECTYPE:[0-9]+]] %[[SCALAR:[0-9]+]] %[[SCALAR]] +; CHECK-NEXT: %{{[0-9]+}} = OpExtInst %[[VECTYPE]] %{{[0-9]+}} u_min %{{[0-9]+}} %[[VEC]] +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd +; CHECK-NEXT: ; -- End function +entry: + %call = tail call spir_func <2 x i32> @_Z3minDv2_jj(<2 x i32> , i32 5) + ret void +} + +define spir_kernel void @S_MAX() { +; CHECK-LABEL: OpFunction %{{[0-9]+}} None %{{[0-9]+}} ; -- Begin function S_MAX +; CHECK-NEXT: OpLabel +; CHECK-NEXT: %[[VEC:[0-9]+]] = OpCompositeConstruct %[[VECTYPE:[0-9]+]] %[[SCALAR:[0-9]+]] %[[SCALAR]] +; CHECK-NEXT: %{{[0-9]+}} = OpExtInst %[[VECTYPE]] %{{[0-9]+}} s_max %{{[0-9]+}} %[[VEC]] +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd +; CHECK-NEXT: ; -- End function +entry: + %call = tail call spir_func <2 x i32> @_Z3maxDv2_ii(<2 x i32> , i32 5) + ret void +} + +define spir_kernel void @F_MIN() { +; CHECK-LABEL: OpFunction %{{[0-9]+}} None %{{[0-9]+}} ; -- Begin function F_MIN +; CHECK-NEXT: OpLabel +; CHECK-NEXT: %[[VEC:[0-9]+]] = OpCompositeConstruct %[[VECTYPE:[0-9]+]] %[[SCALAR:[0-9]+]] %[[SCALAR]] +; CHECK-NEXT: %{{[0-9]+}} = OpExtInst %[[VECTYPE]] %{{[0-9]+}} fmin %{{[0-9]+}} %[[VEC]] +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd +; CHECK-NEXT: ; -- End function +entry: + %call = tail call spir_func <2 x float> @_Z3minDv2_ff(<2 x float> , float 5.0) + ret void +} + +define spir_kernel void @F_MAX() { +; CHECK-LABEL: OpFunction %{{[0-9]+}} None %{{[0-9]+}} ; -- Begin function F_MAX +; CHECK-NEXT: OpLabel +; CHECK-NEXT: %[[VEC:[0-9]+]] = OpCompositeConstruct %[[VECTYPE:[0-9]+]] %[[SCALAR:[0-9]+]] %[[SCALAR]] +; CHECK-NEXT: %{{[0-9]+}} = OpExtInst %[[VECTYPE]] %{{[0-9]+}} fmax %{{[0-9]+}} %[[VEC]] +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd +; CHECK-NEXT: ; -- End function +entry: + %call = tail call spir_func <2 x float> @_Z3maxDv2_ff(<2 x float> , float 5.0) + ret void +} + +define spir_kernel void @F_FMIN() { +; CHECK-LABEL: OpFunction %{{[0-9]+}} None %{{[0-9]+}} ; -- Begin function F_FMIN +; CHECK-NEXT: OpLabel +; CHECK-NEXT: %[[VEC:[0-9]+]] = OpCompositeConstruct %[[VECTYPE:[0-9]+]] %[[SCALAR:[0-9]+]] %[[SCALAR]] +; CHECK-NEXT: %{{[0-9]+}} = OpExtInst %[[VECTYPE]] %{{[0-9]+}} fmin %{{[0-9]+}} %[[VEC]] +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd +; CHECK-NEXT: ; -- End function +entry: + %call = tail call spir_func <2 x float> @_Z4fminDv2_ff(<2 x float> , float 5.0) + ret void +} + +define spir_kernel void @F_FMAX() { +; CHECK-LABEL: OpFunction %{{[0-9]+}} None %{{[0-9]+}} ; -- Begin function F_FMAX +; CHECK-NEXT: OpLabel +; CHECK-NEXT: %[[VEC:[0-9]+]] = OpCompositeConstruct %[[VECTYPE:[0-9]+]] %[[SCALAR:[0-9]+]] %[[SCALAR]] +; CHECK-NEXT: %{{[0-9]+}} = OpExtInst %[[VECTYPE]] %{{[0-9]+}} fmax %{{[0-9]+}} %[[VEC]] +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd +; CHECK-NEXT: ; -- End function +entry: + %call = tail call spir_func <2 x float> @_Z4fmaxDv2_ff(<2 x float> , float 5.0) + ret void +} + +define spir_kernel void @S_CLAMP() { +; CHECK-LABEL: OpFunction %{{[0-9]+}} None %{{[0-9]+}} ; -- Begin function S_CLAMP +; CHECK-NEXT: OpLabel +; CHECK-NEXT: %[[VEC_0:[0-9]+]] = OpCompositeConstruct %[[VECTYPE:[0-9]+]] %[[SCALAR_0:[0-9]+]] %[[SCALAR_0]] +; CHECK-NEXT: %[[VEC_1:[0-9]+]] = OpCompositeConstruct %[[VECTYPE:[0-9]+]] %[[SCALAR_1:[0-9]+]] %[[SCALAR_1]] +; CHECK-NEXT: %{{[0-9]+}} = OpExtInst %[[VECTYPE]] %{{[0-9]+}} s_clamp %{{[0-9]+}} %[[VEC_0]] %[[VEC_1]] +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd +; CHECK-NEXT: ; -- End function +entry: + %call = tail call spir_func <2 x i32> @_Z5clampDv2_iii(<2 x i32> , i32 5, i32 6) + ret void +} + +define spir_kernel void @F_CLAMP() { +; CHECK-LABEL: OpFunction %{{[0-9]+}} None %{{[0-9]+}} ; -- Begin function F_CLAMP +; CHECK-NEXT: OpLabel +; CHECK-NEXT: %[[VEC_0:[0-9]+]] = OpCompositeConstruct %[[VECTYPE:[0-9]+]] %[[SCALAR_0:[0-9]+]] %[[SCALAR_0]] +; CHECK-NEXT: %[[VEC_1:[0-9]+]] = OpCompositeConstruct %[[VECTYPE:[0-9]+]] %[[SCALAR_1:[0-9]+]] %[[SCALAR_1]] +; CHECK-NEXT: %{{[0-9]+}} = OpExtInst %[[VECTYPE]] %{{[0-9]+}} fclamp %{{[0-9]+}} %[[VEC_0]] %[[VEC_1]] +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd +; CHECK-NEXT: ; -- End function +entry: + %call = tail call spir_func <2 x float> @_Z5clampDv2_fff(<2 x float> , float 5.0, float 6.0) + ret void +} + +define spir_kernel void @MIX() { +; CHECK-LABEL: OpFunction %{{[0-9]+}} None %{{[0-9]+}} ; -- Begin function MIX +; CHECK-NEXT: OpLabel +; CHECK-NEXT: %[[VEC:[0-9]+]] = OpCompositeConstruct %[[VECTYPE:[0-9]+]] %[[SCALAR:[0-9]+]] %[[SCALAR]] +; CHECK-NEXT: %{{[0-9]+}} = OpExtInst %[[VECTYPE]] %{{[0-9]+}} mix %{{[0-9]+}} %{{[0-9]+}} %[[VEC]] +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd +; CHECK-NEXT: ; -- End function +entry: + %call = tail call spir_func <2 x float> @_Z3mixDv2_fS_f(<2 x float> , <2 x float> , float 0.5) + ret void +} + +define spir_kernel void @SMOOTHSTEP() { +; CHECK-LABEL: OpFunction %{{[0-9]+}} None %{{[0-9]+}} ; -- Begin function SMOOTHSTEP +; CHECK-NEXT: OpLabel +; CHECK-NEXT: %[[VEC_0:[0-9]+]] = OpCompositeConstruct %[[VECTYPE:[0-9]+]] %[[SCALAR_0:[0-9]+]] %[[SCALAR_0]] +; CHECK-NEXT: %[[VEC_1:[0-9]+]] = OpCompositeConstruct %[[VECTYPE:[0-9]+]] %[[SCALAR_1:[0-9]+]] %[[SCALAR_1]] +; CHECK-NEXT: %{{[0-9]+}} = OpExtInst %[[VECTYPE]] %{{[0-9]+}} smoothstep %[[VEC_0]] %[[VEC_1]] %{{[0-9]+}} +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd +; CHECK-NEXT: ; -- End function +entry: + %call = tail call spir_func <2 x float> @_Z10smoothstepffDv2_f(float 1.0, float 0.5, <2 x float> ) + ret void +} + +define spir_kernel void @ill_0() { +; CHECK-LABEL: OpFunction %{{[0-9]+}} None %{{[0-9]+}} ; -- Begin function ill_0 +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %{{[0-9]+}} %{{[0-9]+}} +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd +; CHECK-NEXT: ; -- End function +entry: + tail call spir_func void @_Z3minv() + ret void +} + +declare spir_func <2 x i32> @_Z3minDv2_ii(<2 x i32>, i32) +declare spir_func <2 x i32> @_Z3minDv2_jj(<2 x i32>, i32) +declare spir_func <2 x i32> @_Z3maxDv2_ii(<2 x i32>, i32) +declare spir_func <2 x float> @_Z3minDv2_ff(<2 x float>, float) +declare spir_func <2 x float> @_Z3maxDv2_ff(<2 x float>, float) +declare spir_func <2 x float> @_Z4fminDv2_ff(<2 x float>, float) +declare spir_func <2 x float> @_Z4fmaxDv2_ff(<2 x float>, float) +declare spir_func <2 x i32> @_Z5clampDv2_iii(<2 x i32>, i32) +declare spir_func <2 x float> @_Z5clampDv2_fff(<2 x float>, float) +declare spir_func <2 x float> @_Z3mixDv2_fS_f(<2 x float>, <2 x float>, float) +declare spir_func <2 x float> @_Z10smoothstepffDv2_f(float, float, <2 x float>) +declare spir_func void @_Z3minv() diff --git a/llvm/test/CodeGen/SPIRV/transcoding/OpExtInst_vector_promotion_bug.ll b/llvm/test/CodeGen/SPIRV/transcoding/OpExtInst_vector_promotion_bug.ll new file mode 100644 index 0000000000000..b81f373be33c3 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/transcoding/OpExtInst_vector_promotion_bug.ll @@ -0,0 +1,21 @@ +; RUN: llc -O0 -mtriple=spirv32-unknown-unknown < %s | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown < %s -filetype=obj | not spirv-val 2>&1 | FileCheck %s --check-prefix=VALIDATOR %} +; +; _Z3miniii is not a valid OpenCL intrinsic, do not treat it like one. +; +; VALIDATOR: Invalid instruction OpExtInst starting at word {{[0-9]+}}: expected no more operands after 7 words, but stated word count is 8 + +define spir_kernel void @ill_1() { +; CHECK-LABEL: OpFunction %{{[0-9]+}} None %{{[0-9]+}} ; -- Begin function ill_1 +; CHECK-NEXT: OpLabel +; This is wrong, we should generate a regular call +; CHECK-NEXT: %{{[0-9]+}} = OpExtInst %{{[0-9]+}} %{{[0-9]+}} s_min %{{[0-9]+}} %{{[0-9]+}} %{{[0-9]+}} +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd +; CHECK-NEXT: ; -- End function +entry: + tail call spir_func void @_Z3miniii(i32 1, i32 2, i32 3) + ret void +} + +declare spir_func i32 @_Z3miniii(i32, i32, i32) diff --git a/llvm/test/CodeGen/SPIRV/transcoding/OpMin.ll b/llvm/test/CodeGen/SPIRV/transcoding/OpMin.ll deleted file mode 100644 index 5cc3ea01e5191..0000000000000 --- a/llvm/test/CodeGen/SPIRV/transcoding/OpMin.ll +++ /dev/null @@ -1,16 +0,0 @@ -; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV - -; CHECK-SPIRV: %[[#SetInstID:]] = OpExtInstImport "OpenCL.std" -; CHECK-SPIRV: %[[#IntTypeID:]] = OpTypeInt 32 [[#]] -; CHECK-SPIRV: %[[#Int2TypeID:]] = OpTypeVector %[[#IntTypeID]] 2 -; CHECK-SPIRV: %[[#CompositeID:]] = OpCompositeInsert %[[#Int2TypeID]] %[[#]] %[[#]] [[#]] -; CHECK-SPIRV: %[[#ShuffleID:]] = OpVectorShuffle %[[#Int2TypeID]] %[[#CompositeID]] %[[#]] [[#]] [[#]] -; CHECK-SPIRV: %[[#]] = OpExtInst %[[#Int2TypeID]] %[[#SetInstID]] s_min %[[#]] %[[#ShuffleID]] - -define spir_kernel void @test() { -entry: - %call = tail call spir_func <2 x i32> @_Z3minDv2_ii(<2 x i32> , i32 5) #2 - ret void -} - -declare spir_func <2 x i32> @_Z3minDv2_ii(<2 x i32>, i32)