| //===- WriterUtils.cpp ----------------------------------------------------===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "WriterUtils.h" | 
 | #include "lld/Common/ErrorHandler.h" | 
 | #include "llvm/ADT/StringExtras.h" | 
 | #include "llvm/Support/Debug.h" | 
 | #include "llvm/Support/EndianStream.h" | 
 | #include "llvm/Support/LEB128.h" | 
 |  | 
 | #define DEBUG_TYPE "lld" | 
 |  | 
 | using namespace llvm; | 
 | using namespace llvm::wasm; | 
 |  | 
 | namespace lld { | 
 | std::string toString(ValType type) { | 
 |   switch (type) { | 
 |   case ValType::I32: | 
 |     return "i32"; | 
 |   case ValType::I64: | 
 |     return "i64"; | 
 |   case ValType::F32: | 
 |     return "f32"; | 
 |   case ValType::F64: | 
 |     return "f64"; | 
 |   case ValType::V128: | 
 |     return "v128"; | 
 |   case ValType::FUNCREF: | 
 |     return "funcref"; | 
 |   case ValType::EXTERNREF: | 
 |     return "externref"; | 
 |   } | 
 |   llvm_unreachable("Invalid wasm::ValType"); | 
 | } | 
 |  | 
 | std::string toString(const WasmSignature &sig) { | 
 |   SmallString<128> s("("); | 
 |   for (ValType type : sig.Params) { | 
 |     if (s.size() != 1) | 
 |       s += ", "; | 
 |     s += toString(type); | 
 |   } | 
 |   s += ") -> "; | 
 |   if (sig.Returns.empty()) | 
 |     s += "void"; | 
 |   else | 
 |     s += toString(sig.Returns[0]); | 
 |   return std::string(s); | 
 | } | 
 |  | 
 | std::string toString(const WasmGlobalType &type) { | 
 |   return (type.Mutable ? "var " : "const ") + | 
 |          toString(static_cast<ValType>(type.Type)); | 
 | } | 
 |  | 
 | static std::string toString(const llvm::wasm::WasmLimits &limits) { | 
 |   std::string ret; | 
 |   ret += "flags=0x" + std::to_string(limits.Flags); | 
 |   ret += "; min=" + std::to_string(limits.Minimum); | 
 |   if (limits.Flags & WASM_LIMITS_FLAG_HAS_MAX) | 
 |     ret += "; max=" + std::to_string(limits.Maximum); | 
 |   return ret; | 
 | } | 
 |  | 
 | std::string toString(const WasmTableType &type) { | 
 |   SmallString<128> ret(""); | 
 |   return "type=" + toString(static_cast<ValType>(type.ElemType)) + | 
 |          "; limits=[" + toString(type.Limits) + "]"; | 
 | } | 
 |  | 
 | namespace wasm { | 
 | #ifdef LLVM_DEBUG | 
 | void debugWrite(uint64_t offset, const Twine &msg) { | 
 |   LLVM_DEBUG(dbgs() << format("  | %08lld: ", offset) << msg << "\n"); | 
 | } | 
 | #endif | 
 |  | 
 | void writeUleb128(raw_ostream &os, uint64_t number, const Twine &msg) { | 
 |   debugWrite(os.tell(), msg + "[" + utohexstr(number) + "]"); | 
 |   encodeULEB128(number, os); | 
 | } | 
 |  | 
 | void writeSleb128(raw_ostream &os, int64_t number, const Twine &msg) { | 
 |   debugWrite(os.tell(), msg + "[" + utohexstr(number) + "]"); | 
 |   encodeSLEB128(number, os); | 
 | } | 
 |  | 
 | void writeBytes(raw_ostream &os, const char *bytes, size_t count, | 
 |                       const Twine &msg) { | 
 |   debugWrite(os.tell(), msg + " [data[" + Twine(count) + "]]"); | 
 |   os.write(bytes, count); | 
 | } | 
 |  | 
 | void writeStr(raw_ostream &os, StringRef string, const Twine &msg) { | 
 |   debugWrite(os.tell(), | 
 |              msg + " [str[" + Twine(string.size()) + "]: " + string + "]"); | 
 |   encodeULEB128(string.size(), os); | 
 |   os.write(string.data(), string.size()); | 
 | } | 
 |  | 
 | void writeU8(raw_ostream &os, uint8_t byte, const Twine &msg) { | 
 |   debugWrite(os.tell(), msg + " [0x" + utohexstr(byte) + "]"); | 
 |   os << byte; | 
 | } | 
 |  | 
 | void writeU32(raw_ostream &os, uint32_t number, const Twine &msg) { | 
 |   debugWrite(os.tell(), msg + "[0x" + utohexstr(number) + "]"); | 
 |   support::endian::write(os, number, llvm::endianness::little); | 
 | } | 
 |  | 
 | void writeU64(raw_ostream &os, uint64_t number, const Twine &msg) { | 
 |   debugWrite(os.tell(), msg + "[0x" + utohexstr(number) + "]"); | 
 |   support::endian::write(os, number, llvm::endianness::little); | 
 | } | 
 |  | 
 | void writeValueType(raw_ostream &os, ValType type, const Twine &msg) { | 
 |   writeU8(os, static_cast<uint8_t>(type), | 
 |           msg + "[type: " + toString(type) + "]"); | 
 | } | 
 |  | 
 | void writeSig(raw_ostream &os, const WasmSignature &sig) { | 
 |   writeU8(os, WASM_TYPE_FUNC, "signature type"); | 
 |   writeUleb128(os, sig.Params.size(), "param Count"); | 
 |   for (ValType paramType : sig.Params) { | 
 |     writeValueType(os, paramType, "param type"); | 
 |   } | 
 |   writeUleb128(os, sig.Returns.size(), "result Count"); | 
 |   for (ValType returnType : sig.Returns) { | 
 |     writeValueType(os, returnType, "result type"); | 
 |   } | 
 | } | 
 |  | 
 | void writeI32Const(raw_ostream &os, int32_t number, const Twine &msg) { | 
 |   writeU8(os, WASM_OPCODE_I32_CONST, "i32.const"); | 
 |   writeSleb128(os, number, msg); | 
 | } | 
 |  | 
 | void writeI64Const(raw_ostream &os, int64_t number, const Twine &msg) { | 
 |   writeU8(os, WASM_OPCODE_I64_CONST, "i64.const"); | 
 |   writeSleb128(os, number, msg); | 
 | } | 
 |  | 
 | void writePtrConst(raw_ostream &os, int64_t number, bool is64, | 
 |                    const Twine &msg) { | 
 |   if (is64) | 
 |     writeI64Const(os, number, msg); | 
 |   else | 
 |     writeI32Const(os, static_cast<int32_t>(number), msg); | 
 | } | 
 |  | 
 | void writeMemArg(raw_ostream &os, uint32_t alignment, uint64_t offset) { | 
 |   writeUleb128(os, alignment, "alignment"); | 
 |   writeUleb128(os, offset, "offset"); | 
 | } | 
 |  | 
 | void writeInitExpr(raw_ostream &os, const WasmInitExpr &initExpr) { | 
 |   assert(!initExpr.Extended); | 
 |   writeInitExprMVP(os, initExpr.Inst); | 
 | } | 
 |  | 
 | void writeInitExprMVP(raw_ostream &os, const WasmInitExprMVP &initExpr) { | 
 |   writeU8(os, initExpr.Opcode, "opcode"); | 
 |   switch (initExpr.Opcode) { | 
 |   case WASM_OPCODE_I32_CONST: | 
 |     writeSleb128(os, initExpr.Value.Int32, "literal (i32)"); | 
 |     break; | 
 |   case WASM_OPCODE_I64_CONST: | 
 |     writeSleb128(os, initExpr.Value.Int64, "literal (i64)"); | 
 |     break; | 
 |   case WASM_OPCODE_F32_CONST: | 
 |     writeU32(os, initExpr.Value.Float32, "literal (f32)"); | 
 |     break; | 
 |   case WASM_OPCODE_F64_CONST: | 
 |     writeU64(os, initExpr.Value.Float64, "literal (f64)"); | 
 |     break; | 
 |   case WASM_OPCODE_GLOBAL_GET: | 
 |     writeUleb128(os, initExpr.Value.Global, "literal (global index)"); | 
 |     break; | 
 |   case WASM_OPCODE_REF_NULL: | 
 |     writeValueType(os, ValType::EXTERNREF, "literal (externref type)"); | 
 |     break; | 
 |   default: | 
 |     fatal("unknown opcode in init expr: " + Twine(initExpr.Opcode)); | 
 |   } | 
 |   writeU8(os, WASM_OPCODE_END, "opcode:end"); | 
 | } | 
 |  | 
 | void writeLimits(raw_ostream &os, const WasmLimits &limits) { | 
 |   writeU8(os, limits.Flags, "limits flags"); | 
 |   writeUleb128(os, limits.Minimum, "limits min"); | 
 |   if (limits.Flags & WASM_LIMITS_FLAG_HAS_MAX) | 
 |     writeUleb128(os, limits.Maximum, "limits max"); | 
 | } | 
 |  | 
 | void writeGlobalType(raw_ostream &os, const WasmGlobalType &type) { | 
 |   // TODO: Update WasmGlobalType to use ValType and remove this cast. | 
 |   writeValueType(os, ValType(type.Type), "global type"); | 
 |   writeU8(os, type.Mutable, "global mutable"); | 
 | } | 
 |  | 
 | void writeTableType(raw_ostream &os, const WasmTableType &type) { | 
 |   writeValueType(os, ValType(type.ElemType), "table type"); | 
 |   writeLimits(os, type.Limits); | 
 | } | 
 |  | 
 | void writeImport(raw_ostream &os, const WasmImport &import) { | 
 |   writeStr(os, import.Module, "import module name"); | 
 |   writeStr(os, import.Field, "import field name"); | 
 |   writeU8(os, import.Kind, "import kind"); | 
 |   switch (import.Kind) { | 
 |   case WASM_EXTERNAL_FUNCTION: | 
 |     writeUleb128(os, import.SigIndex, "import sig index"); | 
 |     break; | 
 |   case WASM_EXTERNAL_GLOBAL: | 
 |     writeGlobalType(os, import.Global); | 
 |     break; | 
 |   case WASM_EXTERNAL_TAG: | 
 |     writeUleb128(os, 0, "tag attribute"); // Reserved "attribute" field | 
 |     writeUleb128(os, import.SigIndex, "import sig index"); | 
 |     break; | 
 |   case WASM_EXTERNAL_MEMORY: | 
 |     writeLimits(os, import.Memory); | 
 |     break; | 
 |   case WASM_EXTERNAL_TABLE: | 
 |     writeTableType(os, import.Table); | 
 |     break; | 
 |   default: | 
 |     fatal("unsupported import type: " + Twine(import.Kind)); | 
 |   } | 
 | } | 
 |  | 
 | void writeExport(raw_ostream &os, const WasmExport &export_) { | 
 |   writeStr(os, export_.Name, "export name"); | 
 |   writeU8(os, export_.Kind, "export kind"); | 
 |   switch (export_.Kind) { | 
 |   case WASM_EXTERNAL_FUNCTION: | 
 |     writeUleb128(os, export_.Index, "function index"); | 
 |     break; | 
 |   case WASM_EXTERNAL_GLOBAL: | 
 |     writeUleb128(os, export_.Index, "global index"); | 
 |     break; | 
 |   case WASM_EXTERNAL_TAG: | 
 |     writeUleb128(os, export_.Index, "tag index"); | 
 |     break; | 
 |   case WASM_EXTERNAL_MEMORY: | 
 |     writeUleb128(os, export_.Index, "memory index"); | 
 |     break; | 
 |   case WASM_EXTERNAL_TABLE: | 
 |     writeUleb128(os, export_.Index, "table index"); | 
 |     break; | 
 |   default: | 
 |     fatal("unsupported export type: " + Twine(export_.Kind)); | 
 |   } | 
 | } | 
 |  | 
 | } // namespace wasm | 
 | } // namespace lld |