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