1 //===- WriterUtils.cpp ----------------------------------------------------===//
2 //
3 //                             The LLVM Linker
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "WriterUtils.h"
11 #include "lld/Common/ErrorHandler.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 using namespace lld::wasm;
21 
22 static const char *valueTypeToString(ValType Type) {
23   switch (Type) {
24   case wasm::ValType::I32:
25     return "i32";
26   case wasm::ValType::I64:
27     return "i64";
28   case wasm::ValType::F32:
29     return "f32";
30   case wasm::ValType::F64:
31     return "f64";
32   default:
33     llvm_unreachable("invalid value type");
34   }
35 }
36 
37 namespace lld {
38 
39 void wasm::debugWrite(uint64_t Offset, const Twine &Msg) {
40   LLVM_DEBUG(dbgs() << format("  | %08lld: ", Offset) << Msg << "\n");
41 }
42 
43 void wasm::writeUleb128(raw_ostream &OS, uint32_t Number, const Twine &Msg) {
44   debugWrite(OS.tell(), Msg + "[" + utohexstr(Number) + "]");
45   encodeULEB128(Number, OS);
46 }
47 
48 void wasm::writeSleb128(raw_ostream &OS, int32_t Number, const Twine &Msg) {
49   debugWrite(OS.tell(), Msg + "[" + utohexstr(Number) + "]");
50   encodeSLEB128(Number, OS);
51 }
52 
53 void wasm::writeBytes(raw_ostream &OS, const char *Bytes, size_t Count,
54                       const Twine &Msg) {
55   debugWrite(OS.tell(), Msg + " [data[" + Twine(Count) + "]]");
56   OS.write(Bytes, Count);
57 }
58 
59 void wasm::writeStr(raw_ostream &OS, StringRef String, const Twine &Msg) {
60   debugWrite(OS.tell(),
61              Msg + " [str[" + Twine(String.size()) + "]: " + String + "]");
62   encodeULEB128(String.size(), OS);
63   OS.write(String.data(), String.size());
64 }
65 
66 void wasm::writeU8(raw_ostream &OS, uint8_t Byte, const Twine &Msg) {
67   debugWrite(OS.tell(), Msg + " [0x" + utohexstr(Byte) + "]");
68   OS << Byte;
69 }
70 
71 void wasm::writeU32(raw_ostream &OS, uint32_t Number, const Twine &Msg) {
72   debugWrite(OS.tell(), Msg + "[0x" + utohexstr(Number) + "]");
73   support::endian::write(OS, Number, support::little);
74 }
75 
76 void wasm::writeValueType(raw_ostream &OS, ValType Type, const Twine &Msg) {
77   writeU8(OS, static_cast<uint8_t>(Type),
78           Msg + "[type: " + valueTypeToString(Type) + "]");
79 }
80 
81 void wasm::writeSig(raw_ostream &OS, const WasmSignature &Sig) {
82   writeU8(OS, WASM_TYPE_FUNC, "signature type");
83   writeUleb128(OS, Sig.Params.size(), "param Count");
84   for (ValType ParamType : Sig.Params) {
85     writeValueType(OS, ParamType, "param type");
86   }
87   writeUleb128(OS, Sig.Returns.size(), "result Count");
88   if (Sig.Returns.size()) {
89     writeValueType(OS, Sig.Returns[0], "result type");
90   }
91 }
92 
93 void wasm::writeInitExpr(raw_ostream &OS, const WasmInitExpr &InitExpr) {
94   writeU8(OS, InitExpr.Opcode, "opcode");
95   switch (InitExpr.Opcode) {
96   case WASM_OPCODE_I32_CONST:
97     writeSleb128(OS, InitExpr.Value.Int32, "literal (i32)");
98     break;
99   case WASM_OPCODE_I64_CONST:
100     writeSleb128(OS, InitExpr.Value.Int64, "literal (i64)");
101     break;
102   case WASM_OPCODE_GET_GLOBAL:
103     writeUleb128(OS, InitExpr.Value.Global, "literal (global index)");
104     break;
105   default:
106     fatal("unknown opcode in init expr: " + Twine(InitExpr.Opcode));
107   }
108   writeU8(OS, WASM_OPCODE_END, "opcode:end");
109 }
110 
111 void wasm::writeLimits(raw_ostream &OS, const WasmLimits &Limits) {
112   writeU8(OS, Limits.Flags, "limits flags");
113   writeUleb128(OS, Limits.Initial, "limits initial");
114   if (Limits.Flags & WASM_LIMITS_FLAG_HAS_MAX)
115     writeUleb128(OS, Limits.Maximum, "limits max");
116 }
117 
118 void wasm::writeGlobalType(raw_ostream &OS, const WasmGlobalType &Type) {
119   // TODO: Update WasmGlobalType to use ValType and remove this cast.
120   writeValueType(OS, ValType(Type.Type), "global type");
121   writeU8(OS, Type.Mutable, "global mutable");
122 }
123 
124 void wasm::writeGlobal(raw_ostream &OS, const WasmGlobal &Global) {
125   writeGlobalType(OS, Global.Type);
126   writeInitExpr(OS, Global.InitExpr);
127 }
128 
129 void wasm::writeTableType(raw_ostream &OS, const llvm::wasm::WasmTable &Type) {
130   writeU8(OS, WASM_TYPE_ANYFUNC, "table type");
131   writeLimits(OS, Type.Limits);
132 }
133 
134 void wasm::writeImport(raw_ostream &OS, const WasmImport &Import) {
135   writeStr(OS, Import.Module, "import module name");
136   writeStr(OS, Import.Field, "import field name");
137   writeU8(OS, Import.Kind, "import kind");
138   switch (Import.Kind) {
139   case WASM_EXTERNAL_FUNCTION:
140     writeUleb128(OS, Import.SigIndex, "import sig index");
141     break;
142   case WASM_EXTERNAL_GLOBAL:
143     writeGlobalType(OS, Import.Global);
144     break;
145   case WASM_EXTERNAL_MEMORY:
146     writeLimits(OS, Import.Memory);
147     break;
148   case WASM_EXTERNAL_TABLE:
149     writeTableType(OS, Import.Table);
150     break;
151   default:
152     fatal("unsupported import type: " + Twine(Import.Kind));
153   }
154 }
155 
156 void wasm::writeExport(raw_ostream &OS, const WasmExport &Export) {
157   writeStr(OS, Export.Name, "export name");
158   writeU8(OS, Export.Kind, "export kind");
159   switch (Export.Kind) {
160   case WASM_EXTERNAL_FUNCTION:
161     writeUleb128(OS, Export.Index, "function index");
162     break;
163   case WASM_EXTERNAL_GLOBAL:
164     writeUleb128(OS, Export.Index, "global index");
165     break;
166   case WASM_EXTERNAL_MEMORY:
167     writeUleb128(OS, Export.Index, "memory index");
168     break;
169   case WASM_EXTERNAL_TABLE:
170     writeUleb128(OS, Export.Index, "table index");
171     break;
172   default:
173     fatal("unsupported export type: " + Twine(Export.Kind));
174   }
175 }
176 } // namespace lld
177 
178 std::string lld::toString(ValType Type) {
179   switch (Type) {
180   case ValType::I32:
181     return "I32";
182   case ValType::I64:
183     return "I64";
184   case ValType::F32:
185     return "F32";
186   case ValType::F64:
187     return "F64";
188   case ValType::V128:
189     return "V128";
190   case ValType::EXCEPT_REF:
191     return "except_ref";
192   }
193   llvm_unreachable("Invalid wasm::ValType");
194 }
195 
196 std::string lld::toString(const WasmSignature &Sig) {
197   SmallString<128> S("(");
198   for (ValType Type : Sig.Params) {
199     if (S.size() != 1)
200       S += ", ";
201     S += toString(Type);
202   }
203   S += ") -> ";
204   if (Sig.Returns.size() == 0)
205     S += "void";
206   else
207     S += toString(Sig.Returns[0]);
208   return S.str();
209 }
210 
211 std::string lld::toString(const WasmGlobalType &Sig) {
212   return (Sig.Mutable ? "var " : "const ") +
213          toString(static_cast<ValType>(Sig.Type));
214 }
215