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