[llvm-branch-commits] [llvm] [M68k] Use UADDO_CARRY/USUBO_CARRY instead of ADDC/ADDE/SUBC/SUBE. (PR #235569)

Craig Topper via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sun Oct 25 19:36:53 PDT 2026


https://github.com/topperc created https://github.com/llvm/llvm-project/pull/235569

Type legalization now expands wide add/sub using UADDO/USUBO and
UADDO_CARRY/USUBO_CARRY, which are lowered to M68kISD::ADD/SUB and
M68kISD::ADDX/SUBX with the carry passed through CCR.

ADDX/SUBX no longer receive a glue operand, so their verifyTargetNode
waivers can be removed.

This improves the code for i64 uadd/usub.with.overflow. Previously the
overflow bit was computed by redoing the subtraction, and usubo also
saved and restored CCR.

Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>

>From 1a1cbff67257ac041cecd20962a4311b91cef1e1 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Sun, 25 Oct 2026 19:05:04 -0700
Subject: [PATCH 1/2] [M68k] Add i64 uadd/usub.with.overflow tests. NFC

Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>
---
 .../CodeGen/M68k/Arith/add-with-overflow.ll   | 30 +++++++++++++++++
 .../CodeGen/M68k/Arith/sub-with-overflow.ll   | 33 +++++++++++++++++++
 2 files changed, 63 insertions(+)

diff --git a/llvm/test/CodeGen/M68k/Arith/add-with-overflow.ll b/llvm/test/CodeGen/M68k/Arith/add-with-overflow.ll
index 4f9eb785859596..a98f7b4fb02dce 100644
--- a/llvm/test/CodeGen/M68k/Arith/add-with-overflow.ll
+++ b/llvm/test/CodeGen/M68k/Arith/add-with-overflow.ll
@@ -73,3 +73,33 @@ entry:
   %obit = extractvalue {i32, i1} %t, 1
   ret i1 %obit
 }
+
+define {i64, i1} @uaddo_i64(i64 %v1, i64 %v2) nounwind {
+; CHECK-LABEL: uaddo_i64:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    suba.l #8, %sp
+; CHECK-NEXT:    movem.l %d2-%d3, (0,%sp) ; 12-byte Folded Spill
+; CHECK-NEXT:    move.l (12,%sp), %a0
+; CHECK-NEXT:    move.l (24,%sp), %d0
+; CHECK-NEXT:    move.l (16,%sp), %d1
+; CHECK-NEXT:    move.l (20,%sp), %d2
+; CHECK-NEXT:    move.l %d2, %d3
+; CHECK-NEXT:    add.l (28,%sp), %d3
+; CHECK-NEXT:    addx.l %d1, %d0
+; CHECK-NEXT:    move.l %d0, (%a0)
+; CHECK-NEXT:    move.l %d3, (4,%a0)
+; CHECK-NEXT:    sub.l %d2, %d3
+; CHECK-NEXT:    subx.l %d1, %d0
+; CHECK-NEXT:    scs %d0
+; CHECK-NEXT:    and.b #1, %d0
+; CHECK-NEXT:    move.b %d0, (8,%a0)
+; CHECK-NEXT:    move.l %a0, %d0
+; CHECK-NEXT:    movem.l (0,%sp), %d2-%d3 ; 12-byte Folded Reload
+; CHECK-NEXT:    adda.l #8, %sp
+; CHECK-NEXT:    move.l (%sp), %a1
+; CHECK-NEXT:    adda.l #4, %sp
+; CHECK-NEXT:    move.l %a1, (%sp)
+; CHECK-NEXT:    rts
+  %t = call {i64, i1} @llvm.uadd.with.overflow.i64(i64 %v1, i64 %v2)
+  ret {i64, i1} %t
+}
diff --git a/llvm/test/CodeGen/M68k/Arith/sub-with-overflow.ll b/llvm/test/CodeGen/M68k/Arith/sub-with-overflow.ll
index 2b2c0a76a83050..86e7b6ee0b8dfe 100644
--- a/llvm/test/CodeGen/M68k/Arith/sub-with-overflow.ll
+++ b/llvm/test/CodeGen/M68k/Arith/sub-with-overflow.ll
@@ -94,3 +94,36 @@ entry:
   %obit = extractvalue {i32, i1} %t, 1
   ret i1 %obit
 }
