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