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 std::string Name; 234 }; 235 236 // Read target features and linkage policies from module metadata 237 SmallVector<FeatureEntry, 4> EmittedFeatures; 238 auto EmitFeature = [&](std::string Feature) { 239 std::string MDKey = (StringRef("wasm-feature-") + Feature).str(); 240 Metadata *Policy = M.getModuleFlag(MDKey); 241 if (Policy == nullptr) 242 return; 243 244 FeatureEntry Entry; 245 Entry.Prefix = 0; 246 Entry.Name = Feature; 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 return; 257 258 EmittedFeatures.push_back(Entry); 259 }; 260 261 for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) { 262 EmitFeature(KV.Key); 263 } 264 // This pseudo-feature tells the linker whether shared memory would be safe 265 EmitFeature("shared-mem"); 266 267 if (EmittedFeatures.size() == 0) 268 return; 269 270 // Emit features and linkage policies into the "target_features" section 271 MCSectionWasm *FeaturesSection = OutContext.getWasmSection( 272 ".custom_section.target_features", SectionKind::getMetadata()); 273 OutStreamer->PushSection(); 274 OutStreamer->SwitchSection(FeaturesSection); 275 276 OutStreamer->emitULEB128IntValue(EmittedFeatures.size()); 277 for (auto &F : EmittedFeatures) { 278 OutStreamer->emitIntValue(F.Prefix, 1); 279 OutStreamer->emitULEB128IntValue(F.Name.size()); 280 OutStreamer->emitBytes(F.Name); 281 } 282 283 OutStreamer->PopSection(); 284 } 285 286 void WebAssemblyAsmPrinter::emitConstantPool() { 287 assert(MF->getConstantPool()->getConstants().empty() && 288 "WebAssembly disables constant pools"); 289 } 290 291 void WebAssemblyAsmPrinter::emitJumpTableInfo() { 292 // Nothing to do; jump tables are incorporated into the instruction stream. 293 } 294 295 void WebAssemblyAsmPrinter::emitFunctionBodyStart() { 296 const Function &F = MF->getFunction(); 297 SmallVector<MVT, 1> ResultVTs; 298 SmallVector<MVT, 4> ParamVTs; 299 computeSignatureVTs(F.getFunctionType(), &F, F, TM, ParamVTs, ResultVTs); 300 301 auto Signature = signatureFromMVTs(ResultVTs, ParamVTs); 302 auto *WasmSym = cast<MCSymbolWasm>(CurrentFnSym); 303 WasmSym->setSignature(Signature.get()); 304 addSignature(std::move(Signature)); 305 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); 306 307 // FIXME: clean up how params and results are emitted (use signatures) 308 getTargetStreamer()->emitFunctionType(WasmSym); 309 310 // Emit the function index. 311 if (MDNode *Idx = F.getMetadata("wasm.index")) { 312 assert(Idx->getNumOperands() == 1); 313 314 getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant( 315 cast<ConstantAsMetadata>(Idx->getOperand(0))->getValue())); 316 } 317 318 SmallVector<wasm::ValType, 16> Locals; 319 valTypesFromMVTs(MFI->getLocals(), Locals); 320 getTargetStreamer()->emitLocal(Locals); 321 322 AsmPrinter::emitFunctionBodyStart(); 323 } 324 325 void WebAssemblyAsmPrinter::emitInstruction(const MachineInstr *MI) { 326 LLVM_DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n'); 327 328 switch (MI->getOpcode()) { 329 case WebAssembly::ARGUMENT_i32: 330 case WebAssembly::ARGUMENT_i32_S: 331 case WebAssembly::ARGUMENT_i64: 332 case WebAssembly::ARGUMENT_i64_S: 333 case WebAssembly::ARGUMENT_f32: 334 case WebAssembly::ARGUMENT_f32_S: 335 case WebAssembly::ARGUMENT_f64: 336 case WebAssembly::ARGUMENT_f64_S: 337 case WebAssembly::ARGUMENT_v16i8: 338 case WebAssembly::ARGUMENT_v16i8_S: 339 case WebAssembly::ARGUMENT_v8i16: 340 case WebAssembly::ARGUMENT_v8i16_S: 341 case WebAssembly::ARGUMENT_v4i32: 342 case WebAssembly::ARGUMENT_v4i32_S: 343 case WebAssembly::ARGUMENT_v2i64: 344 case WebAssembly::ARGUMENT_v2i64_S: 345 case WebAssembly::ARGUMENT_v4f32: 346 case WebAssembly::ARGUMENT_v4f32_S: 347 case WebAssembly::ARGUMENT_v2f64: 348 case WebAssembly::ARGUMENT_v2f64_S: 349 // These represent values which are live into the function entry, so there's 350 // no instruction to emit. 351 break; 352 case WebAssembly::FALLTHROUGH_RETURN: { 353 // These instructions represent the implicit return at the end of a 354 // function body. 355 if (isVerbose()) { 356 OutStreamer->AddComment("fallthrough-return"); 357 OutStreamer->AddBlankLine(); 358 } 359 break; 360 } 361 case WebAssembly::COMPILER_FENCE: 362 // This is a compiler barrier that prevents instruction reordering during 363 // backend compilation, and should not be emitted. 364 break; 365 case WebAssembly::EXTRACT_EXCEPTION_I32: 366 case WebAssembly::EXTRACT_EXCEPTION_I32_S: 367 // These are pseudo instructions that simulates popping values from stack. 368 // We print these only when we have -wasm-keep-registers on for assembly 369 // readability. 370 if (!WasmKeepRegisters) 371 break; 372 LLVM_FALLTHROUGH; 373 default: { 374 WebAssemblyMCInstLower MCInstLowering(OutContext, *this); 375 MCInst TmpInst; 376 MCInstLowering.lower(MI, TmpInst); 377 EmitToStreamer(*OutStreamer, TmpInst); 378 break; 379 } 380 } 381 } 382 383 bool WebAssemblyAsmPrinter::PrintAsmOperand(const MachineInstr *MI, 384 unsigned OpNo, 385 const char *ExtraCode, 386 raw_ostream &OS) { 387 // First try the generic code, which knows about modifiers like 'c' and 'n'. 388 if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS)) 389 return false; 390 391 if (!ExtraCode) { 392 const MachineOperand &MO = MI->getOperand(OpNo); 393 switch (MO.getType()) { 394 case MachineOperand::MO_Immediate: 395 OS << MO.getImm(); 396 return false; 397 case MachineOperand::MO_Register: 398 // FIXME: only opcode that still contains registers, as required by 399 // MachineInstr::getDebugVariable(). 400 assert(MI->getOpcode() == WebAssembly::INLINEASM); 401 OS << regToString(MO); 402 return false; 403 case MachineOperand::MO_GlobalAddress: 404 PrintSymbolOperand(MO, OS); 405 return false; 406 case MachineOperand::MO_ExternalSymbol: 407 GetExternalSymbolSymbol(MO.getSymbolName())->print(OS, MAI); 408 printOffset(MO.getOffset(), OS); 409 return false; 410 case MachineOperand::MO_MachineBasicBlock: 411 MO.getMBB()->getSymbol()->print(OS, MAI); 412 return false; 413 default: 414 break; 415 } 416 } 417 418 return true; 419 } 420 421 bool WebAssemblyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, 422 unsigned OpNo, 423 const char *ExtraCode, 424 raw_ostream &OS) { 425 // The current approach to inline asm is that "r" constraints are expressed 426 // as local indices, rather than values on the operand stack. This simplifies 427 // using "r" as it eliminates the need to push and pop the values in a 428 // particular order, however it also makes it impossible to have an "m" 429 // constraint. So we don't support it. 430 431 return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, ExtraCode, OS); 432 } 433 434 // Force static initialization. 435 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyAsmPrinter() { 436 RegisterAsmPrinter<WebAssemblyAsmPrinter> X(getTheWebAssemblyTarget32()); 437 RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(getTheWebAssemblyTarget64()); 438 } 439