mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-02-22 13:29:44 +00:00
This patch adds support for AArch64 (ARM's 64-bit architecture) to LLVM in the "experimental" category. Currently, it won't be built unless requested explicitly. This initial commit should have support for: + Assembly of all scalar (i.e. non-NEON, non-Crypto) instructions (except the late addition CRC instructions). + CodeGen features required for C++03 and C99. + Compilation for the "small" memory model: code+static data < 4GB. + Absolute and position-independent code. + GNU-style (i.e. "__thread") TLS. + Debugging information. The principal omission, currently, is performance tuning. This patch excludes the NEON support also reviewed due to an outbreak of batshit insanity in our legal department. That will be committed soon bringing the changes to precisely what has been approved. Further reviews would be gratefully received. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@174054 91177308-0d34-0410-b5e6-96231b3b80d8
409 lines
12 KiB
C++
409 lines
12 KiB
C++
//==-- AArch64InstPrinter.cpp - Convert AArch64 MCInst to assembly syntax --==//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This class prints an AArch64 MCInst to a .s file.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#define DEBUG_TYPE "asm-printer"
|
|
#include "AArch64InstPrinter.h"
|
|
#include "MCTargetDesc/AArch64BaseInfo.h"
|
|
#include "MCTargetDesc/AArch64MCTargetDesc.h"
|
|
#include "llvm/MC/MCInst.h"
|
|
#include "llvm/MC/MCExpr.h"
|
|
#include "llvm/MC/MCRegisterInfo.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/Format.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace llvm;
|
|
|
|
#define GET_INSTRUCTION_NAME
|
|
#define PRINT_ALIAS_INSTR
|
|
#include "AArch64GenAsmWriter.inc"
|
|
|
|
static int64_t unpackSignedImm(int BitWidth, uint64_t Value) {
|
|
assert(!(Value & ~((1ULL << BitWidth)-1)) && "immediate not n-bit");
|
|
if (Value & (1ULL << (BitWidth - 1)))
|
|
return static_cast<int64_t>(Value) - (1LL << BitWidth);
|
|
else
|
|
return Value;
|
|
}
|
|
|
|
AArch64InstPrinter::AArch64InstPrinter(const MCAsmInfo &MAI,
|
|
const MCInstrInfo &MII,
|
|
const MCRegisterInfo &MRI,
|
|
const MCSubtargetInfo &STI) :
|
|
MCInstPrinter(MAI, MII, MRI) {
|
|
// Initialize the set of available features.
|
|
setAvailableFeatures(STI.getFeatureBits());
|
|
}
|
|
|
|
void AArch64InstPrinter::printRegName(raw_ostream &OS, unsigned RegNo) const {
|
|
OS << getRegisterName(RegNo);
|
|
}
|
|
|
|
void
|
|
AArch64InstPrinter::printOffsetSImm9Operand(const MCInst *MI,
|
|
unsigned OpNum, raw_ostream &O) {
|
|
const MCOperand &MOImm = MI->getOperand(OpNum);
|
|
int32_t Imm = unpackSignedImm(9, MOImm.getImm());
|
|
|
|
O << '#' << Imm;
|
|
}
|
|
|
|
void
|
|
AArch64InstPrinter::printAddrRegExtendOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O, unsigned MemSize,
|
|
unsigned RmSize) {
|
|
unsigned ExtImm = MI->getOperand(OpNum).getImm();
|
|
unsigned OptionHi = ExtImm >> 1;
|
|
unsigned S = ExtImm & 1;
|
|
bool IsLSL = OptionHi == 1 && RmSize == 64;
|
|
|
|
const char *Ext;
|
|
switch (OptionHi) {
|
|
case 1:
|
|
Ext = (RmSize == 32) ? "uxtw" : "lsl";
|
|
break;
|
|
case 3:
|
|
Ext = (RmSize == 32) ? "sxtw" : "sxtx";
|
|
break;
|
|
default:
|
|
llvm_unreachable("Incorrect Option on load/store (reg offset)");
|
|
}
|
|
O << Ext;
|
|
|
|
if (S) {
|
|
unsigned ShiftAmt = Log2_32(MemSize);
|
|
O << " #" << ShiftAmt;
|
|
} else if (IsLSL) {
|
|
O << " #0";
|
|
}
|
|
}
|
|
|
|
void
|
|
AArch64InstPrinter::printAddSubImmLSL0Operand(const MCInst *MI,
|
|
unsigned OpNum, raw_ostream &O) {
|
|
const MCOperand &Imm12Op = MI->getOperand(OpNum);
|
|
|
|
if (Imm12Op.isImm()) {
|
|
int64_t Imm12 = Imm12Op.getImm();
|
|
assert(Imm12 >= 0 && "Invalid immediate for add/sub imm");
|
|
O << "#" << Imm12;
|
|
} else {
|
|
assert(Imm12Op.isExpr() && "Unexpected shift operand type");
|
|
O << "#" << *Imm12Op.getExpr();
|
|
}
|
|
}
|
|
|
|
void
|
|
AArch64InstPrinter::printAddSubImmLSL12Operand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
|
|
printAddSubImmLSL0Operand(MI, OpNum, O);
|
|
|
|
O << ", lsl #12";
|
|
}
|
|
|
|
void
|
|
AArch64InstPrinter::printBareImmOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
const MCOperand &MO = MI->getOperand(OpNum);
|
|
O << MO.getImm();
|
|
}
|
|
|
|
template<unsigned RegWidth> void
|
|
AArch64InstPrinter::printBFILSBOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
const MCOperand &ImmROp = MI->getOperand(OpNum);
|
|
unsigned LSB = ImmROp.getImm() == 0 ? 0 : RegWidth - ImmROp.getImm();
|
|
|
|
O << '#' << LSB;
|
|
}
|
|
|
|
void AArch64InstPrinter::printBFIWidthOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
const MCOperand &ImmSOp = MI->getOperand(OpNum);
|
|
unsigned Width = ImmSOp.getImm() + 1;
|
|
|
|
O << '#' << Width;
|
|
}
|
|
|
|
void
|
|
AArch64InstPrinter::printBFXWidthOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
const MCOperand &ImmSOp = MI->getOperand(OpNum);
|
|
const MCOperand &ImmROp = MI->getOperand(OpNum - 1);
|
|
|
|
unsigned ImmR = ImmROp.getImm();
|
|
unsigned ImmS = ImmSOp.getImm();
|
|
|
|
assert(ImmS >= ImmR && "Invalid ImmR, ImmS combination for bitfield extract");
|
|
|
|
O << '#' << (ImmS - ImmR + 1);
|
|
}
|
|
|
|
void
|
|
AArch64InstPrinter::printCRxOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
const MCOperand &CRx = MI->getOperand(OpNum);
|
|
|
|
O << 'c' << CRx.getImm();
|
|
}
|
|
|
|
|
|
void
|
|
AArch64InstPrinter::printCVTFixedPosOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
const MCOperand &ScaleOp = MI->getOperand(OpNum);
|
|
|
|
O << '#' << (64 - ScaleOp.getImm());
|
|
}
|
|
|
|
|
|
void AArch64InstPrinter::printFPImmOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &o) {
|
|
const MCOperand &MOImm8 = MI->getOperand(OpNum);
|
|
|
|
assert(MOImm8.isImm()
|
|
&& "Immediate operand required for floating-point immediate inst");
|
|
|
|
uint32_t Imm8 = MOImm8.getImm();
|
|
uint32_t Fraction = Imm8 & 0xf;
|
|
uint32_t Exponent = (Imm8 >> 4) & 0x7;
|
|
uint32_t Negative = (Imm8 >> 7) & 0x1;
|
|
|
|
float Val = 1.0f + Fraction / 16.0f;
|
|
|
|
// That is:
|
|
// 000 -> 2^1, 001 -> 2^2, 010 -> 2^3, 011 -> 2^4,
|
|
// 100 -> 2^-3, 101 -> 2^-2, 110 -> 2^-1, 111 -> 2^0
|
|
if (Exponent & 0x4) {
|
|
Val /= 1 << (7 - Exponent);
|
|
} else {
|
|
Val *= 1 << (Exponent + 1);
|
|
}
|
|
|
|
Val = Negative ? -Val : Val;
|
|
|
|
o << '#' << format("%.8f", Val);
|
|
}
|
|
|
|
void AArch64InstPrinter::printFPZeroOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &o) {
|
|
o << "#0.0";
|
|
}
|
|
|
|
void
|
|
AArch64InstPrinter::printCondCodeOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
const MCOperand &MO = MI->getOperand(OpNum);
|
|
|
|
O << A64CondCodeToString(static_cast<A64CC::CondCodes>(MO.getImm()));
|
|
}
|
|
|
|
template <unsigned field_width, unsigned scale> void
|
|
AArch64InstPrinter::printLabelOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
const MCOperand &MO = MI->getOperand(OpNum);
|
|
|
|
if (!MO.isImm()) {
|
|
printOperand(MI, OpNum, O);
|
|
return;
|
|
}
|
|
|
|
// The immediate of LDR (lit) instructions is a signed 19-bit immediate, which
|
|
// is multiplied by 4 (because all A64 instructions are 32-bits wide).
|
|
uint64_t UImm = MO.getImm();
|
|
uint64_t Sign = UImm & (1LL << (field_width - 1));
|
|
int64_t SImm = scale * ((UImm & ~Sign) - Sign);
|
|
|
|
O << "#" << SImm;
|
|
}
|
|
|
|
template<unsigned RegWidth> void
|
|
AArch64InstPrinter::printLogicalImmOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
const MCOperand &MO = MI->getOperand(OpNum);
|
|
uint64_t Val;
|
|
A64Imms::isLogicalImmBits(RegWidth, MO.getImm(), Val);
|
|
O << "#0x";
|
|
O.write_hex(Val);
|
|
}
|
|
|
|
void
|
|
AArch64InstPrinter::printOffsetUImm12Operand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O, int MemSize) {
|
|
const MCOperand &MOImm = MI->getOperand(OpNum);
|
|
|
|
if (MOImm.isImm()) {
|
|
uint32_t Imm = MOImm.getImm() * MemSize;
|
|
|
|
O << "#" << Imm;
|
|
} else {
|
|
O << "#" << *MOImm.getExpr();
|
|
}
|
|
}
|
|
|
|
void
|
|
AArch64InstPrinter::printShiftOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O,
|
|
A64SE::ShiftExtSpecifiers Shift) {
|
|
const MCOperand &MO = MI->getOperand(OpNum);
|
|
|
|
// LSL #0 is not printed
|
|
if (Shift == A64SE::LSL && MO.isImm() && MO.getImm() == 0)
|
|
return;
|
|
|
|
switch (Shift) {
|
|
case A64SE::LSL: O << "lsl"; break;
|
|
case A64SE::LSR: O << "lsr"; break;
|
|
case A64SE::ASR: O << "asr"; break;
|
|
case A64SE::ROR: O << "ror"; break;
|
|
default: llvm_unreachable("Invalid shift specifier in logical instruction");
|
|
}
|
|
|
|
O << " #" << MO.getImm();
|
|
}
|
|
|
|
void
|
|
AArch64InstPrinter::printMoveWideImmOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
const MCOperand &UImm16MO = MI->getOperand(OpNum);
|
|
const MCOperand &ShiftMO = MI->getOperand(OpNum + 1);
|
|
|
|
if (UImm16MO.isImm()) {
|
|
O << '#' << UImm16MO.getImm();
|
|
|
|
if (ShiftMO.getImm() != 0)
|
|
O << ", lsl #" << (ShiftMO.getImm() * 16);
|
|
|
|
return;
|
|
}
|
|
|
|
O << "#" << *UImm16MO.getExpr();
|
|
}
|
|
|
|
void AArch64InstPrinter::printNamedImmOperand(const NamedImmMapper &Mapper,
|
|
const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
bool ValidName;
|
|
const MCOperand &MO = MI->getOperand(OpNum);
|
|
StringRef Name = Mapper.toString(MO.getImm(), ValidName);
|
|
|
|
if (ValidName)
|
|
O << Name;
|
|
else
|
|
O << '#' << MO.getImm();
|
|
}
|
|
|
|
void
|
|
AArch64InstPrinter::printSysRegOperand(const A64SysReg::SysRegMapper &Mapper,
|
|
const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
const MCOperand &MO = MI->getOperand(OpNum);
|
|
|
|
bool ValidName;
|
|
std::string Name = Mapper.toString(MO.getImm(), ValidName);
|
|
if (ValidName) {
|
|
O << Name;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
void AArch64InstPrinter::printRegExtendOperand(const MCInst *MI,
|
|
unsigned OpNum,
|
|
raw_ostream &O,
|
|
A64SE::ShiftExtSpecifiers Ext) {
|
|
// FIXME: In principle TableGen should be able to detect this itself far more
|
|
// easily. We will only accumulate more of these hacks.
|
|
unsigned Reg0 = MI->getOperand(0).getReg();
|
|
unsigned Reg1 = MI->getOperand(1).getReg();
|
|
|
|
if (isStackReg(Reg0) || isStackReg(Reg1)) {
|
|
A64SE::ShiftExtSpecifiers LSLEquiv;
|
|
|
|
if (Reg0 == AArch64::XSP || Reg1 == AArch64::XSP)
|
|
LSLEquiv = A64SE::UXTX;
|
|
else
|
|
LSLEquiv = A64SE::UXTW;
|
|
|
|
if (Ext == LSLEquiv) {
|
|
O << "lsl #" << MI->getOperand(OpNum).getImm();
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (Ext) {
|
|
case A64SE::UXTB: O << "uxtb"; break;
|
|
case A64SE::UXTH: O << "uxth"; break;
|
|
case A64SE::UXTW: O << "uxtw"; break;
|
|
case A64SE::UXTX: O << "uxtx"; break;
|
|
case A64SE::SXTB: O << "sxtb"; break;
|
|
case A64SE::SXTH: O << "sxth"; break;
|
|
case A64SE::SXTW: O << "sxtw"; break;
|
|
case A64SE::SXTX: O << "sxtx"; break;
|
|
default: llvm_unreachable("Unexpected shift type for printing");
|
|
}
|
|
|
|
const MCOperand &MO = MI->getOperand(OpNum);
|
|
if (MO.getImm() != 0)
|
|
O << " #" << MO.getImm();
|
|
}
|
|
|
|
template<int MemScale> void
|
|
AArch64InstPrinter::printSImm7ScaledOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
const MCOperand &MOImm = MI->getOperand(OpNum);
|
|
int32_t Imm = unpackSignedImm(7, MOImm.getImm());
|
|
|
|
O << "#" << (Imm * MemScale);
|
|
}
|
|
|
|
void AArch64InstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
|
|
raw_ostream &O) {
|
|
const MCOperand &Op = MI->getOperand(OpNo);
|
|
if (Op.isReg()) {
|
|
unsigned Reg = Op.getReg();
|
|
O << getRegisterName(Reg);
|
|
} else if (Op.isImm()) {
|
|
O << '#' << Op.getImm();
|
|
} else {
|
|
assert(Op.isExpr() && "unknown operand kind in printOperand");
|
|
// If a symbolic branch target was added as a constant expression then print
|
|
// that address in hex.
|
|
const MCConstantExpr *BranchTarget = dyn_cast<MCConstantExpr>(Op.getExpr());
|
|
int64_t Address;
|
|
if (BranchTarget && BranchTarget->EvaluateAsAbsolute(Address)) {
|
|
O << "0x";
|
|
O.write_hex(Address);
|
|
}
|
|
else {
|
|
// Otherwise, just print the expression.
|
|
O << *Op.getExpr();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void AArch64InstPrinter::printInst(const MCInst *MI, raw_ostream &O,
|
|
StringRef Annot) {
|
|
if (MI->getOpcode() == AArch64::TLSDESCCALL) {
|
|
// This is a special assembler directive which applies an
|
|
// R_AARCH64_TLSDESC_CALL to the following (BLR) instruction. It has a fixed
|
|
// form outside the normal TableGenerated scheme.
|
|
O << "\t.tlsdesccall " << *MI->getOperand(0).getExpr();
|
|
} else if (!printAliasInstr(MI, O))
|
|
printInstruction(MI, O);
|
|
|
|
printAnnotation(O, Annot);
|
|
}
|