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::writeInitExpr(raw_ostream &OS, const WasmInitExpr &InitExpr) {
77   writeU8(OS, InitExpr.Opcode, "opcode");
78   switch (InitExpr.Opcode) {
79   case WASM_OPCODE_I32_CONST:
80     writeSleb128(OS, InitExpr.Value.Int32, "literal (i32)");
81     break;
82   case WASM_OPCODE_I64_CONST:
83     writeSleb128(OS, InitExpr.Value.Int64, "literal (i64)");
84     break;
85   case WASM_OPCODE_GLOBAL_GET:
86     writeUleb128(OS, InitExpr.Value.Global, "literal (global index)");
87     break;
88   default:
89     fatal("unknown opcode in init expr: " + Twine(InitExpr.Opcode));
90   }
91   writeU8(OS, WASM_OPCODE_END, "opcode:end");
92 }
93 
94 void wasm::writeLimits(raw_ostream &OS, const WasmLimits &Limits) {
95   writeU8(OS, Limits.Flags, "limits flags");
96   writeUleb128(OS, Limits.Initial, "limits initial");
97   if (Limits.Flags & WASM_LIMITS_FLAG_HAS_MAX)
98     writeUleb128(OS, Limits.Maximum, "limits max");
99 }
100 
101 void wasm::writeGlobalType(raw_ostream &OS, const WasmGlobalType &Type) {
102   // TODO: Update WasmGlobalType to use ValType and remove this cast.
103   writeValueType(OS, ValType(Type.Type), "global type");
104   writeU8(OS, Type.Mutable, "global mutable");
105 }
106 
107 void wasm::writeGlobal(raw_ostream &OS, const WasmGlobal &Global) {
108   writeGlobalType(OS, Global.Type);
109   writeInitExpr(OS, Global.InitExpr);
110 }
111 
112 void wasm::writeEventType(raw_ostream &OS, const WasmEventType &Type) {
113   writeUleb128(OS, Type.Attribute, "event attribute");
114   writeUleb128(OS, Type.SigIndex, "sig index");
115 }
116 
117 void wasm::writeEvent(raw_ostream &OS, const WasmEvent &Event) {
118   writeEventType(OS, Event.Type);
119 }
120 
121 void wasm::writeTableType(raw_ostream &OS, const llvm::wasm::WasmTable &Type) {
122   writeU8(OS, WASM_TYPE_FUNCREF, "table type");
123   writeLimits(OS, Type.Limits);
124 }
125 
126 void wasm::writeImport(raw_ostream &OS, const WasmImport &Import) {
127   writeStr(OS, Import.Module, "import module name");
128   writeStr(OS, Import.Field, "import field name");
129   writeU8(OS, Import.Kind, "import kind");
130   switch (Import.Kind) {
131   case WASM_EXTERNAL_FUNCTION:
132     writeUleb128(OS, Import.SigIndex, "import sig index");
133     break;
134   case WASM_EXTERNAL_GLOBAL:
135     writeGlobalType(OS, Import.Global);
136     break;
137   case WASM_EXTERNAL_EVENT:
138     writeEventType(OS, Import.Event);
139     break;
140   case WASM_EXTERNAL_MEMORY:
141     writeLimits(OS, Import.Memory);
142     break;
143   case WASM_EXTERNAL_TABLE:
144     writeTableType(OS, Import.Table);
145     break;
146   default:
147     fatal("unsupported import type: " + Twine(Import.Kind));
148   }
149 }
150 
151 void wasm::writeExport(raw_ostream &OS, const WasmExport &Export) {
152   writeStr(OS, Export.Name, "export name");
153   writeU8(OS, Export.Kind, "export kind");
154   switch (Export.Kind) {
155   case WASM_EXTERNAL_FUNCTION:
156     writeUleb128(OS, Export.Index, "function index");
157     break;
158   case WASM_EXTERNAL_GLOBAL:
159     writeUleb128(OS, Export.Index, "global index");
160     break;
161   case WASM_EXTERNAL_MEMORY:
162     writeUleb128(OS, Export.Index, "memory index");
163     break;
164   case WASM_EXTERNAL_TABLE:
165     writeUleb128(OS, Export.Index, "table index");
166     break;
167   default:
168     fatal("unsupported export type: " + Twine(Export.Kind));
169   }
170 }
171 } // namespace lld
172 
173 std::string lld::toString(ValType Type) {
174   switch (Type) {
175   case ValType::I32:
176     return "i32";
177   case ValType::I64:
178     return "i64";
179   case ValType::F32:
180     return "f32";
181   case ValType::F64:
182     return "f64";
183   case ValType::V128:
184     return "v128";
185   case ValType::EXCEPT_REF:
186     return "except_ref";
187   }
188   llvm_unreachable("Invalid wasm::ValType");
189 }
190 
191 std::string lld::toString(const WasmSignature &Sig) {
192   SmallString<128> S("(");
193   for (ValType Type : Sig.Params) {
194     if (S.size() != 1)
195       S += ", ";
196     S += toString(Type);
197   }
198   S += ") -> ";
199   if (Sig.Returns.empty())
200     S += "void";
201   else
202     S += toString(Sig.Returns[0]);
203   return S.str();
204 }
205 
206 std::string lld::toString(const WasmGlobalType &Type) {
207   return (Type.Mutable ? "var " : "const ") +
208          toString(static_cast<ValType>(Type.Type));
209 }
210 
211 std::string lld::toString(const WasmEventType &Type) {
212   if (Type.Attribute == WASM_EVENT_ATTRIBUTE_EXCEPTION)
213     return "exception";
214   return "unknown";
215 }
216