1//===- WriterUtils.cpp ----------------------------------------------------===//
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#include "WriterUtils.h"
10#include "lld/Common/ErrorHandler.h"
11#include "llvm/ADT/StringExtras.h"
12#include "llvm/Support/Debug.h"
13#include "llvm/Support/EndianStream.h"
14#include "llvm/Support/LEB128.h"
15
16#define DEBUG_TYPE "lld"
17
18using namespace llvm;
19using namespace llvm::wasm;
20
21namespace lld {
22std::string toString(ValType type) {
23 switch (type) {
24 case ValType::I32:
25 return "i32";
26 case ValType::I64:
27 return "i64";
28 case ValType::F32:
29 return "f32";
30 case ValType::F64:
31 return "f64";
32 case ValType::V128:
33 return "v128";
34 case ValType::FUNCREF:
35 return "funcref";
36 case ValType::EXTERNREF:
37 return "externref";
38 case ValType::OTHERREF:
39 return "otherref";
40 }
41 llvm_unreachable("Invalid wasm::ValType");
42}
43
44std::string toString(const WasmSignature &sig) {
45 SmallString<128> s("(");
46 for (ValType type : sig.Params) {
47 if (s.size() != 1)
48 s += ", ";
49 s += toString(type);
50 }
51 s += ") -> ";
52 if (sig.Returns.empty())
53 s += "void";
54 else
55 s += toString(type: sig.Returns[0]);
56 return std::string(s);
57}
58
59std::string toString(const WasmGlobalType &type) {
60 return (type.Mutable ? "var " : "const ") +
61 toString(type: static_cast<ValType>(type.Type));
62}
63
64static std::string toString(const llvm::wasm::WasmLimits &limits) {
65 std::string ret;
66 ret += "flags=0x" + std::to_string(val: limits.Flags);
67 ret += "; min=" + std::to_string(val: limits.Minimum);
68 if (limits.Flags & WASM_LIMITS_FLAG_HAS_MAX)
69 ret += "; max=" + std::to_string(val: limits.Maximum);
70 return ret;
71}
72
73std::string toString(const WasmTableType &type) {
74 SmallString<128> ret("");
75 return "type=" + toString(type: static_cast<ValType>(type.ElemType)) +
76 "; limits=[" + toString(limits: type.Limits) + "]";
77}
78
79namespace wasm {
80#ifdef LLVM_DEBUG
81void debugWrite(uint64_t offset, const Twine &msg) {
82 LLVM_DEBUG(dbgs() << format(" | %08lld: ", offset) << msg << "\n");
83}
84#endif
85
86void writeUleb128(raw_ostream &os, uint64_t number, const Twine &msg) {
87 debugWrite(offset: os.tell(), msg: msg + "[" + utohexstr(X: number) + "]");
88 encodeULEB128(Value: number, OS&: os);
89}
90
91void writeSleb128(raw_ostream &os, int64_t number, const Twine &msg) {
92 debugWrite(offset: os.tell(), msg: msg + "[" + utohexstr(X: number) + "]");
93 encodeSLEB128(Value: number, OS&: os);
94}
95
96void writeBytes(raw_ostream &os, const char *bytes, size_t count,
97 const Twine &msg) {
98 debugWrite(offset: os.tell(), msg: msg + " [data[" + Twine(count) + "]]");
99 os.write(Ptr: bytes, Size: count);
100}
101
102void writeStr(raw_ostream &os, StringRef string, const Twine &msg) {
103 debugWrite(offset: os.tell(),
104 msg: msg + " [str[" + Twine(string.size()) + "]: " + string + "]");
105 encodeULEB128(Value: string.size(), OS&: os);
106 os.write(Ptr: string.data(), Size: string.size());
107}
108
109void writeU8(raw_ostream &os, uint8_t byte, const Twine &msg) {
110 debugWrite(offset: os.tell(), msg: msg + " [0x" + utohexstr(X: byte) + "]");
111 os << byte;
112}
113
114void writeU32(raw_ostream &os, uint32_t number, const Twine &msg) {
115 debugWrite(offset: os.tell(), msg: msg + "[0x" + utohexstr(X: number) + "]");
116 support::endian::write(os, value: number, endian: llvm::endianness::little);
117}
118
119void writeU64(raw_ostream &os, uint64_t number, const Twine &msg) {
120 debugWrite(offset: os.tell(), msg: msg + "[0x" + utohexstr(X: number) + "]");
121 support::endian::write(os, value: number, endian: llvm::endianness::little);
122}
123
124void writeValueType(raw_ostream &os, ValType type, const Twine &msg) {
125 writeU8(os, byte: static_cast<uint8_t>(type),
126 msg: msg + "[type: " + toString(type) + "]");
127}
128
129void writeSig(raw_ostream &os, const WasmSignature &sig) {
130 writeU8(os, byte: WASM_TYPE_FUNC, msg: "signature type");
131 writeUleb128(os, number: sig.Params.size(), msg: "param Count");
132 for (ValType paramType : sig.Params) {
133 writeValueType(os, type: paramType, msg: "param type");
134 }
135 writeUleb128(os, number: sig.Returns.size(), msg: "result Count");
136 for (ValType returnType : sig.Returns) {
137 writeValueType(os, type: returnType, msg: "result type");
138 }
139}
140
141void writeI32Const(raw_ostream &os, int32_t number, const Twine &msg) {
142 writeU8(os, byte: WASM_OPCODE_I32_CONST, msg: "i32.const");
143 writeSleb128(os, number, msg);
144}
145
146void writeI64Const(raw_ostream &os, int64_t number, const Twine &msg) {
147 writeU8(os, byte: WASM_OPCODE_I64_CONST, msg: "i64.const");
148 writeSleb128(os, number, msg);
149}
150
151void writePtrConst(raw_ostream &os, int64_t number, bool is64,
152 const Twine &msg) {
153 if (is64)
154 writeI64Const(os, number, msg);
155 else
156 writeI32Const(os, number: static_cast<int32_t>(number), msg);
157}
158
159void writeMemArg(raw_ostream &os, uint32_t alignment, uint64_t offset) {
160 writeUleb128(os, number: alignment, msg: "alignment");
161 writeUleb128(os, number: offset, msg: "offset");
162}
163
164void writeInitExpr(raw_ostream &os, const WasmInitExpr &initExpr) {
165 assert(!initExpr.Extended);
166 writeInitExprMVP(os, initExpr: initExpr.Inst);
167}
168
169void writeInitExprMVP(raw_ostream &os, const WasmInitExprMVP &initExpr) {
170 writeU8(os, byte: initExpr.Opcode, msg: "opcode");
171 switch (initExpr.Opcode) {
172 case WASM_OPCODE_I32_CONST:
173 writeSleb128(os, number: initExpr.Value.Int32, msg: "literal (i32)");
174 break;
175 case WASM_OPCODE_I64_CONST:
176 writeSleb128(os, number: initExpr.Value.Int64, msg: "literal (i64)");
177 break;
178 case WASM_OPCODE_F32_CONST:
179 writeU32(os, number: initExpr.Value.Float32, msg: "literal (f32)");
180 break;
181 case WASM_OPCODE_F64_CONST:
182 writeU64(os, number: initExpr.Value.Float64, msg: "literal (f64)");
183 break;
184 case WASM_OPCODE_GLOBAL_GET:
185 writeUleb128(os, number: initExpr.Value.Global, msg: "literal (global index)");
186 break;
187 case WASM_OPCODE_REF_NULL:
188 writeValueType(os, type: ValType::EXTERNREF, msg: "literal (externref type)");
189 break;
190 default:
191 fatal(msg: "unknown opcode in init expr: " + Twine(initExpr.Opcode));
192 }
193 writeU8(os, byte: WASM_OPCODE_END, msg: "opcode:end");
194}
195
196void writeLimits(raw_ostream &os, const WasmLimits &limits) {
197 writeU8(os, byte: limits.Flags, msg: "limits flags");
198 writeUleb128(os, number: limits.Minimum, msg: "limits min");
199 if (limits.Flags & WASM_LIMITS_FLAG_HAS_MAX)
200 writeUleb128(os, number: limits.Maximum, msg: "limits max");
201}
202
203void writeGlobalType(raw_ostream &os, const WasmGlobalType &type) {
204 // TODO: Update WasmGlobalType to use ValType and remove this cast.
205 writeValueType(os, type: ValType(type.Type), msg: "global type");
206 writeU8(os, byte: type.Mutable, msg: "global mutable");
207}
208
209void writeTableType(raw_ostream &os, const WasmTableType &type) {
210 writeValueType(os, type: ValType(type.ElemType), msg: "table type");
211 writeLimits(os, limits: type.Limits);
212}
213
214void writeImport(raw_ostream &os, const WasmImport &import) {
215 writeStr(os, string: import.Module, msg: "import module name");
216 writeStr(os, string: import.Field, msg: "import field name");
217 writeU8(os, byte: import.Kind, msg: "import kind");
218 switch (import.Kind) {
219 case WASM_EXTERNAL_FUNCTION:
220 writeUleb128(os, number: import.SigIndex, msg: "import sig index");
221 break;
222 case WASM_EXTERNAL_GLOBAL:
223 writeGlobalType(os, type: import.Global);
224 break;
225 case WASM_EXTERNAL_TAG:
226 writeUleb128(os, number: 0, msg: "tag attribute"); // Reserved "attribute" field
227 writeUleb128(os, number: import.SigIndex, msg: "import sig index");
228 break;
229 case WASM_EXTERNAL_MEMORY:
230 writeLimits(os, limits: import.Memory);
231 break;
232 case WASM_EXTERNAL_TABLE:
233 writeTableType(os, type: import.Table);
234 break;
235 default:
236 fatal(msg: "unsupported import type: " + Twine(import.Kind));
237 }
238}
239
240void writeExport(raw_ostream &os, const WasmExport &export_) {
241 writeStr(os, string: export_.Name, msg: "export name");
242 writeU8(os, byte: export_.Kind, msg: "export kind");
243 switch (export_.Kind) {
244 case WASM_EXTERNAL_FUNCTION:
245 writeUleb128(os, number: export_.Index, msg: "function index");
246 break;
247 case WASM_EXTERNAL_GLOBAL:
248 writeUleb128(os, number: export_.Index, msg: "global index");
249 break;
250 case WASM_EXTERNAL_TAG:
251 writeUleb128(os, number: export_.Index, msg: "tag index");
252 break;
253 case WASM_EXTERNAL_MEMORY:
254 writeUleb128(os, number: export_.Index, msg: "memory index");
255 break;
256 case WASM_EXTERNAL_TABLE:
257 writeUleb128(os, number: export_.Index, msg: "table index");
258 break;
259 default:
260 fatal(msg: "unsupported export type: " + Twine(export_.Kind));
261 }
262}
263
264} // namespace wasm
265} // namespace lld
266

source code of lld/wasm/WriterUtils.cpp