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