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 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::EXNREF: 34 return "exnref"; 35 } 36 llvm_unreachable("Invalid wasm::ValType"); 37 } 38 39 std::string toString(const WasmSignature &sig) { 40 SmallString<128> s("("); 41 for (ValType type : sig.Params) { 42 if (s.size() != 1) 43 s += ", "; 44 s += toString(type); 45 } 46 s += ") -> "; 47 if (sig.Returns.empty()) 48 s += "void"; 49 else 50 s += toString(sig.Returns[0]); 51 return std::string(s.str()); 52 } 53 54 std::string toString(const WasmGlobalType &type) { 55 return (type.Mutable ? "var " : "const ") + 56 toString(static_cast<ValType>(type.Type)); 57 } 58 59 std::string toString(const WasmEventType &type) { 60 if (type.Attribute == WASM_EVENT_ATTRIBUTE_EXCEPTION) 61 return "exception"; 62 return "unknown"; 63 } 64 65 namespace wasm { 66 void debugWrite(uint64_t offset, const Twine &msg) { 67 LLVM_DEBUG(dbgs() << format(" | %08lld: ", offset) << msg << "\n"); 68 } 69 70 void writeUleb128(raw_ostream &os, uint32_t number, const Twine &msg) { 71 debugWrite(os.tell(), msg + "[" + utohexstr(number) + "]"); 72 encodeULEB128(number, os); 73 } 74 75 void writeSleb128(raw_ostream &os, int32_t number, const Twine &msg) { 76 debugWrite(os.tell(), msg + "[" + utohexstr(number) + "]"); 77 encodeSLEB128(number, os); 78 } 79 80 void writeBytes(raw_ostream &os, const char *bytes, size_t count, 81 const Twine &msg) { 82 debugWrite(os.tell(), msg + " [data[" + Twine(count) + "]]"); 83 os.write(bytes, count); 84 } 85 86 void writeStr(raw_ostream &os, StringRef string, const Twine &msg) { 87 debugWrite(os.tell(), 88 msg + " [str[" + Twine(string.size()) + "]: " + string + "]"); 89 encodeULEB128(string.size(), os); 90 os.write(string.data(), string.size()); 91 } 92 93 void writeU8(raw_ostream &os, uint8_t byte, const Twine &msg) { 94 debugWrite(os.tell(), msg + " [0x" + utohexstr(byte) + "]"); 95 os << byte; 96 } 97 98 void writeU32(raw_ostream &os, uint32_t number, const Twine &msg) { 99 debugWrite(os.tell(), msg + "[0x" + utohexstr(number) + "]"); 100 support::endian::write(os, number, support::little); 101 } 102 103 void writeU64(raw_ostream &os, uint64_t number, const Twine &msg) { 104 debugWrite(os.tell(), msg + "[0x" + utohexstr(number) + "]"); 105 support::endian::write(os, number, support::little); 106 } 107 108 void writeValueType(raw_ostream &os, ValType type, const Twine &msg) { 109 writeU8(os, static_cast<uint8_t>(type), 110 msg + "[type: " + toString(type) + "]"); 111 } 112 113 void writeSig(raw_ostream &os, const WasmSignature &sig) { 114 writeU8(os, WASM_TYPE_FUNC, "signature type"); 115 writeUleb128(os, sig.Params.size(), "param Count"); 116 for (ValType paramType : sig.Params) { 117 writeValueType(os, paramType, "param type"); 118 } 119 writeUleb128(os, sig.Returns.size(), "result Count"); 120 if (sig.Returns.size()) { 121 writeValueType(os, sig.Returns[0], "result type"); 122 } 123 } 124 125 void writeI32Const(raw_ostream &os, int32_t number, const Twine &msg) { 126 writeU8(os, WASM_OPCODE_I32_CONST, "i32.const"); 127 writeSleb128(os, number, msg); 128 } 129 130 void writeI64Const(raw_ostream &os, int32_t number, const Twine &msg) { 131 writeU8(os, WASM_OPCODE_I64_CONST, "i64.const"); 132 writeSleb128(os, number, msg); 133 } 134 135 void writeMemArg(raw_ostream &os, uint32_t alignment, uint32_t offset) { 136 writeUleb128(os, alignment, "alignment"); 137 writeUleb128(os, offset, "offset"); 138 } 139 140 void writeInitExpr(raw_ostream &os, const WasmInitExpr &initExpr) { 141 writeU8(os, initExpr.Opcode, "opcode"); 142 switch (initExpr.Opcode) { 143 case WASM_OPCODE_I32_CONST: 144 writeSleb128(os, initExpr.Value.Int32, "literal (i32)"); 145 break; 146 case WASM_OPCODE_I64_CONST: 147 writeSleb128(os, initExpr.Value.Int64, "literal (i64)"); 148 break; 149 case WASM_OPCODE_F32_CONST: 150 writeU32(os, initExpr.Value.Float32, "literal (f32)"); 151 break; 152 case WASM_OPCODE_F64_CONST: 153 writeU64(os, initExpr.Value.Float64, "literal (f64)"); 154 break; 155 case WASM_OPCODE_GLOBAL_GET: 156 writeUleb128(os, initExpr.Value.Global, "literal (global index)"); 157 break; 158 default: 159 fatal("unknown opcode in init expr: " + Twine(initExpr.Opcode)); 160 } 161 writeU8(os, WASM_OPCODE_END, "opcode:end"); 162 } 163 164 void writeLimits(raw_ostream &os, const WasmLimits &limits) { 165 writeU8(os, limits.Flags, "limits flags"); 166 writeUleb128(os, limits.Initial, "limits initial"); 167 if (limits.Flags & WASM_LIMITS_FLAG_HAS_MAX) 168 writeUleb128(os, limits.Maximum, "limits max"); 169 } 170 171 void writeGlobalType(raw_ostream &os, const WasmGlobalType &type) { 172 // TODO: Update WasmGlobalType to use ValType and remove this cast. 173 writeValueType(os, ValType(type.Type), "global type"); 174 writeU8(os, type.Mutable, "global mutable"); 175 } 176 177 void writeGlobal(raw_ostream &os, const WasmGlobal &global) { 178 writeGlobalType(os, global.Type); 179 writeInitExpr(os, global.InitExpr); 180 } 181 182 void writeEventType(raw_ostream &os, const WasmEventType &type) { 183 writeUleb128(os, type.Attribute, "event attribute"); 184 writeUleb128(os, type.SigIndex, "sig index"); 185 } 186 187 void writeEvent(raw_ostream &os, const WasmEvent &event) { 188 writeEventType(os, event.Type); 189 } 190 191 void writeTableType(raw_ostream &os, const llvm::wasm::WasmTable &type) { 192 writeU8(os, WASM_TYPE_FUNCREF, "table type"); 193 writeLimits(os, type.Limits); 194 } 195 196 void writeImport(raw_ostream &os, const WasmImport &import) { 197 writeStr(os, import.Module, "import module name"); 198 writeStr(os, import.Field, "import field name"); 199 writeU8(os, import.Kind, "import kind"); 200 switch (import.Kind) { 201 case WASM_EXTERNAL_FUNCTION: 202 writeUleb128(os, import.SigIndex, "import sig index"); 203 break; 204 case WASM_EXTERNAL_GLOBAL: 205 writeGlobalType(os, import.Global); 206 break; 207 case WASM_EXTERNAL_EVENT: 208 writeEventType(os, import.Event); 209 break; 210 case WASM_EXTERNAL_MEMORY: 211 writeLimits(os, import.Memory); 212 break; 213 case WASM_EXTERNAL_TABLE: 214 writeTableType(os, import.Table); 215 break; 216 default: 217 fatal("unsupported import type: " + Twine(import.Kind)); 218 } 219 } 220 221 void writeExport(raw_ostream &os, const WasmExport &export_) { 222 writeStr(os, export_.Name, "export name"); 223 writeU8(os, export_.Kind, "export kind"); 224 switch (export_.Kind) { 225 case WASM_EXTERNAL_FUNCTION: 226 writeUleb128(os, export_.Index, "function index"); 227 break; 228 case WASM_EXTERNAL_GLOBAL: 229 writeUleb128(os, export_.Index, "global index"); 230 break; 231 case WASM_EXTERNAL_EVENT: 232 writeUleb128(os, export_.Index, "event index"); 233 break; 234 case WASM_EXTERNAL_MEMORY: 235 writeUleb128(os, export_.Index, "memory index"); 236 break; 237 case WASM_EXTERNAL_TABLE: 238 writeUleb128(os, export_.Index, "table index"); 239 break; 240 default: 241 fatal("unsupported export type: " + Twine(export_.Kind)); 242 } 243 } 244 245 } // namespace wasm 246 } // namespace lld 247