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