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:
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.
|
||||
|
||||
@@ -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
|
||||
Reference in new issue
Block a user