| //===-- RISCVAsmPrinter.cpp - RISCV LLVM assembly writer ------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file contains a printer that converts from our internal representation | |
| // of machine-dependent LLVM code to the RISCV assembly language. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "MCTargetDesc/RISCVInstPrinter.h" | |
| #include "MCTargetDesc/RISCVMCExpr.h" | |
| #include "MCTargetDesc/RISCVTargetStreamer.h" | |
| #include "RISCV.h" | |
| #include "RISCVMachineFunctionInfo.h" | |
| #include "RISCVTargetMachine.h" | |
| #include "TargetInfo/RISCVTargetInfo.h" | |
| #include "llvm/ADT/Statistic.h" | |
| #include "llvm/BinaryFormat/ELF.h" | |
| #include "llvm/CodeGen/AsmPrinter.h" | |
| #include "llvm/CodeGen/MachineConstantPool.h" | |
| #include "llvm/CodeGen/MachineFunctionPass.h" | |
| #include "llvm/CodeGen/MachineInstr.h" | |
| #include "llvm/CodeGen/MachineModuleInfo.h" | |
| #include "llvm/MC/MCAsmInfo.h" | |
| #include "llvm/MC/MCContext.h" | |
| #include "llvm/MC/MCInst.h" | |
| #include "llvm/MC/MCInstBuilder.h" | |
| #include "llvm/MC/MCObjectFileInfo.h" | |
| #include "llvm/MC/MCSectionELF.h" | |
| #include "llvm/MC/MCStreamer.h" | |
| #include "llvm/MC/MCSymbol.h" | |
| #include "llvm/MC/TargetRegistry.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h" | |
| using namespace llvm; | |
| #define DEBUG_TYPE "asm-printer" | |
| STATISTIC(RISCVNumInstrsCompressed, | |
| "Number of RISC-V Compressed instructions emitted"); | |
| namespace { | |
| class RISCVAsmPrinter : public AsmPrinter { | |
| const RISCVSubtarget *STI; | |
| public: | |
| explicit RISCVAsmPrinter(TargetMachine &TM, | |
| std::unique_ptr<MCStreamer> Streamer) | |
| : AsmPrinter(TM, std::move(Streamer)) {} | |
| StringRef getPassName() const override { return "RISCV Assembly Printer"; } | |
| bool runOnMachineFunction(MachineFunction &MF) override; | |
| void emitInstruction(const MachineInstr *MI) override; | |
| bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, | |
| const char *ExtraCode, raw_ostream &OS) override; | |
| bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo, | |
| const char *ExtraCode, raw_ostream &OS) override; | |
| void EmitToStreamer(MCStreamer &S, const MCInst &Inst); | |
| bool emitPseudoExpansionLowering(MCStreamer &OutStreamer, | |
| const MachineInstr *MI); | |
| typedef std::tuple<unsigned, uint32_t> HwasanMemaccessTuple; | |
| std::map<HwasanMemaccessTuple, MCSymbol *> HwasanMemaccessSymbols; | |
| void LowerHWASAN_CHECK_MEMACCESS(const MachineInstr &MI); | |
| void EmitHwasanMemaccessSymbols(Module &M); | |
| // Wrapper needed for tblgenned pseudo lowering. | |
| bool lowerOperand(const MachineOperand &MO, MCOperand &MCOp) const { | |
| return lowerRISCVMachineOperandToMCOperand(MO, MCOp, *this); | |
| } | |
| void emitStartOfAsmFile(Module &M) override; | |
| void emitEndOfAsmFile(Module &M) override; | |
| void emitFunctionEntryLabel() override; | |
| private: | |
| void emitAttributes(); | |
| }; | |
| } | |
| void RISCVAsmPrinter::EmitToStreamer(MCStreamer &S, const MCInst &Inst) { | |
| MCInst CInst; | |
| bool Res = RISCVRVC::compress(CInst, Inst, *STI); | |
| if (Res) | |
| ++RISCVNumInstrsCompressed; | |
| AsmPrinter::EmitToStreamer(*OutStreamer, Res ? CInst : Inst); | |
| } | |
| // Simple pseudo-instructions have their lowering (with expansion to real | |
| // instructions) auto-generated. | |
| #include "RISCVGenMCPseudoLowering.inc" | |
| void RISCVAsmPrinter::emitInstruction(const MachineInstr *MI) { | |
| RISCV_MC::verifyInstructionPredicates(MI->getOpcode(), | |
| getSubtargetInfo().getFeatureBits()); | |
| // Do any auto-generated pseudo lowerings. | |
| if (emitPseudoExpansionLowering(*OutStreamer, MI)) | |
| return; | |
| switch (MI->getOpcode()) { | |
| case RISCV::HWASAN_CHECK_MEMACCESS_SHORTGRANULES: | |
| LowerHWASAN_CHECK_MEMACCESS(*MI); | |
| return; | |
| } | |
| MCInst TmpInst; | |
| if (!lowerRISCVMachineInstrToMCInst(MI, TmpInst, *this)) | |
| EmitToStreamer(*OutStreamer, TmpInst); | |
| } | |
| bool RISCVAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, | |
| const char *ExtraCode, raw_ostream &OS) { | |
| // First try the generic code, which knows about modifiers like 'c' and 'n'. | |
| if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS)) | |
| return false; | |
| const MachineOperand &MO = MI->getOperand(OpNo); | |
| if (ExtraCode && ExtraCode[0]) { | |
| if (ExtraCode[1] != 0) | |
| return true; // Unknown modifier. | |
| switch (ExtraCode[0]) { | |
| default: | |
| return true; // Unknown modifier. | |
| case 'z': // Print zero register if zero, regular printing otherwise. | |
| if (MO.isImm() && MO.getImm() == 0) { | |
| OS << RISCVInstPrinter::getRegisterName(RISCV::X0); | |
| return false; | |
| } | |
| break; | |
| case 'i': // Literal 'i' if operand is not a register. | |
| if (!MO.isReg()) | |
| OS << 'i'; | |
| return false; | |
| } | |
| } | |
| switch (MO.getType()) { | |
| case MachineOperand::MO_Immediate: | |
| OS << MO.getImm(); | |
| return false; | |
| case MachineOperand::MO_Register: | |
| OS << RISCVInstPrinter::getRegisterName(MO.getReg()); | |
| return false; | |
| case MachineOperand::MO_GlobalAddress: | |
| PrintSymbolOperand(MO, OS); | |
| return false; | |
| case MachineOperand::MO_BlockAddress: { | |
| MCSymbol *Sym = GetBlockAddressSymbol(MO.getBlockAddress()); | |
| Sym->print(OS, MAI); | |
| return false; | |
| } | |
| default: | |
| break; | |
| } | |
| return true; | |
| } | |
| bool RISCVAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, | |
| unsigned OpNo, | |
| const char *ExtraCode, | |
| raw_ostream &OS) { | |
| if (!ExtraCode) { | |
| const MachineOperand &MO = MI->getOperand(OpNo); | |
| // For now, we only support register memory operands in registers and | |
| // assume there is no addend | |
| if (!MO.isReg()) | |
| return true; | |
| OS << "0(" << RISCVInstPrinter::getRegisterName(MO.getReg()) << ")"; | |
| return false; | |
| } | |
| return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, ExtraCode, OS); | |
| } | |
| bool RISCVAsmPrinter::runOnMachineFunction(MachineFunction &MF) { | |
| STI = &MF.getSubtarget<RISCVSubtarget>(); | |
| SetupMachineFunction(MF); | |
| emitFunctionBody(); | |
| return false; | |
| } | |
| void RISCVAsmPrinter::emitStartOfAsmFile(Module &M) { | |
| RISCVTargetStreamer &RTS = | |
| static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer()); | |
| if (const MDString *ModuleTargetABI = | |
| dyn_cast_or_null<MDString>(M.getModuleFlag("target-abi"))) | |
| RTS.setTargetABI(RISCVABI::getTargetABI(ModuleTargetABI->getString())); | |
| if (TM.getTargetTriple().isOSBinFormatELF()) | |
| emitAttributes(); | |
| } | |
| void RISCVAsmPrinter::emitEndOfAsmFile(Module &M) { | |
| RISCVTargetStreamer &RTS = | |
| static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer()); | |
| if (TM.getTargetTriple().isOSBinFormatELF()) | |
| RTS.finishAttributeSection(); | |
| EmitHwasanMemaccessSymbols(M); | |
| } | |
| void RISCVAsmPrinter::emitAttributes() { | |
| RISCVTargetStreamer &RTS = | |
| static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer()); | |
| // Use MCSubtargetInfo from TargetMachine. Individual functions may have | |
| // attributes that differ from other functions in the module and we have no | |
| // way to know which function is correct. | |
| RTS.emitTargetAttributes(*TM.getMCSubtargetInfo()); | |
| } | |
| void RISCVAsmPrinter::emitFunctionEntryLabel() { | |
| const auto *RMFI = MF->getInfo<RISCVMachineFunctionInfo>(); | |
| if (RMFI->isVectorCall()) { | |
| auto &RTS = | |
| static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer()); | |
| RTS.emitDirectiveVariantCC(*CurrentFnSym); | |
| } | |
| return AsmPrinter::emitFunctionEntryLabel(); | |
| } | |
| // Force static initialization. | |
| extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVAsmPrinter() { | |
| RegisterAsmPrinter<RISCVAsmPrinter> X(getTheRISCV32Target()); | |
| RegisterAsmPrinter<RISCVAsmPrinter> Y(getTheRISCV64Target()); | |
| } | |
| void RISCVAsmPrinter::LowerHWASAN_CHECK_MEMACCESS(const MachineInstr &MI) { | |
| Register Reg = MI.getOperand(0).getReg(); | |
| uint32_t AccessInfo = MI.getOperand(1).getImm(); | |
| MCSymbol *&Sym = | |
| HwasanMemaccessSymbols[HwasanMemaccessTuple(Reg, AccessInfo)]; | |
| if (!Sym) { | |
| // FIXME: Make this work on non-ELF. | |
| if (!TM.getTargetTriple().isOSBinFormatELF()) | |
| report_fatal_error("llvm.hwasan.check.memaccess only supported on ELF"); | |
| std::string SymName = "__hwasan_check_x" + utostr(Reg - RISCV::X0) + "_" + | |
| utostr(AccessInfo) + "_short"; | |
| Sym = OutContext.getOrCreateSymbol(SymName); | |
| } | |
| auto Res = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, OutContext); | |
| auto Expr = RISCVMCExpr::create(Res, RISCVMCExpr::VK_RISCV_CALL, OutContext); | |
| EmitToStreamer(*OutStreamer, MCInstBuilder(RISCV::PseudoCALL).addExpr(Expr)); | |
| } | |
| void RISCVAsmPrinter::EmitHwasanMemaccessSymbols(Module &M) { | |
| if (HwasanMemaccessSymbols.empty()) | |
| return; | |
| assert(TM.getTargetTriple().isOSBinFormatELF()); | |
| // Use MCSubtargetInfo from TargetMachine. Individual functions may have | |
| // attributes that differ from other functions in the module and we have no | |
| // way to know which function is correct. | |
| const MCSubtargetInfo &MCSTI = *TM.getMCSubtargetInfo(); | |
| MCSymbol *HwasanTagMismatchV2Sym = | |
| OutContext.getOrCreateSymbol("__hwasan_tag_mismatch_v2"); | |
| // Annotate symbol as one having incompatible calling convention, so | |
| // run-time linkers can instead eagerly bind this function. | |
| auto &RTS = | |
| static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer()); | |
| RTS.emitDirectiveVariantCC(*HwasanTagMismatchV2Sym); | |
| const MCSymbolRefExpr *HwasanTagMismatchV2Ref = | |
| MCSymbolRefExpr::create(HwasanTagMismatchV2Sym, OutContext); | |
| auto Expr = RISCVMCExpr::create(HwasanTagMismatchV2Ref, | |
| RISCVMCExpr::VK_RISCV_CALL, OutContext); | |
| for (auto &P : HwasanMemaccessSymbols) { | |
| unsigned Reg = std::get<0>(P.first); | |
| uint32_t AccessInfo = std::get<1>(P.first); | |
| MCSymbol *Sym = P.second; | |
| unsigned Size = | |
| 1 << ((AccessInfo >> HWASanAccessInfo::AccessSizeShift) & 0xf); | |
| OutStreamer->switchSection(OutContext.getELFSection( | |
| ".text.hot", ELF::SHT_PROGBITS, | |
| ELF::SHF_EXECINSTR | ELF::SHF_ALLOC | ELF::SHF_GROUP, 0, Sym->getName(), | |
| /*IsComdat=*/true)); | |
| OutStreamer->emitSymbolAttribute(Sym, MCSA_ELF_TypeFunction); | |
| OutStreamer->emitSymbolAttribute(Sym, MCSA_Weak); | |
| OutStreamer->emitSymbolAttribute(Sym, MCSA_Hidden); | |
| OutStreamer->emitLabel(Sym); | |
| // Extract shadow offset from ptr | |
| OutStreamer->emitInstruction( | |
| MCInstBuilder(RISCV::SLLI).addReg(RISCV::X6).addReg(Reg).addImm(8), | |
| MCSTI); | |
| OutStreamer->emitInstruction(MCInstBuilder(RISCV::SRLI) | |
| .addReg(RISCV::X6) | |
| .addReg(RISCV::X6) | |
| .addImm(12), | |
| MCSTI); | |
| // load shadow tag in X6, X5 contains shadow base | |
| OutStreamer->emitInstruction(MCInstBuilder(RISCV::ADD) | |
| .addReg(RISCV::X6) | |
| .addReg(RISCV::X5) | |
| .addReg(RISCV::X6), | |
| MCSTI); | |
| OutStreamer->emitInstruction( | |
| MCInstBuilder(RISCV::LBU).addReg(RISCV::X6).addReg(RISCV::X6).addImm(0), | |
| MCSTI); | |
| // Extract tag from X5 and compare it with loaded tag from shadow | |
| OutStreamer->emitInstruction( | |
| MCInstBuilder(RISCV::SRLI).addReg(RISCV::X7).addReg(Reg).addImm(56), | |
| MCSTI); | |
| MCSymbol *HandleMismatchOrPartialSym = OutContext.createTempSymbol(); | |
| // X7 contains tag from memory, while X6 contains tag from the pointer | |
| OutStreamer->emitInstruction( | |
| MCInstBuilder(RISCV::BNE) | |
| .addReg(RISCV::X7) | |
| .addReg(RISCV::X6) | |
| .addExpr(MCSymbolRefExpr::create(HandleMismatchOrPartialSym, | |
| OutContext)), | |
| MCSTI); | |
| MCSymbol *ReturnSym = OutContext.createTempSymbol(); | |
| OutStreamer->emitLabel(ReturnSym); | |
| OutStreamer->emitInstruction(MCInstBuilder(RISCV::JALR) | |
| .addReg(RISCV::X0) | |
| .addReg(RISCV::X1) | |
| .addImm(0), | |
| MCSTI); | |
| OutStreamer->emitLabel(HandleMismatchOrPartialSym); | |
| OutStreamer->emitInstruction(MCInstBuilder(RISCV::ADDI) | |
| .addReg(RISCV::X28) | |
| .addReg(RISCV::X0) | |
| .addImm(16), | |
| MCSTI); | |
| MCSymbol *HandleMismatchSym = OutContext.createTempSymbol(); | |
| OutStreamer->emitInstruction( | |
| MCInstBuilder(RISCV::BGEU) | |
| .addReg(RISCV::X6) | |
| .addReg(RISCV::X28) | |
| .addExpr(MCSymbolRefExpr::create(HandleMismatchSym, OutContext)), | |
| MCSTI); | |
| OutStreamer->emitInstruction( | |
| MCInstBuilder(RISCV::ANDI).addReg(RISCV::X28).addReg(Reg).addImm(0xF), | |
| MCSTI); | |
| if (Size != 1) | |
| OutStreamer->emitInstruction(MCInstBuilder(RISCV::ADDI) | |
| .addReg(RISCV::X28) | |
| .addReg(RISCV::X28) | |
| .addImm(Size - 1), | |
| MCSTI); | |
| OutStreamer->emitInstruction( | |
| MCInstBuilder(RISCV::BGE) | |
| .addReg(RISCV::X28) | |
| .addReg(RISCV::X6) | |
| .addExpr(MCSymbolRefExpr::create(HandleMismatchSym, OutContext)), | |
| MCSTI); | |
| OutStreamer->emitInstruction( | |
| MCInstBuilder(RISCV::ORI).addReg(RISCV::X6).addReg(Reg).addImm(0xF), | |
| MCSTI); | |
| OutStreamer->emitInstruction( | |
| MCInstBuilder(RISCV::LBU).addReg(RISCV::X6).addReg(RISCV::X6).addImm(0), | |
| MCSTI); | |
| OutStreamer->emitInstruction( | |
| MCInstBuilder(RISCV::BEQ) | |
| .addReg(RISCV::X6) | |
| .addReg(RISCV::X7) | |
| .addExpr(MCSymbolRefExpr::create(ReturnSym, OutContext)), | |
| MCSTI); | |
| OutStreamer->emitLabel(HandleMismatchSym); | |
| // | Previous stack frames... | | |
| // +=================================+ <-- [SP + 256] | |
| // | ... | | |
| // | | | |
| // | Stack frame space for x12 - x31.| | |
| // | | | |
| // | ... | | |
| // +---------------------------------+ <-- [SP + 96] | |
| // | Saved x11(arg1), as | | |
| // | __hwasan_check_* clobbers it. | | |
| // +---------------------------------+ <-- [SP + 88] | |
| // | Saved x10(arg0), as | | |
| // | __hwasan_check_* clobbers it. | | |
| // +---------------------------------+ <-- [SP + 80] | |
| // | | | |
| // | Stack frame space for x9. | | |
| // +---------------------------------+ <-- [SP + 72] | |
| // | | | |
| // | Saved x8(fp), as | | |
| // | __hwasan_check_* clobbers it. | | |
| // +---------------------------------+ <-- [SP + 64] | |
| // | ... | | |
| // | | | |
| // | Stack frame space for x2 - x7. | | |
| // | | | |
| // | ... | | |
| // +---------------------------------+ <-- [SP + 16] | |
| // | Return address (x1) for caller | | |
| // | of __hwasan_check_*. | | |
| // +---------------------------------+ <-- [SP + 8] | |
| // | Reserved place for x0, possibly | | |
| // | junk, since we don't save it. | | |
| // +---------------------------------+ <-- [x2 / SP] | |
| // Adjust sp | |
| OutStreamer->emitInstruction(MCInstBuilder(RISCV::ADDI) | |
| .addReg(RISCV::X2) | |
| .addReg(RISCV::X2) | |
| .addImm(-256), | |
| MCSTI); | |
| // store x10(arg0) by new sp | |
| OutStreamer->emitInstruction(MCInstBuilder(RISCV::SD) | |
| .addReg(RISCV::X10) | |
| .addReg(RISCV::X2) | |
| .addImm(8 * 10), | |
| MCSTI); | |
| // store x11(arg1) by new sp | |
| OutStreamer->emitInstruction(MCInstBuilder(RISCV::SD) | |
| .addReg(RISCV::X11) | |
| .addReg(RISCV::X2) | |
| .addImm(8 * 11), | |
| MCSTI); | |
| // store x8(fp) by new sp | |
| OutStreamer->emitInstruction( | |
| MCInstBuilder(RISCV::SD).addReg(RISCV::X8).addReg(RISCV::X2).addImm(8 * | |
| 8), | |
| MCSTI); | |
| // store x1(ra) by new sp | |
| OutStreamer->emitInstruction( | |
| MCInstBuilder(RISCV::SD).addReg(RISCV::X1).addReg(RISCV::X2).addImm(1 * | |
| 8), | |
| MCSTI); | |
| if (Reg != RISCV::X10) | |
| OutStreamer->emitInstruction(MCInstBuilder(RISCV::ADDI) | |
| .addReg(RISCV::X10) | |
| .addReg(Reg) | |
| .addImm(0), | |
| MCSTI); | |
| OutStreamer->emitInstruction( | |
| MCInstBuilder(RISCV::ADDI) | |
| .addReg(RISCV::X11) | |
| .addReg(RISCV::X0) | |
| .addImm(AccessInfo & HWASanAccessInfo::RuntimeMask), | |
| MCSTI); | |
| OutStreamer->emitInstruction(MCInstBuilder(RISCV::PseudoCALL).addExpr(Expr), | |
| MCSTI); | |
| } | |
| } |