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 "llvm/ADT/SmallSet.h" 27 #include "llvm/ADT/StringExtras.h" 28 #include "llvm/BinaryFormat/Wasm.h" 29 #include "llvm/CodeGen/Analysis.h" 30 #include "llvm/CodeGen/AsmPrinter.h" 31 #include "llvm/CodeGen/MachineConstantPool.h" 32 #include "llvm/CodeGen/MachineInstr.h" 33 #include "llvm/CodeGen/MachineModuleInfoImpls.h" 34 #include "llvm/IR/DataLayout.h" 35 #include "llvm/IR/DebugInfoMetadata.h" 36 #include "llvm/IR/GlobalVariable.h" 37 #include "llvm/IR/Metadata.h" 38 #include "llvm/MC/MCContext.h" 39 #include "llvm/MC/MCSectionWasm.h" 40 #include "llvm/MC/MCStreamer.h" 41 #include "llvm/MC/MCSymbol.h" 42 #include "llvm/MC/MCSymbolWasm.h" 43 #include "llvm/Support/Debug.h" 44 #include "llvm/Support/TargetRegistry.h" 45 #include "llvm/Support/raw_ostream.h" 46 47 using namespace llvm; 48 49 #define DEBUG_TYPE "asm-printer" 50 51 extern cl::opt<bool> WasmKeepRegisters; 52 53 //===----------------------------------------------------------------------===// 54 // Helpers. 55 //===----------------------------------------------------------------------===// 56 57 MVT WebAssemblyAsmPrinter::getRegType(unsigned RegNo) const { 58 const TargetRegisterInfo *TRI = Subtarget->getRegisterInfo(); 59 const TargetRegisterClass *TRC = MRI->getRegClass(RegNo); 60 for (MVT T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64, MVT::v16i8, MVT::v8i16, 61 MVT::v4i32, MVT::v2i64, MVT::v4f32, MVT::v2f64}) 62 if (TRI->isTypeLegalForClass(*TRC, T)) 63 return T; 64 LLVM_DEBUG(errs() << "Unknown type for register number: " << RegNo); 65 llvm_unreachable("Unknown register type"); 66 return MVT::Other; 67 } 68 69 std::string WebAssemblyAsmPrinter::regToString(const MachineOperand &MO) { 70 Register RegNo = MO.getReg(); 71 assert(Register::isVirtualRegister(RegNo) && 72 "Unlowered physical register encountered during assembly printing"); 73 assert(!MFI->isVRegStackified(RegNo)); 74 unsigned WAReg = MFI->getWAReg(RegNo); 75 assert(WAReg != WebAssemblyFunctionInfo::UnusedReg); 76 return '$' + utostr(WAReg); 77 } 78 79 WebAssemblyTargetStreamer *WebAssemblyAsmPrinter::getTargetStreamer() { 80 MCTargetStreamer *TS = OutStreamer->getTargetStreamer(); 81 return static_cast<WebAssemblyTargetStreamer *>(TS); 82 } 83 84 //===----------------------------------------------------------------------===// 85 // WebAssemblyAsmPrinter Implementation. 86 //===----------------------------------------------------------------------===// 87 88 void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) { 89 for (auto &It : OutContext.getSymbols()) { 90 // Emit a .globaltype and .eventtype declaration. 91 auto Sym = cast<MCSymbolWasm>(It.getValue()); 92 if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_GLOBAL) 93 getTargetStreamer()->emitGlobalType(Sym); 94 else if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_EVENT) 95 getTargetStreamer()->emitEventType(Sym); 96 } 97 98 for (const auto &F : M) { 99 if (F.isIntrinsic()) 100 continue; 101 102 // Emit function type info for all undefined functions 103 if (F.isDeclarationForLinker()) { 104 SmallVector<MVT, 4> Results; 105 SmallVector<MVT, 4> Params; 106 computeSignatureVTs(F.getFunctionType(), &F, F, TM, Params, Results); 107 auto *Sym = cast<MCSymbolWasm>(getSymbol(&F)); 108 Sym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); 109 if (!Sym->getSignature()) { 110 auto Signature = signatureFromMVTs(Results, Params); 111 Sym->setSignature(Signature.get()); 112 addSignature(std::move(Signature)); 113 } 114 // FIXME: this was originally intended for post-linking and was only used 115 // for imports that were only called indirectly (i.e. s2wasm could not 116 // infer the type from a call). With object files it applies to all 117 // imports. so fix the names and the tests, or rethink how import 118 // delcarations work in asm files. 119 getTargetStreamer()->emitFunctionType(Sym); 120 121 if (TM.getTargetTriple().isOSBinFormatWasm() && 122 F.hasFnAttribute("wasm-import-module")) { 123 StringRef Name = 124 F.getFnAttribute("wasm-import-module").getValueAsString(); 125 Sym->setImportModule(storeName(Name)); 126 getTargetStreamer()->emitImportModule(Sym, Name); 127 } 128 if (TM.getTargetTriple().isOSBinFormatWasm() && 129 F.hasFnAttribute("wasm-import-name")) { 130 StringRef Name = 131 F.getFnAttribute("wasm-import-name").getValueAsString(); 132 Sym->setImportName(storeName(Name)); 133 getTargetStreamer()->emitImportName(Sym, Name); 134 } 135 } 136 137 if (F.hasFnAttribute("wasm-export-name")) { 138 auto *Sym = cast<MCSymbolWasm>(getSymbol(&F)); 139 StringRef Name = F.getFnAttribute("wasm-export-name").getValueAsString(); 140 Sym->setExportName(storeName(Name)); 141 getTargetStreamer()->emitExportName(Sym, Name); 142 } 143 } 144 145 for (const auto &G : M.globals()) { 146 if (!G.hasInitializer() && G.hasExternalLinkage()) { 147 if (G.getValueType()->isSized()) { 148 uint16_t Size = M.getDataLayout().getTypeAllocSize(G.getValueType()); 149 OutStreamer->emitELFSize(getSymbol(&G), 150 MCConstantExpr::create(Size, OutContext)); 151 } 152 } 153 } 154 155 if (const NamedMDNode *Named = M.getNamedMetadata("wasm.custom_sections")) { 156 for (const Metadata *MD : Named->operands()) { 157 const auto *Tuple = dyn_cast<MDTuple>(MD); 158 if (!Tuple || Tuple->getNumOperands() != 2) 159 continue; 160 const MDString *Name = dyn_cast<MDString>(Tuple->getOperand(0)); 161 const MDString *Contents = dyn_cast<MDString>(Tuple->getOperand(1)); 162 if (!Name || !Contents) 163 continue; 164 165 OutStreamer->PushSection(); 166 std::string SectionName = (".custom_section." + Name->getString()).str(); 167 MCSectionWasm *MySection = 168 OutContext.getWasmSection(SectionName, SectionKind::getMetadata()); 169 OutStreamer->SwitchSection(MySection); 170 OutStreamer->emitBytes(Contents->getString()); 171 OutStreamer->PopSection(); 172 } 173 } 174 175 EmitProducerInfo(M); 176 EmitTargetFeatures(M); 177 } 178 179 void WebAssemblyAsmPrinter::EmitProducerInfo(Module &M) { 180 llvm::SmallVector<std::pair<std::string, std::string>, 4> Languages; 181 if (const NamedMDNode *Debug = M.getNamedMetadata("llvm.dbg.cu")) { 182 llvm::SmallSet<StringRef, 4> SeenLanguages; 183 for (size_t I = 0, E = Debug->getNumOperands(); I < E; ++I) { 184 const auto *CU = cast<DICompileUnit>(Debug->getOperand(I)); 185 StringRef Language = dwarf::LanguageString(CU->getSourceLanguage()); 186 Language.consume_front("DW_LANG_"); 187 if (SeenLanguages.insert(Language).second) 188 Languages.emplace_back(Language.str(), ""); 189 } 190 } 191 192 llvm::SmallVector<std::pair<std::string, std::string>, 4> Tools; 193 if (const NamedMDNode *Ident = M.getNamedMetadata("llvm.ident")) { 194 llvm::SmallSet<StringRef, 4> SeenTools; 195 for (size_t I = 0, E = Ident->getNumOperands(); I < E; ++I) { 196 const auto *S = cast<MDString>(Ident->getOperand(I)->getOperand(0)); 197 std::pair<StringRef, StringRef> Field = S->getString().split("version"); 198 StringRef Name = Field.first.trim(); 199 StringRef Version = Field.second.trim(); 200 if (SeenTools.insert(Name).second) 201 Tools.emplace_back(Name.str(), Version.str()); 202 } 203 } 204 205 int FieldCount = int(!Languages.empty()) + int(!Tools.empty()); 206 if (FieldCount != 0) { 207 MCSectionWasm *Producers = OutContext.getWasmSection( 208 ".custom_section.producers", SectionKind::getMetadata()); 209 OutStreamer->PushSection(); 210 OutStreamer->SwitchSection(Producers); 211 OutStreamer->emitULEB128IntValue(FieldCount); 212 for (auto &Producers : {std::make_pair("language", &Languages), 213 std::make_pair("processed-by", &Tools)}) { 214 if (Producers.second->empty()) 215 continue; 216 OutStreamer->emitULEB128IntValue(strlen(Producers.first)); 217 OutStreamer->emitBytes(Producers.first); 218 OutStreamer->emitULEB128IntValue(Producers.second->size()); 219 for (auto &Producer : *Producers.second) { 220 OutStreamer->emitULEB128IntValue(Producer.first.size()); 221 OutStreamer->emitBytes(Producer.first); 222 OutStreamer->emitULEB128IntValue(Producer.second.size()); 223 OutStreamer->emitBytes(Producer.second); 224 } 225 } 226 OutStreamer->PopSection(); 227 } 228 } 229 230 void WebAssemblyAsmPrinter::EmitTargetFeatures(Module &M) { 231 struct FeatureEntry { 232 uint8_t Prefix; 233 StringRef Name; 234 }; 235 236 // Read target features and linkage policies from module metadata 237 SmallVector<FeatureEntry, 4> EmittedFeatures; 238 for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) { 239 std::string MDKey = (StringRef("wasm-feature-") + KV.Key).str(); 240 Metadata *Policy = M.getModuleFlag(MDKey); 241 if (Policy == nullptr) 242 continue; 243 244 FeatureEntry Entry; 245 Entry.Prefix = 0; 246 Entry.Name = KV.Key; 247 248 if (auto *MD = cast<ConstantAsMetadata>(Policy)) 249 if (auto *I = cast<ConstantInt>(MD->getValue())) 250 Entry.Prefix = I->getZExtValue(); 251 252 // Silently ignore invalid metadata 253 if (Entry.Prefix != wasm::WASM_FEATURE_PREFIX_USED && 254 Entry.Prefix != wasm::WASM_FEATURE_PREFIX_REQUIRED && 255 Entry.Prefix != wasm::WASM_FEATURE_PREFIX_DISALLOWED) 256 continue; 257 258 EmittedFeatures.push_back(Entry); 259 } 260 261 if (EmittedFeatures.size() == 0) 262 return; 263 264 // Emit features and linkage policies into the "target_features" section 265 MCSectionWasm *FeaturesSection = OutContext.getWasmSection( 266 ".custom_section.target_features", SectionKind::getMetadata()); 267 OutStreamer->PushSection(); 268 OutStreamer->SwitchSection(FeaturesSection); 269 270 OutStreamer->emitULEB128IntValue(EmittedFeatures.size()); 271 for (auto &F : EmittedFeatures) { 272 OutStreamer->emitIntValue(F.Prefix, 1); 273 OutStreamer->emitULEB128IntValue(F.Name.size()); 274 OutStreamer->emitBytes(F.Name); 275 } 276 277 OutStreamer->PopSection(); 278 } 279 280 void WebAssemblyAsmPrinter::emitConstantPool() { 281 assert(MF->getConstantPool()->getConstants().empty() && 282 "WebAssembly disables constant pools"); 283 } 284 285 void WebAssemblyAsmPrinter::emitJumpTableInfo() { 286 // Nothing to do; jump tables are incorporated into the instruction stream. 287 } 288 289 void WebAssemblyAsmPrinter::emitFunctionBodyStart() { 290 const Function &F = MF->getFunction(); 291 SmallVector<MVT, 1> ResultVTs; 292 SmallVector<MVT, 4> ParamVTs; 293 computeSignatureVTs(F.getFunctionType(), &F, F, TM, ParamVTs, ResultVTs); 294 295 auto Signature = signatureFromMVTs(ResultVTs, ParamVTs); 296 auto *WasmSym = cast<MCSymbolWasm>(CurrentFnSym); 297 WasmSym->setSignature(Signature.get()); 298 addSignature(std::move(Signature)); 299 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); 300 301 // FIXME: clean up how params and results are emitted (use signatures) 302 getTargetStreamer()->emitFunctionType(WasmSym); 303 304 // Emit the function index. 305 if (MDNode *Idx = F.getMetadata("wasm.index")) { 306 assert(Idx->getNumOperands() == 1); 307 308 getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant( 309 cast<ConstantAsMetadata>(Idx->getOperand(0))->getValue())); 310 } 311 312 SmallVector<wasm::ValType, 16> Locals; 313 valTypesFromMVTs(MFI->getLocals(), Locals); 314 getTargetStreamer()->emitLocal(Locals); 315 316 AsmPrinter::emitFunctionBodyStart(); 317 } 318 319 void WebAssemblyAsmPrinter::emitInstruction(const MachineInstr *MI) { 320 LLVM_DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n'); 321 322 switch (MI->getOpcode()) { 323 case WebAssembly::ARGUMENT_i32: 324 case WebAssembly::ARGUMENT_i32_S: 325 case WebAssembly::ARGUMENT_i64: 326 case WebAssembly::ARGUMENT_i64_S: 327 case WebAssembly::ARGUMENT_f32: 328 case WebAssembly::ARGUMENT_f32_S: 329 case WebAssembly::ARGUMENT_f64: 330 case WebAssembly::ARGUMENT_f64_S: 331 case WebAssembly::ARGUMENT_v16i8: 332 case WebAssembly::ARGUMENT_v16i8_S: 333 case WebAssembly::ARGUMENT_v8i16: 334 case WebAssembly::ARGUMENT_v8i16_S: 335 case WebAssembly::ARGUMENT_v4i32: 336 case WebAssembly::ARGUMENT_v4i32_S: 337 case WebAssembly::ARGUMENT_v2i64: 338 case WebAssembly::ARGUMENT_v2i64_S: 339 case WebAssembly::ARGUMENT_v4f32: 340 case WebAssembly::ARGUMENT_v4f32_S: 341 case WebAssembly::ARGUMENT_v2f64: 342 case WebAssembly::ARGUMENT_v2f64_S: 343 // These represent values which are live into the function entry, so there's 344 // no instruction to emit. 345 break; 346 case WebAssembly::FALLTHROUGH_RETURN: { 347 // These instructions represent the implicit return at the end of a 348 // function body. 349 if (isVerbose()) { 350 OutStreamer->AddComment("fallthrough-return"); 351 OutStreamer->AddBlankLine(); 352 } 353 break; 354 } 355 case WebAssembly::COMPILER_FENCE: 356 // This is a compiler barrier that prevents instruction reordering during 357 // backend compilation, and should not be emitted. 358 break; 359 case WebAssembly::EXTRACT_EXCEPTION_I32: 360 case WebAssembly::EXTRACT_EXCEPTION_I32_S: 361 // These are pseudo instructions that simulates popping values from stack. 362 // We print these only when we have -wasm-keep-registers on for assembly 363 // readability. 364 if (!WasmKeepRegisters) 365 break; 366 LLVM_FALLTHROUGH; 367 default: { 368 WebAssemblyMCInstLower MCInstLowering(OutContext, *this); 369 MCInst TmpInst; 370 MCInstLowering.lower(MI, TmpInst); 371 EmitToStreamer(*OutStreamer, TmpInst); 372 break; 373 } 374 } 375 } 376 377 bool WebAssemblyAsmPrinter::PrintAsmOperand(const MachineInstr *MI, 378 unsigned OpNo, 379 const char *ExtraCode, 380 raw_ostream &OS) { 381 // First try the generic code, which knows about modifiers like 'c' and 'n'. 382 if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS)) 383 return false; 384 385 if (!ExtraCode) { 386 const MachineOperand &MO = MI->getOperand(OpNo); 387 switch (MO.getType()) { 388 case MachineOperand::MO_Immediate: 389 OS << MO.getImm(); 390 return false; 391 case MachineOperand::MO_Register: 392 // FIXME: only opcode that still contains registers, as required by 393 // MachineInstr::getDebugVariable(). 394 assert(MI->getOpcode() == WebAssembly::INLINEASM); 395 OS << regToString(MO); 396 return false; 397 case MachineOperand::MO_GlobalAddress: 398 PrintSymbolOperand(MO, OS); 399 return false; 400 case MachineOperand::MO_ExternalSymbol: 401 GetExternalSymbolSymbol(MO.getSymbolName())->print(OS, MAI); 402 printOffset(MO.getOffset(), OS); 403 return false; 404 case MachineOperand::MO_MachineBasicBlock: 405 MO.getMBB()->getSymbol()->print(OS, MAI); 406 return false; 407 default: 408 break; 409 } 410 } 411 412 return true; 413 } 414 415 bool WebAssemblyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, 416 unsigned OpNo, 417 const char *ExtraCode, 418 raw_ostream &OS) { 419 // The current approach to inline asm is that "r" constraints are expressed 420 // as local indices, rather than values on the operand stack. This simplifies 421 // using "r" as it eliminates the need to push and pop the values in a 422 // particular order, however it also makes it impossible to have an "m" 423 // constraint. So we don't support it. 424 425 return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, ExtraCode, OS); 426 } 427 428 // Force static initialization. 429 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyAsmPrinter() { 430 RegisterAsmPrinter<WebAssemblyAsmPrinter> X(getTheWebAssemblyTarget32()); 431 RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(getTheWebAssemblyTarget64()); 432 } 433