llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
Tim Northover 72062f5744 Add AArch64 as an experimental target.
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
2013-01-31 12:12:40 +00:00

362 lines
14 KiB
C++

//===-- AArch64AsmPrinter.cpp - Print machine code to an AArch64 .s file --===//
//
// The LLVM Compiler Infrastructure
//
// This file 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 AArch64 assembly language.
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "asm-printer"
#include "AArch64AsmPrinter.h"
#include "InstPrinter/AArch64InstPrinter.h"
#include "llvm/DebugInfo.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/CodeGen/MachineConstantPool.h"
#include "llvm/CodeGen/MachineModuleInfoImpls.h"
#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Target/Mangler.h"
using namespace llvm;
MachineLocation
AArch64AsmPrinter::getDebugValueLocation(const MachineInstr *MI) const {
// See emitFrameIndexDebugValue in InstrInfo for where this instruction is
// expected to be created.
assert(MI->getNumOperands() == 4 && MI->getOperand(0).isReg()
&& MI->getOperand(1).isImm() && "unexpected custom DBG_VALUE");
return MachineLocation(MI->getOperand(0).getReg(), MI->getOperand(1).getImm());
}
/// Try to print a floating-point register as if it belonged to a specified
/// register-class. For example the inline asm operand modifier "b" requires its
/// argument to be printed as "bN".
static bool printModifiedFPRAsmOperand(const MachineOperand &MO,
const TargetRegisterInfo *TRI,
const TargetRegisterClass &RegClass,
raw_ostream &O) {
if (!MO.isReg())
return true;
for (MCRegAliasIterator AR(MO.getReg(), TRI, true); AR.isValid(); ++AR) {
if (RegClass.contains(*AR)) {
O << AArch64InstPrinter::getRegisterName(*AR);
return false;
}
}
return true;
}
/// Implements the 'w' and 'x' inline asm operand modifiers, which print a GPR
/// with the obvious type and an immediate 0 as either wzr or xzr.
static bool printModifiedGPRAsmOperand(const MachineOperand &MO,
const TargetRegisterInfo *TRI,
const TargetRegisterClass &RegClass,
raw_ostream &O) {
char Prefix = &RegClass == &AArch64::GPR32RegClass ? 'w' : 'x';
if (MO.isImm() && MO.getImm() == 0) {
O << Prefix << "zr";
return false;
} else if (MO.isReg()) {
if (MO.getReg() == AArch64::XSP || MO.getReg() == AArch64::WSP) {
O << (Prefix == 'x' ? "sp" : "wsp");
return false;
}
for (MCRegAliasIterator AR(MO.getReg(), TRI, true); AR.isValid(); ++AR) {
if (RegClass.contains(*AR)) {
O << AArch64InstPrinter::getRegisterName(*AR);
return false;
}
}
}
return true;
}
bool AArch64AsmPrinter::printSymbolicAddress(const MachineOperand &MO,
bool PrintImmediatePrefix,
StringRef Suffix, raw_ostream &O) {
StringRef Name;
StringRef Modifier;
switch (MO.getType()) {
default: llvm_unreachable("Unexpected operand for symbolic address constraint");
case MachineOperand::MO_GlobalAddress:
Name = Mang->getSymbol(MO.getGlobal())->getName();
// Global variables may be accessed either via a GOT or in various fun and
// interesting TLS-model specific ways. Set the prefix modifier as
// appropriate here.
if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(MO.getGlobal())) {
Reloc::Model RelocM = TM.getRelocationModel();
if (GV->isThreadLocal()) {
switch (TM.getTLSModel(GV)) {
case TLSModel::GeneralDynamic:
Modifier = "tlsdesc";
break;
case TLSModel::LocalDynamic:
Modifier = "dtprel";
break;
case TLSModel::InitialExec:
Modifier = "gottprel";
break;
case TLSModel::LocalExec:
Modifier = "tprel";
break;
}
} else if (Subtarget->GVIsIndirectSymbol(GV, RelocM)) {
Modifier = "got";
}
}
break;
case MachineOperand::MO_BlockAddress:
Name = GetBlockAddressSymbol(MO.getBlockAddress())->getName();
break;
case MachineOperand::MO_ExternalSymbol:
Name = MO.getSymbolName();
break;
case MachineOperand::MO_ConstantPoolIndex:
Name = GetCPISymbol(MO.getIndex())->getName();
break;
}
// Some instructions (notably ADRP) don't take the # prefix for
// immediates. Only print it if asked to.
if (PrintImmediatePrefix)
O << '#';
// Only need the joining "_" if both the prefix and the suffix are
// non-null. This little block simply takes care of the four possibly
// combinations involved there.
if (Modifier == "" && Suffix == "")
O << Name;
else if (Modifier == "" && Suffix != "")
O << ":" << Suffix << ':' << Name;
else if (Modifier != "" && Suffix == "")
O << ":" << Modifier << ':' << Name;
else
O << ":" << Modifier << '_' << Suffix << ':' << Name;
return false;
}
bool AArch64AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
unsigned AsmVariant,
const char *ExtraCode, raw_ostream &O) {
const TargetRegisterInfo *TRI = MF->getTarget().getRegisterInfo();
if (!ExtraCode || !ExtraCode[0]) {
// There's actually no operand modifier, which leads to a slightly eclectic
// set of behaviour which we have to handle here.
const MachineOperand &MO = MI->getOperand(OpNum);
switch (MO.getType()) {
default:
llvm_unreachable("Unexpected operand for inline assembly");
case MachineOperand::MO_Register:
// GCC prints the unmodified operand of a 'w' constraint as the vector
// register. Technically, we could allocate the argument as a VPR128, but
// that leads to extremely dodgy copies being generated to get the data
// there.
if (printModifiedFPRAsmOperand(MO, TRI, AArch64::VPR128RegClass, O))
O << AArch64InstPrinter::getRegisterName(MO.getReg());
break;
case MachineOperand::MO_Immediate:
O << '#' << MO.getImm();
break;
case MachineOperand::MO_FPImmediate:
assert(MO.getFPImm()->isExactlyValue(0.0) && "Only FP 0.0 expected");
O << "#0.0";
break;
case MachineOperand::MO_BlockAddress:
case MachineOperand::MO_ConstantPoolIndex:
case MachineOperand::MO_GlobalAddress:
case MachineOperand::MO_ExternalSymbol:
return printSymbolicAddress(MO, false, "", O);
}
return false;
}
// We have a real modifier to handle.
switch(ExtraCode[0]) {
default:
// See if this is a generic operand
return AsmPrinter::PrintAsmOperand(MI, OpNum, AsmVariant, ExtraCode, O);
case 'c': // Don't print "#" before an immediate operand.
if (!MI->getOperand(OpNum).isImm())
return true;
O << MI->getOperand(OpNum).getImm();
return false;
case 'w':
// Output 32-bit general register operand, constant zero as wzr, or stack
// pointer as wsp. Ignored when used with other operand types.
return printModifiedGPRAsmOperand(MI->getOperand(OpNum), TRI,
AArch64::GPR32RegClass, O);
case 'x':
// Output 64-bit general register operand, constant zero as xzr, or stack
// pointer as sp. Ignored when used with other operand types.
return printModifiedGPRAsmOperand(MI->getOperand(OpNum), TRI,
AArch64::GPR64RegClass, O);
case 'H':
// Output higher numbered of a 64-bit general register pair
case 'Q':
// Output least significant register of a 64-bit general register pair
case 'R':
// Output most significant register of a 64-bit general register pair
// FIXME note: these three operand modifiers will require, to some extent,
// adding a paired GPR64 register class. Initial investigation suggests that
// assertions are hit unless it has a type and is made legal for that type
// in ISelLowering. After that step is made, the number of modifications
// needed explodes (operation legality, calling conventions, stores, reg
// copies ...).
llvm_unreachable("FIXME: Unimplemented register pairs");
case 'b':
// Output 8-bit FP/SIMD scalar register operand, prefixed with b.
return printModifiedFPRAsmOperand(MI->getOperand(OpNum), TRI,
AArch64::FPR8RegClass, O);
case 'h':
// Output 16-bit FP/SIMD scalar register operand, prefixed with h.
return printModifiedFPRAsmOperand(MI->getOperand(OpNum), TRI,
AArch64::FPR16RegClass, O);
case 's':
// Output 32-bit FP/SIMD scalar register operand, prefixed with s.
return printModifiedFPRAsmOperand(MI->getOperand(OpNum), TRI,
AArch64::FPR32RegClass, O);
case 'd':
// Output 64-bit FP/SIMD scalar register operand, prefixed with d.
return printModifiedFPRAsmOperand(MI->getOperand(OpNum), TRI,
AArch64::FPR64RegClass, O);
case 'q':
// Output 128-bit FP/SIMD scalar register operand, prefixed with q.
return printModifiedFPRAsmOperand(MI->getOperand(OpNum), TRI,
AArch64::FPR128RegClass, O);
case 'A':
// Output symbolic address with appropriate relocation modifier (also
// suitable for ADRP).
return printSymbolicAddress(MI->getOperand(OpNum), false, "", O);
case 'L':
// Output bits 11:0 of symbolic address with appropriate :lo12: relocation
// modifier.
return printSymbolicAddress(MI->getOperand(OpNum), true, "lo12", O);
case 'G':
// Output bits 23:12 of symbolic address with appropriate :hi12: relocation
// modifier (currently only for TLS local exec).
return printSymbolicAddress(MI->getOperand(OpNum), true, "hi12", O);
}
}
bool AArch64AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
unsigned OpNum,
unsigned AsmVariant,
const char *ExtraCode,
raw_ostream &O) {
// Currently both the memory constraints (m and Q) behave the same and amount
// to the address as a single register. In future, we may allow "m" to provide
// both a base and an offset.
const MachineOperand &MO = MI->getOperand(OpNum);
assert(MO.isReg() && "unexpected inline assembly memory operand");
O << '[' << AArch64InstPrinter::getRegisterName(MO.getReg()) << ']';
return false;
}
void AArch64AsmPrinter::PrintDebugValueComment(const MachineInstr *MI,
raw_ostream &OS) {
unsigned NOps = MI->getNumOperands();
assert(NOps==4);
OS << '\t' << MAI->getCommentString() << "DEBUG_VALUE: ";
// cast away const; DIetc do not take const operands for some reason.
DIVariable V(const_cast<MDNode *>(MI->getOperand(NOps-1).getMetadata()));
OS << V.getName();
OS << " <- ";
// Frame address. Currently handles register +- offset only.
assert(MI->getOperand(0).isReg() && MI->getOperand(1).isImm());
OS << '[' << AArch64InstPrinter::getRegisterName(MI->getOperand(0).getReg());
OS << '+' << MI->getOperand(1).getImm();
OS << ']';
OS << "+" << MI->getOperand(NOps - 2).getImm();
}
#include "AArch64GenMCPseudoLowering.inc"
void AArch64AsmPrinter::EmitInstruction(const MachineInstr *MI) {
// Do any auto-generated pseudo lowerings.
if (emitPseudoExpansionLowering(OutStreamer, MI))
return;
switch (MI->getOpcode()) {
case AArch64::CONSTPOOL_ENTRY: {
unsigned LabelId = (unsigned)MI->getOperand(0).getImm();
unsigned CPIdx = (unsigned)MI->getOperand(1).getIndex();
OutStreamer.EmitLabel(GetCPISymbol(LabelId));
const MachineConstantPoolEntry &MCPE = MCP->getConstants()[CPIdx];
if (MCPE.isMachineConstantPoolEntry())
EmitMachineConstantPoolValue(MCPE.Val.MachineCPVal);
else
EmitGlobalConstant(MCPE.Val.ConstVal);
return;
}
case AArch64::DBG_VALUE: {
if (isVerbose() && OutStreamer.hasRawTextSupport()) {
SmallString<128> TmpStr;
raw_svector_ostream OS(TmpStr);
PrintDebugValueComment(MI, OS);
OutStreamer.EmitRawText(StringRef(OS.str()));
}
return;
}
}
MCInst TmpInst;
LowerAArch64MachineInstrToMCInst(MI, TmpInst, *this);
OutStreamer.EmitInstruction(TmpInst);
}
void AArch64AsmPrinter::EmitEndOfAsmFile(Module &M) {
if (Subtarget->isTargetELF()) {
const TargetLoweringObjectFileELF &TLOFELF =
static_cast<const TargetLoweringObjectFileELF &>(getObjFileLowering());
MachineModuleInfoELF &MMIELF = MMI->getObjFileInfo<MachineModuleInfoELF>();
// Output stubs for external and common global variables.
MachineModuleInfoELF::SymbolListTy Stubs = MMIELF.GetGVStubList();
if (!Stubs.empty()) {
OutStreamer.SwitchSection(TLOFELF.getDataRelSection());
const DataLayout *TD = TM.getDataLayout();
for (unsigned i = 0, e = Stubs.size(); i != e; ++i) {
OutStreamer.EmitLabel(Stubs[i].first);
OutStreamer.EmitSymbolValue(Stubs[i].second.getPointer(),
TD->getPointerSize(0), 0);
}
Stubs.clear();
}
}
}
bool AArch64AsmPrinter::runOnMachineFunction(MachineFunction &MF) {
MCP = MF.getConstantPool();
return AsmPrinter::runOnMachineFunction(MF);
}
// Force static initialization.
extern "C" void LLVMInitializeAArch64AsmPrinter() {
RegisterAsmPrinter<AArch64AsmPrinter> X(TheAArch64Target);
}