[llvm] [SPIR-V] Add OpSMulExtended and OpUMulExtended builtin support (PR #187474)
Arseniy Obolenskiy via llvm-commits
llvm-commits at lists.llvm.org
Fri Mar 27 09:35:12 PDT 2026
================
@@ -1996,6 +1996,97 @@ static bool generateICarryBorrowInst(const SPIRV::IncomingCall *Call,
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;
+ 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 {
+ 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");
+
+ MachineRegisterInfo *MRI = MIRBuilder.getMRI();
+
+ if (IsSret) {
+ Register ResReg = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
+ if (const TargetRegisterClass *DstRC = MRI->getRegClassOrNull(Op1Reg)) {
+ MRI->setRegClass(ResReg, DstRC);
+ MRI->setType(ResReg, MRI->getType(Op1Reg));
+ } else {
+ MRI->setType(ResReg, LLT::scalar(64));
----------------
aobolensk wrote:
Yes, that's cumbersome. I don't think it is legit either. I wanted to take a look at their unification separately from this PR, but yes, probably it makes sense to just remove it
https://github.com/llvm/llvm-project/pull/187474
More information about the llvm-commits
mailing list