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