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