+
+define {i64, i1} @usubo_i64(i64 %v1, i64 %v2) nounwind {
+; CHECK-LABEL: usubo_i64:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    suba.l #12, %sp
+; CHECK-NEXT:    movem.l %d2-%d4, (0,%sp) ; 16-byte Folded Spill
+; CHECK-NEXT:    move.l (16,%sp), %a0
+; CHECK-NEXT:    move.l (28,%sp), %d0
+; CHECK-NEXT:    move.l (20,%sp), %d1
+; CHECK-NEXT:    move.l (24,%sp), %d2
+; CHECK-NEXT:    move.l %d2, %d3
+; CHECK-NEXT:    sub.l (32,%sp), %d3
+; CHECK-NEXT:    move.w %sr, -(%sp)
+; CHECK-NEXT:    move.l %d1, %d4
+; CHECK-NEXT:    move.w (%sp)+, %ccr
+; CHECK-NEXT:    subx.l %d0, %d4
+; CHECK-NEXT:    move.l %d4, (%a0)
+; CHECK-NEXT:    move.l %d3, (4,%a0)
+; CHECK-NEXT:    sub.l %d3, %d2
+; CHECK-NEXT:    subx.l %d4, %d1
+; CHECK-NEXT:    scs %d0
+; CHECK-NEXT:    and.b #1, %d0
+; CHECK-NEXT:    move.b %d0, (8,%a0)
+; CHECK-NEXT:    move.l %a0, %d0
+; CHECK-NEXT:    movem.l (0,%sp), %d2-%d4 ; 16-byte Folded Reload
+; CHECK-NEXT:    adda.l #12, %sp
+; CHECK-NEXT:    move.l (%sp), %a1
+; CHECK-NEXT:    adda.l #4, %sp
+; CHECK-NEXT:    move.l %a1, (%sp)
+; CHECK-NEXT:    rts
+  %t = call {i64, i1} @llvm.usub.with.overflow.i64(i64 %v1, i64 %v2)
+  ret {i64, i1} %t
+}

>From 52fb120d01091ecaaff210a7403d4cd0f97d5680 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Sun, 25 Oct 2026 19:05:36 -0700
Subject: [PATCH 2/2] [M68k] Use UADDO_CARRY/USUBO_CARRY instead of
 ADDC/ADDE/SUBC/SUBE.

Type legalization now expands wide add/sub using UADDO/USUBO and
UADDO_CARRY/USUBO_CARRY, which are lowered to M68kISD::ADD/SUB and
M68kISD::ADDX/SUBX with the carry passed through CCR.

ADDX/SUBX no longer receive a glue operand, so their verifyTargetNode
waivers can be removed.

This improves the code for i64 uadd/usub.with.overflow. Previously the
overflow bit was computed by redoing the subtraction, and usubo also
saved and restored CCR.

Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>
---
 llvm/lib/Target/M68k/M68kISelLowering.cpp     | 74 ++++++++-----------
 llvm/lib/Target/M68k/M68kISelLowering.h       |  2 +-
 llvm/lib/Target/M68k/M68kSelectionDAGInfo.cpp | 12 ---
 llvm/lib/Target/M68k/M68kSelectionDAGInfo.h   |  3 -
 .../CodeGen/M68k/Arith/add-with-overflow.ll   | 27 +++----
 .../CodeGen/M68k/Arith/sub-with-overflow.ll   | 34 ++++-----
 6 files changed, 58 insertions(+), 94 deletions(-)

