aboutsummaryrefslogtreecommitdiff
path: root/lib/Target/AArch64/AArch64AsmPrinter.cpp
blob: 47ebb826e0d0024b6c0b6df6f5d2695810696843 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
//===-- 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/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::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) {
  return AsmPrinter::runOnMachineFunction(MF);
}

// Force static initialization.
extern "C" void LLVMInitializeAArch64AsmPrinter() {
    RegisterAsmPrinter<AArch64AsmPrinter> X(TheAArch64Target);
}