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