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 
12 #include "lld/Common/ErrorHandler.h"
13 
14 #include "llvm/Support/Debug.h"
15 #include "llvm/Support/EndianStream.h"
16 #include "llvm/Support/FormatVariadic.h"
17 #include "llvm/Support/LEB128.h"
18 
19 #define DEBUG_TYPE "lld"
20 
21 using namespace llvm;
22 using namespace llvm::wasm;
23 using namespace lld::wasm;
24 
25 static const char *valueTypeToString(int32_t Type) {
26   switch (Type) {
27   case WASM_TYPE_I32:
28     return "i32";
29   case WASM_TYPE_I64:
30     return "i64";
31   case WASM_TYPE_F32:
32     return "f32";
33   case WASM_TYPE_F64:
34     return "f64";
35   default:
36     llvm_unreachable("invalid value type");
37   }
38 }
39 
40 namespace lld {
41 
42 void wasm::debugWrite(uint64_t offset, Twine msg) {
43   DEBUG(dbgs() << format("  | %08" PRIx64 ": ", offset) << msg << "\n");
44 }
45 
46 void wasm::writeUleb128(raw_ostream &OS, uint32_t Number, const char *msg) {
47   if (msg)
48     debugWrite(OS.tell(), msg + formatv(" [{0:x}]", Number));
49   encodeULEB128(Number, OS);
50 }
51 
52 void wasm::writeSleb128(raw_ostream &OS, int32_t Number, const char *msg) {
53   if (msg)
54     debugWrite(OS.tell(), msg + formatv(" [{0:x}]", Number));
55   encodeSLEB128(Number, OS);
56 }
57 
58 void wasm::writeBytes(raw_ostream &OS, const char *bytes, size_t count,
59                       const char *msg) {
60   if (msg)
61     debugWrite(OS.tell(), msg + formatv(" [data[{0}]]", count));
62   OS.write(bytes, count);
63 }
64 
65 void wasm::writeStr(raw_ostream &OS, const StringRef String, const char *msg) {
66   if (msg)
67     debugWrite(OS.tell(),
68                msg + formatv(" [str[{0}]: {1}]", String.size(), String));
69   writeUleb128(OS, String.size(), nullptr);
70   writeBytes(OS, String.data(), String.size());
71 }
72 
73 void wasm::writeU8(raw_ostream &OS, uint8_t byte, const char *msg) {
74   OS << byte;
75 }
76 
77 void wasm::writeU32(raw_ostream &OS, uint32_t Number, const char *msg) {
78   debugWrite(OS.tell(), msg + formatv("[{0:x}]", Number));
79   support::endian::Writer<support::little>(OS).write(Number);
80 }
81 
82 void wasm::writeValueType(raw_ostream &OS, int32_t Type, const char *msg) {
83   debugWrite(OS.tell(), msg + formatv("[type: {0}]", valueTypeToString(Type)));
84   writeSleb128(OS, Type, nullptr);
85 }
86 
87 void wasm::writeSig(raw_ostream &OS, const WasmSignature &Sig) {
88   writeSleb128(OS, WASM_TYPE_FUNC, "signature type");
89   writeUleb128(OS, Sig.ParamTypes.size(), "param count");
90   for (int32_t ParamType : Sig.ParamTypes) {
91     writeValueType(OS, ParamType, "param type");
92   }
93   if (Sig.ReturnType == WASM_TYPE_NORESULT) {
94     writeUleb128(OS, 0, "result count");
95   } else {
96     writeUleb128(OS, 1, "result count");
97     writeValueType(OS, Sig.ReturnType, "result type");
98   }
99 }
100 
101 void wasm::writeInitExpr(raw_ostream &OS, const WasmInitExpr &InitExpr) {
102   writeU8(OS, InitExpr.Opcode, "opcode");
103   switch (InitExpr.Opcode) {
104   case WASM_OPCODE_I32_CONST:
105     writeSleb128(OS, InitExpr.Value.Int32, "literal (i32)");
106     break;
107   case WASM_OPCODE_I64_CONST:
108     writeSleb128(OS, InitExpr.Value.Int64, "literal (i64)");
109     break;
110   case WASM_OPCODE_GET_GLOBAL:
111     writeUleb128(OS, InitExpr.Value.Global, "literal (global index)");
112     break;
113   default:
114     fatal("unknown opcode in init expr: " + Twine(InitExpr.Opcode));
115   }
116   writeU8(OS, WASM_OPCODE_END, "opcode:end");
117 }
118 
119 void wasm::writeLimits(raw_ostream &OS, const WasmLimits &Limits) {
120   writeUleb128(OS, Limits.Flags, "limits flags");
121   writeUleb128(OS, Limits.Initial, "limits initial");
122   if (Limits.Flags & WASM_LIMITS_FLAG_HAS_MAX)
123     writeUleb128(OS, Limits.Maximum, "limits max");
124 }
125 
126 void wasm::writeGlobalType(raw_ostream &OS, const WasmGlobalType &Type) {
127   writeValueType(OS, Type.Type, "global type");
128   writeUleb128(OS, Type.Mutable, "global mutable");
129 }
130 
131 void wasm::writeGlobal(raw_ostream &OS, const WasmGlobal &Global) {
132   writeGlobalType(OS, Global.Type);
133   writeInitExpr(OS, Global.InitExpr);
134 }
135 
136 void wasm::writeImport(raw_ostream &OS, const WasmImport &Import) {
137   writeStr(OS, Import.Module, "import module name");
138   writeStr(OS, Import.Field, "import field name");
139   writeU8(OS, Import.Kind, "import kind");
140   switch (Import.Kind) {
141   case WASM_EXTERNAL_FUNCTION:
142     writeUleb128(OS, Import.SigIndex, "import sig index");
143     break;
144   case WASM_EXTERNAL_GLOBAL:
145     writeGlobalType(OS, Import.Global);
146     break;
147   case WASM_EXTERNAL_MEMORY:
148     writeLimits(OS, Import.Memory);
149     break;
150   default:
151     fatal("unsupported import type: " + Twine(Import.Kind));
152   }
153 }
154 
155 void wasm::writeExport(raw_ostream &OS, const WasmExport &Export) {
156   writeStr(OS, Export.Name, "export name");
157   writeU8(OS, Export.Kind, "export kind");
158   switch (Export.Kind) {
159   case WASM_EXTERNAL_FUNCTION:
160     writeUleb128(OS, Export.Index, "function index");
161     break;
162   case WASM_EXTERNAL_GLOBAL:
163     writeUleb128(OS, Export.Index, "global index");
164     break;
165   case WASM_EXTERNAL_MEMORY:
166     writeUleb128(OS, Export.Index, "memory index");
167     break;
168   default:
169     fatal("unsupported export type: " + Twine(Export.Kind));
170   }
171 }
172 
173 void wasm::writeReloc(raw_ostream &OS, const OutputRelocation &Reloc) {
174   writeUleb128(OS, Reloc.Reloc.Type, "reloc type");
175   writeUleb128(OS, Reloc.Reloc.Offset, "reloc offset");
176   writeUleb128(OS, Reloc.NewIndex, "reloc index");
177 
178   switch (Reloc.Reloc.Type) {
179   case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
180   case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
181   case R_WEBASSEMBLY_MEMORY_ADDR_I32:
182     writeUleb128(OS, Reloc.Reloc.Addend, "reloc addend");
183     break;
184   default:
185     break;
186   }
187 }
188 
189 } // namespace lld
190 
191 std::string lld::toString(ValType Type) {
192   switch (Type) {
193   case ValType::I32:
194     return "I32";
195   case ValType::I64:
196     return "I64";
197   case ValType::F32:
198     return "F32";
199   case ValType::F64:
200     return "F64";
201   }
202   llvm_unreachable("Invalid wasm::ValType");
203 }
204 
205 std::string lld::toString(const WasmSignature &Sig) {
206   SmallString<128> S("(");
207   for (uint32_t Type : Sig.ParamTypes) {
208     if (S.size() != 1)
209       S += ", ";
210     S += toString(static_cast<ValType>(Type));
211   }
212   S += ") -> ";
213   if (Sig.ReturnType == WASM_TYPE_NORESULT)
214     S += "void";
215   else
216     S += toString(static_cast<ValType>(Sig.ReturnType));
217   return S.str();
218 }
219