1//===- LoongArchAsmPrinter.cpp - LoongArch LLVM Assembly Printer -*- C++ -*--=//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file contains a printer that converts from our internal representation
10// of machine-dependent LLVM code to GAS-format LoongArch assembly language.
11//
12//===----------------------------------------------------------------------===//
13
14#include "LoongArchAsmPrinter.h"
15#include "LoongArch.h"
16#include "LoongArchTargetMachine.h"
17#include "MCTargetDesc/LoongArchInstPrinter.h"
18#include "TargetInfo/LoongArchTargetInfo.h"
19#include "llvm/CodeGen/AsmPrinter.h"
20#include "llvm/MC/MCContext.h"
21#include "llvm/MC/MCInstBuilder.h"
22#include "llvm/MC/TargetRegistry.h"
23
24using namespace llvm;
25
26#define DEBUG_TYPE "loongarch-asm-printer"
27
28// Simple pseudo-instructions have their lowering (with expansion to real
29// instructions) auto-generated.
30#include "LoongArchGenMCPseudoLowering.inc"
31
32void LoongArchAsmPrinter::emitInstruction(const MachineInstr *MI) {
33 LoongArch_MC::verifyInstructionPredicates(
34 Opcode: MI->getOpcode(), Features: getSubtargetInfo().getFeatureBits());
35
36 // Do any auto-generated pseudo lowerings.
37 if (emitPseudoExpansionLowering(OutStreamer&: *OutStreamer, MI))
38 return;
39
40 switch (MI->getOpcode()) {
41 case TargetOpcode::PATCHABLE_FUNCTION_ENTER:
42 LowerPATCHABLE_FUNCTION_ENTER(MI: *MI);
43 return;
44 case TargetOpcode::PATCHABLE_FUNCTION_EXIT:
45 LowerPATCHABLE_FUNCTION_EXIT(MI: *MI);
46 return;
47 case TargetOpcode::PATCHABLE_TAIL_CALL:
48 LowerPATCHABLE_TAIL_CALL(MI: *MI);
49 return;
50 }
51
52 MCInst TmpInst;
53 if (!lowerLoongArchMachineInstrToMCInst(MI, OutMI&: TmpInst, AP&: *this))
54 EmitToStreamer(S&: *OutStreamer, Inst: TmpInst);
55}
56
57bool LoongArchAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
58 const char *ExtraCode,
59 raw_ostream &OS) {
60 // First try the generic code, which knows about modifiers like 'c' and 'n'.
61 if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS))
62 return false;
63
64 const MachineOperand &MO = MI->getOperand(i: OpNo);
65 if (ExtraCode && ExtraCode[0]) {
66 if (ExtraCode[1] != 0)
67 return true; // Unknown modifier.
68
69 switch (ExtraCode[0]) {
70 default:
71 return true; // Unknown modifier.
72 case 'z': // Print $zero register if zero, regular printing otherwise.
73 if (MO.isImm() && MO.getImm() == 0) {
74 OS << '$' << LoongArchInstPrinter::getRegisterName(Reg: LoongArch::R0);
75 return false;
76 }
77 break;
78 case 'w': // Print LSX registers.
79 if (MO.getReg().id() >= LoongArch::VR0 &&
80 MO.getReg().id() <= LoongArch::VR31)
81 break;
82 // The modifier is 'w' but the operand is not an LSX register; Report an
83 // unknown operand error.
84 return true;
85 case 'u': // Print LASX registers.
86 if (MO.getReg().id() >= LoongArch::XR0 &&
87 MO.getReg().id() <= LoongArch::XR31)
88 break;
89 // The modifier is 'u' but the operand is not an LASX register; Report an
90 // unknown operand error.
91 return true;
92 // TODO: handle other extra codes if any.
93 }
94 }
95
96 switch (MO.getType()) {
97 case MachineOperand::MO_Immediate:
98 OS << MO.getImm();
99 return false;
100 case MachineOperand::MO_Register:
101 OS << '$' << LoongArchInstPrinter::getRegisterName(Reg: MO.getReg());
102 return false;
103 case MachineOperand::MO_GlobalAddress:
104 PrintSymbolOperand(MO, OS);
105 return false;
106 default:
107 llvm_unreachable("not implemented");
108 }
109
110 return true;
111}
112
113bool LoongArchAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
114 unsigned OpNo,
115 const char *ExtraCode,
116 raw_ostream &OS) {
117 // TODO: handle extra code.
118 if (ExtraCode)
119 return true;
120
121 // We only support memory operands like "Base + Offset", where base must be a
122 // register, and offset can be a register or an immediate value.
123 const MachineOperand &BaseMO = MI->getOperand(i: OpNo);
124 // Base address must be a register.
125 if (!BaseMO.isReg())
126 return true;
127 // Print the base address register.
128 OS << "$" << LoongArchInstPrinter::getRegisterName(Reg: BaseMO.getReg());
129 // Print the offset operand.
130 const MachineOperand &OffsetMO = MI->getOperand(i: OpNo + 1);
131 if (OffsetMO.isReg())
132 OS << ", $" << LoongArchInstPrinter::getRegisterName(Reg: OffsetMO.getReg());
133 else if (OffsetMO.isImm())
134 OS << ", " << OffsetMO.getImm();
135 else
136 return true;
137
138 return false;
139}
140
141void LoongArchAsmPrinter::LowerPATCHABLE_FUNCTION_ENTER(
142 const MachineInstr &MI) {
143 const Function &F = MF->getFunction();
144 if (F.hasFnAttribute(Kind: "patchable-function-entry")) {
145 unsigned Num;
146 if (F.getFnAttribute(Kind: "patchable-function-entry")
147 .getValueAsString()
148 .getAsInteger(Radix: 10, Result&: Num))
149 return;
150 emitNops(N: Num);
151 return;
152 }
153
154 emitSled(MI, Kind: SledKind::FUNCTION_ENTER);
155}
156
157void LoongArchAsmPrinter::LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI) {
158 emitSled(MI, Kind: SledKind::FUNCTION_EXIT);
159}
160
161void LoongArchAsmPrinter::LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI) {
162 emitSled(MI, Kind: SledKind::TAIL_CALL);
163}
164
165void LoongArchAsmPrinter::emitSled(const MachineInstr &MI, SledKind Kind) {
166 // For loongarch64 we want to emit the following pattern:
167 //
168 // .Lxray_sled_beginN:
169 // B .Lxray_sled_endN
170 // 11 NOPs (44 bytes)
171 // .Lxray_sled_endN:
172 //
173 // We need the extra bytes because at runtime they may be used for the
174 // actual pattern defined at compiler-rt/lib/xray/xray_loongarch64.cpp.
175 // The count here should be adjusted accordingly if the implementation
176 // changes.
177 const int8_t NoopsInSledCount = 11;
178 OutStreamer->emitCodeAlignment(Alignment: Align(4), STI: &getSubtargetInfo());
179 MCSymbol *BeginOfSled = OutContext.createTempSymbol(Name: "xray_sled_begin");
180 MCSymbol *EndOfSled = OutContext.createTempSymbol(Name: "xray_sled_end");
181 OutStreamer->emitLabel(Symbol: BeginOfSled);
182 EmitToStreamer(S&: *OutStreamer,
183 Inst: MCInstBuilder(LoongArch::B)
184 .addExpr(Val: MCSymbolRefExpr::create(Symbol: EndOfSled, Ctx&: OutContext)));
185 emitNops(N: NoopsInSledCount);
186 OutStreamer->emitLabel(Symbol: EndOfSled);
187 recordSled(Sled: BeginOfSled, MI, Kind, Version: 2);
188}
189
190bool LoongArchAsmPrinter::runOnMachineFunction(MachineFunction &MF) {
191 AsmPrinter::runOnMachineFunction(MF);
192 // Emit the XRay table for this function.
193 emitXRayTable();
194 return true;
195}
196
197// Force static initialization.
198extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeLoongArchAsmPrinter() {
199 RegisterAsmPrinter<LoongArchAsmPrinter> X(getTheLoongArch32Target());
200 RegisterAsmPrinter<LoongArchAsmPrinter> Y(getTheLoongArch64Target());
201}
202