1 //===-- WebAssemblyAsmPrinter.cpp - WebAssembly LLVM assembly writer ------===//
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
7 //===----------------------------------------------------------------------===//
10 /// This file contains a printer that converts from our internal
11 /// representation of machine-dependent LLVM code to the WebAssembly assembly
14 //===----------------------------------------------------------------------===//
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"
49 #define DEBUG_TYPE "asm-printer"
51 extern cl::opt
<bool> WasmKeepRegisters
;
53 //===----------------------------------------------------------------------===//
55 //===----------------------------------------------------------------------===//
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
))
64 LLVM_DEBUG(errs() << "Unknown type for register number: " << RegNo
);
65 llvm_unreachable("Unknown register type");
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
);
79 WebAssemblyTargetStreamer
*WebAssemblyAsmPrinter::getTargetStreamer() {
80 MCTargetStreamer
*TS
= OutStreamer
->getTargetStreamer();
81 return static_cast<WebAssemblyTargetStreamer
*>(TS
);
84 //===----------------------------------------------------------------------===//
85 // WebAssemblyAsmPrinter Implementation.
86 //===----------------------------------------------------------------------===//
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
);
98 for (const auto &F
: M
) {
99 // Emit function type info for all undefined functions
100 if (F
.isDeclarationForLinker() && !F
.isIntrinsic()) {
101 SmallVector
<MVT
, 4> Results
;
102 SmallVector
<MVT
, 4> Params
;
103 computeSignatureVTs(F
.getFunctionType(), F
, TM
, Params
, Results
);
104 auto *Sym
= cast
<MCSymbolWasm
>(getSymbol(&F
));
105 Sym
->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION
);
106 if (!Sym
->getSignature()) {
107 auto Signature
= signatureFromMVTs(Results
, Params
);
108 Sym
->setSignature(Signature
.get());
109 addSignature(std::move(Signature
));
111 // FIXME: this was originally intended for post-linking and was only used
112 // for imports that were only called indirectly (i.e. s2wasm could not
113 // infer the type from a call). With object files it applies to all
114 // imports. so fix the names and the tests, or rethink how import
115 // delcarations work in asm files.
116 getTargetStreamer()->emitFunctionType(Sym
);
118 if (TM
.getTargetTriple().isOSBinFormatWasm() &&
119 F
.hasFnAttribute("wasm-import-module")) {
121 F
.getFnAttribute("wasm-import-module").getValueAsString();
122 Sym
->setImportModule(Name
);
123 getTargetStreamer()->emitImportModule(Sym
, Name
);
125 if (TM
.getTargetTriple().isOSBinFormatWasm() &&
126 F
.hasFnAttribute("wasm-import-name")) {
128 F
.getFnAttribute("wasm-import-name").getValueAsString();
129 Sym
->setImportName(Name
);
130 getTargetStreamer()->emitImportName(Sym
, Name
);
135 for (const auto &G
: M
.globals()) {
136 if (!G
.hasInitializer() && G
.hasExternalLinkage()) {
137 if (G
.getValueType()->isSized()) {
138 uint16_t Size
= M
.getDataLayout().getTypeAllocSize(G
.getValueType());
139 OutStreamer
->emitELFSize(getSymbol(&G
),
140 MCConstantExpr::create(Size
, OutContext
));
145 if (const NamedMDNode
*Named
= M
.getNamedMetadata("wasm.custom_sections")) {
146 for (const Metadata
*MD
: Named
->operands()) {
147 const auto *Tuple
= dyn_cast
<MDTuple
>(MD
);
148 if (!Tuple
|| Tuple
->getNumOperands() != 2)
150 const MDString
*Name
= dyn_cast
<MDString
>(Tuple
->getOperand(0));
151 const MDString
*Contents
= dyn_cast
<MDString
>(Tuple
->getOperand(1));
152 if (!Name
|| !Contents
)
155 OutStreamer
->PushSection();
156 std::string SectionName
= (".custom_section." + Name
->getString()).str();
157 MCSectionWasm
*MySection
=
158 OutContext
.getWasmSection(SectionName
, SectionKind::getMetadata());
159 OutStreamer
->SwitchSection(MySection
);
160 OutStreamer
->EmitBytes(Contents
->getString());
161 OutStreamer
->PopSection();
166 EmitTargetFeatures(M
);
169 void WebAssemblyAsmPrinter::EmitProducerInfo(Module
&M
) {
170 llvm::SmallVector
<std::pair
<std::string
, std::string
>, 4> Languages
;
171 if (const NamedMDNode
*Debug
= M
.getNamedMetadata("llvm.dbg.cu")) {
172 llvm::SmallSet
<StringRef
, 4> SeenLanguages
;
173 for (size_t I
= 0, E
= Debug
->getNumOperands(); I
< E
; ++I
) {
174 const auto *CU
= cast
<DICompileUnit
>(Debug
->getOperand(I
));
175 StringRef Language
= dwarf::LanguageString(CU
->getSourceLanguage());
176 Language
.consume_front("DW_LANG_");
177 if (SeenLanguages
.insert(Language
).second
)
178 Languages
.emplace_back(Language
.str(), "");
182 llvm::SmallVector
<std::pair
<std::string
, std::string
>, 4> Tools
;
183 if (const NamedMDNode
*Ident
= M
.getNamedMetadata("llvm.ident")) {
184 llvm::SmallSet
<StringRef
, 4> SeenTools
;
185 for (size_t I
= 0, E
= Ident
->getNumOperands(); I
< E
; ++I
) {
186 const auto *S
= cast
<MDString
>(Ident
->getOperand(I
)->getOperand(0));
187 std::pair
<StringRef
, StringRef
> Field
= S
->getString().split("version");
188 StringRef Name
= Field
.first
.trim();
189 StringRef Version
= Field
.second
.trim();
190 if (SeenTools
.insert(Name
).second
)
191 Tools
.emplace_back(Name
.str(), Version
.str());
195 int FieldCount
= int(!Languages
.empty()) + int(!Tools
.empty());
196 if (FieldCount
!= 0) {
197 MCSectionWasm
*Producers
= OutContext
.getWasmSection(
198 ".custom_section.producers", SectionKind::getMetadata());
199 OutStreamer
->PushSection();
200 OutStreamer
->SwitchSection(Producers
);
201 OutStreamer
->EmitULEB128IntValue(FieldCount
);
202 for (auto &Producers
: {std::make_pair("language", &Languages
),
203 std::make_pair("processed-by", &Tools
)}) {
204 if (Producers
.second
->empty())
206 OutStreamer
->EmitULEB128IntValue(strlen(Producers
.first
));
207 OutStreamer
->EmitBytes(Producers
.first
);
208 OutStreamer
->EmitULEB128IntValue(Producers
.second
->size());
209 for (auto &Producer
: *Producers
.second
) {
210 OutStreamer
->EmitULEB128IntValue(Producer
.first
.size());
211 OutStreamer
->EmitBytes(Producer
.first
);
212 OutStreamer
->EmitULEB128IntValue(Producer
.second
.size());
213 OutStreamer
->EmitBytes(Producer
.second
);
216 OutStreamer
->PopSection();
220 void WebAssemblyAsmPrinter::EmitTargetFeatures(Module
&M
) {
221 struct FeatureEntry
{
226 // Read target features and linkage policies from module metadata
227 SmallVector
<FeatureEntry
, 4> EmittedFeatures
;
228 for (const SubtargetFeatureKV
&KV
: WebAssemblyFeatureKV
) {
229 std::string MDKey
= (StringRef("wasm-feature-") + KV
.Key
).str();
230 Metadata
*Policy
= M
.getModuleFlag(MDKey
);
231 if (Policy
== nullptr)
238 if (auto *MD
= cast
<ConstantAsMetadata
>(Policy
))
239 if (auto *I
= cast
<ConstantInt
>(MD
->getValue()))
240 Entry
.Prefix
= I
->getZExtValue();
242 // Silently ignore invalid metadata
243 if (Entry
.Prefix
!= wasm::WASM_FEATURE_PREFIX_USED
&&
244 Entry
.Prefix
!= wasm::WASM_FEATURE_PREFIX_REQUIRED
&&
245 Entry
.Prefix
!= wasm::WASM_FEATURE_PREFIX_DISALLOWED
)
248 EmittedFeatures
.push_back(Entry
);
251 if (EmittedFeatures
.size() == 0)
254 // Emit features and linkage policies into the "target_features" section
255 MCSectionWasm
*FeaturesSection
= OutContext
.getWasmSection(
256 ".custom_section.target_features", SectionKind::getMetadata());
257 OutStreamer
->PushSection();
258 OutStreamer
->SwitchSection(FeaturesSection
);
260 OutStreamer
->EmitULEB128IntValue(EmittedFeatures
.size());
261 for (auto &F
: EmittedFeatures
) {
262 OutStreamer
->EmitIntValue(F
.Prefix
, 1);
263 OutStreamer
->EmitULEB128IntValue(F
.Name
.size());
264 OutStreamer
->EmitBytes(F
.Name
);
267 OutStreamer
->PopSection();
270 void WebAssemblyAsmPrinter::EmitConstantPool() {
271 assert(MF
->getConstantPool()->getConstants().empty() &&
272 "WebAssembly disables constant pools");
275 void WebAssemblyAsmPrinter::EmitJumpTableInfo() {
276 // Nothing to do; jump tables are incorporated into the instruction stream.
279 void WebAssemblyAsmPrinter::EmitFunctionBodyStart() {
280 const Function
&F
= MF
->getFunction();
281 SmallVector
<MVT
, 1> ResultVTs
;
282 SmallVector
<MVT
, 4> ParamVTs
;
283 computeSignatureVTs(F
.getFunctionType(), F
, TM
, ParamVTs
, ResultVTs
);
284 auto Signature
= signatureFromMVTs(ResultVTs
, ParamVTs
);
285 auto *WasmSym
= cast
<MCSymbolWasm
>(CurrentFnSym
);
286 WasmSym
->setSignature(Signature
.get());
287 addSignature(std::move(Signature
));
288 WasmSym
->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION
);
290 // FIXME: clean up how params and results are emitted (use signatures)
291 getTargetStreamer()->emitFunctionType(WasmSym
);
293 // Emit the function index.
294 if (MDNode
*Idx
= F
.getMetadata("wasm.index")) {
295 assert(Idx
->getNumOperands() == 1);
297 getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant(
298 cast
<ConstantAsMetadata
>(Idx
->getOperand(0))->getValue()));
301 SmallVector
<wasm::ValType
, 16> Locals
;
302 valTypesFromMVTs(MFI
->getLocals(), Locals
);
303 getTargetStreamer()->emitLocal(Locals
);
305 AsmPrinter::EmitFunctionBodyStart();
308 void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr
*MI
) {
309 LLVM_DEBUG(dbgs() << "EmitInstruction: " << *MI
<< '\n');
311 switch (MI
->getOpcode()) {
312 case WebAssembly::ARGUMENT_i32
:
313 case WebAssembly::ARGUMENT_i32_S
:
314 case WebAssembly::ARGUMENT_i64
:
315 case WebAssembly::ARGUMENT_i64_S
:
316 case WebAssembly::ARGUMENT_f32
:
317 case WebAssembly::ARGUMENT_f32_S
:
318 case WebAssembly::ARGUMENT_f64
:
319 case WebAssembly::ARGUMENT_f64_S
:
320 case WebAssembly::ARGUMENT_v16i8
:
321 case WebAssembly::ARGUMENT_v16i8_S
:
322 case WebAssembly::ARGUMENT_v8i16
:
323 case WebAssembly::ARGUMENT_v8i16_S
:
324 case WebAssembly::ARGUMENT_v4i32
:
325 case WebAssembly::ARGUMENT_v4i32_S
:
326 case WebAssembly::ARGUMENT_v2i64
:
327 case WebAssembly::ARGUMENT_v2i64_S
:
328 case WebAssembly::ARGUMENT_v4f32
:
329 case WebAssembly::ARGUMENT_v4f32_S
:
330 case WebAssembly::ARGUMENT_v2f64
:
331 case WebAssembly::ARGUMENT_v2f64_S
:
332 // These represent values which are live into the function entry, so there's
333 // no instruction to emit.
335 case WebAssembly::FALLTHROUGH_RETURN
: {
336 // These instructions represent the implicit return at the end of a
339 OutStreamer
->AddComment("fallthrough-return");
340 OutStreamer
->AddBlankLine();
344 case WebAssembly::COMPILER_FENCE
:
345 // This is a compiler barrier that prevents instruction reordering during
346 // backend compilation, and should not be emitted.
348 case WebAssembly::EXTRACT_EXCEPTION_I32
:
349 case WebAssembly::EXTRACT_EXCEPTION_I32_S
:
350 // These are pseudo instructions that simulates popping values from stack.
351 // We print these only when we have -wasm-keep-registers on for assembly
353 if (!WasmKeepRegisters
)
357 WebAssemblyMCInstLower
MCInstLowering(OutContext
, *this);
359 MCInstLowering
.lower(MI
, TmpInst
);
360 EmitToStreamer(*OutStreamer
, TmpInst
);
366 bool WebAssemblyAsmPrinter::PrintAsmOperand(const MachineInstr
*MI
,
368 const char *ExtraCode
,
370 // First try the generic code, which knows about modifiers like 'c' and 'n'.
371 if (!AsmPrinter::PrintAsmOperand(MI
, OpNo
, ExtraCode
, OS
))
375 const MachineOperand
&MO
= MI
->getOperand(OpNo
);
376 switch (MO
.getType()) {
377 case MachineOperand::MO_Immediate
:
380 case MachineOperand::MO_Register
:
381 // FIXME: only opcode that still contains registers, as required by
382 // MachineInstr::getDebugVariable().
383 assert(MI
->getOpcode() == WebAssembly::INLINEASM
);
384 OS
<< regToString(MO
);
386 case MachineOperand::MO_GlobalAddress
:
387 PrintSymbolOperand(MO
, OS
);
389 case MachineOperand::MO_ExternalSymbol
:
390 GetExternalSymbolSymbol(MO
.getSymbolName())->print(OS
, MAI
);
391 printOffset(MO
.getOffset(), OS
);
393 case MachineOperand::MO_MachineBasicBlock
:
394 MO
.getMBB()->getSymbol()->print(OS
, MAI
);
404 bool WebAssemblyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr
*MI
,
406 const char *ExtraCode
,
408 // The current approach to inline asm is that "r" constraints are expressed
409 // as local indices, rather than values on the operand stack. This simplifies
410 // using "r" as it eliminates the need to push and pop the values in a
411 // particular order, however it also makes it impossible to have an "m"
412 // constraint. So we don't support it.
414 return AsmPrinter::PrintAsmMemoryOperand(MI
, OpNo
, ExtraCode
, OS
);
417 // Force static initialization.
418 extern "C" void LLVMInitializeWebAssemblyAsmPrinter() {
419 RegisterAsmPrinter
<WebAssemblyAsmPrinter
> X(getTheWebAssemblyTarget32());
420 RegisterAsmPrinter
<WebAssemblyAsmPrinter
> Y(getTheWebAssemblyTarget64());