[llvm] f472ebe - [SPIR-V] Add OpSMulExtended and OpUMulExtended builtin support (#187474)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 16 08:01:45 PDT 2026
Author: Arseniy Obolenskiy
Date: 2026-04-16T17:01:40+02:00
New Revision: f472ebed217c51af47cc6eb3ec697d8555b00c50
URL: https://github.com/llvm/llvm-project/commit/f472ebed217c51af47cc6eb3ec697d8555b00c50
DIFF: https://github.com/llvm/llvm-project/commit/f472ebed217c51af47cc6eb3ec697d8555b00c50.diff
LOG: [SPIR-V] Add OpSMulExtended and OpUMulExtended builtin support (#187474)
Support OpSMulExtended and OpUMulExtended builtins in a new MulExtended
builtin group and handle both calling conventions: direct struct return
and sret pointer.
Added:
llvm/test/CodeGen/SPIRV/smulextended-builtin.ll
llvm/test/CodeGen/SPIRV/umulextended-builtin.ll
Modified:
llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
llvm/lib/Target/SPIRV/SPIRVBuiltins.td
Removed:
################################################################################
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index f91387afdeb22..82c15eed42ef4 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -1996,6 +1996,28 @@ static bool generateWaveInst(const SPIRV::IncomingCall *Call,
/* isConst= */ false, /* LinkageType= */ std::nullopt);
}
+// Build a SPIR-V instruction with struct return via sret pointer:
+// Res = Opcode RetType Op1 Op2
+// OpStore SRetReg Res
+static void buildSRetInst(unsigned Opcode, Register SRetReg, Register Op1Reg,
+ Register Op2Reg, SPIRVTypeInst RetType,
+ MachineIRBuilder &MIRBuilder,
+ SPIRVGlobalRegistry *GR) {
+ MachineRegisterInfo *MRI = MIRBuilder.getMRI();
+ Register ResReg = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
+ if (const TargetRegisterClass *DstRC = MRI->getRegClassOrNull(Op1Reg)) {
+ MRI->setRegClass(ResReg, DstRC);
+ MRI->setType(ResReg, MRI->getType(Op1Reg));
+ }
+ GR->assignSPIRVTypeToVReg(RetType, ResReg, MIRBuilder.getMF());
+ MIRBuilder.buildInstr(Opcode)
+ .addDef(ResReg)
+ .addUse(GR->getSPIRVTypeID(RetType))
+ .addUse(Op1Reg)
+ .addUse(Op2Reg);
+ MIRBuilder.buildInstr(SPIRV::OpStore).addUse(SRetReg).addUse(ResReg);
+}
+
// We expect a builtin
// Name(ptr sret([RetType]) %result, Type %operand1, Type %operand1)
// where %result is a pointer to where the result of the builtin execution
@@ -2032,22 +2054,84 @@ static bool generateICarryBorrowInst(const SPIRV::IncomingCall *Call,
break;
}
- MachineRegisterInfo *MRI = MIRBuilder.getMRI();
- Register ResReg = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
- if (const TargetRegisterClass *DstRC =
- MRI->getRegClassOrNull(Call->Arguments[1])) {
- MRI->setRegClass(ResReg, DstRC);
- MRI->setType(ResReg, MRI->getType(Call->Arguments[1]));
+ buildSRetInst(Opcode, SRetReg, Call->Arguments[1], Call->Arguments[2],
+ RetType, MIRBuilder, GR);
+ return true;
+}
+
+// We expect a builtin in one of two forms:
+//
+// (1) sret convention (3 arguments):
+// void Name(ptr sret([RetType]) %result, Type %operand1, Type %operand2)
+// => Res = Opcode RetType Operand1 Operand2
+// OpStore %result Res
+//
+// (2) direct return convention (2 arguments):
+// RetType Name(Type %operand1, Type %operand2)
+// => Res = Opcode RetType Operand1 Operand2
+//
+// RetType is a struct with two members of the same type as the operands.
+static bool generateMulExtendedInst(const SPIRV::IncomingCall *Call,
+ MachineIRBuilder &MIRBuilder,
+ SPIRVGlobalRegistry *GR) {
+ const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
+ unsigned Opcode =
+ SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
+ assert((Opcode == SPIRV::OpUMulExtended || Opcode == SPIRV::OpSMulExtended) &&
+ "Expected OpUMulExtended or OpSMulExtended");
+
+ const bool IsSret =
+ !Call->ReturnType || Call->ReturnType->getOpcode() == SPIRV::OpTypeVoid;
+ Register Op1Reg = IsSret ? Call->Arguments[1] : Call->Arguments[0];
+ Register Op2Reg = IsSret ? Call->Arguments[2] : Call->Arguments[1];
+
+ SPIRVTypeInst RetType = nullptr;
+ if (IsSret) {
+ Register SRetReg = Call->Arguments[0];
+ SPIRVTypeInst PtrRetType = GR->getSPIRVTypeForVReg(SRetReg);
+ RetType = GR->getPointeeType(PtrRetType);
+ if (!RetType)
+ report_fatal_error("The first parameter must be a pointer");
} else {
- MRI->setType(ResReg, LLT::scalar(64));
+ RetType = Call->ReturnType;
+ }
+
+ if (!RetType || RetType->getOpcode() != SPIRV::OpTypeStruct)
+ report_fatal_error("Expected struct type result for the extended "
+ "multiplication builtins");
+ if (RetType->getNumOperands() != 3)
+ report_fatal_error("Expected struct with exactly two members for the "
+ "extended multiplication builtins");
+ SPIRVTypeInst Member0Type =
+ GR->getSPIRVTypeForVReg(RetType->getOperand(1).getReg());
+ SPIRVTypeInst Member1Type =
+ GR->getSPIRVTypeForVReg(RetType->getOperand(2).getReg());
+ if (!Member0Type || !Member1Type || Member0Type != Member1Type)
+ report_fatal_error("Both struct members must be the same type");
+
+ SPIRVTypeInst OpType1 = GR->getSPIRVTypeForVReg(Op1Reg);
+ SPIRVTypeInst OpType2 = GR->getSPIRVTypeForVReg(Op2Reg);
+ if (!OpType1 || !OpType2 || OpType1 != OpType2)
+ report_fatal_error("Operands must have the same type");
+ if (OpType1 != Member0Type)
+ report_fatal_error("Operand type must match the struct member type");
+
+ if (IsSret) {
+ buildSRetInst(Opcode, Call->Arguments[0], Op1Reg, Op2Reg, RetType,
+ MIRBuilder, GR);
+ } else {
+ MachineRegisterInfo *MRI = MIRBuilder.getMRI();
+ Register ResReg = Call->ReturnRegister;
+ if (const TargetRegisterClass *DstRC = MRI->getRegClassOrNull(Op1Reg)) {
+ MRI->setRegClass(ResReg, DstRC);
+ }
+ GR->assignSPIRVTypeToVReg(RetType, ResReg, MIRBuilder.getMF());
+ MIRBuilder.buildInstr(Opcode)
+ .addDef(ResReg)
+ .addUse(GR->getSPIRVTypeID(RetType))
+ .addUse(Op1Reg)
+ .addUse(Op2Reg);
}
- GR->assignSPIRVTypeToVReg(RetType, ResReg, MIRBuilder.getMF());
- MIRBuilder.buildInstr(Opcode)
- .addDef(ResReg)
- .addUse(GR->getSPIRVTypeID(RetType))
- .addUse(Call->Arguments[1])
- .addUse(Call->Arguments[2]);
- MIRBuilder.buildInstr(SPIRV::OpStore).addUse(SRetReg).addUse(ResReg);
return true;
}
@@ -3367,6 +3451,8 @@ std::optional<bool> lowerBuiltin(const StringRef DemangledCall,
return generateWaveInst(Call.get(), MIRBuilder, GR);
case SPIRV::ICarryBorrow:
return generateICarryBorrowInst(Call.get(), MIRBuilder, GR);
+ case SPIRV::MulExtended:
+ return generateMulExtendedInst(Call.get(), MIRBuilder, GR);
case SPIRV::GetQuery:
return generateGetQueryInst(Call.get(), MIRBuilder, GR);
case SPIRV::ImageSizeQuery:
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.td b/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
index f3daf7c22674e..2bebf95940033 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
@@ -65,6 +65,7 @@ def CastToPtr : BuiltinGroup;
def Construct : BuiltinGroup;
def CoopMatr : BuiltinGroup;
def ICarryBorrow : BuiltinGroup;
+def MulExtended : BuiltinGroup;
def ExtendedBitOps : BuiltinGroup;
def BindlessINTEL : BuiltinGroup;
def TernaryBitwiseINTEL : BuiltinGroup;
@@ -676,6 +677,10 @@ defm : DemangledNativeBuiltin<"__spirv_ControlBarrier", OpenCL_std, Barrier, 3,
defm : DemangledNativeBuiltin<"__spirv_IAddCarry", OpenCL_std, ICarryBorrow, 3, 3, OpIAddCarryS>;
defm : DemangledNativeBuiltin<"__spirv_ISubBorrow", OpenCL_std, ICarryBorrow, 3, 3, OpISubBorrowS>;
+// MulExtended builtin records:
+defm : DemangledNativeBuiltin<"__spirv_UMulExtended", OpenCL_std, MulExtended, 2, 3, OpUMulExtended>;
+defm : DemangledNativeBuiltin<"__spirv_SMulExtended", OpenCL_std, MulExtended, 2, 3, OpSMulExtended>;
+
// cl_intel_split_work_group_barrier
defm : DemangledNativeBuiltin<"intel_work_group_barrier_arrive", OpenCL_std, Barrier, 1, 2, OpControlBarrierArriveINTEL>;
defm : DemangledNativeBuiltin<"__spirv_ControlBarrierArriveINTEL", OpenCL_std, Barrier, 3, 3, OpControlBarrierArriveINTEL>;
diff --git a/llvm/test/CodeGen/SPIRV/smulextended-builtin.ll b/llvm/test/CodeGen/SPIRV/smulextended-builtin.ll
new file mode 100644
index 0000000000000..75e51cc9c1820
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/smulextended-builtin.ll
@@ -0,0 +1,113 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+%i8struct = type {i8, i8}
+%i16struct = type {i16, i16}
+%i32struct = type {i32, i32}
+%i64struct = type {i64, i64}
+%vecstruct = type {<4 x i32>, <4 x i32>}
+
+; CHECK-SPIRV-DAG: %[[#uchar:]] = OpTypeInt 8
+; CHECK-SPIRV-DAG: %[[#ushort:]] = OpTypeInt 16
+; CHECK-SPIRV-DAG: %[[#uint:]] = OpTypeInt 32
+; CHECK-SPIRV-DAG: %[[#ulong:]] = OpTypeInt 64
+; CHECK-SPIRV-DAG: %[[#i8struct:]] = OpTypeStruct %[[#uchar]] %[[#uchar]]
+; CHECK-SPIRV-DAG: %[[#i16struct:]] = OpTypeStruct %[[#ushort]] %[[#ushort]]
+; CHECK-SPIRV-DAG: %[[#i32struct:]] = OpTypeStruct %[[#uint]] %[[#uint]]
+; CHECK-SPIRV-DAG: %[[#i64struct:]] = OpTypeStruct %[[#ulong]] %[[#ulong]]
+; CHECK-SPIRV-DAG: %[[#v4uint:]] = OpTypeVector %[[#uint]] 4
+; CHECK-SPIRV-DAG: %[[#vecstruct:]] = OpTypeStruct %[[#v4uint]] %[[#v4uint]]
+
+define spir_func void @test_builtin_smulext_sret(i32 %a, i32 %b) {
+ entry:
+ %0 = alloca %i32struct
+ call void @__spirv_SMulExtended(ptr sret (%i32struct) %0, i32 %a, i32 %b)
+ ret void
+}
+; CHECK-SPIRV: %[[#]] = OpFunction %[[#]] None %[[#]] ; -- Begin function test_builtin_smulext_sret
+; CHECK-SPIRV-NEXT: %[[#a_s:]] = OpFunctionParameter %[[#uint]]
+; CHECK-SPIRV-NEXT: %[[#b_s:]] = OpFunctionParameter %[[#uint]]
+; CHECK-SPIRV-NEXT: %[[#entry_s:]] = OpLabel
+; CHECK-SPIRV: %[[#var_s:]] = OpVariable %[[#_ptr_Function_i32struct:]] Function
+; CHECK-SPIRV: %[[#res_s:]] = OpSMulExtended %[[#i32struct]] %[[#a_s]] %[[#b_s]]
+; CHECK-SPIRV-NEXT: OpStore %[[#var_s]] %[[#res_s]]
+; CHECK-SPIRV-NEXT: OpReturn
+; CHECK-SPIRV-NEXT: OpFunctionEnd
+
+define spir_func %i8struct @test_builtin_smulextcc(i8 %a, i8 %b) {
+ entry:
+ %0 = call %i8struct @_Z20__spirv_SMulExtendedcc(i8 %a, i8 %b)
+ ret %i8struct %0
+}
+; CHECK-SPIRV: %[[#a:]] = OpFunctionParameter %[[#uchar]]
+; CHECK-SPIRV-NEXT: %[[#b:]] = OpFunctionParameter %[[#uchar]]
+; CHECK-SPIRV-NEXT: %[[#entry:]] = OpLabel
+; CHECK-SPIRV-NEXT: %[[#res:]] = OpSMulExtended %[[#i8struct]] %[[#a]] %[[#b]]
+; CHECK-SPIRV-NEXT: OpReturnValue %[[#res]]
+; CHECK-SPIRV-NEXT: OpFunctionEnd
+
+define spir_func %i16struct @test_builtin_smulextss(i16 %a, i16 %b) {
+ entry:
+ %0 = call %i16struct @_Z20__spirv_SMulExtendedss(i16 %a, i16 %b)
+ ret %i16struct %0
+}
+; CHECK-SPIRV: %[[#a_0:]] = OpFunctionParameter %[[#ushort]]
+; CHECK-SPIRV-NEXT: %[[#b_0:]] = OpFunctionParameter %[[#ushort]]
+; CHECK-SPIRV-NEXT: %[[#entry_0:]] = OpLabel
+; CHECK-SPIRV-NEXT: %[[#res_0:]] = OpSMulExtended %[[#i16struct]] %[[#a_0]] %[[#b_0]]
+; CHECK-SPIRV-NEXT: OpReturnValue %[[#res_0]]
+; CHECK-SPIRV-NEXT: OpFunctionEnd
+
+define spir_func %i32struct @test_builtin_smulextii(i32 %a, i32 %b) {
+ entry:
+ %0 = call %i32struct @_Z20__spirv_SMulExtendedii(i32 %a, i32 %b)
+ ret %i32struct %0
+}
+; CHECK-SPIRV: %[[#a_1:]] = OpFunctionParameter %[[#uint]]
+; CHECK-SPIRV-NEXT: %[[#b_1:]] = OpFunctionParameter %[[#uint]]
+; CHECK-SPIRV-NEXT: %[[#entry_1:]] = OpLabel
+; CHECK-SPIRV-NEXT: %[[#res_1:]] = OpSMulExtended %[[#i32struct]] %[[#a_1]] %[[#b_1]]
+; CHECK-SPIRV-NEXT: OpReturnValue %[[#res_1]]
+; CHECK-SPIRV-NEXT: OpFunctionEnd
+
+define spir_func %i64struct @test_builtin_smulextll(i64 %a, i64 %b) {
+ entry:
+ %0 = call %i64struct @_Z20__spirv_SMulExtendedll(i64 %a, i64 %b)
+ ret %i64struct %0
+}
+; CHECK-SPIRV: %[[#a_2:]] = OpFunctionParameter %[[#ulong]]
+; CHECK-SPIRV-NEXT: %[[#b_2:]] = OpFunctionParameter %[[#ulong]]
+; CHECK-SPIRV-NEXT: %[[#entry_2:]] = OpLabel
+; CHECK-SPIRV-NEXT: %[[#res_2:]] = OpSMulExtended %[[#i64struct]] %[[#a_2]] %[[#b_2]]
+; CHECK-SPIRV-NEXT: OpReturnValue %[[#res_2]]
+; CHECK-SPIRV-NEXT: OpFunctionEnd
+
+define spir_func %vecstruct @test_builtin_smulextDv4_xS_(<4 x i32> %a, <4 x i32> %b) {
+ entry:
+ %0 = call %vecstruct @_Z20__spirv_SMulExtendedDv4_iS_(<4 x i32> %a, <4 x i32> %b)
+ ret %vecstruct %0
+}
+; CHECK-SPIRV: %[[#a_3:]] = OpFunctionParameter %[[#v4uint]]
+; CHECK-SPIRV-NEXT: %[[#b_3:]] = OpFunctionParameter %[[#v4uint]]
+; CHECK-SPIRV-NEXT: %[[#entry_3:]] = OpLabel
+; CHECK-SPIRV-NEXT: %[[#res_3:]] = OpSMulExtended %[[#vecstruct]] %[[#a_3]] %[[#b_3]]
+; CHECK-SPIRV-NEXT: OpReturnValue %[[#res_3]]
+; CHECK-SPIRV-NEXT: OpFunctionEnd
+
+define spir_func %i32struct @test_builtin_smulext_same_arg(i32 %a) {
+ entry:
+ %0 = call %i32struct @_Z20__spirv_SMulExtendedii(i32 %a, i32 %a)
+ ret %i32struct %0
+}
+; CHECK-SPIRV: %[[#a_4:]] = OpFunctionParameter %[[#uint]]
+; CHECK-SPIRV-NEXT: %[[#entry_4:]] = OpLabel
+; CHECK-SPIRV-NEXT: %[[#res_4:]] = OpSMulExtended %[[#i32struct]] %[[#a_4]] %[[#a_4]]
+; CHECK-SPIRV-NEXT: OpReturnValue %[[#res_4]]
+; CHECK-SPIRV-NEXT: OpFunctionEnd
+
+declare %i8struct @_Z20__spirv_SMulExtendedcc(i8, i8)
+declare %i16struct @_Z20__spirv_SMulExtendedss(i16, i16)
+declare %i32struct @_Z20__spirv_SMulExtendedii(i32, i32)
+declare %i64struct @_Z20__spirv_SMulExtendedll(i64, i64)
+declare %vecstruct @_Z20__spirv_SMulExtendedDv4_iS_(<4 x i32>, <4 x i32>)
+declare void @__spirv_SMulExtended(ptr sret (%i32struct), i32, i32)
diff --git a/llvm/test/CodeGen/SPIRV/umulextended-builtin.ll b/llvm/test/CodeGen/SPIRV/umulextended-builtin.ll
new file mode 100644
index 0000000000000..f1405a03e9eab
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/umulextended-builtin.ll
@@ -0,0 +1,115 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+%i8struct = type {i8, i8}
+%i16struct = type {i16, i16}
+%i32struct = type {i32, i32}
+%i64struct = type {i64, i64}
+%vecstruct = type {<4 x i32>, <4 x i32>}
+
+; CHECK-SPIRV-DAG: %[[#uchar:]] = OpTypeInt 8
+; CHECK-SPIRV-DAG: %[[#ushort:]] = OpTypeInt 16
+; CHECK-SPIRV-DAG: %[[#uint:]] = OpTypeInt 32
+; CHECK-SPIRV-DAG: %[[#ulong:]] = OpTypeInt 64
+; CHECK-SPIRV-DAG: %[[#i8struct:]] = OpTypeStruct %[[#uchar]] %[[#uchar]]
+; CHECK-SPIRV-DAG: %[[#i16struct:]] = OpTypeStruct %[[#ushort]] %[[#ushort]]
+; CHECK-SPIRV-DAG: %[[#i32struct:]] = OpTypeStruct %[[#uint]] %[[#uint]]
+; CHECK-SPIRV-DAG: %[[#i64struct:]] = OpTypeStruct %[[#ulong]] %[[#ulong]]
+; CHECK-SPIRV-DAG: %[[#v4uint:]] = OpTypeVector %[[#uint]] 4
+; CHECK-SPIRV-DAG: %[[#vecstruct:]] = OpTypeStruct %[[#v4uint]] %[[#v4uint]]
+
+; The sret test is placed first because its unmangled name causes it to be
+; emitted before the mangled-name functions in the SPIR-V output.
+define spir_func void @test_builtin_umulext_sret(i32 %a, i32 %b) {
+ entry:
+ %0 = alloca %i32struct
+ call void @__spirv_UMulExtended(ptr sret (%i32struct) %0, i32 %a, i32 %b)
+ ret void
+}
+; CHECK-SPIRV: %[[#]] = OpFunction %[[#]] None %[[#]] ; -- Begin function test_builtin_umulext_sret
+; CHECK-SPIRV-NEXT: %[[#a_s:]] = OpFunctionParameter %[[#uint]]
+; CHECK-SPIRV-NEXT: %[[#b_s:]] = OpFunctionParameter %[[#uint]]
+; CHECK-SPIRV-NEXT: %[[#entry_s:]] = OpLabel
+; CHECK-SPIRV: %[[#var_s:]] = OpVariable %[[#_ptr_Function_i32struct:]] Function
+; CHECK-SPIRV: %[[#res_s:]] = OpUMulExtended %[[#i32struct]] %[[#a_s]] %[[#b_s]]
+; CHECK-SPIRV-NEXT: OpStore %[[#var_s]] %[[#res_s]]
+; CHECK-SPIRV-NEXT: OpReturn
+; CHECK-SPIRV-NEXT: OpFunctionEnd
+
+define spir_func %i8struct @test_builtin_umulextcc(i8 %a, i8 %b) {
+ entry:
+ %0 = call %i8struct @_Z20__spirv_UMulExtendedcc(i8 %a, i8 %b)
+ ret %i8struct %0
+}
+; CHECK-SPIRV: %[[#a:]] = OpFunctionParameter %[[#uchar]]
+; CHECK-SPIRV-NEXT: %[[#b:]] = OpFunctionParameter %[[#uchar]]
+; CHECK-SPIRV-NEXT: %[[#entry:]] = OpLabel
+; CHECK-SPIRV-NEXT: %[[#res:]] = OpUMulExtended %[[#i8struct]] %[[#a]] %[[#b]]
+; CHECK-SPIRV-NEXT: OpReturnValue %[[#res]]
+; CHECK-SPIRV-NEXT: OpFunctionEnd
+
+define spir_func %i16struct @test_builtin_umulextss(i16 %a, i16 %b) {
+ entry:
+ %0 = call %i16struct @_Z20__spirv_UMulExtendedss(i16 %a, i16 %b)
+ ret %i16struct %0
+}
+; CHECK-SPIRV: %[[#a_0:]] = OpFunctionParameter %[[#ushort]]
+; CHECK-SPIRV-NEXT: %[[#b_0:]] = OpFunctionParameter %[[#ushort]]
+; CHECK-SPIRV-NEXT: %[[#entry_0:]] = OpLabel
+; CHECK-SPIRV-NEXT: %[[#res_0:]] = OpUMulExtended %[[#i16struct]] %[[#a_0]] %[[#b_0]]
+; CHECK-SPIRV-NEXT: OpReturnValue %[[#res_0]]
+; CHECK-SPIRV-NEXT: OpFunctionEnd
+
+define spir_func %i32struct @test_builtin_umulextii(i32 %a, i32 %b) {
+ entry:
+ %0 = call %i32struct @_Z20__spirv_UMulExtendedii(i32 %a, i32 %b)
+ ret %i32struct %0
+}
+; CHECK-SPIRV: %[[#a_1:]] = OpFunctionParameter %[[#uint]]
+; CHECK-SPIRV-NEXT: %[[#b_1:]] = OpFunctionParameter %[[#uint]]
+; CHECK-SPIRV-NEXT: %[[#entry_1:]] = OpLabel
+; CHECK-SPIRV-NEXT: %[[#res_1:]] = OpUMulExtended %[[#i32struct]] %[[#a_1]] %[[#b_1]]
+; CHECK-SPIRV-NEXT: OpReturnValue %[[#res_1]]
+; CHECK-SPIRV-NEXT: OpFunctionEnd
+
+define spir_func %i64struct @test_builtin_umulextll(i64 %a, i64 %b) {
+ entry:
+ %0 = call %i64struct @_Z20__spirv_UMulExtendedll(i64 %a, i64 %b)
+ ret %i64struct %0
+}
+; CHECK-SPIRV: %[[#a_2:]] = OpFunctionParameter %[[#ulong]]
+; CHECK-SPIRV-NEXT: %[[#b_2:]] = OpFunctionParameter %[[#ulong]]
+; CHECK-SPIRV-NEXT: %[[#entry_2:]] = OpLabel
+; CHECK-SPIRV-NEXT: %[[#res_2:]] = OpUMulExtended %[[#i64struct]] %[[#a_2]] %[[#b_2]]
+; CHECK-SPIRV-NEXT: OpReturnValue %[[#res_2]]
+; CHECK-SPIRV-NEXT: OpFunctionEnd
+
+define spir_func %vecstruct @test_builtin_umulextDv4_xS_(<4 x i32> %a, <4 x i32> %b) {
+ entry:
+ %0 = call %vecstruct @_Z20__spirv_UMulExtendedDv4_iS_(<4 x i32> %a, <4 x i32> %b)
+ ret %vecstruct %0
+}
+; CHECK-SPIRV: %[[#a_3:]] = OpFunctionParameter %[[#v4uint]]
+; CHECK-SPIRV-NEXT: %[[#b_3:]] = OpFunctionParameter %[[#v4uint]]
+; CHECK-SPIRV-NEXT: %[[#entry_3:]] = OpLabel
+; CHECK-SPIRV-NEXT: %[[#res_3:]] = OpUMulExtended %[[#vecstruct]] %[[#a_3]] %[[#b_3]]
+; CHECK-SPIRV-NEXT: OpReturnValue %[[#res_3]]
+; CHECK-SPIRV-NEXT: OpFunctionEnd
+
+define spir_func %i32struct @test_builtin_umulext_same_arg(i32 %a) {
+ entry:
+ %0 = call %i32struct @_Z20__spirv_UMulExtendedii(i32 %a, i32 %a)
+ ret %i32struct %0
+}
+; CHECK-SPIRV: %[[#a_4:]] = OpFunctionParameter %[[#uint]]
+; CHECK-SPIRV-NEXT: %[[#entry_4:]] = OpLabel
+; CHECK-SPIRV-NEXT: %[[#res_4:]] = OpUMulExtended %[[#i32struct]] %[[#a_4]] %[[#a_4]]
+; CHECK-SPIRV-NEXT: OpReturnValue %[[#res_4]]
+; CHECK-SPIRV-NEXT: OpFunctionEnd
+
+declare %i8struct @_Z20__spirv_UMulExtendedcc(i8, i8)
+declare %i16struct @_Z20__spirv_UMulExtendedss(i16, i16)
+declare %i32struct @_Z20__spirv_UMulExtendedii(i32, i32)
+declare %i64struct @_Z20__spirv_UMulExtendedll(i64, i64)
+declare %vecstruct @_Z20__spirv_UMulExtendedDv4_iS_(<4 x i32>, <4 x i32>)
+declare void @__spirv_UMulExtended(ptr sret (%i32struct), i32, i32)
More information about the llvm-commits
mailing list