Skip to content

Conversation

@EbinJose2002
Copy link
Contributor

  • Added lowering for llvm intrinsic sadd.with.overflow
  • Used the logic that overflow occurs when both operands are greater than zero and sum is less than any of the operands.
  • Likewise overflow occurs when both operands are less than zero and sum is greater than any of the operands.
  • Used a series of logical and comparison operations for this

 - Added lowering for llvm intrinsic sadd.with.overflow
 - Used the logic that overflow occurs when both operands are greater than zero and sum is less than any of the operands.
 - Likewise overflow occurs when both operands are less than zero and sum is greater than any of the operands.
@github-actions
Copy link

github-actions bot commented Mar 3, 2025

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot
Copy link
Member

llvmbot commented Mar 3, 2025

@llvm/pr-subscribers-backend-spir-v

Author: None (EbinJose2002)

Changes
  • Added lowering for llvm intrinsic sadd.with.overflow
  • Used the logic that overflow occurs when both operands are greater than zero and sum is less than any of the operands.
  • Likewise overflow occurs when both operands are less than zero and sum is greater than any of the operands.
  • Used a series of logical and comparison operations for this

Full diff: https://github.com/llvm/llvm-project/pull/129475.diff

2 Files Affected:

  • (modified) llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp (+184-1)
  • (added) llvm/test/CodeGen/SPIRV/llvm-intrinsics/sadd.with.overflow.ll (+161)
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index c52b67e72a88c..8f6efc3b5f43c 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -202,6 +202,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
   bool selectOverflowArith(Register ResVReg, const SPIRVType *ResType,
                            MachineInstr &I, unsigned Opcode) const;
 
+  bool selectSignedOverflowArith(Register ResVReg, const SPIRVType *ResType,
+                           MachineInstr &I, bool isVector) const;
+
   bool selectIntegerDot(Register ResVReg, const SPIRVType *ResType,
                         MachineInstr &I, bool Signed) const;
 
@@ -509,7 +512,6 @@ static bool mayApplyGenericSelection(unsigned Opcode) {
   switch (Opcode) {
   case TargetOpcode::G_CONSTANT:
     return false;
-  case TargetOpcode::G_SADDO:
   case TargetOpcode::G_SSUBO:
     return true;
   }
@@ -728,6 +730,11 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
                                ResType->getOpcode() == SPIRV::OpTypeVector
                                    ? SPIRV::OpIAddCarryV
                                    : SPIRV::OpIAddCarryS);
