[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