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, 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(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(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(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, TM, ParamVTs, ResultVTs); 294 auto Signature = signatureFromMVTs(ResultVTs, ParamVTs); 295 auto *WasmSym = cast<MCSymbolWasm>(CurrentFnSym); 296 WasmSym->setSignature(Signature.get()); 297 addSignature(std::move(Signature)); 298 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); 299 300 // FIXME: clean up how params and results are emitted (use signatures) 301 getTargetStreamer()->emitFunctionType(WasmSym); 302 303 // Emit the function index. 304 if (MDNode *Idx = F.getMetadata("wasm.index")) { 305 assert(Idx->getNumOperands() == 1); 306 307 getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant( 308 cast<ConstantAsMetadata>(Idx->getOperand(0))->getValue())); 309 } 310 311 SmallVector<wasm::ValType, 16> Locals; 312 valTypesFromMVTs(MFI->getLocals(), Locals); 313 getTargetStreamer()->emitLocal(Locals); 314 315 AsmPrinter::EmitFunctionBodyStart(); 316 } 317 318 void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) { 319 LLVM_DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n'); 320 321 switch (MI->getOpcode()) { 322 case WebAssembly::ARGUMENT_i32: 323 case WebAssembly::ARGUMENT_i32_S: 324 case WebAssembly::ARGUMENT_i64: 325 case WebAssembly::ARGUMENT_i64_S: 326 case WebAssembly::ARGUMENT_f32: 327 case WebAssembly::ARGUMENT_f32_S: 328 case WebAssembly::ARGUMENT_f64: 329 case WebAssembly::ARGUMENT_f64_S: 330 case WebAssembly::ARGUMENT_v16i8: 331 case WebAssembly::ARGUMENT_v16i8_S: 332 case WebAssembly::ARGUMENT_v8i16: 333 case WebAssembly::ARGUMENT_v8i16_S: 334 case WebAssembly::ARGUMENT_v4i32: 335 case WebAssembly::ARGUMENT_v4i32_S: 336 case WebAssembly::ARGUMENT_v2i64: 337 case WebAssembly::ARGUMENT_v2i64_S: 338 case WebAssembly::ARGUMENT_v4f32: 339 case WebAssembly::ARGUMENT_v4f32_S: 340 case WebAssembly::ARGUMENT_v2f64: 341 case WebAssembly::ARGUMENT_v2f64_S: 342 // These represent values which are live into the function entry, so there's 343 // no instruction to emit. 344 break; 345 case WebAssembly::FALLTHROUGH_RETURN: { 346 // These instructions represent the implicit return at the end of a 347 // function body. 348 if (isVerbose()) { 349 OutStreamer->AddComment("fallthrough-return"); 350 OutStreamer->AddBlankLine(); 351 } 352 break; 353 } 354 case WebAssembly::COMPILER_FENCE: 355 // This is a compiler barrier that prevents instruction reordering during 356 // backend compilation, and should not be emitted. 357 break; 358 case WebAssembly::EXTRACT_EXCEPTION_I32: 359 case WebAssembly::EXTRACT_EXCEPTION_I32_S: 360 // These are pseudo instructions that simulates popping values from stack. 361 // We print these only when we have -wasm-keep-registers on for assembly 362 // readability. 363 if (!WasmKeepRegisters) 364 break; 365 LLVM_FALLTHROUGH; 366 default: { 367 WebAssemblyMCInstLower MCInstLowering(OutContext, *this); 368 MCInst TmpInst; 369 MCInstLowering.lower(MI, TmpInst); 370 EmitToStreamer(*OutStreamer, TmpInst); 371 break; 372 } 373 } 374 } 375 376 bool WebAssemblyAsmPrinter::PrintAsmOperand(const MachineInstr *MI, 377 unsigned OpNo, 378 const char *ExtraCode, 379 raw_ostream &OS) { 380 // First try the generic code, which knows about modifiers like 'c' and 'n'. 381 if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS)) 382 return false; 383 384 if (!ExtraCode) { 385 const MachineOperand &MO = MI->getOperand(OpNo); 386 switch (MO.getType()) { 387 case MachineOperand::MO_Immediate: 388 OS << MO.getImm(); 389 return false; 390 case MachineOperand::MO_Register: 391 // FIXME: only opcode that still contains registers, as required by 392 // MachineInstr::getDebugVariable(). 393 assert(MI->getOpcode() == WebAssembly::INLINEASM); 394 OS << regToString(MO); 395 return false; 396 case MachineOperand::MO_GlobalAddress: 397 PrintSymbolOperand(MO, OS); 398 return false; 399 case MachineOperand::MO_ExternalSymbol: 400 GetExternalSymbolSymbol(MO.getSymbolName())->print(OS, MAI); 401 printOffset(MO.getOffset(), OS); 402 return false; 403 case MachineOperand::MO_MachineBasicBlock: 404 MO.getMBB()->getSymbol()->print(OS, MAI); 405 return false; 406 default: 407 break; 408 } 409 } 410 411 return true; 412 } 413 414 bool WebAssemblyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, 415 unsigned OpNo, 416 const char *ExtraCode, 417 raw_ostream &OS) { 418 // The current approach to inline asm is that "r" constraints are expressed 419 // as local indices, rather than values on the operand stack. This simplifies 420 // using "r" as it eliminates the need to push and pop the values in a 421 // particular order, however it also makes it impossible to have an "m" 422 // constraint. So we don't support it. 423 424 return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, ExtraCode, OS); 425 } 426 427 // Force static initialization. 428 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyAsmPrinter() { 429 RegisterAsmPrinter<WebAssemblyAsmPrinter> X(getTheWebAssemblyTarget32()); 430 RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(getTheWebAssemblyTarget64()); 431 } 432