From 52237119a9b41d6d714c96e730d651300b171298 Mon Sep 17 00:00:00 2001 From: Anton Korobeynikov Date: Wed, 17 Jun 2009 18:13:58 +0000 Subject: [PATCH] Initial support for some Thumb2 instructions. Patch by Viktor Kutuzov and Anton Korzh from Access Softek, Inc. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@73622 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/ARM/ARMISelDAGToDAG.cpp | 40 +++- lib/Target/ARM/ARMInstrInfo.td | 6 + lib/Target/ARM/ARMInstrThumb2.td | 196 ++++++++++++++++++++ lib/Target/ARM/AsmPrinter/ARMAsmPrinter.cpp | 23 +++ test/CodeGen/ARM/thumb2-add.ll | 50 +++++ test/CodeGen/ARM/thumb2-mov.ll | 127 +++++++++++++ test/CodeGen/ARM/thumb2-mov2.ll | 65 +++++++ test/CodeGen/ARM/thumb2-shifter.ll | 40 ++++ 8 files changed, 544 insertions(+), 3 deletions(-) create mode 100644 test/CodeGen/ARM/thumb2-add.ll create mode 100644 test/CodeGen/ARM/thumb2-mov.ll create mode 100644 test/CodeGen/ARM/thumb2-mov2.ll create mode 100644 test/CodeGen/ARM/thumb2-shifter.ll diff --git a/lib/Target/ARM/ARMISelDAGToDAG.cpp b/lib/Target/ARM/ARMISelDAGToDAG.cpp index ca3a9cb4032..1ed9e8080a2 100644 --- a/lib/Target/ARM/ARMISelDAGToDAG.cpp +++ b/lib/Target/ARM/ARMISelDAGToDAG.cpp @@ -52,8 +52,13 @@ public: virtual const char *getPassName() const { return "ARM Instruction Selection"; - } - + } + + /// getI32Imm - Return a target constant with the specified value, of type i32. + inline SDValue getI32Imm(unsigned Imm) { + return CurDAG->getTargetConstant(Imm, MVT::i32); + } + SDNode *Select(SDValue Op); virtual void InstructionSelect(); bool SelectAddrMode2(SDValue Op, SDValue N, SDValue &Base, @@ -84,6 +89,9 @@ public: bool SelectThumbAddrModeSP(SDValue Op, SDValue N, SDValue &Base, SDValue &OffImm); + bool SelectShifterOperand(SDValue Op, SDValue N, + SDValue &BaseReg, SDValue &Opc); + bool SelectShifterOperandReg(SDValue Op, SDValue N, SDValue &A, SDValue &B, SDValue &C); @@ -509,8 +517,30 @@ bool ARMDAGToDAGISel::SelectThumbAddrModeSP(SDValue Op, SDValue N, return false; } +bool ARMDAGToDAGISel::SelectShifterOperand(SDValue Op, + SDValue N, + SDValue &BaseReg, + SDValue &Opc) { + ARM_AM::ShiftOpc ShOpcVal = ARM_AM::getShiftOpcForNode(N); + + // Don't match base register only case. That is matched to a separate + // lower complexity pattern with explicit register operand. + if (ShOpcVal == ARM_AM::no_shift) return false; + + BaseReg = N.getOperand(0); + unsigned ShImmVal = 0; + if (ConstantSDNode *RHS = dyn_cast(N.getOperand(1))) + ShImmVal = RHS->getZExtValue() & 31; + else + return false; + + Opc = getI32Imm(ARM_AM::getSORegOpc(ShOpcVal, ShImmVal)); + + return true; +} + bool ARMDAGToDAGISel::SelectShifterOperandReg(SDValue Op, - SDValue N, + SDValue N, SDValue &BaseReg, SDValue &ShReg, SDValue &Opc) { @@ -549,6 +579,10 @@ SDNode *ARMDAGToDAGISel::Select(SDValue Op) { switch (N->getOpcode()) { default: break; case ISD::Constant: { + // ARMv6T2 and later should materialize imms via MOV / MOVT pair. + if (Subtarget->hasV6T2Ops() || Subtarget->hasThumb2()) + break; + unsigned Val = cast(N)->getZExtValue(); bool UseCP = true; if (Subtarget->isThumb()) diff --git a/lib/Target/ARM/ARMInstrInfo.td b/lib/Target/ARM/ARMInstrInfo.td index b79a0b3c660..5c0bd97fa9f 100644 --- a/lib/Target/ARM/ARMInstrInfo.td +++ b/lib/Target/ARM/ARMInstrInfo.td @@ -1384,6 +1384,12 @@ def : ARMV5TEPat<(add GPR:$acc, include "ARMInstrThumb.td" +//===----------------------------------------------------------------------===// +// Thumb2 Support +// + +include "ARMInstrThumb2.td" + //===----------------------------------------------------------------------===// // Floating Point Support // diff --git a/lib/Target/ARM/ARMInstrThumb2.td b/lib/Target/ARM/ARMInstrThumb2.td index 168fb45f11e..1a38619db72 100644 --- a/lib/Target/ARM/ARMInstrThumb2.td +++ b/lib/Target/ARM/ARMInstrThumb2.td @@ -10,3 +10,199 @@ // This file describes the Thumb2 instruction set. // //===----------------------------------------------------------------------===// + +// Shifted operands. No register controlled shifts for Thumb2. +// Note: We do not support rrx shifted operands yet. +def t2_so_reg : Operand, // reg imm + ComplexPattern { + let PrintMethod = "printSOOperand"; + let MIOperandInfo = (ops GPR, i32imm); +} + +def LO16 : SDNodeXFormgetZExtValue()); +}]>; + +def HI16 : SDNodeXFormgetZExtValue() >> 16); +}]>; + +def imm16high : PatLeaf<(i32 imm), [{ + // Returns true if all bits out of the [31..16] range are 0. + return ((N->getZExtValue() & 0xFFFF0000ULL) == N->getZExtValue()); +}], HI16>; + +def imm16high0xffff : PatLeaf<(i32 imm), [{ + // Returns true if lo 16 bits are set and this is a 32-bit value. + return ((N->getZExtValue() & 0x0000FFFFULL) == 0xFFFFULL); +}], HI16>; + +def imm0_4095 : PatLeaf<(i32 imm), [{ + return (uint32_t)N->getZExtValue() < 4096; +}]>; + +def imm0_4095_neg : PatLeaf<(i32 imm), [{ + return (uint32_t)-N->getZExtValue() < 4096; +}], imm_neg_XFORM>; + +def imm0_65535 : PatLeaf<(i32 imm), [{ + return N->getZExtValue() < 65536; +}]>; + +// A6.3.2 Modified immediate constants in Thumb instructions (#) +// FIXME: Move it the the addrmode matcher code. +def t2_so_imm : PatLeaf<(i32 imm), [{ + uint64_t v = N->getZExtValue(); + if (v == 0 || v > 0xffffffffUL) return false; + // variant1 - 0b0000x - 8-bit which could be zero (not supported for now) + + // variant2 - 0b00nnx - 8-bit repeated inside the 32-bit room + unsigned hi16 = (unsigned)(v >> 16); + unsigned lo16 = (unsigned)(v & 0xffffUL); + bool valid = (hi16 == lo16) && ( + (v & 0x00ff00ffUL) == 0 || // type 0001x + (v & 0xff00ff00UL) == 0 || // type 0010x + ((lo16 >> 8) == (lo16 & 0xff))); // type 0011x + if (valid) return true; + + // variant3 - 0b01000..0b11111 - 8-bit shifted inside the 32-bit room + unsigned shift = CountLeadingZeros_32(v); + uint64_t mask = (0xff000000ULL >> shift); + // If valid, it is type 01000 + shift + return ((shift < 24) && (v & mask) > 0) && ((v & (~mask)) == 0); +}]>; + + +//===----------------------------------------------------------------------===// +// Thumb-2 to cover the functionality of the ARM instruction set. +// + +/// T2I_bin_irs - Defines a set of (op reg, {so_imm|reg|so_reg}) patterns for a +// binary operation that produces a value. +multiclass T2I_bin_irs { + // shifted imm + def ri : PseudoInst<(outs GPR:$dst), (ins GPR:$lhs, i32imm:$rhs), + !strconcat(opc, " $dst, $lhs, $rhs"), + [(set GPR:$dst, (opnode GPR:$lhs, t2_so_imm:$rhs))]>, + Requires<[HasThumb2]>; + // register + def rr : PseudoInst<(outs GPR:$dst), (ins GPR:$lhs, GPR:$rhs), + !strconcat(opc, " $dst, $lhs, $rhs"), + [(set GPR:$dst, (opnode GPR:$lhs, GPR:$rhs))]>, + Requires<[HasThumb2]>; + // shifted register + def rs : PseudoInst<(outs GPR:$dst), (ins GPR:$lhs, t2_so_reg:$rhs), + !strconcat(opc, " $dst, $lhs, $rhs"), + [(set GPR:$dst, (opnode GPR:$lhs, t2_so_reg:$rhs))]>, + Requires<[HasThumb2]>; +} + +/// T2I_bin_s_irs - Similar to T2I_bin_irs except it sets the 's' bit so the +/// instruction modifies the CPSR register. +let Defs = [CPSR] in { +multiclass T2I_bin_s_irs { + // shifted imm + def ri : PseudoInst<(outs GPR:$dst), (ins GPR:$lhs, i32imm:$rhs), + !strconcat(opc, "s $dst, $lhs, $rhs"), + [(set GPR:$dst, (opnode GPR:$lhs, t2_so_imm:$rhs))]>, + Requires<[HasThumb2]>; + + // register + def rr : PseudoInst<(outs GPR:$dst), (ins GPR:$lhs, GPR:$rhs), + !strconcat(opc, "s $dst, $lhs, $rhs"), + [(set GPR:$dst, (opnode GPR:$lhs, GPR:$rhs))]>, + Requires<[HasThumb2]>; + + // shifted register + def rs : PseudoInst<(outs GPR:$dst), (ins GPR:$lhs, t2_so_reg:$rhs), + !strconcat(opc, "s $dst, $lhs, $rhs"), + [(set GPR:$dst, (opnode GPR:$lhs, t2_so_reg:$rhs))]>, + Requires<[HasThumb2]>; +} +} + +/// T2I_bin_c_irs - Similar to T2I_bin_irs except it uses the 's' bit. Also the +/// instruction can optionally set the CPSR register. +let Uses = [CPSR] in { +multiclass T2I_bin_c_irs { + // shifted imm + def ri : PseudoInst<(outs GPR:$dst), (ins GPR:$lhs, i32imm:$rhs, cc_out:$s), + !strconcat(opc, "${s} $dst, $lhs, $rhs"), + [(set GPR:$dst, (opnode GPR:$lhs, t2_so_imm:$rhs))]>, + Requires<[HasThumb2]>; + + // register + def rr : PseudoInst<(outs GPR:$dst), (ins GPR:$lhs, GPR:$rhs, cc_out:$s), + !strconcat(opc, "${s} $dst, $lhs, $rhs"), + [(set GPR:$dst, (opnode GPR:$lhs, GPR:$rhs))]>, + Requires<[HasThumb2]>; + + // shifted register + def rs : PseudoInst<(outs GPR:$dst), (ins GPR:$lhs, t2_so_reg:$rhs, cc_out:$s), + !strconcat(opc, "${s} $dst, $lhs, $rhs"), + [(set GPR:$dst, (opnode GPR:$lhs, t2_so_reg:$rhs))]>, + Requires<[HasThumb2]>; +} +} + +//===----------------------------------------------------------------------===// +// Arithmetic Instructions. +// + +//===----------------------------------------------------------------------===// +// Move Instructions. +// +def tMOVi16 : PseudoInst<(outs GPR:$dst), (ins i32imm:$src), + "mov $dst, $src", + [(set GPR:$dst, imm0_65535:$src)]>, + Requires<[HasThumb2]>; + +let isTwoAddress = 1 in +def tMOVTi16 : PseudoInst<(outs GPR:$dst), (ins GPR:$src, i32imm:$imm), + "movt $dst, $imm", + [(set GPR:$dst, (or (and GPR:$src, 0xffff), + imm16high:$imm))]>, + Requires<[HasThumb2]>; + +def : Pat<(and (or GPR:$src, imm16high:$imm1), imm16high0xffff:$imm2), + (tMOVTi16 GPR:$src, (HI16 imm16high:$imm1))>, + Requires<[HasThumb2]>; + +def : Pat<(i32 imm:$imm), + (tMOVTi16 (tMOVi16 (LO16 imm:$imm)),(HI16 imm:$imm))>, + Requires<[HasThumb2]>; + +//===----------------------------------------------------------------------===// +// Arithmetic Instructions. +// +defm t2ADD : T2I_bin_irs <"add", BinOpFrag<(add node:$LHS, node:$RHS)>>; +defm t2SUB : T2I_bin_irs <"sub", BinOpFrag<(sub node:$LHS, node:$RHS)>>; + +def tADDri12 : PseudoInst<(outs GPR:$dst), (ins GPR:$lhs, i32imm:$rhs), + "add $dst, $lhs, $rhs", + [(set GPR:$dst, (add GPR:$lhs, imm0_4095:$rhs))]>, + Requires<[HasThumb2]>; +def tSUBri12 : PseudoInst<(outs GPR:$dst), (ins GPR:$lhs, i32imm:$rhs), + "sub $dst, $lhs, $rhs", + [(set GPR:$dst, (add GPR:$lhs, imm0_4095_neg:$rhs))]>, + Requires<[HasThumb2]>; + +defm t2ADDS : T2I_bin_s_irs<"add", BinOpFrag<(addc node:$LHS, node:$RHS)>>; +defm t2SUBS : T2I_bin_s_irs<"sub", BinOpFrag<(subc node:$LHS, node:$RHS)>>; + +defm t2ADC : T2I_bin_c_irs<"adc", BinOpFrag<(adde node:$LHS, node:$RHS)>>; +defm t2SBC : T2I_bin_c_irs<"sbc", BinOpFrag<(sube node:$LHS, node:$RHS)>>; + + +def tMLS : PseudoInst<(outs GPR:$dst), (ins GPR:$a, GPR:$b, GPR:$c), + "mls $dst, $a, $b, $c", + [(set GPR:$dst, (sub GPR:$c, (mul GPR:$a, GPR:$b)))]>, + Requires<[HasThumb2]>; + +def tORNrs : PseudoInst<(outs GPR:$dst), (ins GPR:$src1, t2_so_reg:$src2), + "orn $dst, $src1, $src2", + [(set GPR:$dst, (or GPR:$src1, (not t2_so_reg: $src2)))]>, + Requires<[HasThumb2]>; diff --git a/lib/Target/ARM/AsmPrinter/ARMAsmPrinter.cpp b/lib/Target/ARM/AsmPrinter/ARMAsmPrinter.cpp index 662dc435142..c2ef0903615 100644 --- a/lib/Target/ARM/AsmPrinter/ARMAsmPrinter.cpp +++ b/lib/Target/ARM/AsmPrinter/ARMAsmPrinter.cpp @@ -97,6 +97,7 @@ namespace { const char *Modifier = 0); void printSOImmOperand(const MachineInstr *MI, int opNum); void printSOImm2PartOperand(const MachineInstr *MI, int opNum); + void printSOOperand(const MachineInstr *MI, int OpNum); void printSORegOperand(const MachineInstr *MI, int opNum); void printAddrMode2Operand(const MachineInstr *MI, int OpNo); void printAddrMode2OffsetOperand(const MachineInstr *MI, int OpNo); @@ -396,6 +397,28 @@ void ARMAsmPrinter::printSOImm2PartOperand(const MachineInstr *MI, int OpNum) { printSOImm(O, ARM_AM::getSOImmVal(V2), VerboseAsm, TAI); } +// Constant shifts so_reg is a 3-operand unit corresponding to register forms of +// the A5.1 "Addressing Mode 1 - Data-processing operands" forms. This +// includes: +// REG 0 - e.g. R5 +// REG IMM, SH_OPC - e.g. R5, LSL #3 +void ARMAsmPrinter::printSOOperand(const MachineInstr *MI, int OpNum) { + const MachineOperand &MO1 = MI->getOperand(OpNum); + const MachineOperand &MO2 = MI->getOperand(OpNum+1); + + unsigned Reg = MO1.getReg(); + assert(TargetRegisterInfo::isPhysicalRegister(Reg)); + O << TM.getRegisterInfo()->getAsmName(Reg); + + // Print the shift opc. + O << ", " + << ARM_AM::getShiftOpcStr(ARM_AM::getSORegShOp(MO2.getImm())) + << " "; + + assert(MO2.isImm() && "Not a valid t2_so_reg value!"); + O << "#" << ARM_AM::getSORegOffset(MO2.getImm()); +} + // so_reg is a 4-operand unit corresponding to register forms of the A5.1 // "Addressing Mode 1 - Data-processing operands" forms. This includes: // REG 0 0 - e.g. R5 diff --git a/test/CodeGen/ARM/thumb2-add.ll b/test/CodeGen/ARM/thumb2-add.ll new file mode 100644 index 00000000000..d4f408ff76e --- /dev/null +++ b/test/CodeGen/ARM/thumb2-add.ll @@ -0,0 +1,50 @@ +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep add | grep #255 +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep add | grep #256 +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep add | grep #257 +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep add | grep #4094 +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep add | grep #4095 +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep add | grep #4096 +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep add +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep add | grep lsl | grep #8 + +define i32 @t2ADDrc_255(i32 %lhs) { + %Rd = add i32 %lhs, 255; + ret i32 %Rd +} + +define i32 @t2ADDrc_256(i32 %lhs) { + %Rd = add i32 %lhs, 256; + ret i32 %Rd +} + +define i32 @t2ADDrc_257(i32 %lhs) { + %Rd = add i32 %lhs, 257; + ret i32 %Rd +} + +define i32 @t2ADDrc_4094(i32 %lhs) { + %Rd = add i32 %lhs, 4094; + ret i32 %Rd +} + +define i32 @t2ADDrc_4095(i32 %lhs) { + %Rd = add i32 %lhs, 4095; + ret i32 %Rd +} + +define i32 @t2ADDrc_4096(i32 %lhs) { + %Rd = add i32 %lhs, 4096; + ret i32 %Rd +} + +define i32 @t2ADDrr(i32 %lhs, i32 %rhs) { + %Rd = add i32 %lhs, %rhs; + ret i32 %Rd +} + +define i32 @t2ADDrs(i32 %lhs, i32 %rhs) { + %tmp = shl i32 %rhs, 8 + %Rd = add i32 %lhs, %tmp; + ret i32 %Rd +} + diff --git a/test/CodeGen/ARM/thumb2-mov.ll b/test/CodeGen/ARM/thumb2-mov.ll new file mode 100644 index 00000000000..0c4c59689b6 --- /dev/null +++ b/test/CodeGen/ARM/thumb2-mov.ll @@ -0,0 +1,127 @@ +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep #11206827 +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep mov | grep movt +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep mov | grep movt +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep mov | grep movt +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep mov | grep movt +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep #2868947712 +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep mov | grep movt +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep mov | grep movt +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep mov | grep movt +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep mov | grep movt +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep #2880154539 +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep mov | grep movt +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep mov | grep movt +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep mov | grep movt +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep mov | grep movt +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep #251658240 +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep #3948544 +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep mov | grep movt +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep #258 +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep #4026531840 + +; Test # + +; var 2.1 - 0x00ab00ab +define i32 @t2_const_var2_1_ok_1(i32 %lhs) { + %ret = add i32 %lhs, 11206827 ; 0x00ab00ab + ret i32 %ret +} + +define i32 @t2_const_var2_1_fail_1(i32 %lhs) { + %ret = add i32 %lhs, 11206843 ; 0x00ab00bb + ret i32 %ret +} + +define i32 @t2_const_var2_1_fail_2(i32 %lhs) { + %ret = add i32 %lhs, 27984043 ; 0x01ab00ab + ret i32 %ret +} + +define i32 @t2_const_var2_1_fail_3(i32 %lhs) { + %ret = add i32 %lhs, 27984299 ; 0x01ab01ab + ret i32 %ret +} + +define i32 @t2_const_var2_1_fail_4(i32 %lhs) { + %ret = add i32 %lhs, 28027649 ; 0x01abab01 + ret i32 %ret +} + +; var 2.2 - 0xab00ab00 +define i32 @t2_const_var2_2_ok_1(i32 %lhs) { + %ret = add i32 %lhs, 2868947712 ; 0xab00ab00 + ret i32 %ret +} + +define i32 @t2_const_var2_2_fail_1(i32 %lhs) { + %ret = add i32 %lhs, 2868951552 ; 0xab00ba00 + ret i32 %ret +} + +define i32 @t2_const_var2_2_fail_2(i32 %lhs) { + %ret = add i32 %lhs, 2868947728 ; 0xab00ab10 + ret i32 %ret +} + +define i32 @t2_const_var2_2_fail_3(i32 %lhs) { + %ret = add i32 %lhs, 2869996304 ; 0xab10ab10 + ret i32 %ret +} + +define i32 @t2_const_var2_2_fail_4(i32 %lhs) { + %ret = add i32 %lhs, 279685904 ; 0x10abab10 + ret i32 %ret +} + +; var 2.3 - 0xabababab +define i32 @t2_const_var2_3_ok_1(i32 %lhs) { + %ret = add i32 %lhs, 2880154539 ; 0xabababab + ret i32 %ret +} + +define i32 @t2_const_var2_3_fail_1(i32 %lhs) { + %ret = add i32 %lhs, 2880154554 ; 0xabababba + ret i32 %ret +} + +define i32 @t2_const_var2_3_fail_2(i32 %lhs) { + %ret = add i32 %lhs, 2880158379 ; 0xababbaab + ret i32 %ret +} + +define i32 @t2_const_var2_3_fail_3(i32 %lhs) { + %ret = add i32 %lhs, 2881137579 ; 0xabbaabab + ret i32 %ret +} + +define i32 @t2_const_var2_3_fail_4(i32 %lhs) { + %ret = add i32 %lhs, 3131812779 ; 0xbaababab + ret i32 %ret +} + +; var 3 - 0x0F000000 +define i32 @t2_const_var3_1_ok_1(i32 %lhs) { + %ret = add i32 %lhs, 251658240 ; 0x0F000000 + ret i32 %ret +} + +define i32 @t2_const_var3_2_ok_1(i32 %lhs) { + %ret = add i32 %lhs, 3948544 ; 0b00000000001111000100000000000000 + ret i32 %ret +} + +define i32 @t2_const_var3_2_fail_1(i32 %lhs) { + %ret = add i32 %lhs, 3940352 ; 0b00000000001111000010000000000000 + ret i32 %ret +} + +define i32 @t2_const_var3_3_ok_1(i32 %lhs) { + %ret = add i32 %lhs, 258 ; 0b00000000000000000000000100000010 + ret i32 %ret +} + +define i32 @t2_const_var3_4_ok_1(i32 %lhs) { + %ret = add i32 %lhs, 4026531840 ; 0xF0000000 + ret i32 %ret +} + diff --git a/test/CodeGen/ARM/thumb2-mov2.ll b/test/CodeGen/ARM/thumb2-mov2.ll new file mode 100644 index 00000000000..d2f8c0b91a5 --- /dev/null +++ b/test/CodeGen/ARM/thumb2-mov2.ll @@ -0,0 +1,65 @@ +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep movt | grep #1234 +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep movt | grep #1234 +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep movt | grep #1234 +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep movt | grep #1234 +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep mov | grep movt + +define i32 @t2MOVTi16_ok_1(i32 %a) { + %1 = and i32 %a, 65535 + %2 = shl i32 1234, 16 + %3 = or i32 %1, %2 + + ret i32 %3 +} + +define i32 @t2MOVTi16_test_1(i32 %a) { + %1 = shl i32 255, 8 + %2 = shl i32 1234, 8 + %3 = or i32 %1, 255 ; This give us 0xFFFF in %3 + %4 = shl i32 %2, 8 ; This gives us (1234 << 16) in %4 + %5 = and i32 %a, %3 + %6 = or i32 %4, %5 + + ret i32 %6 +} + +define i32 @t2MOVTi16_test_2(i32 %a) { + %1 = shl i32 255, 8 + %2 = shl i32 1234, 8 + %3 = or i32 %1, 255 ; This give us 0xFFFF in %3 + %4 = shl i32 %2, 6 + %5 = and i32 %a, %3 + %6 = shl i32 %4, 2 ; This gives us (1234 << 16) in %6 + %7 = or i32 %5, %6 + + ret i32 %7 +} + +define i32 @t2MOVTi16_test_3(i32 %a) { + %1 = shl i32 255, 8 + %2 = shl i32 1234, 8 + %3 = or i32 %1, 255 ; This give us 0xFFFF in %3 + %4 = shl i32 %2, 6 + %5 = and i32 %a, %3 + %6 = shl i32 %4, 2 ; This gives us (1234 << 16) in %6 + %7 = lshr i32 %6, 6 + %8 = shl i32 %7, 6 + %9 = or i32 %5, %8 + + ret i32 %9 +} + +define i32 @t2MOVTi16_test_nomatch_1(i32 %a) { + %1 = shl i32 255, 8 + %2 = shl i32 1234, 8 + %3 = or i32 %1, 255 ; This give us 0xFFFF in %3 + %4 = shl i32 %2, 6 + %5 = and i32 %a, %3 + %6 = shl i32 %4, 2 ; This gives us (1234 << 16) in %6 + %7 = lshr i32 %6, 3 + %8 = or i32 %5, %7 + + ret i32 %8 +} + + diff --git a/test/CodeGen/ARM/thumb2-shifter.ll b/test/CodeGen/ARM/thumb2-shifter.ll new file mode 100644 index 00000000000..f9ec5067ec0 --- /dev/null +++ b/test/CodeGen/ARM/thumb2-shifter.ll @@ -0,0 +1,40 @@ +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep lsl +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep lsr +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep asr +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep ror +; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep mov + +define i32 @t2ADDrs_lsl(i32 %X, i32 %Y) { + %A = shl i32 %Y, 16 + %B = add i32 %X, %A + ret i32 %B +} + +define i32 @t2ADDrs_lsr(i32 %X, i32 %Y) { + %A = lshr i32 %Y, 16 + %B = add i32 %X, %A + ret i32 %B +} + +define i32 @t2ADDrs_asr(i32 %X, i32 %Y) { + %A = ashr i32 %Y, 16 + %B = add i32 %X, %A + ret i32 %B +} + +; i32 ror(n) = (x >> n) | (x << (32 - n)) +define i32 @t2ADDrs_ror(i32 %X, i32 %Y) { + %A = lshr i32 %Y, 16 + %B = shl i32 %Y, 16 + %C = or i32 %B, %A + %R = add i32 %X, %C + ret i32 %R +} + +define i32 @t2ADDrs_noRegShift(i32 %X, i32 %Y, i8 %sh) { + %shift.upgrd.1 = zext i8 %sh to i32 + %A = shl i32 %Y, %shift.upgrd.1 + %B = add i32 %X, %A + ret i32 %B +} +