2012-02-18 20:03:15 +08:00
|
|
|
//===-- ARMInstrThumb.td - Thumb support for ARM -----------*- tablegen -*-===//
|
2007-01-19 15:51:42 +08:00
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
2007-12-30 04:36:04 +08:00
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
2007-01-19 15:51:42 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This file describes the Thumb instruction set.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Thumb specific DAG Nodes.
|
|
|
|
//
|
|
|
|
|
2011-08-18 05:51:27 +08:00
|
|
|
def imm_sr_XFORM: SDNodeXForm<imm, [{
|
|
|
|
unsigned Imm = N->getZExtValue();
|
2015-04-28 22:05:47 +08:00
|
|
|
return CurDAG->getTargetConstant((Imm == 32 ? 0 : Imm), SDLoc(N), MVT::i32);
|
2011-08-18 05:51:27 +08:00
|
|
|
}]>;
|
2017-04-03 22:50:04 +08:00
|
|
|
def ThumbSRImmAsmOperand: ImmAsmOperand<1,32> { let Name = "ImmThumbSR"; }
|
2011-08-18 05:51:27 +08:00
|
|
|
def imm_sr : Operand<i32>, PatLeaf<(imm), [{
|
|
|
|
uint64_t Imm = N->getZExtValue();
|
2011-08-09 04:42:17 +08:00
|
|
|
return Imm > 0 && Imm <= 32;
|
2011-08-18 05:51:27 +08:00
|
|
|
}], imm_sr_XFORM> {
|
|
|
|
let PrintMethod = "printThumbSRImm";
|
|
|
|
let ParserMatchClass = ThumbSRImmAsmOperand;
|
2011-08-09 04:42:17 +08:00
|
|
|
}
|
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
def imm0_7_neg : PatLeaf<(i32 imm), [{
|
2008-09-13 00:56:44 +08:00
|
|
|
return (uint32_t)-N->getZExtValue() < 8;
|
2007-01-19 15:51:42 +08:00
|
|
|
}], imm_neg_XFORM>;
|
|
|
|
|
[ARM] [Assembler] Support negative immediates for A32, T32 and T16
Summary:
To support negative immediates for certain arithmetic instructions, the
instruction is converted to the inverse instruction with a negated (or inverted)
immediate. For example, "ADD r0, r1, #FFFFFFFF" cannot be encoded as an ADD
instruction. However, "SUB r0, r1, #1" is equivalent.
These conversions are different from instruction aliases. An alias maps
several assembler instructions onto one encoding. A conversion, however, maps
an *invalid* instruction--e.g. with an immediate that cannot be represented in
the encoding--to a different (but equivalent) instruction.
Several instructions with negative immediates were being converted already, but
this was not systematically tested, nor did it cover all instructions.
This patch implements all possible substitutions for ARM, Thumb1 and
Thumb2 assembler and adds tests. It also adds a feature flag
(-mattr=+no-neg-immediates) to turn these substitutions off. This is
helpful for users who want their code to assemble to exactly what they
wrote.
Reviewers: t.p.northover, rovka, samparker, javed.absar, peter.smith, rengolin
Reviewed By: javed.absar
Subscribers: aadg, aemerson, llvm-commits
Differential Revision: https://reviews.llvm.org/D30571
llvm-svn: 298380
2017-03-21 22:59:17 +08:00
|
|
|
def ThumbModImmNeg1_7AsmOperand : AsmOperandClass { let Name = "ThumbModImmNeg1_7"; }
|
|
|
|
def mod_imm1_7_neg : Operand<i32>, PatLeaf<(imm), [{
|
|
|
|
unsigned Value = -(unsigned)N->getZExtValue();
|
|
|
|
return 0 < Value && Value < 8;
|
|
|
|
}], imm_neg_XFORM> {
|
|
|
|
let ParserMatchClass = ThumbModImmNeg1_7AsmOperand;
|
|
|
|
}
|
|
|
|
|
|
|
|
def ThumbModImmNeg8_255AsmOperand : AsmOperandClass { let Name = "ThumbModImmNeg8_255"; }
|
|
|
|
def mod_imm8_255_neg : Operand<i32>, PatLeaf<(imm), [{
|
|
|
|
unsigned Value = -(unsigned)N->getZExtValue();
|
|
|
|
return 7 < Value && Value < 256;
|
|
|
|
}], imm_neg_XFORM> {
|
|
|
|
let ParserMatchClass = ThumbModImmNeg8_255AsmOperand;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
def imm0_255_comp : PatLeaf<(i32 imm), [{
|
2008-09-13 00:56:44 +08:00
|
|
|
return ~((uint32_t)N->getZExtValue()) < 256;
|
2007-01-19 15:51:42 +08:00
|
|
|
}]>;
|
|
|
|
|
|
|
|
def imm8_255_neg : PatLeaf<(i32 imm), [{
|
2008-09-13 00:56:44 +08:00
|
|
|
unsigned Val = -N->getZExtValue();
|
2007-01-19 15:51:42 +08:00
|
|
|
return Val >= 8 && Val < 256;
|
|
|
|
}], imm_neg_XFORM>;
|
|
|
|
|
2010-12-01 10:36:55 +08:00
|
|
|
// Break imm's up into two pieces: an immediate + a left shift. This uses
|
|
|
|
// thumb_immshifted to match and thumb_immshifted_val and thumb_immshifted_shamt
|
|
|
|
// to get the val/shift pieces.
|
2007-01-19 15:51:42 +08:00
|
|
|
def thumb_immshifted : PatLeaf<(imm), [{
|
2008-09-13 00:56:44 +08:00
|
|
|
return ARM_AM::isThumbImmShiftedVal((unsigned)N->getZExtValue());
|
2007-01-19 15:51:42 +08:00
|
|
|
}]>;
|
|
|
|
|
|
|
|
def thumb_immshifted_val : SDNodeXForm<imm, [{
|
2008-09-13 00:56:44 +08:00
|
|
|
unsigned V = ARM_AM::getThumbImmNonShiftedVal((unsigned)N->getZExtValue());
|
2015-04-28 22:05:47 +08:00
|
|
|
return CurDAG->getTargetConstant(V, SDLoc(N), MVT::i32);
|
2007-01-19 15:51:42 +08:00
|
|
|
}]>;
|
|
|
|
|
|
|
|
def thumb_immshifted_shamt : SDNodeXForm<imm, [{
|
2008-09-13 00:56:44 +08:00
|
|
|
unsigned V = ARM_AM::getThumbImmValShift((unsigned)N->getZExtValue());
|
2015-04-28 22:05:47 +08:00
|
|
|
return CurDAG->getTargetConstant(V, SDLoc(N), MVT::i32);
|
2007-01-19 15:51:42 +08:00
|
|
|
}]>;
|
|
|
|
|
2016-06-14 21:33:07 +08:00
|
|
|
def imm256_510 : ImmLeaf<i32, [{
|
|
|
|
return Imm >= 256 && Imm < 511;
|
2016-06-07 21:10:14 +08:00
|
|
|
}]>;
|
|
|
|
|
2016-06-14 21:33:07 +08:00
|
|
|
def thumb_imm256_510_addend : SDNodeXForm<imm, [{
|
2016-06-07 21:10:14 +08:00
|
|
|
return CurDAG->getTargetConstant(N->getZExtValue() - 255, SDLoc(N), MVT::i32);
|
|
|
|
}]>;
|
|
|
|
|
2009-11-19 14:57:41 +08:00
|
|
|
// Scaled 4 immediate.
|
2011-08-25 05:22:15 +08:00
|
|
|
def t_imm0_1020s4_asmoperand: AsmOperandClass { let Name = "Imm0_1020s4"; }
|
|
|
|
def t_imm0_1020s4 : Operand<i32> {
|
2009-11-19 14:57:41 +08:00
|
|
|
let PrintMethod = "printThumbS4ImmOperand";
|
2011-08-25 05:22:15 +08:00
|
|
|
let ParserMatchClass = t_imm0_1020s4_asmoperand;
|
|
|
|
let OperandType = "OPERAND_IMMEDIATE";
|
|
|
|
}
|
|
|
|
|
|
|
|
def t_imm0_508s4_asmoperand: AsmOperandClass { let Name = "Imm0_508s4"; }
|
|
|
|
def t_imm0_508s4 : Operand<i32> {
|
|
|
|
let PrintMethod = "printThumbS4ImmOperand";
|
|
|
|
let ParserMatchClass = t_imm0_508s4_asmoperand;
|
2011-07-15 05:47:24 +08:00
|
|
|
let OperandType = "OPERAND_IMMEDIATE";
|
2009-11-19 14:57:41 +08:00
|
|
|
}
|
2012-04-06 04:57:13 +08:00
|
|
|
// Alias use only, so no printer is necessary.
|
|
|
|
def t_imm0_508s4_neg_asmoperand: AsmOperandClass { let Name = "Imm0_508s4Neg"; }
|
|
|
|
def t_imm0_508s4_neg : Operand<i32> {
|
|
|
|
let ParserMatchClass = t_imm0_508s4_neg_asmoperand;
|
|
|
|
let OperandType = "OPERAND_IMMEDIATE";
|
|
|
|
}
|
2009-11-19 14:57:41 +08:00
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
// Define Thumb specific addressing modes.
|
|
|
|
|
2013-07-03 17:21:44 +08:00
|
|
|
// unsigned 8-bit, 2-scaled memory offset
|
|
|
|
class OperandUnsignedOffset_b8s2 : AsmOperandClass {
|
|
|
|
let Name = "UnsignedOffset_b8s2";
|
|
|
|
let PredicateMethod = "isUnsignedOffset<8, 2>";
|
|
|
|
}
|
|
|
|
|
|
|
|
def UnsignedOffset_b8s2 : OperandUnsignedOffset_b8s2;
|
|
|
|
|
2013-07-22 23:49:36 +08:00
|
|
|
// thumb style PC relative operand. signed, 8 bits magnitude,
|
|
|
|
// two bits shift. can be represented as either [pc, #imm], #imm,
|
|
|
|
// or relocatable expression...
|
|
|
|
def ThumbMemPC : AsmOperandClass {
|
|
|
|
let Name = "ThumbMemPC";
|
|
|
|
}
|
|
|
|
|
2011-07-15 05:47:24 +08:00
|
|
|
let OperandType = "OPERAND_PCREL" in {
|
2010-12-11 02:21:33 +08:00
|
|
|
def t_brtarget : Operand<OtherVT> {
|
|
|
|
let EncoderMethod = "getThumbBRTargetOpValue";
|
2011-08-10 04:55:18 +08:00
|
|
|
let DecoderMethod = "DecodeThumbBROperand";
|
2010-12-11 02:21:33 +08:00
|
|
|
}
|
|
|
|
|
2013-07-03 17:21:44 +08:00
|
|
|
// ADR instruction labels.
|
|
|
|
def t_adrlabel : Operand<i32> {
|
|
|
|
let EncoderMethod = "getThumbAdrLabelOpValue";
|
|
|
|
let PrintMethod = "printAdrLabelOperand<2>";
|
|
|
|
let ParserMatchClass = UnsignedOffset_b8s2;
|
|
|
|
}
|
|
|
|
|
2010-12-11 01:13:40 +08:00
|
|
|
|
2016-07-12 06:29:37 +08:00
|
|
|
def thumb_br_target : Operand<OtherVT> {
|
|
|
|
let ParserMatchClass = ThumbBranchTarget;
|
|
|
|
let EncoderMethod = "getThumbBranchTargetOpValue";
|
|
|
|
let OperandType = "OPERAND_PCREL";
|
2010-12-09 07:01:43 +08:00
|
|
|
}
|
|
|
|
|
2016-07-12 06:29:37 +08:00
|
|
|
def thumb_bl_target : Operand<i32> {
|
|
|
|
let ParserMatchClass = ThumbBranchTarget;
|
2010-12-07 07:57:07 +08:00
|
|
|
let EncoderMethod = "getThumbBLTargetOpValue";
|
2011-08-09 07:25:22 +08:00
|
|
|
let DecoderMethod = "DecodeThumbBLTargetOperand";
|
2010-12-07 07:57:07 +08:00
|
|
|
}
|
|
|
|
|
2016-07-12 06:29:37 +08:00
|
|
|
// Target for BLX *from* thumb mode.
|
|
|
|
def thumb_blx_target : Operand<i32> {
|
|
|
|
let ParserMatchClass = ARMBranchTarget;
|
2010-12-09 08:39:08 +08:00
|
|
|
let EncoderMethod = "getThumbBLXTargetOpValue";
|
2011-08-09 04:42:17 +08:00
|
|
|
let DecoderMethod = "DecodeThumbBLXOffset";
|
2010-12-09 08:39:08 +08:00
|
|
|
}
|
2013-07-22 23:49:36 +08:00
|
|
|
|
2016-07-12 06:29:37 +08:00
|
|
|
def thumb_bcc_target : Operand<OtherVT> {
|
|
|
|
let ParserMatchClass = ThumbBranchTarget;
|
|
|
|
let EncoderMethod = "getThumbBCCTargetOpValue";
|
|
|
|
let DecoderMethod = "DecodeThumbBCCTargetOperand";
|
|
|
|
}
|
|
|
|
|
|
|
|
def thumb_cb_target : Operand<OtherVT> {
|
|
|
|
let ParserMatchClass = ThumbBranchTarget;
|
|
|
|
let EncoderMethod = "getThumbCBTargetOpValue";
|
|
|
|
let DecoderMethod = "DecodeThumbCmpBROperand";
|
|
|
|
}
|
|
|
|
|
2013-07-22 23:49:36 +08:00
|
|
|
// t_addrmode_pc := <label> => pc + imm8 * 4
|
|
|
|
//
|
2015-04-08 04:31:16 +08:00
|
|
|
def t_addrmode_pc : MemOperand {
|
2013-07-22 23:49:36 +08:00
|
|
|
let EncoderMethod = "getAddrModePCOpValue";
|
|
|
|
let DecoderMethod = "DecodeThumbAddrModePC";
|
|
|
|
let PrintMethod = "printThumbLdrLabelOperand";
|
|
|
|
let ParserMatchClass = ThumbMemPC;
|
|
|
|
}
|
2011-07-15 05:47:24 +08:00
|
|
|
}
|
2010-12-09 08:39:08 +08:00
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
// t_addrmode_rr := reg + reg
|
|
|
|
//
|
2011-08-04 07:50:40 +08:00
|
|
|
def t_addrmode_rr_asm_operand : AsmOperandClass { let Name = "MemThumbRR"; }
|
2015-04-08 04:31:16 +08:00
|
|
|
def t_addrmode_rr : MemOperand,
|
2007-01-19 15:51:42 +08:00
|
|
|
ComplexPattern<i32, 2, "SelectThumbAddrModeRR", []> {
|
2010-12-14 11:36:38 +08:00
|
|
|
let EncoderMethod = "getThumbAddrModeRegRegOpValue";
|
|
|
|
let PrintMethod = "printThumbAddrModeRROperand";
|
2011-08-16 03:00:06 +08:00
|
|
|
let DecoderMethod = "DecodeThumbAddrModeRR";
|
2011-08-20 03:17:58 +08:00
|
|
|
let ParserMatchClass = t_addrmode_rr_asm_operand;
|
2010-12-14 11:36:38 +08:00
|
|
|
let MIOperandInfo = (ops tGPR:$base, tGPR:$offsreg);
|
|
|
|
}
|
|
|
|
|
|
|
|
// t_addrmode_rrs := reg + reg
|
|
|
|
//
|
2011-08-20 00:52:32 +08:00
|
|
|
// We use separate scaled versions because the Select* functions need
|
|
|
|
// to explicitly check for a matching constant and return false here so that
|
|
|
|
// the reg+imm forms will match instead. This is a horrible way to do that,
|
|
|
|
// as it forces tight coupling between the methods, but it's how selectiondag
|
|
|
|
// currently works.
|
2015-04-08 04:31:16 +08:00
|
|
|
def t_addrmode_rrs1 : MemOperand,
|
2010-12-14 11:36:38 +08:00
|
|
|
ComplexPattern<i32, 2, "SelectThumbAddrModeRI5S1", []> {
|
|
|
|
let EncoderMethod = "getThumbAddrModeRegRegOpValue";
|
2007-01-19 15:51:42 +08:00
|
|
|
let PrintMethod = "printThumbAddrModeRROperand";
|
2011-08-10 04:55:18 +08:00
|
|
|
let DecoderMethod = "DecodeThumbAddrModeRR";
|
2011-08-04 07:50:40 +08:00
|
|
|
let ParserMatchClass = t_addrmode_rr_asm_operand;
|
2009-04-08 04:34:09 +08:00
|
|
|
let MIOperandInfo = (ops tGPR:$base, tGPR:$offsreg);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
2015-04-08 04:31:16 +08:00
|
|
|
def t_addrmode_rrs2 : MemOperand,
|
2010-12-14 11:36:38 +08:00
|
|
|
ComplexPattern<i32, 2, "SelectThumbAddrModeRI5S2", []> {
|
|
|
|
let EncoderMethod = "getThumbAddrModeRegRegOpValue";
|
2011-08-10 04:55:18 +08:00
|
|
|
let DecoderMethod = "DecodeThumbAddrModeRR";
|
2010-12-14 11:36:38 +08:00
|
|
|
let PrintMethod = "printThumbAddrModeRROperand";
|
2011-08-04 07:50:40 +08:00
|
|
|
let ParserMatchClass = t_addrmode_rr_asm_operand;
|
2010-12-14 11:36:38 +08:00
|
|
|
let MIOperandInfo = (ops tGPR:$base, tGPR:$offsreg);
|
|
|
|
}
|
2015-04-08 04:31:16 +08:00
|
|
|
def t_addrmode_rrs4 : MemOperand,
|
2010-12-14 11:36:38 +08:00
|
|
|
ComplexPattern<i32, 2, "SelectThumbAddrModeRI5S4", []> {
|
|
|
|
let EncoderMethod = "getThumbAddrModeRegRegOpValue";
|
2011-08-10 04:55:18 +08:00
|
|
|
let DecoderMethod = "DecodeThumbAddrModeRR";
|
2010-12-14 11:36:38 +08:00
|
|
|
let PrintMethod = "printThumbAddrModeRROperand";
|
2011-08-04 07:50:40 +08:00
|
|
|
let ParserMatchClass = t_addrmode_rr_asm_operand;
|
2010-12-14 11:36:38 +08:00
|
|
|
let MIOperandInfo = (ops tGPR:$base, tGPR:$offsreg);
|
|
|
|
}
|
|
|
|
|
|
|
|
// t_addrmode_is4 := reg + imm5 * 4
|
2007-01-19 15:51:42 +08:00
|
|
|
//
|
2011-08-20 01:55:24 +08:00
|
|
|
def t_addrmode_is4_asm_operand : AsmOperandClass { let Name = "MemThumbRIs4"; }
|
2015-04-08 04:31:16 +08:00
|
|
|
def t_addrmode_is4 : MemOperand,
|
2010-12-14 11:36:38 +08:00
|
|
|
ComplexPattern<i32, 2, "SelectThumbAddrModeImm5S4", []> {
|
|
|
|
let EncoderMethod = "getAddrModeISOpValue";
|
2011-08-10 04:55:18 +08:00
|
|
|
let DecoderMethod = "DecodeThumbAddrModeIS";
|
2010-12-14 11:36:38 +08:00
|
|
|
let PrintMethod = "printThumbAddrModeImm5S4Operand";
|
2011-08-20 01:55:24 +08:00
|
|
|
let ParserMatchClass = t_addrmode_is4_asm_operand;
|
2010-12-14 11:36:38 +08:00
|
|
|
let MIOperandInfo = (ops tGPR:$base, i32imm:$offsimm);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
2007-01-24 06:59:13 +08:00
|
|
|
|
2010-12-14 11:36:38 +08:00
|
|
|
// t_addrmode_is2 := reg + imm5 * 2
|
2007-01-24 06:59:13 +08:00
|
|
|
//
|
2011-08-20 02:55:51 +08:00
|
|
|
def t_addrmode_is2_asm_operand : AsmOperandClass { let Name = "MemThumbRIs2"; }
|
2015-04-08 04:31:16 +08:00
|
|
|
def t_addrmode_is2 : MemOperand,
|
2010-12-14 11:36:38 +08:00
|
|
|
ComplexPattern<i32, 2, "SelectThumbAddrModeImm5S2", []> {
|
|
|
|
let EncoderMethod = "getAddrModeISOpValue";
|
2011-08-10 04:55:18 +08:00
|
|
|
let DecoderMethod = "DecodeThumbAddrModeIS";
|
2010-12-14 11:36:38 +08:00
|
|
|
let PrintMethod = "printThumbAddrModeImm5S2Operand";
|
2011-08-20 02:55:51 +08:00
|
|
|
let ParserMatchClass = t_addrmode_is2_asm_operand;
|
2010-12-14 11:36:38 +08:00
|
|
|
let MIOperandInfo = (ops tGPR:$base, i32imm:$offsimm);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
2007-01-24 06:59:13 +08:00
|
|
|
|
2010-12-14 11:36:38 +08:00
|
|
|
// t_addrmode_is1 := reg + imm5
|
2007-01-24 06:59:13 +08:00
|
|
|
//
|
2011-08-20 02:49:59 +08:00
|
|
|
def t_addrmode_is1_asm_operand : AsmOperandClass { let Name = "MemThumbRIs1"; }
|
2015-04-08 04:31:16 +08:00
|
|
|
def t_addrmode_is1 : MemOperand,
|
2010-12-14 11:36:38 +08:00
|
|
|
ComplexPattern<i32, 2, "SelectThumbAddrModeImm5S1", []> {
|
|
|
|
let EncoderMethod = "getAddrModeISOpValue";
|
2011-08-10 04:55:18 +08:00
|
|
|
let DecoderMethod = "DecodeThumbAddrModeIS";
|
2010-12-14 11:36:38 +08:00
|
|
|
let PrintMethod = "printThumbAddrModeImm5S1Operand";
|
2011-08-20 02:49:59 +08:00
|
|
|
let ParserMatchClass = t_addrmode_is1_asm_operand;
|
2010-12-14 11:36:38 +08:00
|
|
|
let MIOperandInfo = (ops tGPR:$base, i32imm:$offsimm);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// t_addrmode_sp := sp + imm8 * 4
|
|
|
|
//
|
2011-08-24 02:39:41 +08:00
|
|
|
// FIXME: This really shouldn't have an explicit SP operand at all. It should
|
|
|
|
// be implicit, just like in the instruction encoding itself.
|
2011-08-20 02:13:48 +08:00
|
|
|
def t_addrmode_sp_asm_operand : AsmOperandClass { let Name = "MemThumbSPI"; }
|
2015-04-08 04:31:16 +08:00
|
|
|
def t_addrmode_sp : MemOperand,
|
2007-01-19 15:51:42 +08:00
|
|
|
ComplexPattern<i32, 2, "SelectThumbAddrModeSP", []> {
|
2010-12-08 05:50:47 +08:00
|
|
|
let EncoderMethod = "getAddrModeThumbSPOpValue";
|
2011-08-09 07:25:22 +08:00
|
|
|
let DecoderMethod = "DecodeThumbAddrModeSP";
|
2007-01-19 15:51:42 +08:00
|
|
|
let PrintMethod = "printThumbAddrModeSPOperand";
|
2011-08-20 02:13:48 +08:00
|
|
|
let ParserMatchClass = t_addrmode_sp_asm_operand;
|
2010-01-13 08:43:06 +08:00
|
|
|
let MIOperandInfo = (ops GPR:$base, i32imm:$offsimm);
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2018-06-20 20:09:44 +08:00
|
|
|
// Inspects parent to determine whether an or instruction can be implemented as
|
|
|
|
// an add (i.e. whether we know overflow won't occur in the add).
|
|
|
|
def AddLikeOrOp : ComplexPattern<i32, 1, "SelectAddLikeOr", [],
|
|
|
|
[SDNPWantParent]>;
|
|
|
|
|
|
|
|
// Pattern to exclude immediates from matching
|
|
|
|
def non_imm32 : PatLeaf<(i32 GPR), [{ return !isa<ConstantSDNode>(N); }]>;
|
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Miscellaneous Instructions.
|
|
|
|
//
|
|
|
|
|
2010-02-23 07:10:38 +08:00
|
|
|
// FIXME: Marking these as hasSideEffects is necessary to prevent machine DCE
|
|
|
|
// from removing one half of the matched pairs. That breaks PEI, which assumes
|
|
|
|
// these will always be in pairs, and asserts if it finds otherwise. Better way?
|
|
|
|
let Defs = [SP], Uses = [SP], hasSideEffects = 1 in {
|
2007-05-15 09:29:07 +08:00
|
|
|
def tADJCALLSTACKUP :
|
2010-11-20 06:02:18 +08:00
|
|
|
PseudoInst<(outs), (ins i32imm:$amt1, i32imm:$amt2), NoItinerary,
|
|
|
|
[(ARMcallseq_end imm:$amt1, imm:$amt2)]>,
|
|
|
|
Requires<[IsThumb, IsThumb1Only]>;
|
2007-05-15 09:29:07 +08:00
|
|
|
|
2009-03-28 07:06:27 +08:00
|
|
|
def tADJCALLSTACKDOWN :
|
2017-05-09 21:35:13 +08:00
|
|
|
PseudoInst<(outs), (ins i32imm:$amt, i32imm:$amt2), NoItinerary,
|
|
|
|
[(ARMcallseq_start imm:$amt, imm:$amt2)]>,
|
2010-11-20 06:02:18 +08:00
|
|
|
Requires<[IsThumb, IsThumb1Only]>;
|
2007-09-12 03:55:27 +08:00
|
|
|
}
|
2007-05-15 09:29:07 +08:00
|
|
|
|
2011-08-18 07:08:57 +08:00
|
|
|
class T1SystemEncoding<bits<8> opc>
|
2010-11-30 06:15:03 +08:00
|
|
|
: T1Encoding<0b101111> {
|
2011-08-18 07:08:57 +08:00
|
|
|
let Inst{9-8} = 0b11;
|
|
|
|
let Inst{7-0} = opc;
|
2010-11-30 06:15:03 +08:00
|
|
|
}
|
|
|
|
|
2014-04-26 01:24:24 +08:00
|
|
|
def tHINT : T1pI<(outs), (ins imm0_15:$imm), NoItinerary, "hint", "\t$imm",
|
|
|
|
[(int_arm_hint imm0_15:$imm)]>,
|
2013-10-18 22:09:49 +08:00
|
|
|
T1SystemEncoding<0x00>,
|
|
|
|
Requires<[IsThumb, HasV6M]> {
|
|
|
|
bits<4> imm;
|
|
|
|
let Inst{7-4} = imm;
|
|
|
|
}
|
2010-11-30 06:15:03 +08:00
|
|
|
|
2016-06-03 21:19:43 +08:00
|
|
|
// Note: When EmitPriority == 1, the alias will be used for printing
|
|
|
|
class tHintAlias<string Asm, dag Result, bit EmitPriority = 0> : tInstAlias<Asm, Result, EmitPriority> {
|
2013-10-18 22:09:49 +08:00
|
|
|
let Predicates = [IsThumb, HasV6M];
|
|
|
|
}
|
2013-10-01 20:39:11 +08:00
|
|
|
|
2016-06-03 21:19:43 +08:00
|
|
|
def : tHintAlias<"nop$p", (tHINT 0, pred:$p), 1>; // A8.6.110
|
|
|
|
def : tHintAlias<"yield$p", (tHINT 1, pred:$p), 1>; // A8.6.410
|
|
|
|
def : tHintAlias<"wfe$p", (tHINT 2, pred:$p), 1>; // A8.6.408
|
|
|
|
def : tHintAlias<"wfi$p", (tHINT 3, pred:$p), 1>; // A8.6.409
|
|
|
|
def : tHintAlias<"sev$p", (tHINT 4, pred:$p), 1>; // A8.6.157
|
|
|
|
def : tInstAlias<"sevl$p", (tHINT 5, pred:$p), 1> {
|
2013-10-18 22:09:49 +08:00
|
|
|
let Predicates = [IsThumb2, HasV8];
|
|
|
|
}
|
2013-10-01 20:39:11 +08:00
|
|
|
|
2011-08-18 07:08:57 +08:00
|
|
|
// The imm operand $val can be used by a debugger to store more information
|
2010-11-30 06:15:03 +08:00
|
|
|
// about the breakpoint.
|
2011-08-18 07:08:57 +08:00
|
|
|
def tBKPT : T1I<(outs), (ins imm0_255:$val), NoItinerary, "bkpt\t$val",
|
|
|
|
[]>,
|
|
|
|
T1Encoding<0b101111> {
|
|
|
|
let Inst{9-8} = 0b10;
|
2010-11-30 06:15:03 +08:00
|
|
|
// A8.6.22
|
|
|
|
bits<8> val;
|
|
|
|
let Inst{7-0} = val;
|
|
|
|
}
|
2013-12-24 01:23:58 +08:00
|
|
|
// default immediate for breakpoint mnemonic
|
2016-06-03 21:19:43 +08:00
|
|
|
def : InstAlias<"bkpt", (tBKPT 0), 0>, Requires<[IsThumb]>;
|
2010-02-26 01:51:03 +08:00
|
|
|
|
2013-09-05 22:14:19 +08:00
|
|
|
def tHLT : T1I<(outs), (ins imm0_63:$val), NoItinerary, "hlt\t$val",
|
|
|
|
[]>, T1Encoding<0b101110>, Requires<[IsThumb, HasV8]> {
|
|
|
|
let Inst{9-6} = 0b1010;
|
|
|
|
bits<6> val;
|
|
|
|
let Inst{5-0} = val;
|
|
|
|
}
|
|
|
|
|
2011-07-23 01:52:23 +08:00
|
|
|
def tSETEND : T1I<(outs), (ins setend_op:$end), NoItinerary, "setend\t$end",
|
2017-10-24 17:03:33 +08:00
|
|
|
[]>, T1Encoding<0b101101>, Requires<[IsThumb, IsNotMClass]>, Deprecated<HasV8Ops> {
|
2011-07-23 01:52:23 +08:00
|
|
|
bits<1> end;
|
2010-11-21 18:55:23 +08:00
|
|
|
// A8.6.156
|
2010-02-26 01:51:03 +08:00
|
|
|
let Inst{9-5} = 0b10010;
|
2010-11-20 06:02:18 +08:00
|
|
|
let Inst{4} = 1;
|
2011-07-23 01:52:23 +08:00
|
|
|
let Inst{3} = end;
|
2010-11-20 06:02:18 +08:00
|
|
|
let Inst{2-0} = 0b000;
|
2010-02-26 01:51:03 +08:00
|
|
|
}
|
|
|
|
|
Added 32-bit Thumb instructions: CPS, SDIV, UDIV, SXTB16, SXTAB16, UXTAB16, SEL,
SMMULR, SMMLAR, SMMLSR, TBB, TBH, and 16-bit Thumb instruction CPS for
disassembly only.
llvm-svn: 97573
2010-03-03 02:14:57 +08:00
|
|
|
// Change Processor State is a system instruction -- for disassembly only.
|
2011-02-14 21:09:44 +08:00
|
|
|
def tCPS : T1I<(outs), (ins imod_op:$imod, iflags_op:$iflags),
|
2011-09-20 08:00:06 +08:00
|
|
|
NoItinerary, "cps$imod $iflags", []>,
|
2010-11-29 08:18:15 +08:00
|
|
|
T1Misc<0b0110011> {
|
|
|
|
// A8.6.38 & B6.1.1
|
2011-02-14 21:09:44 +08:00
|
|
|
bit imod;
|
|
|
|
bits<3> iflags;
|
|
|
|
|
|
|
|
let Inst{4} = imod;
|
|
|
|
let Inst{3} = 0;
|
|
|
|
let Inst{2-0} = iflags;
|
2011-08-10 04:55:18 +08:00
|
|
|
let DecoderMethod = "DecodeThumbCPS";
|
2010-11-29 08:18:15 +08:00
|
|
|
}
|
Added 32-bit Thumb instructions: CPS, SDIV, UDIV, SXTB16, SXTAB16, UXTAB16, SEL,
SMMULR, SMMLAR, SMMLSR, TBB, TBH, and 16-bit Thumb instruction CPS for
disassembly only.
llvm-svn: 97573
2010-03-03 02:14:57 +08:00
|
|
|
|
2009-08-05 07:47:55 +08:00
|
|
|
// For both thumb1 and thumb2.
|
2010-11-01 03:15:18 +08:00
|
|
|
let isNotDuplicable = 1, isCodeGenOnly = 1 in
|
2010-10-01 03:53:58 +08:00
|
|
|
def tPICADD : TIt<(outs GPR:$dst), (ins GPR:$lhs, pclabel:$cp), IIC_iALUr, "",
|
2010-11-20 06:37:33 +08:00
|
|
|
[(set GPR:$dst, (ARMpic_add GPR:$lhs, imm:$cp))]>,
|
2013-06-07 01:03:13 +08:00
|
|
|
T1Special<{0,0,?,?}>, Sched<[WriteALU]> {
|
2010-11-30 08:50:22 +08:00
|
|
|
// A8.6.6
|
2010-11-20 06:37:33 +08:00
|
|
|
bits<3> dst;
|
2010-11-30 08:50:22 +08:00
|
|
|
let Inst{6-3} = 0b1111; // Rm = pc
|
2010-11-20 06:37:33 +08:00
|
|
|
let Inst{2-0} = dst;
|
2009-12-16 01:24:14 +08:00
|
|
|
}
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2010-11-20 06:37:33 +08:00
|
|
|
// ADD <Rd>, sp, #<imm8>
|
2011-10-15 08:57:13 +08:00
|
|
|
// FIXME: This should not be marked as having side effects, and it should be
|
|
|
|
// rematerializable. Clearing the side effect bit causes miscompilations,
|
|
|
|
// probably because the instruction can be moved around.
|
2011-08-25 05:22:15 +08:00
|
|
|
def tADDrSPi : T1pI<(outs tGPR:$dst), (ins GPRsp:$sp, t_imm0_1020s4:$imm),
|
|
|
|
IIC_iALUi, "add", "\t$dst, $sp, $imm", []>,
|
2013-06-07 01:03:13 +08:00
|
|
|
T1Encoding<{1,0,1,0,1,?}>, Sched<[WriteALU]> {
|
2010-11-20 06:37:33 +08:00
|
|
|
// A6.2 & A8.6.8
|
|
|
|
bits<3> dst;
|
2011-08-25 05:22:15 +08:00
|
|
|
bits<8> imm;
|
2010-11-20 06:37:33 +08:00
|
|
|
let Inst{10-8} = dst;
|
2011-08-25 05:22:15 +08:00
|
|
|
let Inst{7-0} = imm;
|
2011-08-10 04:55:18 +08:00
|
|
|
let DecoderMethod = "DecodeThumbAddSpecialReg";
|
2010-08-31 03:49:58 +08:00
|
|
|
}
|
2009-06-25 09:05:06 +08:00
|
|
|
|
2014-10-21 05:28:41 +08:00
|
|
|
// Thumb1 frame lowering is rather fragile, we hope to be able to use
|
|
|
|
// tADDrSPi, but we may need to insert a sequence that clobbers CPSR.
|
|
|
|
def tADDframe : PseudoInst<(outs tGPR:$dst), (ins i32imm:$base, i32imm:$offset),
|
|
|
|
NoItinerary, []>,
|
|
|
|
Requires<[IsThumb, IsThumb1Only]> {
|
|
|
|
let Defs = [CPSR];
|
|
|
|
}
|
|
|
|
|
2010-11-20 06:37:33 +08:00
|
|
|
// ADD sp, sp, #<imm7>
|
2011-08-25 05:22:15 +08:00
|
|
|
def tADDspi : T1pIt<(outs GPRsp:$Rdn), (ins GPRsp:$Rn, t_imm0_508s4:$imm),
|
|
|
|
IIC_iALUi, "add", "\t$Rdn, $imm", []>,
|
2013-06-07 01:03:13 +08:00
|
|
|
T1Misc<{0,0,0,0,0,?,?}>, Sched<[WriteALU]> {
|
2010-11-20 06:37:33 +08:00
|
|
|
// A6.2.5 & A8.6.8
|
2011-08-25 05:22:15 +08:00
|
|
|
bits<7> imm;
|
|
|
|
let Inst{6-0} = imm;
|
2011-08-10 04:55:18 +08:00
|
|
|
let DecoderMethod = "DecodeThumbAddSPImm";
|
2010-11-20 06:37:33 +08:00
|
|
|
}
|
2009-06-25 09:05:06 +08:00
|
|
|
|
2010-11-20 06:37:33 +08:00
|
|
|
// SUB sp, sp, #<imm7>
|
|
|
|
// FIXME: The encoding and the ASM string don't match up.
|
2011-08-25 05:22:15 +08:00
|
|
|
def tSUBspi : T1pIt<(outs GPRsp:$Rdn), (ins GPRsp:$Rn, t_imm0_508s4:$imm),
|
|
|
|
IIC_iALUi, "sub", "\t$Rdn, $imm", []>,
|
2013-06-07 01:03:13 +08:00
|
|
|
T1Misc<{0,0,0,0,1,?,?}>, Sched<[WriteALU]> {
|
2010-11-20 06:37:33 +08:00
|
|
|
// A6.2.5 & A8.6.214
|
2011-08-25 05:22:15 +08:00
|
|
|
bits<7> imm;
|
|
|
|
let Inst{6-0} = imm;
|
2011-08-10 04:55:18 +08:00
|
|
|
let DecoderMethod = "DecodeThumbAddSPImm";
|
2010-11-20 06:37:33 +08:00
|
|
|
}
|
2009-08-07 08:34:42 +08:00
|
|
|
|
[ARM] [Assembler] Support negative immediates for A32, T32 and T16
Summary:
To support negative immediates for certain arithmetic instructions, the
instruction is converted to the inverse instruction with a negated (or inverted)
immediate. For example, "ADD r0, r1, #FFFFFFFF" cannot be encoded as an ADD
instruction. However, "SUB r0, r1, #1" is equivalent.
These conversions are different from instruction aliases. An alias maps
several assembler instructions onto one encoding. A conversion, however, maps
an *invalid* instruction--e.g. with an immediate that cannot be represented in
the encoding--to a different (but equivalent) instruction.
Several instructions with negative immediates were being converted already, but
this was not systematically tested, nor did it cover all instructions.
This patch implements all possible substitutions for ARM, Thumb1 and
Thumb2 assembler and adds tests. It also adds a feature flag
(-mattr=+no-neg-immediates) to turn these substitutions off. This is
helpful for users who want their code to assemble to exactly what they
wrote.
Reviewers: t.p.northover, rovka, samparker, javed.absar, peter.smith, rengolin
Reviewed By: javed.absar
Subscribers: aadg, aemerson, llvm-commits
Differential Revision: https://reviews.llvm.org/D30571
llvm-svn: 298380
2017-03-21 22:59:17 +08:00
|
|
|
def : tInstSubst<"add${p} sp, $imm",
|
2012-04-06 04:57:13 +08:00
|
|
|
(tSUBspi SP, t_imm0_508s4_neg:$imm, pred:$p)>;
|
[ARM] [Assembler] Support negative immediates for A32, T32 and T16
Summary:
To support negative immediates for certain arithmetic instructions, the
instruction is converted to the inverse instruction with a negated (or inverted)
immediate. For example, "ADD r0, r1, #FFFFFFFF" cannot be encoded as an ADD
instruction. However, "SUB r0, r1, #1" is equivalent.
These conversions are different from instruction aliases. An alias maps
several assembler instructions onto one encoding. A conversion, however, maps
an *invalid* instruction--e.g. with an immediate that cannot be represented in
the encoding--to a different (but equivalent) instruction.
Several instructions with negative immediates were being converted already, but
this was not systematically tested, nor did it cover all instructions.
This patch implements all possible substitutions for ARM, Thumb1 and
Thumb2 assembler and adds tests. It also adds a feature flag
(-mattr=+no-neg-immediates) to turn these substitutions off. This is
helpful for users who want their code to assemble to exactly what they
wrote.
Reviewers: t.p.northover, rovka, samparker, javed.absar, peter.smith, rengolin
Reviewed By: javed.absar
Subscribers: aadg, aemerson, llvm-commits
Differential Revision: https://reviews.llvm.org/D30571
llvm-svn: 298380
2017-03-21 22:59:17 +08:00
|
|
|
def : tInstSubst<"add${p} sp, sp, $imm",
|
2012-04-06 04:57:13 +08:00
|
|
|
(tSUBspi SP, t_imm0_508s4_neg:$imm, pred:$p)>;
|
|
|
|
|
2011-08-25 05:42:27 +08:00
|
|
|
// Can optionally specify SP as a three operand instruction.
|
|
|
|
def : tInstAlias<"add${p} sp, sp, $imm",
|
|
|
|
(tADDspi SP, t_imm0_508s4:$imm, pred:$p)>;
|
|
|
|
def : tInstAlias<"sub${p} sp, sp, $imm",
|
|
|
|
(tSUBspi SP, t_imm0_508s4:$imm, pred:$p)>;
|
|
|
|
|
2010-11-20 06:37:33 +08:00
|
|
|
// ADD <Rm>, sp
|
2012-04-28 07:51:36 +08:00
|
|
|
def tADDrSP : T1pI<(outs GPR:$Rdn), (ins GPRsp:$sp, GPR:$Rn), IIC_iALUr,
|
|
|
|
"add", "\t$Rdn, $sp, $Rn", []>,
|
2013-06-07 01:03:13 +08:00
|
|
|
T1Special<{0,0,?,?}>, Sched<[WriteALU]> {
|
2010-11-20 06:37:33 +08:00
|
|
|
// A8.6.9 Encoding T1
|
2011-08-25 01:46:13 +08:00
|
|
|
bits<4> Rdn;
|
|
|
|
let Inst{7} = Rdn{3};
|
2010-11-20 06:37:33 +08:00
|
|
|
let Inst{6-3} = 0b1101;
|
2011-08-25 01:46:13 +08:00
|
|
|
let Inst{2-0} = Rdn{2-0};
|
2011-08-10 04:55:18 +08:00
|
|
|
let DecoderMethod = "DecodeThumbAddSPReg";
|
2009-12-16 01:24:14 +08:00
|
|
|
}
|
2009-08-07 08:34:42 +08:00
|
|
|
|
2010-11-20 06:37:33 +08:00
|
|
|
// ADD sp, <Rm>
|
2011-08-25 05:22:15 +08:00
|
|
|
def tADDspr : T1pIt<(outs GPRsp:$Rdn), (ins GPRsp:$Rn, GPR:$Rm), IIC_iALUr,
|
|
|
|
"add", "\t$Rdn, $Rm", []>,
|
2013-06-07 01:03:13 +08:00
|
|
|
T1Special<{0,0,?,?}>, Sched<[WriteALU]> {
|
2009-12-16 01:24:14 +08:00
|
|
|
// A8.6.9 Encoding T2
|
2011-08-25 05:22:15 +08:00
|
|
|
bits<4> Rm;
|
2009-12-16 01:24:14 +08:00
|
|
|
let Inst{7} = 1;
|
2011-08-25 05:22:15 +08:00
|
|
|
let Inst{6-3} = Rm;
|
2009-12-16 01:24:14 +08:00
|
|
|
let Inst{2-0} = 0b101;
|
2011-08-10 04:55:18 +08:00
|
|
|
let DecoderMethod = "DecodeThumbAddSPReg";
|
2009-12-16 01:24:14 +08:00
|
|
|
}
|
2009-08-07 08:34:42 +08:00
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Control Flow Instructions.
|
|
|
|
//
|
|
|
|
|
2009-10-29 02:26:41 +08:00
|
|
|
// Indirect branches
|
|
|
|
let isBranch = 1, isTerminator = 1, isBarrier = 1, isIndirectBranch = 1 in {
|
2011-05-26 11:41:12 +08:00
|
|
|
def tBX : TI<(outs), (ins GPR:$Rm, pred:$p), IIC_Br, "bx${p}\t$Rm", []>,
|
2013-06-07 02:51:01 +08:00
|
|
|
T1Special<{1,1,0,?}>, Sched<[WriteBr]> {
|
2011-05-26 11:41:12 +08:00
|
|
|
// A6.2.3 & A8.6.25
|
|
|
|
bits<4> Rm;
|
|
|
|
let Inst{6-3} = Rm;
|
|
|
|
let Inst{2-0} = 0b000;
|
2012-02-09 18:56:31 +08:00
|
|
|
let Unpredictable{2-0} = 0b111;
|
2011-05-26 11:41:12 +08:00
|
|
|
}
|
2016-01-25 19:24:47 +08:00
|
|
|
def tBXNS : TI<(outs), (ins GPR:$Rm, pred:$p), IIC_Br, "bxns${p}\t$Rm", []>,
|
|
|
|
Requires<[IsThumb, Has8MSecExt]>,
|
|
|
|
T1Special<{1,1,0,?}>, Sched<[WriteBr]> {
|
|
|
|
bits<4> Rm;
|
|
|
|
let Inst{6-3} = Rm;
|
|
|
|
let Inst{2-0} = 0b100;
|
|
|
|
let Unpredictable{1-0} = 0b11;
|
|
|
|
}
|
2009-10-29 02:26:41 +08:00
|
|
|
}
|
|
|
|
|
2011-07-09 05:04:05 +08:00
|
|
|
let isReturn = 1, isTerminator = 1, isBarrier = 1 in {
|
2011-07-14 07:22:26 +08:00
|
|
|
def tBX_RET : tPseudoExpand<(outs), (ins pred:$p), 2, IIC_Br,
|
2013-06-07 02:51:01 +08:00
|
|
|
[(ARMretflag)], (tBX LR, pred:$p)>, Sched<[WriteBr]>;
|
2011-07-09 05:04:05 +08:00
|
|
|
|
|
|
|
// Alternative return instruction used by vararg functions.
|
2011-07-09 05:50:04 +08:00
|
|
|
def tBX_RET_vararg : tPseudoExpand<(outs), (ins tGPR:$Rm, pred:$p),
|
2011-07-14 07:22:26 +08:00
|
|
|
2, IIC_Br, [],
|
2013-06-07 02:51:01 +08:00
|
|
|
(tBX GPR:$Rm, pred:$p)>, Sched<[WriteBr]>;
|
2011-07-09 05:04:05 +08:00
|
|
|
}
|
|
|
|
|
2010-12-01 10:36:55 +08:00
|
|
|
// All calls clobber the non-callee saved registers. SP is marked as a use to
|
|
|
|
// prevent stack-pointer assignments that appear immediately before calls from
|
|
|
|
// potentially appearing dead.
|
2009-03-28 07:06:27 +08:00
|
|
|
let isCall = 1,
|
2012-02-24 09:19:29 +08:00
|
|
|
Defs = [LR], Uses = [SP] in {
|
2009-08-01 08:16:10 +08:00
|
|
|
// Also used for Thumb2
|
2009-12-16 01:24:14 +08:00
|
|
|
def tBL : TIx2<0b11110, 0b11, 1,
|
2016-07-12 06:29:37 +08:00
|
|
|
(outs), (ins pred:$p, thumb_bl_target:$func), IIC_Br,
|
2011-07-19 02:50:52 +08:00
|
|
|
"bl${p}\t$func",
|
2016-05-11 03:17:47 +08:00
|
|
|
[(ARMcall tglobaladdr:$func)]>,
|
2013-06-07 02:51:01 +08:00
|
|
|
Requires<[IsThumb]>, Sched<[WriteBrL]> {
|
2012-05-04 06:41:56 +08:00
|
|
|
bits<24> func;
|
|
|
|
let Inst{26} = func{23};
|
2010-12-07 07:57:07 +08:00
|
|
|
let Inst{25-16} = func{20-11};
|
2012-05-04 06:41:56 +08:00
|
|
|
let Inst{13} = func{22};
|
|
|
|
let Inst{11} = func{21};
|
2010-12-07 07:57:07 +08:00
|
|
|
let Inst{10-0} = func{10-0};
|
2010-12-03 09:55:47 +08:00
|
|
|
}
|
2009-07-30 05:26:42 +08:00
|
|
|
|
2009-08-01 08:16:10 +08:00
|
|
|
// ARMv5T and above, also used for Thumb2
|
2009-12-16 01:24:14 +08:00
|
|
|
def tBLXi : TIx2<0b11110, 0b11, 0,
|
2016-07-12 06:29:37 +08:00
|
|
|
(outs), (ins pred:$p, thumb_blx_target:$func), IIC_Br,
|
2016-05-11 03:17:47 +08:00
|
|
|
"blx${p}\t$func", []>,
|
2014-08-05 23:11:59 +08:00
|
|
|
Requires<[IsThumb, HasV5T, IsNotMClass]>, Sched<[WriteBrL]> {
|
2012-05-04 06:41:56 +08:00
|
|
|
bits<24> func;
|
|
|
|
let Inst{26} = func{23};
|
2010-12-07 07:57:07 +08:00
|
|
|
let Inst{25-16} = func{20-11};
|
2012-05-04 06:41:56 +08:00
|
|
|
let Inst{13} = func{22};
|
|
|
|
let Inst{11} = func{21};
|
2010-12-07 07:57:07 +08:00
|
|
|
let Inst{10-1} = func{10-1};
|
|
|
|
let Inst{0} = 0; // func{0} is assumed zero
|
2010-12-04 06:33:42 +08:00
|
|
|
}
|
2009-07-30 05:26:42 +08:00
|
|
|
|
2009-08-01 08:16:10 +08:00
|
|
|
// Also used for Thumb2
|
2012-07-14 04:27:00 +08:00
|
|
|
def tBLXr : TI<(outs), (ins pred:$p, GPR:$func), IIC_Br,
|
2011-07-19 02:50:52 +08:00
|
|
|
"blx${p}\t$func",
|
2016-05-11 03:17:47 +08:00
|
|
|
[(ARMcall GPR:$func)]>,
|
2012-04-06 08:04:58 +08:00
|
|
|
Requires<[IsThumb, HasV5T]>,
|
2013-06-07 02:51:01 +08:00
|
|
|
T1Special<{1,1,1,?}>, Sched<[WriteBrL]> { // A6.2.3 & A8.6.24;
|
2011-05-12 01:00:48 +08:00
|
|
|
bits<4> func;
|
|
|
|
let Inst{6-3} = func;
|
|
|
|
let Inst{2-0} = 0b000;
|
|
|
|
}
|
2009-07-30 05:26:42 +08:00
|
|
|
|
2016-01-25 19:24:47 +08:00
|
|
|
// ARMv8-M Security Extensions
|
|
|
|
def tBLXNSr : TI<(outs), (ins pred:$p, GPRnopc:$func), IIC_Br,
|
|
|
|
"blxns${p}\t$func", []>,
|
|
|
|
Requires<[IsThumb, Has8MSecExt]>,
|
|
|
|
T1Special<{1,1,1,?}>, Sched<[WriteBrL]> {
|
|
|
|
bits<4> func;
|
|
|
|
let Inst{6-3} = func;
|
|
|
|
let Inst{2-0} = 0b100;
|
|
|
|
let Unpredictable{1-0} = 0b11;
|
|
|
|
}
|
|
|
|
|
2007-03-28 00:19:21 +08:00
|
|
|
// ARMv4T
|
2012-07-14 04:27:00 +08:00
|
|
|
def tBX_CALL : tPseudoInst<(outs), (ins tGPR:$func),
|
2011-07-14 07:22:26 +08:00
|
|
|
4, IIC_Br,
|
2009-07-30 05:26:42 +08:00
|
|
|
[(ARMcall_nolink tGPR:$func)]>,
|
2013-06-07 02:51:01 +08:00
|
|
|
Requires<[IsThumb, IsThumb1Only]>, Sched<[WriteBr]>;
|
2007-01-19 15:51:42 +08:00
|
|
|
}
|
|
|
|
|
2010-12-01 10:36:55 +08:00
|
|
|
let isBranch = 1, isTerminator = 1, isBarrier = 1 in {
|
|
|
|
let isPredicable = 1 in
|
2011-09-10 05:48:23 +08:00
|
|
|
def tB : T1pI<(outs), (ins t_brtarget:$target), IIC_Br,
|
|
|
|
"b", "\t$target", [(br bb:$target)]>,
|
2013-06-07 02:51:01 +08:00
|
|
|
T1Encoding<{1,1,1,0,0,?}>, Sched<[WriteBr]> {
|
2010-12-11 02:21:33 +08:00
|
|
|
bits<11> target;
|
|
|
|
let Inst{10-0} = target;
|
2013-08-09 18:38:32 +08:00
|
|
|
let AsmMatchConverter = "cvtThumbBranches";
|
|
|
|
}
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2007-01-30 09:13:37 +08:00
|
|
|
// Far jump
|
2010-12-17 03:11:16 +08:00
|
|
|
// Just a pseudo for a tBL instruction. Needed to let regalloc know about
|
|
|
|
// the clobber of LR.
|
2009-08-07 13:45:07 +08:00
|
|
|
let Defs = [LR] in
|
2016-07-12 06:29:37 +08:00
|
|
|
def tBfar : tPseudoExpand<(outs), (ins thumb_bl_target:$target, pred:$p),
|
|
|
|
4, IIC_Br, [],
|
|
|
|
(tBL pred:$p, thumb_bl_target:$target)>,
|
2013-06-07 02:51:01 +08:00
|
|
|
Sched<[WriteBrTbl]>;
|
2007-01-30 09:13:37 +08:00
|
|
|
|
2010-11-30 03:32:47 +08:00
|
|
|
def tBR_JTr : tPseudoInst<(outs),
|
2015-05-14 04:28:38 +08:00
|
|
|
(ins tGPR:$target, i32imm:$jt),
|
2011-07-14 07:22:26 +08:00
|
|
|
0, IIC_Br,
|
2015-05-14 04:28:38 +08:00
|
|
|
[(ARMbrjt tGPR:$target, tjumptable:$jt)]>,
|
2013-06-07 02:51:01 +08:00
|
|
|
Sched<[WriteBrTbl]> {
|
2015-06-01 03:22:07 +08:00
|
|
|
let Size = 2;
|
2010-11-30 03:32:47 +08:00
|
|
|
list<Predicate> Predicates = [IsThumb, IsThumb1Only];
|
2009-12-16 10:32:54 +08:00
|
|
|
}
|
2007-01-27 10:29:45 +08:00
|
|
|
}
|
|
|
|
|
2007-07-05 15:13:32 +08:00
|
|
|
// FIXME: should be able to write a pattern for ARMBrcond, but can't use
|
2009-03-28 07:06:27 +08:00
|
|
|
// a two-value operand where a dag node expects two operands. :(
|
2007-07-21 08:34:19 +08:00
|
|
|
let isBranch = 1, isTerminator = 1 in
|
2016-07-12 06:29:37 +08:00
|
|
|
def tBcc : T1I<(outs), (ins thumb_bcc_target:$target, pred:$p), IIC_Br,
|
2010-12-04 08:20:40 +08:00
|
|
|
"b${p}\t$target",
|
2009-12-16 01:24:14 +08:00
|
|
|
[/*(ARMbrcond bb:$target, imm:$cc)*/]>,
|
2013-06-07 02:51:01 +08:00
|
|
|
T1BranchCond<{1,1,0,1}>, Sched<[WriteBr]> {
|
2010-12-04 08:20:40 +08:00
|
|
|
bits<4> p;
|
2010-12-11 01:13:40 +08:00
|
|
|
bits<8> target;
|
2010-12-04 08:20:40 +08:00
|
|
|
let Inst{11-8} = p;
|
2010-12-11 01:13:40 +08:00
|
|
|
let Inst{7-0} = target;
|
2013-08-09 18:38:32 +08:00
|
|
|
let AsmMatchConverter = "cvtThumbBranches";
|
2010-12-04 08:20:40 +08:00
|
|
|
}
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2013-07-03 17:21:44 +08:00
|
|
|
|
2011-07-09 04:13:35 +08:00
|
|
|
// Tail calls
|
|
|
|
let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1 in {
|
2011-12-21 02:26:50 +08:00
|
|
|
// IOS versions.
|
2012-02-24 09:19:29 +08:00
|
|
|
let Uses = [SP] in {
|
2012-07-14 04:27:00 +08:00
|
|
|
def tTAILJMPr : tPseudoExpand<(outs), (ins tcGPR:$dst),
|
2011-07-14 07:22:26 +08:00
|
|
|
4, IIC_Br, [],
|
2011-07-09 04:39:19 +08:00
|
|
|
(tBX GPR:$dst, (ops 14, zero_reg))>,
|
2013-06-07 02:51:01 +08:00
|
|
|
Requires<[IsThumb]>, Sched<[WriteBr]>;
|
2011-07-09 04:13:35 +08:00
|
|
|
}
|
2014-01-06 22:28:05 +08:00
|
|
|
// tTAILJMPd: MachO version uses a Thumb2 branch (no Thumb1 tail calls
|
|
|
|
// on MachO), so it's in ARMInstrThumb2.td.
|
|
|
|
// Non-MachO version:
|
2012-02-24 09:19:29 +08:00
|
|
|
let Uses = [SP] in {
|
2011-09-10 05:48:23 +08:00
|
|
|
def tTAILJMPdND : tPseudoExpand<(outs),
|
2012-07-14 04:27:00 +08:00
|
|
|
(ins t_brtarget:$dst, pred:$p),
|
2011-07-14 07:22:26 +08:00
|
|
|
4, IIC_Br, [],
|
2011-09-10 05:48:23 +08:00
|
|
|
(tB t_brtarget:$dst, pred:$p)>,
|
2014-01-06 22:28:05 +08:00
|
|
|
Requires<[IsThumb, IsNotMachO]>, Sched<[WriteBr]>;
|
2011-07-09 04:13:35 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-08-24 03:49:10 +08:00
|
|
|
// A8.6.218 Supervisor Call (Software Interrupt)
|
2010-02-25 10:21:11 +08:00
|
|
|
// A8.6.16 B: Encoding T1
|
|
|
|
// If Inst{11-8} == 0b1111 then SEE SVC
|
2010-11-30 06:43:27 +08:00
|
|
|
let isCall = 1, Uses = [SP] in
|
2011-07-27 00:24:27 +08:00
|
|
|
def tSVC : T1pI<(outs), (ins imm0_255:$imm), IIC_Br,
|
2013-06-07 02:51:01 +08:00
|
|
|
"svc", "\t$imm", []>, Encoding16, Sched<[WriteBr]> {
|
2010-11-20 08:53:35 +08:00
|
|
|
bits<8> imm;
|
2010-02-25 10:21:11 +08:00
|
|
|
let Inst{15-12} = 0b1101;
|
2010-11-20 08:53:35 +08:00
|
|
|
let Inst{11-8} = 0b1111;
|
|
|
|
let Inst{7-0} = imm;
|
2010-02-25 10:21:11 +08:00
|
|
|
}
|
|
|
|
|
2010-11-30 15:44:32 +08:00
|
|
|
// The assembler uses 0xDEFE for a trap instruction.
|
2010-05-11 15:26:32 +08:00
|
|
|
let isBarrier = 1, isTerminator = 1 in
|
2011-05-12 01:00:48 +08:00
|
|
|
def tTRAP : TI<(outs), (ins), IIC_Br,
|
2013-06-07 02:51:01 +08:00
|
|
|
"trap", [(trap)]>, Encoding16, Sched<[WriteBr]> {
|
2010-11-21 18:55:23 +08:00
|
|
|
let Inst = 0xdefe;
|
2010-02-25 10:21:11 +08:00
|
|
|
}
|
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Load Store Instructions.
|
|
|
|
//
|
|
|
|
|
2015-08-13 18:48:22 +08:00
|
|
|
// PC-relative loads need to be matched first as constant pool accesses need to
|
|
|
|
// always be PC-relative. We do this using AddedComplexity, as the pattern is
|
|
|
|
// simpler than the patterns of the other load instructions.
|
|
|
|
let canFoldAsLoad = 1, isReMaterializable = 1, AddedComplexity = 10 in
|
|
|
|
def tLDRpci : T1pIs<(outs tGPR:$Rt), (ins t_addrmode_pc:$addr), IIC_iLoad_i,
|
|
|
|
"ldr", "\t$Rt, $addr",
|
|
|
|
[(set tGPR:$Rt, (load (ARMWrapper tconstpool:$addr)))]>,
|
|
|
|
T1Encoding<{0,1,0,0,1,?}> {
|
|
|
|
// A6.2 & A8.6.59
|
|
|
|
bits<3> Rt;
|
|
|
|
bits<8> addr;
|
|
|
|
let Inst{10-8} = Rt;
|
|
|
|
let Inst{7-0} = addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// SP-relative loads should be matched before standard immediate-offset loads as
|
|
|
|
// it means we avoid having to move SP to another register.
|
|
|
|
let canFoldAsLoad = 1 in
|
|
|
|
def tLDRspi : T1pIs<(outs tGPR:$Rt), (ins t_addrmode_sp:$addr), IIC_iLoad_i,
|
|
|
|
"ldr", "\t$Rt, $addr",
|
|
|
|
[(set tGPR:$Rt, (load t_addrmode_sp:$addr))]>,
|
|
|
|
T1LdStSP<{1,?,?}> {
|
|
|
|
bits<3> Rt;
|
|
|
|
bits<8> addr;
|
|
|
|
let Inst{10-8} = Rt;
|
|
|
|
let Inst{7-0} = addr;
|
|
|
|
}
|
|
|
|
|
2010-12-15 06:10:49 +08:00
|
|
|
// Loads: reg/reg and reg/imm5
|
2010-02-28 07:47:46 +08:00
|
|
|
let canFoldAsLoad = 1, isReMaterializable = 1 in
|
2010-12-15 06:10:49 +08:00
|
|
|
multiclass thumb_ld_rr_ri_enc<bits<3> reg_opc, bits<4> imm_opc,
|
|
|
|
Operand AddrMode_r, Operand AddrMode_i,
|
|
|
|
AddrMode am, InstrItinClass itin_r,
|
|
|
|
InstrItinClass itin_i, string asm,
|
|
|
|
PatFrag opnode> {
|
2015-08-13 18:48:22 +08:00
|
|
|
// Immediate-offset loads should be matched before register-offset loads as
|
|
|
|
// when the offset is a constant it's simpler to first check if it fits in the
|
|
|
|
// immediate offset field then fall back to register-offset if it doesn't.
|
2010-12-15 07:42:48 +08:00
|
|
|
def i : // reg/imm5
|
2010-12-15 06:10:49 +08:00
|
|
|
T1pILdStEncodeImm<imm_opc, 1 /* Load */,
|
|
|
|
(outs tGPR:$Rt), (ins AddrMode_i:$addr),
|
|
|
|
am, itin_i, asm, "\t$Rt, $addr",
|
|
|
|
[(set tGPR:$Rt, (opnode AddrMode_i:$addr))]>;
|
2015-08-13 18:48:22 +08:00
|
|
|
// Register-offset loads are matched last.
|
|
|
|
def r : // reg/reg
|
|
|
|
T1pILdStEncode<reg_opc,
|
|
|
|
(outs tGPR:$Rt), (ins AddrMode_r:$addr),
|
|
|
|
am, itin_r, asm, "\t$Rt, $addr",
|
|
|
|
[(set tGPR:$Rt, (opnode AddrMode_r:$addr))]>;
|
2010-12-15 06:10:49 +08:00
|
|
|
}
|
|
|
|
// Stores: reg/reg and reg/imm5
|
|
|
|
multiclass thumb_st_rr_ri_enc<bits<3> reg_opc, bits<4> imm_opc,
|
|
|
|
Operand AddrMode_r, Operand AddrMode_i,
|
|
|
|
AddrMode am, InstrItinClass itin_r,
|
|
|
|
InstrItinClass itin_i, string asm,
|
|
|
|
PatFrag opnode> {
|
2010-12-15 07:42:48 +08:00
|
|
|
def i : // reg/imm5
|
2010-12-15 06:10:49 +08:00
|
|
|
T1pILdStEncodeImm<imm_opc, 0 /* Store */,
|
|
|
|
(outs), (ins tGPR:$Rt, AddrMode_i:$addr),
|
|
|
|
am, itin_i, asm, "\t$Rt, $addr",
|
|
|
|
[(opnode tGPR:$Rt, AddrMode_i:$addr)]>;
|
2015-08-13 18:48:22 +08:00
|
|
|
def r : // reg/reg
|
|
|
|
T1pILdStEncode<reg_opc,
|
|
|
|
(outs), (ins tGPR:$Rt, AddrMode_r:$addr),
|
|
|
|
am, itin_r, asm, "\t$Rt, $addr",
|
|
|
|
[(opnode tGPR:$Rt, AddrMode_r:$addr)]>;
|
2010-12-15 06:10:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// A8.6.57 & A8.6.60
|
2015-08-13 18:48:22 +08:00
|
|
|
defm tLDR : thumb_ld_rr_ri_enc<0b100, 0b0110, t_addrmode_rr,
|
2010-12-15 06:10:49 +08:00
|
|
|
t_addrmode_is4, AddrModeT1_4,
|
|
|
|
IIC_iLoad_r, IIC_iLoad_i, "ldr",
|
2016-03-09 00:23:54 +08:00
|
|
|
load>;
|
2010-12-15 06:10:49 +08:00
|
|
|
|
|
|
|
// A8.6.64 & A8.6.61
|
2015-08-13 18:48:22 +08:00
|
|
|
defm tLDRB : thumb_ld_rr_ri_enc<0b110, 0b0111, t_addrmode_rr,
|
2010-12-15 06:10:49 +08:00
|
|
|
t_addrmode_is1, AddrModeT1_1,
|
|
|
|
IIC_iLoad_bh_r, IIC_iLoad_bh_i, "ldrb",
|
2016-03-09 00:23:54 +08:00
|
|
|
zextloadi8>;
|
2010-12-15 06:10:49 +08:00
|
|
|
|
|
|
|
// A8.6.76 & A8.6.73
|
2015-08-13 18:48:22 +08:00
|
|
|
defm tLDRH : thumb_ld_rr_ri_enc<0b101, 0b1000, t_addrmode_rr,
|
2010-12-15 06:10:49 +08:00
|
|
|
t_addrmode_is2, AddrModeT1_2,
|
|
|
|
IIC_iLoad_bh_r, IIC_iLoad_bh_i, "ldrh",
|
2016-03-09 00:23:54 +08:00
|
|
|
zextloadi16>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-07-11 15:08:13 +08:00
|
|
|
let AddedComplexity = 10 in
|
2010-12-01 06:57:21 +08:00
|
|
|
def tLDRSB : // A8.6.80
|
2011-08-16 03:00:06 +08:00
|
|
|
T1pILdStEncode<0b011, (outs tGPR:$Rt), (ins t_addrmode_rr:$addr),
|
2010-12-01 09:38:08 +08:00
|
|
|
AddrModeT1_1, IIC_iLoad_bh_r,
|
2011-08-16 03:00:06 +08:00
|
|
|
"ldrsb", "\t$Rt, $addr",
|
|
|
|
[(set tGPR:$Rt, (sextloadi8 t_addrmode_rr:$addr))]>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-07-11 15:08:13 +08:00
|
|
|
let AddedComplexity = 10 in
|
2010-12-01 06:57:21 +08:00
|
|
|
def tLDRSH : // A8.6.84
|
2011-08-16 03:00:06 +08:00
|
|
|
T1pILdStEncode<0b111, (outs tGPR:$Rt), (ins t_addrmode_rr:$addr),
|
2010-12-01 09:38:08 +08:00
|
|
|
AddrModeT1_2, IIC_iLoad_bh_r,
|
2011-08-16 03:00:06 +08:00
|
|
|
"ldrsh", "\t$Rt, $addr",
|
|
|
|
[(set tGPR:$Rt, (sextloadi16 t_addrmode_rr:$addr))]>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2007-01-24 16:53:17 +08:00
|
|
|
|
2015-08-13 18:48:22 +08:00
|
|
|
def tSTRspi : T1pIs<(outs), (ins tGPR:$Rt, t_addrmode_sp:$addr), IIC_iStore_i,
|
|
|
|
"str", "\t$Rt, $addr",
|
|
|
|
[(store tGPR:$Rt, t_addrmode_sp:$addr)]>,
|
|
|
|
T1LdStSP<{0,?,?}> {
|
2010-12-01 07:54:45 +08:00
|
|
|
bits<3> Rt;
|
2010-12-08 09:57:09 +08:00
|
|
|
bits<8> addr;
|
2010-12-01 07:54:45 +08:00
|
|
|
let Inst{10-8} = Rt;
|
2015-08-13 18:48:22 +08:00
|
|
|
let Inst{7-0} = addr;
|
2010-12-01 07:54:45 +08:00
|
|
|
}
|
2007-03-19 15:20:03 +08:00
|
|
|
|
2010-12-15 06:10:49 +08:00
|
|
|
// A8.6.194 & A8.6.192
|
2015-08-13 18:48:22 +08:00
|
|
|
defm tSTR : thumb_st_rr_ri_enc<0b000, 0b0110, t_addrmode_rr,
|
2010-12-15 06:10:49 +08:00
|
|
|
t_addrmode_is4, AddrModeT1_4,
|
|
|
|
IIC_iStore_r, IIC_iStore_i, "str",
|
2016-03-09 00:23:54 +08:00
|
|
|
store>;
|
2010-12-15 06:10:49 +08:00
|
|
|
|
|
|
|
// A8.6.197 & A8.6.195
|
2015-08-13 18:48:22 +08:00
|
|
|
defm tSTRB : thumb_st_rr_ri_enc<0b010, 0b0111, t_addrmode_rr,
|
2010-12-15 06:10:49 +08:00
|
|
|
t_addrmode_is1, AddrModeT1_1,
|
|
|
|
IIC_iStore_bh_r, IIC_iStore_bh_i, "strb",
|
2016-03-09 00:23:54 +08:00
|
|
|
truncstorei8>;
|
2010-12-15 06:10:49 +08:00
|
|
|
|
|
|
|
// A8.6.207 & A8.6.205
|
2015-08-13 18:48:22 +08:00
|
|
|
defm tSTRH : thumb_st_rr_ri_enc<0b001, 0b1000, t_addrmode_rr,
|
2011-06-14 06:54:22 +08:00
|
|
|
t_addrmode_is2, AddrModeT1_2,
|
|
|
|
IIC_iStore_bh_r, IIC_iStore_bh_i, "strh",
|
2016-03-09 00:23:54 +08:00
|
|
|
truncstorei16>;
|
2010-12-01 06:57:21 +08:00
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Load / store multiple Instructions.
|
|
|
|
//
|
|
|
|
|
2010-11-16 09:16:36 +08:00
|
|
|
// These require base address to be written back or one of the loaded regs.
|
2014-11-26 08:46:26 +08:00
|
|
|
let hasSideEffects = 0 in {
|
2010-11-13 18:57:02 +08:00
|
|
|
|
|
|
|
let mayLoad = 1, hasExtraDefRegAllocReq = 1 in
|
2011-08-24 01:41:15 +08:00
|
|
|
def tLDMIA : T1I<(outs), (ins tGPR:$Rn, pred:$p, reglist:$regs, variable_ops),
|
|
|
|
IIC_iLoad_m, "ldm${p}\t$Rn, $regs", []>, T1Encoding<{1,1,0,0,1,?}> {
|
|
|
|
bits<3> Rn;
|
|
|
|
bits<8> regs;
|
|
|
|
let Inst{10-8} = Rn;
|
|
|
|
let Inst{7-0} = regs;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Writeback version is just a pseudo, as there's no encoding difference.
|
2012-09-27 18:14:43 +08:00
|
|
|
// Writeback happens iff the base register is not in the destination register
|
2011-08-24 01:41:15 +08:00
|
|
|
// list.
|
2015-10-05 22:49:54 +08:00
|
|
|
let mayLoad = 1, hasExtraDefRegAllocReq = 1 in
|
2011-08-24 01:41:15 +08:00
|
|
|
def tLDMIA_UPD :
|
|
|
|
InstTemplate<AddrModeNone, 0, IndexModeNone, Pseudo, GenericDomain,
|
|
|
|
"$Rn = $wb", IIC_iLoad_mu>,
|
|
|
|
PseudoInstExpansion<(tLDMIA tGPR:$Rn, pred:$p, reglist:$regs)> {
|
|
|
|
let Size = 2;
|
|
|
|
let OutOperandList = (outs GPR:$wb);
|
|
|
|
let InOperandList = (ins GPR:$Rn, pred:$p, reglist:$regs, variable_ops);
|
|
|
|
let Pattern = [];
|
|
|
|
let isCodeGenOnly = 1;
|
|
|
|
let isPseudo = 1;
|
|
|
|
list<Predicate> Predicates = [IsThumb];
|
|
|
|
}
|
|
|
|
|
|
|
|
// There is no non-writeback version of STM for Thumb.
|
2010-11-13 18:57:02 +08:00
|
|
|
let mayStore = 1, hasExtraSrcRegAllocReq = 1 in
|
2011-08-25 02:19:42 +08:00
|
|
|
def tSTMIA_UPD : Thumb1I<(outs GPR:$wb),
|
|
|
|
(ins tGPR:$Rn, pred:$p, reglist:$regs, variable_ops),
|
|
|
|
AddrModeNone, 2, IIC_iStore_mu,
|
|
|
|
"stm${p}\t$Rn!, $regs", "$Rn = $wb", []>,
|
2011-08-24 01:41:15 +08:00
|
|
|
T1Encoding<{1,1,0,0,0,?}> {
|
|
|
|
bits<3> Rn;
|
|
|
|
bits<8> regs;
|
|
|
|
let Inst{10-8} = Rn;
|
|
|
|
let Inst{7-0} = regs;
|
|
|
|
}
|
2011-05-12 01:00:48 +08:00
|
|
|
|
2014-11-26 08:46:26 +08:00
|
|
|
} // hasSideEffects
|
2009-08-12 05:11:32 +08:00
|
|
|
|
2011-08-19 05:50:53 +08:00
|
|
|
def : InstAlias<"ldm${p} $Rn!, $regs",
|
2016-06-03 21:19:43 +08:00
|
|
|
(tLDMIA tGPR:$Rn, pred:$p, reglist:$regs), 0>,
|
2011-08-19 05:50:53 +08:00
|
|
|
Requires<[IsThumb, IsThumb1Only]>;
|
|
|
|
|
2009-10-01 16:22:27 +08:00
|
|
|
let mayLoad = 1, Uses = [SP], Defs = [SP], hasExtraDefRegAllocReq = 1 in
|
2010-11-19 09:33:10 +08:00
|
|
|
def tPOP : T1I<(outs), (ins pred:$p, reglist:$regs, variable_ops),
|
2010-10-06 14:27:31 +08:00
|
|
|
IIC_iPop,
|
2010-11-19 09:33:10 +08:00
|
|
|
"pop${p}\t$regs", []>,
|
|
|
|
T1Misc<{1,1,0,?,?,?,?}> {
|
|
|
|
bits<16> regs;
|
|
|
|
let Inst{8} = regs{15};
|
|
|
|
let Inst{7-0} = regs{7-0};
|
|
|
|
}
|
2009-08-12 05:11:32 +08:00
|
|
|
|
2009-10-01 16:22:27 +08:00
|
|
|
let mayStore = 1, Uses = [SP], Defs = [SP], hasExtraSrcRegAllocReq = 1 in
|
2010-11-20 08:53:35 +08:00
|
|
|
def tPUSH : T1I<(outs), (ins pred:$p, reglist:$regs, variable_ops),
|
2010-10-06 14:27:31 +08:00
|
|
|
IIC_iStore_m,
|
2010-11-20 08:53:35 +08:00
|
|
|
"push${p}\t$regs", []>,
|
|
|
|
T1Misc<{0,1,0,?,?,?,?}> {
|
|
|
|
bits<16> regs;
|
|
|
|
let Inst{8} = regs{14};
|
|
|
|
let Inst{7-0} = regs{7-0};
|
|
|
|
}
|
2007-01-19 15:51:42 +08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Arithmetic Instructions.
|
|
|
|
//
|
|
|
|
|
2010-12-01 10:28:08 +08:00
|
|
|
// Helper classes for encoding T1pI patterns:
|
|
|
|
class T1pIDPEncode<bits<4> opA, dag oops, dag iops, InstrItinClass itin,
|
|
|
|
string opc, string asm, list<dag> pattern>
|
|
|
|
: T1pI<oops, iops, itin, opc, asm, pattern>,
|
|
|
|
T1DataProcessing<opA> {
|
|
|
|
bits<3> Rm;
|
|
|
|
bits<3> Rn;
|
|
|
|
let Inst{5-3} = Rm;
|
|
|
|
let Inst{2-0} = Rn;
|
|
|
|
}
|
|
|
|
class T1pIMiscEncode<bits<7> opA, dag oops, dag iops, InstrItinClass itin,
|
|
|
|
string opc, string asm, list<dag> pattern>
|
|
|
|
: T1pI<oops, iops, itin, opc, asm, pattern>,
|
|
|
|
T1Misc<opA> {
|
|
|
|
bits<3> Rm;
|
|
|
|
bits<3> Rd;
|
|
|
|
let Inst{5-3} = Rm;
|
|
|
|
let Inst{2-0} = Rd;
|
|
|
|
}
|
|
|
|
|
2010-12-01 09:20:15 +08:00
|
|
|
// Helper classes for encoding T1sI patterns:
|
|
|
|
class T1sIDPEncode<bits<4> opA, dag oops, dag iops, InstrItinClass itin,
|
|
|
|
string opc, string asm, list<dag> pattern>
|
|
|
|
: T1sI<oops, iops, itin, opc, asm, pattern>,
|
|
|
|
T1DataProcessing<opA> {
|
|
|
|
bits<3> Rd;
|
|
|
|
bits<3> Rn;
|
|
|
|
let Inst{5-3} = Rn;
|
|
|
|
let Inst{2-0} = Rd;
|
|
|
|
}
|
|
|
|
class T1sIGenEncode<bits<5> opA, dag oops, dag iops, InstrItinClass itin,
|
|
|
|
string opc, string asm, list<dag> pattern>
|
|
|
|
: T1sI<oops, iops, itin, opc, asm, pattern>,
|
|
|
|
T1General<opA> {
|
|
|
|
bits<3> Rm;
|
|
|
|
bits<3> Rn;
|
|
|
|
bits<3> Rd;
|
|
|
|
let Inst{8-6} = Rm;
|
|
|
|
let Inst{5-3} = Rn;
|
|
|
|
let Inst{2-0} = Rd;
|
|
|
|
}
|
|
|
|
class T1sIGenEncodeImm<bits<5> opA, dag oops, dag iops, InstrItinClass itin,
|
|
|
|
string opc, string asm, list<dag> pattern>
|
|
|
|
: T1sI<oops, iops, itin, opc, asm, pattern>,
|
|
|
|
T1General<opA> {
|
|
|
|
bits<3> Rd;
|
|
|
|
bits<3> Rm;
|
|
|
|
let Inst{5-3} = Rm;
|
|
|
|
let Inst{2-0} = Rd;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper classes for encoding T1sIt patterns:
|
2010-12-01 08:48:44 +08:00
|
|
|
class T1sItDPEncode<bits<4> opA, dag oops, dag iops, InstrItinClass itin,
|
|
|
|
string opc, string asm, list<dag> pattern>
|
|
|
|
: T1sIt<oops, iops, itin, opc, asm, pattern>,
|
|
|
|
T1DataProcessing<opA> {
|
2010-12-01 07:54:45 +08:00
|
|
|
bits<3> Rdn;
|
|
|
|
bits<3> Rm;
|
2010-12-01 08:48:44 +08:00
|
|
|
let Inst{5-3} = Rm;
|
|
|
|
let Inst{2-0} = Rdn;
|
|
|
|
}
|
|
|
|
class T1sItGenEncodeImm<bits<5> opA, dag oops, dag iops, InstrItinClass itin,
|
|
|
|
string opc, string asm, list<dag> pattern>
|
|
|
|
: T1sIt<oops, iops, itin, opc, asm, pattern>,
|
|
|
|
T1General<opA> {
|
|
|
|
bits<3> Rdn;
|
|
|
|
bits<8> imm8;
|
|
|
|
let Inst{10-8} = Rdn;
|
|
|
|
let Inst{7-0} = imm8;
|
2010-11-20 09:00:29 +08:00
|
|
|
}
|
2009-06-26 06:49:55 +08:00
|
|
|
|
2016-09-14 16:20:03 +08:00
|
|
|
let isAdd = 1 in {
|
|
|
|
// Add with carry register
|
|
|
|
let isCommutable = 1, Uses = [CPSR] in
|
|
|
|
def tADC : // A8.6.2
|
|
|
|
T1sItDPEncode<0b0101, (outs tGPR:$Rdn), (ins tGPR:$Rn, tGPR:$Rm), IIC_iALUr,
|
|
|
|
"adc", "\t$Rdn, $Rm",
|
2017-03-23 07:35:51 +08:00
|
|
|
[]>, Sched<[WriteALU]>;
|
2016-09-14 16:20:03 +08:00
|
|
|
|
|
|
|
// Add immediate
|
|
|
|
def tADDi3 : // A8.6.4 T1
|
|
|
|
T1sIGenEncodeImm<0b01110, (outs tGPR:$Rd), (ins tGPR:$Rm, imm0_7:$imm3),
|
|
|
|
IIC_iALUi,
|
|
|
|
"add", "\t$Rd, $Rm, $imm3",
|
|
|
|
[(set tGPR:$Rd, (add tGPR:$Rm, imm0_7:$imm3))]>,
|
|
|
|
Sched<[WriteALU]> {
|
|
|
|
bits<3> imm3;
|
|
|
|
let Inst{8-6} = imm3;
|
|
|
|
}
|
|
|
|
|
|
|
|
def tADDi8 : // A8.6.4 T2
|
|
|
|
T1sItGenEncodeImm<{1,1,0,?,?}, (outs tGPR:$Rdn),
|
|
|
|
(ins tGPR:$Rn, imm0_255:$imm8), IIC_iALUi,
|
|
|
|
"add", "\t$Rdn, $imm8",
|
|
|
|
[(set tGPR:$Rdn, (add tGPR:$Rn, imm8_255:$imm8))]>,
|
|
|
|
Sched<[WriteALU]>;
|
|
|
|
|
|
|
|
// Add register
|
|
|
|
let isCommutable = 1 in
|
|
|
|
def tADDrr : // A8.6.6 T1
|
|
|
|
T1sIGenEncode<0b01100, (outs tGPR:$Rd), (ins tGPR:$Rn, tGPR:$Rm),
|
|
|
|
IIC_iALUr,
|
|
|
|
"add", "\t$Rd, $Rn, $Rm",
|
|
|
|
[(set tGPR:$Rd, (add tGPR:$Rn, tGPR:$Rm))]>, Sched<[WriteALU]>;
|
|
|
|
|
2017-03-23 07:35:51 +08:00
|
|
|
/// Similar to the above except these set the 's' bit so the
|
|
|
|
/// instruction modifies the CPSR register.
|
|
|
|
///
|
|
|
|
/// These opcodes will be converted to the real non-S opcodes by
|
|
|
|
/// AdjustInstrPostInstrSelection after giving then an optional CPSR operand.
|
|
|
|
let hasPostISelHook = 1, Defs = [CPSR] in {
|
2017-04-21 15:35:21 +08:00
|
|
|
let isCommutable = 1, Uses = [CPSR] in
|
2017-03-23 07:35:51 +08:00
|
|
|
def tADCS : tPseudoInst<(outs tGPR:$Rdn), (ins tGPR:$Rn, tGPR:$Rm),
|
|
|
|
2, IIC_iALUr,
|
|
|
|
[(set tGPR:$Rdn, CPSR, (ARMadde tGPR:$Rn, tGPR:$Rm,
|
|
|
|
CPSR))]>,
|
|
|
|
Requires<[IsThumb1Only]>,
|
|
|
|
Sched<[WriteALU]>;
|
|
|
|
|
|
|
|
def tADDSi3 : tPseudoInst<(outs tGPR:$Rd), (ins tGPR:$Rm, imm0_7:$imm3),
|
|
|
|
2, IIC_iALUi,
|
|
|
|
[(set tGPR:$Rd, CPSR, (ARMaddc tGPR:$Rm,
|
|
|
|
imm0_7:$imm3))]>,
|
|
|
|
Requires<[IsThumb1Only]>,
|
|
|
|
Sched<[WriteALU]>;
|
|
|
|
|
|
|
|
def tADDSi8 : tPseudoInst<(outs tGPR:$Rdn), (ins tGPR:$Rn, imm0_255:$imm8),
|
|
|
|
2, IIC_iALUi,
|
|
|
|
[(set tGPR:$Rdn, CPSR, (ARMaddc tGPR:$Rn,
|
|
|
|
imm8_255:$imm8))]>,
|
|
|
|
Requires<[IsThumb1Only]>,
|
|
|
|
Sched<[WriteALU]>;
|
|
|
|
|
|
|
|
let isCommutable = 1 in
|
|
|
|
def tADDSrr : tPseudoInst<(outs tGPR:$Rd), (ins tGPR:$Rn, tGPR:$Rm),
|
|
|
|
2, IIC_iALUr,
|
|
|
|
[(set tGPR:$Rd, CPSR, (ARMaddc tGPR:$Rn,
|
|
|
|
tGPR:$Rm))]>,
|
|
|
|
Requires<[IsThumb1Only]>,
|
|
|
|
Sched<[WriteALU]>;
|
|
|
|
}
|
|
|
|
|
2016-09-14 16:20:03 +08:00
|
|
|
let hasSideEffects = 0 in
|
|
|
|
def tADDhirr : T1pIt<(outs GPR:$Rdn), (ins GPR:$Rn, GPR:$Rm), IIC_iALUr,
|
|
|
|
"add", "\t$Rdn, $Rm", []>,
|
|
|
|
T1Special<{0,0,?,?}>, Sched<[WriteALU]> {
|
|
|
|
// A8.6.6 T2
|
|
|
|
bits<4> Rdn;
|
|
|
|
bits<4> Rm;
|
|
|
|
let Inst{7} = Rdn{3};
|
|
|
|
let Inst{6-3} = Rm;
|
|
|
|
let Inst{2-0} = Rdn{2-0};
|
|
|
|
}
|
2010-11-20 09:18:47 +08:00
|
|
|
}
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2018-06-20 20:09:44 +08:00
|
|
|
// Thumb has more flexible short encodings for ADD than ORR, so use those where
|
|
|
|
// possible.
|
|
|
|
def : T1Pat<(or AddLikeOrOp:$Rn, imm0_7:$imm), (tADDi3 $Rn, imm0_7:$imm)>;
|
|
|
|
|
|
|
|
def : T1Pat<(or AddLikeOrOp:$Rn, imm8_255:$imm), (tADDi8 $Rn, imm8_255:$imm)>;
|
|
|
|
|
|
|
|
def : T1Pat<(or AddLikeOrOp:$Rn, tGPR:$Rm), (tADDrr $Rn, $Rm)>;
|
|
|
|
|
|
|
|
|
2017-09-01 18:47:25 +08:00
|
|
|
def : tInstAlias <"add${s}${p} $Rdn, $Rm",
|
|
|
|
(tADDrr tGPR:$Rdn,s_cc_out:$s, tGPR:$Rdn, tGPR:$Rm, pred:$p)>;
|
|
|
|
|
[ARM] [Assembler] Support negative immediates for A32, T32 and T16
Summary:
To support negative immediates for certain arithmetic instructions, the
instruction is converted to the inverse instruction with a negated (or inverted)
immediate. For example, "ADD r0, r1, #FFFFFFFF" cannot be encoded as an ADD
instruction. However, "SUB r0, r1, #1" is equivalent.
These conversions are different from instruction aliases. An alias maps
several assembler instructions onto one encoding. A conversion, however, maps
an *invalid* instruction--e.g. with an immediate that cannot be represented in
the encoding--to a different (but equivalent) instruction.
Several instructions with negative immediates were being converted already, but
this was not systematically tested, nor did it cover all instructions.
This patch implements all possible substitutions for ARM, Thumb1 and
Thumb2 assembler and adds tests. It also adds a feature flag
(-mattr=+no-neg-immediates) to turn these substitutions off. This is
helpful for users who want their code to assemble to exactly what they
wrote.
Reviewers: t.p.northover, rovka, samparker, javed.absar, peter.smith, rengolin
Reviewed By: javed.absar
Subscribers: aadg, aemerson, llvm-commits
Differential Revision: https://reviews.llvm.org/D30571
llvm-svn: 298380
2017-03-21 22:59:17 +08:00
|
|
|
def : tInstSubst<"sub${s}${p} $rd, $rn, $imm",
|
|
|
|
(tADDi3 tGPR:$rd, s_cc_out:$s, tGPR:$rn, mod_imm1_7_neg:$imm, pred:$p)>;
|
|
|
|
def : tInstSubst<"sub${s}${p} $rdn, $imm",
|
|
|
|
(tADDi8 tGPR:$rdn, s_cc_out:$s, mod_imm8_255_neg:$imm, pred:$p)>;
|
|
|
|
|
|
|
|
|
2010-11-20 09:18:47 +08:00
|
|
|
// AND register
|
2009-07-11 14:43:01 +08:00
|
|
|
let isCommutable = 1 in
|
2010-12-01 08:48:44 +08:00
|
|
|
def tAND : // A8.6.12
|
|
|
|
T1sItDPEncode<0b0000, (outs tGPR:$Rdn), (ins tGPR:$Rn, tGPR:$Rm),
|
|
|
|
IIC_iBITr,
|
|
|
|
"and", "\t$Rdn, $Rm",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(set tGPR:$Rdn, (and tGPR:$Rn, tGPR:$Rm))]>, Sched<[WriteALU]>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-06-26 06:49:55 +08:00
|
|
|
// ASR immediate
|
2010-12-01 09:20:15 +08:00
|
|
|
def tASRri : // A8.6.14
|
2011-08-09 04:42:17 +08:00
|
|
|
T1sIGenEncodeImm<{0,1,0,?,?}, (outs tGPR:$Rd), (ins tGPR:$Rm, imm_sr:$imm5),
|
2010-12-01 09:20:15 +08:00
|
|
|
IIC_iMOVsi,
|
|
|
|
"asr", "\t$Rd, $Rm, $imm5",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(set tGPR:$Rd, (sra tGPR:$Rm, (i32 imm_sr:$imm5)))]>,
|
|
|
|
Sched<[WriteALU]> {
|
2010-11-20 09:18:47 +08:00
|
|
|
bits<5> imm5;
|
|
|
|
let Inst{10-6} = imm5;
|
|
|
|
}
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-06-26 06:49:55 +08:00
|
|
|
// ASR register
|
2010-12-01 08:48:44 +08:00
|
|
|
def tASRrr : // A8.6.15
|
|
|
|
T1sItDPEncode<0b0100, (outs tGPR:$Rdn), (ins tGPR:$Rn, tGPR:$Rm),
|
|
|
|
IIC_iMOVsr,
|
|
|
|
"asr", "\t$Rdn, $Rm",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(set tGPR:$Rdn, (sra tGPR:$Rn, tGPR:$Rm))]>, Sched<[WriteALU]>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-06-26 06:49:55 +08:00
|
|
|
// BIC register
|
2010-12-01 08:48:44 +08:00
|
|
|
def tBIC : // A8.6.20
|
|
|
|
T1sItDPEncode<0b1110, (outs tGPR:$Rdn), (ins tGPR:$Rn, tGPR:$Rm),
|
|
|
|
IIC_iBITr,
|
|
|
|
"bic", "\t$Rdn, $Rm",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(set tGPR:$Rdn, (and tGPR:$Rn, (not tGPR:$Rm)))]>,
|
|
|
|
Sched<[WriteALU]>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-06-26 06:49:55 +08:00
|
|
|
// CMN register
|
2010-09-15 06:00:50 +08:00
|
|
|
let isCompare = 1, Defs = [CPSR] in {
|
2010-01-22 08:08:13 +08:00
|
|
|
//FIXME: Disable CMN, as CCodes are backwards from compare expectations
|
|
|
|
// Compare-to-zero still works out, just not the relationals
|
2010-12-01 10:36:55 +08:00
|
|
|
//def tCMN : // A8.6.33
|
|
|
|
// T1pIDPEncode<0b1011, (outs), (ins tGPR:$lhs, tGPR:$rhs),
|
|
|
|
// IIC_iCMPr,
|
|
|
|
// "cmn", "\t$lhs, $rhs",
|
|
|
|
// [(ARMcmp tGPR:$lhs, (ineg tGPR:$rhs))]>;
|
2010-12-01 10:28:08 +08:00
|
|
|
|
|
|
|
def tCMNz : // A8.6.33
|
|
|
|
T1pIDPEncode<0b1011, (outs), (ins tGPR:$Rn, tGPR:$Rm),
|
|
|
|
IIC_iCMPr,
|
|
|
|
"cmn", "\t$Rn, $Rm",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(ARMcmpZ tGPR:$Rn, (ineg tGPR:$Rm))]>, Sched<[WriteCMP]>;
|
2010-12-01 10:28:08 +08:00
|
|
|
|
|
|
|
} // isCompare = 1, Defs = [CPSR]
|
2007-04-02 09:30:03 +08:00
|
|
|
|
2009-06-26 06:49:55 +08:00
|
|
|
// CMP immediate
|
2010-09-15 06:00:50 +08:00
|
|
|
let isCompare = 1, Defs = [CPSR] in {
|
2011-08-19 02:08:29 +08:00
|
|
|
def tCMPi8 : T1pI<(outs), (ins tGPR:$Rn, imm0_255:$imm8), IIC_iCMPi,
|
2010-11-21 06:52:33 +08:00
|
|
|
"cmp", "\t$Rn, $imm8",
|
|
|
|
[(ARMcmp tGPR:$Rn, imm0_255:$imm8)]>,
|
2013-06-07 01:03:13 +08:00
|
|
|
T1General<{1,0,1,?,?}>, Sched<[WriteCMP]> {
|
2010-11-21 06:52:33 +08:00
|
|
|
// A8.6.35
|
|
|
|
bits<3> Rn;
|
|
|
|
bits<8> imm8;
|
|
|
|
let Inst{10-8} = Rn;
|
|
|
|
let Inst{7-0} = imm8;
|
|
|
|
}
|
|
|
|
|
2009-06-26 06:49:55 +08:00
|
|
|
// CMP register
|
2010-12-01 10:28:08 +08:00
|
|
|
def tCMPr : // A8.6.36 T1
|
|
|
|
T1pIDPEncode<0b1010, (outs), (ins tGPR:$Rn, tGPR:$Rm),
|
|
|
|
IIC_iCMPr,
|
|
|
|
"cmp", "\t$Rn, $Rm",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(ARMcmp tGPR:$Rn, tGPR:$Rm)]>, Sched<[WriteCMP]>;
|
2010-12-01 10:28:08 +08:00
|
|
|
|
2010-11-29 08:18:15 +08:00
|
|
|
def tCMPhir : T1pI<(outs), (ins GPR:$Rn, GPR:$Rm), IIC_iCMPr,
|
|
|
|
"cmp", "\t$Rn, $Rm", []>,
|
2013-06-07 01:03:13 +08:00
|
|
|
T1Special<{0,1,?,?}>, Sched<[WriteCMP]> {
|
2010-11-29 08:18:15 +08:00
|
|
|
// A8.6.36 T2
|
|
|
|
bits<4> Rm;
|
|
|
|
bits<4> Rn;
|
|
|
|
let Inst{7} = Rn{3};
|
|
|
|
let Inst{6-3} = Rm;
|
|
|
|
let Inst{2-0} = Rn{2-0};
|
|
|
|
}
|
2010-11-21 06:52:33 +08:00
|
|
|
} // isCompare = 1, Defs = [CPSR]
|
2007-04-02 09:30:03 +08:00
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-06-26 06:49:55 +08:00
|
|
|
// XOR register
|
2009-07-11 14:43:01 +08:00
|
|
|
let isCommutable = 1 in
|
2010-12-01 08:48:44 +08:00
|
|
|
def tEOR : // A8.6.45
|
|
|
|
T1sItDPEncode<0b0001, (outs tGPR:$Rdn), (ins tGPR:$Rn, tGPR:$Rm),
|
|
|
|
IIC_iBITr,
|
|
|
|
"eor", "\t$Rdn, $Rm",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(set tGPR:$Rdn, (xor tGPR:$Rn, tGPR:$Rm))]>, Sched<[WriteALU]>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-06-26 06:49:55 +08:00
|
|
|
// LSL immediate
|
2010-12-01 09:20:15 +08:00
|
|
|
def tLSLri : // A8.6.88
|
2011-08-20 03:29:25 +08:00
|
|
|
T1sIGenEncodeImm<{0,0,0,?,?}, (outs tGPR:$Rd), (ins tGPR:$Rm, imm0_31:$imm5),
|
2010-12-01 09:20:15 +08:00
|
|
|
IIC_iMOVsi,
|
|
|
|
"lsl", "\t$Rd, $Rm, $imm5",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(set tGPR:$Rd, (shl tGPR:$Rm, (i32 imm:$imm5)))]>,
|
|
|
|
Sched<[WriteALU]> {
|
2010-11-21 19:49:36 +08:00
|
|
|
bits<5> imm5;
|
|
|
|
let Inst{10-6} = imm5;
|
|
|
|
}
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-06-26 06:49:55 +08:00
|
|
|
// LSL register
|
2010-12-01 08:48:44 +08:00
|
|
|
def tLSLrr : // A8.6.89
|
|
|
|
T1sItDPEncode<0b0010, (outs tGPR:$Rdn), (ins tGPR:$Rn, tGPR:$Rm),
|
|
|
|
IIC_iMOVsr,
|
|
|
|
"lsl", "\t$Rdn, $Rm",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(set tGPR:$Rdn, (shl tGPR:$Rn, tGPR:$Rm))]>, Sched<[WriteALU]>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-06-26 06:49:55 +08:00
|
|
|
// LSR immediate
|
2010-12-01 09:20:15 +08:00
|
|
|
def tLSRri : // A8.6.90
|
2011-08-09 04:42:17 +08:00
|
|
|
T1sIGenEncodeImm<{0,0,1,?,?}, (outs tGPR:$Rd), (ins tGPR:$Rm, imm_sr:$imm5),
|
2010-12-01 09:20:15 +08:00
|
|
|
IIC_iMOVsi,
|
|
|
|
"lsr", "\t$Rd, $Rm, $imm5",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(set tGPR:$Rd, (srl tGPR:$Rm, (i32 imm_sr:$imm5)))]>,
|
|
|
|
Sched<[WriteALU]> {
|
2010-11-21 19:49:36 +08:00
|
|
|
bits<5> imm5;
|
|
|
|
let Inst{10-6} = imm5;
|
|
|
|
}
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-06-26 06:49:55 +08:00
|
|
|
// LSR register
|
2010-12-01 08:48:44 +08:00
|
|
|
def tLSRrr : // A8.6.91
|
|
|
|
T1sItDPEncode<0b0011, (outs tGPR:$Rdn), (ins tGPR:$Rn, tGPR:$Rm),
|
|
|
|
IIC_iMOVsr,
|
|
|
|
"lsr", "\t$Rdn, $Rm",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(set tGPR:$Rdn, (srl tGPR:$Rn, tGPR:$Rm))]>, Sched<[WriteALU]>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2010-11-21 19:49:36 +08:00
|
|
|
// Move register
|
2010-11-18 04:13:28 +08:00
|
|
|
let isMoveImm = 1 in
|
2011-06-28 07:54:06 +08:00
|
|
|
def tMOVi8 : T1sI<(outs tGPR:$Rd), (ins imm0_255:$imm8), IIC_iMOVi,
|
2010-11-21 19:49:36 +08:00
|
|
|
"mov", "\t$Rd, $imm8",
|
|
|
|
[(set tGPR:$Rd, imm0_255:$imm8)]>,
|
2013-06-07 01:03:13 +08:00
|
|
|
T1General<{1,0,0,?,?}>, Sched<[WriteALU]> {
|
2010-11-21 19:49:36 +08:00
|
|
|
// A8.6.96
|
|
|
|
bits<3> Rd;
|
|
|
|
bits<8> imm8;
|
|
|
|
let Inst{10-8} = Rd;
|
|
|
|
let Inst{7-0} = imm8;
|
|
|
|
}
|
2011-08-20 04:46:54 +08:00
|
|
|
// Because we have an explicit tMOVSr below, we need an alias to handle
|
|
|
|
// the immediate "movs" form here. Blech.
|
2011-08-23 02:04:24 +08:00
|
|
|
def : tInstAlias <"movs $Rdn, $imm",
|
|
|
|
(tMOVi8 tGPR:$Rdn, CPSR, imm0_255:$imm, 14, 0)>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2011-07-02 01:14:11 +08:00
|
|
|
// A7-73: MOV(2) - mov setting flag.
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2018-05-23 23:28:28 +08:00
|
|
|
let hasSideEffects = 0, isMoveReg = 1 in {
|
2011-07-01 07:38:17 +08:00
|
|
|
def tMOVr : Thumb1pI<(outs GPR:$Rd), (ins GPR:$Rm), AddrModeNone,
|
2011-07-14 07:22:26 +08:00
|
|
|
2, IIC_iMOVr,
|
2011-07-01 06:10:46 +08:00
|
|
|
"mov", "\t$Rd, $Rm", "", []>,
|
2013-06-07 01:03:13 +08:00
|
|
|
T1Special<{1,0,?,?}>, Sched<[WriteALU]> {
|
2010-12-03 09:55:47 +08:00
|
|
|
// A8.6.97
|
|
|
|
bits<4> Rd;
|
|
|
|
bits<4> Rm;
|
2011-07-01 07:38:17 +08:00
|
|
|
let Inst{7} = Rd{3};
|
|
|
|
let Inst{6-3} = Rm;
|
2010-12-03 09:55:47 +08:00
|
|
|
let Inst{2-0} = Rd{2-0};
|
|
|
|
}
|
2009-07-11 14:43:01 +08:00
|
|
|
let Defs = [CPSR] in
|
2010-12-03 09:55:47 +08:00
|
|
|
def tMOVSr : T1I<(outs tGPR:$Rd), (ins tGPR:$Rm), IIC_iMOVr,
|
2013-06-07 01:03:13 +08:00
|
|
|
"movs\t$Rd, $Rm", []>, Encoding16, Sched<[WriteALU]> {
|
2010-12-03 09:55:47 +08:00
|
|
|
// A8.6.97
|
|
|
|
bits<3> Rd;
|
|
|
|
bits<3> Rm;
|
2009-12-16 01:24:14 +08:00
|
|
|
let Inst{15-6} = 0b0000000000;
|
2010-12-03 09:55:47 +08:00
|
|
|
let Inst{5-3} = Rm;
|
|
|
|
let Inst{2-0} = Rd;
|
2009-12-16 01:24:14 +08:00
|
|
|
}
|
2014-11-26 08:46:26 +08:00
|
|
|
} // hasSideEffects
|
2009-04-08 04:34:09 +08:00
|
|
|
|
2010-12-01 10:36:55 +08:00
|
|
|
// Multiply register
|
2011-08-23 07:25:48 +08:00
|
|
|
let isCommutable = 1 in
|
2010-12-01 08:48:44 +08:00
|
|
|
def tMUL : // A8.6.105 T1
|
2011-08-20 06:07:46 +08:00
|
|
|
Thumb1sI<(outs tGPR:$Rd), (ins tGPR:$Rn, tGPR:$Rm), AddrModeNone, 2,
|
|
|
|
IIC_iMUL32, "mul", "\t$Rd, $Rn, $Rm", "$Rm = $Rd",
|
|
|
|
[(set tGPR:$Rd, (mul tGPR:$Rn, tGPR:$Rm))]>,
|
|
|
|
T1DataProcessing<0b1101> {
|
|
|
|
bits<3> Rd;
|
|
|
|
bits<3> Rn;
|
|
|
|
let Inst{5-3} = Rn;
|
|
|
|
let Inst{2-0} = Rd;
|
|
|
|
let AsmMatchConverter = "cvtThumbMultiply";
|
|
|
|
}
|
|
|
|
|
2011-08-23 02:04:24 +08:00
|
|
|
def :tInstAlias<"mul${s}${p} $Rdm, $Rn", (tMUL tGPR:$Rdm, s_cc_out:$s, tGPR:$Rn,
|
|
|
|
pred:$p)>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2010-12-01 09:20:15 +08:00
|
|
|
// Move inverse register
|
|
|
|
def tMVN : // A8.6.107
|
|
|
|
T1sIDPEncode<0b1111, (outs tGPR:$Rd), (ins tGPR:$Rn), IIC_iMVNr,
|
|
|
|
"mvn", "\t$Rd, $Rn",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(set tGPR:$Rd, (not tGPR:$Rn))]>, Sched<[WriteALU]>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2010-11-21 19:49:36 +08:00
|
|
|
// Bitwise or register
|
2009-07-11 14:43:01 +08:00
|
|
|
let isCommutable = 1 in
|
2010-12-01 08:48:44 +08:00
|
|
|
def tORR : // A8.6.114
|
|
|
|
T1sItDPEncode<0b1100, (outs tGPR:$Rdn), (ins tGPR:$Rn, tGPR:$Rm),
|
|
|
|
IIC_iBITr,
|
|
|
|
"orr", "\t$Rdn, $Rm",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(set tGPR:$Rdn, (or tGPR:$Rn, tGPR:$Rm))]>, Sched<[WriteALU]>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2010-11-21 19:49:36 +08:00
|
|
|
// Swaps
|
2010-12-01 10:28:08 +08:00
|
|
|
def tREV : // A8.6.134
|
|
|
|
T1pIMiscEncode<{1,0,1,0,0,0,?}, (outs tGPR:$Rd), (ins tGPR:$Rm),
|
|
|
|
IIC_iUNAr,
|
|
|
|
"rev", "\t$Rd, $Rm",
|
|
|
|
[(set tGPR:$Rd, (bswap tGPR:$Rm))]>,
|
2013-06-07 01:03:13 +08:00
|
|
|
Requires<[IsThumb, IsThumb1Only, HasV6]>, Sched<[WriteALU]>;
|
2010-12-01 10:28:08 +08:00
|
|
|
|
|
|
|
def tREV16 : // A8.6.135
|
|
|
|
T1pIMiscEncode<{1,0,1,0,0,1,?}, (outs tGPR:$Rd), (ins tGPR:$Rm),
|
|
|
|
IIC_iUNAr,
|
|
|
|
"rev16", "\t$Rd, $Rm",
|
2011-06-21 14:01:08 +08:00
|
|
|
[(set tGPR:$Rd, (rotr (bswap tGPR:$Rm), (i32 16)))]>,
|
2013-06-07 01:03:13 +08:00
|
|
|
Requires<[IsThumb, IsThumb1Only, HasV6]>, Sched<[WriteALU]>;
|
2010-12-01 10:28:08 +08:00
|
|
|
|
|
|
|
def tREVSH : // A8.6.136
|
|
|
|
T1pIMiscEncode<{1,0,1,0,1,1,?}, (outs tGPR:$Rd), (ins tGPR:$Rm),
|
|
|
|
IIC_iUNAr,
|
|
|
|
"revsh", "\t$Rd, $Rm",
|
2011-06-21 14:01:08 +08:00
|
|
|
[(set tGPR:$Rd, (sra (bswap tGPR:$Rm), (i32 16)))]>,
|
2013-06-07 01:03:13 +08:00
|
|
|
Requires<[IsThumb, IsThumb1Only, HasV6]>, Sched<[WriteALU]>;
|
2009-07-11 14:43:01 +08:00
|
|
|
|
2010-12-01 08:48:44 +08:00
|
|
|
// Rotate right register
|
|
|
|
def tROR : // A8.6.139
|
|
|
|
T1sItDPEncode<0b0111, (outs tGPR:$Rdn), (ins tGPR:$Rn, tGPR:$Rm),
|
|
|
|
IIC_iMOVsr,
|
|
|
|
"ror", "\t$Rdn, $Rm",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(set tGPR:$Rdn, (rotr tGPR:$Rn, tGPR:$Rm))]>,
|
|
|
|
Sched<[WriteALU]>;
|
2009-07-11 14:43:01 +08:00
|
|
|
|
2010-12-01 08:48:44 +08:00
|
|
|
// Negate register
|
2010-12-01 09:20:15 +08:00
|
|
|
def tRSB : // A8.6.141
|
|
|
|
T1sIDPEncode<0b1001, (outs tGPR:$Rd), (ins tGPR:$Rn),
|
|
|
|
IIC_iALUi,
|
|
|
|
"rsb", "\t$Rd, $Rn, #0",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(set tGPR:$Rd, (ineg tGPR:$Rn))]>, Sched<[WriteALU]>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-06-26 06:49:55 +08:00
|
|
|
// Subtract with carry register
|
2009-07-11 14:43:01 +08:00
|
|
|
let Uses = [CPSR] in
|
2010-12-01 08:48:44 +08:00
|
|
|
def tSBC : // A8.6.151
|
|
|
|
T1sItDPEncode<0b0110, (outs tGPR:$Rdn), (ins tGPR:$Rn, tGPR:$Rm),
|
|
|
|
IIC_iALUr,
|
|
|
|
"sbc", "\t$Rdn, $Rm",
|
2017-03-23 07:35:51 +08:00
|
|
|
[]>,
|
2013-06-07 01:03:13 +08:00
|
|
|
Sched<[WriteALU]>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-06-26 06:49:55 +08:00
|
|
|
// Subtract immediate
|
2010-12-01 09:20:15 +08:00
|
|
|
def tSUBi3 : // A8.6.210 T1
|
2011-09-17 06:58:42 +08:00
|
|
|
T1sIGenEncodeImm<0b01111, (outs tGPR:$Rd), (ins tGPR:$Rm, imm0_7:$imm3),
|
2010-12-01 09:20:15 +08:00
|
|
|
IIC_iALUi,
|
|
|
|
"sub", "\t$Rd, $Rm, $imm3",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(set tGPR:$Rd, (add tGPR:$Rm, imm0_7_neg:$imm3))]>,
|
|
|
|
Sched<[WriteALU]> {
|
2010-11-29 09:00:43 +08:00
|
|
|
bits<3> imm3;
|
|
|
|
let Inst{8-6} = imm3;
|
|
|
|
}
|
2009-03-28 07:06:27 +08:00
|
|
|
|
2010-12-01 08:48:44 +08:00
|
|
|
def tSUBi8 : // A8.6.210 T2
|
2011-09-17 06:58:42 +08:00
|
|
|
T1sItGenEncodeImm<{1,1,1,?,?}, (outs tGPR:$Rdn),
|
|
|
|
(ins tGPR:$Rn, imm0_255:$imm8), IIC_iALUi,
|
2010-12-01 08:48:44 +08:00
|
|
|
"sub", "\t$Rdn, $imm8",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(set tGPR:$Rdn, (add tGPR:$Rn, imm8_255_neg:$imm8))]>,
|
|
|
|
Sched<[WriteALU]>;
|
2009-03-28 07:06:27 +08:00
|
|
|
|
[ARM] [Assembler] Support negative immediates for A32, T32 and T16
Summary:
To support negative immediates for certain arithmetic instructions, the
instruction is converted to the inverse instruction with a negated (or inverted)
immediate. For example, "ADD r0, r1, #FFFFFFFF" cannot be encoded as an ADD
instruction. However, "SUB r0, r1, #1" is equivalent.
These conversions are different from instruction aliases. An alias maps
several assembler instructions onto one encoding. A conversion, however, maps
an *invalid* instruction--e.g. with an immediate that cannot be represented in
the encoding--to a different (but equivalent) instruction.
Several instructions with negative immediates were being converted already, but
this was not systematically tested, nor did it cover all instructions.
This patch implements all possible substitutions for ARM, Thumb1 and
Thumb2 assembler and adds tests. It also adds a feature flag
(-mattr=+no-neg-immediates) to turn these substitutions off. This is
helpful for users who want their code to assemble to exactly what they
wrote.
Reviewers: t.p.northover, rovka, samparker, javed.absar, peter.smith, rengolin
Reviewed By: javed.absar
Subscribers: aadg, aemerson, llvm-commits
Differential Revision: https://reviews.llvm.org/D30571
llvm-svn: 298380
2017-03-21 22:59:17 +08:00
|
|
|
def : tInstSubst<"add${s}${p} $rd, $rn, $imm",
|
|
|
|
(tSUBi3 tGPR:$rd, s_cc_out:$s, tGPR:$rn, mod_imm1_7_neg:$imm, pred:$p)>;
|
|
|
|
|
|
|
|
|
|
|
|
def : tInstSubst<"add${s}${p} $rdn, $imm",
|
|
|
|
(tSUBi8 tGPR:$rdn, s_cc_out:$s, mod_imm8_255_neg:$imm, pred:$p)>;
|
|
|
|
|
|
|
|
|
2010-12-01 09:20:15 +08:00
|
|
|
// Subtract register
|
|
|
|
def tSUBrr : // A8.6.212
|
|
|
|
T1sIGenEncode<0b01101, (outs tGPR:$Rd), (ins tGPR:$Rn, tGPR:$Rm),
|
|
|
|
IIC_iALUr,
|
|
|
|
"sub", "\t$Rd, $Rn, $Rm",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(set tGPR:$Rd, (sub tGPR:$Rn, tGPR:$Rm))]>,
|
|
|
|
Sched<[WriteALU]>;
|
2009-06-26 06:49:55 +08:00
|
|
|
|
2017-09-01 18:47:25 +08:00
|
|
|
def : tInstAlias <"sub${s}${p} $Rdn, $Rm",
|
|
|
|
(tSUBrr tGPR:$Rdn,s_cc_out:$s, tGPR:$Rdn, tGPR:$Rm, pred:$p)>;
|
|
|
|
|
2017-03-23 07:35:51 +08:00
|
|
|
/// Similar to the above except these set the 's' bit so the
|
|
|
|
/// instruction modifies the CPSR register.
|
|
|
|
///
|
|
|
|
/// These opcodes will be converted to the real non-S opcodes by
|
|
|
|
/// AdjustInstrPostInstrSelection after giving then an optional CPSR operand.
|
|
|
|
let hasPostISelHook = 1, Defs = [CPSR] in {
|
2017-04-21 15:35:21 +08:00
|
|
|
let Uses = [CPSR] in
|
2017-03-23 07:35:51 +08:00
|
|
|
def tSBCS : tPseudoInst<(outs tGPR:$Rdn), (ins tGPR:$Rn, tGPR:$Rm),
|
|
|
|
2, IIC_iALUr,
|
|
|
|
[(set tGPR:$Rdn, CPSR, (ARMsube tGPR:$Rn, tGPR:$Rm,
|
|
|
|
CPSR))]>,
|
|
|
|
Requires<[IsThumb1Only]>,
|
|
|
|
Sched<[WriteALU]>;
|
|
|
|
|
|
|
|
def tSUBSi3 : tPseudoInst<(outs tGPR:$Rd), (ins tGPR:$Rm, imm0_7:$imm3),
|
|
|
|
2, IIC_iALUi,
|
|
|
|
[(set tGPR:$Rd, CPSR, (ARMsubc tGPR:$Rm,
|
|
|
|
imm0_7:$imm3))]>,
|
|
|
|
Requires<[IsThumb1Only]>,
|
|
|
|
Sched<[WriteALU]>;
|
|
|
|
|
|
|
|
def tSUBSi8 : tPseudoInst<(outs tGPR:$Rdn), (ins tGPR:$Rn, imm0_255:$imm8),
|
|
|
|
2, IIC_iALUi,
|
|
|
|
[(set tGPR:$Rdn, CPSR, (ARMsubc tGPR:$Rn,
|
|
|
|
imm8_255:$imm8))]>,
|
|
|
|
Requires<[IsThumb1Only]>,
|
|
|
|
Sched<[WriteALU]>;
|
|
|
|
|
|
|
|
def tSUBSrr : tPseudoInst<(outs tGPR:$Rd), (ins tGPR:$Rn, tGPR:$Rm),
|
|
|
|
2, IIC_iALUr,
|
|
|
|
[(set tGPR:$Rd, CPSR, (ARMsubc tGPR:$Rn,
|
|
|
|
tGPR:$Rm))]>,
|
|
|
|
Requires<[IsThumb1Only]>,
|
|
|
|
Sched<[WriteALU]>;
|
2018-11-01 05:45:48 +08:00
|
|
|
|
|
|
|
def tRSBS : tPseudoInst<(outs tGPR:$Rd), (ins tGPR:$Rn),
|
|
|
|
2, IIC_iALUr,
|
|
|
|
[(set tGPR:$Rd, CPSR, (ARMsubc 0, tGPR:$Rn))]>,
|
|
|
|
Requires<[IsThumb1Only]>,
|
|
|
|
Sched<[WriteALU]>;
|
2017-03-23 07:35:51 +08:00
|
|
|
}
|
|
|
|
|
2010-12-01 09:20:15 +08:00
|
|
|
// Sign-extend byte
|
2010-12-01 10:28:08 +08:00
|
|
|
def tSXTB : // A8.6.222
|
|
|
|
T1pIMiscEncode<{0,0,1,0,0,1,?}, (outs tGPR:$Rd), (ins tGPR:$Rm),
|
|
|
|
IIC_iUNAr,
|
|
|
|
"sxtb", "\t$Rd, $Rm",
|
|
|
|
[(set tGPR:$Rd, (sext_inreg tGPR:$Rm, i8))]>,
|
2013-06-07 01:03:13 +08:00
|
|
|
Requires<[IsThumb, IsThumb1Only, HasV6]>,
|
|
|
|
Sched<[WriteALU]>;
|
2010-12-01 10:28:08 +08:00
|
|
|
|
|
|
|
// Sign-extend short
|
|
|
|
def tSXTH : // A8.6.224
|
|
|
|
T1pIMiscEncode<{0,0,1,0,0,0,?}, (outs tGPR:$Rd), (ins tGPR:$Rm),
|
|
|
|
IIC_iUNAr,
|
|
|
|
"sxth", "\t$Rd, $Rm",
|
|
|
|
[(set tGPR:$Rd, (sext_inreg tGPR:$Rm, i16))]>,
|
2013-06-07 01:03:13 +08:00
|
|
|
Requires<[IsThumb, IsThumb1Only, HasV6]>,
|
|
|
|
Sched<[WriteALU]>;
|
2010-12-01 10:28:08 +08:00
|
|
|
|
|
|
|
// Test
|
2010-09-15 04:47:43 +08:00
|
|
|
let isCompare = 1, isCommutable = 1, Defs = [CPSR] in
|
2010-12-01 10:28:08 +08:00
|
|
|
def tTST : // A8.6.230
|
|
|
|
T1pIDPEncode<0b1000, (outs), (ins tGPR:$Rn, tGPR:$Rm), IIC_iTSTr,
|
|
|
|
"tst", "\t$Rn, $Rm",
|
2013-06-07 01:03:13 +08:00
|
|
|
[(ARMcmpZ (and_su tGPR:$Rn, tGPR:$Rm), 0)]>,
|
|
|
|
Sched<[WriteALU]>;
|
2010-12-01 10:28:08 +08:00
|
|
|
|
2014-05-14 11:47:39 +08:00
|
|
|
// A8.8.247 UDF - Undefined (Encoding T1)
|
2014-05-22 12:46:46 +08:00
|
|
|
def tUDF : TI<(outs), (ins imm0_255:$imm8), IIC_Br, "udf\t$imm8",
|
|
|
|
[(int_arm_undefined imm0_255:$imm8)]>, Encoding16 {
|
2014-05-14 11:47:39 +08:00
|
|
|
bits<8> imm8;
|
|
|
|
let Inst{15-12} = 0b1101;
|
|
|
|
let Inst{11-8} = 0b1110;
|
|
|
|
let Inst{7-0} = imm8;
|
|
|
|
}
|
|
|
|
|
2018-10-25 02:10:38 +08:00
|
|
|
def : Pat<(debugtrap), (tBKPT 0)>, Requires<[IsThumb, HasV5T]>;
|
|
|
|
def : Pat<(debugtrap), (tUDF 254)>, Requires<[IsThumb, NoV5T]>;
|
|
|
|
|
2016-10-28 00:59:22 +08:00
|
|
|
def t__brkdiv0 : TI<(outs), (ins), IIC_Br, "__brkdiv0",
|
|
|
|
[(int_arm_undefined 249)]>, Encoding16,
|
|
|
|
Requires<[IsThumb, IsWindows]> {
|
|
|
|
let Inst = 0xdef9;
|
|
|
|
let isTerminator = 1;
|
|
|
|
}
|
|
|
|
|
2010-12-01 10:28:08 +08:00
|
|
|
// Zero-extend byte
|
|
|
|
def tUXTB : // A8.6.262
|
|
|
|
T1pIMiscEncode<{0,0,1,0,1,1,?}, (outs tGPR:$Rd), (ins tGPR:$Rm),
|
|
|
|
IIC_iUNAr,
|
|
|
|
"uxtb", "\t$Rd, $Rm",
|
|
|
|
[(set tGPR:$Rd, (and tGPR:$Rm, 0xFF))]>,
|
2013-06-07 01:03:13 +08:00
|
|
|
Requires<[IsThumb, IsThumb1Only, HasV6]>,
|
|
|
|
Sched<[WriteALU]>;
|
2010-12-01 10:28:08 +08:00
|
|
|
|
|
|
|
// Zero-extend short
|
|
|
|
def tUXTH : // A8.6.264
|
|
|
|
T1pIMiscEncode<{0,0,1,0,1,0,?}, (outs tGPR:$Rd), (ins tGPR:$Rm),
|
|
|
|
IIC_iUNAr,
|
|
|
|
"uxth", "\t$Rd, $Rm",
|
|
|
|
[(set tGPR:$Rd, (and tGPR:$Rm, 0xFFFF))]>,
|
2013-06-07 01:03:13 +08:00
|
|
|
Requires<[IsThumb, IsThumb1Only, HasV6]>, Sched<[WriteALU]>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2010-02-17 05:23:02 +08:00
|
|
|
// Conditional move tMOVCCr - Used to implement the Thumb SELECT_CC operation.
|
2009-10-30 02:10:34 +08:00
|
|
|
// Expanded after instruction selection into a branch sequence.
|
|
|
|
let usesCustomInserter = 1 in // Expanded after instruction selection.
|
2009-08-12 13:17:19 +08:00
|
|
|
def tMOVCCr_pseudo :
|
2013-08-22 17:57:11 +08:00
|
|
|
PseudoInst<(outs tGPR:$dst), (ins tGPR:$false, tGPR:$true, cmovpred:$p),
|
|
|
|
NoItinerary,
|
|
|
|
[(set tGPR:$dst, (ARMcmov tGPR:$false, tGPR:$true, cmovpred:$p))]>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
|
|
|
// tLEApcrel - Load a pc-relative address into a register without offending the
|
|
|
|
// assembler.
|
2009-07-24 02:26:03 +08:00
|
|
|
|
2010-12-15 06:28:03 +08:00
|
|
|
def tADR : T1I<(outs tGPR:$Rd), (ins t_adrlabel:$addr, pred:$p),
|
2011-08-18 04:37:40 +08:00
|
|
|
IIC_iALUi, "adr{$p}\t$Rd, $addr", []>,
|
2013-06-07 01:03:13 +08:00
|
|
|
T1Encoding<{1,0,1,0,0,?}>, Sched<[WriteALU]> {
|
2010-11-30 08:18:30 +08:00
|
|
|
bits<3> Rd;
|
2010-12-15 06:28:03 +08:00
|
|
|
bits<8> addr;
|
2010-11-30 08:18:30 +08:00
|
|
|
let Inst{10-8} = Rd;
|
2010-12-15 06:28:03 +08:00
|
|
|
let Inst{7-0} = addr;
|
2011-08-10 04:55:18 +08:00
|
|
|
let DecoderMethod = "DecodeThumbAddSpecialReg";
|
2010-11-30 08:18:30 +08:00
|
|
|
}
|
2007-01-27 10:29:45 +08:00
|
|
|
|
2017-05-17 01:59:07 +08:00
|
|
|
let hasSideEffects = 0, isReMaterializable = 1 in
|
2010-12-15 06:28:03 +08:00
|
|
|
def tLEApcrel : tPseudoInst<(outs tGPR:$Rd), (ins i32imm:$label, pred:$p),
|
2013-06-07 01:03:13 +08:00
|
|
|
2, IIC_iALUi, []>, Sched<[WriteALU]>;
|
2010-12-15 06:28:03 +08:00
|
|
|
|
2012-08-25 06:46:55 +08:00
|
|
|
let hasSideEffects = 1 in
|
2010-12-15 06:28:03 +08:00
|
|
|
def tLEApcrelJT : tPseudoInst<(outs tGPR:$Rd),
|
2015-05-14 04:28:38 +08:00
|
|
|
(ins i32imm:$label, pred:$p),
|
2013-06-07 01:03:13 +08:00
|
|
|
2, IIC_iALUi, []>, Sched<[WriteALU]>;
|
2010-12-15 06:28:03 +08:00
|
|
|
|
[Thumb-1] Synthesize TBB/TBH instructions to make use of compressed jump tables
[Reapplying r284580 and r285917 with fix and testing to ensure emitted jump tables for Thumb-1 have 4-byte alignment]
The TBB and TBH instructions in Thumb-2 allow jump tables to be compressed into sequences of bytes or shorts respectively. These instructions do not exist in Thumb-1, however it is possible to synthesize them out of a sequence of other instructions.
It turns out this sequence is so short that it's almost never a lose for performance and is ALWAYS a significant win for code size.
TBB example:
Before: lsls r0, r0, #2 After: add r0, pc
adr r1, .LJTI0_0 ldrb r0, [r0, #6]
ldr r0, [r0, r1] lsls r0, r0, #1
mov pc, r0 add pc, r0
=> No change in prologue code size or dynamic instruction count. Jump table shrunk by a factor of 4.
The only case that can increase dynamic instruction count is the TBH case:
Before: lsls r0, r4, #2 After: lsls r4, r4, #1
adr r1, .LJTI0_0 add r4, pc
ldr r0, [r0, r1] ldrh r4, [r4, #6]
mov pc, r0 lsls r4, r4, #1
add pc, r4
=> 1 more instruction in prologue. Jump table shrunk by a factor of 2.
So there is an argument that this should be disabled when optimizing for performance (and a TBH needs to be generated). I'm not so sure about that in practice, because on small cores with Thumb-1 performance is often tied to code size. But I'm willing to turn it off when optimizing for performance if people want (also note that TBHs are fairly rare in practice!)
llvm-svn: 285690
2016-11-01 21:37:41 +08:00
|
|
|
// Thumb-1 doesn't have the TBB or TBH instructions, but we can synthesize them
|
|
|
|
// and make use of the same compressed jump table format as Thumb-2.
|
2017-05-31 02:52:33 +08:00
|
|
|
let Size = 2, isBranch = 1, isTerminator = 1, isBarrier = 1,
|
|
|
|
isIndirectBranch = 1 in {
|
[Thumb-1] Synthesize TBB/TBH instructions to make use of compressed jump tables
[Reapplying r284580 and r285917 with fix and testing to ensure emitted jump tables for Thumb-1 have 4-byte alignment]
The TBB and TBH instructions in Thumb-2 allow jump tables to be compressed into sequences of bytes or shorts respectively. These instructions do not exist in Thumb-1, however it is possible to synthesize them out of a sequence of other instructions.
It turns out this sequence is so short that it's almost never a lose for performance and is ALWAYS a significant win for code size.
TBB example:
Before: lsls r0, r0, #2 After: add r0, pc
adr r1, .LJTI0_0 ldrb r0, [r0, #6]
ldr r0, [r0, r1] lsls r0, r0, #1
mov pc, r0 add pc, r0
=> No change in prologue code size or dynamic instruction count. Jump table shrunk by a factor of 4.
The only case that can increase dynamic instruction count is the TBH case:
Before: lsls r0, r4, #2 After: lsls r4, r4, #1
adr r1, .LJTI0_0 add r4, pc
ldr r0, [r0, r1] ldrh r4, [r4, #6]
mov pc, r0 lsls r4, r4, #1
add pc, r4
=> 1 more instruction in prologue. Jump table shrunk by a factor of 2.
So there is an argument that this should be disabled when optimizing for performance (and a TBH needs to be generated). I'm not so sure about that in practice, because on small cores with Thumb-1 performance is often tied to code size. But I'm willing to turn it off when optimizing for performance if people want (also note that TBHs are fairly rare in practice!)
llvm-svn: 285690
2016-11-01 21:37:41 +08:00
|
|
|
def tTBB_JT : tPseudoInst<(outs),
|
2017-06-29 16:45:31 +08:00
|
|
|
(ins tGPRwithpc:$base, tGPR:$index, i32imm:$jt, i32imm:$pclbl), 0,
|
|
|
|
IIC_Br, []>, Sched<[WriteBr]>;
|
[Thumb-1] Synthesize TBB/TBH instructions to make use of compressed jump tables
[Reapplying r284580 and r285917 with fix and testing to ensure emitted jump tables for Thumb-1 have 4-byte alignment]
The TBB and TBH instructions in Thumb-2 allow jump tables to be compressed into sequences of bytes or shorts respectively. These instructions do not exist in Thumb-1, however it is possible to synthesize them out of a sequence of other instructions.
It turns out this sequence is so short that it's almost never a lose for performance and is ALWAYS a significant win for code size.
TBB example:
Before: lsls r0, r0, #2 After: add r0, pc
adr r1, .LJTI0_0 ldrb r0, [r0, #6]
ldr r0, [r0, r1] lsls r0, r0, #1
mov pc, r0 add pc, r0
=> No change in prologue code size or dynamic instruction count. Jump table shrunk by a factor of 4.
The only case that can increase dynamic instruction count is the TBH case:
Before: lsls r0, r4, #2 After: lsls r4, r4, #1
adr r1, .LJTI0_0 add r4, pc
ldr r0, [r0, r1] ldrh r4, [r4, #6]
mov pc, r0 lsls r4, r4, #1
add pc, r4
=> 1 more instruction in prologue. Jump table shrunk by a factor of 2.
So there is an argument that this should be disabled when optimizing for performance (and a TBH needs to be generated). I'm not so sure about that in practice, because on small cores with Thumb-1 performance is often tied to code size. But I'm willing to turn it off when optimizing for performance if people want (also note that TBHs are fairly rare in practice!)
llvm-svn: 285690
2016-11-01 21:37:41 +08:00
|
|
|
|
|
|
|
def tTBH_JT : tPseudoInst<(outs),
|
2017-06-29 16:45:31 +08:00
|
|
|
(ins tGPRwithpc:$base, tGPR:$index, i32imm:$jt, i32imm:$pclbl), 0,
|
|
|
|
IIC_Br, []>, Sched<[WriteBr]>;
|
[Thumb-1] Synthesize TBB/TBH instructions to make use of compressed jump tables
[Reapplying r284580 and r285917 with fix and testing to ensure emitted jump tables for Thumb-1 have 4-byte alignment]
The TBB and TBH instructions in Thumb-2 allow jump tables to be compressed into sequences of bytes or shorts respectively. These instructions do not exist in Thumb-1, however it is possible to synthesize them out of a sequence of other instructions.
It turns out this sequence is so short that it's almost never a lose for performance and is ALWAYS a significant win for code size.
TBB example:
Before: lsls r0, r0, #2 After: add r0, pc
adr r1, .LJTI0_0 ldrb r0, [r0, #6]
ldr r0, [r0, r1] lsls r0, r0, #1
mov pc, r0 add pc, r0
=> No change in prologue code size or dynamic instruction count. Jump table shrunk by a factor of 4.
The only case that can increase dynamic instruction count is the TBH case:
Before: lsls r0, r4, #2 After: lsls r4, r4, #1
adr r1, .LJTI0_0 add r4, pc
ldr r0, [r0, r1] ldrh r4, [r4, #6]
mov pc, r0 lsls r4, r4, #1
add pc, r4
=> 1 more instruction in prologue. Jump table shrunk by a factor of 2.
So there is an argument that this should be disabled when optimizing for performance (and a TBH needs to be generated). I'm not so sure about that in practice, because on small cores with Thumb-1 performance is often tied to code size. But I'm willing to turn it off when optimizing for performance if people want (also note that TBHs are fairly rare in practice!)
llvm-svn: 285690
2016-11-01 21:37:41 +08:00
|
|
|
}
|
|
|
|
|
2007-04-27 21:54:47 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// TLS Instructions
|
|
|
|
//
|
|
|
|
|
|
|
|
// __aeabi_read_tp preserves the registers r1-r3.
|
2011-07-01 03:38:01 +08:00
|
|
|
// This is a pseudo inst so that we can get the encoding right,
|
|
|
|
// complete with fixup for the aeabi_read_tp function.
|
|
|
|
let isCall = 1, Defs = [R0, R12, LR, CPSR], Uses = [SP] in
|
2011-07-14 07:22:26 +08:00
|
|
|
def tTPsoft : tPseudoInst<(outs), (ins), 4, IIC_Br,
|
2013-06-07 02:51:01 +08:00
|
|
|
[(set R0, ARMthread_pointer)]>,
|
|
|
|
Sched<[WriteBr]>;
|
2007-04-27 21:54:47 +08:00
|
|
|
|
2010-12-01 10:36:55 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2009-12-02 02:10:36 +08:00
|
|
|
// SJLJ Exception handling intrinsics
|
2011-05-12 01:00:48 +08:00
|
|
|
//
|
2010-12-01 10:36:55 +08:00
|
|
|
|
|
|
|
// eh_sjlj_setjmp() is an instruction sequence to store the return address and
|
|
|
|
// save #0 in R0 for the non-longjmp case. Since by its nature we may be coming
|
|
|
|
// from some other function to get here, and we're using the stack frame for the
|
|
|
|
// containing function to save/restore registers, we can't keep anything live in
|
|
|
|
// regs across the eh_sjlj_setjmp(), else it will almost certainly have been
|
2011-04-15 13:18:47 +08:00
|
|
|
// tromped upon when we get here from a longjmp(). We force everything out of
|
2010-12-01 10:36:55 +08:00
|
|
|
// registers except for our own input by listing the relevant registers in
|
|
|
|
// Defs. By doing so, we also cause the prologue/epilogue code to actively
|
|
|
|
// preserve all of the callee-saved resgisters, which is exactly what we want.
|
|
|
|
// $val is a scratch register for our use.
|
2011-06-07 08:08:49 +08:00
|
|
|
let Defs = [ R0, R1, R2, R3, R4, R5, R6, R7, R12, CPSR ],
|
2011-10-18 06:26:23 +08:00
|
|
|
hasSideEffects = 1, isBarrier = 1, isCodeGenOnly = 1,
|
|
|
|
usesCustomInserter = 1 in
|
2010-11-30 08:50:22 +08:00
|
|
|
def tInt_eh_sjlj_setjmp : ThumbXI<(outs),(ins tGPR:$src, tGPR:$val),
|
2011-07-14 07:22:26 +08:00
|
|
|
AddrModeNone, 0, NoItinerary, "","",
|
2010-11-30 08:50:22 +08:00
|
|
|
[(set R0, (ARMeh_sjlj_setjmp tGPR:$src, tGPR:$val))]>;
|
2010-05-22 09:06:18 +08:00
|
|
|
|
2011-12-21 02:26:50 +08:00
|
|
|
// FIXME: Non-IOS version(s)
|
2010-11-01 03:15:18 +08:00
|
|
|
let isBarrier = 1, hasSideEffects = 1, isTerminator = 1, isCodeGenOnly = 1,
|
2010-11-30 08:50:22 +08:00
|
|
|
Defs = [ R7, LR, SP ] in
|
2010-05-22 09:06:18 +08:00
|
|
|
def tInt_eh_sjlj_longjmp : XI<(outs), (ins GPR:$src, GPR:$scratch),
|
2011-07-14 07:22:26 +08:00
|
|
|
AddrModeNone, 0, IndexModeNone,
|
2010-11-30 08:50:22 +08:00
|
|
|
Pseudo, NoItinerary, "", "",
|
|
|
|
[(ARMeh_sjlj_longjmp GPR:$src, GPR:$scratch)]>,
|
2016-03-11 00:26:37 +08:00
|
|
|
Requires<[IsThumb,IsNotWindows]>;
|
|
|
|
|
|
|
|
let isBarrier = 1, hasSideEffects = 1, isTerminator = 1, isCodeGenOnly = 1,
|
|
|
|
Defs = [ R11, LR, SP ] in
|
|
|
|
def tInt_WIN_eh_sjlj_longjmp
|
|
|
|
: XI<(outs), (ins GPR:$src, GPR:$scratch), AddrModeNone, 0, IndexModeNone,
|
|
|
|
Pseudo, NoItinerary, "", "", [(ARMeh_sjlj_longjmp GPR:$src, GPR:$scratch)]>,
|
|
|
|
Requires<[IsThumb,IsWindows]>;
|
2010-05-22 09:06:18 +08:00
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Non-Instruction Patterns
|
|
|
|
//
|
|
|
|
|
2010-12-08 04:41:06 +08:00
|
|
|
// Comparisons
|
|
|
|
def : T1Pat<(ARMcmpZ tGPR:$Rn, imm0_255:$imm8),
|
|
|
|
(tCMPi8 tGPR:$Rn, imm0_255:$imm8)>;
|
|
|
|
def : T1Pat<(ARMcmpZ tGPR:$Rn, tGPR:$Rm),
|
|
|
|
(tCMPr tGPR:$Rn, tGPR:$Rm)>;
|
|
|
|
|
2014-05-13 03:53:52 +08:00
|
|
|
// Bswap 16 with load/store
|
|
|
|
def : T1Pat<(srl (bswap (extloadi16 t_addrmode_is2:$addr)), (i32 16)),
|
|
|
|
(tREV16 (tLDRHi t_addrmode_is2:$addr))>;
|
2015-08-13 18:48:22 +08:00
|
|
|
def : T1Pat<(srl (bswap (extloadi16 t_addrmode_rr:$addr)), (i32 16)),
|
|
|
|
(tREV16 (tLDRHr t_addrmode_rr:$addr))>;
|
2014-05-13 03:53:52 +08:00
|
|
|
def : T1Pat<(truncstorei16 (srl (bswap tGPR:$Rn), (i32 16)),
|
|
|
|
t_addrmode_is2:$addr),
|
|
|
|
(tSTRHi(tREV16 tGPR:$Rn), t_addrmode_is2:$addr)>;
|
2015-08-13 18:48:22 +08:00
|
|
|
def : T1Pat<(truncstorei16 (srl (bswap tGPR:$Rn), (i32 16)),
|
|
|
|
t_addrmode_rr:$addr),
|
|
|
|
(tSTRHr (tREV16 tGPR:$Rn), t_addrmode_rr:$addr)>;
|
2014-05-13 03:53:52 +08:00
|
|
|
|
2013-11-25 22:40:57 +08:00
|
|
|
// ConstantPool
|
2009-07-28 03:59:26 +08:00
|
|
|
def : T1Pat<(ARMWrapper tconstpool :$dst), (tLEApcrel tconstpool :$dst)>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2013-12-02 18:35:41 +08:00
|
|
|
// GlobalAddress
|
2014-01-13 22:19:17 +08:00
|
|
|
def tLDRLIT_ga_pcrel : PseudoInst<(outs tGPR:$dst), (ins i32imm:$addr),
|
2013-12-02 18:35:41 +08:00
|
|
|
IIC_iLoadiALU,
|
2014-01-13 22:19:17 +08:00
|
|
|
[(set tGPR:$dst,
|
2013-12-02 18:35:41 +08:00
|
|
|
(ARMWrapperPIC tglobaladdr:$addr))]>,
|
2017-11-14 04:45:38 +08:00
|
|
|
Requires<[IsThumb, DontUseMovtInPic]>;
|
2013-12-02 18:35:41 +08:00
|
|
|
|
2014-01-13 22:19:17 +08:00
|
|
|
def tLDRLIT_ga_abs : PseudoInst<(outs tGPR:$dst), (ins i32imm:$src),
|
|
|
|
IIC_iLoad_i,
|
|
|
|
[(set tGPR:$dst,
|
2013-12-02 18:35:41 +08:00
|
|
|
(ARMWrapper tglobaladdr:$src))]>,
|
|
|
|
Requires<[IsThumb, DontUseMovt]>;
|
|
|
|
|
2016-01-07 17:03:03 +08:00
|
|
|
// TLS globals
|
|
|
|
def : Pat<(ARMWrapperPIC tglobaltlsaddr:$addr),
|
|
|
|
(tLDRLIT_ga_pcrel tglobaltlsaddr:$addr)>,
|
2017-11-14 04:45:38 +08:00
|
|
|
Requires<[IsThumb, DontUseMovtInPic]>;
|
2016-01-07 17:03:03 +08:00
|
|
|
def : Pat<(ARMWrapper tglobaltlsaddr:$addr),
|
|
|
|
(tLDRLIT_ga_abs tglobaltlsaddr:$addr)>,
|
|
|
|
Requires<[IsThumb, DontUseMovt]>;
|
|
|
|
|
2013-12-02 18:35:41 +08:00
|
|
|
|
2007-01-27 10:29:45 +08:00
|
|
|
// JumpTable
|
2015-05-14 04:28:38 +08:00
|
|
|
def : T1Pat<(ARMWrapperJT tjumptable:$dst),
|
|
|
|
(tLEApcrelJT tjumptable:$dst)>;
|
2007-01-27 10:29:45 +08:00
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
// Direct calls
|
2016-05-11 03:17:47 +08:00
|
|
|
def : T1Pat<(ARMcall texternalsym:$func), (tBL texternalsym:$func)>,
|
2012-04-06 08:04:58 +08:00
|
|
|
Requires<[IsThumb]>;
|
2009-07-30 05:26:42 +08:00
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
// zextload i1 -> zextload i8
|
2010-12-14 11:36:38 +08:00
|
|
|
def : T1Pat<(zextloadi1 t_addrmode_is1:$addr),
|
|
|
|
(tLDRBi t_addrmode_is1:$addr)>;
|
2015-08-13 18:48:22 +08:00
|
|
|
def : T1Pat<(zextloadi1 t_addrmode_rr:$addr),
|
|
|
|
(tLDRBr t_addrmode_rr:$addr)>;
|
2009-03-28 07:06:27 +08:00
|
|
|
|
2015-02-25 22:41:06 +08:00
|
|
|
// extload from the stack -> word load from the stack, as it avoids having to
|
|
|
|
// materialize the base in a separate register. This only works when a word
|
|
|
|
// load puts the byte/halfword value in the same place in the register that the
|
|
|
|
// byte/halfword load would, i.e. when little-endian.
|
|
|
|
def : T1Pat<(extloadi1 t_addrmode_sp:$addr), (tLDRspi t_addrmode_sp:$addr)>,
|
|
|
|
Requires<[IsThumb, IsThumb1Only, IsLE]>;
|
|
|
|
def : T1Pat<(extloadi8 t_addrmode_sp:$addr), (tLDRspi t_addrmode_sp:$addr)>,
|
|
|
|
Requires<[IsThumb, IsThumb1Only, IsLE]>;
|
|
|
|
def : T1Pat<(extloadi16 t_addrmode_sp:$addr), (tLDRspi t_addrmode_sp:$addr)>,
|
|
|
|
Requires<[IsThumb, IsThumb1Only, IsLE]>;
|
|
|
|
|
2007-01-27 03:13:16 +08:00
|
|
|
// extload -> zextload
|
2015-08-13 18:48:22 +08:00
|
|
|
def : T1Pat<(extloadi1 t_addrmode_is1:$addr), (tLDRBi t_addrmode_is1:$addr)>;
|
|
|
|
def : T1Pat<(extloadi1 t_addrmode_rr:$addr), (tLDRBr t_addrmode_rr:$addr)>;
|
|
|
|
def : T1Pat<(extloadi8 t_addrmode_is1:$addr), (tLDRBi t_addrmode_is1:$addr)>;
|
|
|
|
def : T1Pat<(extloadi8 t_addrmode_rr:$addr), (tLDRBr t_addrmode_rr:$addr)>;
|
|
|
|
def : T1Pat<(extloadi16 t_addrmode_is2:$addr), (tLDRHi t_addrmode_is2:$addr)>;
|
|
|
|
def : T1Pat<(extloadi16 t_addrmode_rr:$addr), (tLDRHr t_addrmode_rr:$addr)>;
|
2007-01-27 03:13:16 +08:00
|
|
|
|
2016-07-15 16:03:56 +08:00
|
|
|
// post-inc loads and stores
|
|
|
|
|
|
|
|
// post-inc LDR -> LDM r0!, {r1}. The way operands are layed out in LDMs is
|
|
|
|
// different to how ISel expects them for a post-inc load, so use a pseudo
|
|
|
|
// and expand it just after ISel.
|
2017-01-21 02:30:28 +08:00
|
|
|
let usesCustomInserter = 1, mayLoad =1,
|
2016-07-15 16:03:56 +08:00
|
|
|
Constraints = "$Rn = $Rn_wb,@earlyclobber $Rn_wb" in
|
|
|
|
def tLDR_postidx: tPseudoInst<(outs rGPR:$Rt, rGPR:$Rn_wb),
|
|
|
|
(ins rGPR:$Rn, pred:$p),
|
|
|
|
4, IIC_iStore_ru,
|
|
|
|
[]>;
|
|
|
|
|
|
|
|
// post-inc STR -> STM r0!, {r1}. The layout of this (because it doesn't def
|
|
|
|
// multiple registers) is the same in ISel as MachineInstr, so there's no need
|
|
|
|
// for a pseudo.
|
|
|
|
def : T1Pat<(post_store rGPR:$Rt, rGPR:$Rn, 4),
|
|
|
|
(tSTMIA_UPD rGPR:$Rn, rGPR:$Rt)>;
|
|
|
|
|
2009-08-28 08:31:43 +08:00
|
|
|
// If it's impossible to use [r,r] address mode for sextload, select to
|
2009-07-11 15:08:13 +08:00
|
|
|
// ldr{b|h} + sxt{b|h} instead.
|
2010-12-15 08:58:57 +08:00
|
|
|
def : T1Pat<(sextloadi8 t_addrmode_is1:$addr),
|
|
|
|
(tSXTB (tLDRBi t_addrmode_is1:$addr))>,
|
|
|
|
Requires<[IsThumb, IsThumb1Only, HasV6]>;
|
2015-08-13 18:48:22 +08:00
|
|
|
def : T1Pat<(sextloadi8 t_addrmode_rr:$addr),
|
|
|
|
(tSXTB (tLDRBr t_addrmode_rr:$addr))>,
|
2010-11-02 01:08:58 +08:00
|
|
|
Requires<[IsThumb, IsThumb1Only, HasV6]>;
|
2010-12-15 08:58:57 +08:00
|
|
|
def : T1Pat<(sextloadi16 t_addrmode_is2:$addr),
|
|
|
|
(tSXTH (tLDRHi t_addrmode_is2:$addr))>,
|
|
|
|
Requires<[IsThumb, IsThumb1Only, HasV6]>;
|
2015-08-13 18:48:22 +08:00
|
|
|
def : T1Pat<(sextloadi16 t_addrmode_rr:$addr),
|
|
|
|
(tSXTH (tLDRHr t_addrmode_rr:$addr))>,
|
2010-11-02 01:08:58 +08:00
|
|
|
Requires<[IsThumb, IsThumb1Only, HasV6]>;
|
2009-07-11 15:08:13 +08:00
|
|
|
|
2010-12-15 08:58:57 +08:00
|
|
|
def : T1Pat<(sextloadi8 t_addrmode_is1:$addr),
|
|
|
|
(tASRri (tLSLri (tLDRBi t_addrmode_is1:$addr), 24), 24)>;
|
2015-08-13 18:48:22 +08:00
|
|
|
def : T1Pat<(sextloadi8 t_addrmode_rr:$addr),
|
|
|
|
(tASRri (tLSLri (tLDRBr t_addrmode_rr:$addr), 24), 24)>;
|
2010-12-15 08:58:57 +08:00
|
|
|
def : T1Pat<(sextloadi16 t_addrmode_is2:$addr),
|
|
|
|
(tASRri (tLSLri (tLDRHi t_addrmode_is2:$addr), 16), 16)>;
|
2015-08-13 18:48:22 +08:00
|
|
|
def : T1Pat<(sextloadi16 t_addrmode_rr:$addr),
|
|
|
|
(tASRri (tLSLri (tLDRHr t_addrmode_rr:$addr), 16), 16)>;
|
2009-07-11 15:08:13 +08:00
|
|
|
|
2011-09-16 06:18:49 +08:00
|
|
|
def : T1Pat<(atomic_load_8 t_addrmode_is1:$src),
|
2012-08-28 11:11:27 +08:00
|
|
|
(tLDRBi t_addrmode_is1:$src)>;
|
2015-08-13 18:48:22 +08:00
|
|
|
def : T1Pat<(atomic_load_8 t_addrmode_rr:$src),
|
|
|
|
(tLDRBr t_addrmode_rr:$src)>;
|
2011-09-16 06:18:49 +08:00
|
|
|
def : T1Pat<(atomic_load_16 t_addrmode_is2:$src),
|
2012-08-28 11:11:27 +08:00
|
|
|
(tLDRHi t_addrmode_is2:$src)>;
|
2015-08-13 18:48:22 +08:00
|
|
|
def : T1Pat<(atomic_load_16 t_addrmode_rr:$src),
|
|
|
|
(tLDRHr t_addrmode_rr:$src)>;
|
2011-09-16 06:18:49 +08:00
|
|
|
def : T1Pat<(atomic_load_32 t_addrmode_is4:$src),
|
2012-08-28 11:11:27 +08:00
|
|
|
(tLDRi t_addrmode_is4:$src)>;
|
2015-08-13 18:48:22 +08:00
|
|
|
def : T1Pat<(atomic_load_32 t_addrmode_rr:$src),
|
|
|
|
(tLDRr t_addrmode_rr:$src)>;
|
2011-09-16 06:18:49 +08:00
|
|
|
def : T1Pat<(atomic_store_8 t_addrmode_is1:$ptr, tGPR:$val),
|
|
|
|
(tSTRBi tGPR:$val, t_addrmode_is1:$ptr)>;
|
2015-08-13 18:48:22 +08:00
|
|
|
def : T1Pat<(atomic_store_8 t_addrmode_rr:$ptr, tGPR:$val),
|
|
|
|
(tSTRBr tGPR:$val, t_addrmode_rr:$ptr)>;
|
2011-09-16 06:18:49 +08:00
|
|
|
def : T1Pat<(atomic_store_16 t_addrmode_is2:$ptr, tGPR:$val),
|
|
|
|
(tSTRHi tGPR:$val, t_addrmode_is2:$ptr)>;
|
2015-08-13 18:48:22 +08:00
|
|
|
def : T1Pat<(atomic_store_16 t_addrmode_rr:$ptr, tGPR:$val),
|
|
|
|
(tSTRHr tGPR:$val, t_addrmode_rr:$ptr)>;
|
2011-09-16 06:18:49 +08:00
|
|
|
def : T1Pat<(atomic_store_32 t_addrmode_is4:$ptr, tGPR:$val),
|
|
|
|
(tSTRi tGPR:$val, t_addrmode_is4:$ptr)>;
|
2015-08-13 18:48:22 +08:00
|
|
|
def : T1Pat<(atomic_store_32 t_addrmode_rr:$ptr, tGPR:$val),
|
|
|
|
(tSTRr tGPR:$val, t_addrmode_rr:$ptr)>;
|
2011-09-16 06:18:49 +08:00
|
|
|
|
2007-01-19 15:51:42 +08:00
|
|
|
// Large immediate handling.
|
|
|
|
|
|
|
|
// Two piece imms.
|
2009-06-27 10:26:13 +08:00
|
|
|
def : T1Pat<(i32 thumb_immshifted:$src),
|
|
|
|
(tLSLri (tMOVi8 (thumb_immshifted_val imm:$src)),
|
|
|
|
(thumb_immshifted_shamt imm:$src))>;
|
2007-01-19 15:51:42 +08:00
|
|
|
|
2009-06-27 10:26:13 +08:00
|
|
|
def : T1Pat<(i32 imm0_255_comp:$src),
|
2017-03-10 21:21:12 +08:00
|
|
|
(tMVN (tMOVi8 (imm_not_XFORM imm:$src)))>;
|
2009-11-07 07:52:48 +08:00
|
|
|
|
2016-06-14 21:33:07 +08:00
|
|
|
def : T1Pat<(i32 imm256_510:$src),
|
2016-06-07 21:10:14 +08:00
|
|
|
(tADDi8 (tMOVi8 255),
|
2016-06-14 21:33:07 +08:00
|
|
|
(thumb_imm256_510_addend imm:$src))>;
|
2016-06-07 21:10:14 +08:00
|
|
|
|
2009-11-07 07:52:48 +08:00
|
|
|
// Pseudo instruction that combines ldr from constpool and add pc. This should
|
|
|
|
// be expanded into two instructions late to allow if-conversion and
|
|
|
|
// scheduling.
|
|
|
|
let isReMaterializable = 1 in
|
|
|
|
def tLDRpci_pic : PseudoInst<(outs GPR:$dst), (ins i32imm:$addr, pclabel:$cp),
|
2010-12-01 10:36:55 +08:00
|
|
|
NoItinerary,
|
2009-11-07 07:52:48 +08:00
|
|
|
[(set GPR:$dst, (ARMpic_add (load (ARMWrapper tconstpool:$addr)),
|
|
|
|
imm:$cp))]>,
|
2010-11-02 01:08:58 +08:00
|
|
|
Requires<[IsThumb, IsThumb1Only]>;
|
2011-07-09 01:40:42 +08:00
|
|
|
|
|
|
|
// Pseudo-instruction for merged POP and return.
|
|
|
|
// FIXME: remove when we have a way to marking a MI with these properties.
|
|
|
|
let isReturn = 1, isTerminator = 1, isBarrier = 1, mayLoad = 1,
|
|
|
|
hasExtraDefRegAllocReq = 1 in
|
|
|
|
def tPOP_RET : tPseudoExpand<(outs), (ins pred:$p, reglist:$regs, variable_ops),
|
2011-07-14 07:22:26 +08:00
|
|
|
2, IIC_iPop_Br, [],
|
2013-06-07 02:51:01 +08:00
|
|
|
(tPOP pred:$p, reglist:$regs)>, Sched<[WriteBrL]>;
|
2011-07-09 01:40:42 +08:00
|
|
|
|
2011-07-09 06:25:23 +08:00
|
|
|
// Indirect branch using "mov pc, $Rm"
|
|
|
|
let isBranch = 1, isTerminator = 1, isBarrier = 1, isIndirectBranch = 1 in {
|
2011-07-09 06:33:49 +08:00
|
|
|
def tBRIND : tPseudoExpand<(outs), (ins GPR:$Rm, pred:$p),
|
2011-07-14 07:22:26 +08:00
|
|
|
2, IIC_Br, [(brind GPR:$Rm)],
|
2013-06-07 02:51:01 +08:00
|
|
|
(tMOVr PC, GPR:$Rm, pred:$p)>, Sched<[WriteBr]>;
|
2011-07-09 06:25:23 +08:00
|
|
|
}
|
2011-08-20 07:24:36 +08:00
|
|
|
|
|
|
|
|
|
|
|
// In Thumb1, "nop" is encoded as a "mov r8, r8". Technically, the bf00
|
|
|
|
// encoding is available on ARMv6K, but we don't differentiate that finely.
|
2016-06-03 21:19:43 +08:00
|
|
|
def : InstAlias<"nop", (tMOVr R8, R8, 14, 0), 0>, Requires<[IsThumb, IsThumb1Only]>;
|
2011-09-20 08:10:37 +08:00
|
|
|
|
|
|
|
|
2011-12-14 04:23:22 +08:00
|
|
|
// "neg" is and alias for "rsb rd, rn, #0"
|
|
|
|
def : tInstAlias<"neg${s}${p} $Rd, $Rm",
|
|
|
|
(tRSB tGPR:$Rd, s_cc_out:$s, tGPR:$Rm, pred:$p)>;
|
|
|
|
|
2012-04-11 08:15:16 +08:00
|
|
|
|
|
|
|
// Implied destination operand forms for shifts.
|
|
|
|
def : tInstAlias<"lsl${s}${p} $Rdm, $imm",
|
|
|
|
(tLSLri tGPR:$Rdm, cc_out:$s, tGPR:$Rdm, imm0_31:$imm, pred:$p)>;
|
|
|
|
def : tInstAlias<"lsr${s}${p} $Rdm, $imm",
|
|
|
|
(tLSRri tGPR:$Rdm, cc_out:$s, tGPR:$Rdm, imm_sr:$imm, pred:$p)>;
|
|
|
|
def : tInstAlias<"asr${s}${p} $Rdm, $imm",
|
|
|
|
(tASRri tGPR:$Rdm, cc_out:$s, tGPR:$Rdm, imm_sr:$imm, pred:$p)>;
|
2016-05-13 05:22:31 +08:00
|
|
|
|
|
|
|
// Pseudo instruction ldr Rt, =immediate
|
|
|
|
def tLDRConstPool
|
|
|
|
: tAsmPseudo<"ldr${p} $Rt, $immediate",
|
|
|
|
(ins tGPR:$Rt, const_pool_asm_imm:$immediate, pred:$p)>;
|