mirror of
https://github.com/RPCS3/llvm.git
synced 2025-05-23 05:46:05 +00:00

Add support for the new relocations and linking metadata section support in https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md. In particular, this allows LLVM to indicate which variable is the stack pointer, so that it can be linked with other objects. This also adds support for emitting type relocations for call_indirect instructions. Right now, this is mainly tested by using wabt and hexdump to examine the output on selected testcases. We'll add more tests as the design stablizes and more of the pieces are in place. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@299141 91177308-0d34-0410-b5e6-96231b3b80d8
288 lines
10 KiB
C++
288 lines
10 KiB
C++
//===-- WebAssemblyAsmPrinter.cpp - WebAssembly LLVM assembly writer ------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file
|
|
/// \brief This file contains a printer that converts from our internal
|
|
/// representation of machine-dependent LLVM code to the WebAssembly assembly
|
|
/// language.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "WebAssemblyAsmPrinter.h"
|
|
#include "InstPrinter/WebAssemblyInstPrinter.h"
|
|
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
|
|
#include "MCTargetDesc/WebAssemblyTargetStreamer.h"
|
|
#include "WebAssembly.h"
|
|
#include "WebAssemblyMCInstLower.h"
|
|
#include "WebAssemblyMachineFunctionInfo.h"
|
|
#include "WebAssemblyRegisterInfo.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/CodeGen/Analysis.h"
|
|
#include "llvm/CodeGen/AsmPrinter.h"
|
|
#include "llvm/CodeGen/MachineConstantPool.h"
|
|
#include "llvm/CodeGen/MachineInstr.h"
|
|
#include "llvm/CodeGen/MachineModuleInfoImpls.h"
|
|
#include "llvm/IR/DataLayout.h"
|
|
#include "llvm/IR/GlobalVariable.h"
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/MC/MCStreamer.h"
|
|
#include "llvm/MC/MCSymbol.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/TargetRegistry.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
using namespace llvm;
|
|
|
|
#define DEBUG_TYPE "asm-printer"
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Helpers.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
MVT WebAssemblyAsmPrinter::getRegType(unsigned RegNo) const {
|
|
const TargetRegisterClass *TRC = MRI->getRegClass(RegNo);
|
|
for (MVT T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64, MVT::v16i8, MVT::v8i16,
|
|
MVT::v4i32, MVT::v4f32})
|
|
if (TRC->hasType(T))
|
|
return T;
|
|
DEBUG(errs() << "Unknown type for register number: " << RegNo);
|
|
llvm_unreachable("Unknown register type");
|
|
return MVT::Other;
|
|
}
|
|
|
|
std::string WebAssemblyAsmPrinter::regToString(const MachineOperand &MO) {
|
|
unsigned RegNo = MO.getReg();
|
|
assert(TargetRegisterInfo::isVirtualRegister(RegNo) &&
|
|
"Unlowered physical register encountered during assembly printing");
|
|
assert(!MFI->isVRegStackified(RegNo));
|
|
unsigned WAReg = MFI->getWAReg(RegNo);
|
|
assert(WAReg != WebAssemblyFunctionInfo::UnusedReg);
|
|
return '$' + utostr(WAReg);
|
|
}
|
|
|
|
WebAssemblyTargetStreamer *WebAssemblyAsmPrinter::getTargetStreamer() {
|
|
MCTargetStreamer *TS = OutStreamer->getTargetStreamer();
|
|
return static_cast<WebAssemblyTargetStreamer *>(TS);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// WebAssemblyAsmPrinter Implementation.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) {
|
|
for (const auto &F : M) {
|
|
// Emit function type info for all undefined functions
|
|
if (F.isDeclarationForLinker() && !F.isIntrinsic()) {
|
|
SmallVector<MVT, 4> Results;
|
|
SmallVector<MVT, 4> Params;
|
|
ComputeSignatureVTs(F, TM, Params, Results);
|
|
getTargetStreamer()->emitIndirectFunctionType(F.getName(), Params,
|
|
Results);
|
|
}
|
|
}
|
|
for (const auto &G : M.globals()) {
|
|
if (!G.hasInitializer() && G.hasExternalLinkage()) {
|
|
uint16_t Size = M.getDataLayout().getTypeAllocSize(G.getValueType());
|
|
getTargetStreamer()->emitGlobalImport(G.getGlobalIdentifier());
|
|
OutStreamer->emitELFSize(getSymbol(&G),
|
|
MCConstantExpr::create(Size, OutContext));
|
|
}
|
|
}
|
|
|
|
if (!TM.getTargetTriple().isOSBinFormatELF()) {
|
|
MachineModuleInfoWasm &MMIW = MMI->getObjFileInfo<MachineModuleInfoWasm>();
|
|
getTargetStreamer()->emitGlobal(MMIW.getGlobals());
|
|
if (MMIW.hasStackPointerGlobal())
|
|
getTargetStreamer()->emitStackPointer(MMIW.getStackPointerGlobal());
|
|
}
|
|
}
|
|
|
|
void WebAssemblyAsmPrinter::EmitConstantPool() {
|
|
assert(MF->getConstantPool()->getConstants().empty() &&
|
|
"WebAssembly disables constant pools");
|
|
}
|
|
|
|
void WebAssemblyAsmPrinter::EmitJumpTableInfo() {
|
|
// Nothing to do; jump tables are incorporated into the instruction stream.
|
|
}
|
|
|
|
void WebAssemblyAsmPrinter::EmitFunctionBodyStart() {
|
|
getTargetStreamer()->emitParam(CurrentFnSym, MFI->getParams());
|
|
|
|
SmallVector<MVT, 4> ResultVTs;
|
|
const Function &F(*MF->getFunction());
|
|
|
|
// Emit the function index.
|
|
if (MDNode *Idx = F.getMetadata("wasm.index")) {
|
|
assert(Idx->getNumOperands() == 1);
|
|
|
|
getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant(
|
|
cast<ConstantAsMetadata>(Idx->getOperand(0))->getValue()));
|
|
}
|
|
|
|
ComputeLegalValueVTs(F, TM, F.getReturnType(), ResultVTs);
|
|
|
|
// If the return type needs to be legalized it will get converted into
|
|
// passing a pointer.
|
|
if (ResultVTs.size() == 1)
|
|
getTargetStreamer()->emitResult(CurrentFnSym, ResultVTs);
|
|
else
|
|
getTargetStreamer()->emitResult(CurrentFnSym, ArrayRef<MVT>());
|
|
|
|
if (TM.getTargetTriple().isOSBinFormatELF()) {
|
|
assert(MFI->getLocals().empty());
|
|
for (unsigned Idx = 0, IdxE = MRI->getNumVirtRegs(); Idx != IdxE; ++Idx) {
|
|
unsigned VReg = TargetRegisterInfo::index2VirtReg(Idx);
|
|
unsigned WAReg = MFI->getWAReg(VReg);
|
|
// Don't declare unused registers.
|
|
if (WAReg == WebAssemblyFunctionInfo::UnusedReg)
|
|
continue;
|
|
// Don't redeclare parameters.
|
|
if (WAReg < MFI->getParams().size())
|
|
continue;
|
|
// Don't declare stackified registers.
|
|
if (int(WAReg) < 0)
|
|
continue;
|
|
MFI->addLocal(getRegType(VReg));
|
|
}
|
|
}
|
|
|
|
getTargetStreamer()->emitLocal(MFI->getLocals());
|
|
|
|
AsmPrinter::EmitFunctionBodyStart();
|
|
}
|
|
|
|
void WebAssemblyAsmPrinter::EmitFunctionBodyEnd() {
|
|
if (TM.getTargetTriple().isOSBinFormatELF())
|
|
getTargetStreamer()->emitEndFunc();
|
|
}
|
|
|
|
void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) {
|
|
DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n');
|
|
|
|
switch (MI->getOpcode()) {
|
|
case WebAssembly::ARGUMENT_I32:
|
|
case WebAssembly::ARGUMENT_I64:
|
|
case WebAssembly::ARGUMENT_F32:
|
|
case WebAssembly::ARGUMENT_F64:
|
|
case WebAssembly::ARGUMENT_v16i8:
|
|
case WebAssembly::ARGUMENT_v8i16:
|
|
case WebAssembly::ARGUMENT_v4i32:
|
|
case WebAssembly::ARGUMENT_v4f32:
|
|
// These represent values which are live into the function entry, so there's
|
|
// no instruction to emit.
|
|
break;
|
|
case WebAssembly::FALLTHROUGH_RETURN_I32:
|
|
case WebAssembly::FALLTHROUGH_RETURN_I64:
|
|
case WebAssembly::FALLTHROUGH_RETURN_F32:
|
|
case WebAssembly::FALLTHROUGH_RETURN_F64:
|
|
case WebAssembly::FALLTHROUGH_RETURN_v16i8:
|
|
case WebAssembly::FALLTHROUGH_RETURN_v8i16:
|
|
case WebAssembly::FALLTHROUGH_RETURN_v4i32:
|
|
case WebAssembly::FALLTHROUGH_RETURN_v4f32: {
|
|
// These instructions represent the implicit return at the end of a
|
|
// function body. The operand is always a pop.
|
|
assert(MFI->isVRegStackified(MI->getOperand(0).getReg()));
|
|
|
|
if (isVerbose()) {
|
|
OutStreamer->AddComment("fallthrough-return: $pop" +
|
|
utostr(MFI->getWARegStackId(
|
|
MFI->getWAReg(MI->getOperand(0).getReg()))));
|
|
OutStreamer->AddBlankLine();
|
|
}
|
|
break;
|
|
}
|
|
case WebAssembly::FALLTHROUGH_RETURN_VOID:
|
|
// This instruction represents the implicit return at the end of a
|
|
// function body with no return value.
|
|
if (isVerbose()) {
|
|
OutStreamer->AddComment("fallthrough-return");
|
|
OutStreamer->AddBlankLine();
|
|
}
|
|
break;
|
|
default: {
|
|
WebAssemblyMCInstLower MCInstLowering(OutContext, *this);
|
|
MCInst TmpInst;
|
|
MCInstLowering.Lower(MI, TmpInst);
|
|
EmitToStreamer(*OutStreamer, TmpInst);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const MCExpr *WebAssemblyAsmPrinter::lowerConstant(const Constant *CV) {
|
|
if (const GlobalValue *GV = dyn_cast<GlobalValue>(CV))
|
|
if (GV->getValueType()->isFunctionTy())
|
|
return MCSymbolRefExpr::create(
|
|
getSymbol(GV), MCSymbolRefExpr::VK_WebAssembly_FUNCTION, OutContext);
|
|
return AsmPrinter::lowerConstant(CV);
|
|
}
|
|
|
|
bool WebAssemblyAsmPrinter::PrintAsmOperand(const MachineInstr *MI,
|
|
unsigned OpNo, unsigned AsmVariant,
|
|
const char *ExtraCode,
|
|
raw_ostream &OS) {
|
|
if (AsmVariant != 0)
|
|
report_fatal_error("There are no defined alternate asm variants");
|
|
|
|
// First try the generic code, which knows about modifiers like 'c' and 'n'.
|
|
if (!AsmPrinter::PrintAsmOperand(MI, OpNo, AsmVariant, ExtraCode, OS))
|
|
return false;
|
|
|
|
if (!ExtraCode) {
|
|
const MachineOperand &MO = MI->getOperand(OpNo);
|
|
switch (MO.getType()) {
|
|
case MachineOperand::MO_Immediate:
|
|
OS << MO.getImm();
|
|
return false;
|
|
case MachineOperand::MO_Register:
|
|
OS << regToString(MO);
|
|
return false;
|
|
case MachineOperand::MO_GlobalAddress:
|
|
getSymbol(MO.getGlobal())->print(OS, MAI);
|
|
printOffset(MO.getOffset(), OS);
|
|
return false;
|
|
case MachineOperand::MO_ExternalSymbol:
|
|
GetExternalSymbolSymbol(MO.getSymbolName())->print(OS, MAI);
|
|
printOffset(MO.getOffset(), OS);
|
|
return false;
|
|
case MachineOperand::MO_MachineBasicBlock:
|
|
MO.getMBB()->getSymbol()->print(OS, MAI);
|
|
return false;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool WebAssemblyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
|
|
unsigned OpNo,
|
|
unsigned AsmVariant,
|
|
const char *ExtraCode,
|
|
raw_ostream &OS) {
|
|
if (AsmVariant != 0)
|
|
report_fatal_error("There are no defined alternate asm variants");
|
|
|
|
if (!ExtraCode) {
|
|
// TODO: For now, we just hard-code 0 as the constant offset; teach
|
|
// SelectInlineAsmMemoryOperand how to do address mode matching.
|
|
OS << "0(" + regToString(MI->getOperand(OpNo)) + ')';
|
|
return false;
|
|
}
|
|
|
|
return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, AsmVariant, ExtraCode, OS);
|
|
}
|
|
|
|
// Force static initialization.
|
|
extern "C" void LLVMInitializeWebAssemblyAsmPrinter() {
|
|
RegisterAsmPrinter<WebAssemblyAsmPrinter> X(getTheWebAssemblyTarget32());
|
|
RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(getTheWebAssemblyTarget64());
|
|
}
|