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(uint8_t Type) {
23   switch (Type) {
24   case WASM_TYPE_I32:
25     return "i32";
26   case WASM_TYPE_I64:
27     return "i64";
28   case WASM_TYPE_F32:
29     return "f32";
30   case WASM_TYPE_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   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::Writer<support::little>(OS).write(Number);
74 }
75 
76 void wasm::writeValueType(raw_ostream &OS, uint8_t Type, const Twine &Msg) {
77   writeU8(OS, Type, Msg + "[type: " + valueTypeToString(Type) + "]");
78 }
79 
80 void wasm::writeSig(raw_ostream &OS, const WasmSignature &Sig) {
81   writeU8(OS, WASM_TYPE_FUNC, "signature type");
82   writeUleb128(OS, Sig.ParamTypes.size(), "param Count");
83   for (uint8_t ParamType : Sig.ParamTypes) {
84     writeValueType(OS, ParamType, "param type");
85   }
86   if (Sig.ReturnType == WASM_TYPE_NORESULT) {
87     writeUleb128(OS, 0, "result Count");
88   } else {
89     writeUleb128(OS, 1, "result Count");
90     writeValueType(OS, Sig.ReturnType, "result type");
91   }
92 }
93 
94 void wasm::writeInitExpr(raw_ostream &OS, const WasmInitExpr &InitExpr) {
95   writeU8(OS, InitExpr.Opcode, "opcode");
96   switch (InitExpr.Opcode) {
97   case WASM_OPCODE_I32_CONST:
98     writeSleb128(OS, InitExpr.Value.Int32, "literal (i32)");
99     break;
100   case WASM_OPCODE_I64_CONST:
101     writeSleb128(OS, InitExpr.Value.Int64, "literal (i64)");
102     break;
103   case WASM_OPCODE_GET_GLOBAL:
104     writeUleb128(OS, InitExpr.Value.Global, "literal (global index)");
105     break;
106   default:
107     fatal("unknown opcode in init expr: " + Twine(InitExpr.Opcode));
108   }
109   writeU8(OS, WASM_OPCODE_END, "opcode:end");
110 }
111 
112 void wasm::writeLimits(raw_ostream &OS, const WasmLimits &Limits) {
113   writeU8(OS, Limits.Flags, "limits flags");
114   writeUleb128(OS, Limits.Initial, "limits initial");
115   if (Limits.Flags & WASM_LIMITS_FLAG_HAS_MAX)
116     writeUleb128(OS, Limits.Maximum, "limits max");
117 }
118 
119 void wasm::writeGlobalType(raw_ostream &OS, const WasmGlobalType &Type) {
120   writeValueType(OS, 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::EXCEPT_REF:
189     return "except_ref";
190   }
191   llvm_unreachable("Invalid wasm::ValType");
192 }
193 
194 std::string lld::toString(const WasmSignature &Sig) {
195   SmallString<128> S("(");
196   for (uint32_t Type : Sig.ParamTypes) {
197     if (S.size() != 1)
198       S += ", ";
199     S += toString(static_cast<ValType>(Type));
200   }
201   S += ") -> ";
202   if (Sig.ReturnType == WASM_TYPE_NORESULT)
203     S += "void";
204   else
205     S += toString(static_cast<ValType>(Sig.ReturnType));
206   return S.str();
207 }
208 
209 std::string lld::toString(const WasmGlobalType &Sig) {
210   return (Sig.Mutable ? "var " : "const ") +
211          toString(static_cast<ValType>(Sig.Type));
212 }
213