diff --git a/llvm/lib/Target/M68k/M68kISelLowering.cpp b/llvm/lib/Target/M68k/M68kISelLowering.cpp
index f8ca134b9641ef..8d921b8c6b91bc 100644
--- a/llvm/lib/Target/M68k/M68kISelLowering.cpp
+++ b/llvm/lib/Target/M68k/M68kISelLowering.cpp
@@ -120,12 +120,9 @@ M68kTargetLowering::M68kTargetLowering(const M68kTargetMachine &TM,
   for (auto OP : {ISD::SHL_PARTS, ISD::SRA_PARTS, ISD::SRL_PARTS})
     setOperationAction(OP, MVT::i32, Custom);
 
-  // Add/Sub overflow ops with MVT::Glues are lowered to CCR dependences.
   for (auto VT : {MVT::i8, MVT::i16, MVT::i32}) {
-    setOperationAction(ISD::ADDC, VT, Custom);
-    setOperationAction(ISD::ADDE, VT, Custom);
-    setOperationAction(ISD::SUBC, VT, Custom);
-    setOperationAction(ISD::SUBE, VT, Custom);
+    setOperationAction(ISD::UADDO_CARRY, VT, Custom);
+    setOperationAction(ISD::USUBO_CARRY, VT, Custom);
   }
 
   // SADDO and friends are legal with this setup, i hope
@@ -1418,11 +1415,9 @@ SDValue M68kTargetLowering::LowerOperation(SDValue Op,
     return LowerSELECT(Op, DAG);
   case ISD::BRCOND:
     return LowerBRCOND(Op, DAG);
-  case ISD::ADDC:
-  case ISD::ADDE:
-  case ISD::SUBC:
-  case ISD::SUBE:
-    return LowerADDC_ADDE_SUBC_SUBE(Op, DAG);
+  case ISD::UADDO_CARRY:
+  case ISD::USUBO_CARRY:
+    return LowerUADDO_USUBO_CARRY(Op, DAG);
   case ISD::ConstantPool:
     return LowerConstantPool(Op, DAG);
   case ISD::GlobalAddress:
@@ -1554,6 +1549,12 @@ bool M68kTargetLowering::decomposeMulByConstant(LLVMContext &Context, EVT VT,
   return VT.bitsLE(MVT::i32) || Subtarget.atLeastM68020();
 }
 
+static SDValue getSETCC(M68k::CondCode Cond, SDValue CCR, const SDLoc &dl,
+                        SelectionDAG &DAG) {
+  return DAG.getNode(M68kISD::SETCC, dl, MVT::i8,
+                     DAG.getConstant(Cond, dl, MVT::i8), CCR);
+}
+
 static bool isOverflowArithmetic(unsigned Opcode) {
   switch (Opcode) {
   case ISD::UADDO:
@@ -2587,41 +2588,33 @@ SDValue M68kTargetLowering::LowerBRCOND(SDValue Op, SelectionDAG &DAG) const {
                      Cond);
 }
 
-SDValue M68kTargetLowering::LowerADDC_ADDE_SUBC_SUBE(SDValue Op,
-                                                     SelectionDAG &DAG) const {
-  MVT VT = Op.getNode()->getSimpleValueType(0);
+SDValue M68kTargetLowering::LowerUADDO_USUBO_CARRY(SDValue Op,
+                                                   SelectionDAG &DAG) const {
+  SDNode *N = Op.getNode();
+  MVT VT = N->getSimpleValueType(0);
 
   // Let legalize expand this if it isn't a legal type yet.
   if (!DAG.getTargetLoweringInfo().isTypeLegal(VT))
     return SDValue();
 
-  SDVTList VTs = DAG.getVTList(VT, MVT::i8);
+  SDLoc DL(N);
 
-  unsigned Opc;
-  bool ExtraOp = false;
-  switch (Op.getOpcode()) {
-  default:
-    llvm_unreachable("Invalid code");
-  case ISD::ADDC:
-    Opc = M68kISD::ADD;
-    break;
-  case ISD::ADDE:
-    Opc = M68kISD::ADDX;
-    ExtraOp = true;
-    break;
-  case ISD::SUBC:
-    Opc = M68kISD::SUB;
-    break;
-  case ISD::SUBE:
-    Opc = M68kISD::SUBX;
-    ExtraOp = true;
-    break;
-  }
+  // Set the X flag from the carry-in. ADD sets X and C; ADDX/SUBX read X.
+  SDValue Carry = Op.getOperand(2);
+  EVT CarryVT = Carry.getValueType();
+  Carry = DAG.getNode(M68kISD::ADD, DL, DAG.getVTList(CarryVT, MVT::i8), Carry,
+                      DAG.getAllOnesConstant(DL, CarryVT));
+
+  bool IsAdd = Op.getOpcode() == ISD::UADDO_CARRY;
+  SDValue Sum = DAG.getNode(IsAdd ? M68kISD::ADDX : M68kISD::SUBX, DL,
+                            DAG.getVTList(VT, MVT::i8), Op.getOperand(0),
+                            Op.getOperand(1), Carry.getValue(1));
 
-  if (!ExtraOp)
-    return DAG.getNode(Opc, SDLoc(Op), VTs, Op.getOperand(0), Op.getOperand(1));
-  return DAG.getNode(Opc, SDLoc(Op), VTs, Op.getOperand(0), Op.getOperand(1),
-                     Op.getOperand(2));
+  SDValue SetCC = getSETCC(M68k::COND_CS, Sum.getValue(1), DL, DAG);
+  if (N->getValueType(1) == MVT::i1)
+    SetCC = DAG.getNode(ISD::TRUNCATE, DL, MVT::i1, SetCC);
+
+  return DAG.getNode(ISD::MERGE_VALUES, DL, N->getVTList(), Sum, SetCC);
 }
 
 // ConstantPool, JumpTable, GlobalAddress, and ExternalSymbol are lowered as
@@ -3509,11 +3502,6 @@ SDValue M68kTargetLowering::LowerShiftRightParts(SDValue Op, SelectionDAG &DAG,
 // DAG Combine
 //===----------------------------------------------------------------------===//
 
-static SDValue getSETCC(M68k::CondCode Cond, SDValue CCR, const SDLoc &dl,
-                        SelectionDAG &DAG) {
-  return DAG.getNode(M68kISD::SETCC, dl, MVT::i8,
-                     DAG.getConstant(Cond, dl, MVT::i8), CCR);
-}
 // When legalizing carry, we create carries via add X, -1
 // If that comes from an actual carry, via setcc, we use the
 // carry directly. If \p NeedX is set, the user reads the X flag rather than C,
diff --git a/llvm/lib/Target/M68k/M68kISelLowering.h b/llvm/lib/Target/M68k/M68kISelLowering.h
index e4a92754606a06..0e77b7f42eb1f7 100644
--- a/llvm/lib/Target/M68k/M68kISelLowering.h
+++ b/llvm/lib/Target/M68k/M68kISelLowering.h
@@ -168,7 +168,7 @@ class M68kTargetLowering : public TargetLowering {
   SDValue LowerSETCCCARRY(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerSELECT(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerBRCOND(SDValue Op, SelectionDAG &DAG) const;
-  SDValue LowerADDC_ADDE_SUBC_SUBE(SDValue Op, SelectionDAG &DAG) const;
+  SDValue LowerUADDO_USUBO_CARRY(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerConstantPool(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const;
diff --git a/llvm/lib/Target/M68k/M68kSelectionDAGInfo.cpp b/llvm/lib/Target/M68k/M68kSelectionDAGInfo.cpp
index 9b2d1d00fa46f3..dd1bfdf00af8c7 100644
--- a/llvm/lib/Target/M68k/M68kSelectionDAGInfo.cpp
+++ b/llvm/lib/Target/M68k/M68kSelectionDAGInfo.cpp
@@ -16,16 +16,4 @@ using namespace llvm;
 M68kSelectionDAGInfo::M68kSelectionDAGInfo()
     : SelectionDAGGenTargetInfo(M68kGenSDNodeInfo) {}
 
-void M68kSelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
-                                            const SDNode *N) const {
-  switch (N->getOpcode()) {
-  case M68kISD::ADDX:
-  case M68kISD::SUBX:
-    // result #1 must have the same type as operand #2 (glue), but has type i8
-    return;
-  }
-
-  SelectionDAGGenTargetInfo::verifyTargetNode(DAG, N);
-}
-
 M68kSelectionDAGInfo::~M68kSelectionDAGInfo() = default;
diff --git a/llvm/lib/Target/M68k/M68kSelectionDAGInfo.h b/llvm/lib/Target/M68k/M68kSelectionDAGInfo.h
index de4667f830d411..87a8c08d2591e4 100644
--- a/llvm/lib/Target/M68k/M68kSelectionDAGInfo.h
+++ b/llvm/lib/Target/M68k/M68kSelectionDAGInfo.h
@@ -21,9 +21,6 @@ class M68kSelectionDAGInfo : public SelectionDAGGenTargetInfo {
   M68kSelectionDAGInfo();
 
   ~M68kSelectionDAGInfo() override;
-
-  void verifyTargetNode(const SelectionDAG &DAG,
-                        const SDNode *N) const override;
 };
 
 } // namespace llvm
diff --git a/llvm/test/CodeGen/M68k/Arith/add-with-overflow.ll b/llvm/test/CodeGen/M68k/Arith/add-with-overflow.ll
index a98f7b4fb02dce..09b99df525d910 100644
--- a/llvm/test/CodeGen/M68k/Arith/add-with-overflow.ll
+++ b/llvm/test/CodeGen/M68k/Arith/add-with-overflow.ll
@@ -77,25 +77,22 @@ entry:
 define {i64, i1} @uaddo_i64(i64 %v1, i64 %v2) nounwind {
 ; CHECK-LABEL: uaddo_i64:
 ; CHECK:       ; %bb.0:
-; CHECK-NEXT:    suba.l #8, %sp
-; CHECK-NEXT:    movem.l %d2-%d3, (0,%sp) ; 12-byte Folded Spill
-; CHECK-NEXT:    move.l (12,%sp), %a0
-; CHECK-NEXT:    move.l (24,%sp), %d0
-; CHECK-NEXT:    move.l (16,%sp), %d1
-; CHECK-NEXT:    move.l (20,%sp), %d2
-; CHECK-NEXT:    move.l %d2, %d3
-; CHECK-NEXT:    add.l (28,%sp), %d3
-; CHECK-NEXT:    addx.l %d1, %d0
-; CHECK-NEXT:    move.l %d0, (%a0)
-; CHECK-NEXT:    move.l %d3, (4,%a0)
-; CHECK-NEXT:    sub.l %d2, %d3
-; CHECK-NEXT:    subx.l %d1, %d0
+; CHECK-NEXT:    suba.l #4, %sp
+; CHECK-NEXT:    movem.l %d2, (0,%sp) ; 8-byte Folded Spill
+; CHECK-NEXT:    move.l (8,%sp), %a0
+; CHECK-NEXT:    move.l (12,%sp), %d0
+; CHECK-NEXT:    move.l (20,%sp), %d1
+; CHECK-NEXT:    move.l (16,%sp), %d2
+; CHECK-NEXT:    add.l (24,%sp), %d2
+; CHECK-NEXT:    addx.l %d0, %d1
 ; CHECK-NEXT:    scs %d0
+; CHECK-NEXT:    move.l %d2, (4,%a0)
+; CHECK-NEXT:    move.l %d1, (%a0)
 ; CHECK-NEXT:    and.b #1, %d0
 ; CHECK-NEXT:    move.b %d0, (8,%a0)
 ; CHECK-NEXT:    move.l %a0, %d0
-; CHECK-NEXT:    movem.l (0,%sp), %d2-%d3 ; 12-byte Folded Reload
-; CHECK-NEXT:    adda.l #8, %sp
+; CHECK-NEXT:    movem.l (0,%sp), %d2 ; 8-byte Folded Reload
+; CHECK-NEXT:    adda.l #4, %sp
 ; CHECK-NEXT:    move.l (%sp), %a1
 ; CHECK-NEXT:    adda.l #4, %sp
 ; CHECK-NEXT:    move.l %a1, (%sp)
diff --git a/llvm/test/CodeGen/M68k/Arith/sub-with-overflow.ll b/llvm/test/CodeGen/M68k/Arith/sub-with-overflow.ll
index 86e7b6ee0b8dfe..58db5560cd01fe 100644
--- a/llvm/test/CodeGen/M68k/Arith/sub-with-overflow.ll
+++ b/llvm/test/CodeGen/M68k/Arith/sub-with-overflow.ll
@@ -98,28 +98,22 @@ entry:
 define {i64, i1} @usubo_i64(i64 %v1, i64 %v2) nounwind {
 ; CHECK-LABEL: usubo_i64:
 ; CHECK:       ; %bb.0:
-; CHECK-NEXT:    suba.l #12, %sp
-; CHECK-NEXT:    movem.l %d2-%d4, (0,%sp) ; 16-byte Folded Spill
-; CHECK-NEXT:    move.l (16,%sp), %a0
-; CHECK-NEXT:    move.l (28,%sp), %d0
+; CHECK-NEXT:    suba.l #4, %sp
+; CHECK-NEXT:    movem.l %d2, (0,%sp) ; 8-byte Folded Spill
+; CHECK-NEXT:    move.l (8,%sp), %a0
+; CHECK-NEXT:    move.l (12,%sp), %d0
 ; CHECK-NEXT:    move.l (20,%sp), %d1
-; CHECK-NEXT:    move.l (24,%sp), %d2
-; CHECK-NEXT:    move.l %d2, %d3
-; CHECK-NEXT:    sub.l (32,%sp), %d3
-; CHECK-NEXT:    move.w %sr, -(%sp)
-; CHECK-NEXT:    move.l %d1, %d4
-; CHECK-NEXT:    move.w (%sp)+, %ccr
-; CHECK-NEXT:    subx.l %d0, %d4
-; CHECK-NEXT:    move.l %d4, (%a0)
-; CHECK-NEXT:    move.l %d3, (4,%a0)
-; CHECK-NEXT:    sub.l %d3, %d2
-; CHECK-NEXT:    subx.l %d4, %d1
-; CHECK-NEXT:    scs %d0
-; CHECK-NEXT:    and.b #1, %d0
-; CHECK-NEXT:    move.b %d0, (8,%a0)
+; CHECK-NEXT:    move.l (16,%sp), %d2
+; CHECK-NEXT:    sub.l (24,%sp), %d2
+; CHECK-NEXT:    subx.l %d1, %d0
+; CHECK-NEXT:    scs %d1
+; CHECK-NEXT:    move.l %d2, (4,%a0)
+; CHECK-NEXT:    move.l %d0, (%a0)
+; CHECK-NEXT:    and.b #1, %d1
+; CHECK-NEXT:    move.b %d1, (8,%a0)
 ; CHECK-NEXT:    move.l %a0, %d0
-; CHECK-NEXT:    movem.l (0,%sp), %d2-%d4 ; 16-byte Folded Reload
-; CHECK-NEXT:    adda.l #12, %sp
+; CHECK-NEXT:    movem.l (0,%sp), %d2 ; 8-byte Folded Reload
+; CHECK-NEXT:    adda.l #4, %sp
 ; CHECK-NEXT:    move.l (%sp), %a1
 ; CHECK-NEXT:    adda.l #4, %sp
 ; CHECK-NEXT:    move.l %a1, (%sp)



More information about the llvm-branch-commits mailing list