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::OTHERREF: |
39 | return "otherref" ; |
40 | } |
41 | llvm_unreachable("Invalid wasm::ValType" ); |
42 | } |
43 | |
44 | std::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 | |
59 | std::string toString(const WasmGlobalType &type) { |
60 | return (type.Mutable ? "var " : "const " ) + |
61 | toString(type: static_cast<ValType>(type.Type)); |
62 | } |
63 | |
64 | static 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 | |
73 | std::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 | |
79 | namespace wasm { |
80 | #ifdef LLVM_DEBUG |
81 | void debugWrite(uint64_t offset, const Twine &msg) { |
82 | LLVM_DEBUG(dbgs() << format(" | %08lld: " , offset) << msg << "\n" ); |
83 | } |
84 | #endif |
85 | |
86 | void 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 | |
91 | void 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 | |
96 | void 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 | |
102 | void 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 | |
109 | void 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 | |
114 | void 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 | |
119 | void 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 | |
124 | void 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 | |
129 | void 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 | |
141 | void 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 | |
146 | void 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 | |
151 | void 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 | |
159 | void 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 | |
164 | void writeInitExpr(raw_ostream &os, const WasmInitExpr &initExpr) { |
165 | assert(!initExpr.Extended); |
166 | writeInitExprMVP(os, initExpr: initExpr.Inst); |
167 | } |
168 | |
169 | void 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 | |
196 | void 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 | |
203 | void 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 | |
209 | void 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 | |
214 | void 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 | |
240 | void 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 | |