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 | |
24 | using 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 | |
32 | void 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 | |
57 | bool LoongArchAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, |
58 | const char *, |
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 | |
113 | bool LoongArchAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, |
114 | unsigned OpNo, |
115 | const char *, |
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 | |
141 | void 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 | |
157 | void LoongArchAsmPrinter::LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI) { |
158 | emitSled(MI, Kind: SledKind::FUNCTION_EXIT); |
159 | } |
160 | |
161 | void LoongArchAsmPrinter::LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI) { |
162 | emitSled(MI, Kind: SledKind::TAIL_CALL); |
163 | } |
164 | |
165 | void 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 | |
190 | bool 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. |
198 | extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeLoongArchAsmPrinter() { |
199 | RegisterAsmPrinter<LoongArchAsmPrinter> X(getTheLoongArch32Target()); |
200 | RegisterAsmPrinter<LoongArchAsmPrinter> Y(getTheLoongArch64Target()); |
201 | } |
202 | |