tms34010: select MPYS/MPYU/DIVS/DIVU for 32-bit arithmetic

Add the register-file multiply and divide encodings and restrict their tied
destination to the odd allocatable A registers, so the instruction uses the
single-register 32-bit form instead of a Rd:Rd+1 pair.  MPYS covers the low
32 bits of a 32x32 product, MPYU the masked 16x16 unsigned product, and
DIVS/DIVU the 32/32 quotients.  SREM/UREM expand to dividend - quotient *
divisor because the odd-destination divide does not return a remainder.

Sign-extension in register (which DAGCombine forms from ordinary short
arithmetic) selects SEXT8/SEXT16 and expands to the verified SLL/SRA pair.
This commit is contained in:
arti committed 2026-11-12 06:04:53 +00:00
1 parent 4b300a75e9
commit 4ff6e2df9d
4 files changed
+236

No files matched your search

@@ -31,6 +31,11 @@ TMS34010TargetLowering::TMS34010TargetLowering(const TargetMachine &TM,
: TargetLowering(TM, STI) {
addRegisterClass(MVT::i32, &TMS34010::ARegsRegClass);
// The backend has no SEXT-as-field-width path that suits every width, so
// sign extension in register selects the SEXT8/SEXT16 pseudo and expands to
// a shift pair after instruction selection (see EmitInstrWithCustomInserter).
// DAGCombine forms these nodes from ordinary `short` shift pairs.
// MOVB is an 8-bit sign-extending load and an 8-bit truncating store.
setLoadExtAction(ISD::SEXTLOAD, MVT::i32, MVT::i8, Legal);
setTruncStoreAction(MVT::i32, MVT::i8, Legal);
@@ -45,6 +50,16 @@ TMS34010TargetLowering::TMS34010TargetLowering(const TargetMachine &TM,
setLoadExtAction(ISD::ZEXTLOAD, MVT::i32, MVT::i8, Custom);
setLoadExtAction(ISD::ZEXTLOAD, MVT::i32, MVT::i16, Custom);
// Multiply and divide are single register-register instructions (MPYS/DIVS/
// DIVU) with an odd tied destination; see TMS34010InstrInfo.td. The
// remainder has no direct 32-bit form, so it is expanded from the quotient
// and a multiply in LowerOperation.
setOperationAction(ISD::MUL, MVT::i32, Legal);
setOperationAction(ISD::SDIV, MVT::i32, Legal);
setOperationAction(ISD::UDIV, MVT::i32, Legal);
setOperationAction(ISD::SREM, MVT::i32, Custom);
setOperationAction(ISD::UREM, MVT::i32, Custom);
// There is no conditional move or dedicated setcc result; let the generic
// legalizer turn setcc into a select_cc over 1/0 and select into a
// select_cc, then lower the compare and branch ourselves.
@@ -476,6 +491,21 @@ SDValue TMS34010TargetLowering::LowerOperation(SDValue Op,
return LowerBR_CC(Op, DAG);
case ISD::SELECT_CC:
return LowerSELECT_CC(Op, DAG);
case ISD::SREM:
case ISD::UREM: {
// The odd-destination DIVS/DIVU leaves only the quotient. Reconstruct the
// remainder in the C semantics (which truncates the quotient toward zero):
// rem = dividend - quotient * divisor
// signed and unsigned remainder both use the corresponding divide and the
// same low-32-bit product, so one expansion covers both.
SDLoc DL(Op);
SDValue LHS = Op.getOperand(0);
SDValue RHS = Op.getOperand(1);
unsigned DivOpc = Op.getOpcode() == ISD::SREM ? ISD::SDIV : ISD::UDIV;
SDValue Q = DAG.getNode(DivOpc, DL, MVT::i32, LHS, RHS);
SDValue P = DAG.getNode(ISD::MUL, DL, MVT::i32, Q, RHS);
return DAG.getNode(ISD::SUB, DL, MVT::i32, LHS, P);
}
default:
llvm_unreachable("unexpected TMS34010 custom lowering operation");
}
@@ -486,6 +516,31 @@ MachineBasicBlock *TMS34010TargetLowering::EmitInstrWithCustomInserter(
switch (MI.getOpcode()) {
case TMS34010::SELECTCC:
return emitSelectCC(MI, MBB);
case TMS34010::SEXT8:
case TMS34010::SEXT16: {
// dst = (src << K) >> K, arithmetic, with K = 32 - width.
MachineFunction *MF = MBB->getParent();
MachineRegisterInfo &MRI = MF->getRegInfo();
const TargetInstrInfo &TII = *MF->getSubtarget().getInstrInfo();
const TargetRegisterClass *RC = &TMS34010::ARegsRegClass;
const DebugLoc &DL = MI.getDebugLoc();
Register Dst = MI.getOperand(0).getReg();
Register Src = MI.getOperand(1).getReg();
int64_t Shift = MI.getOpcode() == TMS34010::SEXT8 ? 24 : 16;
// A machine virtual register has one definition before SSA is destroyed,
// so each step produces a fresh one.
Register T1 = MRI.createVirtualRegister(RC);
Register T2 = MRI.createVirtualRegister(RC);
BuildMI(*MBB, MI, DL, TII.get(TargetOpcode::COPY), T1).addReg(Src);
BuildMI(*MBB, MI, DL, TII.get(TMS34010::SLLK), T2)
.addReg(T1)
.addImm(Shift);
BuildMI(*MBB, MI, DL, TII.get(TMS34010::SRAK), Dst)
.addReg(T2)
.addImm(Shift);
MI.eraseFromParent();
return MBB;
}
default:
return nullptr;
}
@@ -154,6 +154,82 @@ def XOR : TMS34010Inst<(outs ARegs:$dst), (ins ARegs:$src, ARegs:$val),
let Inst{3-0} = dst{3-0};
}
// MPYS Rs, Rd: 0101 110S SSSR DDDD (0x5C00). Signed multiply: Rd is the
// 32-bit multiplicand and the low 32 bits of the product are stored back in
// Rd when Rd is odd. The ABI keeps FS1 = 32, so Rs is a full 32-bit multiplier
// and the low half of the signed and unsigned products is identical. An even
// Rd would write the high half to Rd and the low half to Rd+1, clobbering the
// neighbouring register, so the tied destination is ARegsOdd.
def MPYS : TMS34010Inst<(outs ARegsOdd:$dst), (ins ARegs:$src, ARegsOdd:$val),
"MPYS $src, $dst",
[(set ARegsOdd:$dst, (mul ARegs:$src,
ARegsOdd:$val))]> {
let Constraints = "$val = $dst";
let Defs = [ST];
bits<5> src;
bits<5> val;
bits<5> dst;
let Inst{15-9} = 0b0101110;
let Inst{8-5} = src{3-0};
let Inst{4} = dst{4};
let Inst{3-0} = dst{3-0};
}
// MPYU Rs, Rd: 0101 111S SSSR DDDD (0x5E00). Unsigned multiply. A 16x16
// unsigned product reaches the DAG as both operands masked to 16 bits, which
// is the natural place to select MPYU; the low 32 bits are identical to a
// signed multiply, so this is an encoding choice, not a different result.
def MPYU : TMS34010Inst<(outs ARegsOdd:$dst), (ins ARegs:$src, ARegsOdd:$val),
"MPYU $src, $dst",
[(set ARegsOdd:$dst,
(mul (and i32:$src, 65535),
(and ARegsOdd:$val, 65535)))]> {
let Constraints = "$val = $dst";
let Defs = [ST];
bits<5> src;
bits<5> val;
bits<5> dst;
let Inst{15-9} = 0b0101111;
let Inst{8-5} = src{3-0};
let Inst{4} = dst{4};
let Inst{3-0} = dst{3-0};
}
// DIVS Rs, Rd: 0101 100S SSSR DDDD (0x5800). Signed divide of the 32-bit
// dividend in Rd by Rs. With an odd Rd the quotient replaces Rd and no
// remainder is returned; the remainder is synthesised as dividend -
// quotient * divisor (the hardware only returns it with an even Rd pair).
def DIVS : TMS34010Inst<(outs ARegsOdd:$dst), (ins ARegs:$src, ARegsOdd:$val),
"DIVS $src, $dst",
[(set ARegsOdd:$dst, (sdiv ARegsOdd:$val,
ARegs:$src))]> {
let Constraints = "$val = $dst";
let Defs = [ST];
bits<5> src;
bits<5> val;
bits<5> dst;
let Inst{15-9} = 0b0101100;
let Inst{8-5} = src{3-0};
let Inst{4} = dst{4};
let Inst{3-0} = dst{3-0};
}
// DIVU Rs, Rd: 0101 101S SSSR DDDD (0x5A00). Unsigned divide, same form.
def DIVU : TMS34010Inst<(outs ARegsOdd:$dst), (ins ARegs:$src, ARegsOdd:$val),
"DIVU $src, $dst",
[(set ARegsOdd:$dst, (udiv ARegsOdd:$val,
ARegs:$src))]> {
let Constraints = "$val = $dst";
let Defs = [ST];
bits<5> src;
bits<5> val;
bits<5> dst;
let Inst{15-9} = 0b0101101;
let Inst{8-5} = src{3-0};
let Inst{4} = dst{4};
let Inst{3-0} = dst{3-0};
}
// NEG Rd: 0000 0011 101R DDDD (0x03A0).
def NEG : TMS34010Inst<(outs ARegs:$dst), (ins ARegs:$src), "NEG $dst",
[(set ARegs:$dst, (ineg ARegs:$src))]> {
@@ -570,6 +646,23 @@ def GETFI : TMS34010Inst<(outs ARegs:$dst), (ins i32imm:$off), "# GETFI", []> {
let isPseudo = 1;
}
// Sign extension in register. The ISA's SEXT reads the current field size, and
// the ABI pins FS0 at 16 for `short`, so a width-specific field extension is
// not available for every width. Expand to the verified shift pair
// (SLL K ; SRA K) instead. DAGCombine forms these nodes from shift pairs in
// ordinary `short` arithmetic, so they must select.
def SEXT8 : TMS34010Inst<(outs ARegs:$dst), (ins ARegs:$src), "# SEXT8",
[(set ARegs:$dst, (sext_inreg ARegs:$src, i8))]> {
let isPseudo = 1;
let usesCustomInserter = 1;
}
def SEXT16 : TMS34010Inst<(outs ARegs:$dst), (ins ARegs:$src), "# SEXT16",
[(set ARegs:$dst, (sext_inreg ARegs:$src, i16))]> {
let isPseudo = 1;
let usesCustomInserter = 1;
}
def NOP : TMS34010Inst<(outs), (ins), "NOP", []> {
let Inst{15-0} = 0x0300;
}
@@ -63,6 +63,14 @@ def ARegs : RegisterClass<"TMS34010", [i32], 32,
(add A0, A1, A2, A3, A4, A5, A6, A7,
A9, A10, A11, A12)>;
// MPYS/MPYU/DIVS/DIVU treat an even Rd as the first of a register pair: an
// even multiply writes Rd:Rd+1, and an even divide reads a 64-bit Rd:Rd+1
// dividend and writes the quotient to Rd and the remainder to Rd+1. The
// 32-bit forms here always want the odd (single-register) encoding, so the
// tied destination operand is restricted to the odd allocatable A registers.
def ARegsOdd : RegisterClass<"TMS34010", [i32], 32,
(add A1, A3, A5, A7, A9, A11)>;
// ARegsAll names every A register. It is used only as the operand class of
// the frame and system-stack instructions, which need to name the reserved
// FP/STK/SP registers explicitly; it is never allocated.
+80
View File
@@ -0,0 +1,80 @@
; RUN: llc -march=tms34010 -O0 -filetype=asm < %s | FileCheck %s
; RUN: llc -march=tms34010 -O2 -filetype=asm < %s | FileCheck %s
; M4 adds the register-file ALU multiply and divide instructions. MPYS and
; MPYU multiply the 32-bit Rd (the tied destination) by the multiplier field
; Rs; with an odd Rd the low 32 bits are stored back and no register pair is
; touched. The destination must be odd, so the tied operand uses the
; odd-only ARegsOdd class.
;
; DIVS/DIVU with an odd Rd divide the 32-bit Rd by Rs, leaving the quotient in
; Rd. The remainder is derived as dividend - quotient * divisor, because the
; hardware only returns the remainder with an even destination pair.
; A 32x32 low product: one MPYS to an odd destination.
define i32 @mul32(i32 %a, i32 %b) {
%r = mul i32 %a, %b
ret i32 %r
}
; CHECK-LABEL: _mul32:
; CHECK: MPYS A{{[0-9]+}}, {{A([13579]|11)$}}
; CHECK: RETS 2
; A 16x16 signed product: the operands are truncated to 16 bits and
; sign-extended with a shift pair, then multiplied with MPYS.
define i32 @mul16s(i32 %a, i32 %b) {
%as = shl i32 %a, 16
%bs = shl i32 %b, 16
%ae = ashr i32 %as, 16
%be = ashr i32 %bs, 16
%r = mul i32 %ae, %be
ret i32 %r
}
; CHECK-LABEL: _mul16s:
; CHECK: MPYS
; A 16x16 unsigned product, masked to 16 bits: MPYU.
define i32 @mul16u(i32 %a, i32 %b) {
%ae = and i32 %a, 65535
%be = and i32 %b, 65535
%r = mul i32 %ae, %be
ret i32 %r
}
; CHECK-LABEL: _mul16u:
; CHECK: MPYU
; Signed and unsigned 32/32 division with an odd destination.
define i32 @sdiv32(i32 %a, i32 %b) {
%r = sdiv i32 %a, %b
ret i32 %r
}
; CHECK-LABEL: _sdiv32:
; CHECK: DIVS A{{[0-9]+}}, {{A([13579]|11)$}}
; CHECK: RETS 2
define i32 @udiv32(i32 %a, i32 %b) {
%r = udiv i32 %a, %b
ret i32 %r
}
; CHECK-LABEL: _udiv32:
; CHECK: DIVU A{{[0-9]+}}, {{A([13579]|11)$}}
; CHECK: RETS 2
; Remainder = dividend - quotient * divisor, using the same divide.
define i32 @srem32(i32 %a, i32 %b) {
%r = srem i32 %a, %b
ret i32 %r
}
; CHECK-LABEL: _srem32:
; CHECK: DIVS
; CHECK: MPYS
; CHECK: SUB
define i32 @urem32(i32 %a, i32 %b) {
%r = urem i32 %a, %b
ret i32 %r
}
; CHECK-LABEL: _urem32:
; CHECK: DIVU
; CHECK: MPYS
; CHECK: SUB