[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