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 "LoongArchMachineFunctionInfo.h" 17 #include "MCTargetDesc/LoongArchInstPrinter.h" 18 #include "MCTargetDesc/LoongArchMCTargetDesc.h" 19 #include "TargetInfo/LoongArchTargetInfo.h" 20 #include "llvm/CodeGen/AsmPrinter.h" 21 #include "llvm/CodeGen/MachineJumpTableInfo.h" 22 #include "llvm/CodeGen/MachineModuleInfoImpls.h" 23 #include "llvm/MC/MCContext.h" 24 #include "llvm/MC/MCInstBuilder.h" 25 #include "llvm/MC/MCSectionELF.h" 26 #include "llvm/MC/TargetRegistry.h" 27 28 using namespace llvm; 29 30 #define DEBUG_TYPE "loongarch-asm-printer" 31 32 cl::opt<bool> LArchAnnotateTableJump( 33 "loongarch-annotate-tablejump", cl::Hidden, 34 cl::desc( 35 "Annotate table jump instruction to correlate it with the jump table."), 36 cl::init(false)); 37 38 // Simple pseudo-instructions have their lowering (with expansion to real 39 // instructions) auto-generated. 40 #include "LoongArchGenMCPseudoLowering.inc" 41 42 void LoongArchAsmPrinter::emitInstruction(const MachineInstr *MI) { 43 LoongArch_MC::verifyInstructionPredicates( 44 MI->getOpcode(), getSubtargetInfo().getFeatureBits()); 45 46 // Do any auto-generated pseudo lowerings. 47 if (MCInst OutInst; lowerPseudoInstExpansion(MI, OutInst)) { 48 EmitToStreamer(*OutStreamer, OutInst); 49 return; 50 } 51 52 switch (MI->getOpcode()) { 53 case TargetOpcode::STATEPOINT: 54 LowerSTATEPOINT(*MI); 55 return; 56 case TargetOpcode::PATCHABLE_FUNCTION_ENTER: 57 LowerPATCHABLE_FUNCTION_ENTER(*MI); 58 return; 59 case TargetOpcode::PATCHABLE_FUNCTION_EXIT: 60 LowerPATCHABLE_FUNCTION_EXIT(*MI); 61 return; 62 case TargetOpcode::PATCHABLE_TAIL_CALL: 63 LowerPATCHABLE_TAIL_CALL(*MI); 64 return; 65 } 66 67 MCInst TmpInst; 68 if (!lowerLoongArchMachineInstrToMCInst(MI, TmpInst, *this)) 69 EmitToStreamer(*OutStreamer, TmpInst); 70 } 71 72 bool LoongArchAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, 73 const char *ExtraCode, 74 raw_ostream &OS) { 75 // First try the generic code, which knows about modifiers like 'c' and 'n'. 76 if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS)) 77 return false; 78 79 const MachineOperand &MO = MI->getOperand(OpNo); 80 if (ExtraCode && ExtraCode[0]) { 81 if (ExtraCode[1] != 0) 82 return true; // Unknown modifier. 83 84 switch (ExtraCode[0]) { 85 default: 86 return true; // Unknown modifier. 87 case 'z': // Print $zero register if zero, regular printing otherwise. 88 if (MO.isImm() && MO.getImm() == 0) { 89 OS << '$' << LoongArchInstPrinter::getRegisterName(LoongArch::R0); 90 return false; 91 } 92 break; 93 case 'w': // Print LSX registers. 94 if (MO.getReg().id() >= LoongArch::VR0 && 95 MO.getReg().id() <= LoongArch::VR31) 96 break; 97 // The modifier is 'w' but the operand is not an LSX register; Report an 98 // unknown operand error. 99 return true; 100 case 'u': // Print LASX registers. 101 if (MO.getReg().id() >= LoongArch::XR0 && 102 MO.getReg().id() <= LoongArch::XR31) 103 break; 104 // The modifier is 'u' but the operand is not an LASX register; Report an 105 // unknown operand error. 106 return true; 107 // TODO: handle other extra codes if any. 108 } 109 } 110 111 switch (MO.getType()) { 112 case MachineOperand::MO_Immediate: 113 OS << MO.getImm(); 114 return false; 115 case MachineOperand::MO_Register: 116 OS << '$' << LoongArchInstPrinter::getRegisterName(MO.getReg()); 117 return false; 118 case MachineOperand::MO_GlobalAddress: 119 PrintSymbolOperand(MO, OS); 120 return false; 121 default: 122 llvm_unreachable("not implemented"); 123 } 124 125 return true; 126 } 127 128 bool LoongArchAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, 129 unsigned OpNo, 130 const char *ExtraCode, 131 raw_ostream &OS) { 132 // TODO: handle extra code. 133 if (ExtraCode) 134 return true; 135 136 // We only support memory operands like "Base + Offset", where base must be a 137 // register, and offset can be a register or an immediate value. 138 const MachineOperand &BaseMO = MI->getOperand(OpNo); 139 // Base address must be a register. 140 if (!BaseMO.isReg()) 141 return true; 142 // Print the base address register. 143 OS << "$" << LoongArchInstPrinter::getRegisterName(BaseMO.getReg()); 144 // Print the offset operand. 145 const MachineOperand &OffsetMO = MI->getOperand(OpNo + 1); 146 MCOperand MCO; 147 if (!lowerOperand(OffsetMO, MCO)) 148 return true; 149 if (OffsetMO.isReg()) 150 OS << ", $" << LoongArchInstPrinter::getRegisterName(OffsetMO.getReg()); 151 else if (OffsetMO.isImm()) 152 OS << ", " << OffsetMO.getImm(); 153 else if (OffsetMO.isGlobal() || OffsetMO.isBlockAddress() || 154 OffsetMO.isMCSymbol()) 155 OS << ", " << *MCO.getExpr(); 156 else 157 return true; 158 159 return false; 160 } 161 162 void LoongArchAsmPrinter::LowerSTATEPOINT(const MachineInstr &MI) { 163 StatepointOpers SOpers(&MI); 164 if (unsigned PatchBytes = SOpers.getNumPatchBytes()) { 165 assert(PatchBytes % 4 == 0 && "Invalid number of NOP bytes requested!"); 166 emitNops(PatchBytes / 4); 167 } else { 168 // Lower call target and choose correct opcode. 169 const MachineOperand &CallTarget = SOpers.getCallTarget(); 170 MCOperand CallTargetMCOp; 171 switch (CallTarget.getType()) { 172 case MachineOperand::MO_GlobalAddress: 173 case MachineOperand::MO_ExternalSymbol: 174 lowerOperand(CallTarget, CallTargetMCOp); 175 EmitToStreamer(*OutStreamer, 176 MCInstBuilder(LoongArch::BL).addOperand(CallTargetMCOp)); 177 break; 178 case MachineOperand::MO_Immediate: 179 CallTargetMCOp = MCOperand::createImm(CallTarget.getImm()); 180 EmitToStreamer(*OutStreamer, 181 MCInstBuilder(LoongArch::BL).addOperand(CallTargetMCOp)); 182 break; 183 case MachineOperand::MO_Register: 184 CallTargetMCOp = MCOperand::createReg(CallTarget.getReg()); 185 EmitToStreamer(*OutStreamer, MCInstBuilder(LoongArch::JIRL) 186 .addReg(LoongArch::R1) 187 .addOperand(CallTargetMCOp) 188 .addImm(0)); 189 break; 190 default: 191 llvm_unreachable("Unsupported operand type in statepoint call target"); 192 break; 193 } 194 } 195 196 auto &Ctx = OutStreamer->getContext(); 197 MCSymbol *MILabel = Ctx.createTempSymbol(); 198 OutStreamer->emitLabel(MILabel); 199 SM.recordStatepoint(*MILabel, MI); 200 } 201 202 void LoongArchAsmPrinter::LowerPATCHABLE_FUNCTION_ENTER( 203 const MachineInstr &MI) { 204 const Function &F = MF->getFunction(); 205 if (F.hasFnAttribute("patchable-function-entry")) { 206 unsigned Num; 207 if (F.getFnAttribute("patchable-function-entry") 208 .getValueAsString() 209 .getAsInteger(10, Num)) 210 return; 211 emitNops(Num); 212 return; 213 } 214 215 emitSled(MI, SledKind::FUNCTION_ENTER); 216 } 217 218 void LoongArchAsmPrinter::LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI) { 219 emitSled(MI, SledKind::FUNCTION_EXIT); 220 } 221 222 void LoongArchAsmPrinter::LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI) { 223 emitSled(MI, SledKind::TAIL_CALL); 224 } 225 226 void LoongArchAsmPrinter::emitSled(const MachineInstr &MI, SledKind Kind) { 227 // For loongarch64 we want to emit the following pattern: 228 // 229 // .Lxray_sled_beginN: 230 // B .Lxray_sled_endN 231 // 11 NOPs (44 bytes) 232 // .Lxray_sled_endN: 233 // 234 // We need the extra bytes because at runtime they may be used for the 235 // actual pattern defined at compiler-rt/lib/xray/xray_loongarch64.cpp. 236 // The count here should be adjusted accordingly if the implementation 237 // changes. 238 const int8_t NoopsInSledCount = 11; 239 OutStreamer->emitCodeAlignment(Align(4), &getSubtargetInfo()); 240 MCSymbol *BeginOfSled = OutContext.createTempSymbol("xray_sled_begin"); 241 MCSymbol *EndOfSled = OutContext.createTempSymbol("xray_sled_end"); 242 OutStreamer->emitLabel(BeginOfSled); 243 EmitToStreamer(*OutStreamer, 244 MCInstBuilder(LoongArch::B) 245 .addExpr(MCSymbolRefExpr::create(EndOfSled, OutContext))); 246 emitNops(NoopsInSledCount); 247 OutStreamer->emitLabel(EndOfSled); 248 recordSled(BeginOfSled, MI, Kind, 2); 249 } 250 251 void LoongArchAsmPrinter::emitJumpTableInfo() { 252 AsmPrinter::emitJumpTableInfo(); 253 254 if (!LArchAnnotateTableJump) 255 return; 256 257 assert(TM.getTargetTriple().isOSBinFormatELF()); 258 259 unsigned Size = getDataLayout().getPointerSize(); 260 auto *LAFI = MF->getInfo<LoongArchMachineFunctionInfo>(); 261 unsigned EntrySize = LAFI->getJumpInfoSize(); 262 263 if (0 == EntrySize) 264 return; 265 266 // Emit an additional section to store the correlation info as pairs of 267 // addresses, each pair contains the address of a jump instruction (jr) and 268 // the address of the jump table. 269 OutStreamer->switchSection(MMI->getContext().getELFSection( 270 ".discard.tablejump_annotate", ELF::SHT_PROGBITS, 0)); 271 272 for (unsigned Idx = 0; Idx < EntrySize; ++Idx) { 273 OutStreamer->emitValue( 274 MCSymbolRefExpr::create(LAFI->getJumpInfoJrMI(Idx)->getPreInstrSymbol(), 275 OutContext), 276 Size); 277 OutStreamer->emitValue( 278 MCSymbolRefExpr::create( 279 GetJTISymbol(LAFI->getJumpInfoJTIMO(Idx)->getIndex()), OutContext), 280 Size); 281 } 282 } 283 284 bool LoongArchAsmPrinter::runOnMachineFunction(MachineFunction &MF) { 285 AsmPrinter::runOnMachineFunction(MF); 286 // Emit the XRay table for this function. 287 emitXRayTable(); 288 return true; 289 } 290 291 // Force static initialization. 292 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeLoongArchAsmPrinter() { 293 RegisterAsmPrinter<LoongArchAsmPrinter> X(getTheLoongArch32Target()); 294 RegisterAsmPrinter<LoongArchAsmPrinter> Y(getTheLoongArch64Target()); 295 } 296