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