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::EXNREF:
39 return "exnref";
40 case ValType::OTHERREF:
41 return "otherref";
42 }
43 llvm_unreachable("Invalid wasm::ValType");
44}
45
46std::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
61std::string toString(const WasmGlobalType &type) {
62 return (type.Mutable ? "var " : "const ") +
63 toString(type: static_cast<ValType>(type.Type));
64}
65
66static 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
75std::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
81namespace wasm {
82#ifdef LLVM_DEBUG
83void debugWrite(uint64_t offset, const Twine &msg) {
84 LLVM_DEBUG(dbgs() << format(" | %08lld: ", offset) << msg << "\n");
85}
86#endif
87
88void 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
93void 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
98void 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
104void 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
111void 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
116void 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
121void 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
126void 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
131void 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
143void 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
148void 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
153void 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
161void 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
166void writeInitExpr(raw_ostream &os, const WasmInitExpr &initExpr) {
167 assert(!initExpr.Extended);
168 writeInitExprMVP(os, initExpr: initExpr.Inst);
169}
170
171void 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
198void 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
205void 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
211void 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
216void 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
242void 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