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