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/WebAssemblyInstPrinter.h" 18 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 19 #include "MCTargetDesc/WebAssemblyTargetStreamer.h" 20 #include "TargetInfo/WebAssemblyTargetInfo.h" 21 #include "WebAssembly.h" 22 #include "WebAssemblyMCInstLower.h" 23 #include "WebAssemblyMachineFunctionInfo.h" 24 #include "WebAssemblyRegisterInfo.h" 25 #include "WebAssemblyTargetMachine.h" 26 #include "WebAssemblyUtilities.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::emitEndOfAsmFile(Module &M) { 174 for (auto &It : OutContext.getSymbols()) { 175 // Emit .globaltype, .eventtype, or .tabletype declarations. 176 auto Sym = cast<MCSymbolWasm>(It.getValue()); 177 if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_GLOBAL) 178 getTargetStreamer()->emitGlobalType(Sym); 179 else if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_EVENT) 180 getTargetStreamer()->emitEventType(Sym); 181 else if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_TABLE) 182 getTargetStreamer()->emitTableType(Sym); 183 } 184 185 DenseSet<MCSymbol *> InvokeSymbols; 186 bool HasAddressTakenFunction = false; 187 for (const auto &F : M) { 188 if (F.isIntrinsic()) 189 continue; 190 191 if (F.hasAddressTaken()) 192 HasAddressTakenFunction = true; 193 194 // Emit function type info for all undefined functions 195 if (F.isDeclarationForLinker()) { 196 SmallVector<MVT, 4> Results; 197 SmallVector<MVT, 4> Params; 198 computeSignatureVTs(F.getFunctionType(), &F, F, TM, Params, Results); 199 // At this point these MCSymbols may or may not have been created already 200 // and thus also contain a signature, but we need to get the signature 201 // anyway here in case it is an invoke that has not yet been created. We 202 // will discard it later if it turns out not to be necessary. 203 auto Signature = signatureFromMVTs(Results, Params); 204 bool InvokeDetected = false; 205 auto *Sym = getMCSymbolForFunction(&F, EnableEmException || EnableEmSjLj, 206 Signature.get(), InvokeDetected); 207 208 // Multiple functions can be mapped to the same invoke symbol. For 209 // example, two IR functions '__invoke_void_i8*' and '__invoke_void_i32' 210 // are both mapped to '__invoke_vi'. We keep them in a set once we emit an 211 // Emscripten EH symbol so we don't emit the same symbol twice. 212 if (InvokeDetected && !InvokeSymbols.insert(Sym).second) 213 continue; 214 215 Sym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); 216 if (!Sym->getSignature()) { 217 Sym->setSignature(Signature.get()); 218 addSignature(std::move(Signature)); 219 } else { 220 // This symbol has already been created and had a signature. Discard it. 221 Signature.reset(); 222 } 223 224 getTargetStreamer()->emitFunctionType(Sym); 225 226 if (F.hasFnAttribute("wasm-import-module")) { 227 StringRef Name = 228 F.getFnAttribute("wasm-import-module").getValueAsString(); 229 Sym->setImportModule(storeName(Name)); 230 getTargetStreamer()->emitImportModule(Sym, Name); 231 } 232 if (F.hasFnAttribute("wasm-import-name")) { 233 // If this is a converted Emscripten EH/SjLj symbol, we shouldn't use 234 // the original function name but the converted symbol name. 235 StringRef Name = 236 InvokeDetected 237 ? Sym->getName() 238 : F.getFnAttribute("wasm-import-name").getValueAsString(); 239 Sym->setImportName(storeName(Name)); 240 getTargetStreamer()->emitImportName(Sym, Name); 241 } 242 } 243 244 if (F.hasFnAttribute("wasm-export-name")) { 245 auto *Sym = cast<MCSymbolWasm>(getSymbol(&F)); 246 StringRef Name = F.getFnAttribute("wasm-export-name").getValueAsString(); 247 Sym->setExportName(storeName(Name)); 248 getTargetStreamer()->emitExportName(Sym, Name); 249 } 250 } 251 252 // When a function's address is taken, a TABLE_INDEX relocation is emitted 253 // against the function symbol at the use site. However the relocation 254 // doesn't explicitly refer to the table. In the future we may want to 255 // define a new kind of reloc against both the function and the table, so 256 // that the linker can see that the function symbol keeps the table alive, 257 // but for now manually mark the table as live. 258 if (HasAddressTakenFunction) { 259 MCSymbolWasm *FunctionTable = 260 WebAssembly::getOrCreateFunctionTableSymbol(OutContext, Subtarget); 261 OutStreamer->emitSymbolAttribute(FunctionTable, MCSA_NoDeadStrip); 262 } 263 264 for (const auto &G : M.globals()) { 265 if (!G.hasInitializer() && G.hasExternalLinkage()) { 266 if (G.getValueType()->isSized()) { 267 uint16_t Size = M.getDataLayout().getTypeAllocSize(G.getValueType()); 268 OutStreamer->emitELFSize(getSymbol(&G), 269 MCConstantExpr::create(Size, OutContext)); 270 } 271 } 272 } 273 274 if (const NamedMDNode *Named = M.getNamedMetadata("wasm.custom_sections")) { 275 for (const Metadata *MD : Named->operands()) { 276 const auto *Tuple = dyn_cast<MDTuple>(MD); 277 if (!Tuple || Tuple->getNumOperands() != 2) 278 continue; 279 const MDString *Name = dyn_cast<MDString>(Tuple->getOperand(0)); 280 const MDString *Contents = dyn_cast<MDString>(Tuple->getOperand(1)); 281 if (!Name || !Contents) 282 continue; 283 284 OutStreamer->PushSection(); 285 std::string SectionName = (".custom_section." + Name->getString()).str(); 286 MCSectionWasm *MySection = 287 OutContext.getWasmSection(SectionName, SectionKind::getMetadata()); 288 OutStreamer->SwitchSection(MySection); 289 OutStreamer->emitBytes(Contents->getString()); 290 OutStreamer->PopSection(); 291 } 292 } 293 294 EmitProducerInfo(M); 295 EmitTargetFeatures(M); 296 } 297 298 void WebAssemblyAsmPrinter::EmitProducerInfo(Module &M) { 299 llvm::SmallVector<std::pair<std::string, std::string>, 4> Languages; 300 if (const NamedMDNode *Debug = M.getNamedMetadata("llvm.dbg.cu")) { 301 llvm::SmallSet<StringRef, 4> SeenLanguages; 302 for (size_t I = 0, E = Debug->getNumOperands(); I < E; ++I) { 303 const auto *CU = cast<DICompileUnit>(Debug->getOperand(I)); 304 StringRef Language = dwarf::LanguageString(CU->getSourceLanguage()); 305 Language.consume_front("DW_LANG_"); 306 if (SeenLanguages.insert(Language).second) 307 Languages.emplace_back(Language.str(), ""); 308 } 309 } 310 311 llvm::SmallVector<std::pair<std::string, std::string>, 4> Tools; 312 if (const NamedMDNode *Ident = M.getNamedMetadata("llvm.ident")) { 313 llvm::SmallSet<StringRef, 4> SeenTools; 314 for (size_t I = 0, E = Ident->getNumOperands(); I < E; ++I) { 315 const auto *S = cast<MDString>(Ident->getOperand(I)->getOperand(0)); 316 std::pair<StringRef, StringRef> Field = S->getString().split("version"); 317 StringRef Name = Field.first.trim(); 318 StringRef Version = Field.second.trim(); 319 if (SeenTools.insert(Name).second) 320 Tools.emplace_back(Name.str(), Version.str()); 321 } 322 } 323 324 int FieldCount = int(!Languages.empty()) + int(!Tools.empty()); 325 if (FieldCount != 0) { 326 MCSectionWasm *Producers = OutContext.getWasmSection( 327 ".custom_section.producers", SectionKind::getMetadata()); 328 OutStreamer->PushSection(); 329 OutStreamer->SwitchSection(Producers); 330 OutStreamer->emitULEB128IntValue(FieldCount); 331 for (auto &Producers : {std::make_pair("language", &Languages), 332 std::make_pair("processed-by", &Tools)}) { 333 if (Producers.second->empty()) 334 continue; 335 OutStreamer->emitULEB128IntValue(strlen(Producers.first)); 336 OutStreamer->emitBytes(Producers.first); 337 OutStreamer->emitULEB128IntValue(Producers.second->size()); 338 for (auto &Producer : *Producers.second) { 339 OutStreamer->emitULEB128IntValue(Producer.first.size()); 340 OutStreamer->emitBytes(Producer.first); 341 OutStreamer->emitULEB128IntValue(Producer.second.size()); 342 OutStreamer->emitBytes(Producer.second); 343 } 344 } 345 OutStreamer->PopSection(); 346 } 347 } 348 349 void WebAssemblyAsmPrinter::EmitTargetFeatures(Module &M) { 350 struct FeatureEntry { 351 uint8_t Prefix; 352 std::string Name; 353 }; 354 355 // Read target features and linkage policies from module metadata 356 SmallVector<FeatureEntry, 4> EmittedFeatures; 357 auto EmitFeature = [&](std::string Feature) { 358 std::string MDKey = (StringRef("wasm-feature-") + Feature).str(); 359 Metadata *Policy = M.getModuleFlag(MDKey); 360 if (Policy == nullptr) 361 return; 362 363 FeatureEntry Entry; 364 Entry.Prefix = 0; 365 Entry.Name = Feature; 366 367 if (auto *MD = cast<ConstantAsMetadata>(Policy)) 368 if (auto *I = cast<ConstantInt>(MD->getValue())) 369 Entry.Prefix = I->getZExtValue(); 370 371 // Silently ignore invalid metadata 372 if (Entry.Prefix != wasm::WASM_FEATURE_PREFIX_USED && 373 Entry.Prefix != wasm::WASM_FEATURE_PREFIX_REQUIRED && 374 Entry.Prefix != wasm::WASM_FEATURE_PREFIX_DISALLOWED) 375 return; 376 377 EmittedFeatures.push_back(Entry); 378 }; 379 380 for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) { 381 EmitFeature(KV.Key); 382 } 383 // This pseudo-feature tells the linker whether shared memory would be safe 384 EmitFeature("shared-mem"); 385 386 if (EmittedFeatures.size() == 0) 387 return; 388 389 // Emit features and linkage policies into the "target_features" section 390 MCSectionWasm *FeaturesSection = OutContext.getWasmSection( 391 ".custom_section.target_features", SectionKind::getMetadata()); 392 OutStreamer->PushSection(); 393 OutStreamer->SwitchSection(FeaturesSection); 394 395 OutStreamer->emitULEB128IntValue(EmittedFeatures.size()); 396 for (auto &F : EmittedFeatures) { 397 OutStreamer->emitIntValue(F.Prefix, 1); 398 OutStreamer->emitULEB128IntValue(F.Name.size()); 399 OutStreamer->emitBytes(F.Name); 400 } 401 402 OutStreamer->PopSection(); 403 } 404 405 void WebAssemblyAsmPrinter::emitConstantPool() { 406 assert(MF->getConstantPool()->getConstants().empty() && 407 "WebAssembly disables constant pools"); 408 } 409 410 void WebAssemblyAsmPrinter::emitJumpTableInfo() { 411 // Nothing to do; jump tables are incorporated into the instruction stream. 412 } 413 414 void WebAssemblyAsmPrinter::emitFunctionBodyStart() { 415 const Function &F = MF->getFunction(); 416 SmallVector<MVT, 1> ResultVTs; 417 SmallVector<MVT, 4> ParamVTs; 418 computeSignatureVTs(F.getFunctionType(), &F, F, TM, ParamVTs, ResultVTs); 419 420 auto Signature = signatureFromMVTs(ResultVTs, ParamVTs); 421 auto *WasmSym = cast<MCSymbolWasm>(CurrentFnSym); 422 WasmSym->setSignature(Signature.get()); 423 addSignature(std::move(Signature)); 424 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); 425 426 getTargetStreamer()->emitFunctionType(WasmSym); 427 428 // Emit the function index. 429 if (MDNode *Idx = F.getMetadata("wasm.index")) { 430 assert(Idx->getNumOperands() == 1); 431 432 getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant( 433 cast<ConstantAsMetadata>(Idx->getOperand(0))->getValue())); 434 } 435 436 SmallVector<wasm::ValType, 16> Locals; 437 valTypesFromMVTs(MFI->getLocals(), Locals); 438 getTargetStreamer()->emitLocal(Locals); 439 440 AsmPrinter::emitFunctionBodyStart(); 441 } 442 443 void WebAssemblyAsmPrinter::emitInstruction(const MachineInstr *MI) { 444 LLVM_DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n'); 445 446 switch (MI->getOpcode()) { 447 case WebAssembly::ARGUMENT_i32: 448 case WebAssembly::ARGUMENT_i32_S: 449 case WebAssembly::ARGUMENT_i64: 450 case WebAssembly::ARGUMENT_i64_S: 451 case WebAssembly::ARGUMENT_f32: 452 case WebAssembly::ARGUMENT_f32_S: 453 case WebAssembly::ARGUMENT_f64: 454 case WebAssembly::ARGUMENT_f64_S: 455 case WebAssembly::ARGUMENT_v16i8: 456 case WebAssembly::ARGUMENT_v16i8_S: 457 case WebAssembly::ARGUMENT_v8i16: 458 case WebAssembly::ARGUMENT_v8i16_S: 459 case WebAssembly::ARGUMENT_v4i32: 460 case WebAssembly::ARGUMENT_v4i32_S: 461 case WebAssembly::ARGUMENT_v2i64: 462 case WebAssembly::ARGUMENT_v2i64_S: 463 case WebAssembly::ARGUMENT_v4f32: 464 case WebAssembly::ARGUMENT_v4f32_S: 465 case WebAssembly::ARGUMENT_v2f64: 466 case WebAssembly::ARGUMENT_v2f64_S: 467 // These represent values which are live into the function entry, so there's 468 // no instruction to emit. 469 break; 470 case WebAssembly::FALLTHROUGH_RETURN: { 471 // These instructions represent the implicit return at the end of a 472 // function body. 473 if (isVerbose()) { 474 OutStreamer->AddComment("fallthrough-return"); 475 OutStreamer->AddBlankLine(); 476 } 477 break; 478 } 479 case WebAssembly::COMPILER_FENCE: 480 // This is a compiler barrier that prevents instruction reordering during 481 // backend compilation, and should not be emitted. 482 break; 483 default: { 484 WebAssemblyMCInstLower MCInstLowering(OutContext, *this); 485 MCInst TmpInst; 486 MCInstLowering.lower(MI, TmpInst); 487 EmitToStreamer(*OutStreamer, TmpInst); 488 break; 489 } 490 } 491 } 492 493 bool WebAssemblyAsmPrinter::PrintAsmOperand(const MachineInstr *MI, 494 unsigned OpNo, 495 const char *ExtraCode, 496 raw_ostream &OS) { 497 // First try the generic code, which knows about modifiers like 'c' and 'n'. 498 if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS)) 499 return false; 500 501 if (!ExtraCode) { 502 const MachineOperand &MO = MI->getOperand(OpNo); 503 switch (MO.getType()) { 504 case MachineOperand::MO_Immediate: 505 OS << MO.getImm(); 506 return false; 507 case MachineOperand::MO_Register: 508 // FIXME: only opcode that still contains registers, as required by 509 // MachineInstr::getDebugVariable(). 510 assert(MI->getOpcode() == WebAssembly::INLINEASM); 511 OS << regToString(MO); 512 return false; 513 case MachineOperand::MO_GlobalAddress: 514 PrintSymbolOperand(MO, OS); 515 return false; 516 case MachineOperand::MO_ExternalSymbol: 517 GetExternalSymbolSymbol(MO.getSymbolName())->print(OS, MAI); 518 printOffset(MO.getOffset(), OS); 519 return false; 520 case MachineOperand::MO_MachineBasicBlock: 521 MO.getMBB()->getSymbol()->print(OS, MAI); 522 return false; 523 default: 524 break; 525 } 526 } 527 528 return true; 529 } 530 531 bool WebAssemblyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, 532 unsigned OpNo, 533 const char *ExtraCode, 534 raw_ostream &OS) { 535 // The current approach to inline asm is that "r" constraints are expressed 536 // as local indices, rather than values on the operand stack. This simplifies 537 // using "r" as it eliminates the need to push and pop the values in a 538 // particular order, however it also makes it impossible to have an "m" 539 // constraint. So we don't support it. 540 541 return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, ExtraCode, OS); 542 } 543 544 // Force static initialization. 545 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyAsmPrinter() { 546 RegisterAsmPrinter<WebAssemblyAsmPrinter> X(getTheWebAssemblyTarget32()); 547 RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(getTheWebAssemblyTarget64()); 548 } 549