diff options
author | Dan Gohman <djg@cray.com> | 2007-07-18 16:29:46 +0000 |
---|---|---|
committer | Dan Gohman <djg@cray.com> | 2007-07-18 16:29:46 +0000 |
commit | f17a25c88b892d30c2b41ba7ecdfbdfb2b4be9cc (patch) | |
tree | ebb79ea1ee5e3bc1fdf38541a811a8b804f0679a /lib/Target/ARM | |
download | external_llvm-f17a25c88b892d30c2b41ba7ecdfbdfb2b4be9cc.zip external_llvm-f17a25c88b892d30c2b41ba7ecdfbdfb2b4be9cc.tar.gz external_llvm-f17a25c88b892d30c2b41ba7ecdfbdfb2b4be9cc.tar.bz2 |
It's not necessary to do rounding for alloca operations when the requested
alignment is equal to the stack alignment.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@40004 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Target/ARM')
34 files changed, 13656 insertions, 0 deletions
diff --git a/lib/Target/ARM/ARM.h b/lib/Target/ARM/ARM.h new file mode 100644 index 0000000..8134dcc --- /dev/null +++ b/lib/Target/ARM/ARM.h @@ -0,0 +1,109 @@ +//===-- ARM.h - Top-level interface for ARM representation---- --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the "Instituto Nokia de Tecnologia" and +// is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the entry points for global functions defined in the LLVM +// ARM back-end. +// +//===----------------------------------------------------------------------===// + +#ifndef TARGET_ARM_H +#define TARGET_ARM_H + +#include <iosfwd> +#include <cassert> + +namespace llvm { + +class ARMTargetMachine; +class FunctionPass; +class MachineCodeEmitter; + +// Enums corresponding to ARM condition codes +namespace ARMCC { + enum CondCodes { + EQ, + NE, + HS, + LO, + MI, + PL, + VS, + VC, + HI, + LS, + GE, + LT, + GT, + LE, + AL + }; + + inline static CondCodes getOppositeCondition(CondCodes CC){ + switch (CC) { + default: assert(0 && "Unknown condition code"); + case EQ: return NE; + case NE: return EQ; + case HS: return LO; + case LO: return HS; + case MI: return PL; + case PL: return MI; + case VS: return VC; + case VC: return VS; + case HI: return LS; + case LS: return HI; + case GE: return LT; + case LT: return GE; + case GT: return LE; + case LE: return GT; + } + } +} + +inline static const char *ARMCondCodeToString(ARMCC::CondCodes CC) { + switch (CC) { + default: assert(0 && "Unknown condition code"); + case ARMCC::EQ: return "eq"; + case ARMCC::NE: return "ne"; + case ARMCC::HS: return "hs"; + case ARMCC::LO: return "lo"; + case ARMCC::MI: return "mi"; + case ARMCC::PL: return "pl"; + case ARMCC::VS: return "vs"; + case ARMCC::VC: return "vc"; + case ARMCC::HI: return "hi"; + case ARMCC::LS: return "ls"; + case ARMCC::GE: return "ge"; + case ARMCC::LT: return "lt"; + case ARMCC::GT: return "gt"; + case ARMCC::LE: return "le"; + case ARMCC::AL: return "al"; + } +} + +FunctionPass *createARMISelDag(ARMTargetMachine &TM); +FunctionPass *createARMCodePrinterPass(std::ostream &O, ARMTargetMachine &TM); +FunctionPass *createARMCodeEmitterPass(ARMTargetMachine &TM, + MachineCodeEmitter &MCE); +FunctionPass *createARMLoadStoreOptimizationPass(); +FunctionPass *createARMConstantIslandPass(); + +} // end namespace llvm; + +// Defines symbolic names for ARM registers. This defines a mapping from +// register name to register number. +// +#include "ARMGenRegisterNames.inc" + +// Defines symbolic names for the ARM instructions. +// +#include "ARMGenInstrNames.inc" + + +#endif diff --git a/lib/Target/ARM/ARM.td b/lib/Target/ARM/ARM.td new file mode 100644 index 0000000..0272004 --- /dev/null +++ b/lib/Target/ARM/ARM.td @@ -0,0 +1,119 @@ +//===- ARM.td - Describe the ARM Target Machine -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the "Instituto Nokia de Tecnologia" and +// is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Target-independent interfaces which we are implementing +//===----------------------------------------------------------------------===// + +include "../Target.td" + +//===----------------------------------------------------------------------===// +// ARM Subtarget features. +// + +def ArchV4T : SubtargetFeature<"v4t", "ARMArchVersion", "V4T", + "ARM v4T">; +def ArchV5T : SubtargetFeature<"v5t", "ARMArchVersion", "V5T", + "ARM v5T">; +def ArchV5TE : SubtargetFeature<"v5te", "ARMArchVersion", "V5TE", + "ARM v5TE, v5TEj, v5TExp">; +def ArchV6 : SubtargetFeature<"v6", "ARMArchVersion", "V6", + "ARM v6">; +def FeatureVFP2 : SubtargetFeature<"vfp2", "HasVFP2", "true", + "Enable VFP2 instructions ">; + +//===----------------------------------------------------------------------===// +// ARM Processors supported. +// + +class Proc<string Name, list<SubtargetFeature> Features> + : Processor<Name, NoItineraries, Features>; + +// V4 Processors. +def : Proc<"generic", []>; +def : Proc<"arm8", []>; +def : Proc<"arm810", []>; +def : Proc<"strongarm", []>; +def : Proc<"strongarm110", []>; +def : Proc<"strongarm1100", []>; +def : Proc<"strongarm1110", []>; + +// V4T Processors. +def : Proc<"arm7tdmi", [ArchV4T]>; +def : Proc<"arm7tdmi-s", [ArchV4T]>; +def : Proc<"arm710t", [ArchV4T]>; +def : Proc<"arm720t", [ArchV4T]>; +def : Proc<"arm9", [ArchV4T]>; +def : Proc<"arm9tdmi", [ArchV4T]>; +def : Proc<"arm920", [ArchV4T]>; +def : Proc<"arm920t", [ArchV4T]>; +def : Proc<"arm922t", [ArchV4T]>; +def : Proc<"arm940t", [ArchV4T]>; +def : Proc<"ep9312", [ArchV4T]>; + +// V5T Processors. +def : Proc<"arm10tdmi", [ArchV5T]>; +def : Proc<"arm1020t", [ArchV5T]>; + +// V5TE Processors. +def : Proc<"arm9e", [ArchV5TE]>; +def : Proc<"arm926ej-s", [ArchV5TE]>; +def : Proc<"arm946e-s", [ArchV5TE]>; +def : Proc<"arm966e-s", [ArchV5TE]>; +def : Proc<"arm968e-s", [ArchV5TE]>; +def : Proc<"arm10e", [ArchV5TE]>; +def : Proc<"arm1020e", [ArchV5TE]>; +def : Proc<"arm1022e", [ArchV5TE]>; +def : Proc<"xscale", [ArchV5TE]>; +def : Proc<"iwmmxt", [ArchV5TE]>; + +// V6 Processors. +def : Proc<"arm1136j-s", [ArchV6]>; +def : Proc<"arm1136jf-s", [ArchV6, FeatureVFP2]>; +def : Proc<"arm1176jz-s", [ArchV6]>; +def : Proc<"arm1176jzf-s", [ArchV6, FeatureVFP2]>; +def : Proc<"mpcorenovfp", [ArchV6]>; +def : Proc<"mpcore", [ArchV6, FeatureVFP2]>; + +//===----------------------------------------------------------------------===// +// Register File Description +//===----------------------------------------------------------------------===// + +include "ARMRegisterInfo.td" + +//===----------------------------------------------------------------------===// +// Instruction Descriptions +//===----------------------------------------------------------------------===// + +include "ARMInstrInfo.td" + +def ARMInstrInfo : InstrInfo { + // Define how we want to layout our target-specific information field. + let TSFlagsFields = ["AddrModeBits", + "SizeFlag", + "IndexModeBits", + "Opcode"]; + let TSFlagsShifts = [0, + 4, + 7, + 9]; +} + +//===----------------------------------------------------------------------===// +// Declare the target which we are implementing +//===----------------------------------------------------------------------===// + +def ARM : Target { + // Pull in Instruction Info: + let InstructionSet = ARMInstrInfo; +} diff --git a/lib/Target/ARM/ARMAddressingModes.h b/lib/Target/ARM/ARMAddressingModes.h new file mode 100644 index 0000000..3f47a69 --- /dev/null +++ b/lib/Target/ARM/ARMAddressingModes.h @@ -0,0 +1,394 @@ +//===- ARMAddressingModes.h - ARM Addressing Modes --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Chris Lattner and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the ARM addressing mode implementation stuff. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TARGET_ARM_ARMADDRESSINGMODES_H +#define LLVM_TARGET_ARM_ARMADDRESSINGMODES_H + +#include "llvm/CodeGen/SelectionDAGNodes.h" +#include "llvm/Support/MathExtras.h" +#include <cassert> + +namespace llvm { + +/// ARM_AM - ARM Addressing Mode Stuff +namespace ARM_AM { + enum ShiftOpc { + no_shift = 0, + asr, + lsl, + lsr, + ror, + rrx + }; + + enum AddrOpc { + add = '+', sub = '-' + }; + + static inline const char *getShiftOpcStr(ShiftOpc Op) { + switch (Op) { + default: assert(0 && "Unknown shift opc!"); + case ARM_AM::asr: return "asr"; + case ARM_AM::lsl: return "lsl"; + case ARM_AM::lsr: return "lsr"; + case ARM_AM::ror: return "ror"; + case ARM_AM::rrx: return "rrx"; + } + } + + static inline ShiftOpc getShiftOpcForNode(SDOperand N) { + switch (N.getOpcode()) { + default: return ARM_AM::no_shift; + case ISD::SHL: return ARM_AM::lsl; + case ISD::SRL: return ARM_AM::lsr; + case ISD::SRA: return ARM_AM::asr; + case ISD::ROTR: return ARM_AM::ror; + //case ISD::ROTL: // Only if imm -> turn into ROTR. + // Can't handle RRX here, because it would require folding a flag into + // the addressing mode. :( This causes us to miss certain things. + //case ARMISD::RRX: return ARM_AM::rrx; + } + } + + enum AMSubMode { + bad_am_submode = 0, + ia, + ib, + da, + db + }; + + static inline const char *getAMSubModeStr(AMSubMode Mode) { + switch (Mode) { + default: assert(0 && "Unknown addressing sub-mode!"); + case ARM_AM::ia: return "ia"; + case ARM_AM::ib: return "ib"; + case ARM_AM::da: return "da"; + case ARM_AM::db: return "db"; + } + } + + static inline const char *getAMSubModeAltStr(AMSubMode Mode, bool isLD) { + switch (Mode) { + default: assert(0 && "Unknown addressing sub-mode!"); + case ARM_AM::ia: return isLD ? "fd" : "ea"; + case ARM_AM::ib: return isLD ? "ed" : "fa"; + case ARM_AM::da: return isLD ? "fa" : "ed"; + case ARM_AM::db: return isLD ? "ea" : "fd"; + } + } + + /// rotr32 - Rotate a 32-bit unsigned value right by a specified # bits. + /// + static inline unsigned rotr32(unsigned Val, unsigned Amt) { + assert(Amt < 32 && "Invalid rotate amount"); + return (Val >> Amt) | (Val << ((32-Amt)&31)); + } + + /// rotl32 - Rotate a 32-bit unsigned value left by a specified # bits. + /// + static inline unsigned rotl32(unsigned Val, unsigned Amt) { + assert(Amt < 32 && "Invalid rotate amount"); + return (Val << Amt) | (Val >> ((32-Amt)&31)); + } + + //===--------------------------------------------------------------------===// + // Addressing Mode #1: shift_operand with registers + //===--------------------------------------------------------------------===// + // + // This 'addressing mode' is used for arithmetic instructions. It can + // represent things like: + // reg + // reg [asr|lsl|lsr|ror|rrx] reg + // reg [asr|lsl|lsr|ror|rrx] imm + // + // This is stored three operands [rega, regb, opc]. The first is the base + // reg, the second is the shift amount (or reg0 if not present or imm). The + // third operand encodes the shift opcode and the imm if a reg isn't present. + // + static inline unsigned getSORegOpc(ShiftOpc ShOp, unsigned Imm) { + return ShOp | (Imm << 3); + } + static inline unsigned getSORegOffset(unsigned Op) { + return Op >> 3; + } + static inline ShiftOpc getSORegShOp(unsigned Op) { + return (ShiftOpc)(Op & 7); + } + + /// getSOImmValImm - Given an encoded imm field for the reg/imm form, return + /// the 8-bit imm value. + static inline unsigned getSOImmValImm(unsigned Imm) { + return Imm & 0xFF; + } + /// getSOImmValRotate - Given an encoded imm field for the reg/imm form, return + /// the rotate amount. + static inline unsigned getSOImmValRot(unsigned Imm) { + return (Imm >> 8) * 2; + } + + /// getSOImmValRotate - Try to handle Imm with an immediate shifter operand, + /// computing the rotate amount to use. If this immediate value cannot be + /// handled with a single shifter-op, determine a good rotate amount that will + /// take a maximal chunk of bits out of the immediate. + static inline unsigned getSOImmValRotate(unsigned Imm) { + // 8-bit (or less) immediates are trivially shifter_operands with a rotate + // of zero. + if ((Imm & ~255U) == 0) return 0; + + // Use CTZ to compute the rotate amount. + unsigned TZ = CountTrailingZeros_32(Imm); + + // Rotate amount must be even. Something like 0x200 must be rotated 8 bits, + // not 9. + unsigned RotAmt = TZ & ~1; + + // If we can handle this spread, return it. + if ((rotr32(Imm, RotAmt) & ~255U) == 0) + return (32-RotAmt)&31; // HW rotates right, not left. + + // For values like 0xF000000F, we should skip the first run of ones, then + // retry the hunt. + if (Imm & 1) { + unsigned TrailingOnes = CountTrailingZeros_32(~Imm); + if (TrailingOnes != 32) { // Avoid overflow on 0xFFFFFFFF + // Restart the search for a high-order bit after the initial seconds of + // ones. + unsigned TZ2 = CountTrailingZeros_32(Imm & ~((1 << TrailingOnes)-1)); + + // Rotate amount must be even. + unsigned RotAmt2 = TZ2 & ~1; + + // If this fits, use it. + if (RotAmt2 != 32 && (rotr32(Imm, RotAmt2) & ~255U) == 0) + return (32-RotAmt2)&31; // HW rotates right, not left. + } + } + + // Otherwise, we have no way to cover this span of bits with a single + // shifter_op immediate. Return a chunk of bits that will be useful to + // handle. + return (32-RotAmt)&31; // HW rotates right, not left. + } + + /// getSOImmVal - Given a 32-bit immediate, if it is something that can fit + /// into an shifter_operand immediate operand, return the 12-bit encoding for + /// it. If not, return -1. + static inline int getSOImmVal(unsigned Arg) { + // 8-bit (or less) immediates are trivially shifter_operands with a rotate + // of zero. + if ((Arg & ~255U) == 0) return Arg; + + unsigned RotAmt = getSOImmValRotate(Arg); + + // If this cannot be handled with a single shifter_op, bail out. + if (rotr32(~255U, RotAmt) & Arg) + return -1; + + // Encode this correctly. + return rotl32(Arg, RotAmt) | ((RotAmt>>1) << 8); + } + + /// isSOImmTwoPartVal - Return true if the specified value can be obtained by + /// or'ing together two SOImmVal's. + static inline bool isSOImmTwoPartVal(unsigned V) { + // If this can be handled with a single shifter_op, bail out. + V = rotr32(~255U, getSOImmValRotate(V)) & V; + if (V == 0) + return false; + + // If this can be handled with two shifter_op's, accept. + V = rotr32(~255U, getSOImmValRotate(V)) & V; + return V == 0; + } + + /// getSOImmTwoPartFirst - If V is a value that satisfies isSOImmTwoPartVal, + /// return the first chunk of it. + static inline unsigned getSOImmTwoPartFirst(unsigned V) { + return rotr32(255U, getSOImmValRotate(V)) & V; + } + + /// getSOImmTwoPartSecond - If V is a value that satisfies isSOImmTwoPartVal, + /// return the second chunk of it. + static inline unsigned getSOImmTwoPartSecond(unsigned V) { + // Mask out the first hunk. + V = rotr32(~255U, getSOImmValRotate(V)) & V; + + // Take what's left. + assert(V == (rotr32(255U, getSOImmValRotate(V)) & V)); + return V; + } + + /// getThumbImmValShift - Try to handle Imm with a 8-bit immediate followed + /// by a left shift. Returns the shift amount to use. + static inline unsigned getThumbImmValShift(unsigned Imm) { + // 8-bit (or less) immediates are trivially immediate operand with a shift + // of zero. + if ((Imm & ~255U) == 0) return 0; + + // Use CTZ to compute the shift amount. + return CountTrailingZeros_32(Imm); + } + + /// isThumbImmShiftedVal - Return true if the specified value can be obtained + /// by left shifting a 8-bit immediate. + static inline bool isThumbImmShiftedVal(unsigned V) { + // If this can be handled with + V = (~255U << getThumbImmValShift(V)) & V; + return V == 0; + } + + /// getThumbImmNonShiftedVal - If V is a value that satisfies + /// isThumbImmShiftedVal, return the non-shiftd value. + static inline unsigned getThumbImmNonShiftedVal(unsigned V) { + return V >> getThumbImmValShift(V); + } + + //===--------------------------------------------------------------------===// + // Addressing Mode #2 + //===--------------------------------------------------------------------===// + // + // This is used for most simple load/store instructions. + // + // addrmode2 := reg +/- reg shop imm + // addrmode2 := reg +/- imm12 + // + // The first operand is always a Reg. The second operand is a reg if in + // reg/reg form, otherwise it's reg#0. The third field encodes the operation + // in bit 12, the immediate in bits 0-11, and the shift op in 13-15. + // + // If this addressing mode is a frame index (before prolog/epilog insertion + // and code rewriting), this operand will have the form: FI#, reg0, <offs> + // with no shift amount for the frame offset. + // + static inline unsigned getAM2Opc(AddrOpc Opc, unsigned Imm12, ShiftOpc SO) { + assert(Imm12 < (1 << 12) && "Imm too large!"); + bool isSub = Opc == sub; + return Imm12 | ((int)isSub << 12) | (SO << 13); + } + static inline unsigned getAM2Offset(unsigned AM2Opc) { + return AM2Opc & ((1 << 12)-1); + } + static inline AddrOpc getAM2Op(unsigned AM2Opc) { + return ((AM2Opc >> 12) & 1) ? sub : add; + } + static inline ShiftOpc getAM2ShiftOpc(unsigned AM2Opc) { + return (ShiftOpc)(AM2Opc >> 13); + } + + + //===--------------------------------------------------------------------===// + // Addressing Mode #3 + //===--------------------------------------------------------------------===// + // + // This is used for sign-extending loads, and load/store-pair instructions. + // + // addrmode3 := reg +/- reg + // addrmode3 := reg +/- imm8 + // + // The first operand is always a Reg. The second operand is a reg if in + // reg/reg form, otherwise it's reg#0. The third field encodes the operation + // in bit 8, the immediate in bits 0-7. + + /// getAM3Opc - This function encodes the addrmode3 opc field. + static inline unsigned getAM3Opc(AddrOpc Opc, unsigned char Offset) { + bool isSub = Opc == sub; + return ((int)isSub << 8) | Offset; + } + static inline unsigned char getAM3Offset(unsigned AM3Opc) { + return AM3Opc & 0xFF; + } + static inline AddrOpc getAM3Op(unsigned AM3Opc) { + return ((AM3Opc >> 8) & 1) ? sub : add; + } + + //===--------------------------------------------------------------------===// + // Addressing Mode #4 + //===--------------------------------------------------------------------===// + // + // This is used for load / store multiple instructions. + // + // addrmode4 := reg, <mode> + // + // The four modes are: + // IA - Increment after + // IB - Increment before + // DA - Decrement after + // DB - Decrement before + // + // If the 4th bit (writeback)is set, then the base register is updated after + // the memory transfer. + + static inline AMSubMode getAM4SubMode(unsigned Mode) { + return (AMSubMode)(Mode & 0x7); + } + + static inline unsigned getAM4ModeImm(AMSubMode SubMode, bool WB = false) { + return (int)SubMode | ((int)WB << 3); + } + + static inline bool getAM4WBFlag(unsigned Mode) { + return (Mode >> 3) & 1; + } + + //===--------------------------------------------------------------------===// + // Addressing Mode #5 + //===--------------------------------------------------------------------===// + // + // This is used for coprocessor instructions, such as FP load/stores. + // + // addrmode5 := reg +/- imm8*4 + // + // The first operand is always a Reg. The third field encodes the operation + // in bit 8, the immediate in bits 0-7. + // + // This can also be used for FP load/store multiple ops. The third field encodes + // writeback mode in bit 8, the number of registers (or 2 times the number of + // registers for DPR ops) in bits 0-7. In addition, bit 9-11 encodes one of the + // following two sub-modes: + // + // IA - Increment after + // DB - Decrement before + + /// getAM5Opc - This function encodes the addrmode5 opc field. + static inline unsigned getAM5Opc(AddrOpc Opc, unsigned char Offset) { + bool isSub = Opc == sub; + return ((int)isSub << 8) | Offset; + } + static inline unsigned char getAM5Offset(unsigned AM5Opc) { + return AM5Opc & 0xFF; + } + static inline AddrOpc getAM5Op(unsigned AM5Opc) { + return ((AM5Opc >> 8) & 1) ? sub : add; + } + + /// getAM5Opc - This function encodes the addrmode5 opc field for FLDM and + /// FSTM instructions. + static inline unsigned getAM5Opc(AMSubMode SubMode, bool WB, + unsigned char Offset) { + assert((SubMode == ia || SubMode == db) && + "Illegal addressing mode 5 sub-mode!"); + return ((int)SubMode << 9) | ((int)WB << 8) | Offset; + } + static inline AMSubMode getAM5SubMode(unsigned AM5Opc) { + return (AMSubMode)((AM5Opc >> 9) & 0x7); + } + static inline bool getAM5WBFlag(unsigned AM5Opc) { + return ((AM5Opc >> 8) & 1); + } + +} // end namespace ARM_AM +} // end namespace llvm + +#endif + diff --git a/lib/Target/ARM/ARMAsmPrinter.cpp b/lib/Target/ARM/ARMAsmPrinter.cpp new file mode 100644 index 0000000..5e65226 --- /dev/null +++ b/lib/Target/ARM/ARMAsmPrinter.cpp @@ -0,0 +1,1029 @@ +//===-- ARMAsmPrinter.cpp - ARM LLVM assembly writer ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the "Instituto Nokia de Tecnologia" and +// is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains a printer that converts from our internal representation +// of machine-dependent LLVM code to GAS-format ARM assembly language. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "asm-printer" +#include "ARM.h" +#include "ARMTargetMachine.h" +#include "ARMAddressingModes.h" +#include "ARMConstantPoolValue.h" +#include "ARMMachineFunctionInfo.h" +#include "llvm/Constants.h" +#include "llvm/Module.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/DwarfWriter.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" +#include "llvm/Target/TargetAsmInfo.h" +#include "llvm/Target/TargetData.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Mangler.h" +#include "llvm/Support/MathExtras.h" +#include <cctype> +using namespace llvm; + +STATISTIC(EmittedInsts, "Number of machine instrs printed"); + +namespace { + struct VISIBILITY_HIDDEN ARMAsmPrinter : public AsmPrinter { + ARMAsmPrinter(std::ostream &O, TargetMachine &TM, const TargetAsmInfo *T) + : AsmPrinter(O, TM, T), DW(O, this, T), AFI(NULL), InCPMode(false) { + Subtarget = &TM.getSubtarget<ARMSubtarget>(); + } + + DwarfWriter DW; + + /// Subtarget - Keep a pointer to the ARMSubtarget around so that we can + /// make the right decision when printing asm code for different targets. + const ARMSubtarget *Subtarget; + + /// AFI - Keep a pointer to ARMFunctionInfo for the current + /// MachineFunction + ARMFunctionInfo *AFI; + + /// We name each basic block in a Function with a unique number, so + /// that we can consistently refer to them later. This is cleared + /// at the beginning of each call to runOnMachineFunction(). + /// + typedef std::map<const Value *, unsigned> ValueMapTy; + ValueMapTy NumberForBB; + + /// Keeps the set of GlobalValues that require non-lazy-pointers for + /// indirect access. + std::set<std::string> GVNonLazyPtrs; + + /// Keeps the set of external function GlobalAddresses that the asm + /// printer should generate stubs for. + std::set<std::string> FnStubs; + + /// True if asm printer is printing a series of CONSTPOOL_ENTRY. + bool InCPMode; + + virtual const char *getPassName() const { + return "ARM Assembly Printer"; + } + + void printOperand(const MachineInstr *MI, int opNum, + const char *Modifier = 0); + void printSOImmOperand(const MachineInstr *MI, int opNum); + void printSOImm2PartOperand(const MachineInstr *MI, int opNum); + void printSORegOperand(const MachineInstr *MI, int opNum); + void printAddrMode2Operand(const MachineInstr *MI, int OpNo); + void printAddrMode2OffsetOperand(const MachineInstr *MI, int OpNo); + void printAddrMode3Operand(const MachineInstr *MI, int OpNo); + void printAddrMode3OffsetOperand(const MachineInstr *MI, int OpNo); + void printAddrMode4Operand(const MachineInstr *MI, int OpNo, + const char *Modifier = 0); + void printAddrMode5Operand(const MachineInstr *MI, int OpNo, + const char *Modifier = 0); + void printAddrModePCOperand(const MachineInstr *MI, int OpNo, + const char *Modifier = 0); + void printThumbAddrModeRROperand(const MachineInstr *MI, int OpNo); + void printThumbAddrModeRI5Operand(const MachineInstr *MI, int OpNo, + unsigned Scale); + void printThumbAddrModeS1Operand(const MachineInstr *MI, int OpNo); + void printThumbAddrModeS2Operand(const MachineInstr *MI, int OpNo); + void printThumbAddrModeS4Operand(const MachineInstr *MI, int OpNo); + void printThumbAddrModeSPOperand(const MachineInstr *MI, int OpNo); + void printPredicateOperand(const MachineInstr *MI, int opNum); + void printSBitModifierOperand(const MachineInstr *MI, int opNum); + void printPCLabel(const MachineInstr *MI, int opNum); + void printRegisterList(const MachineInstr *MI, int opNum); + void printCPInstOperand(const MachineInstr *MI, int opNum, + const char *Modifier); + void printJTBlockOperand(const MachineInstr *MI, int opNum); + + virtual bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, + unsigned AsmVariant, const char *ExtraCode); + + bool printInstruction(const MachineInstr *MI); // autogenerated. + void printMachineInstruction(const MachineInstr *MI); + bool runOnMachineFunction(MachineFunction &F); + bool doInitialization(Module &M); + bool doFinalization(Module &M); + + virtual void EmitMachineConstantPoolValue(MachineConstantPoolValue *MCPV) { + printDataDirective(MCPV->getType()); + + ARMConstantPoolValue *ACPV = (ARMConstantPoolValue*)MCPV; + GlobalValue *GV = ACPV->getGV(); + std::string Name = GV ? Mang->getValueName(GV) : TAI->getGlobalPrefix(); + if (!GV) + Name += ACPV->getSymbol(); + if (ACPV->isNonLazyPointer()) { + GVNonLazyPtrs.insert(Name); + O << TAI->getPrivateGlobalPrefix() << Name << "$non_lazy_ptr"; + } else if (ACPV->isStub()) { + FnStubs.insert(Name); + O << TAI->getPrivateGlobalPrefix() << Name << "$stub"; + } else + O << Name; + if (ACPV->hasModifier()) O << "(" << ACPV->getModifier() << ")"; + if (ACPV->getPCAdjustment() != 0) { + O << "-(" << TAI->getPrivateGlobalPrefix() << "PC" + << utostr(ACPV->getLabelId()) + << "+" << (unsigned)ACPV->getPCAdjustment(); + if (ACPV->mustAddCurrentAddress()) + O << "-."; + O << ")"; + } + O << "\n"; + + // If the constant pool value is a extern weak symbol, remember to emit + // the weak reference. + if (GV && GV->hasExternalWeakLinkage()) + ExtWeakSymbols.insert(GV); + } + + void getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); + AU.addRequired<MachineModuleInfo>(); + } + }; +} // end of anonymous namespace + +#include "ARMGenAsmWriter.inc" + +/// createARMCodePrinterPass - Returns a pass that prints the ARM +/// assembly code for a MachineFunction to the given output stream, +/// using the given target machine description. This should work +/// regardless of whether the function is in SSA form. +/// +FunctionPass *llvm::createARMCodePrinterPass(std::ostream &o, + ARMTargetMachine &tm) { + return new ARMAsmPrinter(o, tm, tm.getTargetAsmInfo()); +} + +/// runOnMachineFunction - This uses the printInstruction() +/// method to print assembly for each instruction. +/// +bool ARMAsmPrinter::runOnMachineFunction(MachineFunction &MF) { + AFI = MF.getInfo<ARMFunctionInfo>(); + + DW.SetModuleInfo(&getAnalysis<MachineModuleInfo>()); + + SetupMachineFunction(MF); + O << "\n"; + + // NOTE: we don't print out constant pools here, they are handled as + // instructions. + + O << "\n"; + // Print out labels for the function. + const Function *F = MF.getFunction(); + switch (F->getLinkage()) { + default: assert(0 && "Unknown linkage type!"); + case Function::InternalLinkage: + SwitchToTextSection("\t.text", F); + break; + case Function::ExternalLinkage: + SwitchToTextSection("\t.text", F); + O << "\t.globl\t" << CurrentFnName << "\n"; + break; + case Function::WeakLinkage: + case Function::LinkOnceLinkage: + if (Subtarget->isTargetDarwin()) { + SwitchToTextSection( + ".section __TEXT,__textcoal_nt,coalesced,pure_instructions", F); + O << "\t.globl\t" << CurrentFnName << "\n"; + O << "\t.weak_definition\t" << CurrentFnName << "\n"; + } else { + O << TAI->getWeakRefDirective() << CurrentFnName << "\n"; + } + break; + } + + const char *VisibilityDirective = NULL; + if (F->hasHiddenVisibility()) + VisibilityDirective = TAI->getHiddenDirective(); + else if (F->hasProtectedVisibility()) + VisibilityDirective = TAI->getProtectedDirective(); + + if (VisibilityDirective) + O << VisibilityDirective << CurrentFnName << "\n"; + + if (AFI->isThumbFunction()) { + EmitAlignment(1, F, AFI->getAlign()); + O << "\t.code\t16\n"; + O << "\t.thumb_func"; + if (Subtarget->isTargetDarwin()) + O << "\t" << CurrentFnName; + O << "\n"; + InCPMode = false; + } else + EmitAlignment(2, F); + + O << CurrentFnName << ":\n"; + // Emit pre-function debug information. + DW.BeginFunction(&MF); + + // Print out code for the function. + for (MachineFunction::const_iterator I = MF.begin(), E = MF.end(); + I != E; ++I) { + // Print a label for the basic block. + if (I != MF.begin()) { + printBasicBlockLabel(I, true); + O << '\n'; + } + for (MachineBasicBlock::const_iterator II = I->begin(), E = I->end(); + II != E; ++II) { + // Print the assembly for the instruction. + printMachineInstruction(II); + } + } + + if (TAI->hasDotTypeDotSizeDirective()) + O << "\t.size " << CurrentFnName << ", .-" << CurrentFnName << "\n"; + + // Emit post-function debug information. + DW.EndFunction(); + + return false; +} + +void ARMAsmPrinter::printOperand(const MachineInstr *MI, int opNum, + const char *Modifier) { + const MachineOperand &MO = MI->getOperand(opNum); + switch (MO.getType()) { + case MachineOperand::MO_Register: + if (MRegisterInfo::isPhysicalRegister(MO.getReg())) + O << TM.getRegisterInfo()->get(MO.getReg()).Name; + else + assert(0 && "not implemented"); + break; + case MachineOperand::MO_Immediate: { + if (!Modifier || strcmp(Modifier, "no_hash") != 0) + O << "#"; + + O << (int)MO.getImmedValue(); + break; + } + case MachineOperand::MO_MachineBasicBlock: + printBasicBlockLabel(MO.getMachineBasicBlock()); + return; + case MachineOperand::MO_GlobalAddress: { + bool isCallOp = Modifier && !strcmp(Modifier, "call"); + GlobalValue *GV = MO.getGlobal(); + std::string Name = Mang->getValueName(GV); + bool isExt = (GV->isDeclaration() || GV->hasWeakLinkage() || + GV->hasLinkOnceLinkage()); + if (isExt && isCallOp && Subtarget->isTargetDarwin() && + TM.getRelocationModel() != Reloc::Static) { + O << TAI->getPrivateGlobalPrefix() << Name << "$stub"; + FnStubs.insert(Name); + } else + O << Name; + + if (MO.getOffset() > 0) + O << '+' << MO.getOffset(); + else if (MO.getOffset() < 0) + O << MO.getOffset(); + + if (isCallOp && Subtarget->isTargetELF() && + TM.getRelocationModel() == Reloc::PIC_) + O << "(PLT)"; + if (GV->hasExternalWeakLinkage()) + ExtWeakSymbols.insert(GV); + break; + } + case MachineOperand::MO_ExternalSymbol: { + bool isCallOp = Modifier && !strcmp(Modifier, "call"); + std::string Name(TAI->getGlobalPrefix()); + Name += MO.getSymbolName(); + if (isCallOp && Subtarget->isTargetDarwin() && + TM.getRelocationModel() != Reloc::Static) { + O << TAI->getPrivateGlobalPrefix() << Name << "$stub"; + FnStubs.insert(Name); + } else + O << Name; + if (isCallOp && Subtarget->isTargetELF() && + TM.getRelocationModel() == Reloc::PIC_) + O << "(PLT)"; + break; + } + case MachineOperand::MO_ConstantPoolIndex: + O << TAI->getPrivateGlobalPrefix() << "CPI" << getFunctionNumber() + << '_' << MO.getConstantPoolIndex(); + break; + case MachineOperand::MO_JumpTableIndex: + O << TAI->getPrivateGlobalPrefix() << "JTI" << getFunctionNumber() + << '_' << MO.getJumpTableIndex(); + break; + default: + O << "<unknown operand type>"; abort (); break; + } +} + +static void printSOImm(std::ostream &O, int64_t V, const TargetAsmInfo *TAI) { + assert(V < (1 << 12) && "Not a valid so_imm value!"); + unsigned Imm = ARM_AM::getSOImmValImm(V); + unsigned Rot = ARM_AM::getSOImmValRot(V); + + // Print low-level immediate formation info, per + // A5.1.3: "Data-processing operands - Immediate". + if (Rot) { + O << "#" << Imm << ", " << Rot; + // Pretty printed version. + O << ' ' << TAI->getCommentString() << ' ' << (int)ARM_AM::rotr32(Imm, Rot); + } else { + O << "#" << Imm; + } +} + +/// printSOImmOperand - SOImm is 4-bit rotate amount in bits 8-11 with 8-bit +/// immediate in bits 0-7. +void ARMAsmPrinter::printSOImmOperand(const MachineInstr *MI, int OpNum) { + const MachineOperand &MO = MI->getOperand(OpNum); + assert(MO.isImmediate() && "Not a valid so_imm value!"); + printSOImm(O, MO.getImmedValue(), TAI); +} + +/// printSOImm2PartOperand - SOImm is broken into two pieces using a mov +/// followed by a or to materialize. +void ARMAsmPrinter::printSOImm2PartOperand(const MachineInstr *MI, int OpNum) { + const MachineOperand &MO = MI->getOperand(OpNum); + assert(MO.isImmediate() && "Not a valid so_imm value!"); + unsigned V1 = ARM_AM::getSOImmTwoPartFirst(MO.getImmedValue()); + unsigned V2 = ARM_AM::getSOImmTwoPartSecond(MO.getImmedValue()); + printSOImm(O, ARM_AM::getSOImmVal(V1), TAI); + O << "\n\torr"; + printPredicateOperand(MI, 2); + O << " "; + printOperand(MI, 0); + O << ", "; + printOperand(MI, 0); + O << ", "; + printSOImm(O, ARM_AM::getSOImmVal(V2), TAI); +} + +// so_reg is a 4-operand unit corresponding to register forms of the A5.1 +// "Addressing Mode 1 - Data-processing operands" forms. This includes: +// REG 0 0 - e.g. R5 +// REG REG 0,SH_OPC - e.g. R5, ROR R3 +// REG 0 IMM,SH_OPC - e.g. R5, LSL #3 +void ARMAsmPrinter::printSORegOperand(const MachineInstr *MI, int Op) { + const MachineOperand &MO1 = MI->getOperand(Op); + const MachineOperand &MO2 = MI->getOperand(Op+1); + const MachineOperand &MO3 = MI->getOperand(Op+2); + + assert(MRegisterInfo::isPhysicalRegister(MO1.getReg())); + O << TM.getRegisterInfo()->get(MO1.getReg()).Name; + + // Print the shift opc. + O << ", " + << ARM_AM::getShiftOpcStr(ARM_AM::getSORegShOp(MO3.getImmedValue())) + << " "; + + if (MO2.getReg()) { + assert(MRegisterInfo::isPhysicalRegister(MO2.getReg())); + O << TM.getRegisterInfo()->get(MO2.getReg()).Name; + assert(ARM_AM::getSORegOffset(MO3.getImm()) == 0); + } else { + O << "#" << ARM_AM::getSORegOffset(MO3.getImm()); + } +} + +void ARMAsmPrinter::printAddrMode2Operand(const MachineInstr *MI, int Op) { + const MachineOperand &MO1 = MI->getOperand(Op); + const MachineOperand &MO2 = MI->getOperand(Op+1); + const MachineOperand &MO3 = MI->getOperand(Op+2); + + if (!MO1.isRegister()) { // FIXME: This is for CP entries, but isn't right. + printOperand(MI, Op); + return; + } + + O << "[" << TM.getRegisterInfo()->get(MO1.getReg()).Name; + + if (!MO2.getReg()) { + if (ARM_AM::getAM2Offset(MO3.getImm())) // Don't print +0. + O << ", #" + << (char)ARM_AM::getAM2Op(MO3.getImm()) + << ARM_AM::getAM2Offset(MO3.getImm()); + O << "]"; + return; + } + + O << ", " + << (char)ARM_AM::getAM2Op(MO3.getImm()) + << TM.getRegisterInfo()->get(MO2.getReg()).Name; + + if (unsigned ShImm = ARM_AM::getAM2Offset(MO3.getImm())) + O << ", " + << ARM_AM::getShiftOpcStr(ARM_AM::getAM2ShiftOpc(MO3.getImmedValue())) + << " #" << ShImm; + O << "]"; +} + +void ARMAsmPrinter::printAddrMode2OffsetOperand(const MachineInstr *MI, int Op){ + const MachineOperand &MO1 = MI->getOperand(Op); + const MachineOperand &MO2 = MI->getOperand(Op+1); + + if (!MO1.getReg()) { + unsigned ImmOffs = ARM_AM::getAM2Offset(MO2.getImm()); + assert(ImmOffs && "Malformed indexed load / store!"); + O << "#" + << (char)ARM_AM::getAM2Op(MO2.getImm()) + << ImmOffs; + return; + } + + O << (char)ARM_AM::getAM2Op(MO2.getImm()) + << TM.getRegisterInfo()->get(MO1.getReg()).Name; + + if (unsigned ShImm = ARM_AM::getAM2Offset(MO2.getImm())) + O << ", " + << ARM_AM::getShiftOpcStr(ARM_AM::getAM2ShiftOpc(MO2.getImmedValue())) + << " #" << ShImm; +} + +void ARMAsmPrinter::printAddrMode3Operand(const MachineInstr *MI, int Op) { + const MachineOperand &MO1 = MI->getOperand(Op); + const MachineOperand &MO2 = MI->getOperand(Op+1); + const MachineOperand &MO3 = MI->getOperand(Op+2); + + assert(MRegisterInfo::isPhysicalRegister(MO1.getReg())); + O << "[" << TM.getRegisterInfo()->get(MO1.getReg()).Name; + + if (MO2.getReg()) { + O << ", " + << (char)ARM_AM::getAM3Op(MO3.getImm()) + << TM.getRegisterInfo()->get(MO2.getReg()).Name + << "]"; + return; + } + + if (unsigned ImmOffs = ARM_AM::getAM3Offset(MO3.getImm())) + O << ", #" + << (char)ARM_AM::getAM3Op(MO3.getImm()) + << ImmOffs; + O << "]"; +} + +void ARMAsmPrinter::printAddrMode3OffsetOperand(const MachineInstr *MI, int Op){ + const MachineOperand &MO1 = MI->getOperand(Op); + const MachineOperand &MO2 = MI->getOperand(Op+1); + + if (MO1.getReg()) { + O << (char)ARM_AM::getAM3Op(MO2.getImm()) + << TM.getRegisterInfo()->get(MO1.getReg()).Name; + return; + } + + unsigned ImmOffs = ARM_AM::getAM3Offset(MO2.getImm()); + assert(ImmOffs && "Malformed indexed load / store!"); + O << "#" + << (char)ARM_AM::getAM3Op(MO2.getImm()) + << ImmOffs; +} + +void ARMAsmPrinter::printAddrMode4Operand(const MachineInstr *MI, int Op, + const char *Modifier) { + const MachineOperand &MO1 = MI->getOperand(Op); + const MachineOperand &MO2 = MI->getOperand(Op+1); + ARM_AM::AMSubMode Mode = ARM_AM::getAM4SubMode(MO2.getImm()); + if (Modifier && strcmp(Modifier, "submode") == 0) { + if (MO1.getReg() == ARM::SP) { + bool isLDM = (MI->getOpcode() == ARM::LDM || + MI->getOpcode() == ARM::LDM_RET); + O << ARM_AM::getAMSubModeAltStr(Mode, isLDM); + } else + O << ARM_AM::getAMSubModeStr(Mode); + } else { + printOperand(MI, Op); + if (ARM_AM::getAM4WBFlag(MO2.getImm())) + O << "!"; + } +} + +void ARMAsmPrinter::printAddrMode5Operand(const MachineInstr *MI, int Op, + const char *Modifier) { + const MachineOperand &MO1 = MI->getOperand(Op); + const MachineOperand &MO2 = MI->getOperand(Op+1); + + if (!MO1.isRegister()) { // FIXME: This is for CP entries, but isn't right. + printOperand(MI, Op); + return; + } + + assert(MRegisterInfo::isPhysicalRegister(MO1.getReg())); + + if (Modifier && strcmp(Modifier, "submode") == 0) { + ARM_AM::AMSubMode Mode = ARM_AM::getAM5SubMode(MO2.getImm()); + if (MO1.getReg() == ARM::SP) { + bool isFLDM = (MI->getOpcode() == ARM::FLDMD || + MI->getOpcode() == ARM::FLDMS); + O << ARM_AM::getAMSubModeAltStr(Mode, isFLDM); + } else + O << ARM_AM::getAMSubModeStr(Mode); + return; + } else if (Modifier && strcmp(Modifier, "base") == 0) { + // Used for FSTM{D|S} and LSTM{D|S} operations. + O << TM.getRegisterInfo()->get(MO1.getReg()).Name; + if (ARM_AM::getAM5WBFlag(MO2.getImm())) + O << "!"; + return; + } + + O << "[" << TM.getRegisterInfo()->get(MO1.getReg()).Name; + + if (unsigned ImmOffs = ARM_AM::getAM5Offset(MO2.getImm())) { + O << ", #" + << (char)ARM_AM::getAM5Op(MO2.getImm()) + << ImmOffs*4; + } + O << "]"; +} + +void ARMAsmPrinter::printAddrModePCOperand(const MachineInstr *MI, int Op, + const char *Modifier) { + if (Modifier && strcmp(Modifier, "label") == 0) { + printPCLabel(MI, Op+1); + return; + } + + const MachineOperand &MO1 = MI->getOperand(Op); + assert(MRegisterInfo::isPhysicalRegister(MO1.getReg())); + O << "[pc, +" << TM.getRegisterInfo()->get(MO1.getReg()).Name << "]"; +} + +void +ARMAsmPrinter::printThumbAddrModeRROperand(const MachineInstr *MI, int Op) { + const MachineOperand &MO1 = MI->getOperand(Op); + const MachineOperand &MO2 = MI->getOperand(Op+1); + O << "[" << TM.getRegisterInfo()->get(MO1.getReg()).Name; + O << ", " << TM.getRegisterInfo()->get(MO2.getReg()).Name << "]"; +} + +void +ARMAsmPrinter::printThumbAddrModeRI5Operand(const MachineInstr *MI, int Op, + unsigned Scale) { + const MachineOperand &MO1 = MI->getOperand(Op); + const MachineOperand &MO2 = MI->getOperand(Op+1); + const MachineOperand &MO3 = MI->getOperand(Op+2); + + if (!MO1.isRegister()) { // FIXME: This is for CP entries, but isn't right. + printOperand(MI, Op); + return; + } + + O << "[" << TM.getRegisterInfo()->get(MO1.getReg()).Name; + if (MO3.getReg()) + O << ", " << TM.getRegisterInfo()->get(MO3.getReg()).Name; + else if (unsigned ImmOffs = MO2.getImm()) { + O << ", #" << ImmOffs; + if (Scale > 1) + O << " * " << Scale; + } + O << "]"; +} + +void +ARMAsmPrinter::printThumbAddrModeS1Operand(const MachineInstr *MI, int Op) { + printThumbAddrModeRI5Operand(MI, Op, 1); +} +void +ARMAsmPrinter::printThumbAddrModeS2Operand(const MachineInstr *MI, int Op) { + printThumbAddrModeRI5Operand(MI, Op, 2); +} +void +ARMAsmPrinter::printThumbAddrModeS4Operand(const MachineInstr *MI, int Op) { + printThumbAddrModeRI5Operand(MI, Op, 4); +} + +void ARMAsmPrinter::printThumbAddrModeSPOperand(const MachineInstr *MI,int Op) { + const MachineOperand &MO1 = MI->getOperand(Op); + const MachineOperand &MO2 = MI->getOperand(Op+1); + O << "[" << TM.getRegisterInfo()->get(MO1.getReg()).Name; + if (unsigned ImmOffs = MO2.getImm()) + O << ", #" << ImmOffs << " * 4"; + O << "]"; +} + +void ARMAsmPrinter::printPredicateOperand(const MachineInstr *MI, int opNum) { + ARMCC::CondCodes CC = (ARMCC::CondCodes)MI->getOperand(opNum).getImmedValue(); + if (CC != ARMCC::AL) + O << ARMCondCodeToString(CC); +} + +void ARMAsmPrinter::printSBitModifierOperand(const MachineInstr *MI, int opNum){ + unsigned Reg = MI->getOperand(opNum).getReg(); + if (Reg) { + assert(Reg == ARM::CPSR && "Expect ARM CPSR register!"); + O << 's'; + } +} + +void ARMAsmPrinter::printPCLabel(const MachineInstr *MI, int opNum) { + int Id = (int)MI->getOperand(opNum).getImmedValue(); + O << TAI->getPrivateGlobalPrefix() << "PC" << Id; +} + +void ARMAsmPrinter::printRegisterList(const MachineInstr *MI, int opNum) { + O << "{"; + for (unsigned i = opNum, e = MI->getNumOperands(); i != e; ++i) { + printOperand(MI, i); + if (i != e-1) O << ", "; + } + O << "}"; +} + +void ARMAsmPrinter::printCPInstOperand(const MachineInstr *MI, int OpNo, + const char *Modifier) { + assert(Modifier && "This operand only works with a modifier!"); + // There are two aspects to a CONSTANTPOOL_ENTRY operand, the label and the + // data itself. + if (!strcmp(Modifier, "label")) { + unsigned ID = MI->getOperand(OpNo).getImm(); + O << TAI->getPrivateGlobalPrefix() << "CPI" << getFunctionNumber() + << '_' << ID << ":\n"; + } else { + assert(!strcmp(Modifier, "cpentry") && "Unknown modifier for CPE"); + unsigned CPI = MI->getOperand(OpNo).getConstantPoolIndex(); + + const MachineConstantPoolEntry &MCPE = // Chasing pointers is fun? + MI->getParent()->getParent()->getConstantPool()->getConstants()[CPI]; + + if (MCPE.isMachineConstantPoolEntry()) + EmitMachineConstantPoolValue(MCPE.Val.MachineCPVal); + else { + EmitGlobalConstant(MCPE.Val.ConstVal); + // remember to emit the weak reference + if (const GlobalValue *GV = dyn_cast<GlobalValue>(MCPE.Val.ConstVal)) + if (GV->hasExternalWeakLinkage()) + ExtWeakSymbols.insert(GV); + } + } +} + +void ARMAsmPrinter::printJTBlockOperand(const MachineInstr *MI, int OpNo) { + const MachineOperand &MO1 = MI->getOperand(OpNo); + const MachineOperand &MO2 = MI->getOperand(OpNo+1); // Unique Id + unsigned JTI = MO1.getJumpTableIndex(); + O << TAI->getPrivateGlobalPrefix() << "JTI" << getFunctionNumber() + << '_' << JTI << '_' << MO2.getImmedValue() << ":\n"; + + const char *JTEntryDirective = TAI->getJumpTableDirective(); + if (!JTEntryDirective) + JTEntryDirective = TAI->getData32bitsDirective(); + + const MachineFunction *MF = MI->getParent()->getParent(); + MachineJumpTableInfo *MJTI = MF->getJumpTableInfo(); + const std::vector<MachineJumpTableEntry> &JT = MJTI->getJumpTables(); + const std::vector<MachineBasicBlock*> &JTBBs = JT[JTI].MBBs; + bool UseSet= TAI->getSetDirective() && TM.getRelocationModel() == Reloc::PIC_; + std::set<MachineBasicBlock*> JTSets; + for (unsigned i = 0, e = JTBBs.size(); i != e; ++i) { + MachineBasicBlock *MBB = JTBBs[i]; + if (UseSet && JTSets.insert(MBB).second) + printSetLabel(JTI, MO2.getImmedValue(), MBB); + + O << JTEntryDirective << ' '; + if (UseSet) + O << TAI->getPrivateGlobalPrefix() << getFunctionNumber() + << '_' << JTI << '_' << MO2.getImmedValue() + << "_set_" << MBB->getNumber(); + else if (TM.getRelocationModel() == Reloc::PIC_) { + printBasicBlockLabel(MBB, false, false); + // If the arch uses custom Jump Table directives, don't calc relative to JT + if (!TAI->getJumpTableDirective()) + O << '-' << TAI->getPrivateGlobalPrefix() << "JTI" + << getFunctionNumber() << '_' << JTI << '_' << MO2.getImmedValue(); + } else + printBasicBlockLabel(MBB, false, false); + if (i != e-1) + O << '\n'; + } +} + + +bool ARMAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, + unsigned AsmVariant, const char *ExtraCode){ + // Does this asm operand have a single letter operand modifier? + if (ExtraCode && ExtraCode[0]) { + if (ExtraCode[1] != 0) return true; // Unknown modifier. + + switch (ExtraCode[0]) { + default: return true; // Unknown modifier. + case 'c': // Don't print "$" before a global var name or constant. + case 'P': // Print a VFP double precision register. + printOperand(MI, OpNo); + return false; + case 'Q': + if (TM.getTargetData()->isLittleEndian()) + break; + // Fallthrough + case 'R': + if (TM.getTargetData()->isBigEndian()) + break; + // Fallthrough + case 'H': // Write second word of DI / DF reference. + // Verify that this operand has two consecutive registers. + if (!MI->getOperand(OpNo).isRegister() || + OpNo+1 == MI->getNumOperands() || + !MI->getOperand(OpNo+1).isRegister()) + return true; + ++OpNo; // Return the high-part. + } + } + + printOperand(MI, OpNo); + return false; +} + +void ARMAsmPrinter::printMachineInstruction(const MachineInstr *MI) { + ++EmittedInsts; + + int Opc = MI->getOpcode(); + switch (Opc) { + case ARM::CONSTPOOL_ENTRY: + if (!InCPMode && AFI->isThumbFunction()) { + EmitAlignment(2); + InCPMode = true; + } + break; + default: { + if (InCPMode && AFI->isThumbFunction()) + InCPMode = false; + switch (Opc) { + case ARM::PICADD: + case ARM::PICLD: + case ARM::PICLDZH: + case ARM::PICLDZB: + case ARM::PICLDH: + case ARM::PICLDB: + case ARM::PICLDSH: + case ARM::PICLDSB: + case ARM::PICSTR: + case ARM::PICSTRH: + case ARM::PICSTRB: + case ARM::tPICADD: + break; + default: + O << "\t"; + break; + } + }} + + // Call the autogenerated instruction printer routines. + printInstruction(MI); +} + +bool ARMAsmPrinter::doInitialization(Module &M) { + // Emit initial debug information. + DW.BeginModule(&M); + + AsmPrinter::doInitialization(M); + + // Darwin wants symbols to be quoted if they have complex names. + if (Subtarget->isTargetDarwin()) + Mang->setUseQuotes(true); + + return false; +} + +bool ARMAsmPrinter::doFinalization(Module &M) { + const TargetData *TD = TM.getTargetData(); + + for (Module::const_global_iterator I = M.global_begin(), E = M.global_end(); + I != E; ++I) { + if (!I->hasInitializer()) // External global require no code + continue; + + if (EmitSpecialLLVMGlobal(I)) { + if (Subtarget->isTargetDarwin() && + TM.getRelocationModel() == Reloc::Static) { + if (I->getName() == "llvm.global_ctors") + O << ".reference .constructors_used\n"; + else if (I->getName() == "llvm.global_dtors") + O << ".reference .destructors_used\n"; + } + continue; + } + + std::string name = Mang->getValueName(I); + Constant *C = I->getInitializer(); + const Type *Type = C->getType(); + unsigned Size = TD->getTypeSize(Type); + unsigned Align = TD->getPreferredAlignmentLog(I); + + const char *VisibilityDirective = NULL; + if (I->hasHiddenVisibility()) + VisibilityDirective = TAI->getHiddenDirective(); + else if (I->hasProtectedVisibility()) + VisibilityDirective = TAI->getProtectedDirective(); + + if (VisibilityDirective) + O << VisibilityDirective << name << "\n"; + + if (Subtarget->isTargetELF()) + O << "\t.type " << name << ",%object\n"; + + if (C->isNullValue()) { + if (I->hasExternalLinkage()) { + if (const char *Directive = TAI->getZeroFillDirective()) { + O << "\t.globl\t" << name << "\n"; + O << Directive << "__DATA__, __common, " << name << ", " + << Size << ", " << Align << "\n"; + continue; + } + } + + if (!I->hasSection() && + (I->hasInternalLinkage() || I->hasWeakLinkage() || + I->hasLinkOnceLinkage())) { + if (Size == 0) Size = 1; // .comm Foo, 0 is undefined, avoid it. + if (!NoZerosInBSS && TAI->getBSSSection()) + SwitchToDataSection(TAI->getBSSSection(), I); + else + SwitchToDataSection(TAI->getDataSection(), I); + if (TAI->getLCOMMDirective() != NULL) { + if (I->hasInternalLinkage()) { + O << TAI->getLCOMMDirective() << name << "," << Size; + if (Subtarget->isTargetDarwin()) + O << "," << Align; + } else + O << TAI->getCOMMDirective() << name << "," << Size; + } else { + if (I->hasInternalLinkage()) + O << "\t.local\t" << name << "\n"; + O << TAI->getCOMMDirective() << name << "," << Size; + if (TAI->getCOMMDirectiveTakesAlignment()) + O << "," << (TAI->getAlignmentIsInBytes() ? (1 << Align) : Align); + } + O << "\t\t" << TAI->getCommentString() << " " << I->getName() << "\n"; + continue; + } + } + + switch (I->getLinkage()) { + case GlobalValue::LinkOnceLinkage: + case GlobalValue::WeakLinkage: + if (Subtarget->isTargetDarwin()) { + O << "\t.globl " << name << "\n" + << "\t.weak_definition " << name << "\n"; + SwitchToDataSection("\t.section __DATA,__const_coal,coalesced", I); + } else { + std::string SectionName("\t.section\t.llvm.linkonce.d." + + name + + ",\"aw\",%progbits"); + SwitchToDataSection(SectionName.c_str(), I); + O << "\t.weak " << name << "\n"; + } + break; + case GlobalValue::AppendingLinkage: + // FIXME: appending linkage variables should go into a section of + // their name or something. For now, just emit them as external. + case GlobalValue::ExternalLinkage: + O << "\t.globl " << name << "\n"; + // FALL THROUGH + case GlobalValue::InternalLinkage: { + if (I->isConstant()) { + const ConstantArray *CVA = dyn_cast<ConstantArray>(C); + if (TAI->getCStringSection() && CVA && CVA->isCString()) { + SwitchToDataSection(TAI->getCStringSection(), I); + break; + } + } + // FIXME: special handling for ".ctors" & ".dtors" sections + if (I->hasSection() && + (I->getSection() == ".ctors" || + I->getSection() == ".dtors")) { + assert(!Subtarget->isTargetDarwin()); + std::string SectionName = ".section " + I->getSection(); + SectionName += ",\"aw\",%progbits"; + SwitchToDataSection(SectionName.c_str()); + } else { + if (C->isNullValue() && !NoZerosInBSS && TAI->getBSSSection()) + SwitchToDataSection(I->isThreadLocal() ? TAI->getTLSBSSSection() : + TAI->getBSSSection(), I); + else if (!I->isConstant()) + SwitchToDataSection(I->isThreadLocal() ? TAI->getTLSDataSection() : + TAI->getDataSection(), I); + else if (I->isThreadLocal()) + SwitchToDataSection(TAI->getTLSDataSection()); + else { + // Read-only data. + bool HasReloc = C->ContainsRelocations(); + if (HasReloc && + Subtarget->isTargetDarwin() && + TM.getRelocationModel() != Reloc::Static) + SwitchToDataSection("\t.const_data\n"); + else if (!HasReloc && Size == 4 && + TAI->getFourByteConstantSection()) + SwitchToDataSection(TAI->getFourByteConstantSection(), I); + else if (!HasReloc && Size == 8 && + TAI->getEightByteConstantSection()) + SwitchToDataSection(TAI->getEightByteConstantSection(), I); + else if (!HasReloc && Size == 16 && + TAI->getSixteenByteConstantSection()) + SwitchToDataSection(TAI->getSixteenByteConstantSection(), I); + else if (TAI->getReadOnlySection()) + SwitchToDataSection(TAI->getReadOnlySection(), I); + else + SwitchToDataSection(TAI->getDataSection(), I); + } + } + + break; + } + default: + assert(0 && "Unknown linkage type!"); + break; + } + + EmitAlignment(Align, I); + O << name << ":\t\t\t\t" << TAI->getCommentString() << " " << I->getName() + << "\n"; + if (TAI->hasDotTypeDotSizeDirective()) + O << "\t.size " << name << ", " << Size << "\n"; + // If the initializer is a extern weak symbol, remember to emit the weak + // reference! + if (const GlobalValue *GV = dyn_cast<GlobalValue>(C)) + if (GV->hasExternalWeakLinkage()) + ExtWeakSymbols.insert(GV); + + EmitGlobalConstant(C); + O << '\n'; + } + + if (Subtarget->isTargetDarwin()) { + SwitchToDataSection(""); + + // Output stubs for dynamically-linked functions + unsigned j = 1; + for (std::set<std::string>::iterator i = FnStubs.begin(), e = FnStubs.end(); + i != e; ++i, ++j) { + if (TM.getRelocationModel() == Reloc::PIC_) + SwitchToTextSection(".section __TEXT,__picsymbolstub4,symbol_stubs," + "none,16", 0); + else + SwitchToTextSection(".section __TEXT,__symbol_stub4,symbol_stubs," + "none,12", 0); + + EmitAlignment(2); + O << "\t.code\t32\n"; + + O << "L" << *i << "$stub:\n"; + O << "\t.indirect_symbol " << *i << "\n"; + O << "\tldr ip, L" << *i << "$slp\n"; + if (TM.getRelocationModel() == Reloc::PIC_) { + O << "L" << *i << "$scv:\n"; + O << "\tadd ip, pc, ip\n"; + } + O << "\tldr pc, [ip, #0]\n"; + O << "L" << *i << "$slp:\n"; + if (TM.getRelocationModel() == Reloc::PIC_) + O << "\t.long\tL" << *i << "$lazy_ptr-(L" << *i << "$scv+8)\n"; + else + O << "\t.long\tL" << *i << "$lazy_ptr\n"; + SwitchToDataSection(".lazy_symbol_pointer", 0); + O << "L" << *i << "$lazy_ptr:\n"; + O << "\t.indirect_symbol " << *i << "\n"; + O << "\t.long\tdyld_stub_binding_helper\n"; + } + O << "\n"; + + // Output non-lazy-pointers for external and common global variables. + if (GVNonLazyPtrs.begin() != GVNonLazyPtrs.end()) + SwitchToDataSection(".non_lazy_symbol_pointer", 0); + for (std::set<std::string>::iterator i = GVNonLazyPtrs.begin(), + e = GVNonLazyPtrs.end(); i != e; ++i) { + O << "L" << *i << "$non_lazy_ptr:\n"; + O << "\t.indirect_symbol " << *i << "\n"; + O << "\t.long\t0\n"; + } + + // Emit initial debug information. + DW.EndModule(); + + // Funny Darwin hack: This flag tells the linker that no global symbols + // contain code that falls through to other global symbols (e.g. the obvious + // implementation of multiple entry points). If this doesn't occur, the + // linker can safely perform dead code stripping. Since LLVM never + // generates code that does this, it is always safe to set. + O << "\t.subsections_via_symbols\n"; + } else { + // Emit final debug information for ELF. + DW.EndModule(); + } + + AsmPrinter::doFinalization(M); + return false; // success +} diff --git a/lib/Target/ARM/ARMCodeEmitter.cpp b/lib/Target/ARM/ARMCodeEmitter.cpp new file mode 100644 index 0000000..ed1d287 --- /dev/null +++ b/lib/Target/ARM/ARMCodeEmitter.cpp @@ -0,0 +1,92 @@ +//===-- ARM/ARMCodeEmitter.cpp - Convert ARM code to machine code ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the Raul Herbster and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the pass that transforms the ARM machine instructions into +// relocatable machine code. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "arm-emitter" +#include "ARMInstrInfo.h" +#include "ARMSubtarget.h" +#include "ARMTargetMachine.h" +#include "ARM.h" +#include "llvm/PassManager.h" +#include "llvm/CodeGen/MachineCodeEmitter.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/Function.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Support/Compiler.h" +using namespace llvm; + +STATISTIC(NumEmitted, "Number of machine instructions emitted"); + +namespace { + class VISIBILITY_HIDDEN Emitter : public MachineFunctionPass { + const ARMInstrInfo *II; + const TargetData *TD; + TargetMachine &TM; + MachineCodeEmitter &MCE; + public: + static char ID; + explicit Emitter(TargetMachine &tm, MachineCodeEmitter &mce) + : MachineFunctionPass((intptr_t)&ID), II(0), TD(0), TM(tm), + MCE(mce) {} + Emitter(TargetMachine &tm, MachineCodeEmitter &mce, + const ARMInstrInfo &ii, const TargetData &td) + : MachineFunctionPass((intptr_t)&ID), II(&ii), TD(&td), TM(tm), + MCE(mce) {} + + bool runOnMachineFunction(MachineFunction &MF); + + virtual const char *getPassName() const { + return "ARM Machine Code Emitter"; + } + + void emitInstruction(const MachineInstr &MI); + + private: + + }; + char Emitter::ID = 0; +} + +/// createARMCodeEmitterPass - Return a pass that emits the collected ARM code +/// to the specified MCE object. +FunctionPass *llvm::createARMCodeEmitterPass(ARMTargetMachine &TM, + MachineCodeEmitter &MCE) { + return new Emitter(TM, MCE); +} + +bool Emitter::runOnMachineFunction(MachineFunction &MF) { + assert((MF.getTarget().getRelocationModel() != Reloc::Default || + MF.getTarget().getRelocationModel() != Reloc::Static) && + "JIT relocation model must be set to static or default!"); + II = ((ARMTargetMachine&)MF.getTarget()).getInstrInfo(); + TD = ((ARMTargetMachine&)MF.getTarget()).getTargetData(); + + do { + MCE.startFunction(MF); + for (MachineFunction::iterator MBB = MF.begin(), E = MF.end(); + MBB != E; ++MBB) { + MCE.StartMachineBasicBlock(MBB); + for (MachineBasicBlock::const_iterator I = MBB->begin(), E = MBB->end(); + I != E; ++I) + emitInstruction(*I); + } + } while (MCE.finishFunction(MF)); + + return false; +} + +void Emitter::emitInstruction(const MachineInstr &MI) { + NumEmitted++; // Keep track of the # of mi's emitted +} diff --git a/lib/Target/ARM/ARMConstantIslandPass.cpp b/lib/Target/ARM/ARMConstantIslandPass.cpp new file mode 100644 index 0000000..1b93631 --- /dev/null +++ b/lib/Target/ARM/ARMConstantIslandPass.cpp @@ -0,0 +1,1277 @@ +//===-- ARMConstantIslandPass.cpp - ARM constant islands --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Chris Lattner and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains a pass that splits the constant pool up into 'islands' +// which are scattered through-out the function. This is required due to the +// limited pc-relative displacements that ARM has. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "arm-cp-islands" +#include "ARM.h" +#include "ARMMachineFunctionInfo.h" +#include "ARMInstrInfo.h" +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/Target/TargetData.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Statistic.h" +using namespace llvm; + +STATISTIC(NumCPEs, "Number of constpool entries"); +STATISTIC(NumSplit, "Number of uncond branches inserted"); +STATISTIC(NumCBrFixed, "Number of cond branches fixed"); +STATISTIC(NumUBrFixed, "Number of uncond branches fixed"); + +namespace { + /// ARMConstantIslands - Due to limited PC-relative displacements, ARM + /// requires constant pool entries to be scattered among the instructions + /// inside a function. To do this, it completely ignores the normal LLVM + /// constant pool; instead, it places constants wherever it feels like with + /// special instructions. + /// + /// The terminology used in this pass includes: + /// Islands - Clumps of constants placed in the function. + /// Water - Potential places where an island could be formed. + /// CPE - A constant pool entry that has been placed somewhere, which + /// tracks a list of users. + class VISIBILITY_HIDDEN ARMConstantIslands : public MachineFunctionPass { + /// NextUID - Assign unique ID's to CPE's. + unsigned NextUID; + + /// BBSizes - The size of each MachineBasicBlock in bytes of code, indexed + /// by MBB Number. The two-byte pads required for Thumb alignment are + /// counted as part of the following block (i.e., the offset and size for + /// a padded block will both be ==2 mod 4). + std::vector<unsigned> BBSizes; + + /// BBOffsets - the offset of each MBB in bytes, starting from 0. + /// The two-byte pads required for Thumb alignment are counted as part of + /// the following block. + std::vector<unsigned> BBOffsets; + + /// WaterList - A sorted list of basic blocks where islands could be placed + /// (i.e. blocks that don't fall through to the following block, due + /// to a return, unreachable, or unconditional branch). + std::vector<MachineBasicBlock*> WaterList; + + /// CPUser - One user of a constant pool, keeping the machine instruction + /// pointer, the constant pool being referenced, and the max displacement + /// allowed from the instruction to the CP. + struct CPUser { + MachineInstr *MI; + MachineInstr *CPEMI; + unsigned MaxDisp; + CPUser(MachineInstr *mi, MachineInstr *cpemi, unsigned maxdisp) + : MI(mi), CPEMI(cpemi), MaxDisp(maxdisp) {} + }; + + /// CPUsers - Keep track of all of the machine instructions that use various + /// constant pools and their max displacement. + std::vector<CPUser> CPUsers; + + /// CPEntry - One per constant pool entry, keeping the machine instruction + /// pointer, the constpool index, and the number of CPUser's which + /// reference this entry. + struct CPEntry { + MachineInstr *CPEMI; + unsigned CPI; + unsigned RefCount; + CPEntry(MachineInstr *cpemi, unsigned cpi, unsigned rc = 0) + : CPEMI(cpemi), CPI(cpi), RefCount(rc) {} + }; + + /// CPEntries - Keep track of all of the constant pool entry machine + /// instructions. For each original constpool index (i.e. those that + /// existed upon entry to this pass), it keeps a vector of entries. + /// Original elements are cloned as we go along; the clones are + /// put in the vector of the original element, but have distinct CPIs. + std::vector<std::vector<CPEntry> > CPEntries; + + /// ImmBranch - One per immediate branch, keeping the machine instruction + /// pointer, conditional or unconditional, the max displacement, + /// and (if isCond is true) the corresponding unconditional branch + /// opcode. + struct ImmBranch { + MachineInstr *MI; + unsigned MaxDisp : 31; + bool isCond : 1; + int UncondBr; + ImmBranch(MachineInstr *mi, unsigned maxdisp, bool cond, int ubr) + : MI(mi), MaxDisp(maxdisp), isCond(cond), UncondBr(ubr) {} + }; + + /// ImmBranches - Keep track of all the immediate branch instructions. + /// + std::vector<ImmBranch> ImmBranches; + + /// PushPopMIs - Keep track of all the Thumb push / pop instructions. + /// + SmallVector<MachineInstr*, 4> PushPopMIs; + + /// HasFarJump - True if any far jump instruction has been emitted during + /// the branch fix up pass. + bool HasFarJump; + + const TargetInstrInfo *TII; + ARMFunctionInfo *AFI; + bool isThumb; + public: + static char ID; + ARMConstantIslands() : MachineFunctionPass((intptr_t)&ID) {} + + virtual bool runOnMachineFunction(MachineFunction &Fn); + + virtual const char *getPassName() const { + return "ARM constant island placement and branch shortening pass"; + } + + private: + void DoInitialPlacement(MachineFunction &Fn, + std::vector<MachineInstr*> &CPEMIs); + CPEntry *findConstPoolEntry(unsigned CPI, const MachineInstr *CPEMI); + void InitialFunctionScan(MachineFunction &Fn, + const std::vector<MachineInstr*> &CPEMIs); + MachineBasicBlock *SplitBlockBeforeInstr(MachineInstr *MI); + void UpdateForInsertedWaterBlock(MachineBasicBlock *NewBB); + void AdjustBBOffsetsAfter(MachineBasicBlock *BB, int delta); + bool DecrementOldEntry(unsigned CPI, MachineInstr* CPEMI); + int LookForExistingCPEntry(CPUser& U, unsigned UserOffset); + bool LookForWater(CPUser&U, unsigned UserOffset, + MachineBasicBlock** NewMBB); + MachineBasicBlock* AcceptWater(MachineBasicBlock *WaterBB, + std::vector<MachineBasicBlock*>::iterator IP); + void CreateNewWater(unsigned CPUserIndex, unsigned UserOffset, + MachineBasicBlock** NewMBB); + bool HandleConstantPoolUser(MachineFunction &Fn, unsigned CPUserIndex); + void RemoveDeadCPEMI(MachineInstr *CPEMI); + bool RemoveUnusedCPEntries(); + bool CPEIsInRange(MachineInstr *MI, unsigned UserOffset, + MachineInstr *CPEMI, unsigned Disp, + bool DoDump); + bool WaterIsInRange(unsigned UserOffset, MachineBasicBlock *Water, + CPUser &U); + bool OffsetIsInRange(unsigned UserOffset, unsigned TrialOffset, + unsigned Disp, bool NegativeOK); + bool BBIsInRange(MachineInstr *MI, MachineBasicBlock *BB, unsigned Disp); + bool FixUpImmediateBr(MachineFunction &Fn, ImmBranch &Br); + bool FixUpConditionalBr(MachineFunction &Fn, ImmBranch &Br); + bool FixUpUnconditionalBr(MachineFunction &Fn, ImmBranch &Br); + bool UndoLRSpillRestore(); + + unsigned GetOffsetOf(MachineInstr *MI) const; + void dumpBBs(); + void verify(MachineFunction &Fn); + }; + char ARMConstantIslands::ID = 0; +} + +/// verify - check BBOffsets, BBSizes, alignment of islands +void ARMConstantIslands::verify(MachineFunction &Fn) { + assert(BBOffsets.size() == BBSizes.size()); + for (unsigned i = 1, e = BBOffsets.size(); i != e; ++i) + assert(BBOffsets[i-1]+BBSizes[i-1] == BBOffsets[i]); + if (isThumb) { + for (MachineFunction::iterator MBBI = Fn.begin(), E = Fn.end(); + MBBI != E; ++MBBI) { + MachineBasicBlock *MBB = MBBI; + if (!MBB->empty() && + MBB->begin()->getOpcode() == ARM::CONSTPOOL_ENTRY) + assert((BBOffsets[MBB->getNumber()]%4 == 0 && + BBSizes[MBB->getNumber()]%4 == 0) || + (BBOffsets[MBB->getNumber()]%4 != 0 && + BBSizes[MBB->getNumber()]%4 != 0)); + } + } +} + +/// print block size and offset information - debugging +void ARMConstantIslands::dumpBBs() { + for (unsigned J = 0, E = BBOffsets.size(); J !=E; ++J) { + DOUT << "block " << J << " offset " << BBOffsets[J] << + " size " << BBSizes[J] << "\n"; + } +} + +/// createARMConstantIslandPass - returns an instance of the constpool +/// island pass. +FunctionPass *llvm::createARMConstantIslandPass() { + return new ARMConstantIslands(); +} + +bool ARMConstantIslands::runOnMachineFunction(MachineFunction &Fn) { + MachineConstantPool &MCP = *Fn.getConstantPool(); + + TII = Fn.getTarget().getInstrInfo(); + AFI = Fn.getInfo<ARMFunctionInfo>(); + isThumb = AFI->isThumbFunction(); + + HasFarJump = false; + + // Renumber all of the machine basic blocks in the function, guaranteeing that + // the numbers agree with the position of the block in the function. + Fn.RenumberBlocks(); + + /// Thumb functions containing constant pools get 2-byte alignment. This is so + /// we can keep exact track of where the alignment padding goes. Set default. + AFI->setAlign(isThumb ? 1U : 2U); + + // Perform the initial placement of the constant pool entries. To start with, + // we put them all at the end of the function. + std::vector<MachineInstr*> CPEMIs; + if (!MCP.isEmpty()) { + DoInitialPlacement(Fn, CPEMIs); + if (isThumb) + AFI->setAlign(2U); + } + + /// The next UID to take is the first unused one. + NextUID = CPEMIs.size(); + + // Do the initial scan of the function, building up information about the + // sizes of each block, the location of all the water, and finding all of the + // constant pool users. + InitialFunctionScan(Fn, CPEMIs); + CPEMIs.clear(); + + /// Remove dead constant pool entries. + RemoveUnusedCPEntries(); + + // Iteratively place constant pool entries and fix up branches until there + // is no change. + bool MadeChange = false; + while (true) { + bool Change = false; + for (unsigned i = 0, e = CPUsers.size(); i != e; ++i) + Change |= HandleConstantPoolUser(Fn, i); + DEBUG(dumpBBs()); + for (unsigned i = 0, e = ImmBranches.size(); i != e; ++i) + Change |= FixUpImmediateBr(Fn, ImmBranches[i]); + DEBUG(dumpBBs()); + if (!Change) + break; + MadeChange = true; + } + + // After a while, this might be made debug-only, but it is not expensive. + verify(Fn); + + // If LR has been forced spilled and no far jumps (i.e. BL) has been issued. + // Undo the spill / restore of LR if possible. + if (!HasFarJump && AFI->isLRSpilledForFarJump() && isThumb) + MadeChange |= UndoLRSpillRestore(); + + BBSizes.clear(); + BBOffsets.clear(); + WaterList.clear(); + CPUsers.clear(); + CPEntries.clear(); + ImmBranches.clear(); + PushPopMIs.clear(); + + return MadeChange; +} + +/// DoInitialPlacement - Perform the initial placement of the constant pool +/// entries. To start with, we put them all at the end of the function. +void ARMConstantIslands::DoInitialPlacement(MachineFunction &Fn, + std::vector<MachineInstr*> &CPEMIs){ + // Create the basic block to hold the CPE's. + MachineBasicBlock *BB = new MachineBasicBlock(); + Fn.getBasicBlockList().push_back(BB); + + // Add all of the constants from the constant pool to the end block, use an + // identity mapping of CPI's to CPE's. + const std::vector<MachineConstantPoolEntry> &CPs = + Fn.getConstantPool()->getConstants(); + + const TargetData &TD = *Fn.getTarget().getTargetData(); + for (unsigned i = 0, e = CPs.size(); i != e; ++i) { + unsigned Size = TD.getTypeSize(CPs[i].getType()); + // Verify that all constant pool entries are a multiple of 4 bytes. If not, + // we would have to pad them out or something so that instructions stay + // aligned. + assert((Size & 3) == 0 && "CP Entry not multiple of 4 bytes!"); + MachineInstr *CPEMI = + BuildMI(BB, TII->get(ARM::CONSTPOOL_ENTRY)) + .addImm(i).addConstantPoolIndex(i).addImm(Size); + CPEMIs.push_back(CPEMI); + + // Add a new CPEntry, but no corresponding CPUser yet. + std::vector<CPEntry> CPEs; + CPEs.push_back(CPEntry(CPEMI, i)); + CPEntries.push_back(CPEs); + NumCPEs++; + DOUT << "Moved CPI#" << i << " to end of function as #" << i << "\n"; + } +} + +/// BBHasFallthrough - Return true if the specified basic block can fallthrough +/// into the block immediately after it. +static bool BBHasFallthrough(MachineBasicBlock *MBB) { + // Get the next machine basic block in the function. + MachineFunction::iterator MBBI = MBB; + if (next(MBBI) == MBB->getParent()->end()) // Can't fall off end of function. + return false; + + MachineBasicBlock *NextBB = next(MBBI); + for (MachineBasicBlock::succ_iterator I = MBB->succ_begin(), + E = MBB->succ_end(); I != E; ++I) + if (*I == NextBB) + return true; + + return false; +} + +/// findConstPoolEntry - Given the constpool index and CONSTPOOL_ENTRY MI, +/// look up the corresponding CPEntry. +ARMConstantIslands::CPEntry +*ARMConstantIslands::findConstPoolEntry(unsigned CPI, + const MachineInstr *CPEMI) { + std::vector<CPEntry> &CPEs = CPEntries[CPI]; + // Number of entries per constpool index should be small, just do a + // linear search. + for (unsigned i = 0, e = CPEs.size(); i != e; ++i) { + if (CPEs[i].CPEMI == CPEMI) + return &CPEs[i]; + } + return NULL; +} + +/// InitialFunctionScan - Do the initial scan of the function, building up +/// information about the sizes of each block, the location of all the water, +/// and finding all of the constant pool users. +void ARMConstantIslands::InitialFunctionScan(MachineFunction &Fn, + const std::vector<MachineInstr*> &CPEMIs) { + unsigned Offset = 0; + for (MachineFunction::iterator MBBI = Fn.begin(), E = Fn.end(); + MBBI != E; ++MBBI) { + MachineBasicBlock &MBB = *MBBI; + + // If this block doesn't fall through into the next MBB, then this is + // 'water' that a constant pool island could be placed. + if (!BBHasFallthrough(&MBB)) + WaterList.push_back(&MBB); + + unsigned MBBSize = 0; + for (MachineBasicBlock::iterator I = MBB.begin(), E = MBB.end(); + I != E; ++I) { + // Add instruction size to MBBSize. + MBBSize += ARM::GetInstSize(I); + + int Opc = I->getOpcode(); + if (TII->isBranch(Opc)) { + bool isCond = false; + unsigned Bits = 0; + unsigned Scale = 1; + int UOpc = Opc; + switch (Opc) { + case ARM::tBR_JTr: + // A Thumb table jump may involve padding; for the offsets to + // be right, functions containing these must be 4-byte aligned. + AFI->setAlign(2U); + if ((Offset+MBBSize)%4 != 0) + MBBSize += 2; // padding + continue; // Does not get an entry in ImmBranches + default: + continue; // Ignore other JT branches + case ARM::Bcc: + isCond = true; + UOpc = ARM::B; + // Fallthrough + case ARM::B: + Bits = 24; + Scale = 4; + break; + case ARM::tBcc: + isCond = true; + UOpc = ARM::tB; + Bits = 8; + Scale = 2; + break; + case ARM::tB: + Bits = 11; + Scale = 2; + break; + } + + // Record this immediate branch. + unsigned MaxOffs = ((1 << (Bits-1))-1) * Scale; + ImmBranches.push_back(ImmBranch(I, MaxOffs, isCond, UOpc)); + } + + if (Opc == ARM::tPUSH || Opc == ARM::tPOP_RET) + PushPopMIs.push_back(I); + + // Scan the instructions for constant pool operands. + for (unsigned op = 0, e = I->getNumOperands(); op != e; ++op) + if (I->getOperand(op).isConstantPoolIndex()) { + // We found one. The addressing mode tells us the max displacement + // from the PC that this instruction permits. + + // Basic size info comes from the TSFlags field. + unsigned Bits = 0; + unsigned Scale = 1; + unsigned TSFlags = I->getInstrDescriptor()->TSFlags; + switch (TSFlags & ARMII::AddrModeMask) { + default: + // Constant pool entries can reach anything. + if (I->getOpcode() == ARM::CONSTPOOL_ENTRY) + continue; + if (I->getOpcode() == ARM::tLEApcrel) { + Bits = 8; // Taking the address of a CP entry. + break; + } + assert(0 && "Unknown addressing mode for CP reference!"); + case ARMII::AddrMode1: // AM1: 8 bits << 2 + Bits = 8; + Scale = 4; // Taking the address of a CP entry. + break; + case ARMII::AddrMode2: + Bits = 12; // +-offset_12 + break; + case ARMII::AddrMode3: + Bits = 8; // +-offset_8 + break; + // addrmode4 has no immediate offset. + case ARMII::AddrMode5: + Bits = 8; + Scale = 4; // +-(offset_8*4) + break; + case ARMII::AddrModeT1: + Bits = 5; // +offset_5 + break; + case ARMII::AddrModeT2: + Bits = 5; + Scale = 2; // +(offset_5*2) + break; + case ARMII::AddrModeT4: + Bits = 5; + Scale = 4; // +(offset_5*4) + break; + case ARMII::AddrModeTs: + Bits = 8; + Scale = 4; // +(offset_8*4) + break; + } + + // Remember that this is a user of a CP entry. + unsigned CPI = I->getOperand(op).getConstantPoolIndex(); + MachineInstr *CPEMI = CPEMIs[CPI]; + unsigned MaxOffs = ((1 << Bits)-1) * Scale; + CPUsers.push_back(CPUser(I, CPEMI, MaxOffs)); + + // Increment corresponding CPEntry reference count. + CPEntry *CPE = findConstPoolEntry(CPI, CPEMI); + assert(CPE && "Cannot find a corresponding CPEntry!"); + CPE->RefCount++; + + // Instructions can only use one CP entry, don't bother scanning the + // rest of the operands. + break; + } + } + + // In thumb mode, if this block is a constpool island, we may need padding + // so it's aligned on 4 byte boundary. + if (isThumb && + !MBB.empty() && + MBB.begin()->getOpcode() == ARM::CONSTPOOL_ENTRY && + (Offset%4) != 0) + MBBSize += 2; + + BBSizes.push_back(MBBSize); + BBOffsets.push_back(Offset); + Offset += MBBSize; + } +} + +/// GetOffsetOf - Return the current offset of the specified machine instruction +/// from the start of the function. This offset changes as stuff is moved +/// around inside the function. +unsigned ARMConstantIslands::GetOffsetOf(MachineInstr *MI) const { + MachineBasicBlock *MBB = MI->getParent(); + + // The offset is composed of two things: the sum of the sizes of all MBB's + // before this instruction's block, and the offset from the start of the block + // it is in. + unsigned Offset = BBOffsets[MBB->getNumber()]; + + // If we're looking for a CONSTPOOL_ENTRY in Thumb, see if this block has + // alignment padding, and compensate if so. + if (isThumb && + MI->getOpcode() == ARM::CONSTPOOL_ENTRY && + Offset%4 != 0) + Offset += 2; + + // Sum instructions before MI in MBB. + for (MachineBasicBlock::iterator I = MBB->begin(); ; ++I) { + assert(I != MBB->end() && "Didn't find MI in its own basic block?"); + if (&*I == MI) return Offset; + Offset += ARM::GetInstSize(I); + } +} + +/// CompareMBBNumbers - Little predicate function to sort the WaterList by MBB +/// ID. +static bool CompareMBBNumbers(const MachineBasicBlock *LHS, + const MachineBasicBlock *RHS) { + return LHS->getNumber() < RHS->getNumber(); +} + +/// UpdateForInsertedWaterBlock - When a block is newly inserted into the +/// machine function, it upsets all of the block numbers. Renumber the blocks +/// and update the arrays that parallel this numbering. +void ARMConstantIslands::UpdateForInsertedWaterBlock(MachineBasicBlock *NewBB) { + // Renumber the MBB's to keep them consequtive. + NewBB->getParent()->RenumberBlocks(NewBB); + + // Insert a size into BBSizes to align it properly with the (newly + // renumbered) block numbers. + BBSizes.insert(BBSizes.begin()+NewBB->getNumber(), 0); + + // Likewise for BBOffsets. + BBOffsets.insert(BBOffsets.begin()+NewBB->getNumber(), 0); + + // Next, update WaterList. Specifically, we need to add NewMBB as having + // available water after it. + std::vector<MachineBasicBlock*>::iterator IP = + std::lower_bound(WaterList.begin(), WaterList.end(), NewBB, + CompareMBBNumbers); + WaterList.insert(IP, NewBB); +} + + +/// Split the basic block containing MI into two blocks, which are joined by +/// an unconditional branch. Update datastructures and renumber blocks to +/// account for this change and returns the newly created block. +MachineBasicBlock *ARMConstantIslands::SplitBlockBeforeInstr(MachineInstr *MI) { + MachineBasicBlock *OrigBB = MI->getParent(); + + // Create a new MBB for the code after the OrigBB. + MachineBasicBlock *NewBB = new MachineBasicBlock(OrigBB->getBasicBlock()); + MachineFunction::iterator MBBI = OrigBB; ++MBBI; + OrigBB->getParent()->getBasicBlockList().insert(MBBI, NewBB); + + // Splice the instructions starting with MI over to NewBB. + NewBB->splice(NewBB->end(), OrigBB, MI, OrigBB->end()); + + // Add an unconditional branch from OrigBB to NewBB. + // Note the new unconditional branch is not being recorded. + BuildMI(OrigBB, TII->get(isThumb ? ARM::tB : ARM::B)).addMBB(NewBB); + NumSplit++; + + // Update the CFG. All succs of OrigBB are now succs of NewBB. + while (!OrigBB->succ_empty()) { + MachineBasicBlock *Succ = *OrigBB->succ_begin(); + OrigBB->removeSuccessor(Succ); + NewBB->addSuccessor(Succ); + + // This pass should be run after register allocation, so there should be no + // PHI nodes to update. + assert((Succ->empty() || Succ->begin()->getOpcode() != TargetInstrInfo::PHI) + && "PHI nodes should be eliminated by now!"); + } + + // OrigBB branches to NewBB. + OrigBB->addSuccessor(NewBB); + + // Update internal data structures to account for the newly inserted MBB. + // This is almost the same as UpdateForInsertedWaterBlock, except that + // the Water goes after OrigBB, not NewBB. + NewBB->getParent()->RenumberBlocks(NewBB); + + // Insert a size into BBSizes to align it properly with the (newly + // renumbered) block numbers. + BBSizes.insert(BBSizes.begin()+NewBB->getNumber(), 0); + + // Likewise for BBOffsets. + BBOffsets.insert(BBOffsets.begin()+NewBB->getNumber(), 0); + + // Next, update WaterList. Specifically, we need to add OrigMBB as having + // available water after it (but not if it's already there, which happens + // when splitting before a conditional branch that is followed by an + // unconditional branch - in that case we want to insert NewBB). + std::vector<MachineBasicBlock*>::iterator IP = + std::lower_bound(WaterList.begin(), WaterList.end(), OrigBB, + CompareMBBNumbers); + MachineBasicBlock* WaterBB = *IP; + if (WaterBB == OrigBB) + WaterList.insert(next(IP), NewBB); + else + WaterList.insert(IP, OrigBB); + + // Figure out how large the first NewMBB is. (It cannot + // contain a constpool_entry or tablejump.) + unsigned NewBBSize = 0; + for (MachineBasicBlock::iterator I = NewBB->begin(), E = NewBB->end(); + I != E; ++I) + NewBBSize += ARM::GetInstSize(I); + + unsigned OrigBBI = OrigBB->getNumber(); + unsigned NewBBI = NewBB->getNumber(); + // Set the size of NewBB in BBSizes. + BBSizes[NewBBI] = NewBBSize; + + // We removed instructions from UserMBB, subtract that off from its size. + // Add 2 or 4 to the block to count the unconditional branch we added to it. + unsigned delta = isThumb ? 2 : 4; + BBSizes[OrigBBI] -= NewBBSize - delta; + + // ...and adjust BBOffsets for NewBB accordingly. + BBOffsets[NewBBI] = BBOffsets[OrigBBI] + BBSizes[OrigBBI]; + + // All BBOffsets following these blocks must be modified. + AdjustBBOffsetsAfter(NewBB, delta); + + return NewBB; +} + +/// OffsetIsInRange - Checks whether UserOffset (the location of a constant pool +/// reference) is within MaxDisp of TrialOffset (a proposed location of a +/// constant pool entry). +bool ARMConstantIslands::OffsetIsInRange(unsigned UserOffset, + unsigned TrialOffset, unsigned MaxDisp, bool NegativeOK) { + // On Thumb offsets==2 mod 4 are rounded down by the hardware for + // purposes of the displacement computation; compensate for that here. + // Effectively, the valid range of displacements is 2 bytes smaller for such + // references. + if (isThumb && UserOffset%4 !=0) + UserOffset -= 2; + // CPEs will be rounded up to a multiple of 4. + if (isThumb && TrialOffset%4 != 0) + TrialOffset += 2; + + if (UserOffset <= TrialOffset) { + // User before the Trial. + if (TrialOffset-UserOffset <= MaxDisp) + return true; + } else if (NegativeOK) { + if (UserOffset-TrialOffset <= MaxDisp) + return true; + } + return false; +} + +/// WaterIsInRange - Returns true if a CPE placed after the specified +/// Water (a basic block) will be in range for the specific MI. + +bool ARMConstantIslands::WaterIsInRange(unsigned UserOffset, + MachineBasicBlock* Water, CPUser &U) +{ + unsigned MaxDisp = U.MaxDisp; + MachineFunction::iterator I = next(MachineFunction::iterator(Water)); + unsigned CPEOffset = BBOffsets[Water->getNumber()] + + BBSizes[Water->getNumber()]; + + // If the CPE is to be inserted before the instruction, that will raise + // the offset of the instruction. (Currently applies only to ARM, so + // no alignment compensation attempted here.) + if (CPEOffset < UserOffset) + UserOffset += U.CPEMI->getOperand(2).getImm(); + + return OffsetIsInRange (UserOffset, CPEOffset, MaxDisp, !isThumb); +} + +/// CPEIsInRange - Returns true if the distance between specific MI and +/// specific ConstPool entry instruction can fit in MI's displacement field. +bool ARMConstantIslands::CPEIsInRange(MachineInstr *MI, unsigned UserOffset, + MachineInstr *CPEMI, + unsigned MaxDisp, bool DoDump) { + unsigned CPEOffset = GetOffsetOf(CPEMI); + assert(CPEOffset%4 == 0 && "Misaligned CPE"); + + if (DoDump) { + DOUT << "User of CPE#" << CPEMI->getOperand(0).getImm() + << " max delta=" << MaxDisp + << " insn address=" << UserOffset + << " CPE address=" << CPEOffset + << " offset=" << int(CPEOffset-UserOffset) << "\t" << *MI; + } + + return OffsetIsInRange(UserOffset, CPEOffset, MaxDisp, !isThumb); +} + +/// BBIsJumpedOver - Return true of the specified basic block's only predecessor +/// unconditionally branches to its only successor. +static bool BBIsJumpedOver(MachineBasicBlock *MBB) { + if (MBB->pred_size() != 1 || MBB->succ_size() != 1) + return false; + + MachineBasicBlock *Succ = *MBB->succ_begin(); + MachineBasicBlock *Pred = *MBB->pred_begin(); + MachineInstr *PredMI = &Pred->back(); + if (PredMI->getOpcode() == ARM::B || PredMI->getOpcode() == ARM::tB) + return PredMI->getOperand(0).getMBB() == Succ; + return false; +} + +void ARMConstantIslands::AdjustBBOffsetsAfter(MachineBasicBlock *BB, + int delta) { + MachineFunction::iterator MBBI = BB; MBBI = next(MBBI); + for(unsigned i=BB->getNumber()+1; i<BB->getParent()->getNumBlockIDs(); i++) { + BBOffsets[i] += delta; + // If some existing blocks have padding, adjust the padding as needed, a + // bit tricky. delta can be negative so don't use % on that. + if (isThumb) { + MachineBasicBlock *MBB = MBBI; + if (!MBB->empty()) { + // Constant pool entries require padding. + if (MBB->begin()->getOpcode() == ARM::CONSTPOOL_ENTRY) { + unsigned oldOffset = BBOffsets[i] - delta; + if (oldOffset%4==0 && BBOffsets[i]%4!=0) { + // add new padding + BBSizes[i] += 2; + delta += 2; + } else if (oldOffset%4!=0 && BBOffsets[i]%4==0) { + // remove existing padding + BBSizes[i] -=2; + delta -= 2; + } + } + // Thumb jump tables require padding. They should be at the end; + // following unconditional branches are removed by AnalyzeBranch. + MachineInstr *ThumbJTMI = NULL; + if (prior(MBB->end())->getOpcode() == ARM::tBR_JTr) + ThumbJTMI = prior(MBB->end()); + if (ThumbJTMI) { + unsigned newMIOffset = GetOffsetOf(ThumbJTMI); + unsigned oldMIOffset = newMIOffset - delta; + if (oldMIOffset%4 == 0 && newMIOffset%4 != 0) { + // remove existing padding + BBSizes[i] -= 2; + delta -= 2; + } else if (oldMIOffset%4 != 0 && newMIOffset%4 == 0) { + // add new padding + BBSizes[i] += 2; + delta += 2; + } + } + if (delta==0) + return; + } + MBBI = next(MBBI); + } + } +} + +/// DecrementOldEntry - find the constant pool entry with index CPI +/// and instruction CPEMI, and decrement its refcount. If the refcount +/// becomes 0 remove the entry and instruction. Returns true if we removed +/// the entry, false if we didn't. + +bool ARMConstantIslands::DecrementOldEntry(unsigned CPI, MachineInstr *CPEMI) { + // Find the old entry. Eliminate it if it is no longer used. + CPEntry *CPE = findConstPoolEntry(CPI, CPEMI); + assert(CPE && "Unexpected!"); + if (--CPE->RefCount == 0) { + RemoveDeadCPEMI(CPEMI); + CPE->CPEMI = NULL; + NumCPEs--; + return true; + } + return false; +} + +/// LookForCPEntryInRange - see if the currently referenced CPE is in range; +/// if not, see if an in-range clone of the CPE is in range, and if so, +/// change the data structures so the user references the clone. Returns: +/// 0 = no existing entry found +/// 1 = entry found, and there were no code insertions or deletions +/// 2 = entry found, and there were code insertions or deletions +int ARMConstantIslands::LookForExistingCPEntry(CPUser& U, unsigned UserOffset) +{ + MachineInstr *UserMI = U.MI; + MachineInstr *CPEMI = U.CPEMI; + + // Check to see if the CPE is already in-range. + if (CPEIsInRange(UserMI, UserOffset, CPEMI, U.MaxDisp, true)) { + DOUT << "In range\n"; + return 1; + } + + // No. Look for previously created clones of the CPE that are in range. + unsigned CPI = CPEMI->getOperand(1).getConstantPoolIndex(); + std::vector<CPEntry> &CPEs = CPEntries[CPI]; + for (unsigned i = 0, e = CPEs.size(); i != e; ++i) { + // We already tried this one + if (CPEs[i].CPEMI == CPEMI) + continue; + // Removing CPEs can leave empty entries, skip + if (CPEs[i].CPEMI == NULL) + continue; + if (CPEIsInRange(UserMI, UserOffset, CPEs[i].CPEMI, U.MaxDisp, false)) { + DOUT << "Replacing CPE#" << CPI << " with CPE#" << CPEs[i].CPI << "\n"; + // Point the CPUser node to the replacement + U.CPEMI = CPEs[i].CPEMI; + // Change the CPI in the instruction operand to refer to the clone. + for (unsigned j = 0, e = UserMI->getNumOperands(); j != e; ++j) + if (UserMI->getOperand(j).isConstantPoolIndex()) { + UserMI->getOperand(j).setConstantPoolIndex(CPEs[i].CPI); + break; + } + // Adjust the refcount of the clone... + CPEs[i].RefCount++; + // ...and the original. If we didn't remove the old entry, none of the + // addresses changed, so we don't need another pass. + return DecrementOldEntry(CPI, CPEMI) ? 2 : 1; + } + } + return 0; +} + +/// getUnconditionalBrDisp - Returns the maximum displacement that can fit in +/// the specific unconditional branch instruction. +static inline unsigned getUnconditionalBrDisp(int Opc) { + return (Opc == ARM::tB) ? ((1<<10)-1)*2 : ((1<<23)-1)*4; +} + +/// AcceptWater - Small amount of common code factored out of the following. + +MachineBasicBlock* ARMConstantIslands::AcceptWater(MachineBasicBlock *WaterBB, + std::vector<MachineBasicBlock*>::iterator IP) { + DOUT << "found water in range\n"; + // Remove the original WaterList entry; we want subsequent + // insertions in this vicinity to go after the one we're + // about to insert. This considerably reduces the number + // of times we have to move the same CPE more than once. + WaterList.erase(IP); + // CPE goes before following block (NewMBB). + return next(MachineFunction::iterator(WaterBB)); +} + +/// LookForWater - look for an existing entry in the WaterList in which +/// we can place the CPE referenced from U so it's within range of U's MI. +/// Returns true if found, false if not. If it returns true, *NewMBB +/// is set to the WaterList entry. +/// For ARM, we prefer the water that's farthest away. For Thumb, prefer +/// water that will not introduce padding to water that will; within each +/// group, prefer the water that's farthest away. + +bool ARMConstantIslands::LookForWater(CPUser &U, unsigned UserOffset, + MachineBasicBlock** NewMBB) { + std::vector<MachineBasicBlock*>::iterator IPThatWouldPad; + MachineBasicBlock* WaterBBThatWouldPad = NULL; + if (!WaterList.empty()) { + for (std::vector<MachineBasicBlock*>::iterator IP = prior(WaterList.end()), + B = WaterList.begin();; --IP) { + MachineBasicBlock* WaterBB = *IP; + if (WaterIsInRange(UserOffset, WaterBB, U)) { + if (isThumb && + (BBOffsets[WaterBB->getNumber()] + + BBSizes[WaterBB->getNumber()])%4 != 0) { + // This is valid Water, but would introduce padding. Remember + // it in case we don't find any Water that doesn't do this. + if (!WaterBBThatWouldPad) { + WaterBBThatWouldPad = WaterBB; + IPThatWouldPad = IP; + } + } else { + *NewMBB = AcceptWater(WaterBB, IP); + return true; + } + } + if (IP == B) + break; + } + } + if (isThumb && WaterBBThatWouldPad) { + *NewMBB = AcceptWater(WaterBBThatWouldPad, IPThatWouldPad); + return true; + } + return false; +} + +/// CreateNewWater - No existing WaterList entry will work for +/// CPUsers[CPUserIndex], so create a place to put the CPE. The end of the +/// block is used if in range, and the conditional branch munged so control +/// flow is correct. Otherwise the block is split to create a hole with an +/// unconditional branch around it. In either case *NewMBB is set to a +/// block following which the new island can be inserted (the WaterList +/// is not adjusted). + +void ARMConstantIslands::CreateNewWater(unsigned CPUserIndex, + unsigned UserOffset, MachineBasicBlock** NewMBB) { + CPUser &U = CPUsers[CPUserIndex]; + MachineInstr *UserMI = U.MI; + MachineInstr *CPEMI = U.CPEMI; + MachineBasicBlock *UserMBB = UserMI->getParent(); + unsigned OffsetOfNextBlock = BBOffsets[UserMBB->getNumber()] + + BBSizes[UserMBB->getNumber()]; + assert(OffsetOfNextBlock== BBOffsets[UserMBB->getNumber()+1]); + + // If the use is at the end of the block, or the end of the block + // is within range, make new water there. (The addition below is + // for the unconditional branch we will be adding: 4 bytes on ARM, + // 2 on Thumb. Possible Thumb alignment padding is allowed for + // inside OffsetIsInRange. + // If the block ends in an unconditional branch already, it is water, + // and is known to be out of range, so we'll always be adding a branch.) + if (&UserMBB->back() == UserMI || + OffsetIsInRange(UserOffset, OffsetOfNextBlock + (isThumb ? 2: 4), + U.MaxDisp, !isThumb)) { + DOUT << "Split at end of block\n"; + if (&UserMBB->back() == UserMI) + assert(BBHasFallthrough(UserMBB) && "Expected a fallthrough BB!"); + *NewMBB = next(MachineFunction::iterator(UserMBB)); + // Add an unconditional branch from UserMBB to fallthrough block. + // Record it for branch lengthening; this new branch will not get out of + // range, but if the preceding conditional branch is out of range, the + // targets will be exchanged, and the altered branch may be out of + // range, so the machinery has to know about it. + int UncondBr = isThumb ? ARM::tB : ARM::B; + BuildMI(UserMBB, TII->get(UncondBr)).addMBB(*NewMBB); + unsigned MaxDisp = getUnconditionalBrDisp(UncondBr); + ImmBranches.push_back(ImmBranch(&UserMBB->back(), + MaxDisp, false, UncondBr)); + int delta = isThumb ? 2 : 4; + BBSizes[UserMBB->getNumber()] += delta; + AdjustBBOffsetsAfter(UserMBB, delta); + } else { + // What a big block. Find a place within the block to split it. + // This is a little tricky on Thumb since instructions are 2 bytes + // and constant pool entries are 4 bytes: if instruction I references + // island CPE, and instruction I+1 references CPE', it will + // not work well to put CPE as far forward as possible, since then + // CPE' cannot immediately follow it (that location is 2 bytes + // farther away from I+1 than CPE was from I) and we'd need to create + // a new island. So, we make a first guess, then walk through the + // instructions between the one currently being looked at and the + // possible insertion point, and make sure any other instructions + // that reference CPEs will be able to use the same island area; + // if not, we back up the insertion point. + + // The 4 in the following is for the unconditional branch we'll be + // inserting (allows for long branch on Thumb). Alignment of the + // island is handled inside OffsetIsInRange. + unsigned BaseInsertOffset = UserOffset + U.MaxDisp -4; + // This could point off the end of the block if we've already got + // constant pool entries following this block; only the last one is + // in the water list. Back past any possible branches (allow for a + // conditional and a maximally long unconditional). + if (BaseInsertOffset >= BBOffsets[UserMBB->getNumber()+1]) + BaseInsertOffset = BBOffsets[UserMBB->getNumber()+1] - + (isThumb ? 6 : 8); + unsigned EndInsertOffset = BaseInsertOffset + + CPEMI->getOperand(2).getImm(); + MachineBasicBlock::iterator MI = UserMI; + ++MI; + unsigned CPUIndex = CPUserIndex+1; + for (unsigned Offset = UserOffset+ARM::GetInstSize(UserMI); + Offset < BaseInsertOffset; + Offset += ARM::GetInstSize(MI), + MI = next(MI)) { + if (CPUIndex < CPUsers.size() && CPUsers[CPUIndex].MI == MI) { + if (!OffsetIsInRange(Offset, EndInsertOffset, + CPUsers[CPUIndex].MaxDisp, !isThumb)) { + BaseInsertOffset -= (isThumb ? 2 : 4); + EndInsertOffset -= (isThumb ? 2 : 4); + } + // This is overly conservative, as we don't account for CPEMIs + // being reused within the block, but it doesn't matter much. + EndInsertOffset += CPUsers[CPUIndex].CPEMI->getOperand(2).getImm(); + CPUIndex++; + } + } + DOUT << "Split in middle of big block\n"; + *NewMBB = SplitBlockBeforeInstr(prior(MI)); + } +} + +/// HandleConstantPoolUser - Analyze the specified user, checking to see if it +/// is out-of-range. If so, pick it up the constant pool value and move it some +/// place in-range. Return true if we changed any addresses (thus must run +/// another pass of branch lengthening), false otherwise. +bool ARMConstantIslands::HandleConstantPoolUser(MachineFunction &Fn, + unsigned CPUserIndex){ + CPUser &U = CPUsers[CPUserIndex]; + MachineInstr *UserMI = U.MI; + MachineInstr *CPEMI = U.CPEMI; + unsigned CPI = CPEMI->getOperand(1).getConstantPoolIndex(); + unsigned Size = CPEMI->getOperand(2).getImm(); + MachineBasicBlock *NewMBB; + // Compute this only once, it's expensive. The 4 or 8 is the value the + // hardware keeps in the PC (2 insns ahead of the reference). + unsigned UserOffset = GetOffsetOf(UserMI) + (isThumb ? 4 : 8); + + // Special case: tLEApcrel are two instructions MI's. The actual user is the + // second instruction. + if (UserMI->getOpcode() == ARM::tLEApcrel) + UserOffset += 2; + + // See if the current entry is within range, or there is a clone of it + // in range. + int result = LookForExistingCPEntry(U, UserOffset); + if (result==1) return false; + else if (result==2) return true; + + // No existing clone of this CPE is within range. + // We will be generating a new clone. Get a UID for it. + unsigned ID = NextUID++; + + // Look for water where we can place this CPE. We look for the farthest one + // away that will work. Forward references only for now (although later + // we might find some that are backwards). + + if (!LookForWater(U, UserOffset, &NewMBB)) { + // No water found. + DOUT << "No water found\n"; + CreateNewWater(CPUserIndex, UserOffset, &NewMBB); + } + + // Okay, we know we can put an island before NewMBB now, do it! + MachineBasicBlock *NewIsland = new MachineBasicBlock(); + Fn.getBasicBlockList().insert(NewMBB, NewIsland); + + // Update internal data structures to account for the newly inserted MBB. + UpdateForInsertedWaterBlock(NewIsland); + + // Decrement the old entry, and remove it if refcount becomes 0. + DecrementOldEntry(CPI, CPEMI); + + // Now that we have an island to add the CPE to, clone the original CPE and + // add it to the island. + U.CPEMI = BuildMI(NewIsland, TII->get(ARM::CONSTPOOL_ENTRY)) + .addImm(ID).addConstantPoolIndex(CPI).addImm(Size); + CPEntries[CPI].push_back(CPEntry(U.CPEMI, ID, 1)); + NumCPEs++; + + BBOffsets[NewIsland->getNumber()] = BBOffsets[NewMBB->getNumber()]; + // Compensate for .align 2 in thumb mode. + if (isThumb && BBOffsets[NewIsland->getNumber()]%4 != 0) + Size += 2; + // Increase the size of the island block to account for the new entry. + BBSizes[NewIsland->getNumber()] += Size; + AdjustBBOffsetsAfter(NewIsland, Size); + + // Finally, change the CPI in the instruction operand to be ID. + for (unsigned i = 0, e = UserMI->getNumOperands(); i != e; ++i) + if (UserMI->getOperand(i).isConstantPoolIndex()) { + UserMI->getOperand(i).setConstantPoolIndex(ID); + break; + } + + DOUT << " Moved CPE to #" << ID << " CPI=" << CPI << "\t" << *UserMI; + + return true; +} + +/// RemoveDeadCPEMI - Remove a dead constant pool entry instruction. Update +/// sizes and offsets of impacted basic blocks. +void ARMConstantIslands::RemoveDeadCPEMI(MachineInstr *CPEMI) { + MachineBasicBlock *CPEBB = CPEMI->getParent(); + unsigned Size = CPEMI->getOperand(2).getImm(); + CPEMI->eraseFromParent(); + BBSizes[CPEBB->getNumber()] -= Size; + // All succeeding offsets have the current size value added in, fix this. + if (CPEBB->empty()) { + // In thumb mode, the size of island may be padded by two to compensate for + // the alignment requirement. Then it will now be 2 when the block is + // empty, so fix this. + // All succeeding offsets have the current size value added in, fix this. + if (BBSizes[CPEBB->getNumber()] != 0) { + Size += BBSizes[CPEBB->getNumber()]; + BBSizes[CPEBB->getNumber()] = 0; + } + } + AdjustBBOffsetsAfter(CPEBB, -Size); + // An island has only one predecessor BB and one successor BB. Check if + // this BB's predecessor jumps directly to this BB's successor. This + // shouldn't happen currently. + assert(!BBIsJumpedOver(CPEBB) && "How did this happen?"); + // FIXME: remove the empty blocks after all the work is done? +} + +/// RemoveUnusedCPEntries - Remove constant pool entries whose refcounts +/// are zero. +bool ARMConstantIslands::RemoveUnusedCPEntries() { + unsigned MadeChange = false; + for (unsigned i = 0, e = CPEntries.size(); i != e; ++i) { + std::vector<CPEntry> &CPEs = CPEntries[i]; + for (unsigned j = 0, ee = CPEs.size(); j != ee; ++j) { + if (CPEs[j].RefCount == 0 && CPEs[j].CPEMI) { + RemoveDeadCPEMI(CPEs[j].CPEMI); + CPEs[j].CPEMI = NULL; + MadeChange = true; + } + } + } + return MadeChange; +} + +/// BBIsInRange - Returns true if the distance between specific MI and +/// specific BB can fit in MI's displacement field. +bool ARMConstantIslands::BBIsInRange(MachineInstr *MI,MachineBasicBlock *DestBB, + unsigned MaxDisp) { + unsigned PCAdj = isThumb ? 4 : 8; + unsigned BrOffset = GetOffsetOf(MI) + PCAdj; + unsigned DestOffset = BBOffsets[DestBB->getNumber()]; + + DOUT << "Branch of destination BB#" << DestBB->getNumber() + << " from BB#" << MI->getParent()->getNumber() + << " max delta=" << MaxDisp + << " from " << GetOffsetOf(MI) << " to " << DestOffset + << " offset " << int(DestOffset-BrOffset) << "\t" << *MI; + + if (BrOffset <= DestOffset) { + // Branch before the Dest. + if (DestOffset-BrOffset <= MaxDisp) + return true; + } else { + if (BrOffset-DestOffset <= MaxDisp) + return true; + } + return false; +} + +/// FixUpImmediateBr - Fix up an immediate branch whose destination is too far +/// away to fit in its displacement field. +bool ARMConstantIslands::FixUpImmediateBr(MachineFunction &Fn, ImmBranch &Br) { + MachineInstr *MI = Br.MI; + MachineBasicBlock *DestBB = MI->getOperand(0).getMachineBasicBlock(); + + // Check to see if the DestBB is already in-range. + if (BBIsInRange(MI, DestBB, Br.MaxDisp)) + return false; + + if (!Br.isCond) + return FixUpUnconditionalBr(Fn, Br); + return FixUpConditionalBr(Fn, Br); +} + +/// FixUpUnconditionalBr - Fix up an unconditional branch whose destination is +/// too far away to fit in its displacement field. If the LR register has been +/// spilled in the epilogue, then we can use BL to implement a far jump. +/// Otherwise, add an intermediate branch instruction to to a branch. +bool +ARMConstantIslands::FixUpUnconditionalBr(MachineFunction &Fn, ImmBranch &Br) { + MachineInstr *MI = Br.MI; + MachineBasicBlock *MBB = MI->getParent(); + assert(isThumb && "Expected a Thumb function!"); + + // Use BL to implement far jump. + Br.MaxDisp = (1 << 21) * 2; + MI->setInstrDescriptor(TII->get(ARM::tBfar)); + BBSizes[MBB->getNumber()] += 2; + AdjustBBOffsetsAfter(MBB, 2); + HasFarJump = true; + NumUBrFixed++; + + DOUT << " Changed B to long jump " << *MI; + + return true; +} + +/// FixUpConditionalBr - Fix up a conditional branch whose destination is too +/// far away to fit in its displacement field. It is converted to an inverse +/// conditional branch + an unconditional branch to the destination. +bool +ARMConstantIslands::FixUpConditionalBr(MachineFunction &Fn, ImmBranch &Br) { + MachineInstr *MI = Br.MI; + MachineBasicBlock *DestBB = MI->getOperand(0).getMachineBasicBlock(); + + // Add a unconditional branch to the destination and invert the branch + // condition to jump over it: + // blt L1 + // => + // bge L2 + // b L1 + // L2: + ARMCC::CondCodes CC = (ARMCC::CondCodes)MI->getOperand(1).getImmedValue(); + CC = ARMCC::getOppositeCondition(CC); + unsigned CCReg = MI->getOperand(2).getReg(); + + // If the branch is at the end of its MBB and that has a fall-through block, + // direct the updated conditional branch to the fall-through block. Otherwise, + // split the MBB before the next instruction. + MachineBasicBlock *MBB = MI->getParent(); + MachineInstr *BMI = &MBB->back(); + bool NeedSplit = (BMI != MI) || !BBHasFallthrough(MBB); + + NumCBrFixed++; + if (BMI != MI) { + if (next(MachineBasicBlock::iterator(MI)) == MBB->back() && + BMI->getOpcode() == Br.UncondBr) { + // Last MI in the BB is a unconditional branch. Can we simply invert the + // condition and swap destinations: + // beq L1 + // b L2 + // => + // bne L2 + // b L1 + MachineBasicBlock *NewDest = BMI->getOperand(0).getMachineBasicBlock(); + if (BBIsInRange(MI, NewDest, Br.MaxDisp)) { + DOUT << " Invert Bcc condition and swap its destination with " << *BMI; + BMI->getOperand(0).setMachineBasicBlock(DestBB); + MI->getOperand(0).setMachineBasicBlock(NewDest); + MI->getOperand(1).setImm(CC); + return true; + } + } + } + + if (NeedSplit) { + SplitBlockBeforeInstr(MI); + // No need for the branch to the next block. We're adding a unconditional + // branch to the destination. + int delta = ARM::GetInstSize(&MBB->back()); + BBSizes[MBB->getNumber()] -= delta; + MachineBasicBlock* SplitBB = next(MachineFunction::iterator(MBB)); + AdjustBBOffsetsAfter(SplitBB, -delta); + MBB->back().eraseFromParent(); + // BBOffsets[SplitBB] is wrong temporarily, fixed below + } + MachineBasicBlock *NextBB = next(MachineFunction::iterator(MBB)); + + DOUT << " Insert B to BB#" << DestBB->getNumber() + << " also invert condition and change dest. to BB#" + << NextBB->getNumber() << "\n"; + + // Insert a new conditional branch and a new unconditional branch. + // Also update the ImmBranch as well as adding a new entry for the new branch. + BuildMI(MBB, TII->get(MI->getOpcode())).addMBB(NextBB) + .addImm(CC).addReg(CCReg); + Br.MI = &MBB->back(); + BBSizes[MBB->getNumber()] += ARM::GetInstSize(&MBB->back()); + BuildMI(MBB, TII->get(Br.UncondBr)).addMBB(DestBB); + BBSizes[MBB->getNumber()] += ARM::GetInstSize(&MBB->back()); + unsigned MaxDisp = getUnconditionalBrDisp(Br.UncondBr); + ImmBranches.push_back(ImmBranch(&MBB->back(), MaxDisp, false, Br.UncondBr)); + + // Remove the old conditional branch. It may or may not still be in MBB. + BBSizes[MI->getParent()->getNumber()] -= ARM::GetInstSize(MI); + MI->eraseFromParent(); + + // The net size change is an addition of one unconditional branch. + int delta = ARM::GetInstSize(&MBB->back()); + AdjustBBOffsetsAfter(MBB, delta); + return true; +} + +/// UndoLRSpillRestore - Remove Thumb push / pop instructions that only spills +/// LR / restores LR to pc. +bool ARMConstantIslands::UndoLRSpillRestore() { + bool MadeChange = false; + for (unsigned i = 0, e = PushPopMIs.size(); i != e; ++i) { + MachineInstr *MI = PushPopMIs[i]; + if (MI->getOpcode() == ARM::tPOP_RET && + MI->getOperand(0).getReg() == ARM::PC && + MI->getNumExplicitOperands() == 1) { + BuildMI(MI->getParent(), TII->get(ARM::tBX_RET)); + MI->eraseFromParent(); + MadeChange = true; + } + } + return MadeChange; +} diff --git a/lib/Target/ARM/ARMConstantPoolValue.cpp b/lib/Target/ARM/ARMConstantPoolValue.cpp new file mode 100644 index 0000000..30a8eaf --- /dev/null +++ b/lib/Target/ARM/ARMConstantPoolValue.cpp @@ -0,0 +1,90 @@ +//===- ARMConstantPoolValue.cpp - ARM constantpool value --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Evan Cheng and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the ARM specific constantpool value class. +// +//===----------------------------------------------------------------------===// + +#include "ARMConstantPoolValue.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/GlobalValue.h" +#include "llvm/Type.h" +using namespace llvm; + +ARMConstantPoolValue::ARMConstantPoolValue(GlobalValue *gv, unsigned id, + ARMCP::ARMCPKind k, + unsigned char PCAdj, + const char *Modif, + bool AddCA) + : MachineConstantPoolValue((const Type*)gv->getType()), + GV(gv), S(NULL), LabelId(id), Kind(k), PCAdjust(PCAdj), + Modifier(Modif), AddCurrentAddress(AddCA) {} + +ARMConstantPoolValue::ARMConstantPoolValue(const char *s, unsigned id, + ARMCP::ARMCPKind k, + unsigned char PCAdj, + const char *Modif, + bool AddCA) + : MachineConstantPoolValue((const Type*)Type::Int32Ty), + GV(NULL), S(s), LabelId(id), Kind(k), PCAdjust(PCAdj), + Modifier(Modif), AddCurrentAddress(AddCA) {} + +ARMConstantPoolValue::ARMConstantPoolValue(GlobalValue *gv, + ARMCP::ARMCPKind k, + const char *Modif) + : MachineConstantPoolValue((const Type*)Type::Int32Ty), + GV(gv), S(NULL), LabelId(0), Kind(k), PCAdjust(0), + Modifier(Modif) {} + +int ARMConstantPoolValue::getExistingMachineCPValue(MachineConstantPool *CP, + unsigned Alignment) { + unsigned AlignMask = (1 << Alignment)-1; + const std::vector<MachineConstantPoolEntry> Constants = CP->getConstants(); + for (unsigned i = 0, e = Constants.size(); i != e; ++i) { + if (Constants[i].isMachineConstantPoolEntry() && + (Constants[i].Offset & AlignMask) == 0) { + ARMConstantPoolValue *CPV = + (ARMConstantPoolValue *)Constants[i].Val.MachineCPVal; + if (CPV->GV == GV && + CPV->S == S && + CPV->LabelId == LabelId && + CPV->Kind == Kind && + CPV->PCAdjust == PCAdjust) + return i; + } + } + + return -1; +} + +void +ARMConstantPoolValue::AddSelectionDAGCSEId(FoldingSetNodeID &ID) { + ID.AddPointer(GV); + ID.AddPointer(S); + ID.AddInteger(LabelId); + ID.AddInteger((unsigned)Kind); + ID.AddInteger(PCAdjust); +} + +void ARMConstantPoolValue::print(std::ostream &O) const { + if (GV) + O << GV->getName(); + else + O << S; + if (isNonLazyPointer()) O << "$non_lazy_ptr"; + else if (isStub()) O << "$stub"; + if (Modifier) O << "(" << Modifier << ")"; + if (PCAdjust != 0) { + O << "-(LPIC" << LabelId << "+" + << (unsigned)PCAdjust; + if (AddCurrentAddress) + O << "-."; + O << ")"; + } +} diff --git a/lib/Target/ARM/ARMConstantPoolValue.h b/lib/Target/ARM/ARMConstantPoolValue.h new file mode 100644 index 0000000..d71bcf0 --- /dev/null +++ b/lib/Target/ARM/ARMConstantPoolValue.h @@ -0,0 +1,75 @@ +//===- ARMConstantPoolValue.h - ARM constantpool value ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Evan Cheng and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the ARM specific constantpool value class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TARGET_ARM_CONSTANTPOOLVALUE_H +#define LLVM_TARGET_ARM_CONSTANTPOOLVALUE_H + +#include "llvm/CodeGen/MachineConstantPool.h" + +namespace llvm { + +namespace ARMCP { + enum ARMCPKind { + CPValue, + CPNonLazyPtr, + CPStub + }; +} + +/// ARMConstantPoolValue - ARM specific constantpool value. This is used to +/// represent PC relative displacement between the address of the load +/// instruction and the global value being loaded, i.e. (&GV-(LPIC+8)). +class ARMConstantPoolValue : public MachineConstantPoolValue { + GlobalValue *GV; // GlobalValue being loaded. + const char *S; // ExtSymbol being loaded. + unsigned LabelId; // Label id of the load. + ARMCP::ARMCPKind Kind; // non_lazy_ptr or stub? + unsigned char PCAdjust; // Extra adjustment if constantpool is pc relative. + // 8 for ARM, 4 for Thumb. + const char *Modifier; // GV modifier i.e. (&GV(modifier)-(LPIC+8)) + bool AddCurrentAddress; + +public: + ARMConstantPoolValue(GlobalValue *gv, unsigned id, + ARMCP::ARMCPKind Kind = ARMCP::CPValue, + unsigned char PCAdj = 0, const char *Modifier = NULL, + bool AddCurrentAddress = false); + ARMConstantPoolValue(const char *s, unsigned id, + ARMCP::ARMCPKind Kind = ARMCP::CPValue, + unsigned char PCAdj = 0, const char *Modifier = NULL, + bool AddCurrentAddress = false); + ARMConstantPoolValue(GlobalValue *GV, ARMCP::ARMCPKind Kind, + const char *Modifier); + + + GlobalValue *getGV() const { return GV; } + const char *getSymbol() const { return S; } + const char *getModifier() const { return Modifier; } + bool hasModifier() const { return Modifier != NULL; } + bool mustAddCurrentAddress() const { return AddCurrentAddress; } + unsigned getLabelId() const { return LabelId; } + bool isNonLazyPointer() const { return Kind == ARMCP::CPNonLazyPtr; } + bool isStub() const { return Kind == ARMCP::CPStub; } + unsigned char getPCAdjustment() const { return PCAdjust; } + + virtual int getExistingMachineCPValue(MachineConstantPool *CP, + unsigned Alignment); + + virtual void AddSelectionDAGCSEId(FoldingSetNodeID &ID); + + virtual void print(std::ostream &O) const; +}; + +} + +#endif diff --git a/lib/Target/ARM/ARMFrameInfo.h b/lib/Target/ARM/ARMFrameInfo.h new file mode 100644 index 0000000..c56640a --- /dev/null +++ b/lib/Target/ARM/ARMFrameInfo.h @@ -0,0 +1,33 @@ +//===-- ARMTargetFrameInfo.h - Define TargetFrameInfo for ARM ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the "Instituto Nokia de Tecnologia" and +// is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// +// +//===----------------------------------------------------------------------===// + +#ifndef ARM_FRAMEINFO_H +#define ARM_FRAMEINFO_H + +#include "ARM.h" +#include "llvm/Target/TargetFrameInfo.h" +#include "ARMSubtarget.h" + +namespace llvm { + +class ARMFrameInfo : public TargetFrameInfo { +public: + ARMFrameInfo(const ARMSubtarget &ST) + : TargetFrameInfo(StackGrowsDown, ST.getStackAlignment(), 0) { + } +}; + +} // End llvm namespace + +#endif diff --git a/lib/Target/ARM/ARMISelDAGToDAG.cpp b/lib/Target/ARM/ARMISelDAGToDAG.cpp new file mode 100644 index 0000000..933651d --- /dev/null +++ b/lib/Target/ARM/ARMISelDAGToDAG.cpp @@ -0,0 +1,859 @@ +//===-- ARMISelDAGToDAG.cpp - A dag to dag inst selector for ARM ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Chris Lattner and 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/CodeGen/SSARegMap.h" +#include "llvm/Target/TargetLowering.h" +#include "llvm/Target/TargetOptions.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: + 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 InstructionSelectBasicBlock(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::InstructionSelectBasicBlock(SelectionDAG &DAG) { + DEBUG(BB->dump()); + + DAG.setRoot(SelectRoot(DAG.getRoot())); + DAG.RemoveDeadNodes(); + + ScheduleAndEmitDAG(DAG); +} + +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; + unsigned Opcode = N->getOpcode(); + + if (Opcode >= ISD::BUILTIN_OP_END && Opcode < ARMISD::FIRST_NUMBER) + 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 ARMISD::MULHILOU: { + 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 ARMISD::MULHILOS: { + 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::ValueType LoadedVT = LD->getLoadedVT(); + 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); + 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::ValueType 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) { + 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::ValueType 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) { + 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); +} diff --git a/lib/Target/ARM/ARMISelLowering.cpp b/lib/Target/ARM/ARMISelLowering.cpp new file mode 100644 index 0000000..6f63fbd --- /dev/null +++ b/lib/Target/ARM/ARMISelLowering.cpp @@ -0,0 +1,1859 @@ +//===-- ARMISelLowering.cpp - ARM DAG Lowering Implementation -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Evan Cheng and is distributed under +// the University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the interfaces that ARM uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#include "ARM.h" +#include "ARMAddressingModes.h" +#include "ARMConstantPoolValue.h" +#include "ARMISelLowering.h" +#include "ARMMachineFunctionInfo.h" +#include "ARMRegisterInfo.h" +#include "ARMSubtarget.h" +#include "ARMTargetMachine.h" +#include "llvm/CallingConv.h" +#include "llvm/Constants.h" +#include "llvm/Instruction.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/SelectionDAG.h" +#include "llvm/CodeGen/SSARegMap.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm/ADT/VectorExtras.h" +#include "llvm/Support/MathExtras.h" +using namespace llvm; + +ARMTargetLowering::ARMTargetLowering(TargetMachine &TM) + : TargetLowering(TM), ARMPCLabelIndex(0) { + Subtarget = &TM.getSubtarget<ARMSubtarget>(); + + if (Subtarget->isTargetDarwin()) { + // Don't have these. + setLibcallName(RTLIB::UINTTOFP_I64_F32, NULL); + setLibcallName(RTLIB::UINTTOFP_I64_F64, NULL); + + // Uses VFP for Thumb libfuncs if available. + if (Subtarget->isThumb() && Subtarget->hasVFP2()) { + // Single-precision floating-point arithmetic. + setLibcallName(RTLIB::ADD_F32, "__addsf3vfp"); + setLibcallName(RTLIB::SUB_F32, "__subsf3vfp"); + setLibcallName(RTLIB::MUL_F32, "__mulsf3vfp"); + setLibcallName(RTLIB::DIV_F32, "__divsf3vfp"); + + // Double-precision floating-point arithmetic. + setLibcallName(RTLIB::ADD_F64, "__adddf3vfp"); + setLibcallName(RTLIB::SUB_F64, "__subdf3vfp"); + setLibcallName(RTLIB::MUL_F64, "__muldf3vfp"); + setLibcallName(RTLIB::DIV_F64, "__divdf3vfp"); + + // Single-precision comparisons. + setLibcallName(RTLIB::OEQ_F32, "__eqsf2vfp"); + setLibcallName(RTLIB::UNE_F32, "__nesf2vfp"); + setLibcallName(RTLIB::OLT_F32, "__ltsf2vfp"); + setLibcallName(RTLIB::OLE_F32, "__lesf2vfp"); + setLibcallName(RTLIB::OGE_F32, "__gesf2vfp"); + setLibcallName(RTLIB::OGT_F32, "__gtsf2vfp"); + setLibcallName(RTLIB::UO_F32, "__unordsf2vfp"); + setLibcallName(RTLIB::O_F32, "__unordsf2vfp"); + + setCmpLibcallCC(RTLIB::OEQ_F32, ISD::SETNE); + setCmpLibcallCC(RTLIB::UNE_F32, ISD::SETNE); + setCmpLibcallCC(RTLIB::OLT_F32, ISD::SETNE); + setCmpLibcallCC(RTLIB::OLE_F32, ISD::SETNE); + setCmpLibcallCC(RTLIB::OGE_F32, ISD::SETNE); + setCmpLibcallCC(RTLIB::OGT_F32, ISD::SETNE); + setCmpLibcallCC(RTLIB::UO_F32, ISD::SETNE); + setCmpLibcallCC(RTLIB::O_F32, ISD::SETEQ); + + // Double-precision comparisons. + setLibcallName(RTLIB::OEQ_F64, "__eqdf2vfp"); + setLibcallName(RTLIB::UNE_F64, "__nedf2vfp"); + setLibcallName(RTLIB::OLT_F64, "__ltdf2vfp"); + setLibcallName(RTLIB::OLE_F64, "__ledf2vfp"); + setLibcallName(RTLIB::OGE_F64, "__gedf2vfp"); + setLibcallName(RTLIB::OGT_F64, "__gtdf2vfp"); + setLibcallName(RTLIB::UO_F64, "__unorddf2vfp"); + setLibcallName(RTLIB::O_F64, "__unorddf2vfp"); + + setCmpLibcallCC(RTLIB::OEQ_F64, ISD::SETNE); + setCmpLibcallCC(RTLIB::UNE_F64, ISD::SETNE); + setCmpLibcallCC(RTLIB::OLT_F64, ISD::SETNE); + setCmpLibcallCC(RTLIB::OLE_F64, ISD::SETNE); + setCmpLibcallCC(RTLIB::OGE_F64, ISD::SETNE); + setCmpLibcallCC(RTLIB::OGT_F64, ISD::SETNE); + setCmpLibcallCC(RTLIB::UO_F64, ISD::SETNE); + setCmpLibcallCC(RTLIB::O_F64, ISD::SETEQ); + + // Floating-point to integer conversions. + // i64 conversions are done via library routines even when generating VFP + // instructions, so use the same ones. + setLibcallName(RTLIB::FPTOSINT_F64_I32, "__fixdfsivfp"); + setLibcallName(RTLIB::FPTOUINT_F64_I32, "__fixunsdfsivfp"); + setLibcallName(RTLIB::FPTOSINT_F32_I32, "__fixsfsivfp"); + setLibcallName(RTLIB::FPTOUINT_F32_I32, "__fixunssfsivfp"); + + // Conversions between floating types. + setLibcallName(RTLIB::FPROUND_F64_F32, "__truncdfsf2vfp"); + setLibcallName(RTLIB::FPEXT_F32_F64, "__extendsfdf2vfp"); + + // Integer to floating-point conversions. + // i64 conversions are done via library routines even when generating VFP + // instructions, so use the same ones. + // FIXME: There appears to be some naming inconsistency in ARM libgcc: e.g. + // __floatunsidf vs. __floatunssidfvfp. + setLibcallName(RTLIB::SINTTOFP_I32_F64, "__floatsidfvfp"); + setLibcallName(RTLIB::UINTTOFP_I32_F64, "__floatunssidfvfp"); + setLibcallName(RTLIB::SINTTOFP_I32_F32, "__floatsisfvfp"); + setLibcallName(RTLIB::UINTTOFP_I32_F32, "__floatunssisfvfp"); + } + } + + addRegisterClass(MVT::i32, ARM::GPRRegisterClass); + if (!UseSoftFloat && Subtarget->hasVFP2() && !Subtarget->isThumb()) { + addRegisterClass(MVT::f32, ARM::SPRRegisterClass); + addRegisterClass(MVT::f64, ARM::DPRRegisterClass); + } + computeRegisterProperties(); + + // ARM does not have f32 extending load. + setLoadXAction(ISD::EXTLOAD, MVT::f32, Expand); + + // ARM supports all 4 flavors of integer indexed load / store. + for (unsigned im = (unsigned)ISD::PRE_INC; + im != (unsigned)ISD::LAST_INDEXED_MODE; ++im) { + setIndexedLoadAction(im, MVT::i1, Legal); + setIndexedLoadAction(im, MVT::i8, Legal); + setIndexedLoadAction(im, MVT::i16, Legal); + setIndexedLoadAction(im, MVT::i32, Legal); + setIndexedStoreAction(im, MVT::i1, Legal); + setIndexedStoreAction(im, MVT::i8, Legal); + setIndexedStoreAction(im, MVT::i16, Legal); + setIndexedStoreAction(im, MVT::i32, Legal); + } + + // i64 operation support. + if (Subtarget->isThumb()) { + setOperationAction(ISD::MUL, MVT::i64, Expand); + setOperationAction(ISD::MULHU, MVT::i32, Expand); + setOperationAction(ISD::MULHS, MVT::i32, Expand); + } else { + setOperationAction(ISD::MUL, MVT::i64, Custom); + setOperationAction(ISD::MULHU, MVT::i32, Custom); + if (!Subtarget->hasV6Ops()) + setOperationAction(ISD::MULHS, MVT::i32, Custom); + } + setOperationAction(ISD::SHL_PARTS, MVT::i32, Expand); + setOperationAction(ISD::SRA_PARTS, MVT::i32, Expand); + setOperationAction(ISD::SRL_PARTS, MVT::i32, Expand); + setOperationAction(ISD::SRL, MVT::i64, Custom); + setOperationAction(ISD::SRA, MVT::i64, Custom); + + // ARM does not have ROTL. + setOperationAction(ISD::ROTL, MVT::i32, Expand); + setOperationAction(ISD::CTTZ , MVT::i32, Expand); + setOperationAction(ISD::CTPOP, MVT::i32, Expand); + if (!Subtarget->hasV5TOps() || Subtarget->isThumb()) + setOperationAction(ISD::CTLZ, MVT::i32, Expand); + + // Only ARMv6 has BSWAP. + if (!Subtarget->hasV6Ops()) + setOperationAction(ISD::BSWAP, MVT::i32, Expand); + + // These are expanded into libcalls. + setOperationAction(ISD::SDIV, MVT::i32, Expand); + setOperationAction(ISD::UDIV, MVT::i32, Expand); + setOperationAction(ISD::SREM, MVT::i32, Expand); + setOperationAction(ISD::UREM, MVT::i32, Expand); + + // Support label based line numbers. + setOperationAction(ISD::LOCATION, MVT::Other, Expand); + setOperationAction(ISD::DEBUG_LOC, MVT::Other, Expand); + + setOperationAction(ISD::RET, MVT::Other, Custom); + setOperationAction(ISD::GlobalAddress, MVT::i32, Custom); + setOperationAction(ISD::ConstantPool, MVT::i32, Custom); + setOperationAction(ISD::GLOBAL_OFFSET_TABLE, MVT::i32, Custom); + setOperationAction(ISD::GlobalTLSAddress, MVT::i32, Custom); + + // Expand mem operations genericly. + setOperationAction(ISD::MEMSET , MVT::Other, Expand); + setOperationAction(ISD::MEMCPY , MVT::Other, Custom); + setOperationAction(ISD::MEMMOVE , MVT::Other, Expand); + + // Use the default implementation. + setOperationAction(ISD::VASTART , MVT::Other, Expand); + setOperationAction(ISD::VAARG , MVT::Other, Expand); + setOperationAction(ISD::VACOPY , MVT::Other, Expand); + setOperationAction(ISD::VAEND , MVT::Other, Expand); + setOperationAction(ISD::STACKSAVE, MVT::Other, Expand); + setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); + setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i32 , Expand); + + if (!Subtarget->hasV6Ops()) { + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i16, Expand); + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i8, Expand); + } + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand); + + if (!UseSoftFloat && Subtarget->hasVFP2() && !Subtarget->isThumb()) + // Turn f64->i64 into FMRRD iff target supports vfp2. + setOperationAction(ISD::BIT_CONVERT, MVT::i64, Custom); + + setOperationAction(ISD::SETCC , MVT::i32, Expand); + setOperationAction(ISD::SETCC , MVT::f32, Expand); + setOperationAction(ISD::SETCC , MVT::f64, Expand); + setOperationAction(ISD::SELECT , MVT::i32, Expand); + setOperationAction(ISD::SELECT , MVT::f32, Expand); + setOperationAction(ISD::SELECT , MVT::f64, Expand); + setOperationAction(ISD::SELECT_CC, MVT::i32, Custom); + setOperationAction(ISD::SELECT_CC, MVT::f32, Custom); + setOperationAction(ISD::SELECT_CC, MVT::f64, Custom); + + setOperationAction(ISD::BRCOND , MVT::Other, Expand); + setOperationAction(ISD::BR_CC , MVT::i32, Custom); + setOperationAction(ISD::BR_CC , MVT::f32, Custom); + setOperationAction(ISD::BR_CC , MVT::f64, Custom); + setOperationAction(ISD::BR_JT , MVT::Other, Custom); + + setOperationAction(ISD::VASTART, MVT::Other, Custom); + setOperationAction(ISD::VACOPY, MVT::Other, Expand); + setOperationAction(ISD::VAEND, MVT::Other, Expand); + setOperationAction(ISD::STACKSAVE, MVT::Other, Expand); + setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); + + // FP Constants can't be immediates. + setOperationAction(ISD::ConstantFP, MVT::f64, Expand); + setOperationAction(ISD::ConstantFP, MVT::f32, Expand); + + // We don't support sin/cos/fmod/copysign + setOperationAction(ISD::FSIN , MVT::f64, Expand); + setOperationAction(ISD::FSIN , MVT::f32, Expand); + setOperationAction(ISD::FCOS , MVT::f32, Expand); + setOperationAction(ISD::FCOS , MVT::f64, Expand); + setOperationAction(ISD::FREM , MVT::f64, Expand); + setOperationAction(ISD::FREM , MVT::f32, Expand); + setOperationAction(ISD::FCOPYSIGN, MVT::f64, Custom); + setOperationAction(ISD::FCOPYSIGN, MVT::f32, Custom); + + // int <-> fp are custom expanded into bit_convert + ARMISD ops. + setOperationAction(ISD::SINT_TO_FP, MVT::i32, Custom); + setOperationAction(ISD::UINT_TO_FP, MVT::i32, Custom); + setOperationAction(ISD::FP_TO_UINT, MVT::i32, Custom); + setOperationAction(ISD::FP_TO_SINT, MVT::i32, Custom); + + setStackPointerRegisterToSaveRestore(ARM::SP); + setSchedulingPreference(SchedulingForRegPressure); + setIfCvtBlockSizeLimit(Subtarget->isThumb() ? 0 : 10); + setIfCvtDupBlockSizeLimit(Subtarget->isThumb() ? 0 : 2); + + maxStoresPerMemcpy = 1; //// temporary - rewrite interface to use type +} + + +const char *ARMTargetLowering::getTargetNodeName(unsigned Opcode) const { + switch (Opcode) { + default: return 0; + case ARMISD::Wrapper: return "ARMISD::Wrapper"; + case ARMISD::WrapperJT: return "ARMISD::WrapperJT"; + case ARMISD::CALL: return "ARMISD::CALL"; + case ARMISD::CALL_PRED: return "ARMISD::CALL_PRED"; + case ARMISD::CALL_NOLINK: return "ARMISD::CALL_NOLINK"; + case ARMISD::tCALL: return "ARMISD::tCALL"; + case ARMISD::BRCOND: return "ARMISD::BRCOND"; + case ARMISD::BR_JT: return "ARMISD::BR_JT"; + case ARMISD::RET_FLAG: return "ARMISD::RET_FLAG"; + case ARMISD::PIC_ADD: return "ARMISD::PIC_ADD"; + case ARMISD::CMP: return "ARMISD::CMP"; + case ARMISD::CMPNZ: return "ARMISD::CMPNZ"; + case ARMISD::CMPFP: return "ARMISD::CMPFP"; + case ARMISD::CMPFPw0: return "ARMISD::CMPFPw0"; + case ARMISD::FMSTAT: return "ARMISD::FMSTAT"; + case ARMISD::CMOV: return "ARMISD::CMOV"; + case ARMISD::CNEG: return "ARMISD::CNEG"; + + case ARMISD::FTOSI: return "ARMISD::FTOSI"; + case ARMISD::FTOUI: return "ARMISD::FTOUI"; + case ARMISD::SITOF: return "ARMISD::SITOF"; + case ARMISD::UITOF: return "ARMISD::UITOF"; + case ARMISD::MULHILOU: return "ARMISD::MULHILOU"; + case ARMISD::MULHILOS: return "ARMISD::MULHILOS"; + + case ARMISD::SRL_FLAG: return "ARMISD::SRL_FLAG"; + case ARMISD::SRA_FLAG: return "ARMISD::SRA_FLAG"; + case ARMISD::RRX: return "ARMISD::RRX"; + + case ARMISD::FMRRD: return "ARMISD::FMRRD"; + case ARMISD::FMDRR: return "ARMISD::FMDRR"; + + case ARMISD::THREAD_POINTER:return "ARMISD::THREAD_POINTER"; + } +} + +//===----------------------------------------------------------------------===// +// Lowering Code +//===----------------------------------------------------------------------===// + + +/// IntCCToARMCC - Convert a DAG integer condition code to an ARM CC +static ARMCC::CondCodes IntCCToARMCC(ISD::CondCode CC) { + switch (CC) { + default: assert(0 && "Unknown condition code!"); + case ISD::SETNE: return ARMCC::NE; + case ISD::SETEQ: return ARMCC::EQ; + case ISD::SETGT: return ARMCC::GT; + case ISD::SETGE: return ARMCC::GE; + case ISD::SETLT: return ARMCC::LT; + case ISD::SETLE: return ARMCC::LE; + case ISD::SETUGT: return ARMCC::HI; + case ISD::SETUGE: return ARMCC::HS; + case ISD::SETULT: return ARMCC::LO; + case ISD::SETULE: return ARMCC::LS; + } +} + +/// FPCCToARMCC - Convert a DAG fp condition code to an ARM CC. It +/// returns true if the operands should be inverted to form the proper +/// comparison. +static bool FPCCToARMCC(ISD::CondCode CC, ARMCC::CondCodes &CondCode, + ARMCC::CondCodes &CondCode2) { + bool Invert = false; + CondCode2 = ARMCC::AL; + switch (CC) { + default: assert(0 && "Unknown FP condition!"); + case ISD::SETEQ: + case ISD::SETOEQ: CondCode = ARMCC::EQ; break; + case ISD::SETGT: + case ISD::SETOGT: CondCode = ARMCC::GT; break; + case ISD::SETGE: + case ISD::SETOGE: CondCode = ARMCC::GE; break; + case ISD::SETOLT: CondCode = ARMCC::MI; break; + case ISD::SETOLE: CondCode = ARMCC::GT; Invert = true; break; + case ISD::SETONE: CondCode = ARMCC::MI; CondCode2 = ARMCC::GT; break; + case ISD::SETO: CondCode = ARMCC::VC; break; + case ISD::SETUO: CondCode = ARMCC::VS; break; + case ISD::SETUEQ: CondCode = ARMCC::EQ; CondCode2 = ARMCC::VS; break; + case ISD::SETUGT: CondCode = ARMCC::HI; break; + case ISD::SETUGE: CondCode = ARMCC::PL; break; + case ISD::SETLT: + case ISD::SETULT: CondCode = ARMCC::LT; break; + case ISD::SETLE: + case ISD::SETULE: CondCode = ARMCC::LE; break; + case ISD::SETNE: + case ISD::SETUNE: CondCode = ARMCC::NE; break; + } + return Invert; +} + +static void +HowToPassArgument(MVT::ValueType ObjectVT, unsigned NumGPRs, + unsigned StackOffset, unsigned &NeededGPRs, + unsigned &NeededStackSize, unsigned &GPRPad, + unsigned &StackPad, unsigned Flags) { + NeededStackSize = 0; + NeededGPRs = 0; + StackPad = 0; + GPRPad = 0; + unsigned align = (Flags >> ISD::ParamFlags::OrigAlignmentOffs); + GPRPad = NumGPRs % ((align + 3)/4); + StackPad = StackOffset % align; + unsigned firstGPR = NumGPRs + GPRPad; + switch (ObjectVT) { + default: assert(0 && "Unhandled argument type!"); + case MVT::i32: + case MVT::f32: + if (firstGPR < 4) + NeededGPRs = 1; + else + NeededStackSize = 4; + break; + case MVT::i64: + case MVT::f64: + if (firstGPR < 3) + NeededGPRs = 2; + else if (firstGPR == 3) { + NeededGPRs = 1; + NeededStackSize = 4; + } else + NeededStackSize = 8; + } +} + +/// LowerCALL - Lowering a ISD::CALL node into a callseq_start <- +/// ARMISD:CALL <- callseq_end chain. Also add input and output parameter +/// nodes. +SDOperand ARMTargetLowering::LowerCALL(SDOperand Op, SelectionDAG &DAG) { + MVT::ValueType RetVT= Op.Val->getValueType(0); + SDOperand Chain = Op.getOperand(0); + unsigned CallConv = cast<ConstantSDNode>(Op.getOperand(1))->getValue(); + assert((CallConv == CallingConv::C || + CallConv == CallingConv::Fast) && "unknown calling convention"); + SDOperand Callee = Op.getOperand(4); + unsigned NumOps = (Op.getNumOperands() - 5) / 2; + unsigned ArgOffset = 0; // Frame mechanisms handle retaddr slot + unsigned NumGPRs = 0; // GPRs used for parameter passing. + + // Count how many bytes are to be pushed on the stack. + unsigned NumBytes = 0; + + // Add up all the space actually used. + for (unsigned i = 0; i < NumOps; ++i) { + unsigned ObjSize; + unsigned ObjGPRs; + unsigned StackPad; + unsigned GPRPad; + MVT::ValueType ObjectVT = Op.getOperand(5+2*i).getValueType(); + unsigned Flags = Op.getConstantOperandVal(5+2*i+1); + HowToPassArgument(ObjectVT, NumGPRs, NumBytes, ObjGPRs, ObjSize, + GPRPad, StackPad, Flags); + NumBytes += ObjSize + StackPad; + NumGPRs += ObjGPRs + GPRPad; + } + + // Adjust the stack pointer for the new arguments... + // These operations are automatically eliminated by the prolog/epilog pass + Chain = DAG.getCALLSEQ_START(Chain, + DAG.getConstant(NumBytes, MVT::i32)); + + SDOperand StackPtr = DAG.getRegister(ARM::SP, MVT::i32); + + static const unsigned GPRArgRegs[] = { + ARM::R0, ARM::R1, ARM::R2, ARM::R3 + }; + + NumGPRs = 0; + std::vector<std::pair<unsigned, SDOperand> > RegsToPass; + std::vector<SDOperand> MemOpChains; + for (unsigned i = 0; i != NumOps; ++i) { + SDOperand Arg = Op.getOperand(5+2*i); + unsigned Flags = Op.getConstantOperandVal(5+2*i+1); + MVT::ValueType ArgVT = Arg.getValueType(); + + unsigned ObjSize; + unsigned ObjGPRs; + unsigned GPRPad; + unsigned StackPad; + HowToPassArgument(ArgVT, NumGPRs, ArgOffset, ObjGPRs, + ObjSize, GPRPad, StackPad, Flags); + NumGPRs += GPRPad; + ArgOffset += StackPad; + if (ObjGPRs > 0) { + switch (ArgVT) { + default: assert(0 && "Unexpected ValueType for argument!"); + case MVT::i32: + RegsToPass.push_back(std::make_pair(GPRArgRegs[NumGPRs], Arg)); + break; + case MVT::f32: + RegsToPass.push_back(std::make_pair(GPRArgRegs[NumGPRs], + DAG.getNode(ISD::BIT_CONVERT, MVT::i32, Arg))); + break; + case MVT::i64: { + SDOperand Lo = DAG.getNode(ISD::EXTRACT_ELEMENT, MVT::i32, Arg, + DAG.getConstant(0, getPointerTy())); + SDOperand Hi = DAG.getNode(ISD::EXTRACT_ELEMENT, MVT::i32, Arg, + DAG.getConstant(1, getPointerTy())); + RegsToPass.push_back(std::make_pair(GPRArgRegs[NumGPRs], Lo)); + if (ObjGPRs == 2) + RegsToPass.push_back(std::make_pair(GPRArgRegs[NumGPRs+1], Hi)); + else { + SDOperand PtrOff= DAG.getConstant(ArgOffset, StackPtr.getValueType()); + PtrOff = DAG.getNode(ISD::ADD, MVT::i32, StackPtr, PtrOff); + MemOpChains.push_back(DAG.getStore(Chain, Hi, PtrOff, NULL, 0)); + } + break; + } + case MVT::f64: { + SDOperand Cvt = DAG.getNode(ARMISD::FMRRD, + DAG.getVTList(MVT::i32, MVT::i32), + &Arg, 1); + RegsToPass.push_back(std::make_pair(GPRArgRegs[NumGPRs], Cvt)); + if (ObjGPRs == 2) + RegsToPass.push_back(std::make_pair(GPRArgRegs[NumGPRs+1], + Cvt.getValue(1))); + else { + SDOperand PtrOff= DAG.getConstant(ArgOffset, StackPtr.getValueType()); + PtrOff = DAG.getNode(ISD::ADD, MVT::i32, StackPtr, PtrOff); + MemOpChains.push_back(DAG.getStore(Chain, Cvt.getValue(1), PtrOff, + NULL, 0)); + } + break; + } + } + } else { + assert(ObjSize != 0); + SDOperand PtrOff = DAG.getConstant(ArgOffset, StackPtr.getValueType()); + PtrOff = DAG.getNode(ISD::ADD, MVT::i32, StackPtr, PtrOff); + MemOpChains.push_back(DAG.getStore(Chain, Arg, PtrOff, NULL, 0)); + } + + NumGPRs += ObjGPRs; + ArgOffset += ObjSize; + } + + if (!MemOpChains.empty()) + Chain = DAG.getNode(ISD::TokenFactor, MVT::Other, + &MemOpChains[0], MemOpChains.size()); + + // Build a sequence of copy-to-reg nodes chained together with token chain + // and flag operands which copy the outgoing args into the appropriate regs. + SDOperand InFlag; + for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) { + Chain = DAG.getCopyToReg(Chain, RegsToPass[i].first, RegsToPass[i].second, + InFlag); + InFlag = Chain.getValue(1); + } + + // If the callee is a GlobalAddress/ExternalSymbol node (quite common, every + // direct call is) turn it into a TargetGlobalAddress/TargetExternalSymbol + // node so that legalize doesn't hack it. + bool isDirect = false; + bool isARMFunc = false; + bool isLocalARMFunc = false; + if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) { + GlobalValue *GV = G->getGlobal(); + isDirect = true; + bool isExt = (GV->isDeclaration() || GV->hasWeakLinkage() || + GV->hasLinkOnceLinkage()); + bool isStub = (isExt && Subtarget->isTargetDarwin()) && + getTargetMachine().getRelocationModel() != Reloc::Static; + isARMFunc = !Subtarget->isThumb() || isStub; + // ARM call to a local ARM function is predicable. + isLocalARMFunc = !Subtarget->isThumb() && !isExt; + // tBX takes a register source operand. + if (isARMFunc && Subtarget->isThumb() && !Subtarget->hasV5TOps()) { + ARMConstantPoolValue *CPV = new ARMConstantPoolValue(GV, ARMPCLabelIndex, + ARMCP::CPStub, 4); + SDOperand CPAddr = DAG.getTargetConstantPool(CPV, getPointerTy(), 2); + CPAddr = DAG.getNode(ARMISD::Wrapper, MVT::i32, CPAddr); + Callee = DAG.getLoad(getPointerTy(), DAG.getEntryNode(), CPAddr, NULL, 0); + SDOperand PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32); + Callee = DAG.getNode(ARMISD::PIC_ADD, getPointerTy(), Callee, PICLabel); + } else + Callee = DAG.getTargetGlobalAddress(GV, getPointerTy()); + } else if (ExternalSymbolSDNode *S = dyn_cast<ExternalSymbolSDNode>(Callee)) { + isDirect = true; + bool isStub = Subtarget->isTargetDarwin() && + getTargetMachine().getRelocationModel() != Reloc::Static; + isARMFunc = !Subtarget->isThumb() || isStub; + // tBX takes a register source operand. + const char *Sym = S->getSymbol(); + if (isARMFunc && Subtarget->isThumb() && !Subtarget->hasV5TOps()) { + ARMConstantPoolValue *CPV = new ARMConstantPoolValue(Sym, ARMPCLabelIndex, + ARMCP::CPStub, 4); + SDOperand CPAddr = DAG.getTargetConstantPool(CPV, getPointerTy(), 2); + CPAddr = DAG.getNode(ARMISD::Wrapper, MVT::i32, CPAddr); + Callee = DAG.getLoad(getPointerTy(), DAG.getEntryNode(), CPAddr, NULL, 0); + SDOperand PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32); + Callee = DAG.getNode(ARMISD::PIC_ADD, getPointerTy(), Callee, PICLabel); + } else + Callee = DAG.getTargetExternalSymbol(Sym, getPointerTy()); + } + + // FIXME: handle tail calls differently. + unsigned CallOpc; + if (Subtarget->isThumb()) { + if (!Subtarget->hasV5TOps() && (!isDirect || isARMFunc)) + CallOpc = ARMISD::CALL_NOLINK; + else + CallOpc = isARMFunc ? ARMISD::CALL : ARMISD::tCALL; + } else { + CallOpc = (isDirect || Subtarget->hasV5TOps()) + ? (isLocalARMFunc ? ARMISD::CALL_PRED : ARMISD::CALL) + : ARMISD::CALL_NOLINK; + } + if (CallOpc == ARMISD::CALL_NOLINK && !Subtarget->isThumb()) { + // implicit def LR - LR mustn't be allocated as GRP:$dst of CALL_NOLINK + Chain = DAG.getCopyToReg(Chain, ARM::LR, + DAG.getNode(ISD::UNDEF, MVT::i32), InFlag); + InFlag = Chain.getValue(1); + } + + std::vector<MVT::ValueType> NodeTys; + NodeTys.push_back(MVT::Other); // Returns a chain + NodeTys.push_back(MVT::Flag); // Returns a flag for retval copy to use. + + std::vector<SDOperand> Ops; + Ops.push_back(Chain); + Ops.push_back(Callee); + + // Add argument registers to the end of the list so that they are known live + // into the call. + for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) + Ops.push_back(DAG.getRegister(RegsToPass[i].first, + RegsToPass[i].second.getValueType())); + + if (InFlag.Val) + Ops.push_back(InFlag); + Chain = DAG.getNode(CallOpc, NodeTys, &Ops[0], Ops.size()); + InFlag = Chain.getValue(1); + + SDOperand CSOps[] = { Chain, DAG.getConstant(NumBytes, MVT::i32), InFlag }; + Chain = DAG.getNode(ISD::CALLSEQ_END, + DAG.getNodeValueTypes(MVT::Other, MVT::Flag), + ((RetVT != MVT::Other) ? 2 : 1), CSOps, 3); + if (RetVT != MVT::Other) + InFlag = Chain.getValue(1); + + std::vector<SDOperand> ResultVals; + NodeTys.clear(); + + // If the call has results, copy the values out of the ret val registers. + switch (RetVT) { + default: assert(0 && "Unexpected ret value!"); + case MVT::Other: + break; + case MVT::i32: + Chain = DAG.getCopyFromReg(Chain, ARM::R0, MVT::i32, InFlag).getValue(1); + ResultVals.push_back(Chain.getValue(0)); + if (Op.Val->getValueType(1) == MVT::i32) { + // Returns a i64 value. + Chain = DAG.getCopyFromReg(Chain, ARM::R1, MVT::i32, + Chain.getValue(2)).getValue(1); + ResultVals.push_back(Chain.getValue(0)); + NodeTys.push_back(MVT::i32); + } + NodeTys.push_back(MVT::i32); + break; + case MVT::f32: + Chain = DAG.getCopyFromReg(Chain, ARM::R0, MVT::i32, InFlag).getValue(1); + ResultVals.push_back(DAG.getNode(ISD::BIT_CONVERT, MVT::f32, + Chain.getValue(0))); + NodeTys.push_back(MVT::f32); + break; + case MVT::f64: { + SDOperand Lo = DAG.getCopyFromReg(Chain, ARM::R0, MVT::i32, InFlag); + SDOperand Hi = DAG.getCopyFromReg(Lo, ARM::R1, MVT::i32, Lo.getValue(2)); + ResultVals.push_back(DAG.getNode(ARMISD::FMDRR, MVT::f64, Lo, Hi)); + NodeTys.push_back(MVT::f64); + break; + } + } + + NodeTys.push_back(MVT::Other); + + if (ResultVals.empty()) + return Chain; + + ResultVals.push_back(Chain); + SDOperand Res = DAG.getNode(ISD::MERGE_VALUES, NodeTys, &ResultVals[0], + ResultVals.size()); + return Res.getValue(Op.ResNo); +} + +static SDOperand LowerRET(SDOperand Op, SelectionDAG &DAG) { + SDOperand Copy; + SDOperand Chain = Op.getOperand(0); + switch(Op.getNumOperands()) { + default: + assert(0 && "Do not know how to return this many arguments!"); + abort(); + case 1: { + SDOperand LR = DAG.getRegister(ARM::LR, MVT::i32); + return DAG.getNode(ARMISD::RET_FLAG, MVT::Other, Chain); + } + case 3: + Op = Op.getOperand(1); + if (Op.getValueType() == MVT::f32) { + Op = DAG.getNode(ISD::BIT_CONVERT, MVT::i32, Op); + } else if (Op.getValueType() == MVT::f64) { + // Recursively legalize f64 -> i64. + Op = DAG.getNode(ISD::BIT_CONVERT, MVT::i64, Op); + return DAG.getNode(ISD::RET, MVT::Other, Chain, Op, + DAG.getConstant(0, MVT::i32)); + } + Copy = DAG.getCopyToReg(Chain, ARM::R0, Op, SDOperand()); + if (DAG.getMachineFunction().liveout_empty()) + DAG.getMachineFunction().addLiveOut(ARM::R0); + break; + case 5: + Copy = DAG.getCopyToReg(Chain, ARM::R1, Op.getOperand(3), SDOperand()); + Copy = DAG.getCopyToReg(Copy, ARM::R0, Op.getOperand(1), Copy.getValue(1)); + // If we haven't noted the R0+R1 are live out, do so now. + if (DAG.getMachineFunction().liveout_empty()) { + DAG.getMachineFunction().addLiveOut(ARM::R0); + DAG.getMachineFunction().addLiveOut(ARM::R1); + } + break; + } + + //We must use RET_FLAG instead of BRIND because BRIND doesn't have a flag + return DAG.getNode(ARMISD::RET_FLAG, MVT::Other, Copy, Copy.getValue(1)); +} + +// ConstantPool, JumpTable, GlobalAddress, and ExternalSymbol are lowered as +// their target countpart wrapped in the ARMISD::Wrapper node. Suppose N is +// one of the above mentioned nodes. It has to be wrapped because otherwise +// Select(N) returns N. So the raw TargetGlobalAddress nodes, etc. can only +// be used to form addressing mode. These wrapped nodes will be selected +// into MOVi. +static SDOperand LowerConstantPool(SDOperand Op, SelectionDAG &DAG) { + MVT::ValueType PtrVT = Op.getValueType(); + ConstantPoolSDNode *CP = cast<ConstantPoolSDNode>(Op); + SDOperand Res; + if (CP->isMachineConstantPoolEntry()) + Res = DAG.getTargetConstantPool(CP->getMachineCPVal(), PtrVT, + CP->getAlignment()); + else + Res = DAG.getTargetConstantPool(CP->getConstVal(), PtrVT, + CP->getAlignment()); + return DAG.getNode(ARMISD::Wrapper, MVT::i32, Res); +} + +// Lower ISD::GlobalTLSAddress using the "general dynamic" model +SDOperand +ARMTargetLowering::LowerToTLSGeneralDynamicModel(GlobalAddressSDNode *GA, + SelectionDAG &DAG) { + MVT::ValueType PtrVT = getPointerTy(); + unsigned char PCAdj = Subtarget->isThumb() ? 4 : 8; + ARMConstantPoolValue *CPV = + new ARMConstantPoolValue(GA->getGlobal(), ARMPCLabelIndex, ARMCP::CPValue, + PCAdj, "tlsgd", true); + SDOperand Argument = DAG.getTargetConstantPool(CPV, PtrVT, 2); + Argument = DAG.getNode(ARMISD::Wrapper, MVT::i32, Argument); + Argument = DAG.getLoad(PtrVT, DAG.getEntryNode(), Argument, NULL, 0); + SDOperand Chain = Argument.getValue(1); + + SDOperand PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32); + Argument = DAG.getNode(ARMISD::PIC_ADD, PtrVT, Argument, PICLabel); + + // call __tls_get_addr. + ArgListTy Args; + ArgListEntry Entry; + Entry.Node = Argument; + Entry.Ty = (const Type *) Type::Int32Ty; + Args.push_back(Entry); + std::pair<SDOperand, SDOperand> CallResult = + LowerCallTo(Chain, (const Type *) Type::Int32Ty, false, false, + CallingConv::C, false, + DAG.getExternalSymbol("__tls_get_addr", PtrVT), Args, DAG); + return CallResult.first; +} + +// Lower ISD::GlobalTLSAddress using the "initial exec" or +// "local exec" model. +SDOperand +ARMTargetLowering::LowerToTLSExecModels(GlobalAddressSDNode *GA, + SelectionDAG &DAG) { + GlobalValue *GV = GA->getGlobal(); + SDOperand Offset; + SDOperand Chain = DAG.getEntryNode(); + MVT::ValueType PtrVT = getPointerTy(); + // Get the Thread Pointer + SDOperand ThreadPointer = DAG.getNode(ARMISD::THREAD_POINTER, PtrVT); + + if (GV->isDeclaration()){ + // initial exec model + unsigned char PCAdj = Subtarget->isThumb() ? 4 : 8; + ARMConstantPoolValue *CPV = + new ARMConstantPoolValue(GA->getGlobal(), ARMPCLabelIndex, ARMCP::CPValue, + PCAdj, "gottpoff", true); + Offset = DAG.getTargetConstantPool(CPV, PtrVT, 2); + Offset = DAG.getNode(ARMISD::Wrapper, MVT::i32, Offset); + Offset = DAG.getLoad(PtrVT, Chain, Offset, NULL, 0); + Chain = Offset.getValue(1); + + SDOperand PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32); + Offset = DAG.getNode(ARMISD::PIC_ADD, PtrVT, Offset, PICLabel); + + Offset = DAG.getLoad(PtrVT, Chain, Offset, NULL, 0); + } else { + // local exec model + ARMConstantPoolValue *CPV = + new ARMConstantPoolValue(GV, ARMCP::CPValue, "tpoff"); + Offset = DAG.getTargetConstantPool(CPV, PtrVT, 2); + Offset = DAG.getNode(ARMISD::Wrapper, MVT::i32, Offset); + Offset = DAG.getLoad(PtrVT, Chain, Offset, NULL, 0); + } + + // The address of the thread local variable is the add of the thread + // pointer with the offset of the variable. + return DAG.getNode(ISD::ADD, PtrVT, ThreadPointer, Offset); +} + +SDOperand +ARMTargetLowering::LowerGlobalTLSAddress(SDOperand Op, SelectionDAG &DAG) { + // TODO: implement the "local dynamic" model + assert(Subtarget->isTargetELF() && + "TLS not implemented for non-ELF targets"); + GlobalAddressSDNode *GA = cast<GlobalAddressSDNode>(Op); + // If the relocation model is PIC, use the "General Dynamic" TLS Model, + // otherwise use the "Local Exec" TLS Model + if (getTargetMachine().getRelocationModel() == Reloc::PIC_) + return LowerToTLSGeneralDynamicModel(GA, DAG); + else + return LowerToTLSExecModels(GA, DAG); +} + +SDOperand ARMTargetLowering::LowerGlobalAddressELF(SDOperand Op, + SelectionDAG &DAG) { + MVT::ValueType PtrVT = getPointerTy(); + GlobalValue *GV = cast<GlobalAddressSDNode>(Op)->getGlobal(); + Reloc::Model RelocM = getTargetMachine().getRelocationModel(); + if (RelocM == Reloc::PIC_) { + bool UseGOTOFF = GV->hasInternalLinkage() || GV->hasHiddenVisibility(); + ARMConstantPoolValue *CPV = + new ARMConstantPoolValue(GV, ARMCP::CPValue, UseGOTOFF ? "GOTOFF":"GOT"); + SDOperand CPAddr = DAG.getTargetConstantPool(CPV, PtrVT, 2); + CPAddr = DAG.getNode(ARMISD::Wrapper, MVT::i32, CPAddr); + SDOperand Result = DAG.getLoad(PtrVT, DAG.getEntryNode(), CPAddr, NULL, 0); + SDOperand Chain = Result.getValue(1); + SDOperand GOT = DAG.getNode(ISD::GLOBAL_OFFSET_TABLE, PtrVT); + Result = DAG.getNode(ISD::ADD, PtrVT, Result, GOT); + if (!UseGOTOFF) + Result = DAG.getLoad(PtrVT, Chain, Result, NULL, 0); + return Result; + } else { + SDOperand CPAddr = DAG.getTargetConstantPool(GV, PtrVT, 2); + CPAddr = DAG.getNode(ARMISD::Wrapper, MVT::i32, CPAddr); + return DAG.getLoad(PtrVT, DAG.getEntryNode(), CPAddr, NULL, 0); + } +} + +/// GVIsIndirectSymbol - true if the GV will be accessed via an indirect symbol +/// even in non-static mode. +static bool GVIsIndirectSymbol(GlobalValue *GV, Reloc::Model RelocM) { + return RelocM != Reloc::Static && + (GV->hasWeakLinkage() || GV->hasLinkOnceLinkage() || + (GV->isDeclaration() && !GV->hasNotBeenReadFromBitcode())); +} + +SDOperand ARMTargetLowering::LowerGlobalAddressDarwin(SDOperand Op, + SelectionDAG &DAG) { + MVT::ValueType PtrVT = getPointerTy(); + GlobalValue *GV = cast<GlobalAddressSDNode>(Op)->getGlobal(); + Reloc::Model RelocM = getTargetMachine().getRelocationModel(); + bool IsIndirect = GVIsIndirectSymbol(GV, RelocM); + SDOperand CPAddr; + if (RelocM == Reloc::Static) + CPAddr = DAG.getTargetConstantPool(GV, PtrVT, 2); + else { + unsigned PCAdj = (RelocM != Reloc::PIC_) + ? 0 : (Subtarget->isThumb() ? 4 : 8); + ARMCP::ARMCPKind Kind = IsIndirect ? ARMCP::CPNonLazyPtr + : ARMCP::CPValue; + ARMConstantPoolValue *CPV = new ARMConstantPoolValue(GV, ARMPCLabelIndex, + Kind, PCAdj); + CPAddr = DAG.getTargetConstantPool(CPV, PtrVT, 2); + } + CPAddr = DAG.getNode(ARMISD::Wrapper, MVT::i32, CPAddr); + + SDOperand Result = DAG.getLoad(PtrVT, DAG.getEntryNode(), CPAddr, NULL, 0); + SDOperand Chain = Result.getValue(1); + + if (RelocM == Reloc::PIC_) { + SDOperand PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32); + Result = DAG.getNode(ARMISD::PIC_ADD, PtrVT, Result, PICLabel); + } + if (IsIndirect) + Result = DAG.getLoad(PtrVT, Chain, Result, NULL, 0); + + return Result; +} + +SDOperand ARMTargetLowering::LowerGLOBAL_OFFSET_TABLE(SDOperand Op, + SelectionDAG &DAG){ + assert(Subtarget->isTargetELF() && + "GLOBAL OFFSET TABLE not implemented for non-ELF targets"); + MVT::ValueType PtrVT = getPointerTy(); + unsigned PCAdj = Subtarget->isThumb() ? 4 : 8; + ARMConstantPoolValue *CPV = new ARMConstantPoolValue("_GLOBAL_OFFSET_TABLE_", + ARMPCLabelIndex, + ARMCP::CPValue, PCAdj); + SDOperand CPAddr = DAG.getTargetConstantPool(CPV, PtrVT, 2); + CPAddr = DAG.getNode(ARMISD::Wrapper, MVT::i32, CPAddr); + SDOperand Result = DAG.getLoad(PtrVT, DAG.getEntryNode(), CPAddr, NULL, 0); + SDOperand PICLabel = DAG.getConstant(ARMPCLabelIndex++, MVT::i32); + return DAG.getNode(ARMISD::PIC_ADD, PtrVT, Result, PICLabel); +} + +static SDOperand LowerVASTART(SDOperand Op, SelectionDAG &DAG, + unsigned VarArgsFrameIndex) { + // vastart just stores the address of the VarArgsFrameIndex slot into the + // memory location argument. + MVT::ValueType PtrVT = DAG.getTargetLoweringInfo().getPointerTy(); + SDOperand FR = DAG.getFrameIndex(VarArgsFrameIndex, PtrVT); + SrcValueSDNode *SV = cast<SrcValueSDNode>(Op.getOperand(2)); + return DAG.getStore(Op.getOperand(0), FR, Op.getOperand(1), SV->getValue(), + SV->getOffset()); +} + +static SDOperand LowerFORMAL_ARGUMENT(SDOperand Op, SelectionDAG &DAG, + unsigned *vRegs, unsigned ArgNo, + unsigned &NumGPRs, unsigned &ArgOffset) { + MachineFunction &MF = DAG.getMachineFunction(); + MVT::ValueType ObjectVT = Op.getValue(ArgNo).getValueType(); + SDOperand Root = Op.getOperand(0); + std::vector<SDOperand> ArgValues; + SSARegMap *RegMap = MF.getSSARegMap(); + + static const unsigned GPRArgRegs[] = { + ARM::R0, ARM::R1, ARM::R2, ARM::R3 + }; + + unsigned ObjSize; + unsigned ObjGPRs; + unsigned GPRPad; + unsigned StackPad; + unsigned Flags = Op.getConstantOperandVal(ArgNo + 3); + HowToPassArgument(ObjectVT, NumGPRs, ArgOffset, ObjGPRs, + ObjSize, GPRPad, StackPad, Flags); + NumGPRs += GPRPad; + ArgOffset += StackPad; + + SDOperand ArgValue; + if (ObjGPRs == 1) { + unsigned VReg = RegMap->createVirtualRegister(&ARM::GPRRegClass); + MF.addLiveIn(GPRArgRegs[NumGPRs], VReg); + vRegs[NumGPRs] = VReg; + ArgValue = DAG.getCopyFromReg(Root, VReg, MVT::i32); + if (ObjectVT == MVT::f32) + ArgValue = DAG.getNode(ISD::BIT_CONVERT, MVT::f32, ArgValue); + } else if (ObjGPRs == 2) { + unsigned VReg = RegMap->createVirtualRegister(&ARM::GPRRegClass); + MF.addLiveIn(GPRArgRegs[NumGPRs], VReg); + vRegs[NumGPRs] = VReg; + ArgValue = DAG.getCopyFromReg(Root, VReg, MVT::i32); + + VReg = RegMap->createVirtualRegister(&ARM::GPRRegClass); + MF.addLiveIn(GPRArgRegs[NumGPRs+1], VReg); + vRegs[NumGPRs+1] = VReg; + SDOperand ArgValue2 = DAG.getCopyFromReg(Root, VReg, MVT::i32); + + if (ObjectVT == MVT::i64) + ArgValue = DAG.getNode(ISD::BUILD_PAIR, MVT::i64, ArgValue, ArgValue2); + else + ArgValue = DAG.getNode(ARMISD::FMDRR, MVT::f64, ArgValue, ArgValue2); + } + NumGPRs += ObjGPRs; + + if (ObjSize) { + // If the argument is actually used, emit a load from the right stack + // slot. + if (!Op.Val->hasNUsesOfValue(0, ArgNo)) { + MachineFrameInfo *MFI = MF.getFrameInfo(); + int FI = MFI->CreateFixedObject(ObjSize, ArgOffset); + SDOperand FIN = DAG.getFrameIndex(FI, MVT::i32); + if (ObjGPRs == 0) + ArgValue = DAG.getLoad(ObjectVT, Root, FIN, NULL, 0); + else { + SDOperand ArgValue2 = + DAG.getLoad(MVT::i32, Root, FIN, NULL, 0); + if (ObjectVT == MVT::i64) + ArgValue= DAG.getNode(ISD::BUILD_PAIR, MVT::i64, ArgValue, ArgValue2); + else + ArgValue= DAG.getNode(ARMISD::FMDRR, MVT::f64, ArgValue, ArgValue2); + } + } else { + // Don't emit a dead load. + ArgValue = DAG.getNode(ISD::UNDEF, ObjectVT); + } + + ArgOffset += ObjSize; // Move on to the next argument. + } + + return ArgValue; +} + +SDOperand +ARMTargetLowering::LowerFORMAL_ARGUMENTS(SDOperand Op, SelectionDAG &DAG) { + std::vector<SDOperand> ArgValues; + SDOperand Root = Op.getOperand(0); + unsigned ArgOffset = 0; // Frame mechanisms handle retaddr slot + unsigned NumGPRs = 0; // GPRs used for parameter passing. + unsigned VRegs[4]; + + unsigned NumArgs = Op.Val->getNumValues()-1; + for (unsigned ArgNo = 0; ArgNo < NumArgs; ++ArgNo) + ArgValues.push_back(LowerFORMAL_ARGUMENT(Op, DAG, VRegs, ArgNo, + NumGPRs, ArgOffset)); + + bool isVarArg = cast<ConstantSDNode>(Op.getOperand(2))->getValue() != 0; + if (isVarArg) { + static const unsigned GPRArgRegs[] = { + ARM::R0, ARM::R1, ARM::R2, ARM::R3 + }; + + MachineFunction &MF = DAG.getMachineFunction(); + SSARegMap *RegMap = MF.getSSARegMap(); + MachineFrameInfo *MFI = MF.getFrameInfo(); + ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>(); + unsigned Align = MF.getTarget().getFrameInfo()->getStackAlignment(); + unsigned VARegSize = (4 - NumGPRs) * 4; + unsigned VARegSaveSize = (VARegSize + Align - 1) & ~(Align - 1); + if (VARegSaveSize) { + // If this function is vararg, store any remaining integer argument regs + // to their spots on the stack so that they may be loaded by deferencing + // the result of va_next. + AFI->setVarArgsRegSaveSize(VARegSaveSize); + VarArgsFrameIndex = MFI->CreateFixedObject(VARegSaveSize, ArgOffset + + VARegSaveSize - VARegSize); + SDOperand FIN = DAG.getFrameIndex(VarArgsFrameIndex, getPointerTy()); + + SmallVector<SDOperand, 4> MemOps; + for (; NumGPRs < 4; ++NumGPRs) { + unsigned VReg = RegMap->createVirtualRegister(&ARM::GPRRegClass); + MF.addLiveIn(GPRArgRegs[NumGPRs], VReg); + SDOperand Val = DAG.getCopyFromReg(Root, VReg, MVT::i32); + SDOperand Store = DAG.getStore(Val.getValue(1), Val, FIN, NULL, 0); + MemOps.push_back(Store); + FIN = DAG.getNode(ISD::ADD, getPointerTy(), FIN, + DAG.getConstant(4, getPointerTy())); + } + if (!MemOps.empty()) + Root = DAG.getNode(ISD::TokenFactor, MVT::Other, + &MemOps[0], MemOps.size()); + } else + // This will point to the next argument passed via stack. + VarArgsFrameIndex = MFI->CreateFixedObject(4, ArgOffset); + } + + ArgValues.push_back(Root); + + // Return the new list of results. + std::vector<MVT::ValueType> RetVT(Op.Val->value_begin(), + Op.Val->value_end()); + return DAG.getNode(ISD::MERGE_VALUES, RetVT, &ArgValues[0], ArgValues.size()); +} + +/// isFloatingPointZero - Return true if this is +0.0. +static bool isFloatingPointZero(SDOperand Op) { + if (ConstantFPSDNode *CFP = dyn_cast<ConstantFPSDNode>(Op)) + return CFP->isExactlyValue(0.0); + else if (ISD::isEXTLoad(Op.Val) || ISD::isNON_EXTLoad(Op.Val)) { + // Maybe this has already been legalized into the constant pool? + if (Op.getOperand(1).getOpcode() == ARMISD::Wrapper) { + SDOperand WrapperOp = Op.getOperand(1).getOperand(0); + if (ConstantPoolSDNode *CP = dyn_cast<ConstantPoolSDNode>(WrapperOp)) + if (ConstantFP *CFP = dyn_cast<ConstantFP>(CP->getConstVal())) + return CFP->isExactlyValue(0.0); + } + } + return false; +} + +static bool isLegalCmpImmediate(unsigned C, bool isThumb) { + return ( isThumb && (C & ~255U) == 0) || + (!isThumb && ARM_AM::getSOImmVal(C) != -1); +} + +/// Returns appropriate ARM CMP (cmp) and corresponding condition code for +/// the given operands. +static SDOperand getARMCmp(SDOperand LHS, SDOperand RHS, ISD::CondCode CC, + SDOperand &ARMCC, SelectionDAG &DAG, bool isThumb) { + if (ConstantSDNode *RHSC = dyn_cast<ConstantSDNode>(RHS.Val)) { + unsigned C = RHSC->getValue(); + if (!isLegalCmpImmediate(C, isThumb)) { + // Constant does not fit, try adjusting it by one? + switch (CC) { + default: break; + case ISD::SETLT: + case ISD::SETGE: + if (isLegalCmpImmediate(C-1, isThumb)) { + CC = (CC == ISD::SETLT) ? ISD::SETLE : ISD::SETGT; + RHS = DAG.getConstant(C-1, MVT::i32); + } + break; + case ISD::SETULT: + case ISD::SETUGE: + if (C > 0 && isLegalCmpImmediate(C-1, isThumb)) { + CC = (CC == ISD::SETULT) ? ISD::SETULE : ISD::SETUGT; + RHS = DAG.getConstant(C-1, MVT::i32); + } + break; + case ISD::SETLE: + case ISD::SETGT: + if (isLegalCmpImmediate(C+1, isThumb)) { + CC = (CC == ISD::SETLE) ? ISD::SETLT : ISD::SETGE; + RHS = DAG.getConstant(C+1, MVT::i32); + } + break; + case ISD::SETULE: + case ISD::SETUGT: + if (C < 0xffffffff && isLegalCmpImmediate(C+1, isThumb)) { + CC = (CC == ISD::SETULE) ? ISD::SETULT : ISD::SETUGE; + RHS = DAG.getConstant(C+1, MVT::i32); + } + break; + } + } + } + + ARMCC::CondCodes CondCode = IntCCToARMCC(CC); + ARMISD::NodeType CompareType; + switch (CondCode) { + default: + CompareType = ARMISD::CMP; + break; + case ARMCC::EQ: + case ARMCC::NE: + case ARMCC::MI: + case ARMCC::PL: + // Uses only N and Z Flags + CompareType = ARMISD::CMPNZ; + break; + } + ARMCC = DAG.getConstant(CondCode, MVT::i32); + return DAG.getNode(CompareType, MVT::Flag, LHS, RHS); +} + +/// Returns a appropriate VFP CMP (fcmp{s|d}+fmstat) for the given operands. +static SDOperand getVFPCmp(SDOperand LHS, SDOperand RHS, SelectionDAG &DAG) { + SDOperand Cmp; + if (!isFloatingPointZero(RHS)) + Cmp = DAG.getNode(ARMISD::CMPFP, MVT::Flag, LHS, RHS); + else + Cmp = DAG.getNode(ARMISD::CMPFPw0, MVT::Flag, LHS); + return DAG.getNode(ARMISD::FMSTAT, MVT::Flag, Cmp); +} + +static SDOperand LowerSELECT_CC(SDOperand Op, SelectionDAG &DAG, + const ARMSubtarget *ST) { + MVT::ValueType VT = Op.getValueType(); + SDOperand LHS = Op.getOperand(0); + SDOperand RHS = Op.getOperand(1); + ISD::CondCode CC = cast<CondCodeSDNode>(Op.getOperand(4))->get(); + SDOperand TrueVal = Op.getOperand(2); + SDOperand FalseVal = Op.getOperand(3); + + if (LHS.getValueType() == MVT::i32) { + SDOperand ARMCC; + SDOperand CCR = DAG.getRegister(ARM::CPSR, MVT::i32); + SDOperand Cmp = getARMCmp(LHS, RHS, CC, ARMCC, DAG, ST->isThumb()); + return DAG.getNode(ARMISD::CMOV, VT, FalseVal, TrueVal, ARMCC, CCR, Cmp); + } + + ARMCC::CondCodes CondCode, CondCode2; + if (FPCCToARMCC(CC, CondCode, CondCode2)) + std::swap(TrueVal, FalseVal); + + SDOperand ARMCC = DAG.getConstant(CondCode, MVT::i32); + SDOperand CCR = DAG.getRegister(ARM::CPSR, MVT::i32); + SDOperand Cmp = getVFPCmp(LHS, RHS, DAG); + SDOperand Result = DAG.getNode(ARMISD::CMOV, VT, FalseVal, TrueVal, + ARMCC, CCR, Cmp); + if (CondCode2 != ARMCC::AL) { + SDOperand ARMCC2 = DAG.getConstant(CondCode2, MVT::i32); + // FIXME: Needs another CMP because flag can have but one use. + SDOperand Cmp2 = getVFPCmp(LHS, RHS, DAG); + Result = DAG.getNode(ARMISD::CMOV, VT, Result, TrueVal, ARMCC2, CCR, Cmp2); + } + return Result; +} + +static SDOperand LowerBR_CC(SDOperand Op, SelectionDAG &DAG, + const ARMSubtarget *ST) { + SDOperand Chain = Op.getOperand(0); + ISD::CondCode CC = cast<CondCodeSDNode>(Op.getOperand(1))->get(); + SDOperand LHS = Op.getOperand(2); + SDOperand RHS = Op.getOperand(3); + SDOperand Dest = Op.getOperand(4); + + if (LHS.getValueType() == MVT::i32) { + SDOperand ARMCC; + SDOperand CCR = DAG.getRegister(ARM::CPSR, MVT::i32); + SDOperand Cmp = getARMCmp(LHS, RHS, CC, ARMCC, DAG, ST->isThumb()); + return DAG.getNode(ARMISD::BRCOND, MVT::Other, Chain, Dest, ARMCC, CCR,Cmp); + } + + assert(LHS.getValueType() == MVT::f32 || LHS.getValueType() == MVT::f64); + ARMCC::CondCodes CondCode, CondCode2; + if (FPCCToARMCC(CC, CondCode, CondCode2)) + // Swap the LHS/RHS of the comparison if needed. + std::swap(LHS, RHS); + + SDOperand Cmp = getVFPCmp(LHS, RHS, DAG); + SDOperand ARMCC = DAG.getConstant(CondCode, MVT::i32); + SDOperand CCR = DAG.getRegister(ARM::CPSR, MVT::i32); + SDVTList VTList = DAG.getVTList(MVT::Other, MVT::Flag); + SDOperand Ops[] = { Chain, Dest, ARMCC, CCR, Cmp }; + SDOperand Res = DAG.getNode(ARMISD::BRCOND, VTList, Ops, 5); + if (CondCode2 != ARMCC::AL) { + ARMCC = DAG.getConstant(CondCode2, MVT::i32); + SDOperand Ops[] = { Res, Dest, ARMCC, CCR, Res.getValue(1) }; + Res = DAG.getNode(ARMISD::BRCOND, VTList, Ops, 5); + } + return Res; +} + +SDOperand ARMTargetLowering::LowerBR_JT(SDOperand Op, SelectionDAG &DAG) { + SDOperand Chain = Op.getOperand(0); + SDOperand Table = Op.getOperand(1); + SDOperand Index = Op.getOperand(2); + + MVT::ValueType PTy = getPointerTy(); + JumpTableSDNode *JT = cast<JumpTableSDNode>(Table); + ARMFunctionInfo *AFI = DAG.getMachineFunction().getInfo<ARMFunctionInfo>(); + SDOperand UId = DAG.getConstant(AFI->createJumpTableUId(), PTy); + SDOperand JTI = DAG.getTargetJumpTable(JT->getIndex(), PTy); + Table = DAG.getNode(ARMISD::WrapperJT, MVT::i32, JTI, UId); + Index = DAG.getNode(ISD::MUL, PTy, Index, DAG.getConstant(4, PTy)); + SDOperand Addr = DAG.getNode(ISD::ADD, PTy, Index, Table); + bool isPIC = getTargetMachine().getRelocationModel() == Reloc::PIC_; + Addr = DAG.getLoad(isPIC ? (MVT::ValueType)MVT::i32 : PTy, + Chain, Addr, NULL, 0); + Chain = Addr.getValue(1); + if (isPIC) + Addr = DAG.getNode(ISD::ADD, PTy, Addr, Table); + return DAG.getNode(ARMISD::BR_JT, MVT::Other, Chain, Addr, JTI, UId); +} + +static SDOperand LowerFP_TO_INT(SDOperand Op, SelectionDAG &DAG) { + unsigned Opc = + Op.getOpcode() == ISD::FP_TO_SINT ? ARMISD::FTOSI : ARMISD::FTOUI; + Op = DAG.getNode(Opc, MVT::f32, Op.getOperand(0)); + return DAG.getNode(ISD::BIT_CONVERT, MVT::i32, Op); +} + +static SDOperand LowerINT_TO_FP(SDOperand Op, SelectionDAG &DAG) { + MVT::ValueType VT = Op.getValueType(); + unsigned Opc = + Op.getOpcode() == ISD::SINT_TO_FP ? ARMISD::SITOF : ARMISD::UITOF; + + Op = DAG.getNode(ISD::BIT_CONVERT, MVT::f32, Op.getOperand(0)); + return DAG.getNode(Opc, VT, Op); +} + +static SDOperand LowerFCOPYSIGN(SDOperand Op, SelectionDAG &DAG) { + // Implement fcopysign with a fabs and a conditional fneg. + SDOperand Tmp0 = Op.getOperand(0); + SDOperand Tmp1 = Op.getOperand(1); + MVT::ValueType VT = Op.getValueType(); + MVT::ValueType SrcVT = Tmp1.getValueType(); + SDOperand AbsVal = DAG.getNode(ISD::FABS, VT, Tmp0); + SDOperand Cmp = getVFPCmp(Tmp1, DAG.getConstantFP(0.0, SrcVT), DAG); + SDOperand ARMCC = DAG.getConstant(ARMCC::LT, MVT::i32); + SDOperand CCR = DAG.getRegister(ARM::CPSR, MVT::i32); + return DAG.getNode(ARMISD::CNEG, VT, AbsVal, AbsVal, ARMCC, CCR, Cmp); +} + +static SDOperand LowerBIT_CONVERT(SDOperand Op, SelectionDAG &DAG) { + // Turn f64->i64 into FMRRD. + assert(Op.getValueType() == MVT::i64 && + Op.getOperand(0).getValueType() == MVT::f64); + + Op = Op.getOperand(0); + SDOperand Cvt = DAG.getNode(ARMISD::FMRRD, DAG.getVTList(MVT::i32, MVT::i32), + &Op, 1); + + // Merge the pieces into a single i64 value. + return DAG.getNode(ISD::BUILD_PAIR, MVT::i64, Cvt, Cvt.getValue(1)); +} + +static SDOperand LowerMUL(SDOperand Op, SelectionDAG &DAG) { + // FIXME: All this code is target-independent. Create a new target-indep + // MULHILO node and move this code to the legalizer. + // + assert(Op.getValueType() == MVT::i64 && "Only handles i64 expand right now!"); + + SDOperand LL = DAG.getNode(ISD::EXTRACT_ELEMENT, MVT::i32, Op.getOperand(0), + DAG.getConstant(0, MVT::i32)); + SDOperand RL = DAG.getNode(ISD::EXTRACT_ELEMENT, MVT::i32, Op.getOperand(1), + DAG.getConstant(0, MVT::i32)); + + unsigned LHSSB = DAG.ComputeNumSignBits(Op.getOperand(0)); + unsigned RHSSB = DAG.ComputeNumSignBits(Op.getOperand(1)); + + SDOperand Lo, Hi; + // Figure out how to lower this multiply. + if (LHSSB >= 33 && RHSSB >= 33) { + // If the input values are both sign extended, we can emit a mulhs+mul. + Lo = DAG.getNode(ISD::MUL, MVT::i32, LL, RL); + Hi = DAG.getNode(ISD::MULHS, MVT::i32, LL, RL); + } else if (LHSSB == 32 && RHSSB == 32 && + DAG.MaskedValueIsZero(Op.getOperand(0), 0xFFFFFFFF00000000ULL) && + DAG.MaskedValueIsZero(Op.getOperand(1), 0xFFFFFFFF00000000ULL)) { + // If the inputs are zero extended, use mulhu. + Lo = DAG.getNode(ISD::MUL, MVT::i32, LL, RL); + Hi = DAG.getNode(ISD::MULHU, MVT::i32, LL, RL); + } else { + SDOperand LH = DAG.getNode(ISD::EXTRACT_ELEMENT, MVT::i32, Op.getOperand(0), + DAG.getConstant(1, MVT::i32)); + SDOperand RH = DAG.getNode(ISD::EXTRACT_ELEMENT, MVT::i32, Op.getOperand(1), + DAG.getConstant(1, MVT::i32)); + + // Lo,Hi = umul LHS, RHS. + SDOperand Ops[] = { LL, RL }; + SDOperand UMul64 = DAG.getNode(ARMISD::MULHILOU, + DAG.getVTList(MVT::i32, MVT::i32), Ops, 2); + Lo = UMul64; + Hi = UMul64.getValue(1); + RH = DAG.getNode(ISD::MUL, MVT::i32, LL, RH); + LH = DAG.getNode(ISD::MUL, MVT::i32, LH, RL); + Hi = DAG.getNode(ISD::ADD, MVT::i32, Hi, RH); + Hi = DAG.getNode(ISD::ADD, MVT::i32, Hi, LH); + } + + // Merge the pieces into a single i64 value. + return DAG.getNode(ISD::BUILD_PAIR, MVT::i64, Lo, Hi); +} + +static SDOperand LowerMULHU(SDOperand Op, SelectionDAG &DAG) { + SDOperand Ops[] = { Op.getOperand(0), Op.getOperand(1) }; + return DAG.getNode(ARMISD::MULHILOU, + DAG.getVTList(MVT::i32, MVT::i32), Ops, 2).getValue(1); +} + +static SDOperand LowerMULHS(SDOperand Op, SelectionDAG &DAG) { + SDOperand Ops[] = { Op.getOperand(0), Op.getOperand(1) }; + return DAG.getNode(ARMISD::MULHILOS, + DAG.getVTList(MVT::i32, MVT::i32), Ops, 2).getValue(1); +} + +static SDOperand LowerSRx(SDOperand Op, SelectionDAG &DAG, + const ARMSubtarget *ST) { + assert(Op.getValueType() == MVT::i64 && + (Op.getOpcode() == ISD::SRL || Op.getOpcode() == ISD::SRA) && + "Unknown shift to lower!"); + + // We only lower SRA, SRL of 1 here, all others use generic lowering. + if (!isa<ConstantSDNode>(Op.getOperand(1)) || + cast<ConstantSDNode>(Op.getOperand(1))->getValue() != 1) + return SDOperand(); + + // If we are in thumb mode, we don't have RRX. + if (ST->isThumb()) return SDOperand(); + + // Okay, we have a 64-bit SRA or SRL of 1. Lower this to an RRX expr. + SDOperand Lo = DAG.getNode(ISD::EXTRACT_ELEMENT, MVT::i32, Op.getOperand(0), + DAG.getConstant(0, MVT::i32)); + SDOperand Hi = DAG.getNode(ISD::EXTRACT_ELEMENT, MVT::i32, Op.getOperand(0), + DAG.getConstant(1, MVT::i32)); + + // First, build a SRA_FLAG/SRL_FLAG op, which shifts the top part by one and + // captures the result into a carry flag. + unsigned Opc = Op.getOpcode() == ISD::SRL ? ARMISD::SRL_FLAG:ARMISD::SRA_FLAG; + Hi = DAG.getNode(Opc, DAG.getVTList(MVT::i32, MVT::Flag), &Hi, 1); + + // The low part is an ARMISD::RRX operand, which shifts the carry in. + Lo = DAG.getNode(ARMISD::RRX, MVT::i32, Lo, Hi.getValue(1)); + + // Merge the pieces into a single i64 value. + return DAG.getNode(ISD::BUILD_PAIR, MVT::i64, Lo, Hi); +} + +SDOperand ARMTargetLowering::LowerMEMCPY(SDOperand Op, SelectionDAG &DAG) { + SDOperand Chain = Op.getOperand(0); + SDOperand Dest = Op.getOperand(1); + SDOperand Src = Op.getOperand(2); + SDOperand Count = Op.getOperand(3); + unsigned Align = + (unsigned)cast<ConstantSDNode>(Op.getOperand(4))->getValue(); + if (Align == 0) Align = 1; + + ConstantSDNode *I = dyn_cast<ConstantSDNode>(Count); + // Just call memcpy if: + // not 4-byte aligned + // size is unknown + // size is >= the threshold. + if ((Align & 3) != 0 || + !I || + I->getValue() >= 64 || + (I->getValue() & 3) != 0) { + MVT::ValueType IntPtr = getPointerTy(); + TargetLowering::ArgListTy Args; + TargetLowering::ArgListEntry Entry; + Entry.Ty = getTargetData()->getIntPtrType(); + Entry.Node = Op.getOperand(1); Args.push_back(Entry); + Entry.Node = Op.getOperand(2); Args.push_back(Entry); + Entry.Node = Op.getOperand(3); Args.push_back(Entry); + std::pair<SDOperand,SDOperand> CallResult = + LowerCallTo(Chain, Type::VoidTy, false, false, CallingConv::C, false, + DAG.getExternalSymbol("memcpy", IntPtr), Args, DAG); + return CallResult.second; + } + + // Otherwise do repeated 4-byte loads and stores. To be improved. + assert((I->getValue() & 3) == 0); + assert((Align & 3) == 0); + unsigned NumMemOps = I->getValue() >> 2; + unsigned EmittedNumMemOps = 0; + unsigned SrcOff = 0, DstOff = 0; + MVT::ValueType VT = MVT::i32; + unsigned VTSize = 4; + const unsigned MAX_LOADS_IN_LDM = 6; + SDOperand LoadChains[MAX_LOADS_IN_LDM]; + SDOperand Loads[MAX_LOADS_IN_LDM]; + + // Emit up to 4 loads, then a TokenFactor barrier, then the same + // number of stores. The loads and stores will get combined into + // ldm/stm later on. + while(EmittedNumMemOps < NumMemOps) { + unsigned i; + for (i=0; i<MAX_LOADS_IN_LDM && EmittedNumMemOps+i < NumMemOps; i++) { + Loads[i] = DAG.getLoad(VT, Chain, + DAG.getNode(ISD::ADD, VT, Src, + DAG.getConstant(SrcOff, VT)), + NULL, 0); + LoadChains[i] = Loads[i].getValue(1); + SrcOff += VTSize; + } + + Chain = DAG.getNode(ISD::TokenFactor, MVT::Other, &LoadChains[0], i); + + for (i=0; i<MAX_LOADS_IN_LDM && EmittedNumMemOps+i < NumMemOps; i++) { + Chain = DAG.getStore(Chain, Loads[i], + DAG.getNode(ISD::ADD, VT, Dest, + DAG.getConstant(DstOff, VT)), + NULL, 0); + DstOff += VTSize; + } + EmittedNumMemOps += i; + } + + return Chain; +} + +SDOperand ARMTargetLowering::LowerOperation(SDOperand Op, SelectionDAG &DAG) { + switch (Op.getOpcode()) { + default: assert(0 && "Don't know how to custom lower this!"); abort(); + case ISD::ConstantPool: return LowerConstantPool(Op, DAG); + case ISD::GlobalAddress: + return Subtarget->isTargetDarwin() ? LowerGlobalAddressDarwin(Op, DAG) : + LowerGlobalAddressELF(Op, DAG); + case ISD::GlobalTLSAddress: return LowerGlobalTLSAddress(Op, DAG); + case ISD::CALL: return LowerCALL(Op, DAG); + case ISD::RET: return LowerRET(Op, DAG); + case ISD::SELECT_CC: return LowerSELECT_CC(Op, DAG, Subtarget); + case ISD::BR_CC: return LowerBR_CC(Op, DAG, Subtarget); + case ISD::BR_JT: return LowerBR_JT(Op, DAG); + case ISD::VASTART: return LowerVASTART(Op, DAG, VarArgsFrameIndex); + case ISD::SINT_TO_FP: + case ISD::UINT_TO_FP: return LowerINT_TO_FP(Op, DAG); + case ISD::FP_TO_SINT: + case ISD::FP_TO_UINT: return LowerFP_TO_INT(Op, DAG); + case ISD::FCOPYSIGN: return LowerFCOPYSIGN(Op, DAG); + case ISD::BIT_CONVERT: return LowerBIT_CONVERT(Op, DAG); + case ISD::MUL: return LowerMUL(Op, DAG); + case ISD::MULHU: return LowerMULHU(Op, DAG); + case ISD::MULHS: return LowerMULHS(Op, DAG); + case ISD::SRL: + case ISD::SRA: return LowerSRx(Op, DAG, Subtarget); + case ISD::FORMAL_ARGUMENTS: + return LowerFORMAL_ARGUMENTS(Op, DAG); + case ISD::RETURNADDR: break; + case ISD::FRAMEADDR: break; + case ISD::GLOBAL_OFFSET_TABLE: return LowerGLOBAL_OFFSET_TABLE(Op, DAG); + case ISD::MEMCPY: return LowerMEMCPY(Op, DAG); + } + return SDOperand(); +} + +//===----------------------------------------------------------------------===// +// ARM Scheduler Hooks +//===----------------------------------------------------------------------===// + +MachineBasicBlock * +ARMTargetLowering::InsertAtEndOfBasicBlock(MachineInstr *MI, + MachineBasicBlock *BB) { + const TargetInstrInfo *TII = getTargetMachine().getInstrInfo(); + switch (MI->getOpcode()) { + default: assert(false && "Unexpected instr type to insert"); + case ARM::tMOVCCr: { + // To "insert" a SELECT_CC instruction, we actually have to insert the + // diamond control-flow pattern. The incoming instruction knows the + // destination vreg to set, the condition code register to branch on, the + // true/false values to select between, and a branch opcode to use. + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + ilist<MachineBasicBlock>::iterator It = BB; + ++It; + + // thisMBB: + // ... + // TrueVal = ... + // cmpTY ccX, r1, r2 + // bCC copy1MBB + // fallthrough --> copy0MBB + MachineBasicBlock *thisMBB = BB; + MachineBasicBlock *copy0MBB = new MachineBasicBlock(LLVM_BB); + MachineBasicBlock *sinkMBB = new MachineBasicBlock(LLVM_BB); + BuildMI(BB, TII->get(ARM::tBcc)).addMBB(sinkMBB) + .addImm(MI->getOperand(3).getImm()).addReg(MI->getOperand(4).getReg()); + MachineFunction *F = BB->getParent(); + F->getBasicBlockList().insert(It, copy0MBB); + F->getBasicBlockList().insert(It, sinkMBB); + // Update machine-CFG edges by first adding all successors of the current + // block to the new block which will contain the Phi node for the select. + for(MachineBasicBlock::succ_iterator i = BB->succ_begin(), + e = BB->succ_end(); i != e; ++i) + sinkMBB->addSuccessor(*i); + // Next, remove all successors of the current block, and add the true + // and fallthrough blocks as its successors. + while(!BB->succ_empty()) + BB->removeSuccessor(BB->succ_begin()); + BB->addSuccessor(copy0MBB); + BB->addSuccessor(sinkMBB); + + // copy0MBB: + // %FalseValue = ... + // # fallthrough to sinkMBB + BB = copy0MBB; + + // Update machine-CFG edges + BB->addSuccessor(sinkMBB); + + // sinkMBB: + // %Result = phi [ %FalseValue, copy0MBB ], [ %TrueValue, thisMBB ] + // ... + BB = sinkMBB; + BuildMI(BB, TII->get(ARM::PHI), MI->getOperand(0).getReg()) + .addReg(MI->getOperand(1).getReg()).addMBB(copy0MBB) + .addReg(MI->getOperand(2).getReg()).addMBB(thisMBB); + + delete MI; // The pseudo instruction is gone now. + return BB; + } + } +} + +//===----------------------------------------------------------------------===// +// ARM Optimization Hooks +//===----------------------------------------------------------------------===// + +/// isLegalAddressImmediate - Return true if the integer value can be used +/// as the offset of the target addressing mode for load / store of the +/// given type. +static bool isLegalAddressImmediate(int64_t V, MVT::ValueType VT, + const ARMSubtarget *Subtarget) { + if (V == 0) + return true; + + if (Subtarget->isThumb()) { + if (V < 0) + return false; + + unsigned Scale = 1; + switch (VT) { + default: return false; + case MVT::i1: + case MVT::i8: + // Scale == 1; + break; + case MVT::i16: + // Scale == 2; + Scale = 2; + break; + case MVT::i32: + // Scale == 4; + Scale = 4; + break; + } + + if ((V & (Scale - 1)) != 0) + return false; + V /= Scale; + return V == V & ((1LL << 5) - 1); + } + + if (V < 0) + V = - V; + switch (VT) { + default: return false; + case MVT::i1: + case MVT::i8: + case MVT::i32: + // +- imm12 + return V == V & ((1LL << 12) - 1); + case MVT::i16: + // +- imm8 + return V == V & ((1LL << 8) - 1); + case MVT::f32: + case MVT::f64: + if (!Subtarget->hasVFP2()) + return false; + if ((V & 3) != 0) + return false; + V >>= 2; + return V == V & ((1LL << 8) - 1); + } +} + +/// isLegalAddressingMode - Return true if the addressing mode represented +/// by AM is legal for this target, for a load/store of the specified type. +bool ARMTargetLowering::isLegalAddressingMode(const AddrMode &AM, + const Type *Ty) const { + if (!isLegalAddressImmediate(AM.BaseOffs, getValueType(Ty), Subtarget)) + return false; + + // Can never fold addr of global into load/store. + if (AM.BaseGV) + return false; + + switch (AM.Scale) { + case 0: // no scale reg, must be "r+i" or "r", or "i". + break; + case 1: + if (Subtarget->isThumb()) + return false; + // FALL THROUGH. + default: + // ARM doesn't support any R+R*scale+imm addr modes. + if (AM.BaseOffs) + return false; + + int Scale = AM.Scale; + switch (getValueType(Ty)) { + default: return false; + case MVT::i1: + case MVT::i8: + case MVT::i32: + case MVT::i64: + // This assumes i64 is legalized to a pair of i32. If not (i.e. + // ldrd / strd are used, then its address mode is same as i16. + // r + r + if (Scale < 0) Scale = -Scale; + if (Scale == 1) + return true; + // r + r << imm + return isPowerOf2_32(Scale & ~1); + case MVT::i16: + // r + r + if (((unsigned)AM.HasBaseReg + Scale) <= 2) + return true; + return false; + + case MVT::isVoid: + // Note, we allow "void" uses (basically, uses that aren't loads or + // stores), because arm allows folding a scale into many arithmetic + // operations. This should be made more precise and revisited later. + + // Allow r << imm, but the imm has to be a multiple of two. + if (AM.Scale & 1) return false; + return isPowerOf2_32(AM.Scale); + } + break; + } + return true; +} + + +static bool getIndexedAddressParts(SDNode *Ptr, MVT::ValueType VT, + bool isSEXTLoad, SDOperand &Base, + SDOperand &Offset, bool &isInc, + SelectionDAG &DAG) { + if (Ptr->getOpcode() != ISD::ADD && Ptr->getOpcode() != ISD::SUB) + return false; + + if (VT == MVT::i16 || ((VT == MVT::i8 || VT == MVT::i1) && isSEXTLoad)) { + // AddressingMode 3 + Base = Ptr->getOperand(0); + if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(Ptr->getOperand(1))) { + int RHSC = (int)RHS->getValue(); + if (RHSC < 0 && RHSC > -256) { + isInc = false; + Offset = DAG.getConstant(-RHSC, RHS->getValueType(0)); + return true; + } + } + isInc = (Ptr->getOpcode() == ISD::ADD); + Offset = Ptr->getOperand(1); + return true; + } else if (VT == MVT::i32 || VT == MVT::i8 || VT == MVT::i1) { + // AddressingMode 2 + if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(Ptr->getOperand(1))) { + int RHSC = (int)RHS->getValue(); + if (RHSC < 0 && RHSC > -0x1000) { + isInc = false; + Offset = DAG.getConstant(-RHSC, RHS->getValueType(0)); + Base = Ptr->getOperand(0); + return true; + } + } + + if (Ptr->getOpcode() == ISD::ADD) { + isInc = true; + ARM_AM::ShiftOpc ShOpcVal= ARM_AM::getShiftOpcForNode(Ptr->getOperand(0)); + if (ShOpcVal != ARM_AM::no_shift) { + Base = Ptr->getOperand(1); + Offset = Ptr->getOperand(0); + } else { + Base = Ptr->getOperand(0); + Offset = Ptr->getOperand(1); + } + return true; + } + + isInc = (Ptr->getOpcode() == ISD::ADD); + Base = Ptr->getOperand(0); + Offset = Ptr->getOperand(1); + return true; + } + + // FIXME: Use FLDM / FSTM to emulate indexed FP load / store. + return false; +} + +/// getPreIndexedAddressParts - returns true by value, base pointer and +/// offset pointer and addressing mode by reference if the node's address +/// can be legally represented as pre-indexed load / store address. +bool +ARMTargetLowering::getPreIndexedAddressParts(SDNode *N, SDOperand &Base, + SDOperand &Offset, + ISD::MemIndexedMode &AM, + SelectionDAG &DAG) { + if (Subtarget->isThumb()) + return false; + + MVT::ValueType VT; + SDOperand Ptr; + bool isSEXTLoad = false; + if (LoadSDNode *LD = dyn_cast<LoadSDNode>(N)) { + Ptr = LD->getBasePtr(); + VT = LD->getLoadedVT(); + isSEXTLoad = LD->getExtensionType() == ISD::SEXTLOAD; + } else if (StoreSDNode *ST = dyn_cast<StoreSDNode>(N)) { + Ptr = ST->getBasePtr(); + VT = ST->getStoredVT(); + } else + return false; + + bool isInc; + bool isLegal = getIndexedAddressParts(Ptr.Val, VT, isSEXTLoad, Base, Offset, + isInc, DAG); + if (isLegal) { + AM = isInc ? ISD::PRE_INC : ISD::PRE_DEC; + return true; + } + return false; +} + +/// getPostIndexedAddressParts - returns true by value, base pointer and +/// offset pointer and addressing mode by reference if this node can be +/// combined with a load / store to form a post-indexed load / store. +bool ARMTargetLowering::getPostIndexedAddressParts(SDNode *N, SDNode *Op, + SDOperand &Base, + SDOperand &Offset, + ISD::MemIndexedMode &AM, + SelectionDAG &DAG) { + if (Subtarget->isThumb()) + return false; + + MVT::ValueType VT; + SDOperand Ptr; + bool isSEXTLoad = false; + if (LoadSDNode *LD = dyn_cast<LoadSDNode>(N)) { + VT = LD->getLoadedVT(); + isSEXTLoad = LD->getExtensionType() == ISD::SEXTLOAD; + } else if (StoreSDNode *ST = dyn_cast<StoreSDNode>(N)) { + VT = ST->getStoredVT(); + } else + return false; + + bool isInc; + bool isLegal = getIndexedAddressParts(Op, VT, isSEXTLoad, Base, Offset, + isInc, DAG); + if (isLegal) { + AM = isInc ? ISD::POST_INC : ISD::POST_DEC; + return true; + } + return false; +} + +void ARMTargetLowering::computeMaskedBitsForTargetNode(const SDOperand Op, + uint64_t Mask, + uint64_t &KnownZero, + uint64_t &KnownOne, + const SelectionDAG &DAG, + unsigned Depth) const { + KnownZero = 0; + KnownOne = 0; + switch (Op.getOpcode()) { + default: break; + case ARMISD::CMOV: { + // Bits are known zero/one if known on the LHS and RHS. + DAG.ComputeMaskedBits(Op.getOperand(0), Mask, KnownZero, KnownOne, Depth+1); + if (KnownZero == 0 && KnownOne == 0) return; + + uint64_t KnownZeroRHS, KnownOneRHS; + DAG.ComputeMaskedBits(Op.getOperand(1), Mask, + KnownZeroRHS, KnownOneRHS, Depth+1); + KnownZero &= KnownZeroRHS; + KnownOne &= KnownOneRHS; + return; + } + } +} + +//===----------------------------------------------------------------------===// +// ARM Inline Assembly Support +//===----------------------------------------------------------------------===// + +/// getConstraintType - Given a constraint letter, return the type of +/// constraint it is for this target. +ARMTargetLowering::ConstraintType +ARMTargetLowering::getConstraintType(const std::string &Constraint) const { + if (Constraint.size() == 1) { + switch (Constraint[0]) { + default: break; + case 'l': return C_RegisterClass; + case 'w': return C_RegisterClass; + } + } + return TargetLowering::getConstraintType(Constraint); +} + +std::pair<unsigned, const TargetRegisterClass*> +ARMTargetLowering::getRegForInlineAsmConstraint(const std::string &Constraint, + MVT::ValueType VT) const { + if (Constraint.size() == 1) { + // GCC RS6000 Constraint Letters + switch (Constraint[0]) { + case 'l': + // FIXME: in thumb mode, 'l' is only low-regs. + // FALL THROUGH. + case 'r': + return std::make_pair(0U, ARM::GPRRegisterClass); + case 'w': + if (VT == MVT::f32) + return std::make_pair(0U, ARM::SPRRegisterClass); + if (VT == MVT::f64) + return std::make_pair(0U, ARM::DPRRegisterClass); + break; + } + } + return TargetLowering::getRegForInlineAsmConstraint(Constraint, VT); +} + +std::vector<unsigned> ARMTargetLowering:: +getRegClassForInlineAsmConstraint(const std::string &Constraint, + MVT::ValueType VT) const { + if (Constraint.size() != 1) + return std::vector<unsigned>(); + + switch (Constraint[0]) { // GCC ARM Constraint Letters + default: break; + case 'l': + case 'r': + return make_vector<unsigned>(ARM::R0, ARM::R1, ARM::R2, ARM::R3, + ARM::R4, ARM::R5, ARM::R6, ARM::R7, + ARM::R8, ARM::R9, ARM::R10, ARM::R11, + ARM::R12, ARM::LR, 0); + case 'w': + if (VT == MVT::f32) + return make_vector<unsigned>(ARM::S0, ARM::S1, ARM::S2, ARM::S3, + ARM::S4, ARM::S5, ARM::S6, ARM::S7, + ARM::S8, ARM::S9, ARM::S10, ARM::S11, + ARM::S12,ARM::S13,ARM::S14,ARM::S15, + ARM::S16,ARM::S17,ARM::S18,ARM::S19, + ARM::S20,ARM::S21,ARM::S22,ARM::S23, + ARM::S24,ARM::S25,ARM::S26,ARM::S27, + ARM::S28,ARM::S29,ARM::S30,ARM::S31, 0); + if (VT == MVT::f64) + return make_vector<unsigned>(ARM::D0, ARM::D1, ARM::D2, ARM::D3, + ARM::D4, ARM::D5, ARM::D6, ARM::D7, + ARM::D8, ARM::D9, ARM::D10,ARM::D11, + ARM::D12,ARM::D13,ARM::D14,ARM::D15, 0); + break; + } + + return std::vector<unsigned>(); +} diff --git a/lib/Target/ARM/ARMISelLowering.h b/lib/Target/ARM/ARMISelLowering.h new file mode 100644 index 0000000..2b66f23 --- /dev/null +++ b/lib/Target/ARM/ARMISelLowering.h @@ -0,0 +1,144 @@ +//===-- ARMISelLowering.h - ARM DAG Lowering Interface ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Evan Cheng and is distributed under +// the University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the interfaces that ARM uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#ifndef ARMISELLOWERING_H +#define ARMISELLOWERING_H + +#include "llvm/Target/TargetLowering.h" +#include "llvm/CodeGen/SelectionDAG.h" +#include <vector> + +namespace llvm { + class ARMConstantPoolValue; + class ARMSubtarget; + + namespace ARMISD { + // ARM Specific DAG Nodes + enum NodeType { + // Start the numbering where the builting ops and target ops leave off. + FIRST_NUMBER = ISD::BUILTIN_OP_END+ARM::INSTRUCTION_LIST_END, + + Wrapper, // Wrapper - A wrapper node for TargetConstantPool, + // TargetExternalSymbol, and TargetGlobalAddress. + WrapperJT, // WrapperJT - A wrapper node for TargetJumpTable + + CALL, // Function call. + CALL_PRED, // Function call that's predicable. + CALL_NOLINK, // Function call with branch not branch-and-link. + tCALL, // Thumb function call. + BRCOND, // Conditional branch. + BR_JT, // Jumptable branch. + RET_FLAG, // Return with a flag operand. + + PIC_ADD, // Add with a PC operand and a PIC label. + + CMP, // ARM compare instructions. + CMPNZ, // ARM compare that uses only N or Z flags. + CMPFP, // ARM VFP compare instruction, sets FPSCR. + CMPFPw0, // ARM VFP compare against zero instruction, sets FPSCR. + FMSTAT, // ARM fmstat instruction. + CMOV, // ARM conditional move instructions. + CNEG, // ARM conditional negate instructions. + + FTOSI, // FP to sint within a FP register. + FTOUI, // FP to uint within a FP register. + SITOF, // sint to FP within a FP register. + UITOF, // uint to FP within a FP register. + + MULHILOU, // Lo,Hi = umul LHS, RHS. + MULHILOS, // Lo,Hi = smul LHS, RHS. + + SRL_FLAG, // V,Flag = srl_flag X -> srl X, 1 + save carry out. + SRA_FLAG, // V,Flag = sra_flag X -> sra X, 1 + save carry out. + RRX, // V = RRX X, Flag -> srl X, 1 + shift in carry flag. + + FMRRD, // double to two gprs. + FMDRR, // Two gprs to double. + + THREAD_POINTER + }; + } + + //===----------------------------------------------------------------------===// + // ARMTargetLowering - ARM Implementation of the TargetLowering interface + + class ARMTargetLowering : public TargetLowering { + int VarArgsFrameIndex; // FrameIndex for start of varargs area. + public: + ARMTargetLowering(TargetMachine &TM); + + virtual SDOperand LowerOperation(SDOperand Op, SelectionDAG &DAG); + virtual const char *getTargetNodeName(unsigned Opcode) const; + + virtual MachineBasicBlock *InsertAtEndOfBasicBlock(MachineInstr *MI, + MachineBasicBlock *MBB); + + /// isLegalAddressingMode - Return true if the addressing mode represented + /// by AM is legal for this target, for a load/store of the specified type. + virtual bool isLegalAddressingMode(const AddrMode &AM, const Type *Ty)const; + + /// getPreIndexedAddressParts - returns true by value, base pointer and + /// offset pointer and addressing mode by reference if the node's address + /// can be legally represented as pre-indexed load / store address. + virtual bool getPreIndexedAddressParts(SDNode *N, SDOperand &Base, + SDOperand &Offset, + ISD::MemIndexedMode &AM, + SelectionDAG &DAG); + + /// getPostIndexedAddressParts - returns true by value, base pointer and + /// offset pointer and addressing mode by reference if this node can be + /// combined with a load / store to form a post-indexed load / store. + virtual bool getPostIndexedAddressParts(SDNode *N, SDNode *Op, + SDOperand &Base, SDOperand &Offset, + ISD::MemIndexedMode &AM, + SelectionDAG &DAG); + + virtual void computeMaskedBitsForTargetNode(const SDOperand Op, + uint64_t Mask, + uint64_t &KnownZero, + uint64_t &KnownOne, + const SelectionDAG &DAG, + unsigned Depth) const; + ConstraintType getConstraintType(const std::string &Constraint) const; + std::pair<unsigned, const TargetRegisterClass*> + getRegForInlineAsmConstraint(const std::string &Constraint, + MVT::ValueType VT) const; + std::vector<unsigned> + getRegClassForInlineAsmConstraint(const std::string &Constraint, + MVT::ValueType VT) const; + private: + /// 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; + + /// ARMPCLabelIndex - Keep track the number of ARM PC labels created. + /// + unsigned ARMPCLabelIndex; + + SDOperand LowerCALL(SDOperand Op, SelectionDAG &DAG); + SDOperand LowerGlobalAddressDarwin(SDOperand Op, SelectionDAG &DAG); + SDOperand LowerGlobalAddressELF(SDOperand Op, SelectionDAG &DAG); + SDOperand LowerGlobalTLSAddress(SDOperand Op, SelectionDAG &DAG); + SDOperand LowerToTLSGeneralDynamicModel(GlobalAddressSDNode *GA, + SelectionDAG &DAG); + SDOperand LowerToTLSExecModels(GlobalAddressSDNode *GA, + SelectionDAG &DAG); + SDOperand LowerGLOBAL_OFFSET_TABLE(SDOperand Op, SelectionDAG &DAG); + SDOperand LowerFORMAL_ARGUMENTS(SDOperand Op, SelectionDAG &DAG); + SDOperand LowerBR_JT(SDOperand Op, SelectionDAG &DAG); + SDOperand LowerMEMCPY(SDOperand Op, SelectionDAG &DAG); + }; +} + +#endif // ARMISELLOWERING_H diff --git a/lib/Target/ARM/ARMInstrInfo.cpp b/lib/Target/ARM/ARMInstrInfo.cpp new file mode 100644 index 0000000..b404ec0 --- /dev/null +++ b/lib/Target/ARM/ARMInstrInfo.cpp @@ -0,0 +1,612 @@ +//===- ARMInstrInfo.cpp - ARM Instruction Information -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the "Instituto Nokia de Tecnologia" and +// is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the ARM implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#include "ARMInstrInfo.h" +#include "ARM.h" +#include "ARMAddressingModes.h" +#include "ARMGenInstrInfo.inc" +#include "ARMMachineFunctionInfo.h" +#include "llvm/CodeGen/LiveVariables.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" +#include "llvm/Target/TargetAsmInfo.h" +#include "llvm/Support/CommandLine.h" +using namespace llvm; + +static cl::opt<bool> EnableARM3Addr("enable-arm-3-addr-conv", cl::Hidden, + cl::desc("Enable ARM 2-addr to 3-addr conv")); + +ARMInstrInfo::ARMInstrInfo(const ARMSubtarget &STI) + : TargetInstrInfo(ARMInsts, sizeof(ARMInsts)/sizeof(ARMInsts[0])), + RI(*this, STI) { +} + +const TargetRegisterClass *ARMInstrInfo::getPointerRegClass() const { + return &ARM::GPRRegClass; +} + +/// Return true if the instruction is a register to register move and +/// leave the source and dest operands in the passed parameters. +/// +bool ARMInstrInfo::isMoveInstr(const MachineInstr &MI, + unsigned &SrcReg, unsigned &DstReg) const { + MachineOpCode oc = MI.getOpcode(); + switch (oc) { + default: + return false; + case ARM::FCPYS: + case ARM::FCPYD: + SrcReg = MI.getOperand(1).getReg(); + DstReg = MI.getOperand(0).getReg(); + return true; + case ARM::MOVr: + case ARM::tMOVr: + assert(MI.getInstrDescriptor()->numOperands >= 2 && + MI.getOperand(0).isRegister() && + MI.getOperand(1).isRegister() && + "Invalid ARM MOV instruction"); + SrcReg = MI.getOperand(1).getReg(); + DstReg = MI.getOperand(0).getReg(); + return true; + } +} + +unsigned ARMInstrInfo::isLoadFromStackSlot(MachineInstr *MI, int &FrameIndex) const{ + switch (MI->getOpcode()) { + default: break; + case ARM::LDR: + if (MI->getOperand(1).isFrameIndex() && + MI->getOperand(2).isReg() && + MI->getOperand(3).isImmediate() && + MI->getOperand(2).getReg() == 0 && + MI->getOperand(3).getImmedValue() == 0) { + FrameIndex = MI->getOperand(1).getFrameIndex(); + return MI->getOperand(0).getReg(); + } + break; + case ARM::FLDD: + case ARM::FLDS: + if (MI->getOperand(1).isFrameIndex() && + MI->getOperand(2).isImmediate() && + MI->getOperand(2).getImmedValue() == 0) { + FrameIndex = MI->getOperand(1).getFrameIndex(); + return MI->getOperand(0).getReg(); + } + break; + case ARM::tRestore: + if (MI->getOperand(1).isFrameIndex() && + MI->getOperand(2).isImmediate() && + MI->getOperand(2).getImmedValue() == 0) { + FrameIndex = MI->getOperand(1).getFrameIndex(); + return MI->getOperand(0).getReg(); + } + break; + } + return 0; +} + +unsigned ARMInstrInfo::isStoreToStackSlot(MachineInstr *MI, int &FrameIndex) const { + switch (MI->getOpcode()) { + default: break; + case ARM::STR: + if (MI->getOperand(1).isFrameIndex() && + MI->getOperand(2).isReg() && + MI->getOperand(3).isImmediate() && + MI->getOperand(2).getReg() == 0 && + MI->getOperand(3).getImmedValue() == 0) { + FrameIndex = MI->getOperand(1).getFrameIndex(); + return MI->getOperand(0).getReg(); + } + break; + case ARM::FSTD: + case ARM::FSTS: + if (MI->getOperand(1).isFrameIndex() && + MI->getOperand(2).isImmediate() && + MI->getOperand(2).getImmedValue() == 0) { + FrameIndex = MI->getOperand(1).getFrameIndex(); + return MI->getOperand(0).getReg(); + } + break; + case ARM::tSpill: + if (MI->getOperand(1).isFrameIndex() && + MI->getOperand(2).isImmediate() && + MI->getOperand(2).getImmedValue() == 0) { + FrameIndex = MI->getOperand(1).getFrameIndex(); + return MI->getOperand(0).getReg(); + } + break; + } + return 0; +} + +static unsigned getUnindexedOpcode(unsigned Opc) { + switch (Opc) { + default: break; + case ARM::LDR_PRE: + case ARM::LDR_POST: + return ARM::LDR; + case ARM::LDRH_PRE: + case ARM::LDRH_POST: + return ARM::LDRH; + case ARM::LDRB_PRE: + case ARM::LDRB_POST: + return ARM::LDRB; + case ARM::LDRSH_PRE: + case ARM::LDRSH_POST: + return ARM::LDRSH; + case ARM::LDRSB_PRE: + case ARM::LDRSB_POST: + return ARM::LDRSB; + case ARM::STR_PRE: + case ARM::STR_POST: + return ARM::STR; + case ARM::STRH_PRE: + case ARM::STRH_POST: + return ARM::STRH; + case ARM::STRB_PRE: + case ARM::STRB_POST: + return ARM::STRB; + } + return 0; +} + +MachineInstr * +ARMInstrInfo::convertToThreeAddress(MachineFunction::iterator &MFI, + MachineBasicBlock::iterator &MBBI, + LiveVariables &LV) const { + if (!EnableARM3Addr) + return NULL; + + MachineInstr *MI = MBBI; + unsigned TSFlags = MI->getInstrDescriptor()->TSFlags; + bool isPre = false; + switch ((TSFlags & ARMII::IndexModeMask) >> ARMII::IndexModeShift) { + default: return NULL; + case ARMII::IndexModePre: + isPre = true; + break; + case ARMII::IndexModePost: + break; + } + + // Try spliting an indexed load / store to a un-indexed one plus an add/sub + // operation. + unsigned MemOpc = getUnindexedOpcode(MI->getOpcode()); + if (MemOpc == 0) + return NULL; + + MachineInstr *UpdateMI = NULL; + MachineInstr *MemMI = NULL; + unsigned AddrMode = (TSFlags & ARMII::AddrModeMask); + const TargetInstrDescriptor *TID = MI->getInstrDescriptor(); + unsigned NumOps = TID->numOperands; + bool isLoad = (TID->Flags & M_LOAD_FLAG) != 0; + const MachineOperand &WB = isLoad ? MI->getOperand(1) : MI->getOperand(0); + const MachineOperand &Base = MI->getOperand(2); + const MachineOperand &Offset = MI->getOperand(NumOps-3); + unsigned WBReg = WB.getReg(); + unsigned BaseReg = Base.getReg(); + unsigned OffReg = Offset.getReg(); + unsigned OffImm = MI->getOperand(NumOps-2).getImm(); + ARMCC::CondCodes Pred = (ARMCC::CondCodes)MI->getOperand(NumOps-1).getImm(); + switch (AddrMode) { + default: + assert(false && "Unknown indexed op!"); + return NULL; + case ARMII::AddrMode2: { + bool isSub = ARM_AM::getAM2Op(OffImm) == ARM_AM::sub; + unsigned Amt = ARM_AM::getAM2Offset(OffImm); + if (OffReg == 0) { + int SOImmVal = ARM_AM::getSOImmVal(Amt); + if (SOImmVal == -1) + // Can't encode it in a so_imm operand. This transformation will + // add more than 1 instruction. Abandon! + return NULL; + UpdateMI = BuildMI(get(isSub ? ARM::SUBri : ARM::ADDri), WBReg) + .addReg(BaseReg).addImm(SOImmVal) + .addImm(Pred).addReg(0).addReg(0); + } else if (Amt != 0) { + ARM_AM::ShiftOpc ShOpc = ARM_AM::getAM2ShiftOpc(OffImm); + unsigned SOOpc = ARM_AM::getSORegOpc(ShOpc, Amt); + UpdateMI = BuildMI(get(isSub ? ARM::SUBrs : ARM::ADDrs), WBReg) + .addReg(BaseReg).addReg(OffReg).addReg(0).addImm(SOOpc) + .addImm(Pred).addReg(0).addReg(0); + } else + UpdateMI = BuildMI(get(isSub ? ARM::SUBrr : ARM::ADDrr), WBReg) + .addReg(BaseReg).addReg(OffReg) + .addImm(Pred).addReg(0).addReg(0); + break; + } + case ARMII::AddrMode3 : { + bool isSub = ARM_AM::getAM3Op(OffImm) == ARM_AM::sub; + unsigned Amt = ARM_AM::getAM3Offset(OffImm); + if (OffReg == 0) + // Immediate is 8-bits. It's guaranteed to fit in a so_imm operand. + UpdateMI = BuildMI(get(isSub ? ARM::SUBri : ARM::ADDri), WBReg) + .addReg(BaseReg).addImm(Amt) + .addImm(Pred).addReg(0).addReg(0); + else + UpdateMI = BuildMI(get(isSub ? ARM::SUBrr : ARM::ADDrr), WBReg) + .addReg(BaseReg).addReg(OffReg) + .addImm(Pred).addReg(0).addReg(0); + break; + } + } + + std::vector<MachineInstr*> NewMIs; + if (isPre) { + if (isLoad) + MemMI = BuildMI(get(MemOpc), MI->getOperand(0).getReg()) + .addReg(WBReg).addReg(0).addImm(0).addImm(Pred); + else + MemMI = BuildMI(get(MemOpc)).addReg(MI->getOperand(1).getReg()) + .addReg(WBReg).addReg(0).addImm(0).addImm(Pred); + NewMIs.push_back(MemMI); + NewMIs.push_back(UpdateMI); + } else { + if (isLoad) + MemMI = BuildMI(get(MemOpc), MI->getOperand(0).getReg()) + .addReg(BaseReg).addReg(0).addImm(0).addImm(Pred); + else + MemMI = BuildMI(get(MemOpc)).addReg(MI->getOperand(1).getReg()) + .addReg(BaseReg).addReg(0).addImm(0).addImm(Pred); + if (WB.isDead()) + UpdateMI->getOperand(0).setIsDead(); + NewMIs.push_back(UpdateMI); + NewMIs.push_back(MemMI); + } + + // Transfer LiveVariables states, kill / dead info. + for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) { + MachineOperand &MO = MI->getOperand(i); + if (MO.isRegister() && MO.getReg() && + MRegisterInfo::isVirtualRegister(MO.getReg())) { + unsigned Reg = MO.getReg(); + LiveVariables::VarInfo &VI = LV.getVarInfo(Reg); + if (MO.isDef()) { + MachineInstr *NewMI = (Reg == WBReg) ? UpdateMI : MemMI; + if (MO.isDead()) + LV.addVirtualRegisterDead(Reg, NewMI); + // Update the defining instruction. + if (VI.DefInst == MI) + VI.DefInst = NewMI; + } + if (MO.isUse() && MO.isKill()) { + for (unsigned j = 0; j < 2; ++j) { + // Look at the two new MI's in reverse order. + MachineInstr *NewMI = NewMIs[j]; + int NIdx = NewMI->findRegisterUseOperandIdx(Reg); + if (NIdx == -1) + continue; + LV.addVirtualRegisterKilled(Reg, NewMI); + if (VI.removeKill(MI)) + VI.Kills.push_back(NewMI); + break; + } + } + } + } + + MFI->insert(MBBI, NewMIs[1]); + MFI->insert(MBBI, NewMIs[0]); + return NewMIs[0]; +} + +// Branch analysis. +bool ARMInstrInfo::AnalyzeBranch(MachineBasicBlock &MBB,MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + std::vector<MachineOperand> &Cond) const { + // If the block has no terminators, it just falls into the block after it. + MachineBasicBlock::iterator I = MBB.end(); + if (I == MBB.begin() || !isUnpredicatedTerminator(--I)) + return false; + + // Get the last instruction in the block. + MachineInstr *LastInst = I; + + // If there is only one terminator instruction, process it. + unsigned LastOpc = LastInst->getOpcode(); + if (I == MBB.begin() || !isUnpredicatedTerminator(--I)) { + if (LastOpc == ARM::B || LastOpc == ARM::tB) { + TBB = LastInst->getOperand(0).getMachineBasicBlock(); + return false; + } + if (LastOpc == ARM::Bcc || LastOpc == ARM::tBcc) { + // Block ends with fall-through condbranch. + TBB = LastInst->getOperand(0).getMachineBasicBlock(); + Cond.push_back(LastInst->getOperand(1)); + Cond.push_back(LastInst->getOperand(2)); + return false; + } + return true; // Can't handle indirect branch. + } + + // Get the instruction before it if it is a terminator. + MachineInstr *SecondLastInst = I; + + // If there are three terminators, we don't know what sort of block this is. + if (SecondLastInst && I != MBB.begin() && isUnpredicatedTerminator(--I)) + return true; + + // If the block ends with ARM::B/ARM::tB and a ARM::Bcc/ARM::tBcc, handle it. + unsigned SecondLastOpc = SecondLastInst->getOpcode(); + if ((SecondLastOpc == ARM::Bcc && LastOpc == ARM::B) || + (SecondLastOpc == ARM::tBcc && LastOpc == ARM::tB)) { + TBB = SecondLastInst->getOperand(0).getMachineBasicBlock(); + Cond.push_back(SecondLastInst->getOperand(1)); + Cond.push_back(SecondLastInst->getOperand(2)); + FBB = LastInst->getOperand(0).getMachineBasicBlock(); + return false; + } + + // If the block ends with two unconditional branches, handle it. The second + // one is not executed, so remove it. + if ((SecondLastOpc == ARM::B || SecondLastOpc==ARM::tB) && + (LastOpc == ARM::B || LastOpc == ARM::tB)) { + TBB = SecondLastInst->getOperand(0).getMachineBasicBlock(); + I = LastInst; + I->eraseFromParent(); + return false; + } + + // Likewise if it ends with a branch table followed by an unconditional branch. + // The branch folder can create these, and we must get rid of them for + // correctness of Thumb constant islands. + if ((SecondLastOpc == ARM::BR_JTr || SecondLastOpc==ARM::BR_JTm || + SecondLastOpc == ARM::BR_JTadd || SecondLastOpc==ARM::tBR_JTr) && + (LastOpc == ARM::B || LastOpc == ARM::tB)) { + I = LastInst; + I->eraseFromParent(); + return true; + } + + // Otherwise, can't handle this. + return true; +} + + +unsigned ARMInstrInfo::RemoveBranch(MachineBasicBlock &MBB) const { + MachineFunction &MF = *MBB.getParent(); + ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>(); + int BOpc = AFI->isThumbFunction() ? ARM::tB : ARM::B; + int BccOpc = AFI->isThumbFunction() ? ARM::tBcc : ARM::Bcc; + + MachineBasicBlock::iterator I = MBB.end(); + if (I == MBB.begin()) return 0; + --I; + if (I->getOpcode() != BOpc && I->getOpcode() != BccOpc) + return 0; + + // Remove the branch. + I->eraseFromParent(); + + I = MBB.end(); + + if (I == MBB.begin()) return 1; + --I; + if (I->getOpcode() != BccOpc) + return 1; + + // Remove the branch. + I->eraseFromParent(); + return 2; +} + +unsigned ARMInstrInfo::InsertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, + MachineBasicBlock *FBB, + const std::vector<MachineOperand> &Cond) const { + MachineFunction &MF = *MBB.getParent(); + ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>(); + int BOpc = AFI->isThumbFunction() ? ARM::tB : ARM::B; + int BccOpc = AFI->isThumbFunction() ? ARM::tBcc : ARM::Bcc; + + // Shouldn't be a fall through. + assert(TBB && "InsertBranch must not be told to insert a fallthrough"); + assert((Cond.size() == 2 || Cond.size() == 0) && + "ARM branch conditions have two components!"); + + if (FBB == 0) { + if (Cond.empty()) // Unconditional branch? + BuildMI(&MBB, get(BOpc)).addMBB(TBB); + else + BuildMI(&MBB, get(BccOpc)).addMBB(TBB) + .addImm(Cond[0].getImm()).addReg(Cond[1].getReg()); + return 1; + } + + // Two-way conditional branch. + BuildMI(&MBB, get(BccOpc)).addMBB(TBB) + .addImm(Cond[0].getImm()).addReg(Cond[1].getReg()); + BuildMI(&MBB, get(BOpc)).addMBB(FBB); + return 2; +} + +bool ARMInstrInfo::BlockHasNoFallThrough(MachineBasicBlock &MBB) const { + if (MBB.empty()) return false; + + switch (MBB.back().getOpcode()) { + case ARM::BX_RET: // Return. + case ARM::LDM_RET: + case ARM::tBX_RET: + case ARM::tBX_RET_vararg: + case ARM::tPOP_RET: + case ARM::B: + case ARM::tB: // Uncond branch. + case ARM::tBR_JTr: + case ARM::BR_JTr: // Jumptable branch. + case ARM::BR_JTm: // Jumptable branch through mem. + case ARM::BR_JTadd: // Jumptable branch add to pc. + return true; + default: return false; + } +} + +bool ARMInstrInfo:: +ReverseBranchCondition(std::vector<MachineOperand> &Cond) const { + ARMCC::CondCodes CC = (ARMCC::CondCodes)(int)Cond[0].getImm(); + Cond[0].setImm(ARMCC::getOppositeCondition(CC)); + return false; +} + +bool ARMInstrInfo::isPredicated(const MachineInstr *MI) const { + int PIdx = MI->findFirstPredOperandIdx(); + return PIdx != -1 && MI->getOperand(PIdx).getImmedValue() != ARMCC::AL; +} + +bool ARMInstrInfo::PredicateInstruction(MachineInstr *MI, + const std::vector<MachineOperand> &Pred) const { + unsigned Opc = MI->getOpcode(); + if (Opc == ARM::B || Opc == ARM::tB) { + MI->setInstrDescriptor(get(Opc == ARM::B ? ARM::Bcc : ARM::tBcc)); + MI->addImmOperand(Pred[0].getImmedValue()); + MI->addRegOperand(Pred[1].getReg(), false); + return true; + } + + int PIdx = MI->findFirstPredOperandIdx(); + if (PIdx != -1) { + MachineOperand &PMO = MI->getOperand(PIdx); + PMO.setImm(Pred[0].getImmedValue()); + MI->getOperand(PIdx+1).setReg(Pred[1].getReg()); + return true; + } + return false; +} + +bool +ARMInstrInfo::SubsumesPredicate(const std::vector<MachineOperand> &Pred1, + const std::vector<MachineOperand> &Pred2) const{ + if (Pred1.size() > 2 || Pred2.size() > 2) + return false; + + ARMCC::CondCodes CC1 = (ARMCC::CondCodes)Pred1[0].getImmedValue(); + ARMCC::CondCodes CC2 = (ARMCC::CondCodes)Pred2[0].getImmedValue(); + if (CC1 == CC2) + return true; + + switch (CC1) { + default: + return false; + case ARMCC::AL: + return true; + case ARMCC::HS: + return CC2 == ARMCC::HI; + case ARMCC::LS: + return CC2 == ARMCC::LO || CC2 == ARMCC::EQ; + case ARMCC::GE: + return CC2 == ARMCC::GT; + case ARMCC::LE: + return CC2 == ARMCC::LT; + } +} + +bool ARMInstrInfo::DefinesPredicate(MachineInstr *MI, + std::vector<MachineOperand> &Pred) const { + const TargetInstrDescriptor *TID = MI->getInstrDescriptor(); + if (!TID->ImplicitDefs && (TID->Flags & M_HAS_OPTIONAL_DEF) == 0) + return false; + + bool Found = false; + for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) { + const MachineOperand &MO = MI->getOperand(i); + if (MO.isReg() && MO.getReg() == ARM::CPSR) { + Pred.push_back(MO); + Found = true; + } + } + + return Found; +} + + +/// FIXME: Works around a gcc miscompilation with -fstrict-aliasing +static unsigned getNumJTEntries(const std::vector<MachineJumpTableEntry> &JT, + unsigned JTI) DISABLE_INLINE; +static unsigned getNumJTEntries(const std::vector<MachineJumpTableEntry> &JT, + unsigned JTI) { + return JT[JTI].MBBs.size(); +} + +/// GetInstSize - Return the size of the specified MachineInstr. +/// +unsigned ARM::GetInstSize(MachineInstr *MI) { + MachineBasicBlock &MBB = *MI->getParent(); + const MachineFunction *MF = MBB.getParent(); + const TargetAsmInfo *TAI = MF->getTarget().getTargetAsmInfo(); + + // Basic size info comes from the TSFlags field. + const TargetInstrDescriptor *TID = MI->getInstrDescriptor(); + unsigned TSFlags = TID->TSFlags; + + switch ((TSFlags & ARMII::SizeMask) >> ARMII::SizeShift) { + default: + // If this machine instr is an inline asm, measure it. + if (MI->getOpcode() == ARM::INLINEASM) + return TAI->getInlineAsmLength(MI->getOperand(0).getSymbolName()); + if (MI->getOpcode() == ARM::LABEL) + return 0; + assert(0 && "Unknown or unset size field for instr!"); + break; + case ARMII::Size8Bytes: return 8; // Arm instruction x 2. + case ARMII::Size4Bytes: return 4; // Arm instruction. + case ARMII::Size2Bytes: return 2; // Thumb instruction. + case ARMII::SizeSpecial: { + switch (MI->getOpcode()) { + case ARM::CONSTPOOL_ENTRY: + // If this machine instr is a constant pool entry, its size is recorded as + // operand #2. + return MI->getOperand(2).getImm(); + case ARM::BR_JTr: + case ARM::BR_JTm: + case ARM::BR_JTadd: + case ARM::tBR_JTr: { + // These are jumptable branches, i.e. a branch followed by an inlined + // jumptable. The size is 4 + 4 * number of entries. + unsigned NumOps = TID->numOperands; + MachineOperand JTOP = + MI->getOperand(NumOps - ((TID->Flags & M_PREDICABLE) ? 3 : 2)); + unsigned JTI = JTOP.getJumpTableIndex(); + MachineJumpTableInfo *MJTI = MF->getJumpTableInfo(); + const std::vector<MachineJumpTableEntry> &JT = MJTI->getJumpTables(); + assert(JTI < JT.size()); + // Thumb instructions are 2 byte aligned, but JT entries are 4 byte + // 4 aligned. The assembler / linker may add 2 byte padding just before + // the JT entries. The size does not include this padding; the + // constant islands pass does separate bookkeeping for it. + // FIXME: If we know the size of the function is less than (1 << 16) *2 + // bytes, we can use 16-bit entries instead. Then there won't be an + // alignment issue. + return getNumJTEntries(JT, JTI) * 4 + + (MI->getOpcode()==ARM::tBR_JTr ? 2 : 4); + } + default: + // Otherwise, pseudo-instruction sizes are zero. + return 0; + } + } + } +} + +/// GetFunctionSize - Returns the size of the specified MachineFunction. +/// +unsigned ARM::GetFunctionSize(MachineFunction &MF) { + unsigned FnSize = 0; + for (MachineFunction::iterator MBBI = MF.begin(), E = MF.end(); + MBBI != E; ++MBBI) { + MachineBasicBlock &MBB = *MBBI; + for (MachineBasicBlock::iterator I = MBB.begin(),E = MBB.end(); I != E; ++I) + FnSize += ARM::GetInstSize(I); + } + return FnSize; +} diff --git a/lib/Target/ARM/ARMInstrInfo.h b/lib/Target/ARM/ARMInstrInfo.h new file mode 100644 index 0000000..2c158b8 --- /dev/null +++ b/lib/Target/ARM/ARMInstrInfo.h @@ -0,0 +1,133 @@ +//===- ARMInstrInfo.h - ARM Instruction Information -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the "Instituto Nokia de Tecnologia" and +// is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the ARM implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef ARMINSTRUCTIONINFO_H +#define ARMINSTRUCTIONINFO_H + +#include "llvm/Target/TargetInstrInfo.h" +#include "ARMRegisterInfo.h" + +namespace llvm { + class ARMSubtarget; + +/// ARMII - This namespace holds all of the target specific flags that +/// instruction info tracks. +/// +namespace ARMII { + enum { + //===------------------------------------------------------------------===// + // Instruction Flags. + + //===------------------------------------------------------------------===// + // This three-bit field describes the addressing mode used. Zero is unused + // so that we can tell if we forgot to set a value. + + AddrModeMask = 0xf, + AddrMode1 = 1, + AddrMode2 = 2, + AddrMode3 = 3, + AddrMode4 = 4, + AddrMode5 = 5, + AddrModeT1 = 6, + AddrModeT2 = 7, + AddrModeT4 = 8, + AddrModeTs = 9, // i8 * 4 for pc and sp relative data + + // Size* - Flags to keep track of the size of an instruction. + SizeShift = 4, + SizeMask = 7 << SizeShift, + SizeSpecial = 1, // 0 byte pseudo or special case. + Size8Bytes = 2, + Size4Bytes = 3, + Size2Bytes = 4, + + // IndexMode - Unindex, pre-indexed, or post-indexed. Only valid for load + // and store ops + IndexModeShift = 7, + IndexModeMask = 3 << IndexModeShift, + IndexModePre = 1, + IndexModePost = 2, + + // Opcode + OpcodeShift = 9, + OpcodeMask = 0xf << OpcodeShift + }; +} + +class ARMInstrInfo : public TargetInstrInfo { + const ARMRegisterInfo RI; +public: + ARMInstrInfo(const ARMSubtarget &STI); + + /// getRegisterInfo - TargetInstrInfo is a superset of MRegister info. As + /// such, whenever a client has an instance of instruction info, it should + /// always be able to get register info as well (through this method). + /// + virtual const MRegisterInfo &getRegisterInfo() const { return RI; } + + /// getPointerRegClass - Return the register class to use to hold pointers. + /// This is used for addressing modes. + virtual const TargetRegisterClass *getPointerRegClass() const; + + /// Return true if the instruction is a register to register move and + /// leave the source and dest operands in the passed parameters. + /// + virtual bool isMoveInstr(const MachineInstr &MI, + unsigned &SrcReg, unsigned &DstReg) const; + virtual unsigned isLoadFromStackSlot(MachineInstr *MI, int &FrameIndex) const; + virtual unsigned isStoreToStackSlot(MachineInstr *MI, int &FrameIndex) const; + + virtual MachineInstr *convertToThreeAddress(MachineFunction::iterator &MFI, + MachineBasicBlock::iterator &MBBI, + LiveVariables &LV) const; + + // Branch analysis. + virtual bool AnalyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + std::vector<MachineOperand> &Cond) const; + virtual unsigned RemoveBranch(MachineBasicBlock &MBB) const; + virtual unsigned InsertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, + MachineBasicBlock *FBB, + const std::vector<MachineOperand> &Cond) const; + virtual bool BlockHasNoFallThrough(MachineBasicBlock &MBB) const; + virtual bool ReverseBranchCondition(std::vector<MachineOperand> &Cond) const; + + // Predication support. + virtual bool isPredicated(const MachineInstr *MI) const; + + virtual + bool PredicateInstruction(MachineInstr *MI, + const std::vector<MachineOperand> &Pred) const; + + virtual + bool SubsumesPredicate(const std::vector<MachineOperand> &Pred1, + const std::vector<MachineOperand> &Pred1) const; + + virtual bool DefinesPredicate(MachineInstr *MI, + std::vector<MachineOperand> &Pred) const; +}; + + // Utility routines + namespace ARM { + /// GetInstSize - Returns the size of the specified MachineInstr. + /// + unsigned GetInstSize(MachineInstr *MI); + + /// GetFunctionSize - Returns the size of the specified MachineFunction. + /// + unsigned GetFunctionSize(MachineFunction &MF); + } +} + +#endif diff --git a/lib/Target/ARM/ARMInstrInfo.td b/lib/Target/ARM/ARMInstrInfo.td new file mode 100644 index 0000000..adc203b --- /dev/null +++ b/lib/Target/ARM/ARMInstrInfo.td @@ -0,0 +1,1320 @@ +//===- ARMInstrInfo.td - Target Description for ARM Target -*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the "Instituto Nokia de Tecnologia" and +// is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes the ARM instructions in TableGen format. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// ARM specific DAG Nodes. +// + +// Type profiles. +def SDT_ARMCallSeq : SDTypeProfile<0, 1, [ SDTCisVT<0, i32> ]>; + +def SDT_ARMSaveCallPC : SDTypeProfile<0, 1, []>; + +def SDT_ARMcall : SDTypeProfile<0, -1, [SDTCisInt<0>]>; + +def SDT_ARMCMov : SDTypeProfile<1, 3, + [SDTCisSameAs<0, 1>, SDTCisSameAs<0, 2>, + SDTCisVT<3, i32>]>; + +def SDT_ARMBrcond : SDTypeProfile<0, 2, + [SDTCisVT<0, OtherVT>, SDTCisVT<1, i32>]>; + +def SDT_ARMBrJT : SDTypeProfile<0, 3, + [SDTCisPtrTy<0>, SDTCisVT<1, i32>, + SDTCisVT<2, i32>]>; + +def SDT_ARMCmp : SDTypeProfile<0, 2, [SDTCisSameAs<0, 1>]>; + +def SDT_ARMPICAdd : SDTypeProfile<1, 2, [SDTCisSameAs<0, 1>, + SDTCisPtrTy<1>, SDTCisVT<2, i32>]>; + +def SDT_ARMThreadPointer : SDTypeProfile<1, 0, [SDTCisPtrTy<0>]>; + +// Node definitions. +def ARMWrapper : SDNode<"ARMISD::Wrapper", SDTIntUnaryOp>; +def ARMWrapperJT : SDNode<"ARMISD::WrapperJT", SDTIntBinOp>; + +def ARMcallseq_start : SDNode<"ISD::CALLSEQ_START", SDT_ARMCallSeq, + [SDNPHasChain, SDNPOutFlag]>; +def ARMcallseq_end : SDNode<"ISD::CALLSEQ_END", SDT_ARMCallSeq, + [SDNPHasChain, SDNPInFlag, SDNPOutFlag]>; + +def ARMcall : SDNode<"ARMISD::CALL", SDT_ARMcall, + [SDNPHasChain, SDNPOptInFlag, SDNPOutFlag]>; +def ARMcall_pred : SDNode<"ARMISD::CALL_PRED", SDT_ARMcall, + [SDNPHasChain, SDNPOptInFlag, SDNPOutFlag]>; +def ARMcall_nolink : SDNode<"ARMISD::CALL_NOLINK", SDT_ARMcall, + [SDNPHasChain, SDNPOptInFlag, SDNPOutFlag]>; + +def ARMretflag : SDNode<"ARMISD::RET_FLAG", SDTRet, + [SDNPHasChain, SDNPOptInFlag]>; + +def ARMcmov : SDNode<"ARMISD::CMOV", SDT_ARMCMov, + [SDNPInFlag]>; +def ARMcneg : SDNode<"ARMISD::CNEG", SDT_ARMCMov, + [SDNPInFlag]>; + +def ARMbrcond : SDNode<"ARMISD::BRCOND", SDT_ARMBrcond, + [SDNPHasChain, SDNPInFlag, SDNPOutFlag]>; + +def ARMbrjt : SDNode<"ARMISD::BR_JT", SDT_ARMBrJT, + [SDNPHasChain]>; + +def ARMcmp : SDNode<"ARMISD::CMP", SDT_ARMCmp, + [SDNPOutFlag]>; + +def ARMcmpNZ : SDNode<"ARMISD::CMPNZ", SDT_ARMCmp, + [SDNPOutFlag]>; + +def ARMpic_add : SDNode<"ARMISD::PIC_ADD", SDT_ARMPICAdd>; + +def ARMsrl_flag : SDNode<"ARMISD::SRL_FLAG", SDTIntUnaryOp, [SDNPOutFlag]>; +def ARMsra_flag : SDNode<"ARMISD::SRA_FLAG", SDTIntUnaryOp, [SDNPOutFlag]>; +def ARMrrx : SDNode<"ARMISD::RRX" , SDTIntUnaryOp, [SDNPInFlag ]>; + +def ARMthread_pointer: SDNode<"ARMISD::THREAD_POINTER", SDT_ARMThreadPointer>; + +//===----------------------------------------------------------------------===// +// ARM Instruction Predicate Definitions. +// +def HasV5T : Predicate<"Subtarget->hasV5TOps()">; +def HasV5TE : Predicate<"Subtarget->hasV5TEOps()">; +def HasV6 : Predicate<"Subtarget->hasV6Ops()">; +def IsThumb : Predicate<"Subtarget->isThumb()">; +def IsARM : Predicate<"!Subtarget->isThumb()">; + +//===----------------------------------------------------------------------===// +// ARM Flag Definitions. + +class RegConstraint<string C> { + string Constraints = C; +} + +//===----------------------------------------------------------------------===// +// ARM specific transformation functions and pattern fragments. +// + +// so_imm_XFORM - Return a so_imm value packed into the format described for +// so_imm def below. +def so_imm_XFORM : SDNodeXForm<imm, [{ + return CurDAG->getTargetConstant(ARM_AM::getSOImmVal(N->getValue()), + MVT::i32); +}]>; + +// so_imm_neg_XFORM - Return a so_imm value packed into the format described for +// so_imm_neg def below. +def so_imm_neg_XFORM : SDNodeXForm<imm, [{ + return CurDAG->getTargetConstant(ARM_AM::getSOImmVal(-(int)N->getValue()), + MVT::i32); +}]>; + +// so_imm_not_XFORM - Return a so_imm value packed into the format described for +// so_imm_not def below. +def so_imm_not_XFORM : SDNodeXForm<imm, [{ + return CurDAG->getTargetConstant(ARM_AM::getSOImmVal(~(int)N->getValue()), + MVT::i32); +}]>; + +// rot_imm predicate - True if the 32-bit immediate is equal to 8, 16, or 24. +def rot_imm : PatLeaf<(i32 imm), [{ + int32_t v = (int32_t)N->getValue(); + return v == 8 || v == 16 || v == 24; +}]>; + +/// imm1_15 predicate - True if the 32-bit immediate is in the range [1,15]. +def imm1_15 : PatLeaf<(i32 imm), [{ + return (int32_t)N->getValue() >= 1 && (int32_t)N->getValue() < 16; +}]>; + +/// imm16_31 predicate - True if the 32-bit immediate is in the range [16,31]. +def imm16_31 : PatLeaf<(i32 imm), [{ + return (int32_t)N->getValue() >= 16 && (int32_t)N->getValue() < 32; +}]>; + +def so_imm_neg : + PatLeaf<(imm), [{ return ARM_AM::getSOImmVal(-(int)N->getValue()) != -1; }], + so_imm_neg_XFORM>; + +def so_imm_not : + PatLeaf<(imm), [{ return ARM_AM::getSOImmVal(~(int)N->getValue()) != -1; }], + so_imm_not_XFORM>; + +// sext_16_node predicate - True if the SDNode is sign-extended 16 or more bits. +def sext_16_node : PatLeaf<(i32 GPR:$a), [{ + return CurDAG->ComputeNumSignBits(SDOperand(N,0)) >= 17; +}]>; + + + +//===----------------------------------------------------------------------===// +// Operand Definitions. +// + +// Branch target. +def brtarget : Operand<OtherVT>; + +// A list of registers separated by comma. Used by load/store multiple. +def reglist : Operand<i32> { + let PrintMethod = "printRegisterList"; +} + +// An operand for the CONSTPOOL_ENTRY pseudo-instruction. +def cpinst_operand : Operand<i32> { + let PrintMethod = "printCPInstOperand"; +} + +def jtblock_operand : Operand<i32> { + let PrintMethod = "printJTBlockOperand"; +} + +// Local PC labels. +def pclabel : Operand<i32> { + let PrintMethod = "printPCLabel"; +} + +// shifter_operand operands: so_reg and so_imm. +def so_reg : Operand<i32>, // reg reg imm + ComplexPattern<i32, 3, "SelectShifterOperandReg", + [shl,srl,sra,rotr]> { + let PrintMethod = "printSORegOperand"; + let MIOperandInfo = (ops GPR, GPR, i32imm); +} + +// so_imm - Match a 32-bit shifter_operand immediate operand, which is an +// 8-bit immediate rotated by an arbitrary number of bits. so_imm values are +// represented in the imm field in the same 12-bit form that they are encoded +// into so_imm instructions: the 8-bit immediate is the least significant bits +// [bits 0-7], the 4-bit shift amount is the next 4 bits [bits 8-11]. +def so_imm : Operand<i32>, + PatLeaf<(imm), + [{ return ARM_AM::getSOImmVal(N->getValue()) != -1; }], + so_imm_XFORM> { + let PrintMethod = "printSOImmOperand"; +} + +// Break so_imm's up into two pieces. This handles immediates with up to 16 +// bits set in them. This uses so_imm2part to match and so_imm2part_[12] to +// get the first/second pieces. +def so_imm2part : Operand<i32>, + PatLeaf<(imm), + [{ return ARM_AM::isSOImmTwoPartVal((unsigned)N->getValue()); }]> { + let PrintMethod = "printSOImm2PartOperand"; +} + +def so_imm2part_1 : SDNodeXForm<imm, [{ + unsigned V = ARM_AM::getSOImmTwoPartFirst((unsigned)N->getValue()); + return CurDAG->getTargetConstant(ARM_AM::getSOImmVal(V), MVT::i32); +}]>; + +def so_imm2part_2 : SDNodeXForm<imm, [{ + unsigned V = ARM_AM::getSOImmTwoPartSecond((unsigned)N->getValue()); + return CurDAG->getTargetConstant(ARM_AM::getSOImmVal(V), MVT::i32); +}]>; + + +// Define ARM specific addressing modes. + +// addrmode2 := reg +/- reg shop imm +// addrmode2 := reg +/- imm12 +// +def addrmode2 : Operand<i32>, + ComplexPattern<i32, 3, "SelectAddrMode2", []> { + let PrintMethod = "printAddrMode2Operand"; + let MIOperandInfo = (ops GPR:$base, GPR:$offsreg, i32imm:$offsimm); +} + +def am2offset : Operand<i32>, + ComplexPattern<i32, 2, "SelectAddrMode2Offset", []> { + let PrintMethod = "printAddrMode2OffsetOperand"; + let MIOperandInfo = (ops GPR, i32imm); +} + +// addrmode3 := reg +/- reg +// addrmode3 := reg +/- imm8 +// +def addrmode3 : Operand<i32>, + ComplexPattern<i32, 3, "SelectAddrMode3", []> { + let PrintMethod = "printAddrMode3Operand"; + let MIOperandInfo = (ops GPR:$base, GPR:$offsreg, i32imm:$offsimm); +} + +def am3offset : Operand<i32>, + ComplexPattern<i32, 2, "SelectAddrMode3Offset", []> { + let PrintMethod = "printAddrMode3OffsetOperand"; + let MIOperandInfo = (ops GPR, i32imm); +} + +// addrmode4 := reg, <mode|W> +// +def addrmode4 : Operand<i32>, + ComplexPattern<i32, 2, "", []> { + let PrintMethod = "printAddrMode4Operand"; + let MIOperandInfo = (ops GPR, i32imm); +} + +// addrmode5 := reg +/- imm8*4 +// +def addrmode5 : Operand<i32>, + ComplexPattern<i32, 2, "SelectAddrMode5", []> { + let PrintMethod = "printAddrMode5Operand"; + let MIOperandInfo = (ops GPR, i32imm); +} + +// addrmodepc := pc + reg +// +def addrmodepc : Operand<i32>, + ComplexPattern<i32, 2, "SelectAddrModePC", []> { + let PrintMethod = "printAddrModePCOperand"; + let MIOperandInfo = (ops GPR, i32imm); +} + +// ARM Predicate operand. Default to 14 = always (AL). Second part is CC +// register whose default is 0 (no register). +def pred : PredicateOperand<OtherVT, (ops i32imm, CCR), + (ops (i32 14), (i32 zero_reg))> { + let PrintMethod = "printPredicateOperand"; +} + +// Conditional code result for instructions whose 's' bit is set, e.g. subs. +// +def cc_out : OptionalDefOperand<OtherVT, (ops CCR), (ops (i32 zero_reg))> { + let PrintMethod = "printSBitModifierOperand"; +} + +//===----------------------------------------------------------------------===// +// ARM Instruction flags. These need to match ARMInstrInfo.h. +// + +// Addressing mode. +class AddrMode<bits<4> val> { + bits<4> Value = val; +} +def AddrModeNone : AddrMode<0>; +def AddrMode1 : AddrMode<1>; +def AddrMode2 : AddrMode<2>; +def AddrMode3 : AddrMode<3>; +def AddrMode4 : AddrMode<4>; +def AddrMode5 : AddrMode<5>; +def AddrModeT1 : AddrMode<6>; +def AddrModeT2 : AddrMode<7>; +def AddrModeT4 : AddrMode<8>; +def AddrModeTs : AddrMode<9>; + +// Instruction size. +class SizeFlagVal<bits<3> val> { + bits<3> Value = val; +} +def SizeInvalid : SizeFlagVal<0>; // Unset. +def SizeSpecial : SizeFlagVal<1>; // Pseudo or special. +def Size8Bytes : SizeFlagVal<2>; +def Size4Bytes : SizeFlagVal<3>; +def Size2Bytes : SizeFlagVal<4>; + +// Load / store index mode. +class IndexMode<bits<2> val> { + bits<2> Value = val; +} +def IndexModeNone : IndexMode<0>; +def IndexModePre : IndexMode<1>; +def IndexModePost : IndexMode<2>; + +//===----------------------------------------------------------------------===// +// ARM Instruction templates. +// + +// ARMPat - Same as Pat<>, but requires that the compiler be in ARM mode. +class ARMPat<dag pattern, dag result> : Pat<pattern, result> { + list<Predicate> Predicates = [IsARM]; +} +class ARMV5TEPat<dag pattern, dag result> : Pat<pattern, result> { + list<Predicate> Predicates = [IsARM, HasV5TE]; +} +class ARMV6Pat<dag pattern, dag result> : Pat<pattern, result> { + list<Predicate> Predicates = [IsARM, HasV6]; +} + +class InstARM<bits<4> opcod, AddrMode am, SizeFlagVal sz, IndexMode im, + string cstr> + : Instruction { + let Namespace = "ARM"; + + bits<4> Opcode = opcod; + AddrMode AM = am; + bits<4> AddrModeBits = AM.Value; + + SizeFlagVal SZ = sz; + bits<3> SizeFlag = SZ.Value; + + IndexMode IM = im; + bits<2> IndexModeBits = IM.Value; + + let Constraints = cstr; +} + +class PseudoInst<dag ops, string asm, list<dag> pattern> + : InstARM<0, AddrModeNone, SizeSpecial, IndexModeNone, ""> { + let OperandList = ops; + let AsmString = asm; + let Pattern = pattern; +} + +// Almost all ARM instructions are predicable. +class I<dag oprnds, AddrMode am, SizeFlagVal sz, IndexMode im, + string opc, string asm, string cstr, list<dag> pattern> + // FIXME: Set all opcodes to 0 for now. + : InstARM<0, am, sz, im, cstr> { + let OperandList = !con(oprnds, (ops pred:$p)); + let AsmString = !strconcat(opc, !strconcat("${p}", asm)); + let Pattern = pattern; + list<Predicate> Predicates = [IsARM]; +} + +// Same as I except it can optionally modify CPSR. +class sI<dag oprnds, AddrMode am, SizeFlagVal sz, IndexMode im, + string opc, string asm, string cstr, list<dag> pattern> + // FIXME: Set all opcodes to 0 for now. + : InstARM<0, am, sz, im, cstr> { + let OperandList = !con(oprnds, (ops pred:$p, cc_out:$s)); + let AsmString = !strconcat(opc, !strconcat("${p}${s}", asm)); + let Pattern = pattern; + list<Predicate> Predicates = [IsARM]; +} + +class AI<dag ops, string opc, string asm, list<dag> pattern> + : I<ops, AddrModeNone, Size4Bytes, IndexModeNone, opc, asm, "", pattern>; +class AsI<dag ops, string opc, string asm, list<dag> pattern> + : sI<ops, AddrModeNone, Size4Bytes, IndexModeNone, opc, asm, "", pattern>; +class AI1<dag ops, string opc, string asm, list<dag> pattern> + : I<ops, AddrMode1, Size4Bytes, IndexModeNone, opc, asm, "", pattern>; +class AsI1<dag ops, string opc, string asm, list<dag> pattern> + : sI<ops, AddrMode1, Size4Bytes, IndexModeNone, opc, asm, "", pattern>; +class AI2<dag ops, string opc, string asm, list<dag> pattern> + : I<ops, AddrMode2, Size4Bytes, IndexModeNone, opc, asm, "", pattern>; +class AI3<dag ops, string opc, string asm, list<dag> pattern> + : I<ops, AddrMode3, Size4Bytes, IndexModeNone, opc, asm, "", pattern>; +class AI4<dag ops, string opc, string asm, list<dag> pattern> + : I<ops, AddrMode4, Size4Bytes, IndexModeNone, opc, asm, "", pattern>; +class AI1x2<dag ops, string opc, string asm, list<dag> pattern> + : I<ops, AddrMode1, Size8Bytes, IndexModeNone, opc, asm, "", pattern>; + +// Pre-indexed ops +class AI2pr<dag ops, string opc, string asm, string cstr, list<dag> pattern> + : I<ops, AddrMode2, Size4Bytes, IndexModePre, opc, asm, cstr, pattern>; +class AI3pr<dag ops, string opc, string asm, string cstr, list<dag> pattern> + : I<ops, AddrMode3, Size4Bytes, IndexModePre, opc, asm, cstr, pattern>; + +// Post-indexed ops +class AI2po<dag ops, string opc, string asm, string cstr, list<dag> pattern> + : I<ops, AddrMode2, Size4Bytes, IndexModePost, opc, asm, cstr, pattern>; +class AI3po<dag ops, string opc, string asm, string cstr, list<dag> pattern> + : I<ops, AddrMode3, Size4Bytes, IndexModePost, opc, asm, cstr, pattern>; + + +class BinOpFrag<dag res> : PatFrag<(ops node:$LHS, node:$RHS), res>; +class UnOpFrag <dag res> : PatFrag<(ops node:$Src), res>; + + +/// AI1_bin_irs - Defines a set of (op r, {so_imm|r|so_reg}) patterns for a +/// binop that produces a value. +multiclass AsI1_bin_irs<string opc, PatFrag opnode> { + def ri : AsI1<(ops GPR:$dst, GPR:$a, so_imm:$b), + opc, " $dst, $a, $b", + [(set GPR:$dst, (opnode GPR:$a, so_imm:$b))]>; + def rr : AsI1<(ops GPR:$dst, GPR:$a, GPR:$b), + opc, " $dst, $a, $b", + [(set GPR:$dst, (opnode GPR:$a, GPR:$b))]>; + def rs : AsI1<(ops GPR:$dst, GPR:$a, so_reg:$b), + opc, " $dst, $a, $b", + [(set GPR:$dst, (opnode GPR:$a, so_reg:$b))]>; +} + +/// ASI1_bin_s_irs - Similar to AsI1_bin_irs except it sets the 's' bit so the +/// instruction modifies the CSPR register. +multiclass ASI1_bin_s_irs<string opc, PatFrag opnode> { + def ri : AI1<(ops GPR:$dst, GPR:$a, so_imm:$b), + opc, "s $dst, $a, $b", + [(set GPR:$dst, (opnode GPR:$a, so_imm:$b))]>, Imp<[], [CPSR]>; + def rr : AI1<(ops GPR:$dst, GPR:$a, GPR:$b), + opc, "s $dst, $a, $b", + [(set GPR:$dst, (opnode GPR:$a, GPR:$b))]>, Imp<[], [CPSR]>; + def rs : AI1<(ops GPR:$dst, GPR:$a, so_reg:$b), + opc, "s $dst, $a, $b", + [(set GPR:$dst, (opnode GPR:$a, so_reg:$b))]>, Imp<[], [CPSR]>; +} + +/// AI1_cmp_irs - Defines a set of (op r, {so_imm|r|so_reg}) cmp / test +/// patterns. Similar to AsI1_bin_irs except the instruction does not produce +/// a explicit result, only implicitly set CPSR. +multiclass AI1_cmp_irs<string opc, PatFrag opnode> { + def ri : AI1<(ops GPR:$a, so_imm:$b), + opc, " $a, $b", + [(opnode GPR:$a, so_imm:$b)]>, Imp<[], [CPSR]>; + def rr : AI1<(ops GPR:$a, GPR:$b), + opc, " $a, $b", + [(opnode GPR:$a, GPR:$b)]>, Imp<[], [CPSR]>; + def rs : AI1<(ops GPR:$a, so_reg:$b), + opc, " $a, $b", + [(opnode GPR:$a, so_reg:$b)]>, Imp<[], [CPSR]>; +} + +/// AI_unary_rrot - A unary operation with two forms: one whose operand is a +/// register and one whose operand is a register rotated by 8/16/24. +multiclass AI_unary_rrot<string opc, PatFrag opnode> { + def r : AI<(ops GPR:$dst, GPR:$Src), + opc, " $dst, $Src", + [(set GPR:$dst, (opnode GPR:$Src))]>, Requires<[IsARM, HasV6]>; + def r_rot : AI<(ops GPR:$dst, GPR:$Src, i32imm:$rot), + opc, " $dst, $Src, ror $rot", + [(set GPR:$dst, (opnode (rotr GPR:$Src, rot_imm:$rot)))]>, + Requires<[IsARM, HasV6]>; +} + +/// AI_bin_rrot - A binary operation with two forms: one whose operand is a +/// register and one whose operand is a register rotated by 8/16/24. +multiclass AI_bin_rrot<string opc, PatFrag opnode> { + def rr : AI<(ops GPR:$dst, GPR:$LHS, GPR:$RHS), + opc, " $dst, $LHS, $RHS", + [(set GPR:$dst, (opnode GPR:$LHS, GPR:$RHS))]>, + Requires<[IsARM, HasV6]>; + def rr_rot : AI<(ops GPR:$dst, GPR:$LHS, GPR:$RHS, i32imm:$rot), + opc, " $dst, $LHS, $RHS, ror $rot", + [(set GPR:$dst, (opnode GPR:$LHS, + (rotr GPR:$RHS, rot_imm:$rot)))]>, + Requires<[IsARM, HasV6]>; +} + +// Special cases. +class XI<dag oprnds, AddrMode am, SizeFlagVal sz, IndexMode im, + string asm, string cstr, list<dag> pattern> + // FIXME: Set all opcodes to 0 for now. + : InstARM<0, am, sz, im, cstr> { + let OperandList = oprnds; + let AsmString = asm; + let Pattern = pattern; + list<Predicate> Predicates = [IsARM]; +} + +class AXI<dag ops, string asm, list<dag> pattern> + : XI<ops, AddrModeNone, Size4Bytes, IndexModeNone, asm, "", pattern>; +class AXI1<dag ops, string asm, list<dag> pattern> + : XI<ops, AddrMode1, Size4Bytes, IndexModeNone, asm, "", pattern>; +class AXI2<dag ops, string asm, list<dag> pattern> + : XI<ops, AddrMode2, Size4Bytes, IndexModeNone, asm, "", pattern>; +class AXI3<dag ops, string asm, list<dag> pattern> + : XI<ops, AddrMode3, Size4Bytes, IndexModeNone, asm, "", pattern>; +class AXI4<dag ops, string asm, list<dag> pattern> + : XI<ops, AddrMode4, Size4Bytes, IndexModeNone, asm, "", pattern>; + +class AXIx2<dag ops, string asm, list<dag> pattern> + : XI<ops, AddrModeNone, Size8Bytes, IndexModeNone, asm, "", pattern>; + +// BR_JT instructions +class JTI<dag ops, string asm, list<dag> pattern> + : XI<ops, AddrModeNone, SizeSpecial, IndexModeNone, asm, "", pattern>; +class JTI1<dag ops, string asm, list<dag> pattern> + : XI<ops, AddrMode1, SizeSpecial, IndexModeNone, asm, "", pattern>; +class JTI2<dag ops, string asm, list<dag> pattern> + : XI<ops, AddrMode2, SizeSpecial, IndexModeNone, asm, "", pattern>; + +/// AsXI1_bin_c_irs - Same as AsI1_bin_irs but without the predicate operand and +/// setting carry bit. But it can optionally set CPSR. +multiclass AsXI1_bin_c_irs<string opc, PatFrag opnode> { + def ri : AXI1<(ops GPR:$dst, GPR:$a, so_imm:$b, cc_out:$s), + !strconcat(opc, "${s} $dst, $a, $b"), + [(set GPR:$dst, (opnode GPR:$a, so_imm:$b))]>, Imp<[CPSR], []>; + def rr : AXI1<(ops GPR:$dst, GPR:$a, GPR:$b, cc_out:$s), + !strconcat(opc, "${s} $dst, $a, $b"), + [(set GPR:$dst, (opnode GPR:$a, GPR:$b))]>, Imp<[CPSR], []>; + def rs : AXI1<(ops GPR:$dst, GPR:$a, so_reg:$b, cc_out:$s), + !strconcat(opc, "${s} $dst, $a, $b"), + [(set GPR:$dst, (opnode GPR:$a, so_reg:$b))]>, Imp<[CPSR], []>; +} + +//===----------------------------------------------------------------------===// +// Instructions +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Miscellaneous Instructions. +// +def IMPLICIT_DEF_GPR : +PseudoInst<(ops GPR:$rD, pred:$p), + "@ IMPLICIT_DEF_GPR $rD", + [(set GPR:$rD, (undef))]>; + + +/// CONSTPOOL_ENTRY - This instruction represents a floating constant pool in +/// the function. The first operand is the ID# for this instruction, the second +/// is the index into the MachineConstantPool that this is, the third is the +/// size in bytes of this constant pool entry. +let isNotDuplicable = 1 in +def CONSTPOOL_ENTRY : +PseudoInst<(ops cpinst_operand:$instid, cpinst_operand:$cpidx, i32imm:$size), + "${instid:label} ${cpidx:cpentry}", []>; + +def ADJCALLSTACKUP : +PseudoInst<(ops i32imm:$amt, pred:$p), + "@ ADJCALLSTACKUP $amt", + [(ARMcallseq_end imm:$amt)]>, Imp<[SP],[SP]>; + +def ADJCALLSTACKDOWN : +PseudoInst<(ops i32imm:$amt, pred:$p), + "@ ADJCALLSTACKDOWN $amt", + [(ARMcallseq_start imm:$amt)]>, Imp<[SP],[SP]>; + +def DWARF_LOC : +PseudoInst<(ops i32imm:$line, i32imm:$col, i32imm:$file), + ".loc $file, $line, $col", + [(dwarf_loc (i32 imm:$line), (i32 imm:$col), (i32 imm:$file))]>; + +let isNotDuplicable = 1 in { +def PICADD : AXI1<(ops GPR:$dst, GPR:$a, pclabel:$cp, pred:$p), + "$cp:\n\tadd$p $dst, pc, $a", + [(set GPR:$dst, (ARMpic_add GPR:$a, imm:$cp))]>; + +let isLoad = 1, AddedComplexity = 10 in { +def PICLD : AXI2<(ops GPR:$dst, addrmodepc:$addr, pred:$p), + "${addr:label}:\n\tldr$p $dst, $addr", + [(set GPR:$dst, (load addrmodepc:$addr))]>; + +def PICLDZH : AXI3<(ops GPR:$dst, addrmodepc:$addr, pred:$p), + "${addr:label}:\n\tldr${p}h $dst, $addr", + [(set GPR:$dst, (zextloadi16 addrmodepc:$addr))]>; + +def PICLDZB : AXI2<(ops GPR:$dst, addrmodepc:$addr, pred:$p), + "${addr:label}:\n\tldr${p}b $dst, $addr", + [(set GPR:$dst, (zextloadi8 addrmodepc:$addr))]>; + +def PICLDH : AXI3<(ops GPR:$dst, addrmodepc:$addr, pred:$p), + "${addr:label}:\n\tldr${p}h $dst, $addr", + [(set GPR:$dst, (extloadi16 addrmodepc:$addr))]>; + +def PICLDB : AXI2<(ops GPR:$dst, addrmodepc:$addr, pred:$p), + "${addr:label}:\n\tldr${p}b $dst, $addr", + [(set GPR:$dst, (extloadi8 addrmodepc:$addr))]>; + +def PICLDSH : AXI3<(ops GPR:$dst, addrmodepc:$addr, pred:$p), + "${addr:label}:\n\tldr${p}sh $dst, $addr", + [(set GPR:$dst, (sextloadi16 addrmodepc:$addr))]>; + +def PICLDSB : AXI3<(ops GPR:$dst, addrmodepc:$addr, pred:$p), + "${addr:label}:\n\tldr${p}sb $dst, $addr", + [(set GPR:$dst, (sextloadi8 addrmodepc:$addr))]>; +} +let isStore = 1, AddedComplexity = 10 in { +def PICSTR : AXI2<(ops GPR:$src, addrmodepc:$addr, pred:$p), + "${addr:label}:\n\tstr$p $src, $addr", + [(store GPR:$src, addrmodepc:$addr)]>; + +def PICSTRH : AXI3<(ops GPR:$src, addrmodepc:$addr, pred:$p), + "${addr:label}:\n\tstr${p}h $src, $addr", + [(truncstorei16 GPR:$src, addrmodepc:$addr)]>; + +def PICSTRB : AXI2<(ops GPR:$src, addrmodepc:$addr, pred:$p), + "${addr:label}:\n\tstr${p}b $src, $addr", + [(truncstorei8 GPR:$src, addrmodepc:$addr)]>; +} +} + +//===----------------------------------------------------------------------===// +// Control Flow Instructions. +// + +let isReturn = 1, isTerminator = 1 in + def BX_RET : AI<(ops), "bx", " lr", [(ARMretflag)]>; + +// FIXME: remove when we have a way to marking a MI with these properties. +let isLoad = 1, isReturn = 1, isTerminator = 1 in + def LDM_RET : AXI4<(ops addrmode4:$addr, pred:$p, reglist:$dst1, variable_ops), + "ldm${p}${addr:submode} $addr, $dst1", + []>; + +let isCall = 1, noResults = 1, + Defs = [R0, R1, R2, R3, R12, LR, + D0, D1, D2, D3, D4, D5, D6, D7, CPSR] in { + def BL : AXI<(ops i32imm:$func, variable_ops), + "bl ${func:call}", + [(ARMcall tglobaladdr:$func)]>; + + def BL_pred : AI<(ops i32imm:$func, variable_ops), + "bl", " ${func:call}", + [(ARMcall_pred tglobaladdr:$func)]>; + + // ARMv5T and above + def BLX : AXI<(ops GPR:$dst, variable_ops), + "blx $dst", + [(ARMcall GPR:$dst)]>, Requires<[IsARM, HasV5T]>; + let Uses = [LR] in { + // ARMv4T + def BX : AXIx2<(ops GPR:$dst, variable_ops), + "mov lr, pc\n\tbx $dst", + [(ARMcall_nolink GPR:$dst)]>; + } +} + +let isBranch = 1, isTerminator = 1, noResults = 1 in { + // B is "predicable" since it can be xformed into a Bcc. + let isBarrier = 1 in { + let isPredicable = 1 in + def B : AXI<(ops brtarget:$dst), "b $dst", + [(br bb:$dst)]>; + + let isNotDuplicable = 1 in { + def BR_JTr : JTI<(ops GPR:$dst, jtblock_operand:$jt, i32imm:$id), + "mov pc, $dst \n$jt", + [(ARMbrjt GPR:$dst, tjumptable:$jt, imm:$id)]>; + def BR_JTm : JTI2<(ops addrmode2:$dst, jtblock_operand:$jt, i32imm:$id), + "ldr pc, $dst \n$jt", + [(ARMbrjt (i32 (load addrmode2:$dst)), tjumptable:$jt, + imm:$id)]>; + def BR_JTadd : JTI1<(ops GPR:$dst, GPR:$idx, jtblock_operand:$jt, i32imm:$id), + "add pc, $dst, $idx \n$jt", + [(ARMbrjt (add GPR:$dst, GPR:$idx), tjumptable:$jt, + imm:$id)]>; + } + } + + // FIXME: should be able to write a pattern for ARMBrcond, but can't use + // a two-value operand where a dag node expects two operands. :( + def Bcc : AI<(ops brtarget:$dst), "b", " $dst", + [/*(ARMbrcond bb:$dst, imm:$cc, CCR:$ccr)*/]>; +} + +//===----------------------------------------------------------------------===// +// Load / store Instructions. +// + +// Load +let isLoad = 1 in { +def LDR : AI2<(ops GPR:$dst, addrmode2:$addr), + "ldr", " $dst, $addr", + [(set GPR:$dst, (load addrmode2:$addr))]>; + +// Special LDR for loads from non-pc-relative constpools. +let isReMaterializable = 1 in +def LDRcp : AI2<(ops GPR:$dst, addrmode2:$addr), + "ldr", " $dst, $addr", []>; + +// Loads with zero extension +def LDRH : AI3<(ops GPR:$dst, addrmode3:$addr), + "ldr", "h $dst, $addr", + [(set GPR:$dst, (zextloadi16 addrmode3:$addr))]>; + +def LDRB : AI2<(ops GPR:$dst, addrmode2:$addr), + "ldr", "b $dst, $addr", + [(set GPR:$dst, (zextloadi8 addrmode2:$addr))]>; + +// Loads with sign extension +def LDRSH : AI3<(ops GPR:$dst, addrmode3:$addr), + "ldr", "sh $dst, $addr", + [(set GPR:$dst, (sextloadi16 addrmode3:$addr))]>; + +def LDRSB : AI3<(ops GPR:$dst, addrmode3:$addr), + "ldr", "sb $dst, $addr", + [(set GPR:$dst, (sextloadi8 addrmode3:$addr))]>; + +// Load doubleword +def LDRD : AI3<(ops GPR:$dst, addrmode3:$addr), + "ldr", "d $dst, $addr", + []>, Requires<[IsARM, HasV5T]>; + +// Indexed loads +def LDR_PRE : AI2pr<(ops GPR:$dst, GPR:$base_wb, addrmode2:$addr), + "ldr", " $dst, $addr!", "$addr.base = $base_wb", []>; + +def LDR_POST : AI2po<(ops GPR:$dst, GPR:$base_wb, GPR:$base, am2offset:$offset), + "ldr", " $dst, [$base], $offset", "$base = $base_wb", []>; + +def LDRH_PRE : AI3pr<(ops GPR:$dst, GPR:$base_wb, addrmode3:$addr), + "ldr", "h $dst, $addr!", "$addr.base = $base_wb", []>; + +def LDRH_POST : AI3po<(ops GPR:$dst, GPR:$base_wb, GPR:$base,am3offset:$offset), + "ldr", "h $dst, [$base], $offset", "$base = $base_wb", []>; + +def LDRB_PRE : AI2pr<(ops GPR:$dst, GPR:$base_wb, addrmode2:$addr), + "ldr", "b $dst, $addr!", "$addr.base = $base_wb", []>; + +def LDRB_POST : AI2po<(ops GPR:$dst, GPR:$base_wb, GPR:$base,am2offset:$offset), + "ldr", "b $dst, [$base], $offset", "$base = $base_wb", []>; + +def LDRSH_PRE : AI3pr<(ops GPR:$dst, GPR:$base_wb, addrmode3:$addr), + "ldr", "sh $dst, $addr!", "$addr.base = $base_wb", []>; + +def LDRSH_POST: AI3po<(ops GPR:$dst, GPR:$base_wb, GPR:$base,am3offset:$offset), + "ldr", "sh $dst, [$base], $offset", "$base = $base_wb", []>; + +def LDRSB_PRE : AI3pr<(ops GPR:$dst, GPR:$base_wb, addrmode3:$addr), + "ldr", "sb $dst, $addr!", "$addr.base = $base_wb", []>; + +def LDRSB_POST: AI3po<(ops GPR:$dst, GPR:$base_wb, GPR:$base,am3offset:$offset), + "ldr", "sb $dst, [$base], $offset", "$base = $base_wb", []>; +} // isLoad + +// Store +let isStore = 1 in { +def STR : AI2<(ops GPR:$src, addrmode2:$addr), + "str", " $src, $addr", + [(store GPR:$src, addrmode2:$addr)]>; + +// Stores with truncate +def STRH : AI3<(ops GPR:$src, addrmode3:$addr), + "str", "h $src, $addr", + [(truncstorei16 GPR:$src, addrmode3:$addr)]>; + +def STRB : AI2<(ops GPR:$src, addrmode2:$addr), + "str", "b $src, $addr", + [(truncstorei8 GPR:$src, addrmode2:$addr)]>; + +// Store doubleword +def STRD : AI3<(ops GPR:$src, addrmode3:$addr), + "str", "d $src, $addr", + []>, Requires<[IsARM, HasV5T]>; + +// Indexed stores +def STR_PRE : AI2pr<(ops GPR:$base_wb, GPR:$src, GPR:$base, am2offset:$offset), + "str", " $src, [$base, $offset]!", "$base = $base_wb", + [(set GPR:$base_wb, + (pre_store GPR:$src, GPR:$base, am2offset:$offset))]>; + +def STR_POST : AI2po<(ops GPR:$base_wb, GPR:$src, GPR:$base,am2offset:$offset), + "str", " $src, [$base], $offset", "$base = $base_wb", + [(set GPR:$base_wb, + (post_store GPR:$src, GPR:$base, am2offset:$offset))]>; + +def STRH_PRE : AI3pr<(ops GPR:$base_wb, GPR:$src, GPR:$base,am3offset:$offset), + "str", "h $src, [$base, $offset]!", "$base = $base_wb", + [(set GPR:$base_wb, + (pre_truncsti16 GPR:$src, GPR:$base,am3offset:$offset))]>; + +def STRH_POST: AI3po<(ops GPR:$base_wb, GPR:$src, GPR:$base,am3offset:$offset), + "str", "h $src, [$base], $offset", "$base = $base_wb", + [(set GPR:$base_wb, (post_truncsti16 GPR:$src, + GPR:$base, am3offset:$offset))]>; + +def STRB_PRE : AI2pr<(ops GPR:$base_wb, GPR:$src, GPR:$base,am2offset:$offset), + "str", "b $src, [$base, $offset]!", "$base = $base_wb", + [(set GPR:$base_wb, (pre_truncsti8 GPR:$src, + GPR:$base, am2offset:$offset))]>; + +def STRB_POST: AI2po<(ops GPR:$base_wb, GPR:$src, GPR:$base,am2offset:$offset), + "str", "b $src, [$base], $offset", "$base = $base_wb", + [(set GPR:$base_wb, (post_truncsti8 GPR:$src, + GPR:$base, am2offset:$offset))]>; +} // isStore + +//===----------------------------------------------------------------------===// +// Load / store multiple Instructions. +// + +let isLoad = 1 in +def LDM : AXI4<(ops addrmode4:$addr, pred:$p, reglist:$dst1, variable_ops), + "ldm${p}${addr:submode} $addr, $dst1", + []>; + +let isStore = 1 in +def STM : AXI4<(ops addrmode4:$addr, pred:$p, reglist:$src1, variable_ops), + "stm${p}${addr:submode} $addr, $src1", + []>; + +//===----------------------------------------------------------------------===// +// Move Instructions. +// + +def MOVr : AsI1<(ops GPR:$dst, GPR:$src), + "mov", " $dst, $src", []>; +def MOVs : AsI1<(ops GPR:$dst, so_reg:$src), + "mov", " $dst, $src", [(set GPR:$dst, so_reg:$src)]>; + +let isReMaterializable = 1 in +def MOVi : AsI1<(ops GPR:$dst, so_imm:$src), + "mov", " $dst, $src", [(set GPR:$dst, so_imm:$src)]>; + +def MOVrx : AsI1<(ops GPR:$dst, GPR:$src), + "mov", " $dst, $src, rrx", + [(set GPR:$dst, (ARMrrx GPR:$src))]>; + +// These aren't really mov instructions, but we have to define them this way +// due to flag operands. + +def MOVsrl_flag : AI1<(ops GPR:$dst, GPR:$src), + "mov", "s $dst, $src, lsr #1", + [(set GPR:$dst, (ARMsrl_flag GPR:$src))]>, Imp<[], [CPSR]>; +def MOVsra_flag : AI1<(ops GPR:$dst, GPR:$src), + "mov", "s $dst, $src, asr #1", + [(set GPR:$dst, (ARMsra_flag GPR:$src))]>, Imp<[], [CPSR]>; + +//===----------------------------------------------------------------------===// +// Extend Instructions. +// + +// Sign extenders + +defm SXTB : AI_unary_rrot<"sxtb", UnOpFrag<(sext_inreg node:$Src, i8)>>; +defm SXTH : AI_unary_rrot<"sxth", UnOpFrag<(sext_inreg node:$Src, i16)>>; + +defm SXTAB : AI_bin_rrot<"sxtab", + BinOpFrag<(add node:$LHS, (sext_inreg node:$RHS, i8))>>; +defm SXTAH : AI_bin_rrot<"sxtah", + BinOpFrag<(add node:$LHS, (sext_inreg node:$RHS,i16))>>; + +// TODO: SXT(A){B|H}16 + +// Zero extenders + +let AddedComplexity = 16 in { +defm UXTB : AI_unary_rrot<"uxtb" , UnOpFrag<(and node:$Src, 0x000000FF)>>; +defm UXTH : AI_unary_rrot<"uxth" , UnOpFrag<(and node:$Src, 0x0000FFFF)>>; +defm UXTB16 : AI_unary_rrot<"uxtb16", UnOpFrag<(and node:$Src, 0x00FF00FF)>>; + +def : ARMV6Pat<(and (shl GPR:$Src, 8), 0xFF00FF), + (UXTB16r_rot GPR:$Src, 24)>; +def : ARMV6Pat<(and (srl GPR:$Src, 8), 0xFF00FF), + (UXTB16r_rot GPR:$Src, 8)>; + +defm UXTAB : AI_bin_rrot<"uxtab", + BinOpFrag<(add node:$LHS, (and node:$RHS, 0x00FF))>>; +defm UXTAH : AI_bin_rrot<"uxtah", + BinOpFrag<(add node:$LHS, (and node:$RHS, 0xFFFF))>>; +} + +// This isn't safe in general, the add is two 16-bit units, not a 32-bit add. +//defm UXTAB16 : xxx<"uxtab16", 0xff00ff>; + +// TODO: UXT(A){B|H}16 + +//===----------------------------------------------------------------------===// +// Arithmetic Instructions. +// + +defm ADD : AsI1_bin_irs<"add", BinOpFrag<(add node:$LHS, node:$RHS)>>; +defm SUB : AsI1_bin_irs<"sub", BinOpFrag<(sub node:$LHS, node:$RHS)>>; + +// ADD and SUB with 's' bit set. +defm ADDS : ASI1_bin_s_irs<"add", BinOpFrag<(addc node:$LHS, node:$RHS)>>; +defm SUBS : ASI1_bin_s_irs<"sub", BinOpFrag<(subc node:$LHS, node:$RHS)>>; + +// FIXME: Do not allow ADC / SBC to be predicated for now. +defm ADC : AsXI1_bin_c_irs<"adc", BinOpFrag<(adde node:$LHS, node:$RHS)>>; +defm SBC : AsXI1_bin_c_irs<"sbc", BinOpFrag<(sube node:$LHS, node:$RHS)>>; + +// These don't define reg/reg forms, because they are handled above. +def RSBri : AsI1<(ops GPR:$dst, GPR:$a, so_imm:$b), + "rsb", " $dst, $a, $b", + [(set GPR:$dst, (sub so_imm:$b, GPR:$a))]>; + +def RSBrs : AsI1<(ops GPR:$dst, GPR:$a, so_reg:$b), + "rsb", " $dst, $a, $b", + [(set GPR:$dst, (sub so_reg:$b, GPR:$a))]>; + +// RSB with 's' bit set. +def RSBSri : AI1<(ops GPR:$dst, GPR:$a, so_imm:$b), + "rsb", "s $dst, $a, $b", + [(set GPR:$dst, (subc so_imm:$b, GPR:$a))]>, Imp<[], [CPSR]>; +def RSBSrs : AI1<(ops GPR:$dst, GPR:$a, so_reg:$b), + "rsb", "s $dst, $a, $b", + [(set GPR:$dst, (subc so_reg:$b, GPR:$a))]>, Imp<[], [CPSR]>; + +// FIXME: Do not allow RSC to be predicated for now. But they can set CPSR. +def RSCri : AXI1<(ops GPR:$dst, GPR:$a, so_imm:$b, cc_out:$s), + "rsc${s} $dst, $a, $b", + [(set GPR:$dst, (sube so_imm:$b, GPR:$a))]>, Imp<[CPSR], []>; +def RSCrs : AXI1<(ops GPR:$dst, GPR:$a, so_reg:$b, cc_out:$s), + "rsc${s} $dst, $a, $b", + [(set GPR:$dst, (sube so_reg:$b, GPR:$a))]>, Imp<[CPSR], []>; + +// (sub X, imm) gets canonicalized to (add X, -imm). Match this form. +def : ARMPat<(add GPR:$src, so_imm_neg:$imm), + (SUBri GPR:$src, so_imm_neg:$imm)>; + +//def : ARMPat<(addc GPR:$src, so_imm_neg:$imm), +// (SUBSri GPR:$src, so_imm_neg:$imm)>; +//def : ARMPat<(adde GPR:$src, so_imm_neg:$imm), +// (SBCri GPR:$src, so_imm_neg:$imm)>; + +// Note: These are implemented in C++ code, because they have to generate +// ADD/SUBrs instructions, which use a complex pattern that a xform function +// cannot produce. +// (mul X, 2^n+1) -> (add (X << n), X) +// (mul X, 2^n-1) -> (rsb X, (X << n)) + + +//===----------------------------------------------------------------------===// +// Bitwise Instructions. +// + +defm AND : AsI1_bin_irs<"and", BinOpFrag<(and node:$LHS, node:$RHS)>>; +defm ORR : AsI1_bin_irs<"orr", BinOpFrag<(or node:$LHS, node:$RHS)>>; +defm EOR : AsI1_bin_irs<"eor", BinOpFrag<(xor node:$LHS, node:$RHS)>>; +defm BIC : AsI1_bin_irs<"bic", BinOpFrag<(and node:$LHS, (not node:$RHS))>>; + +def MVNr : AsI<(ops GPR:$dst, GPR:$src), + "mvn", " $dst, $src", [(set GPR:$dst, (not GPR:$src))]>; +def MVNs : AsI<(ops GPR:$dst, so_reg:$src), + "mvn", " $dst, $src", [(set GPR:$dst, (not so_reg:$src))]>; +let isReMaterializable = 1 in +def MVNi : AsI<(ops GPR:$dst, so_imm:$imm), + "mvn", " $dst, $imm", [(set GPR:$dst, so_imm_not:$imm)]>; + +def : ARMPat<(and GPR:$src, so_imm_not:$imm), + (BICri GPR:$src, so_imm_not:$imm)>; + +//===----------------------------------------------------------------------===// +// Multiply Instructions. +// + +def MUL : AsI<(ops GPR:$dst, GPR:$a, GPR:$b), + "mul", " $dst, $a, $b", + [(set GPR:$dst, (mul GPR:$a, GPR:$b))]>; + +def MLA : AsI<(ops GPR:$dst, GPR:$a, GPR:$b, GPR:$c), + "mla", " $dst, $a, $b, $c", + [(set GPR:$dst, (add (mul GPR:$a, GPR:$b), GPR:$c))]>; + +// Extra precision multiplies with low / high results +def SMULL : AsI<(ops GPR:$ldst, GPR:$hdst, GPR:$a, GPR:$b), + "smull", " $ldst, $hdst, $a, $b", []>; + +def UMULL : AsI<(ops GPR:$ldst, GPR:$hdst, GPR:$a, GPR:$b), + "umull", " $ldst, $hdst, $a, $b", []>; + +// Multiply + accumulate +def SMLAL : AsI<(ops GPR:$ldst, GPR:$hdst, GPR:$a, GPR:$b), + "smlal", " $ldst, $hdst, $a, $b", []>; + +def UMLAL : AsI<(ops GPR:$ldst, GPR:$hdst, GPR:$a, GPR:$b), + "umlal", " $ldst, $hdst, $a, $b", []>; + +def UMAAL : AI<(ops GPR:$ldst, GPR:$hdst, GPR:$a, GPR:$b), + "umaal", " $ldst, $hdst, $a, $b", []>, + Requires<[IsARM, HasV6]>; + +// Most significant word multiply +def SMMUL : AI<(ops GPR:$dst, GPR:$a, GPR:$b), + "smmul", " $dst, $a, $b", + [(set GPR:$dst, (mulhs GPR:$a, GPR:$b))]>, + Requires<[IsARM, HasV6]>; + +def SMMLA : AI<(ops GPR:$dst, GPR:$a, GPR:$b, GPR:$c), + "smmla", " $dst, $a, $b, $c", + [(set GPR:$dst, (add (mulhs GPR:$a, GPR:$b), GPR:$c))]>, + Requires<[IsARM, HasV6]>; + + +def SMMLS : AI<(ops GPR:$dst, GPR:$a, GPR:$b, GPR:$c), + "smmls", " $dst, $a, $b, $c", + [(set GPR:$dst, (sub GPR:$c, (mulhs GPR:$a, GPR:$b)))]>, + Requires<[IsARM, HasV6]>; + +multiclass AI_smul<string opc, PatFrag opnode> { + def BB : AI<(ops GPR:$dst, GPR:$a, GPR:$b), + !strconcat(opc, "bb"), " $dst, $a, $b", + [(set GPR:$dst, (opnode (sext_inreg GPR:$a, i16), + (sext_inreg GPR:$b, i16)))]>, + Requires<[IsARM, HasV5TE]>; + def BT : AI<(ops GPR:$dst, GPR:$a, GPR:$b), + !strconcat(opc, "bt"), " $dst, $a, $b", + [(set GPR:$dst, (opnode (sext_inreg GPR:$a, i16), + (sra GPR:$b, 16)))]>, + Requires<[IsARM, HasV5TE]>; + def TB : AI<(ops GPR:$dst, GPR:$a, GPR:$b), + !strconcat(opc, "tb"), " $dst, $a, $b", + [(set GPR:$dst, (opnode (sra GPR:$a, 16), + (sext_inreg GPR:$b, i16)))]>, + Requires<[IsARM, HasV5TE]>; + def TT : AI<(ops GPR:$dst, GPR:$a, GPR:$b), + !strconcat(opc, "tt"), " $dst, $a, $b", + [(set GPR:$dst, (opnode (sra GPR:$a, 16), + (sra GPR:$b, 16)))]>, + Requires<[IsARM, HasV5TE]>; + def WB : AI<(ops GPR:$dst, GPR:$a, GPR:$b), + !strconcat(opc, "wb"), " $dst, $a, $b", + [(set GPR:$dst, (sra (opnode GPR:$a, + (sext_inreg GPR:$b, i16)), 16))]>, + Requires<[IsARM, HasV5TE]>; + def WT : AI<(ops GPR:$dst, GPR:$a, GPR:$b), + !strconcat(opc, "wt"), " $dst, $a, $b", + [(set GPR:$dst, (sra (opnode GPR:$a, + (sra GPR:$b, 16)), 16))]>, + Requires<[IsARM, HasV5TE]>; +} + +multiclass AI_smla<string opc, PatFrag opnode> { + def BB : AI<(ops GPR:$dst, GPR:$a, GPR:$b, GPR:$acc), + !strconcat(opc, "bb"), " $dst, $a, $b, $acc", + [(set GPR:$dst, (add GPR:$acc, + (opnode (sext_inreg GPR:$a, i16), + (sext_inreg GPR:$b, i16))))]>, + Requires<[IsARM, HasV5TE]>; + def BT : AI<(ops GPR:$dst, GPR:$a, GPR:$b, GPR:$acc), + !strconcat(opc, "bt"), " $dst, $a, $b, $acc", + [(set GPR:$dst, (add GPR:$acc, (opnode (sext_inreg GPR:$a, i16), + (sra GPR:$b, 16))))]>, + Requires<[IsARM, HasV5TE]>; + def TB : AI<(ops GPR:$dst, GPR:$a, GPR:$b, GPR:$acc), + !strconcat(opc, "tb"), " $dst, $a, $b, $acc", + [(set GPR:$dst, (add GPR:$acc, (opnode (sra GPR:$a, 16), + (sext_inreg GPR:$b, i16))))]>, + Requires<[IsARM, HasV5TE]>; + def TT : AI<(ops GPR:$dst, GPR:$a, GPR:$b, GPR:$acc), + !strconcat(opc, "tt"), " $dst, $a, $b, $acc", + [(set GPR:$dst, (add GPR:$acc, (opnode (sra GPR:$a, 16), + (sra GPR:$b, 16))))]>, + Requires<[IsARM, HasV5TE]>; + + def WB : AI<(ops GPR:$dst, GPR:$a, GPR:$b, GPR:$acc), + !strconcat(opc, "wb"), " $dst, $a, $b, $acc", + [(set GPR:$dst, (add GPR:$acc, (sra (opnode GPR:$a, + (sext_inreg GPR:$b, i16)), 16)))]>, + Requires<[IsARM, HasV5TE]>; + def WT : AI<(ops GPR:$dst, GPR:$a, GPR:$b, GPR:$acc), + !strconcat(opc, "wt"), " $dst, $a, $b, $acc", + [(set GPR:$dst, (add GPR:$acc, (sra (opnode GPR:$a, + (sra GPR:$b, 16)), 16)))]>, + Requires<[IsARM, HasV5TE]>; +} + +defm SMUL : AI_smul<"smul", BinOpFrag<(mul node:$LHS, node:$RHS)>>; +defm SMLA : AI_smla<"smla", BinOpFrag<(mul node:$LHS, node:$RHS)>>; + +// TODO: Halfword multiple accumulate long: SMLAL<x><y> +// TODO: Dual halfword multiple: SMUAD, SMUSD, SMLAD, SMLSD, SMLALD, SMLSLD + +//===----------------------------------------------------------------------===// +// Misc. Arithmetic Instructions. +// + +def CLZ : AI<(ops GPR:$dst, GPR:$src), + "clz", " $dst, $src", + [(set GPR:$dst, (ctlz GPR:$src))]>, Requires<[IsARM, HasV5T]>; + +def REV : AI<(ops GPR:$dst, GPR:$src), + "rev", " $dst, $src", + [(set GPR:$dst, (bswap GPR:$src))]>, Requires<[IsARM, HasV6]>; + +def REV16 : AI<(ops GPR:$dst, GPR:$src), + "rev16", " $dst, $src", + [(set GPR:$dst, + (or (and (srl GPR:$src, 8), 0xFF), + (or (and (shl GPR:$src, 8), 0xFF00), + (or (and (srl GPR:$src, 8), 0xFF0000), + (and (shl GPR:$src, 8), 0xFF000000)))))]>, + Requires<[IsARM, HasV6]>; + +def REVSH : AI<(ops GPR:$dst, GPR:$src), + "revsh", " $dst, $src", + [(set GPR:$dst, + (sext_inreg + (or (srl (and GPR:$src, 0xFF00), 8), + (shl GPR:$src, 8)), i16))]>, + Requires<[IsARM, HasV6]>; + +def PKHBT : AI<(ops GPR:$dst, GPR:$src1, GPR:$src2, i32imm:$shamt), + "pkhbt", " $dst, $src1, $src2, LSL $shamt", + [(set GPR:$dst, (or (and GPR:$src1, 0xFFFF), + (and (shl GPR:$src2, (i32 imm:$shamt)), + 0xFFFF0000)))]>, + Requires<[IsARM, HasV6]>; + +// Alternate cases for PKHBT where identities eliminate some nodes. +def : ARMV6Pat<(or (and GPR:$src1, 0xFFFF), (and GPR:$src2, 0xFFFF0000)), + (PKHBT GPR:$src1, GPR:$src2, 0)>; +def : ARMV6Pat<(or (and GPR:$src1, 0xFFFF), (shl GPR:$src2, imm16_31:$shamt)), + (PKHBT GPR:$src1, GPR:$src2, imm16_31:$shamt)>; + + +def PKHTB : AI<(ops GPR:$dst, GPR:$src1, GPR:$src2, i32imm:$shamt), + "pkhtb", " $dst, $src1, $src2, ASR $shamt", + [(set GPR:$dst, (or (and GPR:$src1, 0xFFFF0000), + (and (sra GPR:$src2, imm16_31:$shamt), + 0xFFFF)))]>, Requires<[IsARM, HasV6]>; + +// Alternate cases for PKHTB where identities eliminate some nodes. Note that +// a shift amount of 0 is *not legal* here, it is PKHBT instead. +def : ARMV6Pat<(or (and GPR:$src1, 0xFFFF0000), (srl GPR:$src2, 16)), + (PKHTB GPR:$src1, GPR:$src2, 16)>; +def : ARMV6Pat<(or (and GPR:$src1, 0xFFFF0000), + (and (srl GPR:$src2, imm1_15:$shamt), 0xFFFF)), + (PKHTB GPR:$src1, GPR:$src2, imm1_15:$shamt)>; + + +//===----------------------------------------------------------------------===// +// Comparison Instructions... +// + +defm CMP : AI1_cmp_irs<"cmp", BinOpFrag<(ARMcmp node:$LHS, node:$RHS)>>; +defm CMN : AI1_cmp_irs<"cmn", BinOpFrag<(ARMcmp node:$LHS,(ineg node:$RHS))>>; + +// Note that TST/TEQ don't set all the same flags that CMP does! +defm TST : AI1_cmp_irs<"tst", BinOpFrag<(ARMcmpNZ (and node:$LHS, node:$RHS), 0)>>; +defm TEQ : AI1_cmp_irs<"teq", BinOpFrag<(ARMcmpNZ (xor node:$LHS, node:$RHS), 0)>>; + +defm CMPnz : AI1_cmp_irs<"cmp", BinOpFrag<(ARMcmpNZ node:$LHS, node:$RHS)>>; +defm CMNnz : AI1_cmp_irs<"cmn", BinOpFrag<(ARMcmpNZ node:$LHS,(ineg node:$RHS))>>; + +def : ARMPat<(ARMcmp GPR:$src, so_imm_neg:$imm), + (CMNri GPR:$src, so_imm_neg:$imm)>; + +def : ARMPat<(ARMcmpNZ GPR:$src, so_imm_neg:$imm), + (CMNri GPR:$src, so_imm_neg:$imm)>; + + +// Conditional moves +// FIXME: should be able to write a pattern for ARMcmov, but can't use +// a two-value operand where a dag node expects two operands. :( +def MOVCCr : AI<(ops GPR:$dst, GPR:$false, GPR:$true), + "mov", " $dst, $true", + [/*(set GPR:$dst, (ARMcmov GPR:$false, GPR:$true, imm:$cc, CCR:$ccr))*/]>, + RegConstraint<"$false = $dst">; + +def MOVCCs : AI<(ops GPR:$dst, GPR:$false, so_reg:$true), + "mov", " $dst, $true", + [/*(set GPR:$dst, (ARMcmov GPR:$false, so_reg:$true, imm:$cc, CCR:$ccr))*/]>, + RegConstraint<"$false = $dst">; + +def MOVCCi : AI<(ops GPR:$dst, GPR:$false, so_imm:$true), + "mov", " $dst, $true", + [/*(set GPR:$dst, (ARMcmov GPR:$false, so_imm:$true, imm:$cc, CCR:$ccr))*/]>, + RegConstraint<"$false = $dst">; + + +// LEApcrel - Load a pc-relative address into a register without offending the +// assembler. +def LEApcrel : AXI1<(ops GPR:$dst, i32imm:$label, pred:$p), + !strconcat(!strconcat(".set PCRELV${:uid}, ($label-(", + "${:private}PCRELL${:uid}+8))\n"), + !strconcat("${:private}PCRELL${:uid}:\n\t", + "add$p $dst, pc, #PCRELV${:uid}")), + []>; + +def LEApcrelJT : AXI1<(ops GPR:$dst, i32imm:$label, i32imm:$id, pred:$p), + !strconcat(!strconcat(".set PCRELV${:uid}, (${label}_${id:no_hash}-(", + "${:private}PCRELL${:uid}+8))\n"), + !strconcat("${:private}PCRELL${:uid}:\n\t", + "add$p $dst, pc, #PCRELV${:uid}")), + []>; + +//===----------------------------------------------------------------------===// +// TLS Instructions +// + +// __aeabi_read_tp preserves the registers r1-r3. +let isCall = 1, + Defs = [R0, R12, LR, CPSR] in { + def TPsoft : AXI<(ops), + "bl __aeabi_read_tp", + [(set R0, ARMthread_pointer)]>; +} + +//===----------------------------------------------------------------------===// +// Non-Instruction Patterns +// + +// ConstantPool, GlobalAddress, and JumpTable +def : ARMPat<(ARMWrapper tglobaladdr :$dst), (LEApcrel tglobaladdr :$dst)>; +def : ARMPat<(ARMWrapper tconstpool :$dst), (LEApcrel tconstpool :$dst)>; +def : ARMPat<(ARMWrapperJT tjumptable:$dst, imm:$id), + (LEApcrelJT tjumptable:$dst, imm:$id)>; + +// Large immediate handling. + +// Two piece so_imms. +let isReMaterializable = 1 in +def MOVi2pieces : AI1x2<(ops GPR:$dst, so_imm2part:$src), + "mov", " $dst, $src", + [(set GPR:$dst, so_imm2part:$src)]>; + +def : ARMPat<(or GPR:$LHS, so_imm2part:$RHS), + (ORRri (ORRri GPR:$LHS, (so_imm2part_1 imm:$RHS)), + (so_imm2part_2 imm:$RHS))>; +def : ARMPat<(xor GPR:$LHS, so_imm2part:$RHS), + (EORri (EORri GPR:$LHS, (so_imm2part_1 imm:$RHS)), + (so_imm2part_2 imm:$RHS))>; + +// TODO: add,sub,and, 3-instr forms? + + +// Direct calls +def : ARMPat<(ARMcall texternalsym:$func), (BL texternalsym:$func)>; + +// zextload i1 -> zextload i8 +def : ARMPat<(zextloadi1 addrmode2:$addr), (LDRB addrmode2:$addr)>; + +// extload -> zextload +def : ARMPat<(extloadi1 addrmode2:$addr), (LDRB addrmode2:$addr)>; +def : ARMPat<(extloadi8 addrmode2:$addr), (LDRB addrmode2:$addr)>; +def : ARMPat<(extloadi16 addrmode3:$addr), (LDRH addrmode3:$addr)>; + +// truncstore i1 -> truncstore i8 +def : ARMPat<(truncstorei1 GPR:$src, addrmode2:$dst), + (STRB GPR:$src, addrmode2:$dst)>; +def : ARMPat<(pre_truncsti1 GPR:$src, GPR:$base, am2offset:$offset), + (STRB_PRE GPR:$src, GPR:$base, am2offset:$offset)>; +def : ARMPat<(post_truncsti1 GPR:$src, GPR:$base, am2offset:$offset), + (STRB_POST GPR:$src, GPR:$base, am2offset:$offset)>; + +// smul* and smla* +def : ARMV5TEPat<(mul (sra (shl GPR:$a, 16), 16), (sra (shl GPR:$b, 16), 16)), + (SMULBB GPR:$a, GPR:$b)>; +def : ARMV5TEPat<(mul sext_16_node:$a, sext_16_node:$b), + (SMULBB GPR:$a, GPR:$b)>; +def : ARMV5TEPat<(mul (sra (shl GPR:$a, 16), 16), (sra GPR:$b, 16)), + (SMULBT GPR:$a, GPR:$b)>; +def : ARMV5TEPat<(mul sext_16_node:$a, (sra GPR:$b, 16)), + (SMULBT GPR:$a, GPR:$b)>; +def : ARMV5TEPat<(mul (sra GPR:$a, 16), (sra (shl GPR:$b, 16), 16)), + (SMULTB GPR:$a, GPR:$b)>; +def : ARMV5TEPat<(mul (sra GPR:$a, 16), sext_16_node:$b), + (SMULTB GPR:$a, GPR:$b)>; +def : ARMV5TEPat<(sra (mul GPR:$a, (sra (shl GPR:$b, 16), 16)), 16), + (SMULWB GPR:$a, GPR:$b)>; +def : ARMV5TEPat<(sra (mul GPR:$a, sext_16_node:$b), 16), + (SMULWB GPR:$a, GPR:$b)>; + +def : ARMV5TEPat<(add GPR:$acc, + (mul (sra (shl GPR:$a, 16), 16), + (sra (shl GPR:$b, 16), 16))), + (SMLABB GPR:$a, GPR:$b, GPR:$acc)>; +def : ARMV5TEPat<(add GPR:$acc, + (mul sext_16_node:$a, sext_16_node:$b)), + (SMLABB GPR:$a, GPR:$b, GPR:$acc)>; +def : ARMV5TEPat<(add GPR:$acc, + (mul (sra (shl GPR:$a, 16), 16), (sra GPR:$b, 16))), + (SMLABT GPR:$a, GPR:$b, GPR:$acc)>; +def : ARMV5TEPat<(add GPR:$acc, + (mul sext_16_node:$a, (sra GPR:$b, 16))), + (SMLABT GPR:$a, GPR:$b, GPR:$acc)>; +def : ARMV5TEPat<(add GPR:$acc, + (mul (sra GPR:$a, 16), (sra (shl GPR:$b, 16), 16))), + (SMLATB GPR:$a, GPR:$b, GPR:$acc)>; +def : ARMV5TEPat<(add GPR:$acc, + (mul (sra GPR:$a, 16), sext_16_node:$b)), + (SMLATB GPR:$a, GPR:$b, GPR:$acc)>; +def : ARMV5TEPat<(add GPR:$acc, + (sra (mul GPR:$a, (sra (shl GPR:$b, 16), 16)), 16)), + (SMLAWB GPR:$a, GPR:$b, GPR:$acc)>; +def : ARMV5TEPat<(add GPR:$acc, + (sra (mul GPR:$a, sext_16_node:$b), 16)), + (SMLAWB GPR:$a, GPR:$b, GPR:$acc)>; + +//===----------------------------------------------------------------------===// +// Thumb Support +// + +include "ARMInstrThumb.td" + +//===----------------------------------------------------------------------===// +// Floating Point Support +// + +include "ARMInstrVFP.td" diff --git a/lib/Target/ARM/ARMInstrThumb.td b/lib/Target/ARM/ARMInstrThumb.td new file mode 100644 index 0000000..27231da --- /dev/null +++ b/lib/Target/ARM/ARMInstrThumb.td @@ -0,0 +1,596 @@ +//===- ARMInstrThumb.td - Thumb support for ARM ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Chris Lattner and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes the Thumb instruction set. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Thumb specific DAG Nodes. +// + +def ARMtcall : SDNode<"ARMISD::tCALL", SDT_ARMcall, + [SDNPHasChain, SDNPOptInFlag, SDNPOutFlag]>; + +// TI - Thumb instruction. + +// ThumbPat - Same as Pat<>, but requires that the compiler be in Thumb mode. +class ThumbPat<dag pattern, dag result> : Pat<pattern, result> { + list<Predicate> Predicates = [IsThumb]; +} + +class ThumbV5Pat<dag pattern, dag result> : Pat<pattern, result> { + list<Predicate> Predicates = [IsThumb, HasV5T]; +} + +class ThumbI<dag ops, AddrMode am, SizeFlagVal sz, + string asm, string cstr, list<dag> pattern> + // FIXME: Set all opcodes to 0 for now. + : InstARM<0, am, sz, IndexModeNone, cstr> { + let OperandList = ops; + let AsmString = asm; + let Pattern = pattern; + list<Predicate> Predicates = [IsThumb]; +} + +class TI<dag ops, string asm, list<dag> pattern> + : ThumbI<ops, AddrModeNone, Size2Bytes, asm, "", pattern>; +class TI1<dag ops, string asm, list<dag> pattern> + : ThumbI<ops, AddrModeT1, Size2Bytes, asm, "", pattern>; +class TI2<dag ops, string asm, list<dag> pattern> + : ThumbI<ops, AddrModeT2, Size2Bytes, asm, "", pattern>; +class TI4<dag ops, string asm, list<dag> pattern> + : ThumbI<ops, AddrModeT4, Size2Bytes, asm, "", pattern>; +class TIs<dag ops, string asm, list<dag> pattern> + : ThumbI<ops, AddrModeTs, Size2Bytes, asm, "", pattern>; + +// Two-address instructions +class TIt<dag ops, string asm, list<dag> pattern> + : ThumbI<ops, AddrModeNone, Size2Bytes, asm, "$lhs = $dst", pattern>; + +// BL, BLX(1) are translated by assembler into two instructions +class TIx2<dag ops, string asm, list<dag> pattern> + : ThumbI<ops, AddrModeNone, Size4Bytes, asm, "", pattern>; + +// BR_JT instructions +class TJTI<dag ops, string asm, list<dag> pattern> + : ThumbI<ops, AddrModeNone, SizeSpecial, asm, "", pattern>; + +def imm_neg_XFORM : SDNodeXForm<imm, [{ + return CurDAG->getTargetConstant(-(int)N->getValue(), MVT::i32); +}]>; +def imm_comp_XFORM : SDNodeXForm<imm, [{ + return CurDAG->getTargetConstant(~((uint32_t)N->getValue()), MVT::i32); +}]>; + + +/// imm0_7 predicate - True if the 32-bit immediate is in the range [0,7]. +def imm0_7 : PatLeaf<(i32 imm), [{ + return (uint32_t)N->getValue() < 8; +}]>; +def imm0_7_neg : PatLeaf<(i32 imm), [{ + return (uint32_t)-N->getValue() < 8; +}], imm_neg_XFORM>; + +def imm0_255 : PatLeaf<(i32 imm), [{ + return (uint32_t)N->getValue() < 256; +}]>; +def imm0_255_comp : PatLeaf<(i32 imm), [{ + return ~((uint32_t)N->getValue()) < 256; +}]>; + +def imm8_255 : PatLeaf<(i32 imm), [{ + return (uint32_t)N->getValue() >= 8 && (uint32_t)N->getValue() < 256; +}]>; +def imm8_255_neg : PatLeaf<(i32 imm), [{ + unsigned Val = -N->getValue(); + return Val >= 8 && Val < 256; +}], imm_neg_XFORM>; + +// 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. +def thumb_immshifted : PatLeaf<(imm), [{ + return ARM_AM::isThumbImmShiftedVal((unsigned)N->getValue()); +}]>; + +def thumb_immshifted_val : SDNodeXForm<imm, [{ + unsigned V = ARM_AM::getThumbImmNonShiftedVal((unsigned)N->getValue()); + return CurDAG->getTargetConstant(V, MVT::i32); +}]>; + +def thumb_immshifted_shamt : SDNodeXForm<imm, [{ + unsigned V = ARM_AM::getThumbImmValShift((unsigned)N->getValue()); + return CurDAG->getTargetConstant(V, MVT::i32); +}]>; + +// Define Thumb specific addressing modes. + +// t_addrmode_rr := reg + reg +// +def t_addrmode_rr : Operand<i32>, + ComplexPattern<i32, 2, "SelectThumbAddrModeRR", []> { + let PrintMethod = "printThumbAddrModeRROperand"; + let MIOperandInfo = (ops GPR:$base, GPR:$offsreg); +} + +// t_addrmode_s4 := reg + reg +// reg + imm5 * 4 +// +def t_addrmode_s4 : Operand<i32>, + ComplexPattern<i32, 3, "SelectThumbAddrModeS4", []> { + let PrintMethod = "printThumbAddrModeS4Operand"; + let MIOperandInfo = (ops GPR:$base, i32imm:$offsimm, GPR:$offsreg); +} + +// t_addrmode_s2 := reg + reg +// reg + imm5 * 2 +// +def t_addrmode_s2 : Operand<i32>, + ComplexPattern<i32, 3, "SelectThumbAddrModeS2", []> { + let PrintMethod = "printThumbAddrModeS2Operand"; + let MIOperandInfo = (ops GPR:$base, i32imm:$offsimm, GPR:$offsreg); +} + +// t_addrmode_s1 := reg + reg +// reg + imm5 +// +def t_addrmode_s1 : Operand<i32>, + ComplexPattern<i32, 3, "SelectThumbAddrModeS1", []> { + let PrintMethod = "printThumbAddrModeS1Operand"; + let MIOperandInfo = (ops GPR:$base, i32imm:$offsimm, GPR:$offsreg); +} + +// t_addrmode_sp := sp + imm8 * 4 +// +def t_addrmode_sp : Operand<i32>, + ComplexPattern<i32, 2, "SelectThumbAddrModeSP", []> { + let PrintMethod = "printThumbAddrModeSPOperand"; + let MIOperandInfo = (ops GPR:$base, i32imm:$offsimm); +} + +//===----------------------------------------------------------------------===// +// Miscellaneous Instructions. +// + +def tADJCALLSTACKUP : +PseudoInst<(ops i32imm:$amt), + "@ tADJCALLSTACKUP $amt", + [(ARMcallseq_end imm:$amt)]>, Imp<[SP],[SP]>, Requires<[IsThumb]>; + +def tADJCALLSTACKDOWN : +PseudoInst<(ops i32imm:$amt), + "@ tADJCALLSTACKDOWN $amt", + [(ARMcallseq_start imm:$amt)]>, Imp<[SP],[SP]>, Requires<[IsThumb]>; + +let isNotDuplicable = 1 in +def tPICADD : TIt<(ops GPR:$dst, GPR:$lhs, pclabel:$cp), + "$cp:\n\tadd $dst, pc", + [(set GPR:$dst, (ARMpic_add GPR:$lhs, imm:$cp))]>; + +//===----------------------------------------------------------------------===// +// Control Flow Instructions. +// + +let isReturn = 1, isTerminator = 1 in { + def tBX_RET : TI<(ops), "bx lr", [(ARMretflag)]>; + // Alternative return instruction used by vararg functions. + def tBX_RET_vararg : TI<(ops GPR:$dst), "bx $dst", []>; +} + +// FIXME: remove when we have a way to marking a MI with these properties. +let isLoad = 1, isReturn = 1, isTerminator = 1 in +def tPOP_RET : TI<(ops reglist:$dst1, variable_ops), + "pop $dst1", []>; + +let isCall = 1, noResults = 1, + Defs = [R0, R1, R2, R3, LR, + D0, D1, D2, D3, D4, D5, D6, D7] in { + def tBL : TIx2<(ops i32imm:$func, variable_ops), + "bl ${func:call}", + [(ARMtcall tglobaladdr:$func)]>; + // ARMv5T and above + def tBLXi : TIx2<(ops i32imm:$func, variable_ops), + "blx ${func:call}", + [(ARMcall tglobaladdr:$func)]>, Requires<[HasV5T]>; + def tBLXr : TI<(ops GPR:$dst, variable_ops), + "blx $dst", + [(ARMtcall GPR:$dst)]>, Requires<[HasV5T]>; + // ARMv4T + def tBX : TIx2<(ops GPR:$dst, variable_ops), + "cpy lr, pc\n\tbx $dst", + [(ARMcall_nolink GPR:$dst)]>; +} + +let isBranch = 1, isTerminator = 1, noResults = 1 in { + let isBarrier = 1 in { + let isPredicable = 1 in + def tB : TI<(ops brtarget:$dst), "b $dst", [(br bb:$dst)]>; + + // Far jump + def tBfar : TIx2<(ops brtarget:$dst), "bl $dst\t@ far jump", []>; + + def tBR_JTr : TJTI<(ops GPR:$dst, jtblock_operand:$jt, i32imm:$id), + "cpy pc, $dst \n\t.align\t2\n$jt", + [(ARMbrjt GPR:$dst, tjumptable:$jt, imm:$id)]>; + } +} + +// FIXME: should be able to write a pattern for ARMBrcond, but can't use +// a two-value operand where a dag node expects two operands. :( +let isBranch = 1, isTerminator = 1, noResults = 1 in + def tBcc : TI<(ops brtarget:$dst, pred:$cc), "b$cc $dst", + [/*(ARMbrcond bb:$dst, imm:$cc)*/]>; + +//===----------------------------------------------------------------------===// +// Load Store Instructions. +// + +let isLoad = 1 in { +def tLDR : TI4<(ops GPR:$dst, t_addrmode_s4:$addr), + "ldr $dst, $addr", + [(set GPR:$dst, (load t_addrmode_s4:$addr))]>; + +def tLDRB : TI1<(ops GPR:$dst, t_addrmode_s1:$addr), + "ldrb $dst, $addr", + [(set GPR:$dst, (zextloadi8 t_addrmode_s1:$addr))]>; + +def tLDRH : TI2<(ops GPR:$dst, t_addrmode_s2:$addr), + "ldrh $dst, $addr", + [(set GPR:$dst, (zextloadi16 t_addrmode_s2:$addr))]>; + +def tLDRSB : TI1<(ops GPR:$dst, t_addrmode_rr:$addr), + "ldrsb $dst, $addr", + [(set GPR:$dst, (sextloadi8 t_addrmode_rr:$addr))]>; + +def tLDRSH : TI2<(ops GPR:$dst, t_addrmode_rr:$addr), + "ldrsh $dst, $addr", + [(set GPR:$dst, (sextloadi16 t_addrmode_rr:$addr))]>; + +def tLDRspi : TIs<(ops GPR:$dst, t_addrmode_sp:$addr), + "ldr $dst, $addr", + [(set GPR:$dst, (load t_addrmode_sp:$addr))]>; + +// Special instruction for restore. It cannot clobber condition register +// when it's expanded by eliminateCallFramePseudoInstr(). +def tRestore : TIs<(ops GPR:$dst, t_addrmode_sp:$addr), + "ldr $dst, $addr", []>; + +// Load tconstpool +def tLDRpci : TIs<(ops GPR:$dst, i32imm:$addr), + "ldr $dst, $addr", + [(set GPR:$dst, (load (ARMWrapper tconstpool:$addr)))]>; + +// Special LDR for loads from non-pc-relative constpools. +let isReMaterializable = 1 in +def tLDRcp : TIs<(ops GPR:$dst, i32imm:$addr), + "ldr $dst, $addr", []>; +} // isLoad + +let isStore = 1 in { +def tSTR : TI4<(ops GPR:$src, t_addrmode_s4:$addr), + "str $src, $addr", + [(store GPR:$src, t_addrmode_s4:$addr)]>; + +def tSTRB : TI1<(ops GPR:$src, t_addrmode_s1:$addr), + "strb $src, $addr", + [(truncstorei8 GPR:$src, t_addrmode_s1:$addr)]>; + +def tSTRH : TI2<(ops GPR:$src, t_addrmode_s2:$addr), + "strh $src, $addr", + [(truncstorei16 GPR:$src, t_addrmode_s2:$addr)]>; + +def tSTRspi : TIs<(ops GPR:$src, t_addrmode_sp:$addr), + "str $src, $addr", + [(store GPR:$src, t_addrmode_sp:$addr)]>; + +// Special instruction for spill. It cannot clobber condition register +// when it's expanded by eliminateCallFramePseudoInstr(). +def tSpill : TIs<(ops GPR:$src, t_addrmode_sp:$addr), + "str $src, $addr", []>; +} + +//===----------------------------------------------------------------------===// +// Load / store multiple Instructions. +// + +// TODO: A7-44: LDMIA - load multiple + +let isLoad = 1 in +def tPOP : TI<(ops reglist:$dst1, variable_ops), + "pop $dst1", []>; + +let isStore = 1 in +def tPUSH : TI<(ops reglist:$src1, variable_ops), + "push $src1", []>; + +//===----------------------------------------------------------------------===// +// Arithmetic Instructions. +// + +// Add with carry +def tADC : TIt<(ops GPR:$dst, GPR:$lhs, GPR:$rhs), + "adc $dst, $rhs", + [(set GPR:$dst, (adde GPR:$lhs, GPR:$rhs))]>; + +def tADDS : TI<(ops GPR:$dst, GPR:$lhs, GPR:$rhs), + "add $dst, $lhs, $rhs", + [(set GPR:$dst, (addc GPR:$lhs, GPR:$rhs))]>; + + +def tADDi3 : TI<(ops GPR:$dst, GPR:$lhs, i32imm:$rhs), + "add $dst, $lhs, $rhs", + [(set GPR:$dst, (add GPR:$lhs, imm0_7:$rhs))]>; + +def tADDi8 : TIt<(ops GPR:$dst, GPR:$lhs, i32imm:$rhs), + "add $dst, $rhs", + [(set GPR:$dst, (add GPR:$lhs, imm8_255:$rhs))]>; + +def tADDrr : TI<(ops GPR:$dst, GPR:$lhs, GPR:$rhs), + "add $dst, $lhs, $rhs", + [(set GPR:$dst, (add GPR:$lhs, GPR:$rhs))]>; + +def tADDhirr : TIt<(ops GPR:$dst, GPR:$lhs, GPR:$rhs), + "add $dst, $rhs", []>; + +def tADDrPCi : TI<(ops GPR:$dst, i32imm:$rhs), + "add $dst, pc, $rhs * 4", []>; +def tADDrSPi : TI<(ops GPR:$dst, GPR:$sp, i32imm:$rhs), + "add $dst, $sp, $rhs * 4", []>; +def tADDspi : TIt<(ops GPR:$dst, GPR:$lhs, i32imm:$rhs), + "add $dst, $rhs * 4", []>; + +def tAND : TIt<(ops GPR:$dst, GPR:$lhs, GPR:$rhs), + "and $dst, $rhs", + [(set GPR:$dst, (and GPR:$lhs, GPR:$rhs))]>; + +def tASRri : TI<(ops GPR:$dst, GPR:$lhs, i32imm:$rhs), + "asr $dst, $lhs, $rhs", + [(set GPR:$dst, (sra GPR:$lhs, imm:$rhs))]>; + +def tASRrr : TIt<(ops GPR:$dst, GPR:$lhs, GPR:$rhs), + "asr $dst, $rhs", + [(set GPR:$dst, (sra GPR:$lhs, GPR:$rhs))]>; + +def tBIC : TIt<(ops GPR:$dst, GPR:$lhs, GPR:$rhs), + "bic $dst, $rhs", + [(set GPR:$dst, (and GPR:$lhs, (not GPR:$rhs)))]>; + + +def tCMN : TI<(ops GPR:$lhs, GPR:$rhs), + "cmn $lhs, $rhs", + [(ARMcmp GPR:$lhs, (ineg GPR:$rhs))]>; + +def tCMPi8 : TI<(ops GPR:$lhs, i32imm:$rhs), + "cmp $lhs, $rhs", + [(ARMcmp GPR:$lhs, imm0_255:$rhs)]>; + +def tCMPr : TI<(ops GPR:$lhs, GPR:$rhs), + "cmp $lhs, $rhs", + [(ARMcmp GPR:$lhs, GPR:$rhs)]>; + +def tTST : TI<(ops GPR:$lhs, GPR:$rhs), + "tst $lhs, $rhs", + [(ARMcmpNZ (and GPR:$lhs, GPR:$rhs), 0)]>; + +def tCMNNZ : TI<(ops GPR:$lhs, GPR:$rhs), + "cmn $lhs, $rhs", + [(ARMcmpNZ GPR:$lhs, (ineg GPR:$rhs))]>; + +def tCMPNZi8 : TI<(ops GPR:$lhs, i32imm:$rhs), + "cmp $lhs, $rhs", + [(ARMcmpNZ GPR:$lhs, imm0_255:$rhs)]>; + +def tCMPNZr : TI<(ops GPR:$lhs, GPR:$rhs), + "cmp $lhs, $rhs", + [(ARMcmpNZ GPR:$lhs, GPR:$rhs)]>; + +// TODO: A7-37: CMP(3) - cmp hi regs + +def tEOR : TIt<(ops GPR:$dst, GPR:$lhs, GPR:$rhs), + "eor $dst, $rhs", + [(set GPR:$dst, (xor GPR:$lhs, GPR:$rhs))]>; + +def tLSLri : TI<(ops GPR:$dst, GPR:$lhs, i32imm:$rhs), + "lsl $dst, $lhs, $rhs", + [(set GPR:$dst, (shl GPR:$lhs, imm:$rhs))]>; + +def tLSLrr : TIt<(ops GPR:$dst, GPR:$lhs, GPR:$rhs), + "lsl $dst, $rhs", + [(set GPR:$dst, (shl GPR:$lhs, GPR:$rhs))]>; + +def tLSRri : TI<(ops GPR:$dst, GPR:$lhs, i32imm:$rhs), + "lsr $dst, $lhs, $rhs", + [(set GPR:$dst, (srl GPR:$lhs, imm:$rhs))]>; + +def tLSRrr : TIt<(ops GPR:$dst, GPR:$lhs, GPR:$rhs), + "lsr $dst, $rhs", + [(set GPR:$dst, (srl GPR:$lhs, GPR:$rhs))]>; + +// FIXME: This is not rematerializable because mov changes the condition code. +def tMOVi8 : TI<(ops GPR:$dst, i32imm:$src), + "mov $dst, $src", + [(set GPR:$dst, imm0_255:$src)]>; + +// TODO: A7-73: MOV(2) - mov setting flag. + + +// Note: MOV(2) of two low regs updates the flags, so we emit this as 'cpy', +// which is MOV(3). This also supports high registers. +def tMOVr : TI<(ops GPR:$dst, GPR:$src), + "cpy $dst, $src", []>; + +def tMUL : TIt<(ops GPR:$dst, GPR:$lhs, GPR:$rhs), + "mul $dst, $rhs", + [(set GPR:$dst, (mul GPR:$lhs, GPR:$rhs))]>; + +def tMVN : TI<(ops GPR:$dst, GPR:$src), + "mvn $dst, $src", + [(set GPR:$dst, (not GPR:$src))]>; + +def tNEG : TI<(ops GPR:$dst, GPR:$src), + "neg $dst, $src", + [(set GPR:$dst, (ineg GPR:$src))]>; + +def tORR : TIt<(ops GPR:$dst, GPR:$lhs, GPR:$rhs), + "orr $dst, $rhs", + [(set GPR:$dst, (or GPR:$lhs, GPR:$rhs))]>; + + +def tREV : TI<(ops GPR:$dst, GPR:$src), + "rev $dst, $src", + [(set GPR:$dst, (bswap GPR:$src))]>, + Requires<[IsThumb, HasV6]>; + +def tREV16 : TI<(ops GPR:$dst, GPR:$src), + "rev16 $dst, $src", + [(set GPR:$dst, + (or (and (srl GPR:$src, 8), 0xFF), + (or (and (shl GPR:$src, 8), 0xFF00), + (or (and (srl GPR:$src, 8), 0xFF0000), + (and (shl GPR:$src, 8), 0xFF000000)))))]>, + Requires<[IsThumb, HasV6]>; + +def tREVSH : TI<(ops GPR:$dst, GPR:$src), + "revsh $dst, $src", + [(set GPR:$dst, + (sext_inreg + (or (srl (and GPR:$src, 0xFFFF), 8), + (shl GPR:$src, 8)), i16))]>, + Requires<[IsThumb, HasV6]>; + +def tROR : TIt<(ops GPR:$dst, GPR:$lhs, GPR:$rhs), + "ror $dst, $rhs", + [(set GPR:$dst, (rotr GPR:$lhs, GPR:$rhs))]>; + + +// Subtract with carry +def tSBC : TIt<(ops GPR:$dst, GPR:$lhs, GPR:$rhs), + "sbc $dst, $rhs", + [(set GPR:$dst, (sube GPR:$lhs, GPR:$rhs))]>; + +def tSUBS : TI<(ops GPR:$dst, GPR:$lhs, GPR:$rhs), + "sub $dst, $lhs, $rhs", + [(set GPR:$dst, (subc GPR:$lhs, GPR:$rhs))]>; + + +// TODO: A7-96: STMIA - store multiple. + +def tSUBi3 : TI<(ops GPR:$dst, GPR:$lhs, i32imm:$rhs), + "sub $dst, $lhs, $rhs", + [(set GPR:$dst, (add GPR:$lhs, imm0_7_neg:$rhs))]>; + +def tSUBi8 : TIt<(ops GPR:$dst, GPR:$lhs, i32imm:$rhs), + "sub $dst, $rhs", + [(set GPR:$dst, (add GPR:$lhs, imm8_255_neg:$rhs))]>; + +def tSUBrr : TI<(ops GPR:$dst, GPR:$lhs, GPR:$rhs), + "sub $dst, $lhs, $rhs", + [(set GPR:$dst, (sub GPR:$lhs, GPR:$rhs))]>; + +def tSUBspi : TIt<(ops GPR:$dst, GPR:$lhs, i32imm:$rhs), + "sub $dst, $rhs * 4", []>; + +def tSXTB : TI<(ops GPR:$dst, GPR:$src), + "sxtb $dst, $src", + [(set GPR:$dst, (sext_inreg GPR:$src, i8))]>, + Requires<[IsThumb, HasV6]>; +def tSXTH : TI<(ops GPR:$dst, GPR:$src), + "sxth $dst, $src", + [(set GPR:$dst, (sext_inreg GPR:$src, i16))]>, + Requires<[IsThumb, HasV6]>; + + +def tUXTB : TI<(ops GPR:$dst, GPR:$src), + "uxtb $dst, $src", + [(set GPR:$dst, (and GPR:$src, 0xFF))]>, + Requires<[IsThumb, HasV6]>; +def tUXTH : TI<(ops GPR:$dst, GPR:$src), + "uxth $dst, $src", + [(set GPR:$dst, (and GPR:$src, 0xFFFF))]>, + Requires<[IsThumb, HasV6]>; + + +// Conditional move tMOVCCr - Used to implement the Thumb SELECT_CC DAG operation. +// Expanded by the scheduler into a branch sequence. +let usesCustomDAGSchedInserter = 1 in // Expanded by the scheduler. + def tMOVCCr : + PseudoInst<(ops GPR:$dst, GPR:$false, GPR:$true, pred:$cc), + "@ tMOVCCr $cc", + [/*(set GPR:$dst, (ARMcmov GPR:$false, GPR:$true, imm:$cc))*/]>; + +// tLEApcrel - Load a pc-relative address into a register without offending the +// assembler. +def tLEApcrel : TIx2<(ops GPR:$dst, i32imm:$label), + !strconcat(!strconcat(".set PCRELV${:uid}, ($label-(", + "${:private}PCRELL${:uid}+4))\n"), + !strconcat("\tmov $dst, #PCRELV${:uid}\n", + "${:private}PCRELL${:uid}:\n\tadd $dst, pc")), + []>; + +def tLEApcrelJT : TIx2<(ops GPR:$dst, i32imm:$label, i32imm:$id), + !strconcat(!strconcat(".set PCRELV${:uid}, (${label}_${id:no_hash}-(", + "${:private}PCRELL${:uid}+4))\n"), + !strconcat("\tmov $dst, #PCRELV${:uid}\n", + "${:private}PCRELL${:uid}:\n\tadd $dst, pc")), + []>; + +//===----------------------------------------------------------------------===// +// TLS Instructions +// + +// __aeabi_read_tp preserves the registers r1-r3. +let isCall = 1, + Defs = [R0, LR] in { + def tTPsoft : TIx2<(ops), + "bl __aeabi_read_tp", + [(set R0, ARMthread_pointer)]>; +} + +//===----------------------------------------------------------------------===// +// Non-Instruction Patterns +// + +// ConstantPool, GlobalAddress +def : ThumbPat<(ARMWrapper tglobaladdr :$dst), (tLEApcrel tglobaladdr :$dst)>; +def : ThumbPat<(ARMWrapper tconstpool :$dst), (tLEApcrel tconstpool :$dst)>; + +// JumpTable +def : ThumbPat<(ARMWrapperJT tjumptable:$dst, imm:$id), + (tLEApcrelJT tjumptable:$dst, imm:$id)>; + +// Direct calls +def : ThumbPat<(ARMtcall texternalsym:$func), (tBL texternalsym:$func)>; +def : ThumbV5Pat<(ARMcall texternalsym:$func), (tBLXi texternalsym:$func)>; + +// Indirect calls to ARM routines +def : ThumbV5Pat<(ARMcall GPR:$dst), (tBLXr GPR:$dst)>; + +// zextload i1 -> zextload i8 +def : ThumbPat<(zextloadi1 t_addrmode_s1:$addr), + (tLDRB t_addrmode_s1:$addr)>; + +// extload -> zextload +def : ThumbPat<(extloadi1 t_addrmode_s1:$addr), (tLDRB t_addrmode_s1:$addr)>; +def : ThumbPat<(extloadi8 t_addrmode_s1:$addr), (tLDRB t_addrmode_s1:$addr)>; +def : ThumbPat<(extloadi16 t_addrmode_s2:$addr), (tLDRH t_addrmode_s2:$addr)>; + +// truncstore i1 -> truncstore i8 +def : ThumbPat<(truncstorei1 GPR:$src, t_addrmode_s1:$dst), + (tSTRB GPR:$src, t_addrmode_s1:$dst)>; + +// Large immediate handling. + +// Two piece imms. +def : ThumbPat<(i32 thumb_immshifted:$src), + (tLSLri (tMOVi8 (thumb_immshifted_val imm:$src)), + (thumb_immshifted_shamt imm:$src))>; + +def : ThumbPat<(i32 imm0_255_comp:$src), + (tMVN (tMOVi8 (imm_comp_XFORM imm:$src)))>; diff --git a/lib/Target/ARM/ARMInstrVFP.td b/lib/Target/ARM/ARMInstrVFP.td new file mode 100644 index 0000000..4bb9f04 --- /dev/null +++ b/lib/Target/ARM/ARMInstrVFP.td @@ -0,0 +1,386 @@ +//===- ARMInstrVFP.td - VFP support for ARM -------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Chris Lattner and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes the ARM VP instruction set. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// ARM VFP Instruction templates. +// + +// ARM Float Instruction +class ASI<dag ops, string opc, string asm, list<dag> pattern> + : AI<ops, opc, asm, pattern> { + // TODO: Mark the instructions with the appropriate subtarget info. +} + +class ASI5<dag ops, string opc, string asm, list<dag> pattern> + : I<ops, AddrMode5, Size4Bytes, IndexModeNone, opc, asm, "", pattern> { + // TODO: Mark the instructions with the appropriate subtarget info. +} + +// ARM Double Instruction +class ADI<dag ops, string opc, string asm, list<dag> pattern> + : AI<ops, opc, asm, pattern> { + // TODO: Mark the instructions with the appropriate subtarget info. +} + +class ADI5<dag ops, string opc, string asm, list<dag> pattern> + : I<ops, AddrMode5, Size4Bytes, IndexModeNone, opc, asm, "", pattern> { + // TODO: Mark the instructions with the appropriate subtarget info. +} + +// Special cases. +class AXSI<dag ops, string asm, list<dag> pattern> + : XI<ops, AddrModeNone, Size4Bytes, IndexModeNone, asm, "", pattern> { + // TODO: Mark the instructions with the appropriate subtarget info. +} + +class AXSI5<dag ops, string asm, list<dag> pattern> + : XI<ops, AddrMode5, Size4Bytes, IndexModeNone, asm, "", pattern> { + // TODO: Mark the instructions with the appropriate subtarget info. +} + +class AXDI<dag ops, string asm, list<dag> pattern> + : XI<ops, AddrModeNone, Size4Bytes, IndexModeNone, asm, "", pattern> { + // TODO: Mark the instructions with the appropriate subtarget info. +} + +class AXDI5<dag ops, string asm, list<dag> pattern> + : XI<ops, AddrMode5, Size4Bytes, IndexModeNone, asm, "", pattern> { + // TODO: Mark the instructions with the appropriate subtarget info. +} + + +def SDT_FTOI : +SDTypeProfile<1, 1, [SDTCisVT<0, f32>, SDTCisFP<1>]>; +def SDT_ITOF : +SDTypeProfile<1, 1, [SDTCisFP<0>, SDTCisVT<1, f32>]>; +def SDT_CMPFP0 : +SDTypeProfile<0, 1, [SDTCisFP<0>]>; +def SDT_FMDRR : +SDTypeProfile<1, 2, [SDTCisVT<0, f64>, SDTCisVT<1, i32>, + SDTCisSameAs<1, 2>]>; + +def arm_ftoui : SDNode<"ARMISD::FTOUI", SDT_FTOI>; +def arm_ftosi : SDNode<"ARMISD::FTOSI", SDT_FTOI>; +def arm_sitof : SDNode<"ARMISD::SITOF", SDT_ITOF>; +def arm_uitof : SDNode<"ARMISD::UITOF", SDT_ITOF>; +def arm_fmstat : SDNode<"ARMISD::FMSTAT", SDTRet, [SDNPInFlag,SDNPOutFlag]>; +def arm_cmpfp : SDNode<"ARMISD::CMPFP", SDT_ARMCmp, [SDNPOutFlag]>; +def arm_cmpfp0 : SDNode<"ARMISD::CMPFPw0", SDT_CMPFP0, [SDNPOutFlag]>; +def arm_fmdrr : SDNode<"ARMISD::FMDRR", SDT_FMDRR>; + +//===----------------------------------------------------------------------===// +// Load / store Instructions. +// + +let isLoad = 1 in { +def FLDD : ADI5<(ops DPR:$dst, addrmode5:$addr), + "fldd", " $dst, $addr", + [(set DPR:$dst, (load addrmode5:$addr))]>; + +def FLDS : ASI5<(ops SPR:$dst, addrmode5:$addr), + "flds", " $dst, $addr", + [(set SPR:$dst, (load addrmode5:$addr))]>; +} // isLoad + +let isStore = 1 in { +def FSTD : ADI5<(ops DPR:$src, addrmode5:$addr), + "fstd", " $src, $addr", + [(store DPR:$src, addrmode5:$addr)]>; + +def FSTS : ASI5<(ops SPR:$src, addrmode5:$addr), + "fsts", " $src, $addr", + [(store SPR:$src, addrmode5:$addr)]>; +} // isStore + +//===----------------------------------------------------------------------===// +// Load / store multiple Instructions. +// + +let isLoad = 1 in { +def FLDMD : AXDI5<(ops addrmode5:$addr, pred:$p, reglist:$dst1, variable_ops), + "fldm${addr:submode}d${p} ${addr:base}, $dst1", + []>; + +def FLDMS : AXSI5<(ops addrmode5:$addr, pred:$p, reglist:$dst1, variable_ops), + "fldm${addr:submode}s${p} ${addr:base}, $dst1", + []>; +} // isLoad + +let isStore = 1 in { +def FSTMD : AXDI5<(ops addrmode5:$addr, pred:$p, reglist:$src1, variable_ops), + "fstm${addr:submode}d${p} ${addr:base}, $src1", + []>; + +def FSTMS : AXSI5<(ops addrmode5:$addr, pred:$p, reglist:$src1, variable_ops), + "fstm${addr:submode}s${p} ${addr:base}, $src1", + []>; +} // isStore + +// FLDMX, FSTMX - mixing S/D registers for pre-armv6 cores + +//===----------------------------------------------------------------------===// +// FP Binary Operations. +// + +def FADDD : ADI<(ops DPR:$dst, DPR:$a, DPR:$b), + "faddd", " $dst, $a, $b", + [(set DPR:$dst, (fadd DPR:$a, DPR:$b))]>; + +def FADDS : ASI<(ops SPR:$dst, SPR:$a, SPR:$b), + "fadds", " $dst, $a, $b", + [(set SPR:$dst, (fadd SPR:$a, SPR:$b))]>; + +def FCMPED : ADI<(ops DPR:$a, DPR:$b), + "fcmped", " $a, $b", + [(arm_cmpfp DPR:$a, DPR:$b)]>; + +def FCMPES : ASI<(ops SPR:$a, SPR:$b), + "fcmpes", " $a, $b", + [(arm_cmpfp SPR:$a, SPR:$b)]>; + +def FDIVD : ADI<(ops DPR:$dst, DPR:$a, DPR:$b), + "fdivd", " $dst, $a, $b", + [(set DPR:$dst, (fdiv DPR:$a, DPR:$b))]>; + +def FDIVS : ASI<(ops SPR:$dst, SPR:$a, SPR:$b), + "fdivs", " $dst, $a, $b", + [(set SPR:$dst, (fdiv SPR:$a, SPR:$b))]>; + +def FMULD : ADI<(ops DPR:$dst, DPR:$a, DPR:$b), + "fmuld", " $dst, $a, $b", + [(set DPR:$dst, (fmul DPR:$a, DPR:$b))]>; + +def FMULS : ASI<(ops SPR:$dst, SPR:$a, SPR:$b), + "fmuls", " $dst, $a, $b", + [(set SPR:$dst, (fmul SPR:$a, SPR:$b))]>; + +def FNMULD : ADI<(ops DPR:$dst, DPR:$a, DPR:$b), + "fnmuld", " $dst, $a, $b", + [(set DPR:$dst, (fneg (fmul DPR:$a, DPR:$b)))]>; + +def FNMULS : ASI<(ops SPR:$dst, SPR:$a, SPR:$b), + "fnmuls", " $dst, $a, $b", + [(set SPR:$dst, (fneg (fmul SPR:$a, SPR:$b)))]>; + +// Match reassociated forms only if not sign dependent rounding. +def : Pat<(fmul (fneg DPR:$a), DPR:$b), + (FNMULD DPR:$a, DPR:$b)>, Requires<[NoHonorSignDependentRounding]>; +def : Pat<(fmul (fneg SPR:$a), SPR:$b), + (FNMULS SPR:$a, SPR:$b)>, Requires<[NoHonorSignDependentRounding]>; + + +def FSUBD : ADI<(ops DPR:$dst, DPR:$a, DPR:$b), + "fsubd", " $dst, $a, $b", + [(set DPR:$dst, (fsub DPR:$a, DPR:$b))]>; + +def FSUBS : ASI<(ops SPR:$dst, SPR:$a, SPR:$b), + "fsubs", " $dst, $a, $b", + [(set SPR:$dst, (fsub SPR:$a, SPR:$b))]>; + +//===----------------------------------------------------------------------===// +// FP Unary Operations. +// + +def FABSD : ADI<(ops DPR:$dst, DPR:$a), + "fabsd", " $dst, $a", + [(set DPR:$dst, (fabs DPR:$a))]>; + +def FABSS : ASI<(ops SPR:$dst, SPR:$a), + "fabss", " $dst, $a", + [(set SPR:$dst, (fabs SPR:$a))]>; + +def FCMPEZD : ADI<(ops DPR:$a), + "fcmpezd", " $a", + [(arm_cmpfp0 DPR:$a)]>; + +def FCMPEZS : ASI<(ops SPR:$a), + "fcmpezs", " $a", + [(arm_cmpfp0 SPR:$a)]>; + +def FCVTDS : ADI<(ops DPR:$dst, SPR:$a), + "fcvtds", " $dst, $a", + [(set DPR:$dst, (fextend SPR:$a))]>; + +def FCVTSD : ADI<(ops SPR:$dst, DPR:$a), + "fcvtsd", " $dst, $a", + [(set SPR:$dst, (fround DPR:$a))]>; + +def FCPYD : ADI<(ops DPR:$dst, DPR:$a), + "fcpyd", " $dst, $a", []>; + +def FCPYS : ASI<(ops SPR:$dst, SPR:$a), + "fcpys", " $dst, $a", []>; + +def FNEGD : ADI<(ops DPR:$dst, DPR:$a), + "fnegd", " $dst, $a", + [(set DPR:$dst, (fneg DPR:$a))]>; + +def FNEGS : ASI<(ops SPR:$dst, SPR:$a), + "fnegs", " $dst, $a", + [(set SPR:$dst, (fneg SPR:$a))]>; + +def FSQRTD : ADI<(ops DPR:$dst, DPR:$a), + "fsqrtd", " $dst, $a", + [(set DPR:$dst, (fsqrt DPR:$a))]>; + +def FSQRTS : ASI<(ops SPR:$dst, SPR:$a), + "fsqrts", " $dst, $a", + [(set SPR:$dst, (fsqrt SPR:$a))]>; + +//===----------------------------------------------------------------------===// +// FP <-> GPR Copies. Int <-> FP Conversions. +// + +def IMPLICIT_DEF_SPR : PseudoInst<(ops SPR:$rD, pred:$p), + "@ IMPLICIT_DEF_SPR $rD", + [(set SPR:$rD, (undef))]>; +def IMPLICIT_DEF_DPR : PseudoInst<(ops DPR:$rD, pred:$p), + "@ IMPLICIT_DEF_DPR $rD", + [(set DPR:$rD, (undef))]>; + +def FMRS : ASI<(ops GPR:$dst, SPR:$src), + "fmrs", " $dst, $src", + [(set GPR:$dst, (bitconvert SPR:$src))]>; + +def FMSR : ASI<(ops SPR:$dst, GPR:$src), + "fmsr", " $dst, $src", + [(set SPR:$dst, (bitconvert GPR:$src))]>; + + +def FMRRD : ADI<(ops GPR:$dst1, GPR:$dst2, DPR:$src), + "fmrrd", " $dst1, $dst2, $src", + [/* FIXME: Can't write pattern for multiple result instr*/]>; + +// FMDHR: GPR -> SPR +// FMDLR: GPR -> SPR + +def FMDRR : ADI<(ops DPR:$dst, GPR:$src1, GPR:$src2), + "fmdrr", " $dst, $src1, $src2", + [(set DPR:$dst, (arm_fmdrr GPR:$src1, GPR:$src2))]>; + +// FMRDH: SPR -> GPR +// FMRDL: SPR -> GPR +// FMRRS: SPR -> GPR +// FMRX : SPR system reg -> GPR + +// FMSRR: GPR -> SPR + +def FMSTAT : ASI<(ops), "fmstat", "", [(arm_fmstat)]>, Imp<[], [CPSR]>; + +// FMXR: GPR -> VFP Sstem reg + + +// Int to FP: + +def FSITOD : ADI<(ops DPR:$dst, SPR:$a), + "fsitod", " $dst, $a", + [(set DPR:$dst, (arm_sitof SPR:$a))]>; + +def FSITOS : ASI<(ops SPR:$dst, SPR:$a), + "fsitos", " $dst, $a", + [(set SPR:$dst, (arm_sitof SPR:$a))]>; + +def FUITOD : ADI<(ops DPR:$dst, SPR:$a), + "fuitod", " $dst, $a", + [(set DPR:$dst, (arm_uitof SPR:$a))]>; + +def FUITOS : ASI<(ops SPR:$dst, SPR:$a), + "fuitos", " $dst, $a", + [(set SPR:$dst, (arm_uitof SPR:$a))]>; + +// FP to Int: +// Always set Z bit in the instruction, i.e. "round towards zero" variants. + +def FTOSIZD : ADI<(ops SPR:$dst, DPR:$a), + "ftosizd", " $dst, $a", + [(set SPR:$dst, (arm_ftosi DPR:$a))]>; + +def FTOSIZS : ASI<(ops SPR:$dst, SPR:$a), + "ftosizs", " $dst, $a", + [(set SPR:$dst, (arm_ftosi SPR:$a))]>; + +def FTOUIZD : ADI<(ops SPR:$dst, DPR:$a), + "ftouizd", " $dst, $a", + [(set SPR:$dst, (arm_ftoui DPR:$a))]>; + +def FTOUIZS : ASI<(ops SPR:$dst, SPR:$a), + "ftouizs", " $dst, $a", + [(set SPR:$dst, (arm_ftoui SPR:$a))]>; + +//===----------------------------------------------------------------------===// +// FP FMA Operations. +// + +def FMACD : ADI<(ops DPR:$dst, DPR:$dstin, DPR:$a, DPR:$b), + "fmacd", " $dst, $a, $b", + [(set DPR:$dst, (fadd (fmul DPR:$a, DPR:$b), DPR:$dstin))]>, + RegConstraint<"$dstin = $dst">; + +def FMACS : ASI<(ops SPR:$dst, SPR:$dstin, SPR:$a, SPR:$b), + "fmacs", " $dst, $a, $b", + [(set SPR:$dst, (fadd (fmul SPR:$a, SPR:$b), SPR:$dstin))]>, + RegConstraint<"$dstin = $dst">; + +def FMSCD : ADI<(ops DPR:$dst, DPR:$dstin, DPR:$a, DPR:$b), + "fmscd", " $dst, $a, $b", + [(set DPR:$dst, (fsub (fmul DPR:$a, DPR:$b), DPR:$dstin))]>, + RegConstraint<"$dstin = $dst">; + +def FMSCS : ASI<(ops SPR:$dst, SPR:$dstin, SPR:$a, SPR:$b), + "fmscs", " $dst, $a, $b", + [(set SPR:$dst, (fsub (fmul SPR:$a, SPR:$b), SPR:$dstin))]>, + RegConstraint<"$dstin = $dst">; + +def FNMACD : ADI<(ops DPR:$dst, DPR:$dstin, DPR:$a, DPR:$b), + "fnmacd", " $dst, $a, $b", + [(set DPR:$dst, (fadd (fneg (fmul DPR:$a, DPR:$b)), DPR:$dstin))]>, + RegConstraint<"$dstin = $dst">; + +def FNMACS : ASI<(ops SPR:$dst, SPR:$dstin, SPR:$a, SPR:$b), + "fnmacs", " $dst, $a, $b", + [(set SPR:$dst, (fadd (fneg (fmul SPR:$a, SPR:$b)), SPR:$dstin))]>, + RegConstraint<"$dstin = $dst">; + +def FNMSCD : ADI<(ops DPR:$dst, DPR:$dstin, DPR:$a, DPR:$b), + "fnmscd", " $dst, $a, $b", + [(set DPR:$dst, (fsub (fneg (fmul DPR:$a, DPR:$b)), DPR:$dstin))]>, + RegConstraint<"$dstin = $dst">; + +def FNMSCS : ASI<(ops SPR:$dst, SPR:$dstin, SPR:$a, SPR:$b), + "fnmscs", " $dst, $a, $b", + [(set SPR:$dst, (fsub (fneg (fmul SPR:$a, SPR:$b)), SPR:$dstin))]>, + RegConstraint<"$dstin = $dst">; + +//===----------------------------------------------------------------------===// +// FP Conditional moves. +// + +def FCPYDcc : ADI<(ops DPR:$dst, DPR:$false, DPR:$true), + "fcpyd", " $dst, $true", + [/*(set DPR:$dst, (ARMcmov DPR:$false, DPR:$true, imm:$cc))*/]>, + RegConstraint<"$false = $dst">; + +def FCPYScc : ASI<(ops SPR:$dst, SPR:$false, SPR:$true), + "fcpys", " $dst, $true", + [/*(set SPR:$dst, (ARMcmov SPR:$false, SPR:$true, imm:$cc))*/]>, + RegConstraint<"$false = $dst">; + +def FNEGDcc : ADI<(ops DPR:$dst, DPR:$false, DPR:$true), + "fnegd", " $dst, $true", + [/*(set DPR:$dst, (ARMcneg DPR:$false, DPR:$true, imm:$cc))*/]>, + RegConstraint<"$false = $dst">; + +def FNEGScc : ASI<(ops SPR:$dst, SPR:$false, SPR:$true), + "fnegs", " $dst, $true", + [/*(set SPR:$dst, (ARMcneg SPR:$false, SPR:$true, imm:$cc))*/]>, + RegConstraint<"$false = $dst">; diff --git a/lib/Target/ARM/ARMJITInfo.cpp b/lib/Target/ARM/ARMJITInfo.cpp new file mode 100644 index 0000000..294a12b --- /dev/null +++ b/lib/Target/ARM/ARMJITInfo.cpp @@ -0,0 +1,131 @@ +//===-- ARMJITInfo.cpp - Implement the JIT interfaces for the ARM target --===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the Raul Herbster and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the JIT interfaces for the ARM target. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "jit" +#include "ARMJITInfo.h" +#include "ARMRelocations.h" +#include "ARMSubtarget.h" +#include "llvm/CodeGen/MachineCodeEmitter.h" +#include "llvm/Config/alloca.h" +#include <cstdlib> +using namespace llvm; + +void ARMJITInfo::replaceMachineCodeForFunction(void *Old, void *New) { + unsigned char *OldByte = (unsigned char *)Old; + *OldByte++ = 0xEA; // Emit B opcode. + unsigned *OldWord = (unsigned *)OldByte; + unsigned NewAddr = (intptr_t)New; + unsigned OldAddr = (intptr_t)OldWord; + *OldWord = NewAddr - OldAddr - 4; // Emit PC-relative addr of New code. +} + +/// JITCompilerFunction - This contains the address of the JIT function used to +/// compile a function lazily. +static TargetJITInfo::JITCompilerFn JITCompilerFunction; + +// CompilationCallback stub - We can't use a C function with inline assembly in +// it, because we the prolog/epilog inserted by GCC won't work for us. Instead, +// write our own wrapper, which does things our way, so we have complete control +// over register saving and restoring. +extern "C" { +#if defined(__arm__) + void ARMCompilationCallback(void); + asm( + ".text\n" + ".align 2\n" + ".globl ARMCompilationCallback\n" + "ARMCompilationCallback:\n" + // save main registers + "mov ip, sp\n" + "stmfd sp!, {fp, ip, lr, pc}\n" + "sub fp, ip, #4\n" + // arguments to Compilation Callback + // r0 - our lr (address of the call instruction in stub plus 4) + // r1 - stub's lr (address of instruction that called the stub plus 4) + "mov r0, fp\n" // stub's frame + "mov r1, lr\n" // stub's lr + "bl ARMCompilationCallbackC\n" + // restore main registers + "ldmfd sp, {fp, sp, pc}\n"); +#else // Not an ARM host + void ARMCompilationCallback() { + assert(0 && "Cannot call ARMCompilationCallback() on a non-ARM arch!\n"); + abort(); + } +#endif +} + +/// ARMCompilationCallbackC - This is the target-specific function invoked by the +/// function stub when we did not know the real target of a call. This function +/// must locate the start of the stub or call site and pass it into the JIT +/// compiler function. +extern "C" void ARMCompilationCallbackC(intptr_t *StackPtr, intptr_t RetAddr) { + intptr_t *RetAddrLoc = &StackPtr[-1]; + + assert(*RetAddrLoc == RetAddr && + "Could not find return address on the stack!"); +#if 0 + DOUT << "In callback! Addr=" << (void*)RetAddr + << " FP=" << (void*)StackPtr + << ": Resolving call to function: " + << TheVM->getFunctionReferencedName((void*)RetAddr) << "\n"; +#endif + + // Sanity check to make sure this really is a branch and link instruction. + assert(((unsigned char*)RetAddr-1)[3] == 0xEB && "Not a branch and link instr!"); + + intptr_t NewVal = (intptr_t)JITCompilerFunction((void*)RetAddr); + + // Rewrite the call target... so that we don't end up here every time we + // execute the call. + *(intptr_t *)RetAddr = (intptr_t)(NewVal-RetAddr-4); + + // Change the return address to reexecute the branch and link instruction... + *RetAddrLoc -= 1; +} + +TargetJITInfo::LazyResolverFn +ARMJITInfo::getLazyResolverFunction(JITCompilerFn F) { + JITCompilerFunction = F; + return ARMCompilationCallback; +} + +void *ARMJITInfo::emitFunctionStub(void *Fn, MachineCodeEmitter &MCE) { + unsigned addr = (intptr_t)Fn-MCE.getCurrentPCValue()-4; + // If this is just a call to an external function, emit a branch instead of a + // call. The code is the same except for one bit of the last instruction. + if (Fn != (void*)(intptr_t)ARMCompilationCallback) { + MCE.startFunctionStub(4, 2); + MCE.emitByte(0xEA); // branch to the corresponding function addr + MCE.emitByte((unsigned char)(addr >> 0)); + MCE.emitByte((unsigned char)(addr >> 8)); + MCE.emitByte((unsigned char)(addr >> 16)); + return MCE.finishFunctionStub(0); + } else { + MCE.startFunctionStub(5, 2); + MCE.emitByte(0xEB); // branch and link to the corresponding function addr + } + MCE.emitByte((unsigned char)(addr >> 0)); + MCE.emitByte((unsigned char)(addr >> 8)); + MCE.emitByte((unsigned char)(addr >> 16)); + + return MCE.finishFunctionStub(0); +} + +/// relocate - Before the JIT can run a block of code that has been emitted, +/// it must rewrite the code to contain the actual addresses of any +/// referenced global symbols. +void ARMJITInfo::relocate(void *Function, MachineRelocation *MR, + unsigned NumRelocs, unsigned char* GOTBase) { + +} diff --git a/lib/Target/ARM/ARMJITInfo.h b/lib/Target/ARM/ARMJITInfo.h new file mode 100644 index 0000000..bd0ea84 --- /dev/null +++ b/lib/Target/ARM/ARMJITInfo.h @@ -0,0 +1,50 @@ +//===- ARMJITInfo.h - ARM implementation of the JIT interface --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the Raul Herbster and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the declaration of the ARMJITInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef ARMJITINFO_H +#define ARMJITINFO_H + +#include "llvm/Target/TargetJITInfo.h" + +namespace llvm { + class ARMTargetMachine; + + class ARMJITInfo : public TargetJITInfo { + ARMTargetMachine &TM; + public: + ARMJITInfo(ARMTargetMachine &tm) : TM(tm) {useGOT = 0;} + + /// replaceMachineCodeForFunction - Make it so that calling the function + /// whose machine code is at OLD turns into a call to NEW, perhaps by + /// overwriting OLD with a branch to NEW. This is used for self-modifying + /// code. + /// + virtual void replaceMachineCodeForFunction(void *Old, void *New); + + /// emitFunctionStub - Use the specified MachineCodeEmitter object to emit a + /// small native function that simply calls the function at the specified + /// address. + virtual void *emitFunctionStub(void *Fn, MachineCodeEmitter &MCE); + + /// getLazyResolverFunction - Expose the lazy resolver to the JIT. + virtual LazyResolverFn getLazyResolverFunction(JITCompilerFn); + + /// relocate - Before the JIT can run a block of code that has been emitted, + /// it must rewrite the code to contain the actual addresses of any + /// referenced global symbols. + virtual void relocate(void *Function, MachineRelocation *MR, + unsigned NumRelocs, unsigned char* GOTBase); + }; +} + +#endif diff --git a/lib/Target/ARM/ARMLoadStoreOptimizer.cpp b/lib/Target/ARM/ARMLoadStoreOptimizer.cpp new file mode 100644 index 0000000..7562c5b --- /dev/null +++ b/lib/Target/ARM/ARMLoadStoreOptimizer.cpp @@ -0,0 +1,750 @@ +//===-- ARMLoadStoreOptimizer.cpp - ARM load / store opt. pass ----*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Evan Cheng and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains a pass that performs load / store related peephole +// optimizations. This pass should be run after register allocation. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "arm-ldst-opt" +#include "ARM.h" +#include "ARMAddressingModes.h" +#include "ARMMachineFunctionInfo.h" +#include "ARMRegisterInfo.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/RegisterScavenging.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Target/MRegisterInfo.h" +#include "llvm/Target/TargetInstrInfo.h" +#include "llvm/Target/TargetMachine.h" +using namespace llvm; + +STATISTIC(NumLDMGened , "Number of ldm instructions generated"); +STATISTIC(NumSTMGened , "Number of stm instructions generated"); +STATISTIC(NumFLDMGened, "Number of fldm instructions generated"); +STATISTIC(NumFSTMGened, "Number of fstm instructions generated"); + +namespace { + struct VISIBILITY_HIDDEN ARMLoadStoreOpt : public MachineFunctionPass { + static char ID; + ARMLoadStoreOpt() : MachineFunctionPass((intptr_t)&ID) {} + + const TargetInstrInfo *TII; + const MRegisterInfo *MRI; + ARMFunctionInfo *AFI; + RegScavenger *RS; + + virtual bool runOnMachineFunction(MachineFunction &Fn); + + virtual const char *getPassName() const { + return "ARM load / store optimization pass"; + } + + private: + struct MemOpQueueEntry { + int Offset; + unsigned Position; + MachineBasicBlock::iterator MBBI; + bool Merged; + MemOpQueueEntry(int o, int p, MachineBasicBlock::iterator i) + : Offset(o), Position(p), MBBI(i), Merged(false) {}; + }; + typedef SmallVector<MemOpQueueEntry,8> MemOpQueue; + typedef MemOpQueue::iterator MemOpQueueIter; + + SmallVector<MachineBasicBlock::iterator, 4> + MergeLDR_STR(MachineBasicBlock &MBB, unsigned SIndex, unsigned Base, + int Opcode, unsigned Size, + ARMCC::CondCodes Pred, unsigned PredReg, + unsigned Scratch, MemOpQueue &MemOps); + + void AdvanceRS(MachineBasicBlock &MBB, MemOpQueue &MemOps); + bool LoadStoreMultipleOpti(MachineBasicBlock &MBB); + bool MergeReturnIntoLDM(MachineBasicBlock &MBB); + }; + char ARMLoadStoreOpt::ID = 0; +} + +/// createARMLoadStoreOptimizationPass - returns an instance of the load / store +/// optimization pass. +FunctionPass *llvm::createARMLoadStoreOptimizationPass() { + return new ARMLoadStoreOpt(); +} + +static int getLoadStoreMultipleOpcode(int Opcode) { + switch (Opcode) { + case ARM::LDR: + NumLDMGened++; + return ARM::LDM; + case ARM::STR: + NumSTMGened++; + return ARM::STM; + case ARM::FLDS: + NumFLDMGened++; + return ARM::FLDMS; + case ARM::FSTS: + NumFSTMGened++; + return ARM::FSTMS; + case ARM::FLDD: + NumFLDMGened++; + return ARM::FLDMD; + case ARM::FSTD: + NumFSTMGened++; + return ARM::FSTMD; + default: abort(); + } + return 0; +} + +/// mergeOps - Create and insert a LDM or STM with Base as base register and +/// registers in Regs as the register operands that would be loaded / stored. +/// It returns true if the transformation is done. +static bool mergeOps(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, + int Offset, unsigned Base, bool BaseKill, int Opcode, + ARMCC::CondCodes Pred, unsigned PredReg, unsigned Scratch, + SmallVector<std::pair<unsigned, bool>, 8> &Regs, + const TargetInstrInfo *TII) { + // Only a single register to load / store. Don't bother. + unsigned NumRegs = Regs.size(); + if (NumRegs <= 1) + return false; + + ARM_AM::AMSubMode Mode = ARM_AM::ia; + bool isAM4 = Opcode == ARM::LDR || Opcode == ARM::STR; + if (isAM4 && Offset == 4) + Mode = ARM_AM::ib; + else if (isAM4 && Offset == -4 * (int)NumRegs + 4) + Mode = ARM_AM::da; + else if (isAM4 && Offset == -4 * (int)NumRegs) + Mode = ARM_AM::db; + else if (Offset != 0) { + // If starting offset isn't zero, insert a MI to materialize a new base. + // But only do so if it is cost effective, i.e. merging more than two + // loads / stores. + if (NumRegs <= 2) + return false; + + unsigned NewBase; + if (Opcode == ARM::LDR) + // If it is a load, then just use one of the destination register to + // use as the new base. + NewBase = Regs[NumRegs-1].first; + else { + // Use the scratch register to use as a new base. + NewBase = Scratch; + if (NewBase == 0) + return false; + } + int BaseOpc = ARM::ADDri; + if (Offset < 0) { + BaseOpc = ARM::SUBri; + Offset = - Offset; + } + int ImmedOffset = ARM_AM::getSOImmVal(Offset); + if (ImmedOffset == -1) + return false; // Probably not worth it then. + + BuildMI(MBB, MBBI, TII->get(BaseOpc), NewBase) + .addReg(Base, false, false, BaseKill).addImm(ImmedOffset) + .addImm(Pred).addReg(PredReg).addReg(0); + Base = NewBase; + BaseKill = true; // New base is always killed right its use. + } + + bool isDPR = Opcode == ARM::FLDD || Opcode == ARM::FSTD; + bool isDef = Opcode == ARM::LDR || Opcode == ARM::FLDS || Opcode == ARM::FLDD; + Opcode = getLoadStoreMultipleOpcode(Opcode); + MachineInstrBuilder MIB = (isAM4) + ? BuildMI(MBB, MBBI, TII->get(Opcode)).addReg(Base, false, false, BaseKill) + .addImm(ARM_AM::getAM4ModeImm(Mode)).addImm(Pred).addReg(PredReg) + : BuildMI(MBB, MBBI, TII->get(Opcode)).addReg(Base, false, false, BaseKill) + .addImm(ARM_AM::getAM5Opc(Mode, false, isDPR ? NumRegs<<1 : NumRegs)) + .addImm(Pred).addReg(PredReg); + for (unsigned i = 0; i != NumRegs; ++i) + MIB = MIB.addReg(Regs[i].first, isDef, false, Regs[i].second); + + return true; +} + +/// MergeLDR_STR - Merge a number of load / store instructions into one or more +/// load / store multiple instructions. +SmallVector<MachineBasicBlock::iterator, 4> +ARMLoadStoreOpt::MergeLDR_STR(MachineBasicBlock &MBB, unsigned SIndex, + unsigned Base, int Opcode, unsigned Size, + ARMCC::CondCodes Pred, unsigned PredReg, + unsigned Scratch, MemOpQueue &MemOps) { + SmallVector<MachineBasicBlock::iterator, 4> Merges; + bool isAM4 = Opcode == ARM::LDR || Opcode == ARM::STR; + int Offset = MemOps[SIndex].Offset; + int SOffset = Offset; + unsigned Pos = MemOps[SIndex].Position; + MachineBasicBlock::iterator Loc = MemOps[SIndex].MBBI; + unsigned PReg = MemOps[SIndex].MBBI->getOperand(0).getReg(); + unsigned PRegNum = ARMRegisterInfo::getRegisterNumbering(PReg); + bool isKill = MemOps[SIndex].MBBI->getOperand(0).isKill(); + + SmallVector<std::pair<unsigned,bool>, 8> Regs; + Regs.push_back(std::make_pair(PReg, isKill)); + for (unsigned i = SIndex+1, e = MemOps.size(); i != e; ++i) { + int NewOffset = MemOps[i].Offset; + unsigned Reg = MemOps[i].MBBI->getOperand(0).getReg(); + unsigned RegNum = ARMRegisterInfo::getRegisterNumbering(Reg); + isKill = MemOps[i].MBBI->getOperand(0).isKill(); + // AM4 - register numbers in ascending order. + // AM5 - consecutive register numbers in ascending order. + if (NewOffset == Offset + (int)Size && + ((isAM4 && RegNum > PRegNum) || RegNum == PRegNum+1)) { + Offset += Size; + Regs.push_back(std::make_pair(Reg, isKill)); + PRegNum = RegNum; + } else { + // Can't merge this in. Try merge the earlier ones first. + if (mergeOps(MBB, ++Loc, SOffset, Base, false, Opcode, Pred, PredReg, + Scratch, Regs, TII)) { + Merges.push_back(prior(Loc)); + for (unsigned j = SIndex; j < i; ++j) { + MBB.erase(MemOps[j].MBBI); + MemOps[j].Merged = true; + } + } + SmallVector<MachineBasicBlock::iterator, 4> Merges2 = + MergeLDR_STR(MBB, i, Base, Opcode, Size, Pred, PredReg, Scratch,MemOps); + Merges.append(Merges2.begin(), Merges2.end()); + return Merges; + } + + if (MemOps[i].Position > Pos) { + Pos = MemOps[i].Position; + Loc = MemOps[i].MBBI; + } + } + + bool BaseKill = Loc->findRegisterUseOperandIdx(Base, true) != -1; + if (mergeOps(MBB, ++Loc, SOffset, Base, BaseKill, Opcode, Pred, PredReg, + Scratch, Regs, TII)) { + Merges.push_back(prior(Loc)); + for (unsigned i = SIndex, e = MemOps.size(); i != e; ++i) { + MBB.erase(MemOps[i].MBBI); + MemOps[i].Merged = true; + } + } + + return Merges; +} + +/// getInstrPredicate - If instruction is predicated, returns its predicate +/// condition, otherwise returns AL. It also returns the condition code +/// register by reference. +static ARMCC::CondCodes getInstrPredicate(MachineInstr *MI, unsigned &PredReg) { + int PIdx = MI->findFirstPredOperandIdx(); + if (PIdx == -1) { + PredReg = 0; + return ARMCC::AL; + } + + PredReg = MI->getOperand(PIdx+1).getReg(); + return (ARMCC::CondCodes)MI->getOperand(PIdx).getImmedValue(); +} + +static inline bool isMatchingDecrement(MachineInstr *MI, unsigned Base, + unsigned Bytes, ARMCC::CondCodes Pred, + unsigned PredReg) { + unsigned MyPredReg = 0; + return (MI && MI->getOpcode() == ARM::SUBri && + MI->getOperand(0).getReg() == Base && + MI->getOperand(1).getReg() == Base && + ARM_AM::getAM2Offset(MI->getOperand(2).getImm()) == Bytes && + getInstrPredicate(MI, MyPredReg) == Pred && + MyPredReg == PredReg); +} + +static inline bool isMatchingIncrement(MachineInstr *MI, unsigned Base, + unsigned Bytes, ARMCC::CondCodes Pred, + unsigned PredReg) { + unsigned MyPredReg = 0; + return (MI && MI->getOpcode() == ARM::ADDri && + MI->getOperand(0).getReg() == Base && + MI->getOperand(1).getReg() == Base && + ARM_AM::getAM2Offset(MI->getOperand(2).getImm()) == Bytes && + getInstrPredicate(MI, MyPredReg) == Pred && + MyPredReg == PredReg); +} + +static inline unsigned getLSMultipleTransferSize(MachineInstr *MI) { + switch (MI->getOpcode()) { + default: return 0; + case ARM::LDR: + case ARM::STR: + case ARM::FLDS: + case ARM::FSTS: + return 4; + case ARM::FLDD: + case ARM::FSTD: + return 8; + case ARM::LDM: + case ARM::STM: + return (MI->getNumOperands() - 4) * 4; + case ARM::FLDMS: + case ARM::FSTMS: + case ARM::FLDMD: + case ARM::FSTMD: + return ARM_AM::getAM5Offset(MI->getOperand(1).getImm()) * 4; + } +} + +/// mergeBaseUpdateLSMultiple - Fold proceeding/trailing inc/dec of base +/// register into the LDM/STM/FLDM{D|S}/FSTM{D|S} op when possible: +/// +/// stmia rn, <ra, rb, rc> +/// rn := rn + 4 * 3; +/// => +/// stmia rn!, <ra, rb, rc> +/// +/// rn := rn - 4 * 3; +/// ldmia rn, <ra, rb, rc> +/// => +/// ldmdb rn!, <ra, rb, rc> +static bool mergeBaseUpdateLSMultiple(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI) { + MachineInstr *MI = MBBI; + unsigned Base = MI->getOperand(0).getReg(); + unsigned Bytes = getLSMultipleTransferSize(MI); + unsigned PredReg = 0; + ARMCC::CondCodes Pred = getInstrPredicate(MI, PredReg); + int Opcode = MI->getOpcode(); + bool isAM4 = Opcode == ARM::LDM || Opcode == ARM::STM; + + if (isAM4) { + if (ARM_AM::getAM4WBFlag(MI->getOperand(1).getImm())) + return false; + + // Can't use the updating AM4 sub-mode if the base register is also a dest + // register. e.g. ldmdb r0!, {r0, r1, r2}. The behavior is undefined. + for (unsigned i = 3, e = MI->getNumOperands(); i != e; ++i) { + if (MI->getOperand(i).getReg() == Base) + return false; + } + + ARM_AM::AMSubMode Mode = ARM_AM::getAM4SubMode(MI->getOperand(1).getImm()); + if (MBBI != MBB.begin()) { + MachineBasicBlock::iterator PrevMBBI = prior(MBBI); + if (Mode == ARM_AM::ia && + isMatchingDecrement(PrevMBBI, Base, Bytes, Pred, PredReg)) { + MI->getOperand(1).setImm(ARM_AM::getAM4ModeImm(ARM_AM::db, true)); + MBB.erase(PrevMBBI); + return true; + } else if (Mode == ARM_AM::ib && + isMatchingDecrement(PrevMBBI, Base, Bytes, Pred, PredReg)) { + MI->getOperand(1).setImm(ARM_AM::getAM4ModeImm(ARM_AM::da, true)); + MBB.erase(PrevMBBI); + return true; + } + } + + if (MBBI != MBB.end()) { + MachineBasicBlock::iterator NextMBBI = next(MBBI); + if ((Mode == ARM_AM::ia || Mode == ARM_AM::ib) && + isMatchingIncrement(NextMBBI, Base, Bytes, Pred, PredReg)) { + MI->getOperand(1).setImm(ARM_AM::getAM4ModeImm(Mode, true)); + MBB.erase(NextMBBI); + return true; + } else if ((Mode == ARM_AM::da || Mode == ARM_AM::db) && + isMatchingDecrement(NextMBBI, Base, Bytes, Pred, PredReg)) { + MI->getOperand(1).setImm(ARM_AM::getAM4ModeImm(Mode, true)); + MBB.erase(NextMBBI); + return true; + } + } + } else { + // FLDM{D|S}, FSTM{D|S} addressing mode 5 ops. + if (ARM_AM::getAM5WBFlag(MI->getOperand(1).getImm())) + return false; + + ARM_AM::AMSubMode Mode = ARM_AM::getAM5SubMode(MI->getOperand(1).getImm()); + unsigned Offset = ARM_AM::getAM5Offset(MI->getOperand(1).getImm()); + if (MBBI != MBB.begin()) { + MachineBasicBlock::iterator PrevMBBI = prior(MBBI); + if (Mode == ARM_AM::ia && + isMatchingDecrement(PrevMBBI, Base, Bytes, Pred, PredReg)) { + MI->getOperand(1).setImm(ARM_AM::getAM5Opc(ARM_AM::db, true, Offset)); + MBB.erase(PrevMBBI); + return true; + } + } + + if (MBBI != MBB.end()) { + MachineBasicBlock::iterator NextMBBI = next(MBBI); + if (Mode == ARM_AM::ia && + isMatchingIncrement(NextMBBI, Base, Bytes, Pred, PredReg)) { + MI->getOperand(1).setImm(ARM_AM::getAM5Opc(ARM_AM::ia, true, Offset)); + MBB.erase(NextMBBI); + } + return true; + } + } + + return false; +} + +static unsigned getPreIndexedLoadStoreOpcode(unsigned Opc) { + switch (Opc) { + case ARM::LDR: return ARM::LDR_PRE; + case ARM::STR: return ARM::STR_PRE; + case ARM::FLDS: return ARM::FLDMS; + case ARM::FLDD: return ARM::FLDMD; + case ARM::FSTS: return ARM::FSTMS; + case ARM::FSTD: return ARM::FSTMD; + default: abort(); + } + return 0; +} + +static unsigned getPostIndexedLoadStoreOpcode(unsigned Opc) { + switch (Opc) { + case ARM::LDR: return ARM::LDR_POST; + case ARM::STR: return ARM::STR_POST; + case ARM::FLDS: return ARM::FLDMS; + case ARM::FLDD: return ARM::FLDMD; + case ARM::FSTS: return ARM::FSTMS; + case ARM::FSTD: return ARM::FSTMD; + default: abort(); + } + return 0; +} + +/// mergeBaseUpdateLoadStore - Fold proceeding/trailing inc/dec of base +/// register into the LDR/STR/FLD{D|S}/FST{D|S} op when possible: +static bool mergeBaseUpdateLoadStore(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + const TargetInstrInfo *TII) { + MachineInstr *MI = MBBI; + unsigned Base = MI->getOperand(1).getReg(); + bool BaseKill = MI->getOperand(1).isKill(); + unsigned Bytes = getLSMultipleTransferSize(MI); + int Opcode = MI->getOpcode(); + bool isAM2 = Opcode == ARM::LDR || Opcode == ARM::STR; + if ((isAM2 && ARM_AM::getAM2Offset(MI->getOperand(3).getImm()) != 0) || + (!isAM2 && ARM_AM::getAM5Offset(MI->getOperand(2).getImm()) != 0)) + return false; + + bool isLd = Opcode == ARM::LDR || Opcode == ARM::FLDS || Opcode == ARM::FLDD; + // Can't do the merge if the destination register is the same as the would-be + // writeback register. + if (isLd && MI->getOperand(0).getReg() == Base) + return false; + + unsigned PredReg = 0; + ARMCC::CondCodes Pred = getInstrPredicate(MI, PredReg); + bool DoMerge = false; + ARM_AM::AddrOpc AddSub = ARM_AM::add; + unsigned NewOpc = 0; + if (MBBI != MBB.begin()) { + MachineBasicBlock::iterator PrevMBBI = prior(MBBI); + if (isMatchingDecrement(PrevMBBI, Base, Bytes, Pred, PredReg)) { + DoMerge = true; + AddSub = ARM_AM::sub; + NewOpc = getPreIndexedLoadStoreOpcode(Opcode); + } else if (isAM2 && isMatchingIncrement(PrevMBBI, Base, Bytes, + Pred, PredReg)) { + DoMerge = true; + NewOpc = getPreIndexedLoadStoreOpcode(Opcode); + } + if (DoMerge) + MBB.erase(PrevMBBI); + } + + if (!DoMerge && MBBI != MBB.end()) { + MachineBasicBlock::iterator NextMBBI = next(MBBI); + if (isAM2 && isMatchingDecrement(NextMBBI, Base, Bytes, Pred, PredReg)) { + DoMerge = true; + AddSub = ARM_AM::sub; + NewOpc = getPostIndexedLoadStoreOpcode(Opcode); + } else if (isMatchingIncrement(NextMBBI, Base, Bytes, Pred, PredReg)) { + DoMerge = true; + NewOpc = getPostIndexedLoadStoreOpcode(Opcode); + } + if (DoMerge) + MBB.erase(NextMBBI); + } + + if (!DoMerge) + return false; + + bool isDPR = NewOpc == ARM::FLDMD || NewOpc == ARM::FSTMD; + unsigned Offset = isAM2 ? ARM_AM::getAM2Opc(AddSub, Bytes, ARM_AM::no_shift) + : ARM_AM::getAM5Opc((AddSub == ARM_AM::sub) ? ARM_AM::db : ARM_AM::ia, + true, isDPR ? 2 : 1); + if (isLd) { + if (isAM2) + // LDR_PRE, LDR_POST; + BuildMI(MBB, MBBI, TII->get(NewOpc), MI->getOperand(0).getReg()) + .addReg(Base, true) + .addReg(Base).addReg(0).addImm(Offset).addImm(Pred).addReg(PredReg); + else + // FLDMS, FLDMD + BuildMI(MBB, MBBI, TII->get(NewOpc)).addReg(Base, false, false, BaseKill) + .addImm(Offset).addImm(Pred).addReg(PredReg) + .addReg(MI->getOperand(0).getReg(), true); + } else { + MachineOperand &MO = MI->getOperand(0); + if (isAM2) + // STR_PRE, STR_POST; + BuildMI(MBB, MBBI, TII->get(NewOpc), Base) + .addReg(MO.getReg(), false, false, MO.isKill()) + .addReg(Base).addReg(0).addImm(Offset).addImm(Pred).addReg(PredReg); + else + // FSTMS, FSTMD + BuildMI(MBB, MBBI, TII->get(NewOpc)).addReg(Base).addImm(Offset) + .addImm(Pred).addReg(PredReg) + .addReg(MO.getReg(), false, false, MO.isKill()); + } + MBB.erase(MBBI); + + return true; +} + +/// isMemoryOp - Returns true if instruction is a memory operations (that this +/// pass is capable of operating on). +static bool isMemoryOp(MachineInstr *MI) { + int Opcode = MI->getOpcode(); + switch (Opcode) { + default: break; + case ARM::LDR: + case ARM::STR: + return MI->getOperand(1).isRegister() && MI->getOperand(2).getReg() == 0; + case ARM::FLDS: + case ARM::FSTS: + return MI->getOperand(1).isRegister(); + case ARM::FLDD: + case ARM::FSTD: + return MI->getOperand(1).isRegister(); + } + return false; +} + +/// AdvanceRS - Advance register scavenger to just before the earliest memory +/// op that is being merged. +void ARMLoadStoreOpt::AdvanceRS(MachineBasicBlock &MBB, MemOpQueue &MemOps) { + MachineBasicBlock::iterator Loc = MemOps[0].MBBI; + unsigned Position = MemOps[0].Position; + for (unsigned i = 1, e = MemOps.size(); i != e; ++i) { + if (MemOps[i].Position < Position) { + Position = MemOps[i].Position; + Loc = MemOps[i].MBBI; + } + } + + if (Loc != MBB.begin()) + RS->forward(prior(Loc)); +} + +/// LoadStoreMultipleOpti - An optimization pass to turn multiple LDR / STR +/// ops of the same base and incrementing offset into LDM / STM ops. +bool ARMLoadStoreOpt::LoadStoreMultipleOpti(MachineBasicBlock &MBB) { + unsigned NumMerges = 0; + unsigned NumMemOps = 0; + MemOpQueue MemOps; + unsigned CurrBase = 0; + int CurrOpc = -1; + unsigned CurrSize = 0; + ARMCC::CondCodes CurrPred = ARMCC::AL; + unsigned CurrPredReg = 0; + unsigned Position = 0; + + RS->enterBasicBlock(&MBB); + MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end(); + while (MBBI != E) { + bool Advance = false; + bool TryMerge = false; + bool Clobber = false; + + bool isMemOp = isMemoryOp(MBBI); + if (isMemOp) { + int Opcode = MBBI->getOpcode(); + bool isAM2 = Opcode == ARM::LDR || Opcode == ARM::STR; + unsigned Size = getLSMultipleTransferSize(MBBI); + unsigned Base = MBBI->getOperand(1).getReg(); + unsigned PredReg = 0; + ARMCC::CondCodes Pred = getInstrPredicate(MBBI, PredReg); + const TargetInstrDescriptor *TID = MBBI->getInstrDescriptor(); + unsigned OffField = MBBI->getOperand(TID->numOperands-3).getImm(); + int Offset = isAM2 + ? ARM_AM::getAM2Offset(OffField) : ARM_AM::getAM5Offset(OffField) * 4; + if (isAM2) { + if (ARM_AM::getAM2Op(OffField) == ARM_AM::sub) + Offset = -Offset; + } else { + if (ARM_AM::getAM5Op(OffField) == ARM_AM::sub) + Offset = -Offset; + } + // Watch out for: + // r4 := ldr [r5] + // r5 := ldr [r5, #4] + // r6 := ldr [r5, #8] + // + // The second ldr has effectively broken the chain even though it + // looks like the later ldr(s) use the same base register. Try to + // merge the ldr's so far, including this one. But don't try to + // combine the following ldr(s). + Clobber = (Opcode == ARM::LDR && Base == MBBI->getOperand(0).getReg()); + if (CurrBase == 0 && !Clobber) { + // Start of a new chain. + CurrBase = Base; + CurrOpc = Opcode; + CurrSize = Size; + CurrPred = Pred; + CurrPredReg = PredReg; + MemOps.push_back(MemOpQueueEntry(Offset, Position, MBBI)); + NumMemOps++; + Advance = true; + } else { + if (Clobber) { + TryMerge = true; + Advance = true; + } + + if (CurrOpc == Opcode && CurrBase == Base && CurrPred == Pred) { + // No need to match PredReg. + // Continue adding to the queue. + if (Offset > MemOps.back().Offset) { + MemOps.push_back(MemOpQueueEntry(Offset, Position, MBBI)); + NumMemOps++; + Advance = true; + } else { + for (MemOpQueueIter I = MemOps.begin(), E = MemOps.end(); + I != E; ++I) { + if (Offset < I->Offset) { + MemOps.insert(I, MemOpQueueEntry(Offset, Position, MBBI)); + NumMemOps++; + Advance = true; + break; + } else if (Offset == I->Offset) { + // Collision! This can't be merged! + break; + } + } + } + } + } + } + + if (Advance) { + ++Position; + ++MBBI; + } else + TryMerge = true; + + if (TryMerge) { + if (NumMemOps > 1) { + // Try to find a free register to use as a new base in case it's needed. + // First advance to the instruction just before the start of the chain. + AdvanceRS(MBB, MemOps); + // Find a scratch register. Make sure it's a call clobbered register or + // a spilled callee-saved register. + unsigned Scratch = RS->FindUnusedReg(&ARM::GPRRegClass, true); + if (!Scratch) + Scratch = RS->FindUnusedReg(&ARM::GPRRegClass, + AFI->getSpilledCSRegisters()); + // Process the load / store instructions. + RS->forward(prior(MBBI)); + + // Merge ops. + SmallVector<MachineBasicBlock::iterator,4> MBBII = + MergeLDR_STR(MBB, 0, CurrBase, CurrOpc, CurrSize, + CurrPred, CurrPredReg, Scratch, MemOps); + + // Try folding preceeding/trailing base inc/dec into the generated + // LDM/STM ops. + for (unsigned i = 0, e = MBBII.size(); i < e; ++i) + if (mergeBaseUpdateLSMultiple(MBB, MBBII[i])) + NumMerges++; + NumMerges += MBBII.size(); + + // Try folding preceeding/trailing base inc/dec into those load/store + // that were not merged to form LDM/STM ops. + for (unsigned i = 0; i != NumMemOps; ++i) + if (!MemOps[i].Merged) + if (mergeBaseUpdateLoadStore(MBB, MemOps[i].MBBI, TII)) + NumMerges++; + + // RS may be pointing to an instruction that's deleted. + RS->skipTo(prior(MBBI)); + } + + CurrBase = 0; + CurrOpc = -1; + CurrSize = 0; + CurrPred = ARMCC::AL; + CurrPredReg = 0; + if (NumMemOps) { + MemOps.clear(); + NumMemOps = 0; + } + + // If iterator hasn't been advanced and this is not a memory op, skip it. + // It can't start a new chain anyway. + if (!Advance && !isMemOp && MBBI != E) { + ++Position; + ++MBBI; + } + } + } + return NumMerges > 0; +} + +/// MergeReturnIntoLDM - If this is a exit BB, try merging the return op +/// (bx lr) into the preceeding stack restore so it directly restore the value +/// of LR into pc. +/// ldmfd sp!, {r7, lr} +/// bx lr +/// => +/// ldmfd sp!, {r7, pc} +bool ARMLoadStoreOpt::MergeReturnIntoLDM(MachineBasicBlock &MBB) { + if (MBB.empty()) return false; + + MachineBasicBlock::iterator MBBI = prior(MBB.end()); + if (MBBI->getOpcode() == ARM::BX_RET && MBBI != MBB.begin()) { + MachineInstr *PrevMI = prior(MBBI); + if (PrevMI->getOpcode() == ARM::LDM) { + MachineOperand &MO = PrevMI->getOperand(PrevMI->getNumOperands()-1); + if (MO.getReg() == ARM::LR) { + PrevMI->setInstrDescriptor(TII->get(ARM::LDM_RET)); + MO.setReg(ARM::PC); + MBB.erase(MBBI); + return true; + } + } + } + return false; +} + +bool ARMLoadStoreOpt::runOnMachineFunction(MachineFunction &Fn) { + const TargetMachine &TM = Fn.getTarget(); + AFI = Fn.getInfo<ARMFunctionInfo>(); + TII = TM.getInstrInfo(); + MRI = TM.getRegisterInfo(); + RS = new RegScavenger(); + + bool Modified = false; + for (MachineFunction::iterator MFI = Fn.begin(), E = Fn.end(); MFI != E; + ++MFI) { + MachineBasicBlock &MBB = *MFI; + Modified |= LoadStoreMultipleOpti(MBB); + Modified |= MergeReturnIntoLDM(MBB); + } + + delete RS; + return Modified; +} diff --git a/lib/Target/ARM/ARMMachineFunctionInfo.h b/lib/Target/ARM/ARMMachineFunctionInfo.h new file mode 100644 index 0000000..665c5e3 --- /dev/null +++ b/lib/Target/ARM/ARMMachineFunctionInfo.h @@ -0,0 +1,220 @@ +//====- ARMMachineFuctionInfo.h - ARM machine function info -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the Evan Cheng and is distributed under +// the University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares ARM-specific per-machine-function information. +// +//===----------------------------------------------------------------------===// + +#ifndef ARMMACHINEFUNCTIONINFO_H +#define ARMMACHINEFUNCTIONINFO_H + +#include "ARMSubtarget.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/Target/MRegisterInfo.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/ADT/BitVector.h" + +namespace llvm { + +/// ARMFunctionInfo - This class is derived from MachineFunction private +/// ARM target-specific information for each MachineFunction. +class ARMFunctionInfo : public MachineFunctionInfo { + + /// isThumb - True if this function is compiled under Thumb mode. + /// Used to initialized Align, so must precede it. + bool isThumb; + + /// Align - required alignment. ARM functions and Thumb functions with + /// constant pools require 4-byte alignment; other Thumb functions + /// require only 2-byte alignment. + unsigned Align; + + /// VarArgsRegSaveSize - Size of the register save area for vararg functions. + /// + unsigned VarArgsRegSaveSize; + + /// HasStackFrame - True if this function has a stack frame. Set by + /// processFunctionBeforeCalleeSavedScan(). + bool HasStackFrame; + + /// LRSpilledForFarJump - True if the LR register has been for spilled to + /// enable far jump. + bool LRSpilledForFarJump; + + /// R3IsLiveIn - True if R3 is live in to this function. + /// FIXME: Remove when register scavenger for Thumb is done. + bool R3IsLiveIn; + + /// FramePtrSpillOffset - If HasStackFrame, this records the frame pointer + /// spill stack offset. + unsigned FramePtrSpillOffset; + + /// GPRCS1Offset, GPRCS2Offset, DPRCSOffset - Starting offset of callee saved + /// register spills areas. For Mac OS X: + /// + /// GPR callee-saved (1) : r4, r5, r6, r7, lr + /// -------------------------------------------- + /// GPR callee-saved (2) : r8, r10, r11 + /// -------------------------------------------- + /// DPR callee-saved : d8 - d15 + unsigned GPRCS1Offset; + unsigned GPRCS2Offset; + unsigned DPRCSOffset; + + /// GPRCS1Size, GPRCS2Size, DPRCSSize - Sizes of callee saved register spills + /// areas. + unsigned GPRCS1Size; + unsigned GPRCS2Size; + unsigned DPRCSSize; + + /// GPRCS1Frames, GPRCS2Frames, DPRCSFrames - Keeps track of frame indices + /// which belong to these spill areas. + BitVector GPRCS1Frames; + BitVector GPRCS2Frames; + BitVector DPRCSFrames; + + /// SpilledCSRegs - A BitVector mask of all spilled callee-saved registers. + /// + BitVector SpilledCSRegs; + + /// JumpTableUId - Unique id for jumptables. + /// + unsigned JumpTableUId; + +public: + ARMFunctionInfo() : + isThumb(false), + Align(2U), + VarArgsRegSaveSize(0), HasStackFrame(false), + LRSpilledForFarJump(false), R3IsLiveIn(false), + FramePtrSpillOffset(0), GPRCS1Offset(0), GPRCS2Offset(0), DPRCSOffset(0), + GPRCS1Size(0), GPRCS2Size(0), DPRCSSize(0), + GPRCS1Frames(0), GPRCS2Frames(0), DPRCSFrames(0), + JumpTableUId(0) {} + + ARMFunctionInfo(MachineFunction &MF) : + isThumb(MF.getTarget().getSubtarget<ARMSubtarget>().isThumb()), + Align(isThumb ? 1U : 2U), + VarArgsRegSaveSize(0), HasStackFrame(false), + LRSpilledForFarJump(false), R3IsLiveIn(false), + FramePtrSpillOffset(0), GPRCS1Offset(0), GPRCS2Offset(0), DPRCSOffset(0), + GPRCS1Size(0), GPRCS2Size(0), DPRCSSize(0), + GPRCS1Frames(32), GPRCS2Frames(32), DPRCSFrames(32), + SpilledCSRegs(MF.getTarget().getRegisterInfo()->getNumRegs()), + JumpTableUId(0) {} + + bool isThumbFunction() const { return isThumb; } + + unsigned getAlign() const { return Align; } + void setAlign(unsigned a) { Align = a; } + + unsigned getVarArgsRegSaveSize() const { return VarArgsRegSaveSize; } + void setVarArgsRegSaveSize(unsigned s) { VarArgsRegSaveSize = s; } + + bool hasStackFrame() const { return HasStackFrame; } + void setHasStackFrame(bool s) { HasStackFrame = s; } + + bool isLRSpilledForFarJump() const { return LRSpilledForFarJump; } + void setLRIsSpilledForFarJump(bool s) { LRSpilledForFarJump = s; } + + // FIXME: Remove when register scavenger for Thumb is done. + bool isR3LiveIn() const { return R3IsLiveIn; } + void setR3IsLiveIn(bool l) { R3IsLiveIn = l; } + + unsigned getFramePtrSpillOffset() const { return FramePtrSpillOffset; } + void setFramePtrSpillOffset(unsigned o) { FramePtrSpillOffset = o; } + + unsigned getGPRCalleeSavedArea1Offset() const { return GPRCS1Offset; } + unsigned getGPRCalleeSavedArea2Offset() const { return GPRCS2Offset; } + unsigned getDPRCalleeSavedAreaOffset() const { return DPRCSOffset; } + + void setGPRCalleeSavedArea1Offset(unsigned o) { GPRCS1Offset = o; } + void setGPRCalleeSavedArea2Offset(unsigned o) { GPRCS2Offset = o; } + void setDPRCalleeSavedAreaOffset(unsigned o) { DPRCSOffset = o; } + + unsigned getGPRCalleeSavedArea1Size() const { return GPRCS1Size; } + unsigned getGPRCalleeSavedArea2Size() const { return GPRCS2Size; } + unsigned getDPRCalleeSavedAreaSize() const { return DPRCSSize; } + + void setGPRCalleeSavedArea1Size(unsigned s) { GPRCS1Size = s; } + void setGPRCalleeSavedArea2Size(unsigned s) { GPRCS2Size = s; } + void setDPRCalleeSavedAreaSize(unsigned s) { DPRCSSize = s; } + + bool isGPRCalleeSavedArea1Frame(int fi) const { + if (fi < 0 || fi >= (int)GPRCS1Frames.size()) + return false; + return GPRCS1Frames[fi]; + } + bool isGPRCalleeSavedArea2Frame(int fi) const { + if (fi < 0 || fi >= (int)GPRCS2Frames.size()) + return false; + return GPRCS2Frames[fi]; + } + bool isDPRCalleeSavedAreaFrame(int fi) const { + if (fi < 0 || fi >= (int)DPRCSFrames.size()) + return false; + return DPRCSFrames[fi]; + } + + void addGPRCalleeSavedArea1Frame(int fi) { + if (fi >= 0) { + int Size = GPRCS1Frames.size(); + if (fi >= Size) { + Size *= 2; + if (fi >= Size) + Size = fi+1; + GPRCS1Frames.resize(Size); + } + GPRCS1Frames[fi] = true; + } + } + void addGPRCalleeSavedArea2Frame(int fi) { + if (fi >= 0) { + int Size = GPRCS2Frames.size(); + if (fi >= Size) { + Size *= 2; + if (fi >= Size) + Size = fi+1; + GPRCS2Frames.resize(Size); + } + GPRCS2Frames[fi] = true; + } + } + void addDPRCalleeSavedAreaFrame(int fi) { + if (fi >= 0) { + int Size = DPRCSFrames.size(); + if (fi >= Size) { + Size *= 2; + if (fi >= Size) + Size = fi+1; + DPRCSFrames.resize(Size); + } + DPRCSFrames[fi] = true; + } + } + + void setCSRegisterIsSpilled(unsigned Reg) { + SpilledCSRegs.set(Reg); + } + + bool isCSRegisterSpilled(unsigned Reg) { + return SpilledCSRegs[Reg]; + } + + const BitVector &getSpilledCSRegisters() const { + return SpilledCSRegs; + } + + unsigned createJumpTableUId() { + return JumpTableUId++; + } +}; +} // End llvm namespace + +#endif // ARMMACHINEFUNCTIONINFO_H diff --git a/lib/Target/ARM/ARMRegisterInfo.cpp b/lib/Target/ARM/ARMRegisterInfo.cpp new file mode 100644 index 0000000..f8e10de --- /dev/null +++ b/lib/Target/ARM/ARMRegisterInfo.cpp @@ -0,0 +1,1566 @@ +//===- ARMRegisterInfo.cpp - ARM Register Information -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the "Instituto Nokia de Tecnologia" and +// is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the ARM implementation of the MRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#include "ARM.h" +#include "ARMAddressingModes.h" +#include "ARMInstrInfo.h" +#include "ARMMachineFunctionInfo.h" +#include "ARMRegisterInfo.h" +#include "ARMSubtarget.h" +#include "llvm/Constants.h" +#include "llvm/DerivedTypes.h" +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineLocation.h" +#include "llvm/CodeGen/RegisterScavenging.h" +#include "llvm/Target/TargetFrameInfo.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/CommandLine.h" +#include <algorithm> +using namespace llvm; + +static cl::opt<bool> ThumbRegScavenging("enable-thumb-reg-scavenging", + cl::Hidden, + cl::desc("Enable register scavenging on Thumb")); + +unsigned ARMRegisterInfo::getRegisterNumbering(unsigned RegEnum) { + using namespace ARM; + switch (RegEnum) { + case R0: case S0: case D0: return 0; + case R1: case S1: case D1: return 1; + case R2: case S2: case D2: return 2; + case R3: case S3: case D3: return 3; + case R4: case S4: case D4: return 4; + case R5: case S5: case D5: return 5; + case R6: case S6: case D6: return 6; + case R7: case S7: case D7: return 7; + case R8: case S8: case D8: return 8; + case R9: case S9: case D9: return 9; + case R10: case S10: case D10: return 10; + case R11: case S11: case D11: return 11; + case R12: case S12: case D12: return 12; + case SP: case S13: case D13: return 13; + case LR: case S14: case D14: return 14; + case PC: case S15: case D15: return 15; + case S16: return 16; + case S17: return 17; + case S18: return 18; + case S19: return 19; + case S20: return 20; + case S21: return 21; + case S22: return 22; + case S23: return 23; + case S24: return 24; + case S25: return 25; + case S26: return 26; + case S27: return 27; + case S28: return 28; + case S29: return 29; + case S30: return 30; + case S31: return 31; + default: + assert(0 && "Unknown ARM register!"); + abort(); + } +} + +ARMRegisterInfo::ARMRegisterInfo(const TargetInstrInfo &tii, + const ARMSubtarget &sti) + : ARMGenRegisterInfo(ARM::ADJCALLSTACKDOWN, ARM::ADJCALLSTACKUP), + TII(tii), STI(sti), + FramePtr((STI.useThumbBacktraces() || STI.isThumb()) ? ARM::R7 : ARM::R11) { +} + +bool ARMRegisterInfo::spillCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const std::vector<CalleeSavedInfo> &CSI) const { + MachineFunction &MF = *MBB.getParent(); + ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>(); + if (!AFI->isThumbFunction() || CSI.empty()) + return false; + + MachineInstrBuilder MIB = BuildMI(MBB, MI, TII.get(ARM::tPUSH)); + for (unsigned i = CSI.size(); i != 0; --i) { + unsigned Reg = CSI[i-1].getReg(); + // Add the callee-saved register as live-in. It's killed at the spill. + MBB.addLiveIn(Reg); + MIB.addReg(Reg, false/*isDef*/,false/*isImp*/,true/*isKill*/); + } + return true; +} + +bool ARMRegisterInfo::restoreCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const std::vector<CalleeSavedInfo> &CSI) const { + MachineFunction &MF = *MBB.getParent(); + ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>(); + if (!AFI->isThumbFunction() || CSI.empty()) + return false; + + bool isVarArg = AFI->getVarArgsRegSaveSize() > 0; + MachineInstr *PopMI = new MachineInstr(TII.get(ARM::tPOP)); + MBB.insert(MI, PopMI); + for (unsigned i = CSI.size(); i != 0; --i) { + unsigned Reg = CSI[i-1].getReg(); + if (Reg == ARM::LR) { + // Special epilogue for vararg functions. See emitEpilogue + if (isVarArg) + continue; + Reg = ARM::PC; + PopMI->setInstrDescriptor(TII.get(ARM::tPOP_RET)); + MBB.erase(MI); + } + PopMI->addRegOperand(Reg, true); + } + return true; +} + +void ARMRegisterInfo:: +storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, + unsigned SrcReg, int FI, + const TargetRegisterClass *RC) const { + if (RC == ARM::GPRRegisterClass) { + MachineFunction &MF = *MBB.getParent(); + ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>(); + if (AFI->isThumbFunction()) + BuildMI(MBB, I, TII.get(ARM::tSpill)).addReg(SrcReg, false, false, true) + .addFrameIndex(FI).addImm(0); + else + BuildMI(MBB, I, TII.get(ARM::STR)).addReg(SrcReg, false, false, true) + .addFrameIndex(FI).addReg(0).addImm(0).addImm((int64_t)ARMCC::AL) + .addReg(0); + } else if (RC == ARM::DPRRegisterClass) { + BuildMI(MBB, I, TII.get(ARM::FSTD)).addReg(SrcReg, false, false, true) + .addFrameIndex(FI).addImm(0).addImm((int64_t)ARMCC::AL).addReg(0); + } else { + assert(RC == ARM::SPRRegisterClass && "Unknown regclass!"); + BuildMI(MBB, I, TII.get(ARM::FSTS)).addReg(SrcReg, false, false, true) + .addFrameIndex(FI).addImm(0).addImm((int64_t)ARMCC::AL).addReg(0); + } +} + +void ARMRegisterInfo:: +loadRegFromStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, + unsigned DestReg, int FI, + const TargetRegisterClass *RC) const { + if (RC == ARM::GPRRegisterClass) { + MachineFunction &MF = *MBB.getParent(); + ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>(); + if (AFI->isThumbFunction()) + BuildMI(MBB, I, TII.get(ARM::tRestore), DestReg) + .addFrameIndex(FI).addImm(0); + else + BuildMI(MBB, I, TII.get(ARM::LDR), DestReg) + .addFrameIndex(FI).addReg(0).addImm(0).addImm((int64_t)ARMCC::AL) + .addReg(0); + } else if (RC == ARM::DPRRegisterClass) { + BuildMI(MBB, I, TII.get(ARM::FLDD), DestReg) + .addFrameIndex(FI).addImm(0).addImm((int64_t)ARMCC::AL).addReg(0); + } else { + assert(RC == ARM::SPRRegisterClass && "Unknown regclass!"); + BuildMI(MBB, I, TII.get(ARM::FLDS), DestReg) + .addFrameIndex(FI).addImm(0).addImm((int64_t)ARMCC::AL).addReg(0); + } +} + +void ARMRegisterInfo::copyRegToReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + unsigned DestReg, unsigned SrcReg, + const TargetRegisterClass *RC) const { + if (RC == ARM::GPRRegisterClass) { + MachineFunction &MF = *MBB.getParent(); + ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>(); + if (AFI->isThumbFunction()) + BuildMI(MBB, I, TII.get(ARM::tMOVr), DestReg).addReg(SrcReg); + else + BuildMI(MBB, I, TII.get(ARM::MOVr), DestReg).addReg(SrcReg) + .addImm((int64_t)ARMCC::AL).addReg(0).addReg(0); + } else if (RC == ARM::SPRRegisterClass) + BuildMI(MBB, I, TII.get(ARM::FCPYS), DestReg).addReg(SrcReg) + .addImm((int64_t)ARMCC::AL).addReg(0); + else if (RC == ARM::DPRRegisterClass) + BuildMI(MBB, I, TII.get(ARM::FCPYD), DestReg).addReg(SrcReg) + .addImm((int64_t)ARMCC::AL).addReg(0); + else + abort(); +} + +/// emitLoadConstPool - Emits a load from constpool to materialize the +/// specified immediate. +static void emitLoadConstPool(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI, + unsigned DestReg, int Val, + ARMCC::CondCodes Pred, unsigned PredReg, + const TargetInstrInfo &TII, bool isThumb) { + MachineFunction &MF = *MBB.getParent(); + MachineConstantPool *ConstantPool = MF.getConstantPool(); + Constant *C = ConstantInt::get(Type::Int32Ty, Val); + unsigned Idx = ConstantPool->getConstantPoolIndex(C, 2); + if (isThumb) + BuildMI(MBB, MBBI, TII.get(ARM::tLDRcp), DestReg).addConstantPoolIndex(Idx); + else + BuildMI(MBB, MBBI, TII.get(ARM::LDRcp), DestReg).addConstantPoolIndex(Idx) + .addReg(0).addImm(0).addImm((unsigned)Pred).addReg(PredReg); +} + +void ARMRegisterInfo::reMaterialize(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + unsigned DestReg, + const MachineInstr *Orig) const { + if (Orig->getOpcode() == ARM::MOVi2pieces) { + emitLoadConstPool(MBB, I, DestReg, + Orig->getOperand(1).getImmedValue(), + (ARMCC::CondCodes)Orig->getOperand(2).getImmedValue(), + Orig->getOperand(3).getReg(), + TII, false); + return; + } + + MachineInstr *MI = Orig->clone(); + MI->getOperand(0).setReg(DestReg); + MBB.insert(I, MI); +} + +/// isLowRegister - Returns true if the register is low register r0-r7. +/// +static bool isLowRegister(unsigned Reg) { + using namespace ARM; + switch (Reg) { + case R0: case R1: case R2: case R3: + case R4: case R5: case R6: case R7: + return true; + default: + return false; + } +} + +MachineInstr *ARMRegisterInfo::foldMemoryOperand(MachineInstr *MI, + unsigned OpNum, int FI) const { + unsigned Opc = MI->getOpcode(); + MachineInstr *NewMI = NULL; + switch (Opc) { + default: break; + case ARM::MOVr: { + if (MI->getOperand(4).getReg() == ARM::CPSR) + // If it is updating CPSR, then it cannot be foled. + break; + unsigned Pred = MI->getOperand(2).getImmedValue(); + unsigned PredReg = MI->getOperand(3).getReg(); + if (OpNum == 0) { // move -> store + unsigned SrcReg = MI->getOperand(1).getReg(); + NewMI = BuildMI(TII.get(ARM::STR)).addReg(SrcReg).addFrameIndex(FI) + .addReg(0).addImm(0).addImm(Pred).addReg(PredReg); + } else { // move -> load + unsigned DstReg = MI->getOperand(0).getReg(); + NewMI = BuildMI(TII.get(ARM::LDR), DstReg).addFrameIndex(FI).addReg(0) + .addImm(0).addImm(Pred).addReg(PredReg); + } + break; + } + case ARM::tMOVr: { + if (OpNum == 0) { // move -> store + unsigned SrcReg = MI->getOperand(1).getReg(); + if (isPhysicalRegister(SrcReg) && !isLowRegister(SrcReg)) + // tSpill cannot take a high register operand. + break; + NewMI = BuildMI(TII.get(ARM::tSpill)).addReg(SrcReg).addFrameIndex(FI) + .addImm(0); + } else { // move -> load + unsigned DstReg = MI->getOperand(0).getReg(); + if (isPhysicalRegister(DstReg) && !isLowRegister(DstReg)) + // tRestore cannot target a high register operand. + break; + NewMI = BuildMI(TII.get(ARM::tRestore), DstReg).addFrameIndex(FI) + .addImm(0); + } + break; + } + case ARM::FCPYS: { + unsigned Pred = MI->getOperand(2).getImmedValue(); + unsigned PredReg = MI->getOperand(3).getReg(); + if (OpNum == 0) { // move -> store + unsigned SrcReg = MI->getOperand(1).getReg(); + NewMI = BuildMI(TII.get(ARM::FSTS)).addReg(SrcReg).addFrameIndex(FI) + .addImm(0).addImm(Pred).addReg(PredReg); + } else { // move -> load + unsigned DstReg = MI->getOperand(0).getReg(); + NewMI = BuildMI(TII.get(ARM::FLDS), DstReg).addFrameIndex(FI) + .addImm(0).addImm(Pred).addReg(PredReg); + } + break; + } + case ARM::FCPYD: { + unsigned Pred = MI->getOperand(2).getImmedValue(); + unsigned PredReg = MI->getOperand(3).getReg(); + if (OpNum == 0) { // move -> store + unsigned SrcReg = MI->getOperand(1).getReg(); + NewMI = BuildMI(TII.get(ARM::FSTD)).addReg(SrcReg).addFrameIndex(FI) + .addImm(0).addImm(Pred).addReg(PredReg); + } else { // move -> load + unsigned DstReg = MI->getOperand(0).getReg(); + NewMI = BuildMI(TII.get(ARM::FLDD), DstReg).addFrameIndex(FI) + .addImm(0).addImm(Pred).addReg(PredReg); + } + break; + } + } + + if (NewMI) + NewMI->copyKillDeadInfo(MI); + return NewMI; +} + +const unsigned* ARMRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) + const { + static const unsigned CalleeSavedRegs[] = { + ARM::LR, ARM::R11, ARM::R10, ARM::R9, ARM::R8, + ARM::R7, ARM::R6, ARM::R5, ARM::R4, + + ARM::D15, ARM::D14, ARM::D13, ARM::D12, + ARM::D11, ARM::D10, ARM::D9, ARM::D8, + 0 + }; + + static const unsigned DarwinCalleeSavedRegs[] = { + ARM::LR, ARM::R7, ARM::R6, ARM::R5, ARM::R4, + ARM::R11, ARM::R10, ARM::R9, ARM::R8, + + ARM::D15, ARM::D14, ARM::D13, ARM::D12, + ARM::D11, ARM::D10, ARM::D9, ARM::D8, + 0 + }; + return STI.isTargetDarwin() ? DarwinCalleeSavedRegs : CalleeSavedRegs; +} + +const TargetRegisterClass* const * +ARMRegisterInfo::getCalleeSavedRegClasses(const MachineFunction *MF) const { + static const TargetRegisterClass * const CalleeSavedRegClasses[] = { + &ARM::GPRRegClass, &ARM::GPRRegClass, &ARM::GPRRegClass, + &ARM::GPRRegClass, &ARM::GPRRegClass, &ARM::GPRRegClass, + &ARM::GPRRegClass, &ARM::GPRRegClass, &ARM::GPRRegClass, + + &ARM::DPRRegClass, &ARM::DPRRegClass, &ARM::DPRRegClass, &ARM::DPRRegClass, + &ARM::DPRRegClass, &ARM::DPRRegClass, &ARM::DPRRegClass, &ARM::DPRRegClass, + 0 + }; + return CalleeSavedRegClasses; +} + +BitVector ARMRegisterInfo::getReservedRegs(const MachineFunction &MF) const { + // FIXME: avoid re-calculating this everytime. + BitVector Reserved(getNumRegs()); + Reserved.set(ARM::SP); + Reserved.set(ARM::PC); + if (STI.isTargetDarwin() || hasFP(MF)) + Reserved.set(FramePtr); + // Some targets reserve R9. + if (STI.isR9Reserved()) + Reserved.set(ARM::R9); + return Reserved; +} + +bool +ARMRegisterInfo::isReservedReg(const MachineFunction &MF, unsigned Reg) const { + switch (Reg) { + default: break; + case ARM::SP: + case ARM::PC: + return true; + case ARM::R7: + case ARM::R11: + if (FramePtr == Reg && (STI.isTargetDarwin() || hasFP(MF))) + return true; + break; + case ARM::R9: + return STI.isR9Reserved(); + } + + return false; +} + +bool +ARMRegisterInfo::requiresRegisterScavenging(const MachineFunction &MF) const { + const ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>(); + return ThumbRegScavenging || !AFI->isThumbFunction(); +} + +/// hasFP - Return true if the specified function should have a dedicated frame +/// pointer register. This is true if the function has variable sized allocas +/// or if frame pointer elimination is disabled. +/// +bool ARMRegisterInfo::hasFP(const MachineFunction &MF) const { + return NoFramePointerElim || MF.getFrameInfo()->hasVarSizedObjects(); +} + +// hasReservedCallFrame - Under normal circumstances, when a frame pointer is +// not required, we reserve argument space for call sites in the function +// immediately on entry to the current function. This eliminates the need for +// add/sub sp brackets around call sites. Returns true if the call frame is +// included as part of the stack frame. +bool ARMRegisterInfo::hasReservedCallFrame(MachineFunction &MF) const { + const MachineFrameInfo *FFI = MF.getFrameInfo(); + unsigned CFSize = FFI->getMaxCallFrameSize(); + ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>(); + // It's not always a good idea to include the call frame as part of the + // stack frame. ARM (especially Thumb) has small immediate offset to + // address the stack frame. So a large call frame can cause poor codegen + // and may even makes it impossible to scavenge a register. + if (AFI->isThumbFunction()) { + if (CFSize >= ((1 << 8) - 1) * 4 / 2) // Half of imm8 * 4 + return false; + } else { + if (CFSize >= ((1 << 12) - 1) / 2) // Half of imm12 + return false; + } + return !hasFP(MF); +} + +/// emitARMRegPlusImmediate - Emits a series of instructions to materialize +/// a destreg = basereg + immediate in ARM code. +static +void emitARMRegPlusImmediate(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI, + unsigned DestReg, unsigned BaseReg, int NumBytes, + ARMCC::CondCodes Pred, unsigned PredReg, + const TargetInstrInfo &TII) { + bool isSub = NumBytes < 0; + if (isSub) NumBytes = -NumBytes; + + while (NumBytes) { + unsigned RotAmt = ARM_AM::getSOImmValRotate(NumBytes); + unsigned ThisVal = NumBytes & ARM_AM::rotr32(0xFF, RotAmt); + assert(ThisVal && "Didn't extract field correctly"); + + // We will handle these bits from offset, clear them. + NumBytes &= ~ThisVal; + + // Get the properly encoded SOImmVal field. + int SOImmVal = ARM_AM::getSOImmVal(ThisVal); + assert(SOImmVal != -1 && "Bit extraction didn't work?"); + + // Build the new ADD / SUB. + BuildMI(MBB, MBBI, TII.get(isSub ? ARM::SUBri : ARM::ADDri), DestReg) + .addReg(BaseReg, false, false, true).addImm(SOImmVal) + .addImm((unsigned)Pred).addReg(PredReg).addReg(0); + BaseReg = DestReg; + } +} + +/// calcNumMI - Returns the number of instructions required to materialize +/// the specific add / sub r, c instruction. +static unsigned calcNumMI(int Opc, int ExtraOpc, unsigned Bytes, + unsigned NumBits, unsigned Scale) { + unsigned NumMIs = 0; + unsigned Chunk = ((1 << NumBits) - 1) * Scale; + + if (Opc == ARM::tADDrSPi) { + unsigned ThisVal = (Bytes > Chunk) ? Chunk : Bytes; + Bytes -= ThisVal; + NumMIs++; + NumBits = 8; + Scale = 1; // Followed by a number of tADDi8. + Chunk = ((1 << NumBits) - 1) * Scale; + } + + NumMIs += Bytes / Chunk; + if ((Bytes % Chunk) != 0) + NumMIs++; + if (ExtraOpc) + NumMIs++; + return NumMIs; +} + +/// emitThumbRegPlusImmInReg - Emits a series of instructions to materialize +/// a destreg = basereg + immediate in Thumb code. Materialize the immediate +/// in a register using mov / mvn sequences or load the immediate from a +/// constpool entry. +static +void emitThumbRegPlusImmInReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI, + unsigned DestReg, unsigned BaseReg, + int NumBytes, bool CanChangeCC, + const TargetInstrInfo &TII) { + bool isHigh = !isLowRegister(DestReg) || + (BaseReg != 0 && !isLowRegister(BaseReg)); + bool isSub = false; + // Subtract doesn't have high register version. Load the negative value + // if either base or dest register is a high register. Also, if do not + // issue sub as part of the sequence if condition register is to be + // preserved. + if (NumBytes < 0 && !isHigh && CanChangeCC) { + isSub = true; + NumBytes = -NumBytes; + } + unsigned LdReg = DestReg; + if (DestReg == ARM::SP) { + assert(BaseReg == ARM::SP && "Unexpected!"); + LdReg = ARM::R3; + BuildMI(MBB, MBBI, TII.get(ARM::tMOVr), ARM::R12) + .addReg(ARM::R3, false, false, true); + } + + if (NumBytes <= 255 && NumBytes >= 0) + BuildMI(MBB, MBBI, TII.get(ARM::tMOVi8), LdReg).addImm(NumBytes); + else if (NumBytes < 0 && NumBytes >= -255) { + BuildMI(MBB, MBBI, TII.get(ARM::tMOVi8), LdReg).addImm(NumBytes); + BuildMI(MBB, MBBI, TII.get(ARM::tNEG), LdReg) + .addReg(LdReg, false, false, true); + } else + emitLoadConstPool(MBB, MBBI, LdReg, NumBytes, ARMCC::AL, 0, TII, true); + + // Emit add / sub. + int Opc = (isSub) ? ARM::tSUBrr : (isHigh ? ARM::tADDhirr : ARM::tADDrr); + const MachineInstrBuilder MIB = BuildMI(MBB, MBBI, TII.get(Opc), DestReg); + if (DestReg == ARM::SP || isSub) + MIB.addReg(BaseReg).addReg(LdReg, false, false, true); + else + MIB.addReg(LdReg).addReg(BaseReg, false, false, true); + if (DestReg == ARM::SP) + BuildMI(MBB, MBBI, TII.get(ARM::tMOVr), ARM::R3) + .addReg(ARM::R12, false, false, true); +} + +/// emitThumbRegPlusImmediate - Emits a series of instructions to materialize +/// a destreg = basereg + immediate in Thumb code. +static +void emitThumbRegPlusImmediate(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI, + unsigned DestReg, unsigned BaseReg, + int NumBytes, const TargetInstrInfo &TII) { + bool isSub = NumBytes < 0; + unsigned Bytes = (unsigned)NumBytes; + if (isSub) Bytes = -NumBytes; + bool isMul4 = (Bytes & 3) == 0; + bool isTwoAddr = false; + bool DstNotEqBase = false; + unsigned NumBits = 1; + unsigned Scale = 1; + int Opc = 0; + int ExtraOpc = 0; + + if (DestReg == BaseReg && BaseReg == ARM::SP) { + assert(isMul4 && "Thumb sp inc / dec size must be multiple of 4!"); + NumBits = 7; + Scale = 4; + Opc = isSub ? ARM::tSUBspi : ARM::tADDspi; + isTwoAddr = true; + } else if (!isSub && BaseReg == ARM::SP) { + // r1 = add sp, 403 + // => + // r1 = add sp, 100 * 4 + // r1 = add r1, 3 + if (!isMul4) { + Bytes &= ~3; + ExtraOpc = ARM::tADDi3; + } + NumBits = 8; + Scale = 4; + Opc = ARM::tADDrSPi; + } else { + // sp = sub sp, c + // r1 = sub sp, c + // r8 = sub sp, c + if (DestReg != BaseReg) + DstNotEqBase = true; + NumBits = 8; + Opc = isSub ? ARM::tSUBi8 : ARM::tADDi8; + isTwoAddr = true; + } + + unsigned NumMIs = calcNumMI(Opc, ExtraOpc, Bytes, NumBits, Scale); + unsigned Threshold = (DestReg == ARM::SP) ? 3 : 2; + if (NumMIs > Threshold) { + // This will expand into too many instructions. Load the immediate from a + // constpool entry. + emitThumbRegPlusImmInReg(MBB, MBBI, DestReg, BaseReg, NumBytes, true, TII); + return; + } + + if (DstNotEqBase) { + if (isLowRegister(DestReg) && isLowRegister(BaseReg)) { + // If both are low registers, emit DestReg = add BaseReg, max(Imm, 7) + unsigned Chunk = (1 << 3) - 1; + unsigned ThisVal = (Bytes > Chunk) ? Chunk : Bytes; + Bytes -= ThisVal; + BuildMI(MBB, MBBI, TII.get(isSub ? ARM::tSUBi3 : ARM::tADDi3), DestReg) + .addReg(BaseReg, false, false, true).addImm(ThisVal); + } else { + BuildMI(MBB, MBBI, TII.get(ARM::tMOVr), DestReg) + .addReg(BaseReg, false, false, true); + } + BaseReg = DestReg; + } + + unsigned Chunk = ((1 << NumBits) - 1) * Scale; + while (Bytes) { + unsigned ThisVal = (Bytes > Chunk) ? Chunk : Bytes; + Bytes -= ThisVal; + ThisVal /= Scale; + // Build the new tADD / tSUB. + if (isTwoAddr) + BuildMI(MBB, MBBI, TII.get(Opc), DestReg).addReg(DestReg).addImm(ThisVal); + else { + bool isKill = BaseReg != ARM::SP; + BuildMI(MBB, MBBI, TII.get(Opc), DestReg) + .addReg(BaseReg, false, false, isKill).addImm(ThisVal); + BaseReg = DestReg; + + if (Opc == ARM::tADDrSPi) { + // r4 = add sp, imm + // r4 = add r4, imm + // ... + NumBits = 8; + Scale = 1; + Chunk = ((1 << NumBits) - 1) * Scale; + Opc = isSub ? ARM::tSUBi8 : ARM::tADDi8; + isTwoAddr = true; + } + } + } + + if (ExtraOpc) + BuildMI(MBB, MBBI, TII.get(ExtraOpc), DestReg) + .addReg(DestReg, false, false, true) + .addImm(((unsigned)NumBytes) & 3); +} + +static +void emitSPUpdate(MachineBasicBlock &MBB, MachineBasicBlock::iterator &MBBI, + int NumBytes, ARMCC::CondCodes Pred, unsigned PredReg, + bool isThumb, const TargetInstrInfo &TII) { + if (isThumb) + emitThumbRegPlusImmediate(MBB, MBBI, ARM::SP, ARM::SP, NumBytes, TII); + else + emitARMRegPlusImmediate(MBB, MBBI, ARM::SP, ARM::SP, NumBytes, + Pred, PredReg, TII); +} + +void ARMRegisterInfo:: +eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + if (!hasReservedCallFrame(MF)) { + // If we have alloca, convert as follows: + // ADJCALLSTACKDOWN -> sub, sp, sp, amount + // ADJCALLSTACKUP -> add, sp, sp, amount + MachineInstr *Old = I; + unsigned Amount = Old->getOperand(0).getImmedValue(); + if (Amount != 0) { + ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>(); + // We need to keep the stack aligned properly. To do this, we round the + // amount of space needed for the outgoing arguments up to the next + // alignment boundary. + unsigned Align = MF.getTarget().getFrameInfo()->getStackAlignment(); + Amount = (Amount+Align-1)/Align*Align; + + // Replace the pseudo instruction with a new instruction... + unsigned Opc = Old->getOpcode(); + bool isThumb = AFI->isThumbFunction(); + ARMCC::CondCodes Pred = isThumb + ? ARMCC::AL : (ARMCC::CondCodes)Old->getOperand(1).getImmedValue(); + unsigned PredReg = isThumb ? 0 : Old->getOperand(2).getReg(); + if (Opc == ARM::ADJCALLSTACKDOWN || Opc == ARM::tADJCALLSTACKDOWN) { + emitSPUpdate(MBB, I, -Amount, Pred, PredReg, isThumb, TII); + } else { + assert(Opc == ARM::ADJCALLSTACKUP || Opc == ARM::tADJCALLSTACKUP); + emitSPUpdate(MBB, I, Amount, Pred, PredReg, isThumb, TII); + } + } + } + MBB.erase(I); +} + +/// emitThumbConstant - Emit a series of instructions to materialize a +/// constant. +static void emitThumbConstant(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI, + unsigned DestReg, int Imm, + const TargetInstrInfo &TII) { + bool isSub = Imm < 0; + if (isSub) Imm = -Imm; + + int Chunk = (1 << 8) - 1; + int ThisVal = (Imm > Chunk) ? Chunk : Imm; + Imm -= ThisVal; + BuildMI(MBB, MBBI, TII.get(ARM::tMOVi8), DestReg).addImm(ThisVal); + if (Imm > 0) + emitThumbRegPlusImmediate(MBB, MBBI, DestReg, DestReg, Imm, TII); + if (isSub) + BuildMI(MBB, MBBI, TII.get(ARM::tNEG), DestReg) + .addReg(DestReg, false, false, true); +} + +/// findScratchRegister - Find a 'free' ARM register. If register scavenger +/// is not being used, R12 is available. Otherwise, try for a call-clobbered +/// register first and then a spilled callee-saved register if that fails. +static +unsigned findScratchRegister(RegScavenger *RS, const TargetRegisterClass *RC, + ARMFunctionInfo *AFI) { + unsigned Reg = RS ? RS->FindUnusedReg(RC, true) : (unsigned) ARM::R12; + if (Reg == 0) + // Try a already spilled CS register. + Reg = RS->FindUnusedReg(RC, AFI->getSpilledCSRegisters()); + + return Reg; +} + +void ARMRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, + int SPAdj, RegScavenger *RS) const{ + unsigned i = 0; + MachineInstr &MI = *II; + MachineBasicBlock &MBB = *MI.getParent(); + MachineFunction &MF = *MBB.getParent(); + ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>(); + bool isThumb = AFI->isThumbFunction(); + + while (!MI.getOperand(i).isFrameIndex()) { + ++i; + assert(i < MI.getNumOperands() && "Instr doesn't have FrameIndex operand!"); + } + + unsigned FrameReg = ARM::SP; + int FrameIndex = MI.getOperand(i).getFrameIndex(); + int Offset = MF.getFrameInfo()->getObjectOffset(FrameIndex) + + MF.getFrameInfo()->getStackSize() + SPAdj; + + if (AFI->isGPRCalleeSavedArea1Frame(FrameIndex)) + Offset -= AFI->getGPRCalleeSavedArea1Offset(); + else if (AFI->isGPRCalleeSavedArea2Frame(FrameIndex)) + Offset -= AFI->getGPRCalleeSavedArea2Offset(); + else if (AFI->isDPRCalleeSavedAreaFrame(FrameIndex)) + Offset -= AFI->getDPRCalleeSavedAreaOffset(); + else if (hasFP(MF)) { + assert(SPAdj == 0 && "Unexpected"); + // There is alloca()'s in this function, must reference off the frame + // pointer instead. + FrameReg = getFrameRegister(MF); + Offset -= AFI->getFramePtrSpillOffset(); + } + + unsigned Opcode = MI.getOpcode(); + const TargetInstrDescriptor &Desc = TII.get(Opcode); + unsigned AddrMode = (Desc.TSFlags & ARMII::AddrModeMask); + bool isSub = false; + + if (Opcode == ARM::ADDri) { + Offset += MI.getOperand(i+1).getImm(); + if (Offset == 0) { + // Turn it into a move. + MI.setInstrDescriptor(TII.get(ARM::MOVr)); + MI.getOperand(i).ChangeToRegister(FrameReg, false); + MI.RemoveOperand(i+1); + return; + } else if (Offset < 0) { + Offset = -Offset; + isSub = true; + MI.setInstrDescriptor(TII.get(ARM::SUBri)); + } + + // Common case: small offset, fits into instruction. + int ImmedOffset = ARM_AM::getSOImmVal(Offset); + if (ImmedOffset != -1) { + // Replace the FrameIndex with sp / fp + MI.getOperand(i).ChangeToRegister(FrameReg, false); + MI.getOperand(i+1).ChangeToImmediate(ImmedOffset); + return; + } + + // Otherwise, we fallback to common code below to form the imm offset with + // a sequence of ADDri instructions. First though, pull as much of the imm + // into this ADDri as possible. + unsigned RotAmt = ARM_AM::getSOImmValRotate(Offset); + unsigned ThisImmVal = Offset & ARM_AM::rotr32(0xFF, RotAmt); + + // We will handle these bits from offset, clear them. + Offset &= ~ThisImmVal; + + // Get the properly encoded SOImmVal field. + int ThisSOImmVal = ARM_AM::getSOImmVal(ThisImmVal); + assert(ThisSOImmVal != -1 && "Bit extraction didn't work?"); + MI.getOperand(i+1).ChangeToImmediate(ThisSOImmVal); + } else if (Opcode == ARM::tADDrSPi) { + Offset += MI.getOperand(i+1).getImm(); + + // Can't use tADDrSPi if it's based off the frame pointer. + unsigned NumBits = 0; + unsigned Scale = 1; + if (FrameReg != ARM::SP) { + Opcode = ARM::tADDi3; + MI.setInstrDescriptor(TII.get(ARM::tADDi3)); + NumBits = 3; + } else { + NumBits = 8; + Scale = 4; + assert((Offset & 3) == 0 && + "Thumb add/sub sp, #imm immediate must be multiple of 4!"); + } + + if (Offset == 0) { + // Turn it into a move. + MI.setInstrDescriptor(TII.get(ARM::tMOVr)); + MI.getOperand(i).ChangeToRegister(FrameReg, false); + MI.RemoveOperand(i+1); + return; + } + + // Common case: small offset, fits into instruction. + unsigned Mask = (1 << NumBits) - 1; + if (((Offset / Scale) & ~Mask) == 0) { + // Replace the FrameIndex with sp / fp + MI.getOperand(i).ChangeToRegister(FrameReg, false); + MI.getOperand(i+1).ChangeToImmediate(Offset / Scale); + return; + } + + unsigned DestReg = MI.getOperand(0).getReg(); + unsigned Bytes = (Offset > 0) ? Offset : -Offset; + unsigned NumMIs = calcNumMI(Opcode, 0, Bytes, NumBits, Scale); + // MI would expand into a large number of instructions. Don't try to + // simplify the immediate. + if (NumMIs > 2) { + emitThumbRegPlusImmediate(MBB, II, DestReg, FrameReg, Offset, TII); + MBB.erase(II); + return; + } + + if (Offset > 0) { + // Translate r0 = add sp, imm to + // r0 = add sp, 255*4 + // r0 = add r0, (imm - 255*4) + MI.getOperand(i).ChangeToRegister(FrameReg, false); + MI.getOperand(i+1).ChangeToImmediate(Mask); + Offset = (Offset - Mask * Scale); + MachineBasicBlock::iterator NII = next(II); + emitThumbRegPlusImmediate(MBB, NII, DestReg, DestReg, Offset, TII); + } else { + // Translate r0 = add sp, -imm to + // r0 = -imm (this is then translated into a series of instructons) + // r0 = add r0, sp + emitThumbConstant(MBB, II, DestReg, Offset, TII); + MI.setInstrDescriptor(TII.get(ARM::tADDhirr)); + MI.getOperand(i).ChangeToRegister(DestReg, false, false, true); + MI.getOperand(i+1).ChangeToRegister(FrameReg, false); + } + return; + } else { + unsigned ImmIdx = 0; + int InstrOffs = 0; + unsigned NumBits = 0; + unsigned Scale = 1; + switch (AddrMode) { + case ARMII::AddrMode2: { + ImmIdx = i+2; + InstrOffs = ARM_AM::getAM2Offset(MI.getOperand(ImmIdx).getImm()); + if (ARM_AM::getAM2Op(MI.getOperand(ImmIdx).getImm()) == ARM_AM::sub) + InstrOffs *= -1; + NumBits = 12; + break; + } + case ARMII::AddrMode3: { + ImmIdx = i+2; + InstrOffs = ARM_AM::getAM3Offset(MI.getOperand(ImmIdx).getImm()); + if (ARM_AM::getAM3Op(MI.getOperand(ImmIdx).getImm()) == ARM_AM::sub) + InstrOffs *= -1; + NumBits = 8; + break; + } + case ARMII::AddrMode5: { + ImmIdx = i+1; + InstrOffs = ARM_AM::getAM5Offset(MI.getOperand(ImmIdx).getImm()); + if (ARM_AM::getAM5Op(MI.getOperand(ImmIdx).getImm()) == ARM_AM::sub) + InstrOffs *= -1; + NumBits = 8; + Scale = 4; + break; + } + case ARMII::AddrModeTs: { + ImmIdx = i+1; + InstrOffs = MI.getOperand(ImmIdx).getImm(); + NumBits = (FrameReg == ARM::SP) ? 8 : 5; + Scale = 4; + break; + } + default: + assert(0 && "Unsupported addressing mode!"); + abort(); + break; + } + + Offset += InstrOffs * Scale; + assert((Offset & (Scale-1)) == 0 && "Can't encode this offset!"); + if (Offset < 0 && !isThumb) { + Offset = -Offset; + isSub = true; + } + + // Common case: small offset, fits into instruction. + MachineOperand &ImmOp = MI.getOperand(ImmIdx); + int ImmedOffset = Offset / Scale; + unsigned Mask = (1 << NumBits) - 1; + if ((unsigned)Offset <= Mask * Scale) { + // Replace the FrameIndex with sp + MI.getOperand(i).ChangeToRegister(FrameReg, false); + if (isSub) + ImmedOffset |= 1 << NumBits; + ImmOp.ChangeToImmediate(ImmedOffset); + return; + } + + bool isThumSpillRestore = Opcode == ARM::tRestore || Opcode == ARM::tSpill; + if (AddrMode == ARMII::AddrModeTs) { + // Thumb tLDRspi, tSTRspi. These will change to instructions that use + // a different base register. + NumBits = 5; + Mask = (1 << NumBits) - 1; + } + // If this is a thumb spill / restore, we will be using a constpool load to + // materialize the offset. + if (AddrMode == ARMII::AddrModeTs && isThumSpillRestore) + ImmOp.ChangeToImmediate(0); + else { + // Otherwise, it didn't fit. Pull in what we can to simplify the immed. + ImmedOffset = ImmedOffset & Mask; + if (isSub) + ImmedOffset |= 1 << NumBits; + ImmOp.ChangeToImmediate(ImmedOffset); + Offset &= ~(Mask*Scale); + } + } + + // If we get here, the immediate doesn't fit into the instruction. We folded + // as much as possible above, handle the rest, providing a register that is + // SP+LargeImm. + assert(Offset && "This code isn't needed if offset already handled!"); + + if (isThumb) { + if (TII.isLoad(Opcode)) { + // Use the destination register to materialize sp + offset. + unsigned TmpReg = MI.getOperand(0).getReg(); + bool UseRR = false; + if (Opcode == ARM::tRestore) { + if (FrameReg == ARM::SP) + emitThumbRegPlusImmInReg(MBB, II, TmpReg, FrameReg,Offset,false,TII); + else { + emitLoadConstPool(MBB, II, TmpReg, Offset, ARMCC::AL, 0, TII, true); + UseRR = true; + } + } else + emitThumbRegPlusImmediate(MBB, II, TmpReg, FrameReg, Offset, TII); + MI.setInstrDescriptor(TII.get(ARM::tLDR)); + MI.getOperand(i).ChangeToRegister(TmpReg, false, false, true); + if (UseRR) + MI.addRegOperand(FrameReg, false); // Use [reg, reg] addrmode. + else + MI.addRegOperand(0, false); // tLDR has an extra register operand. + } else if (TII.isStore(Opcode)) { + // FIXME! This is horrific!!! We need register scavenging. + // Our temporary workaround has marked r3 unavailable. Of course, r3 is + // also a ABI register so it's possible that is is the register that is + // being storing here. If that's the case, we do the following: + // r12 = r2 + // Use r2 to materialize sp + offset + // str r3, r2 + // r2 = r12 + unsigned ValReg = MI.getOperand(0).getReg(); + unsigned TmpReg = ARM::R3; + bool UseRR = false; + if (ValReg == ARM::R3) { + BuildMI(MBB, II, TII.get(ARM::tMOVr), ARM::R12) + .addReg(ARM::R2, false, false, true); + TmpReg = ARM::R2; + } + if (TmpReg == ARM::R3 && AFI->isR3LiveIn()) + BuildMI(MBB, II, TII.get(ARM::tMOVr), ARM::R12) + .addReg(ARM::R3, false, false, true); + if (Opcode == ARM::tSpill) { + if (FrameReg == ARM::SP) + emitThumbRegPlusImmInReg(MBB, II, TmpReg, FrameReg,Offset,false,TII); + else { + emitLoadConstPool(MBB, II, TmpReg, Offset, ARMCC::AL, 0, TII, true); + UseRR = true; + } + } else + emitThumbRegPlusImmediate(MBB, II, TmpReg, FrameReg, Offset, TII); + MI.setInstrDescriptor(TII.get(ARM::tSTR)); + MI.getOperand(i).ChangeToRegister(TmpReg, false, false, true); + if (UseRR) + MI.addRegOperand(FrameReg, false); // Use [reg, reg] addrmode. + else + MI.addRegOperand(0, false); // tSTR has an extra register operand. + + MachineBasicBlock::iterator NII = next(II); + if (ValReg == ARM::R3) + BuildMI(MBB, NII, TII.get(ARM::tMOVr), ARM::R2) + .addReg(ARM::R12, false, false, true); + if (TmpReg == ARM::R3 && AFI->isR3LiveIn()) + BuildMI(MBB, NII, TII.get(ARM::tMOVr), ARM::R3) + .addReg(ARM::R12, false, false, true); + } else + assert(false && "Unexpected opcode!"); + } else { + // Insert a set of r12 with the full address: r12 = sp + offset + // If the offset we have is too large to fit into the instruction, we need + // to form it with a series of ADDri's. Do this by taking 8-bit chunks + // out of 'Offset'. + unsigned ScratchReg = findScratchRegister(RS, &ARM::GPRRegClass, AFI); + if (ScratchReg == 0) + // No register is "free". Scavenge a register. + ScratchReg = RS->scavengeRegister(&ARM::GPRRegClass, II, SPAdj); + int PIdx = MI.findFirstPredOperandIdx(); + ARMCC::CondCodes Pred = (PIdx == -1) + ? ARMCC::AL : (ARMCC::CondCodes)MI.getOperand(PIdx).getImmedValue(); + unsigned PredReg = (PIdx == -1) ? 0 : MI.getOperand(PIdx+1).getReg(); + emitARMRegPlusImmediate(MBB, II, ScratchReg, FrameReg, + isSub ? -Offset : Offset, Pred, PredReg, TII); + MI.getOperand(i).ChangeToRegister(ScratchReg, false, false, true); + } +} + +static unsigned estimateStackSize(MachineFunction &MF, MachineFrameInfo *MFI) { + const MachineFrameInfo *FFI = MF.getFrameInfo(); + int Offset = 0; + for (int i = FFI->getObjectIndexBegin(); i != 0; ++i) { + int FixedOff = -FFI->getObjectOffset(i); + if (FixedOff > Offset) Offset = FixedOff; + } + for (unsigned i = 0, e = FFI->getObjectIndexEnd(); i != e; ++i) { + Offset += FFI->getObjectSize(i); + unsigned Align = FFI->getObjectAlignment(i); + // Adjust to alignment boundary + Offset = (Offset+Align-1)/Align*Align; + } + return (unsigned)Offset; +} + +void +ARMRegisterInfo::processFunctionBeforeCalleeSavedScan(MachineFunction &MF, + RegScavenger *RS) const { + // This tells PEI to spill the FP as if it is any other callee-save register + // to take advantage the eliminateFrameIndex machinery. This also ensures it + // is spilled in the order specified by getCalleeSavedRegs() to make it easier + // to combine multiple loads / stores. + bool CanEliminateFrame = true; + bool CS1Spilled = false; + bool LRSpilled = false; + unsigned NumGPRSpills = 0; + SmallVector<unsigned, 4> UnspilledCS1GPRs; + SmallVector<unsigned, 4> UnspilledCS2GPRs; + ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>(); + + // Don't spill FP if the frame can be eliminated. This is determined + // by scanning the callee-save registers to see if any is used. + const unsigned *CSRegs = getCalleeSavedRegs(); + const TargetRegisterClass* const *CSRegClasses = getCalleeSavedRegClasses(); + for (unsigned i = 0; CSRegs[i]; ++i) { + unsigned Reg = CSRegs[i]; + bool Spilled = false; + if (MF.isPhysRegUsed(Reg)) { + AFI->setCSRegisterIsSpilled(Reg); + Spilled = true; + CanEliminateFrame = false; + } else { + // Check alias registers too. + for (const unsigned *Aliases = getAliasSet(Reg); *Aliases; ++Aliases) { + if (MF.isPhysRegUsed(*Aliases)) { + Spilled = true; + CanEliminateFrame = false; + } + } + } + + if (CSRegClasses[i] == &ARM::GPRRegClass) { + if (Spilled) { + NumGPRSpills++; + + if (!STI.isTargetDarwin()) { + if (Reg == ARM::LR) + LRSpilled = true; + CS1Spilled = true; + continue; + } + + // Keep track if LR and any of R4, R5, R6, and R7 is spilled. + switch (Reg) { + case ARM::LR: + LRSpilled = true; + // Fallthrough + case ARM::R4: + case ARM::R5: + case ARM::R6: + case ARM::R7: + CS1Spilled = true; + break; + default: + break; + } + } else { + if (!STI.isTargetDarwin()) { + UnspilledCS1GPRs.push_back(Reg); + continue; + } + + switch (Reg) { + case ARM::R4: + case ARM::R5: + case ARM::R6: + case ARM::R7: + case ARM::LR: + UnspilledCS1GPRs.push_back(Reg); + break; + default: + UnspilledCS2GPRs.push_back(Reg); + break; + } + } + } + } + + bool ForceLRSpill = false; + if (!LRSpilled && AFI->isThumbFunction()) { + unsigned FnSize = ARM::GetFunctionSize(MF); + // Force LR to be spilled if the Thumb function size is > 2048. This enables + // use of BL to implement far jump. If it turns out that it's not needed + // then the branch fix up path will undo it. + if (FnSize >= (1 << 11)) { + CanEliminateFrame = false; + ForceLRSpill = true; + } + } + + bool ExtraCSSpill = false; + if (!CanEliminateFrame || hasFP(MF)) { + AFI->setHasStackFrame(true); + + // If LR is not spilled, but at least one of R4, R5, R6, and R7 is spilled. + // Spill LR as well so we can fold BX_RET to the registers restore (LDM). + if (!LRSpilled && CS1Spilled) { + MF.setPhysRegUsed(ARM::LR); + AFI->setCSRegisterIsSpilled(ARM::LR); + NumGPRSpills++; + UnspilledCS1GPRs.erase(std::find(UnspilledCS1GPRs.begin(), + UnspilledCS1GPRs.end(), (unsigned)ARM::LR)); + ForceLRSpill = false; + ExtraCSSpill = true; + } + + // Darwin ABI requires FP to point to the stack slot that contains the + // previous FP. + if (STI.isTargetDarwin() || hasFP(MF)) { + MF.setPhysRegUsed(FramePtr); + NumGPRSpills++; + } + + // If stack and double are 8-byte aligned and we are spilling an odd number + // of GPRs. Spill one extra callee save GPR so we won't have to pad between + // the integer and double callee save areas. + unsigned TargetAlign = MF.getTarget().getFrameInfo()->getStackAlignment(); + if (TargetAlign == 8 && (NumGPRSpills & 1)) { + if (CS1Spilled && !UnspilledCS1GPRs.empty()) { + for (unsigned i = 0, e = UnspilledCS1GPRs.size(); i != e; ++i) { + unsigned Reg = UnspilledCS1GPRs[i]; + // Don't spiil high register if the function is thumb + if (!AFI->isThumbFunction() || isLowRegister(Reg) || Reg == ARM::LR) { + MF.setPhysRegUsed(Reg); + AFI->setCSRegisterIsSpilled(Reg); + if (!isReservedReg(MF, Reg)) + ExtraCSSpill = true; + break; + } + } + } else if (!UnspilledCS2GPRs.empty() && + !AFI->isThumbFunction()) { + unsigned Reg = UnspilledCS2GPRs.front(); + MF.setPhysRegUsed(Reg); + AFI->setCSRegisterIsSpilled(Reg); + if (!isReservedReg(MF, Reg)) + ExtraCSSpill = true; + } + } + + // Estimate if we might need to scavenge a register at some point in order + // to materialize a stack offset. If so, either spill one additiona + // callee-saved register or reserve a special spill slot to facilitate + // register scavenging. + if (RS && !ExtraCSSpill && !AFI->isThumbFunction()) { + MachineFrameInfo *MFI = MF.getFrameInfo(); + unsigned Size = estimateStackSize(MF, MFI); + unsigned Limit = (1 << 12) - 1; + for (MachineFunction::iterator BB = MF.begin(),E = MF.end();BB != E; ++BB) + for (MachineBasicBlock::iterator I= BB->begin(); I != BB->end(); ++I) { + for (unsigned i = 0, e = I->getNumOperands(); i != e; ++i) + if (I->getOperand(i).isFrameIndex()) { + unsigned Opcode = I->getOpcode(); + const TargetInstrDescriptor &Desc = TII.get(Opcode); + unsigned AddrMode = (Desc.TSFlags & ARMII::AddrModeMask); + if (AddrMode == ARMII::AddrMode3) { + Limit = (1 << 8) - 1; + goto DoneEstimating; + } else if (AddrMode == ARMII::AddrMode5) { + unsigned ThisLimit = ((1 << 8) - 1) * 4; + if (ThisLimit < Limit) + Limit = ThisLimit; + } + } + } + DoneEstimating: + if (Size >= Limit) { + // If any non-reserved CS register isn't spilled, just spill one or two + // extra. That should take care of it! + unsigned NumExtras = TargetAlign / 4; + SmallVector<unsigned, 2> Extras; + while (NumExtras && !UnspilledCS1GPRs.empty()) { + unsigned Reg = UnspilledCS1GPRs.back(); + UnspilledCS1GPRs.pop_back(); + if (!isReservedReg(MF, Reg)) { + Extras.push_back(Reg); + NumExtras--; + } + } + while (NumExtras && !UnspilledCS2GPRs.empty()) { + unsigned Reg = UnspilledCS2GPRs.back(); + UnspilledCS2GPRs.pop_back(); + if (!isReservedReg(MF, Reg)) { + Extras.push_back(Reg); + NumExtras--; + } + } + if (Extras.size() && NumExtras == 0) { + for (unsigned i = 0, e = Extras.size(); i != e; ++i) { + MF.setPhysRegUsed(Extras[i]); + AFI->setCSRegisterIsSpilled(Extras[i]); + } + } else { + // Reserve a slot closest to SP or frame pointer. + const TargetRegisterClass *RC = &ARM::GPRRegClass; + RS->setScavengingFrameIndex(MFI->CreateStackObject(RC->getSize(), + RC->getAlignment())); + } + } + } + } + + if (ForceLRSpill) { + MF.setPhysRegUsed(ARM::LR); + AFI->setCSRegisterIsSpilled(ARM::LR); + AFI->setLRIsSpilledForFarJump(true); + } +} + +/// Move iterator pass the next bunch of callee save load / store ops for +/// the particular spill area (1: integer area 1, 2: integer area 2, +/// 3: fp area, 0: don't care). +static void movePastCSLoadStoreOps(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI, + int Opc, unsigned Area, + const ARMSubtarget &STI) { + while (MBBI != MBB.end() && + MBBI->getOpcode() == Opc && MBBI->getOperand(1).isFrameIndex()) { + if (Area != 0) { + bool Done = false; + unsigned Category = 0; + switch (MBBI->getOperand(0).getReg()) { + case ARM::R4: case ARM::R5: case ARM::R6: case ARM::R7: + case ARM::LR: + Category = 1; + break; + case ARM::R8: case ARM::R9: case ARM::R10: case ARM::R11: + Category = STI.isTargetDarwin() ? 2 : 1; + break; + case ARM::D8: case ARM::D9: case ARM::D10: case ARM::D11: + case ARM::D12: case ARM::D13: case ARM::D14: case ARM::D15: + Category = 3; + break; + default: + Done = true; + break; + } + if (Done || Category != Area) + break; + } + + ++MBBI; + } +} + +void ARMRegisterInfo::emitPrologue(MachineFunction &MF) const { + MachineBasicBlock &MBB = MF.front(); + MachineBasicBlock::iterator MBBI = MBB.begin(); + MachineFrameInfo *MFI = MF.getFrameInfo(); + ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>(); + bool isThumb = AFI->isThumbFunction(); + unsigned VARegSaveSize = AFI->getVarArgsRegSaveSize(); + unsigned NumBytes = MFI->getStackSize(); + const std::vector<CalleeSavedInfo> &CSI = MFI->getCalleeSavedInfo(); + + if (isThumb) { + // Check if R3 is live in. It might have to be used as a scratch register. + for (MachineFunction::livein_iterator I=MF.livein_begin(),E=MF.livein_end(); + I != E; ++I) { + if ((*I).first == ARM::R3) { + AFI->setR3IsLiveIn(true); + break; + } + } + + // Thumb add/sub sp, imm8 instructions implicitly multiply the offset by 4. + NumBytes = (NumBytes + 3) & ~3; + MFI->setStackSize(NumBytes); + } + + // Determine the sizes of each callee-save spill areas and record which frame + // belongs to which callee-save spill areas. + unsigned GPRCS1Size = 0, GPRCS2Size = 0, DPRCSSize = 0; + int FramePtrSpillFI = 0; + + if (VARegSaveSize) + emitSPUpdate(MBB, MBBI, -VARegSaveSize, ARMCC::AL, 0, isThumb, TII); + + if (!AFI->hasStackFrame()) { + if (NumBytes != 0) + emitSPUpdate(MBB, MBBI, -NumBytes, ARMCC::AL, 0, isThumb, TII); + return; + } + + for (unsigned i = 0, e = CSI.size(); i != e; ++i) { + unsigned Reg = CSI[i].getReg(); + int FI = CSI[i].getFrameIdx(); + switch (Reg) { + case ARM::R4: + case ARM::R5: + case ARM::R6: + case ARM::R7: + case ARM::LR: + if (Reg == FramePtr) + FramePtrSpillFI = FI; + AFI->addGPRCalleeSavedArea1Frame(FI); + GPRCS1Size += 4; + break; + case ARM::R8: + case ARM::R9: + case ARM::R10: + case ARM::R11: + if (Reg == FramePtr) + FramePtrSpillFI = FI; + if (STI.isTargetDarwin()) { + AFI->addGPRCalleeSavedArea2Frame(FI); + GPRCS2Size += 4; + } else { + AFI->addGPRCalleeSavedArea1Frame(FI); + GPRCS1Size += 4; + } + break; + default: + AFI->addDPRCalleeSavedAreaFrame(FI); + DPRCSSize += 8; + } + } + + if (!isThumb) { + // Build the new SUBri to adjust SP for integer callee-save spill area 1. + emitSPUpdate(MBB, MBBI, -GPRCS1Size, ARMCC::AL, 0, isThumb, TII); + movePastCSLoadStoreOps(MBB, MBBI, ARM::STR, 1, STI); + } else if (MBBI != MBB.end() && MBBI->getOpcode() == ARM::tPUSH) + ++MBBI; + + // Darwin ABI requires FP to point to the stack slot that contains the + // previous FP. + if (STI.isTargetDarwin() || hasFP(MF)) { + MachineInstrBuilder MIB = + BuildMI(MBB, MBBI, TII.get(isThumb ? ARM::tADDrSPi : ARM::ADDri),FramePtr) + .addFrameIndex(FramePtrSpillFI).addImm(0); + if (!isThumb) MIB.addImm(ARMCC::AL).addReg(0).addReg(0); + } + + if (!isThumb) { + // Build the new SUBri to adjust SP for integer callee-save spill area 2. + emitSPUpdate(MBB, MBBI, -GPRCS2Size, ARMCC::AL, 0, false, TII); + + // Build the new SUBri to adjust SP for FP callee-save spill area. + movePastCSLoadStoreOps(MBB, MBBI, ARM::STR, 2, STI); + emitSPUpdate(MBB, MBBI, -DPRCSSize, ARMCC::AL, 0, false, TII); + } + + // Determine starting offsets of spill areas. + unsigned DPRCSOffset = NumBytes - (GPRCS1Size + GPRCS2Size + DPRCSSize); + unsigned GPRCS2Offset = DPRCSOffset + DPRCSSize; + unsigned GPRCS1Offset = GPRCS2Offset + GPRCS2Size; + AFI->setFramePtrSpillOffset(MFI->getObjectOffset(FramePtrSpillFI) + NumBytes); + AFI->setGPRCalleeSavedArea1Offset(GPRCS1Offset); + AFI->setGPRCalleeSavedArea2Offset(GPRCS2Offset); + AFI->setDPRCalleeSavedAreaOffset(DPRCSOffset); + + NumBytes = DPRCSOffset; + if (NumBytes) { + // Insert it after all the callee-save spills. + if (!isThumb) + movePastCSLoadStoreOps(MBB, MBBI, ARM::FSTD, 3, STI); + emitSPUpdate(MBB, MBBI, -NumBytes, ARMCC::AL, 0, isThumb, TII); + } + + if(STI.isTargetELF() && hasFP(MF)) { + MFI->setOffsetAdjustment(MFI->getOffsetAdjustment() - + AFI->getFramePtrSpillOffset()); + } + + AFI->setGPRCalleeSavedArea1Size(GPRCS1Size); + AFI->setGPRCalleeSavedArea2Size(GPRCS2Size); + AFI->setDPRCalleeSavedAreaSize(DPRCSSize); +} + +static bool isCalleeSavedRegister(unsigned Reg, const unsigned *CSRegs) { + for (unsigned i = 0; CSRegs[i]; ++i) + if (Reg == CSRegs[i]) + return true; + return false; +} + +static bool isCSRestore(MachineInstr *MI, const unsigned *CSRegs) { + return ((MI->getOpcode() == ARM::FLDD || + MI->getOpcode() == ARM::LDR || + MI->getOpcode() == ARM::tRestore) && + MI->getOperand(1).isFrameIndex() && + isCalleeSavedRegister(MI->getOperand(0).getReg(), CSRegs)); +} + +void ARMRegisterInfo::emitEpilogue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + MachineBasicBlock::iterator MBBI = prior(MBB.end()); + assert((MBBI->getOpcode() == ARM::BX_RET || + MBBI->getOpcode() == ARM::tBX_RET || + MBBI->getOpcode() == ARM::tPOP_RET) && + "Can only insert epilog into returning blocks"); + + MachineFrameInfo *MFI = MF.getFrameInfo(); + ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>(); + bool isThumb = AFI->isThumbFunction(); + unsigned VARegSaveSize = AFI->getVarArgsRegSaveSize(); + int NumBytes = (int)MFI->getStackSize(); + if (!AFI->hasStackFrame()) { + if (NumBytes != 0) + emitSPUpdate(MBB, MBBI, NumBytes, ARMCC::AL, 0, isThumb, TII); + } else { + // Unwind MBBI to point to first LDR / FLDD. + const unsigned *CSRegs = getCalleeSavedRegs(); + if (MBBI != MBB.begin()) { + do + --MBBI; + while (MBBI != MBB.begin() && isCSRestore(MBBI, CSRegs)); + if (!isCSRestore(MBBI, CSRegs)) + ++MBBI; + } + + // Move SP to start of FP callee save spill area. + NumBytes -= (AFI->getGPRCalleeSavedArea1Size() + + AFI->getGPRCalleeSavedArea2Size() + + AFI->getDPRCalleeSavedAreaSize()); + if (isThumb) { + if (hasFP(MF)) { + NumBytes = AFI->getFramePtrSpillOffset() - NumBytes; + // Reset SP based on frame pointer only if the stack frame extends beyond + // frame pointer stack slot or target is ELF and the function has FP. + if (NumBytes) + emitThumbRegPlusImmediate(MBB, MBBI, ARM::SP, FramePtr, -NumBytes, TII); + else + BuildMI(MBB, MBBI, TII.get(ARM::tMOVr), ARM::SP).addReg(FramePtr); + } else { + if (MBBI->getOpcode() == ARM::tBX_RET && + &MBB.front() != MBBI && + prior(MBBI)->getOpcode() == ARM::tPOP) { + MachineBasicBlock::iterator PMBBI = prior(MBBI); + emitSPUpdate(MBB, PMBBI, NumBytes, ARMCC::AL, 0, isThumb, TII); + } else + emitSPUpdate(MBB, MBBI, NumBytes, ARMCC::AL, 0, isThumb, TII); + } + } else { + // Darwin ABI requires FP to point to the stack slot that contains the + // previous FP. + if ((STI.isTargetDarwin() && NumBytes) || hasFP(MF)) { + NumBytes = AFI->getFramePtrSpillOffset() - NumBytes; + // Reset SP based on frame pointer only if the stack frame extends beyond + // frame pointer stack slot or target is ELF and the function has FP. + if (AFI->getGPRCalleeSavedArea2Size() || + AFI->getDPRCalleeSavedAreaSize() || + AFI->getDPRCalleeSavedAreaOffset()|| + hasFP(MF)) + if (NumBytes) + BuildMI(MBB, MBBI, TII.get(ARM::SUBri), ARM::SP).addReg(FramePtr) + .addImm(NumBytes) + .addImm((unsigned)ARMCC::AL).addReg(0).addReg(0); + else + BuildMI(MBB, MBBI, TII.get(ARM::MOVr), ARM::SP).addReg(FramePtr) + .addImm((unsigned)ARMCC::AL).addReg(0).addReg(0); + } else if (NumBytes) { + emitSPUpdate(MBB, MBBI, NumBytes, ARMCC::AL, 0, false, TII); + } + + // Move SP to start of integer callee save spill area 2. + movePastCSLoadStoreOps(MBB, MBBI, ARM::FLDD, 3, STI); + emitSPUpdate(MBB, MBBI, AFI->getDPRCalleeSavedAreaSize(), ARMCC::AL, 0, + false, TII); + + // Move SP to start of integer callee save spill area 1. + movePastCSLoadStoreOps(MBB, MBBI, ARM::LDR, 2, STI); + emitSPUpdate(MBB, MBBI, AFI->getGPRCalleeSavedArea2Size(), ARMCC::AL, 0, + false, TII); + + // Move SP to SP upon entry to the function. + movePastCSLoadStoreOps(MBB, MBBI, ARM::LDR, 1, STI); + emitSPUpdate(MBB, MBBI, AFI->getGPRCalleeSavedArea1Size(), ARMCC::AL, 0, + false, TII); + } + } + + if (VARegSaveSize) { + if (isThumb) + // Epilogue for vararg functions: pop LR to R3 and branch off it. + // FIXME: Verify this is still ok when R3 is no longer being reserved. + BuildMI(MBB, MBBI, TII.get(ARM::tPOP)).addReg(ARM::R3); + + emitSPUpdate(MBB, MBBI, VARegSaveSize, ARMCC::AL, 0, isThumb, TII); + + if (isThumb) { + BuildMI(MBB, MBBI, TII.get(ARM::tBX_RET_vararg)).addReg(ARM::R3); + MBB.erase(MBBI); + } + } +} + +unsigned ARMRegisterInfo::getRARegister() const { + return ARM::LR; +} + +unsigned ARMRegisterInfo::getFrameRegister(MachineFunction &MF) const { + if (STI.isTargetDarwin() || hasFP(MF)) + return (STI.useThumbBacktraces() || STI.isThumb()) ? ARM::R7 : ARM::R11; + else + return ARM::SP; +} + +unsigned ARMRegisterInfo::getEHExceptionRegister() const { + assert(0 && "What is the exception register"); + return 0; +} + +unsigned ARMRegisterInfo::getEHHandlerRegister() const { + assert(0 && "What is the exception handler register"); + return 0; +} + +#include "ARMGenRegisterInfo.inc" + diff --git a/lib/Target/ARM/ARMRegisterInfo.h b/lib/Target/ARM/ARMRegisterInfo.h new file mode 100644 index 0000000..3db1d89 --- /dev/null +++ b/lib/Target/ARM/ARMRegisterInfo.h @@ -0,0 +1,108 @@ +//===- ARMRegisterInfo.h - ARM Register Information Impl --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the "Instituto Nokia de Tecnologia" and +// is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the ARM implementation of the MRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef ARMREGISTERINFO_H +#define ARMREGISTERINFO_H + +#include "llvm/Target/MRegisterInfo.h" +#include "ARMGenRegisterInfo.h.inc" + +namespace llvm { + class ARMSubtarget; + class TargetInstrInfo; + class Type; + +struct ARMRegisterInfo : public ARMGenRegisterInfo { + const TargetInstrInfo &TII; + const ARMSubtarget &STI; +private: + /// FramePtr - ARM physical register used as frame ptr. + unsigned FramePtr; + +public: + ARMRegisterInfo(const TargetInstrInfo &tii, const ARMSubtarget &STI); + + /// getRegisterNumbering - Given the enum value for some register, e.g. + /// ARM::LR, return the number that it corresponds to (e.g. 14). + static unsigned getRegisterNumbering(unsigned RegEnum); + + /// Code Generation virtual methods... + bool spillCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const std::vector<CalleeSavedInfo> &CSI) const; + + bool restoreCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const std::vector<CalleeSavedInfo> &CSI) const; + + void storeRegToStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + unsigned SrcReg, int FrameIndex, + const TargetRegisterClass *RC) const; + + void loadRegFromStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + unsigned DestReg, int FrameIndex, + const TargetRegisterClass *RC) const; + + void copyRegToReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, + unsigned DestReg, unsigned SrcReg, + const TargetRegisterClass *RC) const; + + void reMaterialize(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + unsigned DestReg, const MachineInstr *Orig) const; + + MachineInstr* foldMemoryOperand(MachineInstr* MI, unsigned OpNum, + int FrameIndex) const; + + const unsigned *getCalleeSavedRegs(const MachineFunction *MF = 0) const; + + const TargetRegisterClass* const* getCalleeSavedRegClasses( + const MachineFunction *MF = 0) const; + + BitVector getReservedRegs(const MachineFunction &MF) const; + + bool isReservedReg(const MachineFunction &MF, unsigned Reg) const; + + bool requiresRegisterScavenging(const MachineFunction &MF) const; + + bool hasFP(const MachineFunction &MF) const; + + bool hasReservedCallFrame(MachineFunction &MF) const; + + void eliminateCallFramePseudoInstr(MachineFunction &MF, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const; + + void eliminateFrameIndex(MachineBasicBlock::iterator II, + int SPAdj, RegScavenger *RS = NULL) const; + + void processFunctionBeforeCalleeSavedScan(MachineFunction &MF, + RegScavenger *RS = NULL) const; + + void emitPrologue(MachineFunction &MF) const; + void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const; + + // Debug information queries. + unsigned getRARegister() const; + unsigned getFrameRegister(MachineFunction &MF) const; + + // Exception handling queries. + unsigned getEHExceptionRegister() const; + unsigned getEHHandlerRegister() const; +}; + +} // end namespace llvm + +#endif diff --git a/lib/Target/ARM/ARMRegisterInfo.td b/lib/Target/ARM/ARMRegisterInfo.td new file mode 100644 index 0000000..3d2646e --- /dev/null +++ b/lib/Target/ARM/ARMRegisterInfo.td @@ -0,0 +1,196 @@ +//===- ARMRegisterInfo.td - ARM Register defs -------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the "Instituto Nokia de Tecnologia" and +// is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Declarations that describe the ARM register file +//===----------------------------------------------------------------------===// + +// Registers are identified with 4-bit ID numbers. +class ARMReg<bits<4> num, string n, list<Register> subregs = []> : Register<n> { + field bits<4> Num; + let Namespace = "ARM"; + let SubRegs = subregs; +} + +class ARMFReg<bits<5> num, string n> : Register<n> { + field bits<5> Num; + let Namespace = "ARM"; +} + +// Integer registers +def R0 : ARMReg< 0, "r0">, DwarfRegNum<0>; +def R1 : ARMReg< 1, "r1">, DwarfRegNum<1>; +def R2 : ARMReg< 2, "r2">, DwarfRegNum<2>; +def R3 : ARMReg< 3, "r3">, DwarfRegNum<3>; +def R4 : ARMReg< 4, "r4">, DwarfRegNum<4>; +def R5 : ARMReg< 5, "r5">, DwarfRegNum<5>; +def R6 : ARMReg< 6, "r6">, DwarfRegNum<6>; +def R7 : ARMReg< 7, "r7">, DwarfRegNum<7>; +def R8 : ARMReg< 8, "r8">, DwarfRegNum<8>; +def R9 : ARMReg< 9, "r9">, DwarfRegNum<9>; +def R10 : ARMReg<10, "r10">, DwarfRegNum<10>; +def R11 : ARMReg<11, "r11">, DwarfRegNum<11>; +def R12 : ARMReg<12, "r12">, DwarfRegNum<12>; +def SP : ARMReg<13, "sp">, DwarfRegNum<13>; +def LR : ARMReg<14, "lr">, DwarfRegNum<14>; +def PC : ARMReg<15, "pc">, DwarfRegNum<15>; + +// Float registers +def S0 : ARMFReg< 0, "s0">; def S1 : ARMFReg< 1, "s1">; +def S2 : ARMFReg< 2, "s2">; def S3 : ARMFReg< 3, "s3">; +def S4 : ARMFReg< 4, "s4">; def S5 : ARMFReg< 5, "s5">; +def S6 : ARMFReg< 6, "s6">; def S7 : ARMFReg< 7, "s7">; +def S8 : ARMFReg< 8, "s8">; def S9 : ARMFReg< 9, "s9">; +def S10 : ARMFReg<10, "s10">; def S11 : ARMFReg<11, "s11">; +def S12 : ARMFReg<12, "s12">; def S13 : ARMFReg<13, "s13">; +def S14 : ARMFReg<14, "s14">; def S15 : ARMFReg<15, "s15">; +def S16 : ARMFReg<16, "s16">; def S17 : ARMFReg<17, "s17">; +def S18 : ARMFReg<18, "s18">; def S19 : ARMFReg<19, "s19">; +def S20 : ARMFReg<20, "s20">; def S21 : ARMFReg<21, "s21">; +def S22 : ARMFReg<22, "s22">; def S23 : ARMFReg<23, "s23">; +def S24 : ARMFReg<24, "s24">; def S25 : ARMFReg<25, "s25">; +def S26 : ARMFReg<26, "s26">; def S27 : ARMFReg<27, "s27">; +def S28 : ARMFReg<28, "s28">; def S29 : ARMFReg<29, "s29">; +def S30 : ARMFReg<30, "s30">; def S31 : ARMFReg<31, "s31">; + +// Aliases of the F* registers used to hold 64-bit fp values (doubles) +def D0 : ARMReg< 0, "d0", [S0, S1]>; +def D1 : ARMReg< 1, "d1", [S2, S3]>; +def D2 : ARMReg< 2, "d2", [S4, S5]>; +def D3 : ARMReg< 3, "d3", [S6, S7]>; +def D4 : ARMReg< 4, "d4", [S8, S9]>; +def D5 : ARMReg< 5, "d5", [S10, S11]>; +def D6 : ARMReg< 6, "d6", [S12, S13]>; +def D7 : ARMReg< 7, "d7", [S14, S15]>; +def D8 : ARMReg< 8, "d8", [S16, S17]>; +def D9 : ARMReg< 9, "d9", [S18, S19]>; +def D10 : ARMReg<10, "d10", [S20, S21]>; +def D11 : ARMReg<11, "d11", [S22, S23]>; +def D12 : ARMReg<12, "d12", [S24, S25]>; +def D13 : ARMReg<13, "d13", [S26, S27]>; +def D14 : ARMReg<14, "d14", [S28, S29]>; +def D15 : ARMReg<15, "d15", [S30, S31]>; + +// Current Program Status Register. +def CPSR : ARMReg<0, "cpsr">; + +// Register classes. +// +// pc == Program Counter +// lr == Link Register +// sp == Stack Pointer +// r12 == ip (scratch) +// r7 == Frame Pointer (thumb-style backtraces) +// r11 == Frame Pointer (arm-style backtraces) +// r10 == Stack Limit +// +def GPR : RegisterClass<"ARM", [i32], 32, [R0, R1, R2, R3, R4, R5, R6, + R7, R8, R9, R10, R12, R11, + LR, SP, PC]> { + let MethodProtos = [{ + iterator allocation_order_begin(const MachineFunction &MF) const; + iterator allocation_order_end(const MachineFunction &MF) const; + }]; + // FIXME: We are reserving r12 in case the PEI needs to use it to + // generate large stack offset. Make it available once we have register + // scavenging. Similarly r3 is reserved in Thumb mode for now. + let MethodBodies = [{ + // FP is R11, R9 is available. + static const unsigned ARM_GPR_AO_1[] = { + ARM::R3, ARM::R2, ARM::R1, ARM::R0, + ARM::R12,ARM::LR, + ARM::R4, ARM::R5, ARM::R6, ARM::R7, + ARM::R8, ARM::R9, ARM::R10, + ARM::R11 }; + // FP is R11, R9 is not available. + static const unsigned ARM_GPR_AO_2[] = { + ARM::R3, ARM::R2, ARM::R1, ARM::R0, + ARM::R12,ARM::LR, + ARM::R4, ARM::R5, ARM::R6, ARM::R7, + ARM::R8, ARM::R10, + ARM::R11 }; + // FP is R7, R9 is available. + static const unsigned ARM_GPR_AO_3[] = { + ARM::R3, ARM::R2, ARM::R1, ARM::R0, + ARM::R12,ARM::LR, + ARM::R4, ARM::R5, ARM::R6, + ARM::R8, ARM::R9, ARM::R10,ARM::R11, + ARM::R7 }; + // FP is R7, R9 is not available. + static const unsigned ARM_GPR_AO_4[] = { + ARM::R3, ARM::R2, ARM::R1, ARM::R0, + ARM::R12,ARM::LR, + ARM::R4, ARM::R5, ARM::R6, + ARM::R8, ARM::R10,ARM::R11, + ARM::R7 }; + + // FP is R7, only low registers available. + static const unsigned THUMB_GPR_AO[] = { + ARM::R2, ARM::R1, ARM::R0, + ARM::R4, ARM::R5, ARM::R6, ARM::R7 }; + + GPRClass::iterator + GPRClass::allocation_order_begin(const MachineFunction &MF) const { + const TargetMachine &TM = MF.getTarget(); + const ARMSubtarget &Subtarget = TM.getSubtarget<ARMSubtarget>(); + if (Subtarget.isThumb()) + return THUMB_GPR_AO; + if (Subtarget.useThumbBacktraces()) { + if (Subtarget.isR9Reserved()) + return ARM_GPR_AO_4; + else + return ARM_GPR_AO_3; + } else { + if (Subtarget.isR9Reserved()) + return ARM_GPR_AO_2; + else + return ARM_GPR_AO_1; + } + } + + GPRClass::iterator + GPRClass::allocation_order_end(const MachineFunction &MF) const { + const TargetMachine &TM = MF.getTarget(); + const MRegisterInfo *RI = TM.getRegisterInfo(); + const ARMSubtarget &Subtarget = TM.getSubtarget<ARMSubtarget>(); + GPRClass::iterator I; + if (Subtarget.isThumb()) + I = THUMB_GPR_AO + (sizeof(THUMB_GPR_AO)/sizeof(unsigned)); + else if (Subtarget.useThumbBacktraces()) { + if (Subtarget.isR9Reserved()) { + I = ARM_GPR_AO_4 + (sizeof(ARM_GPR_AO_4)/sizeof(unsigned)); + } else { + I = ARM_GPR_AO_3 + (sizeof(ARM_GPR_AO_3)/sizeof(unsigned)); + } + } else { + if (Subtarget.isR9Reserved()) { + I = ARM_GPR_AO_2 + (sizeof(ARM_GPR_AO_2)/sizeof(unsigned)); + } else { + I = ARM_GPR_AO_1 + (sizeof(ARM_GPR_AO_1)/sizeof(unsigned)); + } + } + + // Mac OS X requires FP not to be clobbered for backtracing purpose. + return (Subtarget.isTargetDarwin() || RI->hasFP(MF)) ? I-1 : I; + } + }]; +} + +def SPR : RegisterClass<"ARM", [f32], 32, [S0, S1, S2, S3, S4, S5, S6, S7, S8, + S9, S10, S11, S12, S13, S14, S15, S16, S17, S18, S19, S20, S21, S22, + S23, S24, S25, S26, S27, S28, S29, S30, S31]>; + +// ARM requires only word alignment for double. It's more performant if it +// is double-word alignment though. +def DPR : RegisterClass<"ARM", [f64], 64, [D0, D1, D2, D3, D4, D5, D6, D7, D8, + D9, D10, D11, D12, D13, D14, D15]>; + +// Condition code registers. +def CCR : RegisterClass<"ARM", [i32], 32, [CPSR]>; diff --git a/lib/Target/ARM/ARMRelocations.h b/lib/Target/ARM/ARMRelocations.h new file mode 100644 index 0000000..beea52b --- /dev/null +++ b/lib/Target/ARM/ARMRelocations.h @@ -0,0 +1,28 @@ +//===- ARMRelocations.h - ARM Code Relocations ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the Raul Herbster and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the ARM target-specific relocation types. +// +//===----------------------------------------------------------------------===// + +#ifndef ARMRELOCATIONS_H +#define ARMRELOCATIONS_H + +#include "llvm/CodeGen/MachineRelocation.h" + +namespace llvm { + namespace ARM { + enum RelocationType { + + }; + } +} + +#endif + diff --git a/lib/Target/ARM/ARMSubtarget.cpp b/lib/Target/ARM/ARMSubtarget.cpp new file mode 100644 index 0000000..6db36df --- /dev/null +++ b/lib/Target/ARM/ARMSubtarget.cpp @@ -0,0 +1,57 @@ +//===-- ARMSubtarget.cpp - ARM Subtarget Information ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Evan Cheng and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the ARM specific subclass of TargetSubtarget. +// +//===----------------------------------------------------------------------===// + +#include "ARMSubtarget.h" +#include "ARMGenSubtarget.inc" +#include "llvm/Module.h" +using namespace llvm; + +ARMSubtarget::ARMSubtarget(const Module &M, const std::string &FS, bool thumb) + : ARMArchVersion(V4T) + , HasVFP2(false) + , IsThumb(thumb) + , UseThumbBacktraces(false) + , IsR9Reserved(false) + , stackAlignment(4) + , TargetType(isELF) // Default to ELF unless otherwise specified. + , TargetABI(ARM_ABI_APCS) { + + // Determine default and user specified characteristics + std::string CPU = "generic"; + + // Parse features string. + ParseSubtargetFeatures(FS, CPU); + + // Set the boolean corresponding to the current target triple, or the default + // if one cannot be determined, to true. + const std::string& TT = M.getTargetTriple(); + if (TT.length() > 5) { + if (TT.find("-darwin") != std::string::npos) + TargetType = isDarwin; + } else if (TT.empty()) { +#if defined(__APPLE__) + TargetType = isDarwin; +#endif + } + + if (TT.find("eabi") != std::string::npos) + TargetABI = ARM_ABI_AAPCS; + + if (isAAPCS_ABI()) + stackAlignment = 8; + + if (isTargetDarwin()) { + UseThumbBacktraces = true; + IsR9Reserved = true; + } +} diff --git a/lib/Target/ARM/ARMSubtarget.h b/lib/Target/ARM/ARMSubtarget.h new file mode 100644 index 0000000..62367ca --- /dev/null +++ b/lib/Target/ARM/ARMSubtarget.h @@ -0,0 +1,94 @@ +//=====---- ARMSubtarget.h - Define Subtarget for the ARM -----*- C++ -*--====// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Evan Cheng and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares the ARM specific subclass of TargetSubtarget. +// +//===----------------------------------------------------------------------===// + +#ifndef ARMSUBTARGET_H +#define ARMSUBTARGET_H + +#include "llvm/Target/TargetSubtarget.h" +#include <string> + +namespace llvm { +class Module; + +class ARMSubtarget : public TargetSubtarget { +protected: + enum ARMArchEnum { + V4T, V5T, V5TE, V6 + }; + + /// ARMArchVersion - ARM architecture vecrsion: V4T (base), V5T, V5TE, + /// and V6. + ARMArchEnum ARMArchVersion; + + /// HasVFP2 - True if the processor supports Vector Floating Point (VFP) V2 + /// instructions. + bool HasVFP2; + + /// IsThumb - True if we are in thumb mode, false if in ARM mode. + bool IsThumb; + + /// UseThumbBacktraces - True if we use thumb style backtraces. + bool UseThumbBacktraces; + + /// IsR9Reserved - True if R9 is a not available as general purpose register. + bool IsR9Reserved; + + /// stackAlignment - The minimum alignment known to hold of the stack frame on + /// entry to the function and which must be maintained by every function. + unsigned stackAlignment; + + public: + enum { + isELF, isDarwin + } TargetType; + + enum { + ARM_ABI_APCS, + ARM_ABI_AAPCS // ARM EABI + } TargetABI; + + /// This constructor initializes the data members to match that + /// of the specified module. + /// + ARMSubtarget(const Module &M, const std::string &FS, bool thumb); + + /// ParseSubtargetFeatures - Parses features string setting specified + /// subtarget options. Definition of function is auto generated by tblgen. + void ParseSubtargetFeatures(const std::string &FS, const std::string &CPU); + + bool hasV4TOps() const { return ARMArchVersion >= V4T; } + bool hasV5TOps() const { return ARMArchVersion >= V5T; } + bool hasV5TEOps() const { return ARMArchVersion >= V5TE; } + bool hasV6Ops() const { return ARMArchVersion >= V6; } + + bool hasVFP2() const { return HasVFP2; } + + bool isTargetDarwin() const { return TargetType == isDarwin; } + bool isTargetELF() const { return TargetType == isELF; } + + bool isAPCS_ABI() const { return TargetABI == ARM_ABI_APCS; } + bool isAAPCS_ABI() const { return TargetABI == ARM_ABI_AAPCS; } + + bool isThumb() const { return IsThumb; } + + bool useThumbBacktraces() const { return UseThumbBacktraces; } + bool isR9Reserved() const { return IsR9Reserved; } + + /// getStackAlignment - Returns the minimum alignment known to hold of the + /// stack frame on entry to the function and which must be maintained by every + /// function for this subtarget. + unsigned getStackAlignment() const { return stackAlignment; } +}; +} // End llvm namespace + +#endif // ARMSUBTARGET_H diff --git a/lib/Target/ARM/ARMTargetAsmInfo.cpp b/lib/Target/ARM/ARMTargetAsmInfo.cpp new file mode 100644 index 0000000..1dea1c1 --- /dev/null +++ b/lib/Target/ARM/ARMTargetAsmInfo.cpp @@ -0,0 +1,276 @@ + +//===-- ARMTargetAsmInfo.cpp - ARM asm properties ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by James M. Laskey and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the declarations of the ARMTargetAsmInfo properties. +// +//===----------------------------------------------------------------------===// + +#include "ARMTargetAsmInfo.h" +#include "ARMTargetMachine.h" +#include <cstring> +#include <cctype> +using namespace llvm; + +static const char* arm_asm_table[] = {"{r0}", "r0", + "{r1}", "r1", + "{r2}", "r2", + "{r3}", "r3", + "{r4}", "r4", + "{r5}", "r5", + "{r6}", "r6", + "{r7}", "r7", + "{r8}", "r8", + "{r9}", "r9", + "{r10}", "r10", + "{r11}", "r11", + "{r12}", "r12", + "{r13}", "r13", + "{r14}", "r14", + "{lr}", "lr", + "{sp}", "sp", + "{ip}", "ip", + "{fp}", "fp", + "{sl}", "sl", + "{memory}", "memory", + "{cc}", "cc", + 0,0}; + +ARMTargetAsmInfo::ARMTargetAsmInfo(const ARMTargetMachine &TM) { + Subtarget = &TM.getSubtarget<ARMSubtarget>(); + AsmTransCBE = arm_asm_table; + if (Subtarget->isTargetDarwin()) { + GlobalPrefix = "_"; + PrivateGlobalPrefix = "L"; + BSSSection = 0; // no BSS section. + ZeroFillDirective = "\t.zerofill\t"; // Uses .zerofill + SetDirective = "\t.set"; + WeakRefDirective = "\t.weak_reference\t"; + HiddenDirective = "\t.private_extern\t"; + ProtectedDirective = NULL; + JumpTableDataSection = ".const"; + CStringSection = "\t.cstring"; + FourByteConstantSection = "\t.literal4\n"; + EightByteConstantSection = "\t.literal8\n"; + ReadOnlySection = "\t.const\n"; + HasDotTypeDotSizeDirective = false; + if (TM.getRelocationModel() == Reloc::Static) { + StaticCtorsSection = ".constructor"; + StaticDtorsSection = ".destructor"; + } else { + StaticCtorsSection = ".mod_init_func"; + StaticDtorsSection = ".mod_term_func"; + } + + // In non-PIC modes, emit a special label before jump tables so that the + // linker can perform more accurate dead code stripping. + if (TM.getRelocationModel() != Reloc::PIC_) { + // Emit a local label that is preserved until the linker runs. + JumpTableSpecialLabelPrefix = "l"; + } + + NeedsSet = true; + DwarfAbbrevSection = ".section __DWARF,__debug_abbrev,regular,debug"; + DwarfInfoSection = ".section __DWARF,__debug_info,regular,debug"; + DwarfLineSection = ".section __DWARF,__debug_line,regular,debug"; + DwarfFrameSection = ".section __DWARF,__debug_frame,regular,debug"; + DwarfPubNamesSection = ".section __DWARF,__debug_pubnames,regular,debug"; + DwarfPubTypesSection = ".section __DWARF,__debug_pubtypes,regular,debug"; + DwarfStrSection = ".section __DWARF,__debug_str,regular,debug"; + DwarfLocSection = ".section __DWARF,__debug_loc,regular,debug"; + DwarfARangesSection = ".section __DWARF,__debug_aranges,regular,debug"; + DwarfRangesSection = ".section __DWARF,__debug_ranges,regular,debug"; + DwarfMacInfoSection = ".section __DWARF,__debug_macinfo,regular,debug"; + } else { + NeedsSet = false; + HasLEB128 = true; + AbsoluteDebugSectionOffsets = true; + ReadOnlySection = "\t.section\t.rodata\n"; + PrivateGlobalPrefix = ".L"; + WeakRefDirective = "\t.weak\t"; + SetDirective = "\t.set\t"; + DwarfRequiresFrameSection = false; + DwarfAbbrevSection = "\t.section\t.debug_abbrev,\"\",%progbits"; + DwarfInfoSection = "\t.section\t.debug_info,\"\",%progbits"; + DwarfLineSection = "\t.section\t.debug_line,\"\",%progbits"; + DwarfFrameSection = "\t.section\t.debug_frame,\"\",%progbits"; + DwarfPubNamesSection ="\t.section\t.debug_pubnames,\"\",%progbits"; + DwarfPubTypesSection ="\t.section\t.debug_pubtypes,\"\",%progbits"; + DwarfStrSection = "\t.section\t.debug_str,\"\",%progbits"; + DwarfLocSection = "\t.section\t.debug_loc,\"\",%progbits"; + DwarfARangesSection = "\t.section\t.debug_aranges,\"\",%progbits"; + DwarfRangesSection = "\t.section\t.debug_ranges,\"\",%progbits"; + DwarfMacInfoSection = "\t.section\t.debug_macinfo,\"\",%progbits"; + + if (Subtarget->isAAPCS_ABI()) { + StaticCtorsSection = "\t.section .init_array,\"aw\",%init_array"; + StaticDtorsSection = "\t.section .fini_array,\"aw\",%fini_array"; + } else { + StaticCtorsSection = "\t.section .ctors,\"aw\",%progbits"; + StaticDtorsSection = "\t.section .dtors,\"aw\",%progbits"; + } + TLSDataSection = "\t.section .tdata,\"awT\",%progbits"; + TLSBSSSection = "\t.section .tbss,\"awT\",%nobits"; + } + + ZeroDirective = "\t.space\t"; + AlignmentIsInBytes = false; + Data64bitsDirective = 0; + CommentString = "@"; + DataSection = "\t.data"; + ConstantPoolSection = "\t.text\n"; + COMMDirectiveTakesAlignment = false; + InlineAsmStart = "@ InlineAsm Start"; + InlineAsmEnd = "@ InlineAsm End"; + LCOMMDirective = "\t.lcomm\t"; +} + +/// Count the number of comma-separated arguments. +/// Do not try to detect errors. +unsigned ARMTargetAsmInfo::countArguments(const char* p) const { + unsigned count = 0; + while (*p && isspace(*p) && *p != '\n') + p++; + count++; + while (*p && *p!='\n' && + strncmp(p, CommentString, strlen(CommentString))!=0) { + if (*p==',') + count++; + p++; + } + return count; +} + +/// Count the length of a string enclosed in quote characters. +/// Do not try to detect errors. +unsigned ARMTargetAsmInfo::countString(const char* p) const { + unsigned count = 0; + while (*p && isspace(*p) && *p!='\n') + p++; + if (!*p || *p != '\"') + return count; + while (*++p && *p != '\"') + count++; + return count; +} + +/// ARM-specific version of TargetAsmInfo::getInlineAsmLength. +unsigned ARMTargetAsmInfo::getInlineAsmLength(const char *Str) const { + // Count the number of bytes in the asm. + bool atInsnStart = true; + bool inTextSection = true; + unsigned Length = 0; + for (; *Str; ++Str) { + if (atInsnStart) { + // Skip whitespace + while (*Str && isspace(*Str) && *Str != '\n') + Str++; + // Skip label + for (const char* p = Str; *p && !isspace(*p); p++) + if (*p == ':') { + Str = p+1; + while (*Str && isspace(*Str) && *Str != '\n') + Str++; + break; + } + // Ignore everything from comment char(s) to EOL + if (strncmp(Str, CommentString, strlen(CommentString))==-0) + atInsnStart = false; + // FIXME do something like the following for non-Darwin + else if (*Str == '.' && Subtarget->isTargetDarwin()) { + // Directive. + atInsnStart = false; + // Some change the section, but don't generate code. + if (strncasecmp(Str, ".literal4", strlen(".literal4"))==0 || + strncasecmp(Str, ".literal8", strlen(".literal8"))==0 || + strncasecmp(Str, ".const", strlen(".const"))==0 || + strncasecmp(Str, ".constructor", strlen(".constructor"))==0 || + strncasecmp(Str, ".cstring", strlen(".cstring"))==0 || + strncasecmp(Str, ".data", strlen(".data"))==0 || + strncasecmp(Str, ".destructor", strlen(".destructor"))==0 || + strncasecmp(Str, ".fvmlib_init0", strlen(".fvmlib_init0"))==0 || + strncasecmp(Str, ".fvmlib_init1", strlen(".fvmlib_init1"))==0 || + strncasecmp(Str, ".mod_init_func", strlen(".mod_init_func"))==0 || + strncasecmp(Str, ".mod_term_func", strlen(".mod_term_func"))==0 || + strncasecmp(Str, ".picsymbol_stub", strlen(".picsymbol_stub"))==0 || + strncasecmp(Str, ".symbol_stub", strlen(".symbol_stub"))==0 || + strncasecmp(Str, ".static_data", strlen(".static_data"))==0 || + strncasecmp(Str, ".section", strlen(".section"))==0 || + strncasecmp(Str, ".lazy_symbol_pointer", strlen(".lazy_symbol_pointer"))==0 || + strncasecmp(Str, ".non_lazy_symbol_pointer", strlen(".non_lazy_symbol_pointer"))==0 || + strncasecmp(Str, ".dyld", strlen(".dyld"))==0 || + strncasecmp(Str, ".const_data", strlen(".const_data"))==0 || + strncasecmp(Str, ".objc", strlen(".objc"))==0 || //// many directives + strncasecmp(Str, ".static_const", strlen(".static_const"))==0) + inTextSection=false; + else if (strncasecmp(Str, ".text", strlen(".text"))==0) + inTextSection = true; + // Some can't really be handled without implementing significant pieces + // of an assembler. Others require dynamic adjustment of block sizes in + // AdjustBBOffsetsAfter; it's a big compile-time speed hit to check every + // instruction in there, and none of these are currently used in the kernel. + else if (strncasecmp(Str, ".macro", strlen(".macro"))==0 || + strncasecmp(Str, ".if", strlen(".if"))==0 || + strncasecmp(Str, ".align", strlen(".align"))==0 || + strncasecmp(Str, ".fill", strlen(".fill"))==0 || + strncasecmp(Str, ".space", strlen(".space"))==0 || + strncasecmp(Str, ".zerofill", strlen(".zerofill"))==0 || + strncasecmp(Str, ".p2align", strlen(".p2align"))==0 || + strncasecmp(Str, ".p2alignw", strlen(".p2alignw"))==0 || + strncasecmp(Str, ".p2alignl", strlen(".p2alignl"))==0 || + strncasecmp(Str, ".align32", strlen(".p2align32"))==0 || + strncasecmp(Str, ".include", strlen(".include"))==0) + cerr << "Directive " << Str << " in asm may lead to invalid offsets for" << + " constant pools (the assembler will tell you if this happens).\n"; + // Some generate code, but this is only interesting in the text section. + else if (inTextSection) { + if (strncasecmp(Str, ".long", strlen(".long"))==0) + Length += 4*countArguments(Str+strlen(".long")); + else if (strncasecmp(Str, ".short", strlen(".short"))==0) + Length += 2*countArguments(Str+strlen(".short")); + else if (strncasecmp(Str, ".byte", strlen(".byte"))==0) + Length += 1*countArguments(Str+strlen(".byte")); + else if (strncasecmp(Str, ".single", strlen(".single"))==0) + Length += 4*countArguments(Str+strlen(".single")); + else if (strncasecmp(Str, ".double", strlen(".double"))==0) + Length += 8*countArguments(Str+strlen(".double")); + else if (strncasecmp(Str, ".quad", strlen(".quad"))==0) + Length += 16*countArguments(Str+strlen(".quad")); + else if (strncasecmp(Str, ".ascii", strlen(".ascii"))==0) + Length += countString(Str+strlen(".ascii")); + else if (strncasecmp(Str, ".asciz", strlen(".asciz"))==0) + Length += countString(Str+strlen(".asciz"))+1; + } + } else if (inTextSection) { + // An instruction + atInsnStart = false; + if (Subtarget->isThumb()) { + // BL and BLX <non-reg> are 4 bytes, all others 2. + if (strncasecmp(Str, "blx", strlen("blx"))==0) { + const char* p = Str+3; + while (*p && isspace(*p)) + p++; + if (*p == 'r' || *p=='R') + Length += 2; // BLX reg + else + Length += 4; // BLX non-reg + } else if (strncasecmp(Str, "bl", strlen("bl"))==0) + Length += 4; // BL + else + Length += 2; // Thumb anything else + } + else + Length += 4; // ARM + } + } + if (*Str == '\n' || *Str == SeparatorChar) + atInsnStart = true; + } + return Length; +} diff --git a/lib/Target/ARM/ARMTargetAsmInfo.h b/lib/Target/ARM/ARMTargetAsmInfo.h new file mode 100644 index 0000000..9dd45e5 --- /dev/null +++ b/lib/Target/ARM/ARMTargetAsmInfo.h @@ -0,0 +1,38 @@ +//=====-- ARMTargetAsmInfo.h - ARM asm properties -------------*- C++ -*--====// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by James M. Laskey and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the declaration of the ARMTargetAsmInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef ARMTARGETASMINFO_H +#define ARMTARGETASMINFO_H + +#include "llvm/Target/TargetAsmInfo.h" +#include "ARMSubtarget.h" + +namespace llvm { + + // Forward declaration. + class ARMTargetMachine; + + struct ARMTargetAsmInfo : public TargetAsmInfo { + ARMTargetAsmInfo(const ARMTargetMachine &TM); + + const ARMSubtarget *Subtarget; + + virtual unsigned getInlineAsmLength(const char *Str) const; + unsigned countArguments(const char *p) const; + unsigned countString(const char *p) const; + }; + + +} // namespace llvm + +#endif diff --git a/lib/Target/ARM/ARMTargetMachine.cpp b/lib/Target/ARM/ARMTargetMachine.cpp new file mode 100644 index 0000000..58b3ab9 --- /dev/null +++ b/lib/Target/ARM/ARMTargetMachine.cpp @@ -0,0 +1,160 @@ +//===-- ARMTargetMachine.cpp - Define TargetMachine for ARM ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the "Instituto Nokia de Tecnologia" and +// is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// + +#include "ARMTargetMachine.h" +#include "ARMTargetAsmInfo.h" +#include "ARMFrameInfo.h" +#include "ARM.h" +#include "llvm/Module.h" +#include "llvm/PassManager.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Target/TargetMachineRegistry.h" +#include "llvm/Target/TargetOptions.h" +using namespace llvm; + +static cl::opt<bool> DisableLdStOpti("disable-arm-loadstore-opti", cl::Hidden, + cl::desc("Disable load store optimization pass")); +static cl::opt<bool> EnableIfConversion("enable-arm-if-conversion", cl::Hidden, + cl::desc("Enable if-conversion pass")); + +namespace { + // Register the target. + RegisterTarget<ARMTargetMachine> X("arm", " ARM"); + RegisterTarget<ThumbTargetMachine> Y("thumb", " Thumb"); +} + +/// ThumbTargetMachine - Create an Thumb architecture model. +/// +unsigned ThumbTargetMachine::getJITMatchQuality() { +#if defined(__arm__) + return 10; +#endif + return 0; +} + +unsigned ThumbTargetMachine::getModuleMatchQuality(const Module &M) { + std::string TT = M.getTargetTriple(); + if (TT.size() >= 6 && std::string(TT.begin(), TT.begin()+6) == "thumb-") + return 20; + + // If the target triple is something non-thumb, we don't match. + if (!TT.empty()) return 0; + + if (M.getEndianness() == Module::LittleEndian && + M.getPointerSize() == Module::Pointer32) + return 10; // Weak match + else if (M.getEndianness() != Module::AnyEndianness || + M.getPointerSize() != Module::AnyPointerSize) + return 0; // Match for some other target + + return getJITMatchQuality()/2; +} + +ThumbTargetMachine::ThumbTargetMachine(const Module &M, const std::string &FS) + : ARMTargetMachine(M, FS, true) { +} + +/// TargetMachine ctor - Create an ARM architecture model. +/// +ARMTargetMachine::ARMTargetMachine(const Module &M, const std::string &FS, + bool isThumb) + : Subtarget(M, FS, isThumb), + DataLayout(Subtarget.isAPCS_ABI() ? + // APCS ABI + (isThumb ? + std::string("e-p:32:32-f64:32:32-i64:32:32-" + "i16:16:32-i8:8:32-i1:8:32-a:0:32") : + std::string("e-p:32:32-f64:32:32-i64:32:32")) : + // AAPCS ABI + (isThumb ? + std::string("e-p:32:32-f64:64:64-i64:64:64-" + "i16:16:32-i8:8:32-i1:8:32-a:0:32") : + std::string("e-p:32:32-f64:64:64-i64:64:64"))), + InstrInfo(Subtarget), + FrameInfo(Subtarget), + JITInfo(*this), + TLInfo(*this) {} + +unsigned ARMTargetMachine::getJITMatchQuality() { +#if defined(__thumb__) + return 10; +#endif + return 0; +} + +unsigned ARMTargetMachine::getModuleMatchQuality(const Module &M) { + std::string TT = M.getTargetTriple(); + if (TT.size() >= 4 && std::string(TT.begin(), TT.begin()+4) == "arm-") + return 20; + // If the target triple is something non-arm, we don't match. + if (!TT.empty()) return 0; + + if (M.getEndianness() == Module::LittleEndian && + M.getPointerSize() == Module::Pointer32) + return 10; // Weak match + else if (M.getEndianness() != Module::AnyEndianness || + M.getPointerSize() != Module::AnyPointerSize) + return 0; // Match for some other target + + return getJITMatchQuality()/2; +} + + +const TargetAsmInfo *ARMTargetMachine::createTargetAsmInfo() const { + return new ARMTargetAsmInfo(*this); +} + + +// Pass Pipeline Configuration +bool ARMTargetMachine::addInstSelector(FunctionPassManager &PM, bool Fast) { + PM.add(createARMISelDag(*this)); + return false; +} + +bool ARMTargetMachine::addPreEmitPass(FunctionPassManager &PM, bool Fast) { + // FIXME: temporarily disabling load / store optimization pass for Thumb mode. + if (!Fast && !DisableLdStOpti && !Subtarget.isThumb()) + PM.add(createARMLoadStoreOptimizationPass()); + + if (!Fast && EnableIfConversion && !Subtarget.isThumb()) + PM.add(createIfConverterPass()); + + PM.add(createARMConstantIslandPass()); + return true; +} + +bool ARMTargetMachine::addAssemblyEmitter(FunctionPassManager &PM, bool Fast, + std::ostream &Out) { + // Output assembly language. + PM.add(createARMCodePrinterPass(Out, *this)); + return false; +} + + +bool ARMTargetMachine::addCodeEmitter(FunctionPassManager &PM, bool Fast, + MachineCodeEmitter &MCE) { + // FIXME: Move this to TargetJITInfo! + setRelocationModel(Reloc::Static); + + // Machine code emitter pass for ARM. + PM.add(createARMCodeEmitterPass(*this, MCE)); + return false; +} + +bool ARMTargetMachine::addSimpleCodeEmitter(FunctionPassManager &PM, bool Fast, + MachineCodeEmitter &MCE) { + // Machine code emitter pass for ARM. + PM.add(createARMCodeEmitterPass(*this, MCE)); + return false; +} diff --git a/lib/Target/ARM/ARMTargetMachine.h b/lib/Target/ARM/ARMTargetMachine.h new file mode 100644 index 0000000..183a582 --- /dev/null +++ b/lib/Target/ARM/ARMTargetMachine.h @@ -0,0 +1,81 @@ +//===-- ARMTargetMachine.h - Define TargetMachine for ARM -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the "Instituto Nokia de Tecnologia" and +// is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares the ARM specific subclass of TargetMachine. +// +//===----------------------------------------------------------------------===// + +#ifndef ARMTARGETMACHINE_H +#define ARMTARGETMACHINE_H + +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetData.h" +#include "llvm/Target/TargetFrameInfo.h" +#include "ARMInstrInfo.h" +#include "ARMFrameInfo.h" +#include "ARMJITInfo.h" +#include "ARMSubtarget.h" +#include "ARMISelLowering.h" + +namespace llvm { + +class Module; + +class ARMTargetMachine : public LLVMTargetMachine { + ARMSubtarget Subtarget; + const TargetData DataLayout; // Calculates type size & alignment + ARMInstrInfo InstrInfo; + ARMFrameInfo FrameInfo; + ARMJITInfo JITInfo; + ARMTargetLowering TLInfo; + +public: + ARMTargetMachine(const Module &M, const std::string &FS, bool isThumb = false); + + virtual const ARMInstrInfo *getInstrInfo() const { return &InstrInfo; } + virtual const TargetFrameInfo *getFrameInfo() const { return &FrameInfo; } + virtual TargetJITInfo *getJITInfo() { return &JITInfo; } + virtual const MRegisterInfo *getRegisterInfo() const { + return &InstrInfo.getRegisterInfo(); + } + virtual const TargetData *getTargetData() const { return &DataLayout; } + virtual const ARMSubtarget *getSubtargetImpl() const { return &Subtarget; } + virtual ARMTargetLowering *getTargetLowering() const { + return const_cast<ARMTargetLowering*>(&TLInfo); + } + static unsigned getModuleMatchQuality(const Module &M); + static unsigned getJITMatchQuality(); + + virtual const TargetAsmInfo *createTargetAsmInfo() const; + + // Pass Pipeline Configuration + virtual bool addInstSelector(FunctionPassManager &PM, bool Fast); + virtual bool addPreEmitPass(FunctionPassManager &PM, bool Fast); + virtual bool addAssemblyEmitter(FunctionPassManager &PM, bool Fast, + std::ostream &Out); + virtual bool addCodeEmitter(FunctionPassManager &PM, bool Fast, + MachineCodeEmitter &MCE); + virtual bool addSimpleCodeEmitter(FunctionPassManager &PM, bool Fast, + MachineCodeEmitter &MCE); +}; + +/// ThumbTargetMachine - Thumb target machine. +/// +class ThumbTargetMachine : public ARMTargetMachine { +public: + ThumbTargetMachine(const Module &M, const std::string &FS); + + static unsigned getJITMatchQuality(); + static unsigned getModuleMatchQuality(const Module &M); +}; + +} // end namespace llvm + +#endif diff --git a/lib/Target/ARM/Makefile b/lib/Target/ARM/Makefile new file mode 100644 index 0000000..77300a1 --- /dev/null +++ b/lib/Target/ARM/Makefile @@ -0,0 +1,21 @@ +##===- lib/Target/ARM/Makefile -----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file was developed by the "Instituto Nokia de Tecnologia" and +# is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../../.. +LIBRARYNAME = LLVMARM +TARGET = ARM + +# Make sure that tblgen is run, first thing. +BUILT_SOURCES = ARMGenRegisterInfo.h.inc ARMGenRegisterNames.inc \ + ARMGenRegisterInfo.inc ARMGenInstrNames.inc \ + ARMGenInstrInfo.inc ARMGenAsmWriter.inc \ + ARMGenDAGISel.inc ARMGenSubtarget.inc + +include $(LEVEL)/Makefile.common diff --git a/lib/Target/ARM/README-Thumb.txt b/lib/Target/ARM/README-Thumb.txt new file mode 100644 index 0000000..380097d --- /dev/null +++ b/lib/Target/ARM/README-Thumb.txt @@ -0,0 +1,223 @@ +//===---------------------------------------------------------------------===// +// Random ideas for the ARM backend (Thumb specific). +//===---------------------------------------------------------------------===// + +* Add support for compiling functions in both ARM and Thumb mode, then taking + the smallest. +* Add support for compiling individual basic blocks in thumb mode, when in a + larger ARM function. This can be used for presumed cold code, like paths + to abort (failure path of asserts), EH handling code, etc. + +* Thumb doesn't have normal pre/post increment addressing modes, but you can + load/store 32-bit integers with pre/postinc by using load/store multiple + instrs with a single register. + +* Make better use of high registers r8, r10, r11, r12 (ip). Some variants of add + and cmp instructions can use high registers. Also, we can use them as + temporaries to spill values into. + +* In thumb mode, short, byte, and bool preferred alignments are currently set + to 4 to accommodate ISA restriction (i.e. add sp, #imm, imm must be multiple + of 4). + +//===---------------------------------------------------------------------===// + +Potential jumptable improvements: + +* If we know function size is less than (1 << 16) * 2 bytes, we can use 16-bit + jumptable entries (e.g. (L1 - L2) >> 1). Or even smaller entries if the + function is even smaller. This also applies to ARM. + +* Thumb jumptable codegen can improve given some help from the assembler. This + is what we generate right now: + + .set PCRELV0, (LJTI1_0_0-(LPCRELL0+4)) +LPCRELL0: + mov r1, #PCRELV0 + add r1, pc + ldr r0, [r0, r1] + cpy pc, r0 + .align 2 +LJTI1_0_0: + .long LBB1_3 + ... + +Note there is another pc relative add that we can take advantage of. + add r1, pc, #imm_8 * 4 + +We should be able to generate: + +LPCRELL0: + add r1, LJTI1_0_0 + ldr r0, [r0, r1] + cpy pc, r0 + .align 2 +LJTI1_0_0: + .long LBB1_3 + +if the assembler can translate the add to: + add r1, pc, #((LJTI1_0_0-(LPCRELL0+4))&0xfffffffc) + +Note the assembler also does something similar to constpool load: +LPCRELL0: + ldr r0, LCPI1_0 +=> + ldr r0, pc, #((LCPI1_0-(LPCRELL0+4))&0xfffffffc) + + +//===---------------------------------------------------------------------===// + +We compiles the following: + +define i16 @func_entry_2E_ce(i32 %i) { + switch i32 %i, label %bb12.exitStub [ + i32 0, label %bb4.exitStub + i32 1, label %bb9.exitStub + i32 2, label %bb4.exitStub + i32 3, label %bb4.exitStub + i32 7, label %bb9.exitStub + i32 8, label %bb.exitStub + i32 9, label %bb9.exitStub + ] + +bb12.exitStub: + ret i16 0 + +bb4.exitStub: + ret i16 1 + +bb9.exitStub: + ret i16 2 + +bb.exitStub: + ret i16 3 +} + +into: + +_func_entry_2E_ce: + mov r2, #1 + lsl r2, r0 + cmp r0, #9 + bhi LBB1_4 @bb12.exitStub +LBB1_1: @newFuncRoot + mov r1, #13 + tst r2, r1 + bne LBB1_5 @bb4.exitStub +LBB1_2: @newFuncRoot + ldr r1, LCPI1_0 + tst r2, r1 + bne LBB1_6 @bb9.exitStub +LBB1_3: @newFuncRoot + mov r1, #1 + lsl r1, r1, #8 + tst r2, r1 + bne LBB1_7 @bb.exitStub +LBB1_4: @bb12.exitStub + mov r0, #0 + bx lr +LBB1_5: @bb4.exitStub + mov r0, #1 + bx lr +LBB1_6: @bb9.exitStub + mov r0, #2 + bx lr +LBB1_7: @bb.exitStub + mov r0, #3 + bx lr +LBB1_8: + .align 2 +LCPI1_0: + .long 642 + + +gcc compiles to: + + cmp r0, #9 + @ lr needed for prologue + bhi L2 + ldr r3, L11 + mov r2, #1 + mov r1, r2, asl r0 + ands r0, r3, r2, asl r0 + movne r0, #2 + bxne lr + tst r1, #13 + beq L9 +L3: + mov r0, r2 + bx lr +L9: + tst r1, #256 + movne r0, #3 + bxne lr +L2: + mov r0, #0 + bx lr +L12: + .align 2 +L11: + .long 642 + + +GCC is doing a couple of clever things here: + 1. It is predicating one of the returns. This isn't a clear win though: in + cases where that return isn't taken, it is replacing one condbranch with + two 'ne' predicated instructions. + 2. It is sinking the shift of "1 << i" into the tst, and using ands instead of + tst. This will probably require whole function isel. + 3. GCC emits: + tst r1, #256 + we emit: + mov r1, #1 + lsl r1, r1, #8 + tst r2, r1 + + +//===---------------------------------------------------------------------===// + +When spilling in thumb mode and the sp offset is too large to fit in the ldr / +str offset field, we load the offset from a constpool entry and add it to sp: + +ldr r2, LCPI +add r2, sp +ldr r2, [r2] + +These instructions preserve the condition code which is important if the spill +is between a cmp and a bcc instruction. However, we can use the (potentially) +cheaper sequnce if we know it's ok to clobber the condition register. + +add r2, sp, #255 * 4 +add r2, #132 +ldr r2, [r2, #7 * 4] + +This is especially bad when dynamic alloca is used. The all fixed size stack +objects are referenced off the frame pointer with negative offsets. See +oggenc for an example. + +//===---------------------------------------------------------------------===// + +We are reserving R3 as a scratch register under thumb mode. So if it is live in +to the function, we save / restore R3 to / from R12. Until register scavenging +is done, we should save R3 to a high callee saved reg at emitPrologue time +(when hasFP is true or stack size is large) and restore R3 from that register +instead. This allows us to at least get rid of the save to r12 everytime it is +used. + +//===---------------------------------------------------------------------===// + +Poor codegen test/CodeGen/ARM/select.ll f7: + + ldr r5, LCPI1_0 +LPC0: + add r5, pc + ldr r6, LCPI1_1 + ldr r2, LCPI1_2 + cpy r3, r6 + cpy lr, pc + bx r5 + +//===---------------------------------------------------------------------===// + +Make register allocator / spiller smarter so we can re-materialize "mov r, imm", +etc. Almost all Thumb instructions clobber condition code. diff --git a/lib/Target/ARM/README.txt b/lib/Target/ARM/README.txt new file mode 100644 index 0000000..3db8f54 --- /dev/null +++ b/lib/Target/ARM/README.txt @@ -0,0 +1,530 @@ +//===---------------------------------------------------------------------===// +// Random ideas for the ARM backend. +//===---------------------------------------------------------------------===// + +Reimplement 'select' in terms of 'SEL'. + +* We would really like to support UXTAB16, but we need to prove that the + add doesn't need to overflow between the two 16-bit chunks. + +* Implement pre/post increment support. (e.g. PR935) +* Coalesce stack slots! +* Implement smarter constant generation for binops with large immediates. + +* Consider materializing FP constants like 0.0f and 1.0f using integer + immediate instructions then copy to FPU. Slower than load into FPU? + +//===---------------------------------------------------------------------===// + +Crazy idea: Consider code that uses lots of 8-bit or 16-bit values. By the +time regalloc happens, these values are now in a 32-bit register, usually with +the top-bits known to be sign or zero extended. If spilled, we should be able +to spill these to a 8-bit or 16-bit stack slot, zero or sign extending as part +of the reload. + +Doing this reduces the size of the stack frame (important for thumb etc), and +also increases the likelihood that we will be able to reload multiple values +from the stack with a single load. + +//===---------------------------------------------------------------------===// + +The constant island pass is in good shape. Some cleanups might be desirable, +but there is unlikely to be much improvement in the generated code. + +1. There may be some advantage to trying to be smarter about the initial +placement, rather than putting everything at the end. + +2. There might be some compile-time efficiency to be had by representing +consecutive islands as a single block rather than multiple blocks. + +3. Use a priority queue to sort constant pool users in inverse order of + position so we always process the one closed to the end of functions + first. This may simply CreateNewWater. + +//===---------------------------------------------------------------------===// + +Eliminate copysign custom expansion. We are still generating crappy code with +default expansion + if-conversion. + +//===---------------------------------------------------------------------===// + +Eliminate one instruction from: + +define i32 @_Z6slow4bii(i32 %x, i32 %y) { + %tmp = icmp sgt i32 %x, %y + %retval = select i1 %tmp, i32 %x, i32 %y + ret i32 %retval +} + +__Z6slow4bii: + cmp r0, r1 + movgt r1, r0 + mov r0, r1 + bx lr +=> + +__Z6slow4bii: + cmp r0, r1 + movle r0, r1 + bx lr + +//===---------------------------------------------------------------------===// + +Implement long long "X-3" with instructions that fold the immediate in. These +were disabled due to badness with the ARM carry flag on subtracts. + +//===---------------------------------------------------------------------===// + +We currently compile abs: +int foo(int p) { return p < 0 ? -p : p; } + +into: + +_foo: + rsb r1, r0, #0 + cmn r0, #1 + movgt r1, r0 + mov r0, r1 + bx lr + +This is very, uh, literal. This could be a 3 operation sequence: + t = (p sra 31); + res = (p xor t)-t + +Which would be better. This occurs in png decode. + +//===---------------------------------------------------------------------===// + +More load / store optimizations: +1) Look past instructions without side-effects (not load, store, branch, etc.) + when forming the list of loads / stores to optimize. + +2) Smarter register allocation? +We are probably missing some opportunities to use ldm / stm. Consider: + +ldr r5, [r0] +ldr r4, [r0, #4] + +This cannot be merged into a ldm. Perhaps we will need to do the transformation +before register allocation. Then teach the register allocator to allocate a +chunk of consecutive registers. + +3) Better representation for block transfer? This is from Olden/power: + + fldd d0, [r4] + fstd d0, [r4, #+32] + fldd d0, [r4, #+8] + fstd d0, [r4, #+40] + fldd d0, [r4, #+16] + fstd d0, [r4, #+48] + fldd d0, [r4, #+24] + fstd d0, [r4, #+56] + +If we can spare the registers, it would be better to use fldm and fstm here. +Need major register allocator enhancement though. + +4) Can we recognize the relative position of constantpool entries? i.e. Treat + + ldr r0, LCPI17_3 + ldr r1, LCPI17_4 + ldr r2, LCPI17_5 + + as + ldr r0, LCPI17 + ldr r1, LCPI17+4 + ldr r2, LCPI17+8 + + Then the ldr's can be combined into a single ldm. See Olden/power. + +Note for ARM v4 gcc uses ldmia to load a pair of 32-bit values to represent a +double 64-bit FP constant: + + adr r0, L6 + ldmia r0, {r0-r1} + + .align 2 +L6: + .long -858993459 + .long 1074318540 + +5) Can we make use of ldrd and strd? Instead of generating ldm / stm, use +ldrd/strd instead if there are only two destination registers that form an +odd/even pair. However, we probably would pay a penalty if the address is not +aligned on 8-byte boundary. This requires more information on load / store +nodes (and MI's?) then we currently carry. + +6) struct copies appear to be done field by field +instead of by words, at least sometimes: + +struct foo { int x; short s; char c1; char c2; }; +void cpy(struct foo*a, struct foo*b) { *a = *b; } + +llvm code (-O2) + ldrb r3, [r1, #+6] + ldr r2, [r1] + ldrb r12, [r1, #+7] + ldrh r1, [r1, #+4] + str r2, [r0] + strh r1, [r0, #+4] + strb r3, [r0, #+6] + strb r12, [r0, #+7] +gcc code (-O2) + ldmia r1, {r1-r2} + stmia r0, {r1-r2} + +In this benchmark poor handling of aggregate copies has shown up as +having a large effect on size, and possibly speed as well (we don't have +a good way to measure on ARM). + +//===---------------------------------------------------------------------===// + +* Consider this silly example: + +double bar(double x) { + double r = foo(3.1); + return x+r; +} + +_bar: + sub sp, sp, #16 + str r4, [sp, #+12] + str r5, [sp, #+8] + str lr, [sp, #+4] + mov r4, r0 + mov r5, r1 + ldr r0, LCPI2_0 + bl _foo + fmsr f0, r0 + fcvtsd d0, f0 + fmdrr d1, r4, r5 + faddd d0, d0, d1 + fmrrd r0, r1, d0 + ldr lr, [sp, #+4] + ldr r5, [sp, #+8] + ldr r4, [sp, #+12] + add sp, sp, #16 + bx lr + +Ignore the prologue and epilogue stuff for a second. Note + mov r4, r0 + mov r5, r1 +the copys to callee-save registers and the fact they are only being used by the +fmdrr instruction. It would have been better had the fmdrr been scheduled +before the call and place the result in a callee-save DPR register. The two +mov ops would not have been necessary. + +//===---------------------------------------------------------------------===// + +Calling convention related stuff: + +* gcc's parameter passing implementation is terrible and we suffer as a result: + +e.g. +struct s { + double d1; + int s1; +}; + +void foo(struct s S) { + printf("%g, %d\n", S.d1, S.s1); +} + +'S' is passed via registers r0, r1, r2. But gcc stores them to the stack, and +then reload them to r1, r2, and r3 before issuing the call (r0 contains the +address of the format string): + + stmfd sp!, {r7, lr} + add r7, sp, #0 + sub sp, sp, #12 + stmia sp, {r0, r1, r2} + ldmia sp, {r1-r2} + ldr r0, L5 + ldr r3, [sp, #8] +L2: + add r0, pc, r0 + bl L_printf$stub + +Instead of a stmia, ldmia, and a ldr, wouldn't it be better to do three moves? + +* Return an aggregate type is even worse: + +e.g. +struct s foo(void) { + struct s S = {1.1, 2}; + return S; +} + + mov ip, r0 + ldr r0, L5 + sub sp, sp, #12 +L2: + add r0, pc, r0 + @ lr needed for prologue + ldmia r0, {r0, r1, r2} + stmia sp, {r0, r1, r2} + stmia ip, {r0, r1, r2} + mov r0, ip + add sp, sp, #12 + bx lr + +r0 (and later ip) is the hidden parameter from caller to store the value in. The +first ldmia loads the constants into r0, r1, r2. The last stmia stores r0, r1, +r2 into the address passed in. However, there is one additional stmia that +stores r0, r1, and r2 to some stack location. The store is dead. + +The llvm-gcc generated code looks like this: + +csretcc void %foo(%struct.s* %agg.result) { +entry: + %S = alloca %struct.s, align 4 ; <%struct.s*> [#uses=1] + %memtmp = alloca %struct.s ; <%struct.s*> [#uses=1] + cast %struct.s* %S to sbyte* ; <sbyte*>:0 [#uses=2] + call void %llvm.memcpy.i32( sbyte* %0, sbyte* cast ({ double, int }* %C.0.904 to sbyte*), uint 12, uint 4 ) + cast %struct.s* %agg.result to sbyte* ; <sbyte*>:1 [#uses=2] + call void %llvm.memcpy.i32( sbyte* %1, sbyte* %0, uint 12, uint 0 ) + cast %struct.s* %memtmp to sbyte* ; <sbyte*>:2 [#uses=1] + call void %llvm.memcpy.i32( sbyte* %2, sbyte* %1, uint 12, uint 0 ) + ret void +} + +llc ends up issuing two memcpy's (the first memcpy becomes 3 loads from +constantpool). Perhaps we should 1) fix llvm-gcc so the memcpy is translated +into a number of load and stores, or 2) custom lower memcpy (of small size) to +be ldmia / stmia. I think option 2 is better but the current register +allocator cannot allocate a chunk of registers at a time. + +A feasible temporary solution is to use specific physical registers at the +lowering time for small (<= 4 words?) transfer size. + +* ARM CSRet calling convention requires the hidden argument to be returned by +the callee. + +//===---------------------------------------------------------------------===// + +We can definitely do a better job on BB placements to eliminate some branches. +It's very common to see llvm generated assembly code that looks like this: + +LBB3: + ... +LBB4: +... + beq LBB3 + b LBB2 + +If BB4 is the only predecessor of BB3, then we can emit BB3 after BB4. We can +then eliminate beq and and turn the unconditional branch to LBB2 to a bne. + +See McCat/18-imp/ComputeBoundingBoxes for an example. + +//===---------------------------------------------------------------------===// + +Register scavenging is now implemented. The example in the previous version +of this document produces optimal code at -O2. + +//===---------------------------------------------------------------------===// + +Pre-/post- indexed load / stores: + +1) We should not make the pre/post- indexed load/store transform if the base ptr +is guaranteed to be live beyond the load/store. This can happen if the base +ptr is live out of the block we are performing the optimization. e.g. + +mov r1, r2 +ldr r3, [r1], #4 +... + +vs. + +ldr r3, [r2] +add r1, r2, #4 +... + +In most cases, this is just a wasted optimization. However, sometimes it can +negatively impact the performance because two-address code is more restrictive +when it comes to scheduling. + +Unfortunately, liveout information is currently unavailable during DAG combine +time. + +2) Consider spliting a indexed load / store into a pair of add/sub + load/store + to solve #1 (in TwoAddressInstructionPass.cpp). + +3) Enhance LSR to generate more opportunities for indexed ops. + +4) Once we added support for multiple result patterns, write indexed loads + patterns instead of C++ instruction selection code. + +5) Use FLDM / FSTM to emulate indexed FP load / store. + +//===---------------------------------------------------------------------===// + +We should add i64 support to take advantage of the 64-bit load / stores. +We can add a pseudo i64 register class containing pseudo registers that are +register pairs. All other ops (e.g. add, sub) would be expanded as usual. + +We need to add pseudo instructions (i.e. gethi / getlo) to extract i32 registers +from the i64 register. These are single moves which can be eliminated if the +destination register is a sub-register of the source. We should implement proper +subreg support in the register allocator to coalesce these away. + +There are other minor issues such as multiple instructions for a spill / restore +/ move. + +//===---------------------------------------------------------------------===// + +Implement support for some more tricky ways to materialize immediates. For +example, to get 0xffff8000, we can use: + +mov r9, #&3f8000 +sub r9, r9, #&400000 + +//===---------------------------------------------------------------------===// + +We sometimes generate multiple add / sub instructions to update sp in prologue +and epilogue if the inc / dec value is too large to fit in a single immediate +operand. In some cases, perhaps it might be better to load the value from a +constantpool instead. + +//===---------------------------------------------------------------------===// + +GCC generates significantly better code for this function. + +int foo(int StackPtr, unsigned char *Line, unsigned char *Stack, int LineLen) { + int i = 0; + + if (StackPtr != 0) { + while (StackPtr != 0 && i < (((LineLen) < (32768))? (LineLen) : (32768))) + Line[i++] = Stack[--StackPtr]; + if (LineLen > 32768) + { + while (StackPtr != 0 && i < LineLen) + { + i++; + --StackPtr; + } + } + } + return StackPtr; +} + +//===---------------------------------------------------------------------===// + +This should compile to the mlas instruction: +int mlas(int x, int y, int z) { return ((x * y + z) < 0) ? 7 : 13; } + +//===---------------------------------------------------------------------===// + +At some point, we should triage these to see if they still apply to us: + +http://gcc.gnu.org/bugzilla/show_bug.cgi?id=19598 +http://gcc.gnu.org/bugzilla/show_bug.cgi?id=18560 +http://gcc.gnu.org/bugzilla/show_bug.cgi?id=27016 + +http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11831 +http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11826 +http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11825 +http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11824 +http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11823 +http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11820 +http://gcc.gnu.org/bugzilla/show_bug.cgi?id=10982 + +http://gcc.gnu.org/bugzilla/show_bug.cgi?id=10242 +http://gcc.gnu.org/bugzilla/show_bug.cgi?id=9831 +http://gcc.gnu.org/bugzilla/show_bug.cgi?id=9760 +http://gcc.gnu.org/bugzilla/show_bug.cgi?id=9759 +http://gcc.gnu.org/bugzilla/show_bug.cgi?id=9703 +http://gcc.gnu.org/bugzilla/show_bug.cgi?id=9702 +http://gcc.gnu.org/bugzilla/show_bug.cgi?id=9663 + +http://www.inf.u-szeged.hu/gcc-arm/ +http://citeseer.ist.psu.edu/debus04linktime.html + +//===---------------------------------------------------------------------===// + +gcc generates smaller code for this function at -O2 or -Os: + +void foo(signed char* p) { + if (*p == 3) + bar(); + else if (*p == 4) + baz(); + else if (*p == 5) + quux(); +} + +llvm decides it's a good idea to turn the repeated if...else into a +binary tree, as if it were a switch; the resulting code requires -1 +compare-and-branches when *p<=2 or *p==5, the same number if *p==4 +or *p>6, and +1 if *p==3. So it should be a speed win +(on balance). However, the revised code is larger, with 4 conditional +branches instead of 3. + +More seriously, there is a byte->word extend before +each comparison, where there should be only one, and the condition codes +are not remembered when the same two values are compared twice. + +//===---------------------------------------------------------------------===// + +More register scavenging work: + +1. Use the register scavenger to track frame index materialized into registers + (those that do not fit in addressing modes) to allow reuse in the same BB. +2. Finish scavenging for Thumb. +3. We know some spills and restores are unnecessary. The issue is once live + intervals are merged, they are not never split. So every def is spilled + and every use requires a restore if the register allocator decides the + resulting live interval is not assigned a physical register. It may be + possible (with the help of the scavenger) to turn some spill / restore + pairs into register copies. + +//===---------------------------------------------------------------------===// + +More LSR enhancements possible: + +1. Teach LSR about pre- and post- indexed ops to allow iv increment be merged + in a load / store. +2. Allow iv reuse even when a type conversion is required. For example, i8 + and i32 load / store addressing modes are identical. + + +//===---------------------------------------------------------------------===// + +This: + +int foo(int a, int b, int c, int d) { + long long acc = (long long)a * (long long)b; + acc += (long long)c * (long long)d; + return (int)(acc >> 32); +} + +Should compile to use SMLAL (Signed Multiply Accumulate Long) which multiplies +two signed 32-bit values to produce a 64-bit value, and accumulates this with +a 64-bit value. + +We currently get this with v6: + +_foo: + mul r12, r1, r0 + smmul r1, r1, r0 + smmul r0, r3, r2 + mul r3, r3, r2 + adds r3, r3, r12 + adc r0, r0, r1 + bx lr + +and this with v4: + +_foo: + stmfd sp!, {r7, lr} + mov r7, sp + mul r12, r1, r0 + smull r0, r1, r1, r0 + smull lr, r0, r3, r2 + mul r3, r3, r2 + adds r3, r3, r12 + adc r0, r0, r1 + ldmfd sp!, {r7, pc} + +This apparently occurs in real code. + +//===---------------------------------------------------------------------===// |