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 /// 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 "WebAssemblyAsmPrinter.h" 18 #include "InstPrinter/WebAssemblyInstPrinter.h" 19 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 20 #include "MCTargetDesc/WebAssemblyTargetStreamer.h" 21 #include "WebAssembly.h" 22 #include "WebAssemblyMCInstLower.h" 23 #include "WebAssemblyMachineFunctionInfo.h" 24 #include "WebAssemblyRegisterInfo.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/CodeGen/MachineModuleInfoImpls.h" 31 #include "llvm/IR/DataLayout.h" 32 #include "llvm/IR/GlobalVariable.h" 33 #include "llvm/MC/MCContext.h" 34 #include "llvm/MC/MCSectionWasm.h" 35 #include "llvm/MC/MCStreamer.h" 36 #include "llvm/MC/MCSymbol.h" 37 #include "llvm/MC/MCSymbolELF.h" 38 #include "llvm/MC/MCSymbolWasm.h" 39 #include "llvm/Support/Debug.h" 40 #include "llvm/Support/TargetRegistry.h" 41 #include "llvm/Support/raw_ostream.h" 42 using namespace llvm; 43 44 #define DEBUG_TYPE "asm-printer" 45 46 //===----------------------------------------------------------------------===// 47 // Helpers. 48 //===----------------------------------------------------------------------===// 49 50 MVT WebAssemblyAsmPrinter::getRegType(unsigned RegNo) const { 51 const TargetRegisterInfo *TRI = Subtarget->getRegisterInfo(); 52 const TargetRegisterClass *TRC = MRI->getRegClass(RegNo); 53 for (MVT T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64, MVT::v16i8, MVT::v8i16, 54 MVT::v4i32, MVT::v4f32}) 55 if (TRI->isTypeLegalForClass(*TRC, T)) 56 return T; 57 LLVM_DEBUG(errs() << "Unknown type for register number: " << RegNo); 58 llvm_unreachable("Unknown register type"); 59 return MVT::Other; 60 } 61 62 std::string WebAssemblyAsmPrinter::regToString(const MachineOperand &MO) { 63 unsigned RegNo = MO.getReg(); 64 assert(TargetRegisterInfo::isVirtualRegister(RegNo) && 65 "Unlowered physical register encountered during assembly printing"); 66 assert(!MFI->isVRegStackified(RegNo)); 67 unsigned WAReg = MFI->getWAReg(RegNo); 68 assert(WAReg != WebAssemblyFunctionInfo::UnusedReg); 69 return '$' + utostr(WAReg); 70 } 71 72 WebAssemblyTargetStreamer *WebAssemblyAsmPrinter::getTargetStreamer() { 73 MCTargetStreamer *TS = OutStreamer->getTargetStreamer(); 74 return static_cast<WebAssemblyTargetStreamer *>(TS); 75 } 76 77 //===----------------------------------------------------------------------===// 78 // WebAssemblyAsmPrinter Implementation. 79 //===----------------------------------------------------------------------===// 80 81 void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) { 82 for (const auto &F : M) { 83 // Emit function type info for all undefined functions 84 if (F.isDeclarationForLinker() && !F.isIntrinsic()) { 85 SmallVector<MVT, 4> Results; 86 SmallVector<MVT, 4> Params; 87 ComputeSignatureVTs(F, TM, Params, Results); 88 MCSymbol *Sym = getSymbol(&F); 89 getTargetStreamer()->emitIndirectFunctionType(Sym, Params, Results); 90 91 if (TM.getTargetTriple().isOSBinFormatWasm() && 92 F.hasFnAttribute("wasm-import-module")) { 93 MCSymbolWasm *WasmSym = cast<MCSymbolWasm>(Sym); 94 StringRef Name = F.getFnAttribute("wasm-import-module") 95 .getValueAsString(); 96 getTargetStreamer()->emitImportModule(WasmSym, Name); 97 } 98 } 99 } 100 for (const auto &G : M.globals()) { 101 if (!G.hasInitializer() && G.hasExternalLinkage()) { 102 if (G.getValueType()->isSized()) { 103 uint16_t Size = M.getDataLayout().getTypeAllocSize(G.getValueType()); 104 if (TM.getTargetTriple().isOSBinFormatELF()) 105 getTargetStreamer()->emitGlobalImport(G.getGlobalIdentifier()); 106 OutStreamer->emitELFSize(getSymbol(&G), 107 MCConstantExpr::create(Size, OutContext)); 108 } 109 } 110 } 111 112 if (const NamedMDNode *Named = M.getNamedMetadata("wasm.custom_sections")) { 113 for (const Metadata *MD : Named->operands()) { 114 const MDTuple *Tuple = dyn_cast<MDTuple>(MD); 115 if (!Tuple || Tuple->getNumOperands() != 2) 116 continue; 117 const MDString *Name = dyn_cast<MDString>(Tuple->getOperand(0)); 118 const MDString *Contents = dyn_cast<MDString>(Tuple->getOperand(1)); 119 if (!Name || !Contents) 120 continue; 121 122 OutStreamer->PushSection(); 123 std::string SectionName = (".custom_section." + Name->getString()).str(); 124 MCSectionWasm *mySection = 125 OutContext.getWasmSection(SectionName, SectionKind::getMetadata()); 126 OutStreamer->SwitchSection(mySection); 127 OutStreamer->EmitBytes(Contents->getString()); 128 OutStreamer->PopSection(); 129 } 130 } 131 } 132 133 void WebAssemblyAsmPrinter::EmitConstantPool() { 134 assert(MF->getConstantPool()->getConstants().empty() && 135 "WebAssembly disables constant pools"); 136 } 137 138 void WebAssemblyAsmPrinter::EmitJumpTableInfo() { 139 // Nothing to do; jump tables are incorporated into the instruction stream. 140 } 141 142 void WebAssemblyAsmPrinter::EmitFunctionBodyStart() { 143 getTargetStreamer()->emitParam(CurrentFnSym, MFI->getParams()); 144 145 SmallVector<MVT, 4> ResultVTs; 146 const Function &F = MF->getFunction(); 147 148 // Emit the function index. 149 if (MDNode *Idx = F.getMetadata("wasm.index")) { 150 assert(Idx->getNumOperands() == 1); 151 152 getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant( 153 cast<ConstantAsMetadata>(Idx->getOperand(0))->getValue())); 154 } 155 156 ComputeLegalValueVTs(F, TM, F.getReturnType(), ResultVTs); 157 158 // If the return type needs to be legalized it will get converted into 159 // passing a pointer. 160 if (ResultVTs.size() == 1) 161 getTargetStreamer()->emitResult(CurrentFnSym, ResultVTs); 162 else 163 getTargetStreamer()->emitResult(CurrentFnSym, ArrayRef<MVT>()); 164 165 if (TM.getTargetTriple().isOSBinFormatELF()) { 166 assert(MFI->getLocals().empty()); 167 for (unsigned Idx = 0, IdxE = MRI->getNumVirtRegs(); Idx != IdxE; ++Idx) { 168 unsigned VReg = TargetRegisterInfo::index2VirtReg(Idx); 169 unsigned WAReg = MFI->getWAReg(VReg); 170 // Don't declare unused registers. 171 if (WAReg == WebAssemblyFunctionInfo::UnusedReg) 172 continue; 173 // Don't redeclare parameters. 174 if (WAReg < MFI->getParams().size()) 175 continue; 176 // Don't declare stackified registers. 177 if (int(WAReg) < 0) 178 continue; 179 MFI->addLocal(getRegType(VReg)); 180 } 181 } 182 183 getTargetStreamer()->emitLocal(MFI->getLocals()); 184 185 AsmPrinter::EmitFunctionBodyStart(); 186 } 187 188 void WebAssemblyAsmPrinter::EmitFunctionBodyEnd() { 189 if (TM.getTargetTriple().isOSBinFormatELF()) 190 getTargetStreamer()->emitEndFunc(); 191 } 192 193 void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) { 194 LLVM_DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n'); 195 196 switch (MI->getOpcode()) { 197 case WebAssembly::ARGUMENT_I32: 198 case WebAssembly::ARGUMENT_I64: 199 case WebAssembly::ARGUMENT_F32: 200 case WebAssembly::ARGUMENT_F64: 201 case WebAssembly::ARGUMENT_v16i8: 202 case WebAssembly::ARGUMENT_v8i16: 203 case WebAssembly::ARGUMENT_v4i32: 204 case WebAssembly::ARGUMENT_v4f32: 205 // These represent values which are live into the function entry, so there's 206 // no instruction to emit. 207 break; 208 case WebAssembly::FALLTHROUGH_RETURN_I32: 209 case WebAssembly::FALLTHROUGH_RETURN_I64: 210 case WebAssembly::FALLTHROUGH_RETURN_F32: 211 case WebAssembly::FALLTHROUGH_RETURN_F64: 212 case WebAssembly::FALLTHROUGH_RETURN_v16i8: 213 case WebAssembly::FALLTHROUGH_RETURN_v8i16: 214 case WebAssembly::FALLTHROUGH_RETURN_v4i32: 215 case WebAssembly::FALLTHROUGH_RETURN_v4f32: { 216 // These instructions represent the implicit return at the end of a 217 // function body. The operand is always a pop. 218 assert(MFI->isVRegStackified(MI->getOperand(0).getReg())); 219 220 if (isVerbose()) { 221 OutStreamer->AddComment("fallthrough-return: $pop" + 222 Twine(MFI->getWARegStackId( 223 MFI->getWAReg(MI->getOperand(0).getReg())))); 224 OutStreamer->AddBlankLine(); 225 } 226 break; 227 } 228 case WebAssembly::FALLTHROUGH_RETURN_VOID: 229 // This instruction represents the implicit return at the end of a 230 // function body with no return value. 231 if (isVerbose()) { 232 OutStreamer->AddComment("fallthrough-return"); 233 OutStreamer->AddBlankLine(); 234 } 235 break; 236 default: { 237 WebAssemblyMCInstLower MCInstLowering(OutContext, *this); 238 MCInst TmpInst; 239 MCInstLowering.Lower(MI, TmpInst); 240 EmitToStreamer(*OutStreamer, TmpInst); 241 break; 242 } 243 } 244 } 245 246 const MCExpr *WebAssemblyAsmPrinter::lowerConstant(const Constant *CV) { 247 if (const GlobalValue *GV = dyn_cast<GlobalValue>(CV)) 248 if (GV->getValueType()->isFunctionTy()) { 249 return MCSymbolRefExpr::create( 250 getSymbol(GV), MCSymbolRefExpr::VK_WebAssembly_FUNCTION, OutContext); 251 } 252 return AsmPrinter::lowerConstant(CV); 253 } 254 255 bool WebAssemblyAsmPrinter::PrintAsmOperand(const MachineInstr *MI, 256 unsigned OpNo, unsigned AsmVariant, 257 const char *ExtraCode, 258 raw_ostream &OS) { 259 if (AsmVariant != 0) 260 report_fatal_error("There are no defined alternate asm variants"); 261 262 // First try the generic code, which knows about modifiers like 'c' and 'n'. 263 if (!AsmPrinter::PrintAsmOperand(MI, OpNo, AsmVariant, ExtraCode, OS)) 264 return false; 265 266 if (!ExtraCode) { 267 const MachineOperand &MO = MI->getOperand(OpNo); 268 switch (MO.getType()) { 269 case MachineOperand::MO_Immediate: 270 OS << MO.getImm(); 271 return false; 272 case MachineOperand::MO_Register: 273 OS << regToString(MO); 274 return false; 275 case MachineOperand::MO_GlobalAddress: 276 getSymbol(MO.getGlobal())->print(OS, MAI); 277 printOffset(MO.getOffset(), OS); 278 return false; 279 case MachineOperand::MO_ExternalSymbol: 280 GetExternalSymbolSymbol(MO.getSymbolName())->print(OS, MAI); 281 printOffset(MO.getOffset(), OS); 282 return false; 283 case MachineOperand::MO_MachineBasicBlock: 284 MO.getMBB()->getSymbol()->print(OS, MAI); 285 return false; 286 default: 287 break; 288 } 289 } 290 291 return true; 292 } 293 294 bool WebAssemblyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, 295 unsigned OpNo, 296 unsigned AsmVariant, 297 const char *ExtraCode, 298 raw_ostream &OS) { 299 if (AsmVariant != 0) 300 report_fatal_error("There are no defined alternate asm variants"); 301 302 // The current approach to inline asm is that "r" constraints are expressed 303 // as local indices, rather than values on the operand stack. This simplifies 304 // using "r" as it eliminates the need to push and pop the values in a 305 // particular order, however it also makes it impossible to have an "m" 306 // constraint. So we don't support it. 307 308 return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, AsmVariant, ExtraCode, OS); 309 } 310 311 // Force static initialization. 312 extern "C" void LLVMInitializeWebAssemblyAsmPrinter() { 313 RegisterAsmPrinter<WebAssemblyAsmPrinter> X(getTheWebAssemblyTarget32()); 314 RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(getTheWebAssemblyTarget64()); 315 } 316