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 "WebAssemblyMCInstLower.h" 22 #include "WebAssemblyMachineFunctionInfo.h" 23 #include "WebAssemblyRegisterInfo.h" 24 #include "WebAssemblyRuntimeLibcallSignatures.h" 25 #include "WebAssemblyUtilities.h" 26 #include "llvm/ADT/MapVector.h" 27 #include "llvm/ADT/SmallSet.h" 28 #include "llvm/ADT/StringExtras.h" 29 #include "llvm/Analysis/ValueTracking.h" 30 #include "llvm/BinaryFormat/Wasm.h" 31 #include "llvm/CodeGen/Analysis.h" 32 #include "llvm/CodeGen/AsmPrinter.h" 33 #include "llvm/CodeGen/MachineConstantPool.h" 34 #include "llvm/CodeGen/MachineInstr.h" 35 #include "llvm/CodeGen/MachineModuleInfoImpls.h" 36 #include "llvm/IR/DataLayout.h" 37 #include "llvm/IR/DebugInfoMetadata.h" 38 #include "llvm/IR/GlobalVariable.h" 39 #include "llvm/IR/Metadata.h" 40 #include "llvm/IR/Module.h" 41 #include "llvm/MC/MCContext.h" 42 #include "llvm/MC/MCSectionWasm.h" 43 #include "llvm/MC/MCStreamer.h" 44 #include "llvm/MC/MCSymbol.h" 45 #include "llvm/MC/MCSymbolWasm.h" 46 #include "llvm/MC/TargetRegistry.h" 47 #include "llvm/Support/Debug.h" 48 #include "llvm/Support/raw_ostream.h" 49 50 using namespace llvm; 51 52 #define DEBUG_TYPE "asm-printer" 53 54 extern cl::opt<bool> WasmKeepRegisters; 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, MVT::v8f16}) 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(RegNo.isVirtual() && 75 "Unlowered physical register encountered during assembly printing"); 76 assert(!MFI->isVRegStackified(RegNo)); 77 unsigned WAReg = MFI->getWAReg(RegNo); 78 assert(WAReg != WebAssembly::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.starts_with("__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 case wasm::ValType::EXNREF: 128 return 'E'; 129 default: 130 llvm_unreachable("Unhandled wasm::ValType enum"); 131 } 132 } 133 134 // Given the wasm signature, generate the invoke name in the format JS glue code 135 // expects. 136 static std::string getEmscriptenInvokeSymbolName(wasm::WasmSignature *Sig) { 137 assert(Sig->Returns.size() <= 1); 138 std::string Ret = "invoke_"; 139 if (!Sig->Returns.empty()) 140 for (auto VT : Sig->Returns) 141 Ret += getInvokeSig(VT); 142 else 143 Ret += 'v'; 144 // Invokes' first argument is a pointer to the original function, so skip it 145 for (unsigned I = 1, E = Sig->Params.size(); I < E; I++) 146 Ret += getInvokeSig(Sig->Params[I]); 147 return Ret; 148 } 149 150 //===----------------------------------------------------------------------===// 151 // WebAssemblyAsmPrinter Implementation. 152 //===----------------------------------------------------------------------===// 153 154 MCSymbolWasm *WebAssemblyAsmPrinter::getMCSymbolForFunction( 155 const Function *F, bool EnableEmEH, wasm::WasmSignature *Sig, 156 bool &InvokeDetected) { 157 MCSymbolWasm *WasmSym = nullptr; 158 if (EnableEmEH && isEmscriptenInvokeName(F->getName())) { 159 assert(Sig); 160 InvokeDetected = true; 161 if (Sig->Returns.size() > 1) { 162 std::string Msg = 163 "Emscripten EH/SjLj does not support multivalue returns: " + 164 std::string(F->getName()) + ": " + 165 WebAssembly::signatureToString(Sig); 166 report_fatal_error(Twine(Msg)); 167 } 168 WasmSym = cast<MCSymbolWasm>( 169 GetExternalSymbolSymbol(getEmscriptenInvokeSymbolName(Sig))); 170 } else { 171 WasmSym = cast<MCSymbolWasm>(getSymbol(F)); 172 } 173 return WasmSym; 174 } 175 176 void WebAssemblyAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { 177 if (!WebAssembly::isWasmVarAddressSpace(GV->getAddressSpace())) { 178 AsmPrinter::emitGlobalVariable(GV); 179 return; 180 } 181 182 assert(!GV->isThreadLocal()); 183 184 MCSymbolWasm *Sym = cast<MCSymbolWasm>(getSymbol(GV)); 185 186 if (!Sym->getType()) { 187 SmallVector<MVT, 1> VTs; 188 Type *GlobalVT = GV->getValueType(); 189 if (Subtarget) { 190 // Subtarget is only set when a function is defined, because 191 // each function can declare a different subtarget. For example, 192 // on ARM a compilation unit might have a function on ARM and 193 // another on Thumb. Therefore only if Subtarget is non-null we 194 // can actually calculate the legal VTs. 195 const WebAssemblyTargetLowering &TLI = *Subtarget->getTargetLowering(); 196 computeLegalValueVTs(TLI, GV->getParent()->getContext(), 197 GV->getDataLayout(), GlobalVT, VTs); 198 } 199 WebAssembly::wasmSymbolSetType(Sym, GlobalVT, VTs); 200 } 201 202 emitVisibility(Sym, GV->getVisibility(), !GV->isDeclaration()); 203 emitSymbolType(Sym); 204 if (GV->hasInitializer()) { 205 assert(getSymbolPreferLocal(*GV) == Sym); 206 emitLinkage(GV, Sym); 207 OutStreamer->emitLabel(Sym); 208 // TODO: Actually emit the initializer value. Otherwise the global has the 209 // default value for its type (0, ref.null, etc). 210 OutStreamer->addBlankLine(); 211 } 212 } 213 214 MCSymbol *WebAssemblyAsmPrinter::getOrCreateWasmSymbol(StringRef Name) { 215 auto *WasmSym = cast<MCSymbolWasm>(GetExternalSymbolSymbol(Name)); 216 217 // May be called multiple times, so early out. 218 if (WasmSym->getType()) 219 return WasmSym; 220 221 const WebAssemblySubtarget &Subtarget = getSubtarget(); 222 223 // Except for certain known symbols, all symbols used by CodeGen are 224 // functions. It's OK to hardcode knowledge of specific symbols here; this 225 // method is precisely there for fetching the signatures of known 226 // Clang-provided symbols. 227 if (Name == "__stack_pointer" || Name == "__tls_base" || 228 Name == "__memory_base" || Name == "__table_base" || 229 Name == "__tls_size" || Name == "__tls_align") { 230 bool Mutable = 231 Name == "__stack_pointer" || Name == "__tls_base"; 232 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL); 233 WasmSym->setGlobalType(wasm::WasmGlobalType{ 234 uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64 235 : wasm::WASM_TYPE_I32), 236 Mutable}); 237 return WasmSym; 238 } 239 240 if (Name.starts_with("GCC_except_table")) { 241 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_DATA); 242 return WasmSym; 243 } 244 245 SmallVector<wasm::ValType, 4> Returns; 246 SmallVector<wasm::ValType, 4> Params; 247 if (Name == "__cpp_exception" || Name == "__c_longjmp") { 248 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TAG); 249 // In static linking we define tag symbols in WasmException::endModule(). 250 // But we may have multiple objects to be linked together, each of which 251 // defines the tag symbols. To resolve them, we declare them as weak. In 252 // dynamic linking we make tag symbols undefined in the backend, define it 253 // in JS, and feed them to each importing module. 254 if (!isPositionIndependent()) 255 WasmSym->setWeak(true); 256 WasmSym->setExternal(true); 257 258 // Currently both C++ exceptions and C longjmps have a single pointer type 259 // param. For C++ exceptions it is a pointer to an exception object, and for 260 // C longjmps it is pointer to a struct that contains a setjmp buffer and a 261 // longjmp return value. We may consider using multiple value parameters for 262 // longjmps later when multivalue support is ready. 263 wasm::ValType AddrType = 264 Subtarget.hasAddr64() ? wasm::ValType::I64 : wasm::ValType::I32; 265 Params.push_back(AddrType); 266 } else { // Function symbols 267 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); 268 WebAssembly::getLibcallSignature(Subtarget, Name, Returns, Params); 269 } 270 auto Signature = OutContext.createWasmSignature(); 271 Signature->Returns = std::move(Returns); 272 Signature->Params = std::move(Params); 273 WasmSym->setSignature(Signature); 274 275 return WasmSym; 276 } 277 278 void WebAssemblyAsmPrinter::emitSymbolType(const MCSymbolWasm *Sym) { 279 std::optional<wasm::WasmSymbolType> WasmTy = Sym->getType(); 280 if (!WasmTy) 281 return; 282 283 switch (*WasmTy) { 284 case wasm::WASM_SYMBOL_TYPE_GLOBAL: 285 getTargetStreamer()->emitGlobalType(Sym); 286 break; 287 case wasm::WASM_SYMBOL_TYPE_TAG: 288 getTargetStreamer()->emitTagType(Sym); 289 break; 290 case wasm::WASM_SYMBOL_TYPE_TABLE: 291 getTargetStreamer()->emitTableType(Sym); 292 break; 293 default: 294 break; // We only handle globals, tags and tables here 295 } 296 } 297 298 void WebAssemblyAsmPrinter::emitDecls(const Module &M) { 299 if (signaturesEmitted) 300 return; 301 signaturesEmitted = true; 302 303 // Normally symbols for globals get discovered as the MI gets lowered, 304 // but we need to know about them ahead of time. This will however, 305 // only find symbols that have been used. Unused symbols from globals will 306 // not be found here. 307 MachineModuleInfoWasm &MMIW = MMI->getObjFileInfo<MachineModuleInfoWasm>(); 308 for (StringRef Name : MMIW.MachineSymbolsUsed) { 309 auto *WasmSym = cast<MCSymbolWasm>(getOrCreateWasmSymbol(Name)); 310 if (WasmSym->isFunction()) { 311 // TODO(wvo): is there any case where this overlaps with the call to 312 // emitFunctionType in the loop below? 313 getTargetStreamer()->emitFunctionType(WasmSym); 314 } 315 } 316 317 for (auto &It : OutContext.getSymbols()) { 318 // Emit .globaltype, .tagtype, or .tabletype declarations for extern 319 // declarations, i.e. those that have only been declared (but not defined) 320 // in the current module 321 auto Sym = cast_or_null<MCSymbolWasm>(It.getValue().Symbol); 322 if (Sym && !Sym->isDefined()) 323 emitSymbolType(Sym); 324 } 325 326 DenseSet<MCSymbol *> InvokeSymbols; 327 for (const auto &F : M) { 328 if (F.isIntrinsic()) 329 continue; 330 331 // Emit function type info for all functions. This will emit duplicate 332 // information for defined functions (which already have function type 333 // info emitted alongside their definition), but this is necessary in 334 // order to enable the single-pass WebAssemblyAsmTypeCheck to succeed. 335 SmallVector<MVT, 4> Results; 336 SmallVector<MVT, 4> Params; 337 computeSignatureVTs(F.getFunctionType(), &F, F, TM, Params, Results); 338 // At this point these MCSymbols may or may not have been created already 339 // and thus also contain a signature, but we need to get the signature 340 // anyway here in case it is an invoke that has not yet been created. We 341 // will discard it later if it turns out not to be necessary. 342 auto Signature = signatureFromMVTs(OutContext, Results, Params); 343 bool InvokeDetected = false; 344 auto *Sym = getMCSymbolForFunction( 345 &F, WebAssembly::WasmEnableEmEH || WebAssembly::WasmEnableEmSjLj, 346 Signature, InvokeDetected); 347 348 // Multiple functions can be mapped to the same invoke symbol. For 349 // example, two IR functions '__invoke_void_i8*' and '__invoke_void_i32' 350 // are both mapped to '__invoke_vi'. We keep them in a set once we emit an 351 // Emscripten EH symbol so we don't emit the same symbol twice. 352 if (InvokeDetected && !InvokeSymbols.insert(Sym).second) 353 continue; 354 355 Sym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); 356 if (!Sym->getSignature()) { 357 Sym->setSignature(Signature); 358 } 359 360 getTargetStreamer()->emitFunctionType(Sym); 361 362 if (F.hasFnAttribute("wasm-import-module")) { 363 StringRef Name = 364 F.getFnAttribute("wasm-import-module").getValueAsString(); 365 Sym->setImportModule(OutContext.allocateString(Name)); 366 getTargetStreamer()->emitImportModule(Sym, Name); 367 } 368 if (F.hasFnAttribute("wasm-import-name")) { 369 // If this is a converted Emscripten EH/SjLj symbol, we shouldn't use 370 // the original function name but the converted symbol name. 371 StringRef Name = 372 InvokeDetected 373 ? Sym->getName() 374 : F.getFnAttribute("wasm-import-name").getValueAsString(); 375 Sym->setImportName(OutContext.allocateString(Name)); 376 getTargetStreamer()->emitImportName(Sym, Name); 377 } 378 379 if (F.hasFnAttribute("wasm-export-name")) { 380 auto *Sym = cast<MCSymbolWasm>(getSymbol(&F)); 381 StringRef Name = F.getFnAttribute("wasm-export-name").getValueAsString(); 382 Sym->setExportName(OutContext.allocateString(Name)); 383 getTargetStreamer()->emitExportName(Sym, Name); 384 } 385 } 386 } 387 388 void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) { 389 // This is required to emit external declarations (like .functypes) when 390 // no functions are defined in the compilation unit and therefore, 391 // emitDecls() is not called until now. 392 emitDecls(M); 393 394 // When a function's address is taken, a TABLE_INDEX relocation is emitted 395 // against the function symbol at the use site. However the relocation 396 // doesn't explicitly refer to the table. In the future we may want to 397 // define a new kind of reloc against both the function and the table, so 398 // that the linker can see that the function symbol keeps the table alive, 399 // but for now manually mark the table as live. 400 for (const auto &F : M) { 401 if (!F.isIntrinsic() && F.hasAddressTaken()) { 402 MCSymbolWasm *FunctionTable = 403 WebAssembly::getOrCreateFunctionTableSymbol(OutContext, Subtarget); 404 OutStreamer->emitSymbolAttribute(FunctionTable, MCSA_NoDeadStrip); 405 break; 406 } 407 } 408 409 for (const auto &G : M.globals()) { 410 if (!G.hasInitializer() && G.hasExternalLinkage() && 411 !WebAssembly::isWasmVarAddressSpace(G.getAddressSpace()) && 412 G.getValueType()->isSized()) { 413 uint16_t Size = M.getDataLayout().getTypeAllocSize(G.getValueType()); 414 OutStreamer->emitELFSize(getSymbol(&G), 415 MCConstantExpr::create(Size, OutContext)); 416 } 417 } 418 419 if (const NamedMDNode *Named = M.getNamedMetadata("wasm.custom_sections")) { 420 for (const Metadata *MD : Named->operands()) { 421 const auto *Tuple = dyn_cast<MDTuple>(MD); 422 if (!Tuple || Tuple->getNumOperands() != 2) 423 continue; 424 const MDString *Name = dyn_cast<MDString>(Tuple->getOperand(0)); 425 const MDString *Contents = dyn_cast<MDString>(Tuple->getOperand(1)); 426 if (!Name || !Contents) 427 continue; 428 429 OutStreamer->pushSection(); 430 std::string SectionName = (".custom_section." + Name->getString()).str(); 431 MCSectionWasm *MySection = 432 OutContext.getWasmSection(SectionName, SectionKind::getMetadata()); 433 OutStreamer->switchSection(MySection); 434 OutStreamer->emitBytes(Contents->getString()); 435 OutStreamer->popSection(); 436 } 437 } 438 439 EmitProducerInfo(M); 440 EmitTargetFeatures(M); 441 EmitFunctionAttributes(M); 442 } 443 444 void WebAssemblyAsmPrinter::EmitProducerInfo(Module &M) { 445 llvm::SmallVector<std::pair<std::string, std::string>, 4> Languages; 446 if (const NamedMDNode *Debug = M.getNamedMetadata("llvm.dbg.cu")) { 447 llvm::SmallSet<StringRef, 4> SeenLanguages; 448 for (size_t I = 0, E = Debug->getNumOperands(); I < E; ++I) { 449 const auto *CU = cast<DICompileUnit>(Debug->getOperand(I)); 450 StringRef Language = dwarf::LanguageString(CU->getSourceLanguage()); 451 Language.consume_front("DW_LANG_"); 452 if (SeenLanguages.insert(Language).second) 453 Languages.emplace_back(Language.str(), ""); 454 } 455 } 456 457 llvm::SmallVector<std::pair<std::string, std::string>, 4> Tools; 458 if (const NamedMDNode *Ident = M.getNamedMetadata("llvm.ident")) { 459 llvm::SmallSet<StringRef, 4> SeenTools; 460 for (size_t I = 0, E = Ident->getNumOperands(); I < E; ++I) { 461 const auto *S = cast<MDString>(Ident->getOperand(I)->getOperand(0)); 462 std::pair<StringRef, StringRef> Field = S->getString().split("version"); 463 StringRef Name = Field.first.trim(); 464 StringRef Version = Field.second.trim(); 465 if (SeenTools.insert(Name).second) 466 Tools.emplace_back(Name.str(), Version.str()); 467 } 468 } 469 470 int FieldCount = int(!Languages.empty()) + int(!Tools.empty()); 471 if (FieldCount != 0) { 472 MCSectionWasm *Producers = OutContext.getWasmSection( 473 ".custom_section.producers", SectionKind::getMetadata()); 474 OutStreamer->pushSection(); 475 OutStreamer->switchSection(Producers); 476 OutStreamer->emitULEB128IntValue(FieldCount); 477 for (auto &Producers : {std::make_pair("language", &Languages), 478 std::make_pair("processed-by", &Tools)}) { 479 if (Producers.second->empty()) 480 continue; 481 OutStreamer->emitULEB128IntValue(strlen(Producers.first)); 482 OutStreamer->emitBytes(Producers.first); 483 OutStreamer->emitULEB128IntValue(Producers.second->size()); 484 for (auto &Producer : *Producers.second) { 485 OutStreamer->emitULEB128IntValue(Producer.first.size()); 486 OutStreamer->emitBytes(Producer.first); 487 OutStreamer->emitULEB128IntValue(Producer.second.size()); 488 OutStreamer->emitBytes(Producer.second); 489 } 490 } 491 OutStreamer->popSection(); 492 } 493 } 494 495 void WebAssemblyAsmPrinter::EmitTargetFeatures(Module &M) { 496 struct FeatureEntry { 497 uint8_t Prefix; 498 std::string Name; 499 }; 500 501 // Read target features and linkage policies from module metadata 502 SmallVector<FeatureEntry, 4> EmittedFeatures; 503 auto EmitFeature = [&](std::string Feature) { 504 std::string MDKey = (StringRef("wasm-feature-") + Feature).str(); 505 Metadata *Policy = M.getModuleFlag(MDKey); 506 if (Policy == nullptr) 507 return; 508 509 FeatureEntry Entry; 510 Entry.Prefix = 0; 511 Entry.Name = Feature; 512 513 if (auto *MD = cast<ConstantAsMetadata>(Policy)) 514 if (auto *I = cast<ConstantInt>(MD->getValue())) 515 Entry.Prefix = I->getZExtValue(); 516 517 // Silently ignore invalid metadata 518 if (Entry.Prefix != wasm::WASM_FEATURE_PREFIX_USED && 519 Entry.Prefix != wasm::WASM_FEATURE_PREFIX_DISALLOWED) 520 return; 521 522 EmittedFeatures.push_back(Entry); 523 }; 524 525 for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) { 526 EmitFeature(KV.Key); 527 } 528 // This pseudo-feature tells the linker whether shared memory would be safe 529 EmitFeature("shared-mem"); 530 531 // This is an "architecture", not a "feature", but we emit it as such for 532 // the benefit of tools like Binaryen and consistency with other producers. 533 // FIXME: Subtarget is null here, so can't Subtarget->hasAddr64() ? 534 if (M.getDataLayout().getPointerSize() == 8) { 535 // Can't use EmitFeature since "wasm-feature-memory64" is not a module 536 // flag. 537 EmittedFeatures.push_back({wasm::WASM_FEATURE_PREFIX_USED, "memory64"}); 538 } 539 540 if (EmittedFeatures.size() == 0) 541 return; 542 543 // Emit features and linkage policies into the "target_features" section 544 MCSectionWasm *FeaturesSection = OutContext.getWasmSection( 545 ".custom_section.target_features", SectionKind::getMetadata()); 546 OutStreamer->pushSection(); 547 OutStreamer->switchSection(FeaturesSection); 548 549 OutStreamer->emitULEB128IntValue(EmittedFeatures.size()); 550 for (auto &F : EmittedFeatures) { 551 OutStreamer->emitIntValue(F.Prefix, 1); 552 OutStreamer->emitULEB128IntValue(F.Name.size()); 553 OutStreamer->emitBytes(F.Name); 554 } 555 556 OutStreamer->popSection(); 557 } 558 559 void WebAssemblyAsmPrinter::EmitFunctionAttributes(Module &M) { 560 auto V = M.getNamedGlobal("llvm.global.annotations"); 561 if (!V) 562 return; 563 564 // Group all the custom attributes by name. 565 MapVector<StringRef, SmallVector<MCSymbol *, 4>> CustomSections; 566 const ConstantArray *CA = cast<ConstantArray>(V->getOperand(0)); 567 for (Value *Op : CA->operands()) { 568 auto *CS = cast<ConstantStruct>(Op); 569 // The first field is a pointer to the annotated variable. 570 Value *AnnotatedVar = CS->getOperand(0)->stripPointerCasts(); 571 // Only annotated functions are supported for now. 572 if (!isa<Function>(AnnotatedVar)) 573 continue; 574 auto *F = cast<Function>(AnnotatedVar); 575 576 // The second field is a pointer to a global annotation string. 577 auto *GV = cast<GlobalVariable>(CS->getOperand(1)->stripPointerCasts()); 578 StringRef AnnotationString; 579 getConstantStringInfo(GV, AnnotationString); 580 auto *Sym = cast<MCSymbolWasm>(getSymbol(F)); 581 CustomSections[AnnotationString].push_back(Sym); 582 } 583 584 // Emit a custom section for each unique attribute. 585 for (const auto &[Name, Symbols] : CustomSections) { 586 MCSectionWasm *CustomSection = OutContext.getWasmSection( 587 ".custom_section.llvm.func_attr.annotate." + Name, SectionKind::getMetadata()); 588 OutStreamer->pushSection(); 589 OutStreamer->switchSection(CustomSection); 590 591 for (auto &Sym : Symbols) { 592 OutStreamer->emitValue( 593 MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_WASM_FUNCINDEX, 594 OutContext), 595 4); 596 } 597 OutStreamer->popSection(); 598 } 599 } 600 601 void WebAssemblyAsmPrinter::emitConstantPool() { 602 emitDecls(*MMI->getModule()); 603 assert(MF->getConstantPool()->getConstants().empty() && 604 "WebAssembly disables constant pools"); 605 } 606 607 void WebAssemblyAsmPrinter::emitJumpTableInfo() { 608 // Nothing to do; jump tables are incorporated into the instruction stream. 609 } 610 611 void WebAssemblyAsmPrinter::emitFunctionBodyStart() { 612 const Function &F = MF->getFunction(); 613 SmallVector<MVT, 1> ResultVTs; 614 SmallVector<MVT, 4> ParamVTs; 615 computeSignatureVTs(F.getFunctionType(), &F, F, TM, ParamVTs, ResultVTs); 616 617 auto Signature = signatureFromMVTs(OutContext, ResultVTs, ParamVTs); 618 auto *WasmSym = cast<MCSymbolWasm>(CurrentFnSym); 619 WasmSym->setSignature(Signature); 620 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); 621 622 getTargetStreamer()->emitFunctionType(WasmSym); 623 624 // Emit the function index. 625 if (MDNode *Idx = F.getMetadata("wasm.index")) { 626 assert(Idx->getNumOperands() == 1); 627 628 getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant( 629 cast<ConstantAsMetadata>(Idx->getOperand(0))->getValue())); 630 } 631 632 SmallVector<wasm::ValType, 16> Locals; 633 valTypesFromMVTs(MFI->getLocals(), Locals); 634 getTargetStreamer()->emitLocal(Locals); 635 636 AsmPrinter::emitFunctionBodyStart(); 637 } 638 639 void WebAssemblyAsmPrinter::emitInstruction(const MachineInstr *MI) { 640 LLVM_DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n'); 641 WebAssembly_MC::verifyInstructionPredicates(MI->getOpcode(), 642 Subtarget->getFeatureBits()); 643 644 switch (MI->getOpcode()) { 645 case WebAssembly::ARGUMENT_i32: 646 case WebAssembly::ARGUMENT_i32_S: 647 case WebAssembly::ARGUMENT_i64: 648 case WebAssembly::ARGUMENT_i64_S: 649 case WebAssembly::ARGUMENT_f32: 650 case WebAssembly::ARGUMENT_f32_S: 651 case WebAssembly::ARGUMENT_f64: 652 case WebAssembly::ARGUMENT_f64_S: 653 case WebAssembly::ARGUMENT_v16i8: 654 case WebAssembly::ARGUMENT_v16i8_S: 655 case WebAssembly::ARGUMENT_v8i16: 656 case WebAssembly::ARGUMENT_v8i16_S: 657 case WebAssembly::ARGUMENT_v4i32: 658 case WebAssembly::ARGUMENT_v4i32_S: 659 case WebAssembly::ARGUMENT_v2i64: 660 case WebAssembly::ARGUMENT_v2i64_S: 661 case WebAssembly::ARGUMENT_v4f32: 662 case WebAssembly::ARGUMENT_v4f32_S: 663 case WebAssembly::ARGUMENT_v2f64: 664 case WebAssembly::ARGUMENT_v2f64_S: 665 case WebAssembly::ARGUMENT_v8f16: 666 case WebAssembly::ARGUMENT_v8f16_S: 667 // These represent values which are live into the function entry, so there's 668 // no instruction to emit. 669 break; 670 case WebAssembly::FALLTHROUGH_RETURN: { 671 // These instructions represent the implicit return at the end of a 672 // function body. 673 if (isVerbose()) { 674 OutStreamer->AddComment("fallthrough-return"); 675 OutStreamer->addBlankLine(); 676 } 677 break; 678 } 679 case WebAssembly::COMPILER_FENCE: 680 // This is a compiler barrier that prevents instruction reordering during 681 // backend compilation, and should not be emitted. 682 break; 683 case WebAssembly::CATCH: 684 case WebAssembly::CATCH_S: 685 case WebAssembly::CATCH_REF: 686 case WebAssembly::CATCH_REF_S: 687 case WebAssembly::CATCH_ALL: 688 case WebAssembly::CATCH_ALL_S: 689 case WebAssembly::CATCH_ALL_REF: 690 case WebAssembly::CATCH_ALL_REF_S: 691 // These are pseudo instructions to represent catch clauses in try_table 692 // instruction to simulate block return values. 693 break; 694 default: { 695 WebAssemblyMCInstLower MCInstLowering(OutContext, *this); 696 MCInst TmpInst; 697 MCInstLowering.lower(MI, TmpInst); 698 EmitToStreamer(*OutStreamer, TmpInst); 699 break; 700 } 701 } 702 } 703 704 bool WebAssemblyAsmPrinter::PrintAsmOperand(const MachineInstr *MI, 705 unsigned OpNo, 706 const char *ExtraCode, 707 raw_ostream &OS) { 708 // First try the generic code, which knows about modifiers like 'c' and 'n'. 709 if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS)) 710 return false; 711 712 if (!ExtraCode) { 713 const MachineOperand &MO = MI->getOperand(OpNo); 714 switch (MO.getType()) { 715 case MachineOperand::MO_Immediate: 716 OS << MO.getImm(); 717 return false; 718 case MachineOperand::MO_Register: 719 // FIXME: only opcode that still contains registers, as required by 720 // MachineInstr::getDebugVariable(). 721 assert(MI->getOpcode() == WebAssembly::INLINEASM); 722 OS << regToString(MO); 723 return false; 724 case MachineOperand::MO_GlobalAddress: 725 PrintSymbolOperand(MO, OS); 726 return false; 727 case MachineOperand::MO_ExternalSymbol: 728 GetExternalSymbolSymbol(MO.getSymbolName())->print(OS, MAI); 729 printOffset(MO.getOffset(), OS); 730 return false; 731 case MachineOperand::MO_MachineBasicBlock: 732 MO.getMBB()->getSymbol()->print(OS, MAI); 733 return false; 734 default: 735 break; 736 } 737 } 738 739 return true; 740 } 741 742 bool WebAssemblyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, 743 unsigned OpNo, 744 const char *ExtraCode, 745 raw_ostream &OS) { 746 // The current approach to inline asm is that "r" constraints are expressed 747 // as local indices, rather than values on the operand stack. This simplifies 748 // using "r" as it eliminates the need to push and pop the values in a 749 // particular order, however it also makes it impossible to have an "m" 750 // constraint. So we don't support it. 751 752 return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, ExtraCode, OS); 753 } 754 755 // Force static initialization. 756 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyAsmPrinter() { 757 RegisterAsmPrinter<WebAssemblyAsmPrinter> X(getTheWebAssemblyTarget32()); 758 RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(getTheWebAssemblyTarget64()); 759 } 760