WebAssemblyAsmPrinter.cpp revision 314564
1//===-- WebAssemblyAsmPrinter.cpp - WebAssembly LLVM assembly writer ------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9/// 10/// \file 11/// \brief This file contains a printer that converts from our internal 12/// representation of machine-dependent LLVM code to the WebAssembly assembly 13/// language. 14/// 15//===----------------------------------------------------------------------===// 16 17#include "InstPrinter/WebAssemblyInstPrinter.h" 18#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 19#include "MCTargetDesc/WebAssemblyTargetStreamer.h" 20#include "WebAssembly.h" 21#include "WebAssemblyMCInstLower.h" 22#include "WebAssemblyMachineFunctionInfo.h" 23#include "WebAssemblyRegisterInfo.h" 24#include "WebAssemblySubtarget.h" 25#include "llvm/ADT/StringExtras.h" 26#include "llvm/CodeGen/Analysis.h" 27#include "llvm/CodeGen/AsmPrinter.h" 28#include "llvm/CodeGen/MachineConstantPool.h" 29#include "llvm/CodeGen/MachineInstr.h" 30#include "llvm/IR/DataLayout.h" 31#include "llvm/MC/MCContext.h" 32#include "llvm/MC/MCStreamer.h" 33#include "llvm/MC/MCSymbol.h" 34#include "llvm/Support/Debug.h" 35#include "llvm/Support/TargetRegistry.h" 36#include "llvm/Support/raw_ostream.h" 37using namespace llvm; 38 39#define DEBUG_TYPE "asm-printer" 40 41namespace { 42 43class WebAssemblyAsmPrinter final : public AsmPrinter { 44 const MachineRegisterInfo *MRI; 45 WebAssemblyFunctionInfo *MFI; 46 47public: 48 WebAssemblyAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer) 49 : AsmPrinter(TM, std::move(Streamer)), MRI(nullptr), MFI(nullptr) {} 50 51private: 52 StringRef getPassName() const override { 53 return "WebAssembly Assembly Printer"; 54 } 55 56 //===------------------------------------------------------------------===// 57 // MachineFunctionPass Implementation. 58 //===------------------------------------------------------------------===// 59 60 bool runOnMachineFunction(MachineFunction &MF) override { 61 MRI = &MF.getRegInfo(); 62 MFI = MF.getInfo<WebAssemblyFunctionInfo>(); 63 return AsmPrinter::runOnMachineFunction(MF); 64 } 65 66 //===------------------------------------------------------------------===// 67 // AsmPrinter Implementation. 68 //===------------------------------------------------------------------===// 69 70 void EmitEndOfAsmFile(Module &M) override; 71 void EmitJumpTableInfo() override; 72 void EmitConstantPool() override; 73 void EmitFunctionBodyStart() override; 74 void EmitFunctionBodyEnd() override; 75 void EmitInstruction(const MachineInstr *MI) override; 76 const MCExpr *lowerConstant(const Constant *CV) override; 77 bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, 78 unsigned AsmVariant, const char *ExtraCode, 79 raw_ostream &OS) override; 80 bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo, 81 unsigned AsmVariant, const char *ExtraCode, 82 raw_ostream &OS) override; 83 84 MVT getRegType(unsigned RegNo) const; 85 std::string regToString(const MachineOperand &MO); 86 WebAssemblyTargetStreamer *getTargetStreamer(); 87}; 88 89} // end anonymous namespace 90 91//===----------------------------------------------------------------------===// 92// Helpers. 93//===----------------------------------------------------------------------===// 94 95MVT WebAssemblyAsmPrinter::getRegType(unsigned RegNo) const { 96 const TargetRegisterClass *TRC = MRI->getRegClass(RegNo); 97 for (MVT T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64, MVT::v16i8, MVT::v8i16, 98 MVT::v4i32, MVT::v4f32}) 99 if (TRC->hasType(T)) 100 return T; 101 DEBUG(errs() << "Unknown type for register number: " << RegNo); 102 llvm_unreachable("Unknown register type"); 103 return MVT::Other; 104} 105 106std::string WebAssemblyAsmPrinter::regToString(const MachineOperand &MO) { 107 unsigned RegNo = MO.getReg(); 108 assert(TargetRegisterInfo::isVirtualRegister(RegNo) && 109 "Unlowered physical register encountered during assembly printing"); 110 assert(!MFI->isVRegStackified(RegNo)); 111 unsigned WAReg = MFI->getWAReg(RegNo); 112 assert(WAReg != WebAssemblyFunctionInfo::UnusedReg); 113 return '$' + utostr(WAReg); 114} 115 116WebAssemblyTargetStreamer *WebAssemblyAsmPrinter::getTargetStreamer() { 117 MCTargetStreamer *TS = OutStreamer->getTargetStreamer(); 118 return static_cast<WebAssemblyTargetStreamer *>(TS); 119} 120 121//===----------------------------------------------------------------------===// 122// WebAssemblyAsmPrinter Implementation. 123//===----------------------------------------------------------------------===// 124 125void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) { 126 for (const auto &F : M) { 127 // Emit function type info for all undefined functions 128 if (F.isDeclarationForLinker() && !F.isIntrinsic()) { 129 SmallVector<MVT, 4> Results; 130 SmallVector<MVT, 4> Params; 131 ComputeSignatureVTs(F, TM, Params, Results); 132 getTargetStreamer()->emitIndirectFunctionType(F.getName(), Params, 133 Results); 134 } 135 } 136 for (const auto &G : M.globals()) { 137 if (!G.hasInitializer() && G.hasExternalLinkage()) { 138 getTargetStreamer()->emitGlobalImport(G.getGlobalIdentifier()); 139 } 140 } 141} 142 143void WebAssemblyAsmPrinter::EmitConstantPool() { 144 assert(MF->getConstantPool()->getConstants().empty() && 145 "WebAssembly disables constant pools"); 146} 147 148void WebAssemblyAsmPrinter::EmitJumpTableInfo() { 149 // Nothing to do; jump tables are incorporated into the instruction stream. 150} 151 152void WebAssemblyAsmPrinter::EmitFunctionBodyStart() { 153 if (!MFI->getParams().empty()) 154 getTargetStreamer()->emitParam(MFI->getParams()); 155 156 SmallVector<MVT, 4> ResultVTs; 157 const Function &F(*MF->getFunction()); 158 159 // Emit the function index. 160 if (MDNode *Idx = F.getMetadata("wasm.index")) { 161 assert(Idx->getNumOperands() == 1); 162 163 getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant( 164 cast<ConstantAsMetadata>(Idx->getOperand(0))->getValue())); 165 } 166 167 ComputeLegalValueVTs(F, TM, F.getReturnType(), ResultVTs); 168 169 // If the return type needs to be legalized it will get converted into 170 // passing a pointer. 171 if (ResultVTs.size() == 1) 172 getTargetStreamer()->emitResult(ResultVTs); 173 174 // FIXME: When ExplicitLocals is enabled by default, we won't need 175 // to define the locals here (and MFI can go back to being pointer-to-const). 176 for (unsigned Idx = 0, IdxE = MRI->getNumVirtRegs(); Idx != IdxE; ++Idx) { 177 unsigned VReg = TargetRegisterInfo::index2VirtReg(Idx); 178 unsigned WAReg = MFI->getWAReg(VReg); 179 // Don't declare unused registers. 180 if (WAReg == WebAssemblyFunctionInfo::UnusedReg) 181 continue; 182 // Don't redeclare parameters. 183 if (WAReg < MFI->getParams().size()) 184 continue; 185 // Don't declare stackified registers. 186 if (int(WAReg) < 0) 187 continue; 188 MFI->addLocal(getRegType(VReg)); 189 } 190 191 getTargetStreamer()->emitLocal(MFI->getLocals()); 192 193 AsmPrinter::EmitFunctionBodyStart(); 194} 195 196void WebAssemblyAsmPrinter::EmitFunctionBodyEnd() { 197 getTargetStreamer()->emitEndFunc(); 198} 199 200void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) { 201 DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n'); 202 203 switch (MI->getOpcode()) { 204 case WebAssembly::ARGUMENT_I32: 205 case WebAssembly::ARGUMENT_I64: 206 case WebAssembly::ARGUMENT_F32: 207 case WebAssembly::ARGUMENT_F64: 208 case WebAssembly::ARGUMENT_v16i8: 209 case WebAssembly::ARGUMENT_v8i16: 210 case WebAssembly::ARGUMENT_v4i32: 211 case WebAssembly::ARGUMENT_v4f32: 212 // These represent values which are live into the function entry, so there's 213 // no instruction to emit. 214 break; 215 case WebAssembly::FALLTHROUGH_RETURN_I32: 216 case WebAssembly::FALLTHROUGH_RETURN_I64: 217 case WebAssembly::FALLTHROUGH_RETURN_F32: 218 case WebAssembly::FALLTHROUGH_RETURN_F64: 219 case WebAssembly::FALLTHROUGH_RETURN_v16i8: 220 case WebAssembly::FALLTHROUGH_RETURN_v8i16: 221 case WebAssembly::FALLTHROUGH_RETURN_v4i32: 222 case WebAssembly::FALLTHROUGH_RETURN_v4f32: { 223 // These instructions represent the implicit return at the end of a 224 // function body. The operand is always a pop. 225 assert(MFI->isVRegStackified(MI->getOperand(0).getReg())); 226 227 if (isVerbose()) { 228 OutStreamer->AddComment("fallthrough-return: $pop" + 229 utostr(MFI->getWARegStackId( 230 MFI->getWAReg(MI->getOperand(0).getReg())))); 231 OutStreamer->AddBlankLine(); 232 } 233 break; 234 } 235 case WebAssembly::FALLTHROUGH_RETURN_VOID: 236 // This instruction represents the implicit return at the end of a 237 // function body with no return value. 238 if (isVerbose()) { 239 OutStreamer->AddComment("fallthrough-return"); 240 OutStreamer->AddBlankLine(); 241 } 242 break; 243 default: { 244 WebAssemblyMCInstLower MCInstLowering(OutContext, *this); 245 MCInst TmpInst; 246 MCInstLowering.Lower(MI, TmpInst); 247 EmitToStreamer(*OutStreamer, TmpInst); 248 break; 249 } 250 } 251} 252 253const MCExpr *WebAssemblyAsmPrinter::lowerConstant(const Constant *CV) { 254 if (const GlobalValue *GV = dyn_cast<GlobalValue>(CV)) 255 if (GV->getValueType()->isFunctionTy()) 256 return MCSymbolRefExpr::create( 257 getSymbol(GV), MCSymbolRefExpr::VK_WebAssembly_FUNCTION, OutContext); 258 return AsmPrinter::lowerConstant(CV); 259} 260 261bool WebAssemblyAsmPrinter::PrintAsmOperand(const MachineInstr *MI, 262 unsigned OpNo, unsigned AsmVariant, 263 const char *ExtraCode, 264 raw_ostream &OS) { 265 if (AsmVariant != 0) 266 report_fatal_error("There are no defined alternate asm variants"); 267 268 // First try the generic code, which knows about modifiers like 'c' and 'n'. 269 if (!AsmPrinter::PrintAsmOperand(MI, OpNo, AsmVariant, ExtraCode, OS)) 270 return false; 271 272 if (!ExtraCode) { 273 const MachineOperand &MO = MI->getOperand(OpNo); 274 switch (MO.getType()) { 275 case MachineOperand::MO_Immediate: 276 OS << MO.getImm(); 277 return false; 278 case MachineOperand::MO_Register: 279 OS << regToString(MO); 280 return false; 281 case MachineOperand::MO_GlobalAddress: 282 getSymbol(MO.getGlobal())->print(OS, MAI); 283 printOffset(MO.getOffset(), OS); 284 return false; 285 case MachineOperand::MO_ExternalSymbol: 286 GetExternalSymbolSymbol(MO.getSymbolName())->print(OS, MAI); 287 printOffset(MO.getOffset(), OS); 288 return false; 289 case MachineOperand::MO_MachineBasicBlock: 290 MO.getMBB()->getSymbol()->print(OS, MAI); 291 return false; 292 default: 293 break; 294 } 295 } 296 297 return true; 298} 299 300bool WebAssemblyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, 301 unsigned OpNo, 302 unsigned AsmVariant, 303 const char *ExtraCode, 304 raw_ostream &OS) { 305 if (AsmVariant != 0) 306 report_fatal_error("There are no defined alternate asm variants"); 307 308 if (!ExtraCode) { 309 // TODO: For now, we just hard-code 0 as the constant offset; teach 310 // SelectInlineAsmMemoryOperand how to do address mode matching. 311 OS << "0(" + regToString(MI->getOperand(OpNo)) + ')'; 312 return false; 313 } 314 315 return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, AsmVariant, ExtraCode, OS); 316} 317 318// Force static initialization. 319extern "C" void LLVMInitializeWebAssemblyAsmPrinter() { 320 RegisterAsmPrinter<WebAssemblyAsmPrinter> X(getTheWebAssemblyTarget32()); 321 RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(getTheWebAssemblyTarget64()); 322} 323