+  case TargetOpcode::G_SADDO:
+    return selectSignedOverflowArith(ResVReg, ResType, I,
+                               ResType->getOpcode() == SPIRV::OpTypeVector
+                                   ? true
+                                   : false);
   case TargetOpcode::G_USUBO:
     return selectOverflowArith(ResVReg, ResType, I,
                                ResType->getOpcode() == SPIRV::OpTypeVector
@@ -1376,6 +1383,182 @@ bool SPIRVInstructionSelector::selectOverflowArith(Register ResVReg,
                        .constrainAllUses(TII, TRI, RBI);
 }
 
+bool SPIRVInstructionSelector::selectSignedOverflowArith(Register ResVReg,
+                                                        const SPIRVType *ResType, 
+                                                        MachineInstr &I,
+                                                        bool isVector) const { 
+
+
+//Checking overflow based on the logic that if two operands are positive and the sum is 
+//less than one of the operands then an overflow occured. Likewise if two operands are
+//negative and if sum is greater than one operand then also overflow occured.
+
+  Type *ResTy = nullptr;
+  StringRef ResName;
+  MachineIRBuilder MIRBuilder(I);
+  if (!GR.findValueAttrs(&I, ResTy, ResName))
+    report_fatal_error(
+        "Not enough info to select the signed arithmetic instruction");
+  if (!ResTy || !ResTy->isStructTy())
+    report_fatal_error(
+      "Expect struct type result for the signed arithmetic instruction");
+  
+  StructType *ResStructTy = cast<StructType>(ResTy);
+  Type *ResElemTy = ResStructTy->getElementType(0);
+  Type *OverflowTy = ResStructTy->getElementType(1);
+  ResTy = StructType::get(ResElemTy, OverflowTy);
+  SPIRVType *StructType = GR.getOrCreateSPIRVType(
+    ResTy, MIRBuilder, SPIRV::AccessQualifier::ReadWrite, false);
+  if (!StructType) {
+    report_fatal_error("Failed to create SPIR-V type for struct");
+  }
+  SPIRVType *BoolType = GR.getOrCreateSPIRVBoolType(I, TII);
+  unsigned N = GR.getScalarOrVectorComponentCount(ResType);
+  if (N > 1)
+    BoolType = GR.getOrCreateSPIRVVectorType(BoolType, N, I, TII);
+  Register BoolTypeReg = GR.getSPIRVTypeID(BoolType);
+  Register ZeroReg = buildZerosVal(ResType, I);
+  Register StructVReg = MRI->createGenericVirtualRegister(LLT::scalar(64));
+  MRI->setRegClass(StructVReg, &SPIRV::IDRegClass);
+  
+  if (ResName.size() > 0)
+    buildOpName(StructVReg, ResName, MIRBuilder);
+  
+  MachineBasicBlock &BB = *I.getParent();
+  Register SumVReg = MRI->createGenericVirtualRegister(LLT::scalar(64));
+  MRI->setRegClass(SumVReg, &SPIRV::IDRegClass);
+  SPIRVType *IntType = GR.getOrCreateSPIRVType(ResElemTy,MIRBuilder,SPIRV::AccessQualifier::ReadWrite,true);
+
+  auto SumMIB = BuildMI(BB, MIRBuilder.getInsertPt(), I.getDebugLoc(), TII.get(isVector ? SPIRV::OpIAddV : SPIRV::OpIAddS))
+                    .addDef(SumVReg)
+                    .addUse(GR.getSPIRVTypeID(IntType));
+  for (unsigned i = I.getNumDefs(); i < I.getNumOperands(); ++i)
+    SumMIB.addUse(I.getOperand(i).getReg());
+  bool Result = SumMIB.constrainAllUses(TII, TRI, RBI);
+
+  Register OverflowVReg = MRI->createGenericVirtualRegister(LLT::scalar(1));
+  MRI->setRegClass(OverflowVReg, &SPIRV::IDRegClass);
+  unsigned i = I.getNumDefs();
+
+  Register posCheck1 = MRI->createGenericVirtualRegister(LLT::scalar(1));
+  MRI->setRegClass(posCheck1, &SPIRV::IDRegClass);
+  Register posCheck2 = MRI->createGenericVirtualRegister(LLT::scalar(1));
+  MRI->setRegClass(posCheck2, &SPIRV::IDRegClass);
+  Register posCheck3 = MRI->createGenericVirtualRegister(LLT::scalar(1));
+  MRI->setRegClass(posCheck3, &SPIRV::IDRegClass);
+  Register posOverflow = MRI->createGenericVirtualRegister(LLT::scalar(1));
+  MRI->setRegClass(posOverflow, &SPIRV::IDRegClass);
+  Register posOverflowCheck = MRI->createGenericVirtualRegister(LLT::scalar(1));
+  MRI->setRegClass(posOverflowCheck, &SPIRV::IDRegClass);
+  
+  BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpSGreaterThan))
+        .addDef(posCheck1)
+        .addUse(GR.getSPIRVTypeID(BoolType))
+        .addUse(I.getOperand(i).getReg())
+        .addUse(ZeroReg);
+  BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpSGreaterThan))
+        .addDef(posCheck2)
+        .addUse(GR.getSPIRVTypeID(BoolType))
+        .addUse(I.getOperand(i+1).getReg())
+        .addUse(ZeroReg);
+  BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpSLessThan))
+        .addDef(posCheck3)
+        .addUse(GR.getSPIRVTypeID(BoolType))
+        .addUse(SumVReg)
+        .addUse(I.getOperand(i+1).getReg());
+  BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpLogicalAnd))
+        .addDef(posOverflow)
+        .addUse(GR.getSPIRVTypeID(BoolType))
+        .addUse(posCheck1)
+        .addUse(posCheck2);
+  BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpLogicalAnd))
+        .addDef(posOverflowCheck)
+        .addUse(GR.getSPIRVTypeID(BoolType))
+        .addUse(posOverflow)
+        .addUse(posCheck3);
+
+  Register negCheck1 = MRI->createGenericVirtualRegister(LLT::scalar(1));
+  MRI->setRegClass(negCheck1, &SPIRV::IDRegClass);
+  Register negCheck2 = MRI->createGenericVirtualRegister(LLT::scalar(1));
+  MRI->setRegClass(negCheck2, &SPIRV::IDRegClass);
+  Register negCheck3 = MRI->createGenericVirtualRegister(LLT::scalar(1));
+  MRI->setRegClass(negCheck3, &SPIRV::IDRegClass);
+  Register negOverflow = MRI->createGenericVirtualRegister(LLT::scalar(1));
+  MRI->setRegClass(negOverflow, &SPIRV::IDRegClass);
+  Register negOverflowCheck = MRI->createGenericVirtualRegister(LLT::scalar(1));
+  MRI->setRegClass(negOverflowCheck, &SPIRV::IDRegClass);
+  
+  BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpSLessThan))
+        .addDef(negCheck1)
+        .addUse(GR.getSPIRVTypeID(BoolType))
+        .addUse(I.getOperand(i).getReg())
+        .addUse(ZeroReg);
+  BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpSLessThan))
+        .addDef(negCheck2)
+        .addUse(GR.getSPIRVTypeID(BoolType))
+        .addUse(I.getOperand(i+1).getReg())
+        .addUse(ZeroReg);
+  BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpSGreaterThan))
+        .addDef(negCheck3)
+        .addUse(GR.getSPIRVTypeID(BoolType))
+        .addUse(SumVReg)
+        .addUse(I.getOperand(i+1).getReg());
+  BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpLogicalAnd))
+        .addDef(negOverflow)
+        .addUse(GR.getSPIRVTypeID(BoolType))
+        .addUse(negCheck1)
+        .addUse(negCheck2);
+  BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpLogicalAnd))
+        .addDef(negOverflowCheck)
+        .addUse(GR.getSPIRVTypeID(BoolType))
+        .addUse(negOverflow)
+        .addUse(negCheck3);
+
+  BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpLogicalOr))
+        .addDef(OverflowVReg)
+        .addUse(GR.getSPIRVTypeID(BoolType))
+        .addUse(negOverflowCheck)
+        .addUse(posOverflowCheck);
+            
+  // Construct the result struct containing sum and overflow flag
+  BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpCompositeConstruct))
+        .addDef(StructVReg)
+        .addUse(GR.getSPIRVTypeID(StructType))
+        .addUse(SumVReg)
+        .addUse(OverflowVReg);
+
+  Register HigherVReg = MRI->createGenericVirtualRegister(LLT::scalar(64));
+  MRI->setRegClass(HigherVReg, &SPIRV::iIDRegClass);
+  
+  for (unsigned i = 0; i < I.getNumDefs(); ++i) {
+    auto MIB =
+        BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpCompositeExtract))
+            .addDef(i == 1 ? HigherVReg : I.getOperand(i).getReg())
+            .addUse(i == 1 ? GR.getSPIRVTypeID(BoolType) : GR.getSPIRVTypeID(ResType))
+            .addUse(StructVReg)
+            .addImm(i);
+    Result &= MIB.constrainAllUses(TII, TRI, RBI);
+  }
+  Register FalseReg = MRI->createGenericVirtualRegister(LLT::scalar(1));
+  MRI->setRegClass(FalseReg, &SPIRV::IDRegClass);
+
+  // Use OpConstantFalse to initialize it
+  BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpConstantNull))
+      .addDef(FalseReg)
+      .addUse(GR.getSPIRVTypeID(BoolType));
+
+  BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpLogicalNotEqual))
+              .addDef(I.getOperand(1).getReg())
+              .addUse(BoolTypeReg)
+              .addUse(HigherVReg)
+              .addUse(FalseReg)
+              .constrainAllUses(TII, TRI, RBI);
+  return true;
+
+
+
+}
+
 bool SPIRVInstructionSelector::selectAtomicCmpXchg(Register ResVReg,
                                                    const SPIRVType *ResType,
                                                    MachineInstr &I) const {
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/sadd.with.overflow.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/sadd.with.overflow.ll
new file mode 100644
index 0000000000000..a2ccfa1d11523
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/sadd.with.overflow.ll
@@ -0,0 +1,161 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+;===---------------------------------------------------------------------===//
+; Type definitions.
+; CHECK-DAG: %[[I16:.*]] = OpTypeInt 16 0
+; CHECK-DAG: %[[Bool:.*]] = OpTypeBool
+; CHECK-DAG: %[[I32:.*]] = OpTypeInt 32 0
+; CHECK-DAG: %[[I64:.*]] = OpTypeInt 64 0
+; CHECK-DAG: %[[PtrI16:.*]] = OpTypePointer Function %[[I16]]
+; CHECK-DAG: %[[PtrI32:.*]] = OpTypePointer Function %[[I32]]
+; CHECK-DAG: %[[PtrI64:.*]] = OpTypePointer Function %[[I64]]
+; CHECK-DAG: %[[StructI16:.*]] = OpTypeStruct %[[I16]] %[[Bool]]
+; CHECK-DAG: %[[StructI32:.*]] = OpTypeStruct %[[I32]] %[[Bool]]
+; CHECK-DAG: %[[StructI64:.*]] = OpTypeStruct %[[I64]] %[[Bool]]
+; CHECK-DAG: %[[ZeroI16:.*]] = OpConstant %[[I16]] 0
+; CHECK-DAG: %[[ZeroI32:.*]] = OpConstant %[[I32]] 0
+; CHECK-DAG: %[[ZeroI64:.*]] = OpConstantNull %[[I64]]
+; CHECK-DAG: %[[V4I32:.*]] = OpTypeVector %[[I32]] 4
+; CHECK-DAG: %[[V4Bool:.*]] = OpTypeVector %[[Bool]] 4
+; CHECK-DAG: %[[PtrV4I32:.*]] = OpTypePointer Function %[[V4I32]]
+; CHECK-DAG: %[[StructV4I32:.*]] = OpTypeStruct %[[V4I32]] %[[V4Bool]]
+; CHECK-DAG: %[[ZeroV4I32:.*]] = OpConstantNull %[[V4I32]]
+;===---------------------------------------------------------------------===//
+; Function for i16 sadd.with.overflow.
+; CHECK: OpFunction
+; CHECK: %[[A16:.*]] = OpFunctionParameter %[[I16]]
+; CHECK: %[[B16:.*]] = OpFunctionParameter %[[I16]]
+; CHECK: %[[Ptr16:.*]] = OpFunctionParameter %[[PtrI16]]
+; CHECK: %[[Sum16:.*]] = OpIAdd %[[I16]] %[[A16]] %[[B16]]
+; CHECK: %[[PosCmp16_1:.*]] = OpSGreaterThan %[[Bool]] %[[A16]] %[[ZeroI16]]
+; CHECK: %[[PosCmp16_2:.*]] = OpSGreaterThan %[[Bool]] %[[B16]] %[[ZeroI16]]
+; CHECK: %[[PosCmp16_3:.*]] = OpSLessThan %[[Bool]] %[[Sum16]] %[[B16]]
+; CHECK: %[[PosCond16:.*]] = OpLogicalAnd %[[Bool]] %[[PosCmp16_1]] %[[PosCmp16_2]]
+; CHECK: %[[PosOverflow16:.*]] = OpLogicalAnd %[[Bool]] %[[PosCond16]] %[[PosCmp16_3]]
+; CHECK: %[[NegCmp16_1:.*]] = OpSLessThan %[[Bool]] %[[A16]] %[[ZeroI16]]
+; CHECK: %[[NegCmp16_2:.*]] = OpSLessThan %[[Bool]] %[[B16]] %[[ZeroI16]]
+; CHECK: %[[NegCmp16_3:.*]] = OpSGreaterThan %[[Bool]] %[[Sum16]] %[[B16]]
+; CHECK: %[[NegCond16:.*]] = OpLogicalAnd %[[Bool]] %[[NegCmp16_1]] %[[NegCmp16_2]]
+; CHECK: %[[NegOverflow16:.*]] = OpLogicalAnd %[[Bool]] %[[NegCond16]] %[[NegCmp16_3]]
+; CHECK: %[[Overflow16:.*]] = OpLogicalOr %[[Bool]] %[[NegOverflow16]] %[[PosOverflow16]]
+; CHECK: %[[Comp16:.*]] = OpCompositeConstruct %[[StructI16]] %[[Sum16]] %[[Overflow16]]
+; CHECK: %[[ExtOver16:.*]] = OpCompositeExtract %[[Bool]] %[[Comp16]] 1
+; CHECK: %[[Final16:.*]] = OpLogicalNotEqual %[[Bool]] %[[ExtOver16]] %[[#]]
+; CHECK: OpReturn
+define spir_func void @smulo_i16(i16 %a, i16 %b, ptr nocapture %c) {
+entry:
+  %umul = tail call { i16, i1 } @llvm.sadd.with.overflow.i16(i16 %a, i16 %b)
+  %cmp = extractvalue { i16, i1 } %umul, 1
+  %umul.value = extractvalue { i16, i1 } %umul, 0
+  %storemerge = select i1 %cmp, i16 0, i16 %umul.value
+  store i16 %storemerge, ptr %c, align 1
+  ret void
+}
+
+;===---------------------------------------------------------------------===//
+; Function for i32 sadd.with.overflow.
+; CHECK: OpFunction
+; CHECK: %[[A32:.*]] = OpFunctionParameter %[[I32]]
+; CHECK: %[[B32:.*]] = OpFunctionParameter %[[I32]]
+; CHECK: %[[Ptr32:.*]] = OpFunctionParameter %[[PtrI32]]
+; CHECK: %[[Sum32:.*]] = OpIAdd %[[I32]] %[[A32]] %[[B32]]
+; CHECK: %[[PosCmp32_1:.*]] = OpSGreaterThan %[[Bool]] %[[A32]] %[[ZeroI32]]
+; CHECK: %[[PosCmp32_2:.*]] = OpSGreaterThan %[[Bool]] %[[B32]] %[[ZeroI32]]
+; CHECK: %[[PosCmp32_3:.*]] = OpSLessThan %[[Bool]] %[[Sum32]] %[[B32]]
+; CHECK: %[[PosCond32:.*]] = OpLogicalAnd %[[Bool]] %[[PosCmp32_1]] %[[PosCmp32_2]]
+; CHECK: %[[PosOverflow32:.*]] = OpLogicalAnd %[[Bool]] %[[PosCond32]] %[[PosCmp32_3]]
+; CHECK: %[[NegCmp32_1:.*]] = OpSLessThan %[[Bool]] %[[A32]] %[[ZeroI32]]
+; CHECK: %[[NegCmp32_2:.*]] = OpSLessThan %[[Bool]] %[[B32]] %[[ZeroI32]]
+; CHECK: %[[NegCmp32_3:.*]] = OpSGreaterThan %[[Bool]] %[[Sum32]] %[[B32]]
+; CHECK: %[[NegCond32:.*]] = OpLogicalAnd %[[Bool]] %[[NegCmp32_1]] %[[NegCmp32_2]]
+; CHECK: %[[NegOverflow32:.*]] = OpLogicalAnd %[[Bool]] %[[NegCond32]] %[[NegCmp32_3]]
+; CHECK: %[[Overflow32:.*]] = OpLogicalOr %[[Bool]] %[[NegOverflow32]] %[[PosOverflow32]]
+; CHECK: %[[Comp32:.*]] = OpCompositeConstruct %[[StructI32]] %[[Sum32]] %[[Overflow32]]
+; CHECK: %[[ExtOver32:.*]] = OpCompositeExtract %[[Bool]] %[[Comp32]] 1
+; CHECK: %[[Final32:.*]] = OpLogicalNotEqual %[[Bool]] %[[ExtOver32]] %[[#]]
+; CHECK: OpReturn
+define spir_func void @smulo_i32(i32 %a, i32 %b, ptr nocapture %c) {
+entry:
+  %umul = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %a, i32 %b)
+  %cmp = extractvalue { i32, i1 } %umul, 1
+  %umul.value = extractvalue { i32, i1 } %umul, 0
+  %storemerge = select i1 %cmp, i32 0, i32 %umul.value
+  store i32 %storemerge, ptr %c, align 4
+  ret void
+}
+
+;===---------------------------------------------------------------------===//
+; Function for i64 sadd.with.overflow.
+; CHECK: OpFunction
+; CHECK: %[[A64:.*]] = OpFunctionParameter %[[I64]]
+; CHECK: %[[B64:.*]] = OpFunctionParameter %[[I64]]
+; CHECK: %[[Ptr64:.*]] = OpFunctionParameter %[[PtrI64]]
+; CHECK: %[[Sum64:.*]] = OpIAdd %[[I64]] %[[A64]] %[[B64]]
+; CHECK: %[[PosCmp64_1:.*]] = OpSGreaterThan %[[Bool]] %[[A64]] %[[ZeroI64]]
+; CHECK: %[[PosCmp64_2:.*]] = OpSGreaterThan %[[Bool]] %[[B64]] %[[ZeroI64]]
+; CHECK: %[[PosCmp64_3:.*]] = OpSLessThan %[[Bool]] %[[Sum64]] %[[B64]]
+; CHECK: %[[PosCond64:.*]] = OpLogicalAnd %[[Bool]] %[[PosCmp64_1]] %[[PosCmp64_2]]
+; CHECK: %[[PosOverflow64:.*]] = OpLogicalAnd %[[Bool]] %[[PosCond64]] %[[PosCmp64_3]]
+; CHECK: %[[NegCmp64_1:.*]] = OpSLessThan %[[Bool]] %[[A64]] %[[ZeroI64]]
+; CHECK: %[[NegCmp64_2:.*]] = OpSLessThan %[[Bool]] %[[B64]] %[[ZeroI64]]
+; CHECK: %[[NegCmp64_3:.*]] = OpSGreaterThan %[[Bool]] %[[Sum64]] %[[B64]]
+; CHECK: %[[NegCond64:.*]] = OpLogicalAnd %[[Bool]] %[[NegCmp64_1]] %[[NegCmp64_2]]
+; CHECK: %[[NegOverflow64:.*]] = OpLogicalAnd %[[Bool]] %[[NegCond64]] %[[NegCmp64_3]]
+; CHECK: %[[Overflow64:.*]] = OpLogicalOr %[[Bool]] %[[NegOverflow64]] %[[PosOverflow64]]
+; CHECK: %[[Comp64:.*]] = OpCompositeConstruct %[[StructI64]] %[[Sum64]] %[[Overflow64]]
+; CHECK: %[[ExtOver64:.*]] = OpCompositeExtract %[[Bool]] %[[Comp64]] 1
+; CHECK: %[[Final64:.*]] = OpLogicalNotEqual %[[Bool]] %[[ExtOver64]] %[[#]]
+; CHECK: OpReturn
+define spir_func void @smulo_i64(i64 %a, i64 %b, ptr nocapture %c) {
+entry:
+  %umul = tail call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %a, i64 %b)
+  %cmp = extractvalue { i64, i1 } %umul, 1
+  %umul.value = extractvalue { i64, i1 } %umul, 0
+  %storemerge = select i1 %cmp, i64 0, i64 %umul.value
+  store i64 %storemerge, ptr %c, align 8
+  ret void
+}
+
+;===---------------------------------------------------------------------===//
+; Function for vector (4 x i32) sadd.with.overflow.
+
+; CHECK: OpFunction
+; CHECK: %[[A4:.*]] = OpFunctionParameter %[[V4I32]]
+; CHECK: %[[B4:.*]] = OpFunctionParameter %[[V4I32]]
+; CHECK: %[[Ptr4:.*]] = OpFunctionParameter %[[PtrV4I32]]
+; CHECK: %[[Sum4:.*]] = OpIAdd %[[V4I32]] %[[A4]] %[[B4]]
+; CHECK: %[[PosCmp4_1:.*]] = OpSGreaterThan %[[V4Bool]] %[[A4]] %[[ZeroV4I32]]
+; CHECK: %[[PosCmp4_2:.*]] = OpSGreaterThan %[[V4Bool]] %[[B4]] %[[ZeroV4I32]]
+; CHECK: %[[PosCmp4_3:.*]] = OpSLessThan %[[V4Bool]] %[[Sum4]] %[[B4]]
+; CHECK: %[[PosCond4:.*]] = OpLogicalAnd %[[V4Bool]] %[[PosCmp4_1]] %[[PosCmp4_2]]
+; CHECK: %[[PosOverflow4:.*]] = OpLogicalAnd %[[V4Bool]] %[[PosCond4]] %[[PosCmp4_3]]
+; CHECK: %[[NegCmp4_1:.*]] = OpSLessThan %[[V4Bool]] %[[A4]] %[[ZeroV4I32]]
+; CHECK: %[[NegCmp4_2:.*]] = OpSLessThan %[[V4Bool]] %[[B4]] %[[ZeroV4I32]]
+; CHECK: %[[NegCmp4_3:.*]] = OpSGreaterThan %[[V4Bool]] %[[Sum4]] %[[B4]]
+; CHECK: %[[NegCond4:.*]] = OpLogicalAnd %[[V4Bool]] %[[NegCmp4_1]] %[[NegCmp4_2]]
+; CHECK: %[[NegOverflow4:.*]] = OpLogicalAnd %[[V4Bool]] %[[NegCond4]] %[[NegCmp4_3]]
+; CHECK: %[[Overflow4:.*]] = OpLogicalOr %[[V4Bool]] %[[NegOverflow4]] %[[PosOverflow4]]
+; CHECK: %[[Comp4:.*]] = OpCompositeConstruct %[[StructV4I32]] %[[Sum4]] %[[Overflow4]]
+; CHECK: %[[ExtOver4:.*]] = OpCompositeExtract %[[V4Bool]] %[[Comp4]] 1
+; CHECK: %[[Final4:.*]] = OpLogicalNotEqual %[[V4Bool]] %[[ExtOver4]] %[[#]]
+; CHECK: OpReturn
+define spir_func void @smulo_v4i32(<4 x i32> %a, <4 x i32> %b, ptr nocapture %c) {
+entry:
+  %umul = tail call { <4 x i32>, <4 x i1> } @llvm.sadd.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b)
+  %cmp = extractvalue { <4 x i32>, <4 x i1> } %umul, 1
+  %umul.value = extractvalue { <4 x i32>, <4 x i1> } %umul, 0
+  %storemerge = select <4 x i1> %cmp, <4 x i32> zeroinitializer, <4 x i32> %umul.value
+  store <4 x i32> %storemerge, ptr %c, align 16
+  ret void
+}
+
+;===---------------------------------------------------------------------===//
+; Declarations of the intrinsics.
+declare { i16, i1 } @llvm.sadd.with.overflow.i16(i16, i16)
+declare { i32, i1 } @llvm.sadd.with.overflow.i32(i32, i32)
+declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64)
+declare { <4 x i32>, <4 x i1> } @llvm.sadd.with.overflow.v4i32(<4 x i32>, <4 x i32>)

@efriedma-quic
Copy link
Collaborator

See discussion on #125805

@github-actions
Copy link

github-actions bot commented Mar 3, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff e42ab4c54eca0e792a0ae461481f9acbd0260363 1dfb08ce291171de681c35905b874351569bafca --extensions cpp -- llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
View the diff from clang-format here.
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index dce53b07b5..31749c45d6 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -203,7 +203,7 @@ private:
                            MachineInstr &I, unsigned Opcode) const;
 
   bool selectSignedOverflowArith(Register ResVReg, const SPIRVType *ResType,
-                           MachineInstr &I, bool isVector) const;
+                                 MachineInstr &I, bool isVector) const;
 
   bool selectIntegerDot(Register ResVReg, const SPIRVType *ResType,
                         MachineInstr &I, bool Signed) const;
@@ -1535,7 +1535,7 @@ bool SPIRVInstructionSelector::selectSignedOverflowArith(
         BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpCompositeExtract))
             .addDef(i == 1 ? HigherVReg : I.getOperand(i).getReg())
             .addUse(i == 1 ? GR.getSPIRVTypeID(BoolType)
-                          : GR.getSPIRVTypeID(ResType))
+                           : GR.getSPIRVTypeID(ResType))
             .addUse(StructVReg)
             .addImm(i);
     Result &= MIB.constrainAllUses(TII, TRI, RBI);

 - Formatted the code as per clang format
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants