blob: a4b999e6aa3b322252b2877ca27a59748dd3f639 [file] [log] [blame]
//===-- 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);
}
}