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 "InstPrinter/WebAssemblyInstPrinter.h"
18 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
19 #include "MCTargetDesc/WebAssemblyTargetStreamer.h"
20 #include "WebAssembly.h"
21 #include "WebAssemblyMCInstLower.h"
22 #include "WebAssemblyMachineFunctionInfo.h"
23 #include "WebAssemblyRegisterInfo.h"
24 #include "llvm/ADT/SmallSet.h"
25 #include "llvm/ADT/StringExtras.h"
26 #include "llvm/CodeGen/Analysis.h"
27 #include "llvm/CodeGen/AsmPrinter.h"
28 #include "llvm/CodeGen/MachineConstantPool.h"
29 #include "llvm/CodeGen/MachineInstr.h"
30 #include "llvm/CodeGen/MachineModuleInfoImpls.h"
31 #include "llvm/IR/DataLayout.h"
32 #include "llvm/IR/DebugInfoMetadata.h"
33 #include "llvm/IR/GlobalVariable.h"
34 #include "llvm/MC/MCContext.h"
35 #include "llvm/MC/MCSectionWasm.h"
36 #include "llvm/MC/MCStreamer.h"
37 #include "llvm/MC/MCSymbol.h"
38 #include "llvm/MC/MCSymbolWasm.h"
39 #include "llvm/Support/Debug.h"
40 #include "llvm/Support/TargetRegistry.h"
41 #include "llvm/Support/raw_ostream.h"
44 #define DEBUG_TYPE "asm-printer"
46 extern cl::opt
<bool> WasmKeepRegisters
;
48 //===----------------------------------------------------------------------===//
50 //===----------------------------------------------------------------------===//
52 MVT
WebAssemblyAsmPrinter::getRegType(unsigned RegNo
) const {
53 const TargetRegisterInfo
*TRI
= Subtarget
->getRegisterInfo();
54 const TargetRegisterClass
*TRC
= MRI
->getRegClass(RegNo
);
55 for (MVT T
: {MVT::i32
, MVT::i64
, MVT::f32
, MVT::f64
, MVT::v16i8
, MVT::v8i16
,
56 MVT::v4i32
, MVT::v2i64
, MVT::v4f32
, MVT::v2f64
})
57 if (TRI
->isTypeLegalForClass(*TRC
, T
))
59 LLVM_DEBUG(errs() << "Unknown type for register number: " << RegNo
);
60 llvm_unreachable("Unknown register type");
64 std::string
WebAssemblyAsmPrinter::regToString(const MachineOperand
&MO
) {
65 unsigned RegNo
= MO
.getReg();
66 assert(TargetRegisterInfo::isVirtualRegister(RegNo
) &&
67 "Unlowered physical register encountered during assembly printing");
68 assert(!MFI
->isVRegStackified(RegNo
));
69 unsigned WAReg
= MFI
->getWAReg(RegNo
);
70 assert(WAReg
!= WebAssemblyFunctionInfo::UnusedReg
);
71 return '$' + utostr(WAReg
);
74 WebAssemblyTargetStreamer
*WebAssemblyAsmPrinter::getTargetStreamer() {
75 MCTargetStreamer
*TS
= OutStreamer
->getTargetStreamer();
76 return static_cast<WebAssemblyTargetStreamer
*>(TS
);
79 //===----------------------------------------------------------------------===//
80 // WebAssemblyAsmPrinter Implementation.
81 //===----------------------------------------------------------------------===//
83 void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module
&M
) {
84 for (auto &It
: OutContext
.getSymbols()) {
85 // Emit a .globaltype and .eventtype declaration.
86 auto Sym
= cast
<MCSymbolWasm
>(It
.getValue());
87 if (Sym
->getType() == wasm::WASM_SYMBOL_TYPE_GLOBAL
)
88 getTargetStreamer()->emitGlobalType(Sym
);
89 else if (Sym
->getType() == wasm::WASM_SYMBOL_TYPE_EVENT
)
90 getTargetStreamer()->emitEventType(Sym
);
93 for (const auto &F
: M
) {
94 // Emit function type info for all undefined functions
95 if (F
.isDeclarationForLinker() && !F
.isIntrinsic()) {
96 SmallVector
<MVT
, 4> Results
;
97 SmallVector
<MVT
, 4> Params
;
98 computeSignatureVTs(F
.getFunctionType(), F
, TM
, Params
, Results
);
99 auto *Sym
= cast
<MCSymbolWasm
>(getSymbol(&F
));
100 Sym
->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION
);
101 if (!Sym
->getSignature()) {
102 auto Signature
= signatureFromMVTs(Results
, Params
);
103 Sym
->setSignature(Signature
.get());
104 addSignature(std::move(Signature
));
106 // FIXME: this was originally intended for post-linking and was only used
107 // for imports that were only called indirectly (i.e. s2wasm could not
108 // infer the type from a call). With object files it applies to all
109 // imports. so fix the names and the tests, or rethink how import
110 // delcarations work in asm files.
111 getTargetStreamer()->emitFunctionType(Sym
);
113 if (TM
.getTargetTriple().isOSBinFormatWasm() &&
114 F
.hasFnAttribute("wasm-import-module")) {
116 F
.getFnAttribute("wasm-import-module").getValueAsString();
117 Sym
->setImportModule(Name
);
118 getTargetStreamer()->emitImportModule(Sym
, Name
);
120 if (TM
.getTargetTriple().isOSBinFormatWasm() &&
121 F
.hasFnAttribute("wasm-import-name")) {
123 F
.getFnAttribute("wasm-import-name").getValueAsString();
124 Sym
->setImportName(Name
);
125 getTargetStreamer()->emitImportName(Sym
, Name
);
130 for (const auto &G
: M
.globals()) {
131 if (!G
.hasInitializer() && G
.hasExternalLinkage()) {
132 if (G
.getValueType()->isSized()) {
133 uint16_t Size
= M
.getDataLayout().getTypeAllocSize(G
.getValueType());
134 OutStreamer
->emitELFSize(getSymbol(&G
),
135 MCConstantExpr::create(Size
, OutContext
));
140 if (const NamedMDNode
*Named
= M
.getNamedMetadata("wasm.custom_sections")) {
141 for (const Metadata
*MD
: Named
->operands()) {
142 const auto *Tuple
= dyn_cast
<MDTuple
>(MD
);
143 if (!Tuple
|| Tuple
->getNumOperands() != 2)
145 const MDString
*Name
= dyn_cast
<MDString
>(Tuple
->getOperand(0));
146 const MDString
*Contents
= dyn_cast
<MDString
>(Tuple
->getOperand(1));
147 if (!Name
|| !Contents
)
150 OutStreamer
->PushSection();
151 std::string SectionName
= (".custom_section." + Name
->getString()).str();
152 MCSectionWasm
*MySection
=
153 OutContext
.getWasmSection(SectionName
, SectionKind::getMetadata());
154 OutStreamer
->SwitchSection(MySection
);
155 OutStreamer
->EmitBytes(Contents
->getString());
156 OutStreamer
->PopSection();
163 void WebAssemblyAsmPrinter::EmitProducerInfo(Module
&M
) {
164 llvm::SmallVector
<std::pair
<std::string
, std::string
>, 4> Languages
;
165 if (const NamedMDNode
*Debug
= M
.getNamedMetadata("llvm.dbg.cu")) {
166 llvm::SmallSet
<StringRef
, 4> SeenLanguages
;
167 for (size_t I
= 0, E
= Debug
->getNumOperands(); I
< E
; ++I
) {
168 const auto *CU
= cast
<DICompileUnit
>(Debug
->getOperand(I
));
169 StringRef Language
= dwarf::LanguageString(CU
->getSourceLanguage());
170 Language
.consume_front("DW_LANG_");
171 if (SeenLanguages
.insert(Language
).second
)
172 Languages
.emplace_back(Language
.str(), "");
176 llvm::SmallVector
<std::pair
<std::string
, std::string
>, 4> Tools
;
177 if (const NamedMDNode
*Ident
= M
.getNamedMetadata("llvm.ident")) {
178 llvm::SmallSet
<StringRef
, 4> SeenTools
;
179 for (size_t I
= 0, E
= Ident
->getNumOperands(); I
< E
; ++I
) {
180 const auto *S
= cast
<MDString
>(Ident
->getOperand(I
)->getOperand(0));
181 std::pair
<StringRef
, StringRef
> Field
= S
->getString().split("version");
182 StringRef Name
= Field
.first
.trim();
183 StringRef Version
= Field
.second
.trim();
184 if (SeenTools
.insert(Name
).second
)
185 Tools
.emplace_back(Name
.str(), Version
.str());
189 int FieldCount
= int(!Languages
.empty()) + int(!Tools
.empty());
190 if (FieldCount
!= 0) {
191 MCSectionWasm
*Producers
= OutContext
.getWasmSection(
192 ".custom_section.producers", SectionKind::getMetadata());
193 OutStreamer
->PushSection();
194 OutStreamer
->SwitchSection(Producers
);
195 OutStreamer
->EmitULEB128IntValue(FieldCount
);
196 for (auto &Producers
: {std::make_pair("language", &Languages
),
197 std::make_pair("processed-by", &Tools
)}) {
198 if (Producers
.second
->empty())
200 OutStreamer
->EmitULEB128IntValue(strlen(Producers
.first
));
201 OutStreamer
->EmitBytes(Producers
.first
);
202 OutStreamer
->EmitULEB128IntValue(Producers
.second
->size());
203 for (auto &Producer
: *Producers
.second
) {
204 OutStreamer
->EmitULEB128IntValue(Producer
.first
.size());
205 OutStreamer
->EmitBytes(Producer
.first
);
206 OutStreamer
->EmitULEB128IntValue(Producer
.second
.size());
207 OutStreamer
->EmitBytes(Producer
.second
);
210 OutStreamer
->PopSection();
214 void WebAssemblyAsmPrinter::EmitConstantPool() {
215 assert(MF
->getConstantPool()->getConstants().empty() &&
216 "WebAssembly disables constant pools");
219 void WebAssemblyAsmPrinter::EmitJumpTableInfo() {
220 // Nothing to do; jump tables are incorporated into the instruction stream.
223 void WebAssemblyAsmPrinter::EmitFunctionBodyStart() {
224 const Function
&F
= MF
->getFunction();
225 SmallVector
<MVT
, 1> ResultVTs
;
226 SmallVector
<MVT
, 4> ParamVTs
;
227 computeSignatureVTs(F
.getFunctionType(), F
, TM
, ParamVTs
, ResultVTs
);
228 auto Signature
= signatureFromMVTs(ResultVTs
, ParamVTs
);
229 auto *WasmSym
= cast
<MCSymbolWasm
>(CurrentFnSym
);
230 WasmSym
->setSignature(Signature
.get());
231 addSignature(std::move(Signature
));
232 WasmSym
->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION
);
234 // FIXME: clean up how params and results are emitted (use signatures)
235 getTargetStreamer()->emitFunctionType(WasmSym
);
237 // Emit the function index.
238 if (MDNode
*Idx
= F
.getMetadata("wasm.index")) {
239 assert(Idx
->getNumOperands() == 1);
241 getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant(
242 cast
<ConstantAsMetadata
>(Idx
->getOperand(0))->getValue()));
245 SmallVector
<wasm::ValType
, 16> Locals
;
246 valTypesFromMVTs(MFI
->getLocals(), Locals
);
247 getTargetStreamer()->emitLocal(Locals
);
249 AsmPrinter::EmitFunctionBodyStart();
252 void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr
*MI
) {
253 LLVM_DEBUG(dbgs() << "EmitInstruction: " << *MI
<< '\n');
255 switch (MI
->getOpcode()) {
256 case WebAssembly::ARGUMENT_i32
:
257 case WebAssembly::ARGUMENT_i32_S
:
258 case WebAssembly::ARGUMENT_i64
:
259 case WebAssembly::ARGUMENT_i64_S
:
260 case WebAssembly::ARGUMENT_f32
:
261 case WebAssembly::ARGUMENT_f32_S
:
262 case WebAssembly::ARGUMENT_f64
:
263 case WebAssembly::ARGUMENT_f64_S
:
264 case WebAssembly::ARGUMENT_v16i8
:
265 case WebAssembly::ARGUMENT_v16i8_S
:
266 case WebAssembly::ARGUMENT_v8i16
:
267 case WebAssembly::ARGUMENT_v8i16_S
:
268 case WebAssembly::ARGUMENT_v4i32
:
269 case WebAssembly::ARGUMENT_v4i32_S
:
270 case WebAssembly::ARGUMENT_v2i64
:
271 case WebAssembly::ARGUMENT_v2i64_S
:
272 case WebAssembly::ARGUMENT_v4f32
:
273 case WebAssembly::ARGUMENT_v4f32_S
:
274 case WebAssembly::ARGUMENT_v2f64
:
275 case WebAssembly::ARGUMENT_v2f64_S
:
276 // These represent values which are live into the function entry, so there's
277 // no instruction to emit.
279 case WebAssembly::FALLTHROUGH_RETURN_I32
:
280 case WebAssembly::FALLTHROUGH_RETURN_I32_S
:
281 case WebAssembly::FALLTHROUGH_RETURN_I64
:
282 case WebAssembly::FALLTHROUGH_RETURN_I64_S
:
283 case WebAssembly::FALLTHROUGH_RETURN_F32
:
284 case WebAssembly::FALLTHROUGH_RETURN_F32_S
:
285 case WebAssembly::FALLTHROUGH_RETURN_F64
:
286 case WebAssembly::FALLTHROUGH_RETURN_F64_S
:
287 case WebAssembly::FALLTHROUGH_RETURN_v16i8
:
288 case WebAssembly::FALLTHROUGH_RETURN_v16i8_S
:
289 case WebAssembly::FALLTHROUGH_RETURN_v8i16
:
290 case WebAssembly::FALLTHROUGH_RETURN_v8i16_S
:
291 case WebAssembly::FALLTHROUGH_RETURN_v4i32
:
292 case WebAssembly::FALLTHROUGH_RETURN_v4i32_S
:
293 case WebAssembly::FALLTHROUGH_RETURN_v2i64
:
294 case WebAssembly::FALLTHROUGH_RETURN_v2i64_S
:
295 case WebAssembly::FALLTHROUGH_RETURN_v4f32
:
296 case WebAssembly::FALLTHROUGH_RETURN_v4f32_S
:
297 case WebAssembly::FALLTHROUGH_RETURN_v2f64
:
298 case WebAssembly::FALLTHROUGH_RETURN_v2f64_S
: {
299 // These instructions represent the implicit return at the end of a
300 // function body. Always pops one value off the stack.
302 OutStreamer
->AddComment("fallthrough-return-value");
303 OutStreamer
->AddBlankLine();
307 case WebAssembly::FALLTHROUGH_RETURN_VOID
:
308 case WebAssembly::FALLTHROUGH_RETURN_VOID_S
:
309 // This instruction represents the implicit return at the end of a
310 // function body with no return value.
312 OutStreamer
->AddComment("fallthrough-return-void");
313 OutStreamer
->AddBlankLine();
316 case WebAssembly::EXTRACT_EXCEPTION_I32
:
317 case WebAssembly::EXTRACT_EXCEPTION_I32_S
:
318 // These are pseudo instructions that simulates popping values from stack.
319 // We print these only when we have -wasm-keep-registers on for assembly
321 if (!WasmKeepRegisters
)
325 WebAssemblyMCInstLower
MCInstLowering(OutContext
, *this);
327 MCInstLowering
.lower(MI
, TmpInst
);
328 EmitToStreamer(*OutStreamer
, TmpInst
);
334 const MCExpr
*WebAssemblyAsmPrinter::lowerConstant(const Constant
*CV
) {
335 if (const auto *GV
= dyn_cast
<GlobalValue
>(CV
))
336 if (GV
->getValueType()->isFunctionTy()) {
337 return MCSymbolRefExpr::create(
338 getSymbol(GV
), MCSymbolRefExpr::VK_WebAssembly_FUNCTION
, OutContext
);
340 return AsmPrinter::lowerConstant(CV
);
343 bool WebAssemblyAsmPrinter::PrintAsmOperand(const MachineInstr
*MI
,
344 unsigned OpNo
, unsigned AsmVariant
,
345 const char *ExtraCode
,
348 report_fatal_error("There are no defined alternate asm variants");
350 // First try the generic code, which knows about modifiers like 'c' and 'n'.
351 if (!AsmPrinter::PrintAsmOperand(MI
, OpNo
, AsmVariant
, ExtraCode
, OS
))
355 const MachineOperand
&MO
= MI
->getOperand(OpNo
);
356 switch (MO
.getType()) {
357 case MachineOperand::MO_Immediate
:
360 case MachineOperand::MO_Register
:
361 // FIXME: only opcode that still contains registers, as required by
362 // MachineInstr::getDebugVariable().
363 assert(MI
->getOpcode() == WebAssembly::INLINEASM
);
364 OS
<< regToString(MO
);
366 case MachineOperand::MO_GlobalAddress
:
367 getSymbol(MO
.getGlobal())->print(OS
, MAI
);
368 printOffset(MO
.getOffset(), OS
);
370 case MachineOperand::MO_ExternalSymbol
:
371 GetExternalSymbolSymbol(MO
.getSymbolName())->print(OS
, MAI
);
372 printOffset(MO
.getOffset(), OS
);
374 case MachineOperand::MO_MachineBasicBlock
:
375 MO
.getMBB()->getSymbol()->print(OS
, MAI
);
385 bool WebAssemblyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr
*MI
,
388 const char *ExtraCode
,
391 report_fatal_error("There are no defined alternate asm variants");
393 // The current approach to inline asm is that "r" constraints are expressed
394 // as local indices, rather than values on the operand stack. This simplifies
395 // using "r" as it eliminates the need to push and pop the values in a
396 // particular order, however it also makes it impossible to have an "m"
397 // constraint. So we don't support it.
399 return AsmPrinter::PrintAsmMemoryOperand(MI
, OpNo
, AsmVariant
, ExtraCode
, OS
);
402 // Force static initialization.
403 extern "C" void LLVMInitializeWebAssemblyAsmPrinter() {
404 RegisterAsmPrinter
<WebAssemblyAsmPrinter
> X(getTheWebAssemblyTarget32());
405 RegisterAsmPrinter
<WebAssemblyAsmPrinter
> Y(getTheWebAssemblyTarget64());