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