1 //===-- WebAssemblyAsmPrinter.cpp - WebAssembly LLVM assembly writer ------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 /// 9 /// \file 10 /// This file contains a printer that converts from our internal 11 /// representation of machine-dependent LLVM code to the WebAssembly assembly 12 /// language. 13 /// 14 //===----------------------------------------------------------------------===// 15 16 #include "WebAssemblyAsmPrinter.h" 17 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 18 #include "MCTargetDesc/WebAssemblyTargetStreamer.h" 19 #include "TargetInfo/WebAssemblyTargetInfo.h" 20 #include "Utils/WebAssemblyTypeUtilities.h" 21 #include "Utils/WebAssemblyUtilities.h" 22 #include "WebAssembly.h" 23 #include "WebAssemblyMCInstLower.h" 24 #include "WebAssemblyMachineFunctionInfo.h" 25 #include "WebAssemblyRegisterInfo.h" 26 #include "WebAssemblyTargetMachine.h" 27 #include "llvm/ADT/SmallSet.h" 28 #include "llvm/ADT/StringExtras.h" 29 #include "llvm/BinaryFormat/Wasm.h" 30 #include "llvm/CodeGen/Analysis.h" 31 #include "llvm/CodeGen/AsmPrinter.h" 32 #include "llvm/CodeGen/MachineConstantPool.h" 33 #include "llvm/CodeGen/MachineInstr.h" 34 #include "llvm/CodeGen/MachineModuleInfoImpls.h" 35 #include "llvm/IR/DataLayout.h" 36 #include "llvm/IR/DebugInfoMetadata.h" 37 #include "llvm/IR/GlobalVariable.h" 38 #include "llvm/IR/Metadata.h" 39 #include "llvm/MC/MCContext.h" 40 #include "llvm/MC/MCSectionWasm.h" 41 #include "llvm/MC/MCStreamer.h" 42 #include "llvm/MC/MCSymbol.h" 43 #include "llvm/MC/MCSymbolWasm.h" 44 #include "llvm/Support/Debug.h" 45 #include "llvm/Support/TargetRegistry.h" 46 #include "llvm/Support/raw_ostream.h" 47 48 using namespace llvm; 49 50 #define DEBUG_TYPE "asm-printer" 51 52 extern cl::opt<bool> WasmKeepRegisters; 53 extern cl::opt<bool> EnableEmException; 54 extern cl::opt<bool> EnableEmSjLj; 55 56 //===----------------------------------------------------------------------===// 57 // Helpers. 58 //===----------------------------------------------------------------------===// 59 60 MVT WebAssemblyAsmPrinter::getRegType(unsigned RegNo) const { 61 const TargetRegisterInfo *TRI = Subtarget->getRegisterInfo(); 62 const TargetRegisterClass *TRC = MRI->getRegClass(RegNo); 63 for (MVT T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64, MVT::v16i8, MVT::v8i16, 64 MVT::v4i32, MVT::v2i64, MVT::v4f32, MVT::v2f64}) 65 if (TRI->isTypeLegalForClass(*TRC, T)) 66 return T; 67 LLVM_DEBUG(errs() << "Unknown type for register number: " << RegNo); 68 llvm_unreachable("Unknown register type"); 69 return MVT::Other; 70 } 71 72 std::string WebAssemblyAsmPrinter::regToString(const MachineOperand &MO) { 73 Register RegNo = MO.getReg(); 74 assert(Register::isVirtualRegister(RegNo) && 75 "Unlowered physical register encountered during assembly printing"); 76 assert(!MFI->isVRegStackified(RegNo)); 77 unsigned WAReg = MFI->getWAReg(RegNo); 78 assert(WAReg != WebAssemblyFunctionInfo::UnusedReg); 79 return '$' + utostr(WAReg); 80 } 81 82 WebAssemblyTargetStreamer *WebAssemblyAsmPrinter::getTargetStreamer() { 83 MCTargetStreamer *TS = OutStreamer->getTargetStreamer(); 84 return static_cast<WebAssemblyTargetStreamer *>(TS); 85 } 86 87 // Emscripten exception handling helpers 88 // 89 // This converts invoke names generated by LowerEmscriptenEHSjLj to real names 90 // that are expected by JavaScript glue code. The invoke names generated by 91 // Emscripten JS glue code are based on their argument and return types; for 92 // example, for a function that takes an i32 and returns nothing, it is 93 // 'invoke_vi'. But the format of invoke generated by LowerEmscriptenEHSjLj pass 94 // contains a mangled string generated from their IR types, for example, 95 // "__invoke_void_%struct.mystruct*_int", because final wasm types are not 96 // available in the IR pass. So we convert those names to the form that 97 // Emscripten JS code expects. 98 // 99 // Refer to LowerEmscriptenEHSjLj pass for more details. 100 101 // Returns true if the given function name is an invoke name generated by 102 // LowerEmscriptenEHSjLj pass. 103 static bool isEmscriptenInvokeName(StringRef Name) { 104 if (Name.front() == '"' && Name.back() == '"') 105 Name = Name.substr(1, Name.size() - 2); 106 return Name.startswith("__invoke_"); 107 } 108 109 // Returns a character that represents the given wasm value type in invoke 110 // signatures. 111 static char getInvokeSig(wasm::ValType VT) { 112 switch (VT) { 113 case wasm::ValType::I32: 114 return 'i'; 115 case wasm::ValType::I64: 116 return 'j'; 117 case wasm::ValType::F32: 118 return 'f'; 119 case wasm::ValType::F64: 120 return 'd'; 121 case wasm::ValType::V128: 122 return 'V'; 123 case wasm::ValType::FUNCREF: 124 return 'F'; 125 case wasm::ValType::EXTERNREF: 126 return 'X'; 127 } 128 llvm_unreachable("Unhandled wasm::ValType enum"); 129 } 130 131 // Given the wasm signature, generate the invoke name in the format JS glue code 132 // expects. 133 static std::string getEmscriptenInvokeSymbolName(wasm::WasmSignature *Sig) { 134 assert(Sig->Returns.size() <= 1); 135 std::string Ret = "invoke_"; 136 if (!Sig->Returns.empty()) 137 for (auto VT : Sig->Returns) 138 Ret += getInvokeSig(VT); 139 else 140 Ret += 'v'; 141 // Invokes' first argument is a pointer to the original function, so skip it 142 for (unsigned I = 1, E = Sig->Params.size(); I < E; I++) 143 Ret += getInvokeSig(Sig->Params[I]); 144 return Ret; 145 } 146 147 //===----------------------------------------------------------------------===// 148 // WebAssemblyAsmPrinter Implementation. 149 //===----------------------------------------------------------------------===// 150 151 MCSymbolWasm *WebAssemblyAsmPrinter::getMCSymbolForFunction( 152 const Function *F, bool EnableEmEH, wasm::WasmSignature *Sig, 153 bool &InvokeDetected) { 154 MCSymbolWasm *WasmSym = nullptr; 155 if (EnableEmEH && isEmscriptenInvokeName(F->getName())) { 156 assert(Sig); 157 InvokeDetected = true; 158 if (Sig->Returns.size() > 1) { 159 std::string Msg = 160 "Emscripten EH/SjLj does not support multivalue returns: " + 161 std::string(F->getName()) + ": " + 162 WebAssembly::signatureToString(Sig); 163 report_fatal_error(Msg); 164 } 165 WasmSym = cast<MCSymbolWasm>( 166 GetExternalSymbolSymbol(getEmscriptenInvokeSymbolName(Sig))); 167 } else { 168 WasmSym = cast<MCSymbolWasm>(getSymbol(F)); 169 } 170 return WasmSym; 171 } 172 173 void WebAssemblyAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { 174 if (!WebAssembly::isWasmVarAddressSpace(GV->getAddressSpace())) { 175 AsmPrinter::emitGlobalVariable(GV); 176 return; 177 } 178 179 assert(!GV->isThreadLocal()); 180 181 MCSymbolWasm *Sym = cast<MCSymbolWasm>(getSymbol(GV)); 182 183 if (!Sym->getType()) { 184 const WebAssemblyTargetLowering &TLI = *Subtarget->getTargetLowering(); 185 SmallVector<EVT, 1> VTs; 186 ComputeValueVTs(TLI, GV->getParent()->getDataLayout(), GV->getValueType(), 187 VTs); 188 if (VTs.size() != 1 || 189 TLI.getNumRegisters(GV->getParent()->getContext(), VTs[0]) != 1) 190 report_fatal_error("Aggregate globals not yet implemented"); 191 MVT VT = TLI.getRegisterType(GV->getParent()->getContext(), VTs[0]); 192 bool Mutable = true; 193 wasm::ValType Type = WebAssembly::toValType(VT); 194 Sym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL); 195 Sym->setGlobalType(wasm::WasmGlobalType{uint8_t(Type), Mutable}); 196 } 197 198 emitVisibility(Sym, GV->getVisibility(), !GV->isDeclaration()); 199 if (GV->hasInitializer()) { 200 assert(getSymbolPreferLocal(*GV) == Sym); 201 emitLinkage(GV, Sym); 202 getTargetStreamer()->emitGlobalType(Sym); 203 OutStreamer->emitLabel(Sym); 204 // TODO: Actually emit the initializer value. Otherwise the global has the 205 // default value for its type (0, ref.null, etc). 206 OutStreamer->AddBlankLine(); 207 } 208 } 209 210 void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) { 211 for (auto &It : OutContext.getSymbols()) { 212 // Emit .globaltype, .eventtype, or .tabletype declarations. 213 auto Sym = cast<MCSymbolWasm>(It.getValue()); 214 if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_GLOBAL) { 215 // .globaltype already handled by emitGlobalVariable for defined 216 // variables; here we make sure the types of external wasm globals get 217 // written to the file. 218 if (Sym->isUndefined()) 219 getTargetStreamer()->emitGlobalType(Sym); 220 } else if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_EVENT) 221 getTargetStreamer()->emitEventType(Sym); 222 else if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_TABLE) 223 getTargetStreamer()->emitTableType(Sym); 224 } 225 226 DenseSet<MCSymbol *> InvokeSymbols; 227 bool HasAddressTakenFunction = false; 228 for (const auto &F : M) { 229 if (F.isIntrinsic()) 230 continue; 231 232 if (F.hasAddressTaken()) 233 HasAddressTakenFunction = true; 234 235 // Emit function type info for all undefined functions 236 if (F.isDeclarationForLinker()) { 237 SmallVector<MVT, 4> Results; 238 SmallVector<MVT, 4> Params; 239 computeSignatureVTs(F.getFunctionType(), &F, F, TM, Params, Results); 240 // At this point these MCSymbols may or may not have been created already 241 // and thus also contain a signature, but we need to get the signature 242 // anyway here in case it is an invoke that has not yet been created. We 243 // will discard it later if it turns out not to be necessary. 244 auto Signature = signatureFromMVTs(Results, Params); 245 bool InvokeDetected = false; 246 auto *Sym = getMCSymbolForFunction(&F, EnableEmException || EnableEmSjLj, 247 Signature.get(), InvokeDetected); 248 249 // Multiple functions can be mapped to the same invoke symbol. For 250 // example, two IR functions '__invoke_void_i8*' and '__invoke_void_i32' 251 // are both mapped to '__invoke_vi'. We keep them in a set once we emit an 252 // Emscripten EH symbol so we don't emit the same symbol twice. 253 if (InvokeDetected && !InvokeSymbols.insert(Sym).second) 254 continue; 255 256 Sym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); 257 if (!Sym->getSignature()) { 258 Sym->setSignature(Signature.get()); 259 addSignature(std::move(Signature)); 260 } else { 261 // This symbol has already been created and had a signature. Discard it. 262 Signature.reset(); 263 } 264 265 getTargetStreamer()->emitFunctionType(Sym); 266 267 if (F.hasFnAttribute("wasm-import-module")) { 268 StringRef Name = 269 F.getFnAttribute("wasm-import-module").getValueAsString(); 270 Sym->setImportModule(storeName(Name)); 271 getTargetStreamer()->emitImportModule(Sym, Name); 272 } 273 if (F.hasFnAttribute("wasm-import-name")) { 274 // If this is a converted Emscripten EH/SjLj symbol, we shouldn't use 275 // the original function name but the converted symbol name. 276 StringRef Name = 277 InvokeDetected 278 ? Sym->getName() 279 : F.getFnAttribute("wasm-import-name").getValueAsString(); 280 Sym->setImportName(storeName(Name)); 281 getTargetStreamer()->emitImportName(Sym, Name); 282 } 283 } 284 285 if (F.hasFnAttribute("wasm-export-name")) { 286 auto *Sym = cast<MCSymbolWasm>(getSymbol(&F)); 287 StringRef Name = F.getFnAttribute("wasm-export-name").getValueAsString(); 288 Sym->setExportName(storeName(Name)); 289 getTargetStreamer()->emitExportName(Sym, Name); 290 } 291 } 292 293 // When a function's address is taken, a TABLE_INDEX relocation is emitted 294 // against the function symbol at the use site. However the relocation 295 // doesn't explicitly refer to the table. In the future we may want to 296 // define a new kind of reloc against both the function and the table, so 297 // that the linker can see that the function symbol keeps the table alive, 298 // but for now manually mark the table as live. 299 if (HasAddressTakenFunction) { 300 MCSymbolWasm *FunctionTable = 301 WebAssembly::getOrCreateFunctionTableSymbol(OutContext, Subtarget); 302 OutStreamer->emitSymbolAttribute(FunctionTable, MCSA_NoDeadStrip); 303 } 304 305 for (const auto &G : M.globals()) { 306 if (!G.hasInitializer() && G.hasExternalLinkage() && 307 !WebAssembly::isWasmVarAddressSpace(G.getAddressSpace()) && 308 G.getValueType()->isSized()) { 309 uint16_t Size = M.getDataLayout().getTypeAllocSize(G.getValueType()); 310 OutStreamer->emitELFSize(getSymbol(&G), 311 MCConstantExpr::create(Size, OutContext)); 312 } 313 } 314 315 if (const NamedMDNode *Named = M.getNamedMetadata("wasm.custom_sections")) { 316 for (const Metadata *MD : Named->operands()) { 317 const auto *Tuple = dyn_cast<MDTuple>(MD); 318 if (!Tuple || Tuple->getNumOperands() != 2) 319 continue; 320 const MDString *Name = dyn_cast<MDString>(Tuple->getOperand(0)); 321 const MDString *Contents = dyn_cast<MDString>(Tuple->getOperand(1)); 322 if (!Name || !Contents) 323 continue; 324 325 OutStreamer->PushSection(); 326 std::string SectionName = (".custom_section." + Name->getString()).str(); 327 MCSectionWasm *MySection = 328 OutContext.getWasmSection(SectionName, SectionKind::getMetadata()); 329 OutStreamer->SwitchSection(MySection); 330 OutStreamer->emitBytes(Contents->getString()); 331 OutStreamer->PopSection(); 332 } 333 } 334 335 EmitProducerInfo(M); 336 EmitTargetFeatures(M); 337 } 338 339 void WebAssemblyAsmPrinter::EmitProducerInfo(Module &M) { 340 llvm::SmallVector<std::pair<std::string, std::string>, 4> Languages; 341 if (const NamedMDNode *Debug = M.getNamedMetadata("llvm.dbg.cu")) { 342 llvm::SmallSet<StringRef, 4> SeenLanguages; 343 for (size_t I = 0, E = Debug->getNumOperands(); I < E; ++I) { 344 const auto *CU = cast<DICompileUnit>(Debug->getOperand(I)); 345 StringRef Language = dwarf::LanguageString(CU->getSourceLanguage()); 346 Language.consume_front("DW_LANG_"); 347 if (SeenLanguages.insert(Language).second) 348 Languages.emplace_back(Language.str(), ""); 349 } 350 } 351 352 llvm::SmallVector<std::pair<std::string, std::string>, 4> Tools; 353 if (const NamedMDNode *Ident = M.getNamedMetadata("llvm.ident")) { 354 llvm::SmallSet<StringRef, 4> SeenTools; 355 for (size_t I = 0, E = Ident->getNumOperands(); I < E; ++I) { 356 const auto *S = cast<MDString>(Ident->getOperand(I)->getOperand(0)); 357 std::pair<StringRef, StringRef> Field = S->getString().split("version"); 358 StringRef Name = Field.first.trim(); 359 StringRef Version = Field.second.trim(); 360 if (SeenTools.insert(Name).second) 361 Tools.emplace_back(Name.str(), Version.str()); 362 } 363 } 364 365 int FieldCount = int(!Languages.empty()) + int(!Tools.empty()); 366 if (FieldCount != 0) { 367 MCSectionWasm *Producers = OutContext.getWasmSection( 368 ".custom_section.producers", SectionKind::getMetadata()); 369 OutStreamer->PushSection(); 370 OutStreamer->SwitchSection(Producers); 371 OutStreamer->emitULEB128IntValue(FieldCount); 372 for (auto &Producers : {std::make_pair("language", &Languages), 373 std::make_pair("processed-by", &Tools)}) { 374 if (Producers.second->empty()) 375 continue; 376 OutStreamer->emitULEB128IntValue(strlen(Producers.first)); 377 OutStreamer->emitBytes(Producers.first); 378 OutStreamer->emitULEB128IntValue(Producers.second->size()); 379 for (auto &Producer : *Producers.second) { 380 OutStreamer->emitULEB128IntValue(Producer.first.size()); 381 OutStreamer->emitBytes(Producer.first); 382 OutStreamer->emitULEB128IntValue(Producer.second.size()); 383 OutStreamer->emitBytes(Producer.second); 384 } 385 } 386 OutStreamer->PopSection(); 387 } 388 } 389 390 void WebAssemblyAsmPrinter::EmitTargetFeatures(Module &M) { 391 struct FeatureEntry { 392 uint8_t Prefix; 393 std::string Name; 394 }; 395 396 // Read target features and linkage policies from module metadata 397 SmallVector<FeatureEntry, 4> EmittedFeatures; 398 auto EmitFeature = [&](std::string Feature) { 399 std::string MDKey = (StringRef("wasm-feature-") + Feature).str(); 400 Metadata *Policy = M.getModuleFlag(MDKey); 401 if (Policy == nullptr) 402 return; 403 404 FeatureEntry Entry; 405 Entry.Prefix = 0; 406 Entry.Name = Feature; 407 408 if (auto *MD = cast<ConstantAsMetadata>(Policy)) 409 if (auto *I = cast<ConstantInt>(MD->getValue())) 410 Entry.Prefix = I->getZExtValue(); 411 412 // Silently ignore invalid metadata 413 if (Entry.Prefix != wasm::WASM_FEATURE_PREFIX_USED && 414 Entry.Prefix != wasm::WASM_FEATURE_PREFIX_REQUIRED && 415 Entry.Prefix != wasm::WASM_FEATURE_PREFIX_DISALLOWED) 416 return; 417 418 EmittedFeatures.push_back(Entry); 419 }; 420 421 for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) { 422 EmitFeature(KV.Key); 423 } 424 // This pseudo-feature tells the linker whether shared memory would be safe 425 EmitFeature("shared-mem"); 426 427 if (EmittedFeatures.size() == 0) 428 return; 429 430 // Emit features and linkage policies into the "target_features" section 431 MCSectionWasm *FeaturesSection = OutContext.getWasmSection( 432 ".custom_section.target_features", SectionKind::getMetadata()); 433 OutStreamer->PushSection(); 434 OutStreamer->SwitchSection(FeaturesSection); 435 436 OutStreamer->emitULEB128IntValue(EmittedFeatures.size()); 437 for (auto &F : EmittedFeatures) { 438 OutStreamer->emitIntValue(F.Prefix, 1); 439 OutStreamer->emitULEB128IntValue(F.Name.size()); 440 OutStreamer->emitBytes(F.Name); 441 } 442 443 OutStreamer->PopSection(); 444 } 445 446 void WebAssemblyAsmPrinter::emitConstantPool() { 447 assert(MF->getConstantPool()->getConstants().empty() && 448 "WebAssembly disables constant pools"); 449 } 450 451 void WebAssemblyAsmPrinter::emitJumpTableInfo() { 452 // Nothing to do; jump tables are incorporated into the instruction stream. 453 } 454 455 void WebAssemblyAsmPrinter::emitFunctionBodyStart() { 456 const Function &F = MF->getFunction(); 457 SmallVector<MVT, 1> ResultVTs; 458 SmallVector<MVT, 4> ParamVTs; 459 computeSignatureVTs(F.getFunctionType(), &F, F, TM, ParamVTs, ResultVTs); 460 461 auto Signature = signatureFromMVTs(ResultVTs, ParamVTs); 462 auto *WasmSym = cast<MCSymbolWasm>(CurrentFnSym); 463 WasmSym->setSignature(Signature.get()); 464 addSignature(std::move(Signature)); 465 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); 466 467 getTargetStreamer()->emitFunctionType(WasmSym); 468 469 // Emit the function index. 470 if (MDNode *Idx = F.getMetadata("wasm.index")) { 471 assert(Idx->getNumOperands() == 1); 472 473 getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant( 474 cast<ConstantAsMetadata>(Idx->getOperand(0))->getValue())); 475 } 476 477 SmallVector<wasm::ValType, 16> Locals; 478 valTypesFromMVTs(MFI->getLocals(), Locals); 479 getTargetStreamer()->emitLocal(Locals); 480 481 AsmPrinter::emitFunctionBodyStart(); 482 } 483 484 void WebAssemblyAsmPrinter::emitInstruction(const MachineInstr *MI) { 485 LLVM_DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n'); 486 487 switch (MI->getOpcode()) { 488 case WebAssembly::ARGUMENT_i32: 489 case WebAssembly::ARGUMENT_i32_S: 490 case WebAssembly::ARGUMENT_i64: 491 case WebAssembly::ARGUMENT_i64_S: 492 case WebAssembly::ARGUMENT_f32: 493 case WebAssembly::ARGUMENT_f32_S: 494 case WebAssembly::ARGUMENT_f64: 495 case WebAssembly::ARGUMENT_f64_S: 496 case WebAssembly::ARGUMENT_v16i8: 497 case WebAssembly::ARGUMENT_v16i8_S: 498 case WebAssembly::ARGUMENT_v8i16: 499 case WebAssembly::ARGUMENT_v8i16_S: 500 case WebAssembly::ARGUMENT_v4i32: 501 case WebAssembly::ARGUMENT_v4i32_S: 502 case WebAssembly::ARGUMENT_v2i64: 503 case WebAssembly::ARGUMENT_v2i64_S: 504 case WebAssembly::ARGUMENT_v4f32: 505 case WebAssembly::ARGUMENT_v4f32_S: 506 case WebAssembly::ARGUMENT_v2f64: 507 case WebAssembly::ARGUMENT_v2f64_S: 508 // These represent values which are live into the function entry, so there's 509 // no instruction to emit. 510 break; 511 case WebAssembly::FALLTHROUGH_RETURN: { 512 // These instructions represent the implicit return at the end of a 513 // function body. 514 if (isVerbose()) { 515 OutStreamer->AddComment("fallthrough-return"); 516 OutStreamer->AddBlankLine(); 517 } 518 break; 519 } 520 case WebAssembly::COMPILER_FENCE: 521 // This is a compiler barrier that prevents instruction reordering during 522 // backend compilation, and should not be emitted. 523 break; 524 default: { 525 WebAssemblyMCInstLower MCInstLowering(OutContext, *this); 526 MCInst TmpInst; 527 MCInstLowering.lower(MI, TmpInst); 528 EmitToStreamer(*OutStreamer, TmpInst); 529 break; 530 } 531 } 532 } 533 534 bool WebAssemblyAsmPrinter::PrintAsmOperand(const MachineInstr *MI, 535 unsigned OpNo, 536 const char *ExtraCode, 537 raw_ostream &OS) { 538 // First try the generic code, which knows about modifiers like 'c' and 'n'. 539 if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS)) 540 return false; 541 542 if (!ExtraCode) { 543 const MachineOperand &MO = MI->getOperand(OpNo); 544 switch (MO.getType()) { 545 case MachineOperand::MO_Immediate: 546 OS << MO.getImm(); 547 return false; 548 case MachineOperand::MO_Register: 549 // FIXME: only opcode that still contains registers, as required by 550 // MachineInstr::getDebugVariable(). 551 assert(MI->getOpcode() == WebAssembly::INLINEASM); 552 OS << regToString(MO); 553 return false; 554 case MachineOperand::MO_GlobalAddress: 555 PrintSymbolOperand(MO, OS); 556 return false; 557 case MachineOperand::MO_ExternalSymbol: 558 GetExternalSymbolSymbol(MO.getSymbolName())->print(OS, MAI); 559 printOffset(MO.getOffset(), OS); 560 return false; 561 case MachineOperand::MO_MachineBasicBlock: 562 MO.getMBB()->getSymbol()->print(OS, MAI); 563 return false; 564 default: 565 break; 566 } 567 } 568 569 return true; 570 } 571 572 bool WebAssemblyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, 573 unsigned OpNo, 574 const char *ExtraCode, 575 raw_ostream &OS) { 576 // The current approach to inline asm is that "r" constraints are expressed 577 // as local indices, rather than values on the operand stack. This simplifies 578 // using "r" as it eliminates the need to push and pop the values in a 579 // particular order, however it also makes it impossible to have an "m" 580 // constraint. So we don't support it. 581 582 return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, ExtraCode, OS); 583 } 584 585 // Force static initialization. 586 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyAsmPrinter() { 587 RegisterAsmPrinter<WebAssemblyAsmPrinter> X(getTheWebAssemblyTarget32()); 588 RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(getTheWebAssemblyTarget64()); 589 } 590