mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-22 07:32:48 +00:00
e8be6c6391
replacement of multiple values. This is slightly more efficient than doing multiple ReplaceAllUsesOfValueWith calls, and theoretically could be optimized even further. However, an important property of this new function is that it handles the case where the source value set and destination value set overlap. This makes it feasible for isel to use SelectNodeTo in many very common cases, which is advantageous because SelectNodeTo avoids a temporary node and it doesn't require CSEMap updates for users of values that don't change position. Revamp MorphNodeTo, which is what does all the work of SelectNodeTo, to handle operand lists more efficiently, and to correctly handle a number of corner cases to which its new wider use exposes it. This commit also includes a change to the encoding of post-isel opcodes in SDNodes; now instead of being sandwiched between the target-independent pre-isel opcodes and the target-dependent pre-isel opcodes, post-isel opcodes are now represented as negative values. This makes it possible to test if an opcode is pre-isel or post-isel without having to know the size of the current target's post-isel instruction set. These changes speed up llc overall by 3% and reduce memory usage by 10% on the InstructionCombining.cpp testcase with -fast and -regalloc=local. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@53728 91177308-0d34-0410-b5e6-96231b3b80d8
859 lines
32 KiB
C++
859 lines
32 KiB
C++
//===-- ARMISelDAGToDAG.cpp - A dag to dag inst selector for ARM ----------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines an instruction selector for the ARM target.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ARM.h"
|
|
#include "ARMISelLowering.h"
|
|
#include "ARMTargetMachine.h"
|
|
#include "ARMAddressingModes.h"
|
|
#include "llvm/CallingConv.h"
|
|
#include "llvm/Constants.h"
|
|
#include "llvm/DerivedTypes.h"
|
|
#include "llvm/Function.h"
|
|
#include "llvm/Intrinsics.h"
|
|
#include "llvm/CodeGen/MachineFrameInfo.h"
|
|
#include "llvm/CodeGen/MachineFunction.h"
|
|
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
|
#include "llvm/CodeGen/SelectionDAG.h"
|
|
#include "llvm/CodeGen/SelectionDAGISel.h"
|
|
#include "llvm/Target/TargetLowering.h"
|
|
#include "llvm/Target/TargetOptions.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "llvm/Support/Debug.h"
|
|
using namespace llvm;
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
/// ARMDAGToDAGISel - ARM specific code to select ARM machine
|
|
/// instructions for SelectionDAG operations.
|
|
///
|
|
namespace {
|
|
class ARMDAGToDAGISel : public SelectionDAGISel {
|
|
ARMTargetLowering Lowering;
|
|
|
|
/// Subtarget - Keep a pointer to the ARMSubtarget around so that we can
|
|
/// make the right decision when generating code for different targets.
|
|
const ARMSubtarget *Subtarget;
|
|
|
|
public:
|
|
explicit ARMDAGToDAGISel(ARMTargetMachine &TM)
|
|
: SelectionDAGISel(Lowering), Lowering(TM),
|
|
Subtarget(&TM.getSubtarget<ARMSubtarget>()) {
|
|
}
|
|
|
|
virtual const char *getPassName() const {
|
|
return "ARM Instruction Selection";
|
|
}
|
|
|
|
SDNode *Select(SDOperand Op);
|
|
virtual void InstructionSelect(SelectionDAG &DAG);
|
|
bool SelectAddrMode2(SDOperand Op, SDOperand N, SDOperand &Base,
|
|
SDOperand &Offset, SDOperand &Opc);
|
|
bool SelectAddrMode2Offset(SDOperand Op, SDOperand N,
|
|
SDOperand &Offset, SDOperand &Opc);
|
|
bool SelectAddrMode3(SDOperand Op, SDOperand N, SDOperand &Base,
|
|
SDOperand &Offset, SDOperand &Opc);
|
|
bool SelectAddrMode3Offset(SDOperand Op, SDOperand N,
|
|
SDOperand &Offset, SDOperand &Opc);
|
|
bool SelectAddrMode5(SDOperand Op, SDOperand N, SDOperand &Base,
|
|
SDOperand &Offset);
|
|
|
|
bool SelectAddrModePC(SDOperand Op, SDOperand N, SDOperand &Offset,
|
|
SDOperand &Label);
|
|
|
|
bool SelectThumbAddrModeRR(SDOperand Op, SDOperand N, SDOperand &Base,
|
|
SDOperand &Offset);
|
|
bool SelectThumbAddrModeRI5(SDOperand Op, SDOperand N, unsigned Scale,
|
|
SDOperand &Base, SDOperand &OffImm,
|
|
SDOperand &Offset);
|
|
bool SelectThumbAddrModeS1(SDOperand Op, SDOperand N, SDOperand &Base,
|
|
SDOperand &OffImm, SDOperand &Offset);
|
|
bool SelectThumbAddrModeS2(SDOperand Op, SDOperand N, SDOperand &Base,
|
|
SDOperand &OffImm, SDOperand &Offset);
|
|
bool SelectThumbAddrModeS4(SDOperand Op, SDOperand N, SDOperand &Base,
|
|
SDOperand &OffImm, SDOperand &Offset);
|
|
bool SelectThumbAddrModeSP(SDOperand Op, SDOperand N, SDOperand &Base,
|
|
SDOperand &OffImm);
|
|
|
|
bool SelectShifterOperandReg(SDOperand Op, SDOperand N, SDOperand &A,
|
|
SDOperand &B, SDOperand &C);
|
|
|
|
// Include the pieces autogenerated from the target description.
|
|
#include "ARMGenDAGISel.inc"
|
|
};
|
|
}
|
|
|
|
void ARMDAGToDAGISel::InstructionSelect(SelectionDAG &DAG) {
|
|
DEBUG(BB->dump());
|
|
|
|
DAG.setRoot(SelectRoot(DAG.getRoot()));
|
|
DAG.RemoveDeadNodes();
|
|
}
|
|
|
|
bool ARMDAGToDAGISel::SelectAddrMode2(SDOperand Op, SDOperand N,
|
|
SDOperand &Base, SDOperand &Offset,
|
|
SDOperand &Opc) {
|
|
if (N.getOpcode() == ISD::MUL) {
|
|
if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(N.getOperand(1))) {
|
|
// X * [3,5,9] -> X + X * [2,4,8] etc.
|
|
int RHSC = (int)RHS->getValue();
|
|
if (RHSC & 1) {
|
|
RHSC = RHSC & ~1;
|
|
ARM_AM::AddrOpc AddSub = ARM_AM::add;
|
|
if (RHSC < 0) {
|
|
AddSub = ARM_AM::sub;
|
|
RHSC = - RHSC;
|
|
}
|
|
if (isPowerOf2_32(RHSC)) {
|
|
unsigned ShAmt = Log2_32(RHSC);
|
|
Base = Offset = N.getOperand(0);
|
|
Opc = CurDAG->getTargetConstant(ARM_AM::getAM2Opc(AddSub, ShAmt,
|
|
ARM_AM::lsl),
|
|
MVT::i32);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (N.getOpcode() != ISD::ADD && N.getOpcode() != ISD::SUB) {
|
|
Base = N;
|
|
if (N.getOpcode() == ISD::FrameIndex) {
|
|
int FI = cast<FrameIndexSDNode>(N)->getIndex();
|
|
Base = CurDAG->getTargetFrameIndex(FI, TLI.getPointerTy());
|
|
} else if (N.getOpcode() == ARMISD::Wrapper) {
|
|
Base = N.getOperand(0);
|
|
}
|
|
Offset = CurDAG->getRegister(0, MVT::i32);
|
|
Opc = CurDAG->getTargetConstant(ARM_AM::getAM2Opc(ARM_AM::add, 0,
|
|
ARM_AM::no_shift),
|
|
MVT::i32);
|
|
return true;
|
|
}
|
|
|
|
// Match simple R +/- imm12 operands.
|
|
if (N.getOpcode() == ISD::ADD)
|
|
if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(N.getOperand(1))) {
|
|
int RHSC = (int)RHS->getValue();
|
|
if ((RHSC >= 0 && RHSC < 0x1000) ||
|
|
(RHSC < 0 && RHSC > -0x1000)) { // 12 bits.
|
|
Base = N.getOperand(0);
|
|
if (Base.getOpcode() == ISD::FrameIndex) {
|
|
int FI = cast<FrameIndexSDNode>(Base)->getIndex();
|
|
Base = CurDAG->getTargetFrameIndex(FI, TLI.getPointerTy());
|
|
}
|
|
Offset = CurDAG->getRegister(0, MVT::i32);
|
|
|
|
ARM_AM::AddrOpc AddSub = ARM_AM::add;
|
|
if (RHSC < 0) {
|
|
AddSub = ARM_AM::sub;
|
|
RHSC = - RHSC;
|
|
}
|
|
Opc = CurDAG->getTargetConstant(ARM_AM::getAM2Opc(AddSub, RHSC,
|
|
ARM_AM::no_shift),
|
|
MVT::i32);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Otherwise this is R +/- [possibly shifted] R
|
|
ARM_AM::AddrOpc AddSub = N.getOpcode() == ISD::ADD ? ARM_AM::add:ARM_AM::sub;
|
|
ARM_AM::ShiftOpc ShOpcVal = ARM_AM::getShiftOpcForNode(N.getOperand(1));
|
|
unsigned ShAmt = 0;
|
|
|
|
Base = N.getOperand(0);
|
|
Offset = N.getOperand(1);
|
|
|
|
if (ShOpcVal != ARM_AM::no_shift) {
|
|
// Check to see if the RHS of the shift is a constant, if not, we can't fold
|
|
// it.
|
|
if (ConstantSDNode *Sh =
|
|
dyn_cast<ConstantSDNode>(N.getOperand(1).getOperand(1))) {
|
|
ShAmt = Sh->getValue();
|
|
Offset = N.getOperand(1).getOperand(0);
|
|
} else {
|
|
ShOpcVal = ARM_AM::no_shift;
|
|
}
|
|
}
|
|
|
|
// Try matching (R shl C) + (R).
|
|
if (N.getOpcode() == ISD::ADD && ShOpcVal == ARM_AM::no_shift) {
|
|
ShOpcVal = ARM_AM::getShiftOpcForNode(N.getOperand(0));
|
|
if (ShOpcVal != ARM_AM::no_shift) {
|
|
// Check to see if the RHS of the shift is a constant, if not, we can't
|
|
// fold it.
|
|
if (ConstantSDNode *Sh =
|
|
dyn_cast<ConstantSDNode>(N.getOperand(0).getOperand(1))) {
|
|
ShAmt = Sh->getValue();
|
|
Offset = N.getOperand(0).getOperand(0);
|
|
Base = N.getOperand(1);
|
|
} else {
|
|
ShOpcVal = ARM_AM::no_shift;
|
|
}
|
|
}
|
|
}
|
|
|
|
Opc = CurDAG->getTargetConstant(ARM_AM::getAM2Opc(AddSub, ShAmt, ShOpcVal),
|
|
MVT::i32);
|
|
return true;
|
|
}
|
|
|
|
bool ARMDAGToDAGISel::SelectAddrMode2Offset(SDOperand Op, SDOperand N,
|
|
SDOperand &Offset, SDOperand &Opc) {
|
|
unsigned Opcode = Op.getOpcode();
|
|
ISD::MemIndexedMode AM = (Opcode == ISD::LOAD)
|
|
? cast<LoadSDNode>(Op)->getAddressingMode()
|
|
: cast<StoreSDNode>(Op)->getAddressingMode();
|
|
ARM_AM::AddrOpc AddSub = (AM == ISD::PRE_INC || AM == ISD::POST_INC)
|
|
? ARM_AM::add : ARM_AM::sub;
|
|
if (ConstantSDNode *C = dyn_cast<ConstantSDNode>(N)) {
|
|
int Val = (int)C->getValue();
|
|
if (Val >= 0 && Val < 0x1000) { // 12 bits.
|
|
Offset = CurDAG->getRegister(0, MVT::i32);
|
|
Opc = CurDAG->getTargetConstant(ARM_AM::getAM2Opc(AddSub, Val,
|
|
ARM_AM::no_shift),
|
|
MVT::i32);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
Offset = N;
|
|
ARM_AM::ShiftOpc ShOpcVal = ARM_AM::getShiftOpcForNode(N);
|
|
unsigned ShAmt = 0;
|
|
if (ShOpcVal != ARM_AM::no_shift) {
|
|
// Check to see if the RHS of the shift is a constant, if not, we can't fold
|
|
// it.
|
|
if (ConstantSDNode *Sh = dyn_cast<ConstantSDNode>(N.getOperand(1))) {
|
|
ShAmt = Sh->getValue();
|
|
Offset = N.getOperand(0);
|
|
} else {
|
|
ShOpcVal = ARM_AM::no_shift;
|
|
}
|
|
}
|
|
|
|
Opc = CurDAG->getTargetConstant(ARM_AM::getAM2Opc(AddSub, ShAmt, ShOpcVal),
|
|
MVT::i32);
|
|
return true;
|
|
}
|
|
|
|
|
|
bool ARMDAGToDAGISel::SelectAddrMode3(SDOperand Op, SDOperand N,
|
|
SDOperand &Base, SDOperand &Offset,
|
|
SDOperand &Opc) {
|
|
if (N.getOpcode() == ISD::SUB) {
|
|
// X - C is canonicalize to X + -C, no need to handle it here.
|
|
Base = N.getOperand(0);
|
|
Offset = N.getOperand(1);
|
|
Opc = CurDAG->getTargetConstant(ARM_AM::getAM3Opc(ARM_AM::sub, 0),MVT::i32);
|
|
return true;
|
|
}
|
|
|
|
if (N.getOpcode() != ISD::ADD) {
|
|
Base = N;
|
|
if (N.getOpcode() == ISD::FrameIndex) {
|
|
int FI = cast<FrameIndexSDNode>(N)->getIndex();
|
|
Base = CurDAG->getTargetFrameIndex(FI, TLI.getPointerTy());
|
|
}
|
|
Offset = CurDAG->getRegister(0, MVT::i32);
|
|
Opc = CurDAG->getTargetConstant(ARM_AM::getAM3Opc(ARM_AM::add, 0),MVT::i32);
|
|
return true;
|
|
}
|
|
|
|
// If the RHS is +/- imm8, fold into addr mode.
|
|
if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(N.getOperand(1))) {
|
|
int RHSC = (int)RHS->getValue();
|
|
if ((RHSC >= 0 && RHSC < 256) ||
|
|
(RHSC < 0 && RHSC > -256)) { // note -256 itself isn't allowed.
|
|
Base = N.getOperand(0);
|
|
if (Base.getOpcode() == ISD::FrameIndex) {
|
|
int FI = cast<FrameIndexSDNode>(Base)->getIndex();
|
|
Base = CurDAG->getTargetFrameIndex(FI, TLI.getPointerTy());
|
|
}
|
|
Offset = CurDAG->getRegister(0, MVT::i32);
|
|
|
|
ARM_AM::AddrOpc AddSub = ARM_AM::add;
|
|
if (RHSC < 0) {
|
|
AddSub = ARM_AM::sub;
|
|
RHSC = - RHSC;
|
|
}
|
|
Opc = CurDAG->getTargetConstant(ARM_AM::getAM3Opc(AddSub, RHSC),MVT::i32);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
Base = N.getOperand(0);
|
|
Offset = N.getOperand(1);
|
|
Opc = CurDAG->getTargetConstant(ARM_AM::getAM3Opc(ARM_AM::add, 0), MVT::i32);
|
|
return true;
|
|
}
|
|
|
|
bool ARMDAGToDAGISel::SelectAddrMode3Offset(SDOperand Op, SDOperand N,
|
|
SDOperand &Offset, SDOperand &Opc) {
|
|
unsigned Opcode = Op.getOpcode();
|
|
ISD::MemIndexedMode AM = (Opcode == ISD::LOAD)
|
|
? cast<LoadSDNode>(Op)->getAddressingMode()
|
|
: cast<StoreSDNode>(Op)->getAddressingMode();
|
|
ARM_AM::AddrOpc AddSub = (AM == ISD::PRE_INC || AM == ISD::POST_INC)
|
|
? ARM_AM::add : ARM_AM::sub;
|
|
if (ConstantSDNode *C = dyn_cast<ConstantSDNode>(N)) {
|
|
int Val = (int)C->getValue();
|
|
if (Val >= 0 && Val < 256) {
|
|
Offset = CurDAG->getRegister(0, MVT::i32);
|
|
Opc = CurDAG->getTargetConstant(ARM_AM::getAM3Opc(AddSub, Val), MVT::i32);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
Offset = N;
|
|
Opc = CurDAG->getTargetConstant(ARM_AM::getAM3Opc(AddSub, 0), MVT::i32);
|
|
return true;
|
|
}
|
|
|
|
|
|
bool ARMDAGToDAGISel::SelectAddrMode5(SDOperand Op, SDOperand N,
|
|
SDOperand &Base, SDOperand &Offset) {
|
|
if (N.getOpcode() != ISD::ADD) {
|
|
Base = N;
|
|
if (N.getOpcode() == ISD::FrameIndex) {
|
|
int FI = cast<FrameIndexSDNode>(N)->getIndex();
|
|
Base = CurDAG->getTargetFrameIndex(FI, TLI.getPointerTy());
|
|
} else if (N.getOpcode() == ARMISD::Wrapper) {
|
|
Base = N.getOperand(0);
|
|
}
|
|
Offset = CurDAG->getTargetConstant(ARM_AM::getAM5Opc(ARM_AM::add, 0),
|
|
MVT::i32);
|
|
return true;
|
|
}
|
|
|
|
// If the RHS is +/- imm8, fold into addr mode.
|
|
if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(N.getOperand(1))) {
|
|
int RHSC = (int)RHS->getValue();
|
|
if ((RHSC & 3) == 0) { // The constant is implicitly multiplied by 4.
|
|
RHSC >>= 2;
|
|
if ((RHSC >= 0 && RHSC < 256) ||
|
|
(RHSC < 0 && RHSC > -256)) { // note -256 itself isn't allowed.
|
|
Base = N.getOperand(0);
|
|
if (Base.getOpcode() == ISD::FrameIndex) {
|
|
int FI = cast<FrameIndexSDNode>(Base)->getIndex();
|
|
Base = CurDAG->getTargetFrameIndex(FI, TLI.getPointerTy());
|
|
}
|
|
|
|
ARM_AM::AddrOpc AddSub = ARM_AM::add;
|
|
if (RHSC < 0) {
|
|
AddSub = ARM_AM::sub;
|
|
RHSC = - RHSC;
|
|
}
|
|
Offset = CurDAG->getTargetConstant(ARM_AM::getAM5Opc(AddSub, RHSC),
|
|
MVT::i32);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
Base = N;
|
|
Offset = CurDAG->getTargetConstant(ARM_AM::getAM5Opc(ARM_AM::add, 0),
|
|
MVT::i32);
|
|
return true;
|
|
}
|
|
|
|
bool ARMDAGToDAGISel::SelectAddrModePC(SDOperand Op, SDOperand N,
|
|
SDOperand &Offset, SDOperand &Label) {
|
|
if (N.getOpcode() == ARMISD::PIC_ADD && N.hasOneUse()) {
|
|
Offset = N.getOperand(0);
|
|
SDOperand N1 = N.getOperand(1);
|
|
Label = CurDAG->getTargetConstant(cast<ConstantSDNode>(N1)->getValue(),
|
|
MVT::i32);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ARMDAGToDAGISel::SelectThumbAddrModeRR(SDOperand Op, SDOperand N,
|
|
SDOperand &Base, SDOperand &Offset){
|
|
if (N.getOpcode() != ISD::ADD) {
|
|
Base = N;
|
|
// We must materialize a zero in a reg! Returning an constant here won't
|
|
// work since its node is -1 so it won't get added to the selection queue.
|
|
// Explicitly issue a tMOVri8 node!
|
|
Offset = SDOperand(CurDAG->getTargetNode(ARM::tMOVi8, MVT::i32,
|
|
CurDAG->getTargetConstant(0, MVT::i32)), 0);
|
|
return true;
|
|
}
|
|
|
|
Base = N.getOperand(0);
|
|
Offset = N.getOperand(1);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
ARMDAGToDAGISel::SelectThumbAddrModeRI5(SDOperand Op, SDOperand N,
|
|
unsigned Scale, SDOperand &Base,
|
|
SDOperand &OffImm, SDOperand &Offset) {
|
|
if (Scale == 4) {
|
|
SDOperand TmpBase, TmpOffImm;
|
|
if (SelectThumbAddrModeSP(Op, N, TmpBase, TmpOffImm))
|
|
return false; // We want to select tLDRspi / tSTRspi instead.
|
|
if (N.getOpcode() == ARMISD::Wrapper &&
|
|
N.getOperand(0).getOpcode() == ISD::TargetConstantPool)
|
|
return false; // We want to select tLDRpci instead.
|
|
}
|
|
|
|
if (N.getOpcode() != ISD::ADD) {
|
|
Base = (N.getOpcode() == ARMISD::Wrapper) ? N.getOperand(0) : N;
|
|
Offset = CurDAG->getRegister(0, MVT::i32);
|
|
OffImm = CurDAG->getTargetConstant(0, MVT::i32);
|
|
return true;
|
|
}
|
|
|
|
// Thumb does not have [sp, r] address mode.
|
|
RegisterSDNode *LHSR = dyn_cast<RegisterSDNode>(N.getOperand(0));
|
|
RegisterSDNode *RHSR = dyn_cast<RegisterSDNode>(N.getOperand(1));
|
|
if ((LHSR && LHSR->getReg() == ARM::SP) ||
|
|
(RHSR && RHSR->getReg() == ARM::SP)) {
|
|
Base = N;
|
|
Offset = CurDAG->getRegister(0, MVT::i32);
|
|
OffImm = CurDAG->getTargetConstant(0, MVT::i32);
|
|
return true;
|
|
}
|
|
|
|
// If the RHS is + imm5 * scale, fold into addr mode.
|
|
if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(N.getOperand(1))) {
|
|
int RHSC = (int)RHS->getValue();
|
|
if ((RHSC & (Scale-1)) == 0) { // The constant is implicitly multiplied.
|
|
RHSC /= Scale;
|
|
if (RHSC >= 0 && RHSC < 32) {
|
|
Base = N.getOperand(0);
|
|
Offset = CurDAG->getRegister(0, MVT::i32);
|
|
OffImm = CurDAG->getTargetConstant(RHSC, MVT::i32);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
Base = N.getOperand(0);
|
|
Offset = N.getOperand(1);
|
|
OffImm = CurDAG->getTargetConstant(0, MVT::i32);
|
|
return true;
|
|
}
|
|
|
|
bool ARMDAGToDAGISel::SelectThumbAddrModeS1(SDOperand Op, SDOperand N,
|
|
SDOperand &Base, SDOperand &OffImm,
|
|
SDOperand &Offset) {
|
|
return SelectThumbAddrModeRI5(Op, N, 1, Base, OffImm, Offset);
|
|
}
|
|
|
|
bool ARMDAGToDAGISel::SelectThumbAddrModeS2(SDOperand Op, SDOperand N,
|
|
SDOperand &Base, SDOperand &OffImm,
|
|
SDOperand &Offset) {
|
|
return SelectThumbAddrModeRI5(Op, N, 2, Base, OffImm, Offset);
|
|
}
|
|
|
|
bool ARMDAGToDAGISel::SelectThumbAddrModeS4(SDOperand Op, SDOperand N,
|
|
SDOperand &Base, SDOperand &OffImm,
|
|
SDOperand &Offset) {
|
|
return SelectThumbAddrModeRI5(Op, N, 4, Base, OffImm, Offset);
|
|
}
|
|
|
|
bool ARMDAGToDAGISel::SelectThumbAddrModeSP(SDOperand Op, SDOperand N,
|
|
SDOperand &Base, SDOperand &OffImm) {
|
|
if (N.getOpcode() == ISD::FrameIndex) {
|
|
int FI = cast<FrameIndexSDNode>(N)->getIndex();
|
|
Base = CurDAG->getTargetFrameIndex(FI, TLI.getPointerTy());
|
|
OffImm = CurDAG->getTargetConstant(0, MVT::i32);
|
|
return true;
|
|
}
|
|
|
|
if (N.getOpcode() != ISD::ADD)
|
|
return false;
|
|
|
|
RegisterSDNode *LHSR = dyn_cast<RegisterSDNode>(N.getOperand(0));
|
|
if (N.getOperand(0).getOpcode() == ISD::FrameIndex ||
|
|
(LHSR && LHSR->getReg() == ARM::SP)) {
|
|
// If the RHS is + imm8 * scale, fold into addr mode.
|
|
if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(N.getOperand(1))) {
|
|
int RHSC = (int)RHS->getValue();
|
|
if ((RHSC & 3) == 0) { // The constant is implicitly multiplied.
|
|
RHSC >>= 2;
|
|
if (RHSC >= 0 && RHSC < 256) {
|
|
Base = N.getOperand(0);
|
|
if (Base.getOpcode() == ISD::FrameIndex) {
|
|
int FI = cast<FrameIndexSDNode>(Base)->getIndex();
|
|
Base = CurDAG->getTargetFrameIndex(FI, TLI.getPointerTy());
|
|
}
|
|
OffImm = CurDAG->getTargetConstant(RHSC, MVT::i32);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ARMDAGToDAGISel::SelectShifterOperandReg(SDOperand Op,
|
|
SDOperand N,
|
|
SDOperand &BaseReg,
|
|
SDOperand &ShReg,
|
|
SDOperand &Opc) {
|
|
ARM_AM::ShiftOpc ShOpcVal = ARM_AM::getShiftOpcForNode(N);
|
|
|
|
// Don't match base register only case. That is matched to a separate
|
|
// lower complexity pattern with explicit register operand.
|
|
if (ShOpcVal == ARM_AM::no_shift) return false;
|
|
|
|
BaseReg = N.getOperand(0);
|
|
unsigned ShImmVal = 0;
|
|
if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(N.getOperand(1))) {
|
|
ShReg = CurDAG->getRegister(0, MVT::i32);
|
|
ShImmVal = RHS->getValue() & 31;
|
|
} else {
|
|
ShReg = N.getOperand(1);
|
|
}
|
|
Opc = CurDAG->getTargetConstant(ARM_AM::getSORegOpc(ShOpcVal, ShImmVal),
|
|
MVT::i32);
|
|
return true;
|
|
}
|
|
|
|
/// getAL - Returns a ARMCC::AL immediate node.
|
|
static inline SDOperand getAL(SelectionDAG *CurDAG) {
|
|
return CurDAG->getTargetConstant((uint64_t)ARMCC::AL, MVT::i32);
|
|
}
|
|
|
|
|
|
SDNode *ARMDAGToDAGISel::Select(SDOperand Op) {
|
|
SDNode *N = Op.Val;
|
|
|
|
if (N->isMachineOpcode())
|
|
return NULL; // Already selected.
|
|
|
|
switch (N->getOpcode()) {
|
|
default: break;
|
|
case ISD::Constant: {
|
|
unsigned Val = cast<ConstantSDNode>(N)->getValue();
|
|
bool UseCP = true;
|
|
if (Subtarget->isThumb())
|
|
UseCP = (Val > 255 && // MOV
|
|
~Val > 255 && // MOV + MVN
|
|
!ARM_AM::isThumbImmShiftedVal(Val)); // MOV + LSL
|
|
else
|
|
UseCP = (ARM_AM::getSOImmVal(Val) == -1 && // MOV
|
|
ARM_AM::getSOImmVal(~Val) == -1 && // MVN
|
|
!ARM_AM::isSOImmTwoPartVal(Val)); // two instrs.
|
|
if (UseCP) {
|
|
SDOperand CPIdx =
|
|
CurDAG->getTargetConstantPool(ConstantInt::get(Type::Int32Ty, Val),
|
|
TLI.getPointerTy());
|
|
|
|
SDNode *ResNode;
|
|
if (Subtarget->isThumb())
|
|
ResNode = CurDAG->getTargetNode(ARM::tLDRcp, MVT::i32, MVT::Other,
|
|
CPIdx, CurDAG->getEntryNode());
|
|
else {
|
|
SDOperand Ops[] = {
|
|
CPIdx,
|
|
CurDAG->getRegister(0, MVT::i32),
|
|
CurDAG->getTargetConstant(0, MVT::i32),
|
|
getAL(CurDAG),
|
|
CurDAG->getRegister(0, MVT::i32),
|
|
CurDAG->getEntryNode()
|
|
};
|
|
ResNode=CurDAG->getTargetNode(ARM::LDRcp, MVT::i32, MVT::Other, Ops, 6);
|
|
}
|
|
ReplaceUses(Op, SDOperand(ResNode, 0));
|
|
return NULL;
|
|
}
|
|
|
|
// Other cases are autogenerated.
|
|
break;
|
|
}
|
|
case ISD::FrameIndex: {
|
|
// Selects to ADDri FI, 0 which in turn will become ADDri SP, imm.
|
|
int FI = cast<FrameIndexSDNode>(N)->getIndex();
|
|
SDOperand TFI = CurDAG->getTargetFrameIndex(FI, TLI.getPointerTy());
|
|
if (Subtarget->isThumb())
|
|
return CurDAG->SelectNodeTo(N, ARM::tADDrSPi, MVT::i32, TFI,
|
|
CurDAG->getTargetConstant(0, MVT::i32));
|
|
else {
|
|
SDOperand Ops[] = { TFI, CurDAG->getTargetConstant(0, MVT::i32),
|
|
getAL(CurDAG), CurDAG->getRegister(0, MVT::i32),
|
|
CurDAG->getRegister(0, MVT::i32) };
|
|
return CurDAG->SelectNodeTo(N, ARM::ADDri, MVT::i32, Ops, 5);
|
|
}
|
|
}
|
|
case ISD::ADD: {
|
|
// Select add sp, c to tADDhirr.
|
|
SDOperand N0 = Op.getOperand(0);
|
|
SDOperand N1 = Op.getOperand(1);
|
|
RegisterSDNode *LHSR = dyn_cast<RegisterSDNode>(Op.getOperand(0));
|
|
RegisterSDNode *RHSR = dyn_cast<RegisterSDNode>(Op.getOperand(1));
|
|
if (LHSR && LHSR->getReg() == ARM::SP) {
|
|
std::swap(N0, N1);
|
|
std::swap(LHSR, RHSR);
|
|
}
|
|
if (RHSR && RHSR->getReg() == ARM::SP) {
|
|
AddToISelQueue(N0);
|
|
AddToISelQueue(N1);
|
|
return CurDAG->SelectNodeTo(N, ARM::tADDhirr, Op.getValueType(), N0, N1);
|
|
}
|
|
break;
|
|
}
|
|
case ISD::MUL:
|
|
if (Subtarget->isThumb())
|
|
break;
|
|
if (ConstantSDNode *C = dyn_cast<ConstantSDNode>(Op.getOperand(1))) {
|
|
unsigned RHSV = C->getValue();
|
|
if (!RHSV) break;
|
|
if (isPowerOf2_32(RHSV-1)) { // 2^n+1?
|
|
SDOperand V = Op.getOperand(0);
|
|
AddToISelQueue(V);
|
|
unsigned ShImm = ARM_AM::getSORegOpc(ARM_AM::lsl, Log2_32(RHSV-1));
|
|
SDOperand Ops[] = { V, V, CurDAG->getRegister(0, MVT::i32),
|
|
CurDAG->getTargetConstant(ShImm, MVT::i32),
|
|
getAL(CurDAG), CurDAG->getRegister(0, MVT::i32),
|
|
CurDAG->getRegister(0, MVT::i32) };
|
|
return CurDAG->SelectNodeTo(N, ARM::ADDrs, MVT::i32, Ops, 7);
|
|
}
|
|
if (isPowerOf2_32(RHSV+1)) { // 2^n-1?
|
|
SDOperand V = Op.getOperand(0);
|
|
AddToISelQueue(V);
|
|
unsigned ShImm = ARM_AM::getSORegOpc(ARM_AM::lsl, Log2_32(RHSV+1));
|
|
SDOperand Ops[] = { V, V, CurDAG->getRegister(0, MVT::i32),
|
|
CurDAG->getTargetConstant(ShImm, MVT::i32),
|
|
getAL(CurDAG), CurDAG->getRegister(0, MVT::i32),
|
|
CurDAG->getRegister(0, MVT::i32) };
|
|
return CurDAG->SelectNodeTo(N, ARM::RSBrs, MVT::i32, Ops, 7);
|
|
}
|
|
}
|
|
break;
|
|
case ARMISD::FMRRD:
|
|
AddToISelQueue(Op.getOperand(0));
|
|
return CurDAG->getTargetNode(ARM::FMRRD, MVT::i32, MVT::i32,
|
|
Op.getOperand(0), getAL(CurDAG),
|
|
CurDAG->getRegister(0, MVT::i32));
|
|
case ISD::UMUL_LOHI: {
|
|
AddToISelQueue(Op.getOperand(0));
|
|
AddToISelQueue(Op.getOperand(1));
|
|
SDOperand Ops[] = { Op.getOperand(0), Op.getOperand(1),
|
|
getAL(CurDAG), CurDAG->getRegister(0, MVT::i32),
|
|
CurDAG->getRegister(0, MVT::i32) };
|
|
return CurDAG->getTargetNode(ARM::UMULL, MVT::i32, MVT::i32, Ops, 5);
|
|
}
|
|
case ISD::SMUL_LOHI: {
|
|
AddToISelQueue(Op.getOperand(0));
|
|
AddToISelQueue(Op.getOperand(1));
|
|
SDOperand Ops[] = { Op.getOperand(0), Op.getOperand(1),
|
|
getAL(CurDAG), CurDAG->getRegister(0, MVT::i32),
|
|
CurDAG->getRegister(0, MVT::i32) };
|
|
return CurDAG->getTargetNode(ARM::SMULL, MVT::i32, MVT::i32, Ops, 5);
|
|
}
|
|
case ISD::LOAD: {
|
|
LoadSDNode *LD = cast<LoadSDNode>(Op);
|
|
ISD::MemIndexedMode AM = LD->getAddressingMode();
|
|
MVT LoadedVT = LD->getMemoryVT();
|
|
if (AM != ISD::UNINDEXED) {
|
|
SDOperand Offset, AMOpc;
|
|
bool isPre = (AM == ISD::PRE_INC) || (AM == ISD::PRE_DEC);
|
|
unsigned Opcode = 0;
|
|
bool Match = false;
|
|
if (LoadedVT == MVT::i32 &&
|
|
SelectAddrMode2Offset(Op, LD->getOffset(), Offset, AMOpc)) {
|
|
Opcode = isPre ? ARM::LDR_PRE : ARM::LDR_POST;
|
|
Match = true;
|
|
} else if (LoadedVT == MVT::i16 &&
|
|
SelectAddrMode3Offset(Op, LD->getOffset(), Offset, AMOpc)) {
|
|
Match = true;
|
|
Opcode = (LD->getExtensionType() == ISD::SEXTLOAD)
|
|
? (isPre ? ARM::LDRSH_PRE : ARM::LDRSH_POST)
|
|
: (isPre ? ARM::LDRH_PRE : ARM::LDRH_POST);
|
|
} else if (LoadedVT == MVT::i8 || LoadedVT == MVT::i1) {
|
|
if (LD->getExtensionType() == ISD::SEXTLOAD) {
|
|
if (SelectAddrMode3Offset(Op, LD->getOffset(), Offset, AMOpc)) {
|
|
Match = true;
|
|
Opcode = isPre ? ARM::LDRSB_PRE : ARM::LDRSB_POST;
|
|
}
|
|
} else {
|
|
if (SelectAddrMode2Offset(Op, LD->getOffset(), Offset, AMOpc)) {
|
|
Match = true;
|
|
Opcode = isPre ? ARM::LDRB_PRE : ARM::LDRB_POST;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Match) {
|
|
SDOperand Chain = LD->getChain();
|
|
SDOperand Base = LD->getBasePtr();
|
|
AddToISelQueue(Chain);
|
|
AddToISelQueue(Base);
|
|
AddToISelQueue(Offset);
|
|
SDOperand Ops[]= { Base, Offset, AMOpc, getAL(CurDAG),
|
|
CurDAG->getRegister(0, MVT::i32), Chain };
|
|
return CurDAG->getTargetNode(Opcode, MVT::i32, MVT::i32,
|
|
MVT::Other, Ops, 6);
|
|
}
|
|
}
|
|
// Other cases are autogenerated.
|
|
break;
|
|
}
|
|
case ARMISD::BRCOND: {
|
|
// Pattern: (ARMbrcond:void (bb:Other):$dst, (imm:i32):$cc)
|
|
// Emits: (Bcc:void (bb:Other):$dst, (imm:i32):$cc)
|
|
// Pattern complexity = 6 cost = 1 size = 0
|
|
|
|
// Pattern: (ARMbrcond:void (bb:Other):$dst, (imm:i32):$cc)
|
|
// Emits: (tBcc:void (bb:Other):$dst, (imm:i32):$cc)
|
|
// Pattern complexity = 6 cost = 1 size = 0
|
|
|
|
unsigned Opc = Subtarget->isThumb() ? ARM::tBcc : ARM::Bcc;
|
|
SDOperand Chain = Op.getOperand(0);
|
|
SDOperand N1 = Op.getOperand(1);
|
|
SDOperand N2 = Op.getOperand(2);
|
|
SDOperand N3 = Op.getOperand(3);
|
|
SDOperand InFlag = Op.getOperand(4);
|
|
assert(N1.getOpcode() == ISD::BasicBlock);
|
|
assert(N2.getOpcode() == ISD::Constant);
|
|
assert(N3.getOpcode() == ISD::Register);
|
|
|
|
AddToISelQueue(Chain);
|
|
AddToISelQueue(N1);
|
|
AddToISelQueue(InFlag);
|
|
SDOperand Tmp2 = CurDAG->getTargetConstant(((unsigned)
|
|
cast<ConstantSDNode>(N2)->getValue()), MVT::i32);
|
|
SDOperand Ops[] = { N1, Tmp2, N3, Chain, InFlag };
|
|
SDNode *ResNode = CurDAG->getTargetNode(Opc, MVT::Other, MVT::Flag, Ops, 5);
|
|
Chain = SDOperand(ResNode, 0);
|
|
if (Op.Val->getNumValues() == 2) {
|
|
InFlag = SDOperand(ResNode, 1);
|
|
ReplaceUses(SDOperand(Op.Val, 1), InFlag);
|
|
}
|
|
ReplaceUses(SDOperand(Op.Val, 0), SDOperand(Chain.Val, Chain.ResNo));
|
|
return NULL;
|
|
}
|
|
case ARMISD::CMOV: {
|
|
bool isThumb = Subtarget->isThumb();
|
|
MVT VT = Op.getValueType();
|
|
SDOperand N0 = Op.getOperand(0);
|
|
SDOperand N1 = Op.getOperand(1);
|
|
SDOperand N2 = Op.getOperand(2);
|
|
SDOperand N3 = Op.getOperand(3);
|
|
SDOperand InFlag = Op.getOperand(4);
|
|
assert(N2.getOpcode() == ISD::Constant);
|
|
assert(N3.getOpcode() == ISD::Register);
|
|
|
|
// Pattern: (ARMcmov:i32 GPR:i32:$false, so_reg:i32:$true, (imm:i32):$cc)
|
|
// Emits: (MOVCCs:i32 GPR:i32:$false, so_reg:i32:$true, (imm:i32):$cc)
|
|
// Pattern complexity = 18 cost = 1 size = 0
|
|
SDOperand CPTmp0;
|
|
SDOperand CPTmp1;
|
|
SDOperand CPTmp2;
|
|
if (!isThumb && VT == MVT::i32 &&
|
|
SelectShifterOperandReg(Op, N1, CPTmp0, CPTmp1, CPTmp2)) {
|
|
AddToISelQueue(N0);
|
|
AddToISelQueue(CPTmp0);
|
|
AddToISelQueue(CPTmp1);
|
|
AddToISelQueue(CPTmp2);
|
|
AddToISelQueue(InFlag);
|
|
SDOperand Tmp2 = CurDAG->getTargetConstant(((unsigned)
|
|
cast<ConstantSDNode>(N2)->getValue()), MVT::i32);
|
|
SDOperand Ops[] = { N0, CPTmp0, CPTmp1, CPTmp2, Tmp2, N3, InFlag };
|
|
return CurDAG->SelectNodeTo(Op.Val, ARM::MOVCCs, MVT::i32, Ops, 7);
|
|
}
|
|
|
|
// Pattern: (ARMcmov:i32 GPR:i32:$false,
|
|
// (imm:i32)<<P:Predicate_so_imm>><<X:so_imm_XFORM>>:$true,
|
|
// (imm:i32):$cc)
|
|
// Emits: (MOVCCi:i32 GPR:i32:$false,
|
|
// (so_imm_XFORM:i32 (imm:i32):$true), (imm:i32):$cc)
|
|
// Pattern complexity = 10 cost = 1 size = 0
|
|
if (VT == MVT::i32 &&
|
|
N3.getOpcode() == ISD::Constant &&
|
|
Predicate_so_imm(N3.Val)) {
|
|
AddToISelQueue(N0);
|
|
AddToISelQueue(InFlag);
|
|
SDOperand Tmp1 = CurDAG->getTargetConstant(((unsigned)
|
|
cast<ConstantSDNode>(N1)->getValue()), MVT::i32);
|
|
Tmp1 = Transform_so_imm_XFORM(Tmp1.Val);
|
|
SDOperand Tmp2 = CurDAG->getTargetConstant(((unsigned)
|
|
cast<ConstantSDNode>(N2)->getValue()), MVT::i32);
|
|
SDOperand Ops[] = { N0, Tmp1, Tmp2, N3, InFlag };
|
|
return CurDAG->SelectNodeTo(Op.Val, ARM::MOVCCi, MVT::i32, Ops, 5);
|
|
}
|
|
|
|
// Pattern: (ARMcmov:i32 GPR:i32:$false, GPR:i32:$true, (imm:i32):$cc)
|
|
// Emits: (MOVCCr:i32 GPR:i32:$false, GPR:i32:$true, (imm:i32):$cc)
|
|
// Pattern complexity = 6 cost = 1 size = 0
|
|
//
|
|
// Pattern: (ARMcmov:i32 GPR:i32:$false, GPR:i32:$true, (imm:i32):$cc)
|
|
// Emits: (tMOVCCr:i32 GPR:i32:$false, GPR:i32:$true, (imm:i32):$cc)
|
|
// Pattern complexity = 6 cost = 11 size = 0
|
|
//
|
|
// Also FCPYScc and FCPYDcc.
|
|
AddToISelQueue(N0);
|
|
AddToISelQueue(N1);
|
|
AddToISelQueue(InFlag);
|
|
SDOperand Tmp2 = CurDAG->getTargetConstant(((unsigned)
|
|
cast<ConstantSDNode>(N2)->getValue()), MVT::i32);
|
|
SDOperand Ops[] = { N0, N1, Tmp2, N3, InFlag };
|
|
unsigned Opc = 0;
|
|
switch (VT.getSimpleVT()) {
|
|
default: assert(false && "Illegal conditional move type!");
|
|
break;
|
|
case MVT::i32:
|
|
Opc = isThumb ? ARM::tMOVCCr : ARM::MOVCCr;
|
|
break;
|
|
case MVT::f32:
|
|
Opc = ARM::FCPYScc;
|
|
break;
|
|
case MVT::f64:
|
|
Opc = ARM::FCPYDcc;
|
|
break;
|
|
}
|
|
return CurDAG->SelectNodeTo(Op.Val, Opc, VT, Ops, 5);
|
|
}
|
|
case ARMISD::CNEG: {
|
|
MVT VT = Op.getValueType();
|
|
SDOperand N0 = Op.getOperand(0);
|
|
SDOperand N1 = Op.getOperand(1);
|
|
SDOperand N2 = Op.getOperand(2);
|
|
SDOperand N3 = Op.getOperand(3);
|
|
SDOperand InFlag = Op.getOperand(4);
|
|
assert(N2.getOpcode() == ISD::Constant);
|
|
assert(N3.getOpcode() == ISD::Register);
|
|
|
|
AddToISelQueue(N0);
|
|
AddToISelQueue(N1);
|
|
AddToISelQueue(InFlag);
|
|
SDOperand Tmp2 = CurDAG->getTargetConstant(((unsigned)
|
|
cast<ConstantSDNode>(N2)->getValue()), MVT::i32);
|
|
SDOperand Ops[] = { N0, N1, Tmp2, N3, InFlag };
|
|
unsigned Opc = 0;
|
|
switch (VT.getSimpleVT()) {
|
|
default: assert(false && "Illegal conditional move type!");
|
|
break;
|
|
case MVT::f32:
|
|
Opc = ARM::FNEGScc;
|
|
break;
|
|
case MVT::f64:
|
|
Opc = ARM::FNEGDcc;
|
|
break;
|
|
}
|
|
return CurDAG->SelectNodeTo(Op.Val, Opc, VT, Ops, 5);
|
|
}
|
|
}
|
|
return SelectCode(Op);
|
|
}
|
|
|
|
/// createARMISelDag - This pass converts a legalized DAG into a
|
|
/// ARM-specific DAG, ready for instruction scheduling.
|
|
///
|
|
FunctionPass *llvm::createARMISelDag(ARMTargetMachine &TM) {
|
|
return new ARMDAGToDAGISel(TM);
|
|
}
|