llvm-project/lld/wasm/WriterUtils.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

263 lines
7.9 KiB
C++
Raw Normal View History

//===- 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/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.str());
}
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) + "]]");
2018-02-17 03:53:29 +08:00
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, support::little);
}
void writeU64(raw_ostream &os, uint64_t number, const Twine &msg) {
debugWrite(os.tell(), msg + "[0x" + utohexstr(number) + "]");
support::endian::write(os, number, support::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:
[WebAssembly] Remove WasmTagType This removes `WasmTagType`. `WasmTagType` contained an attribute and a signature index: ``` struct WasmTagType { uint8_t Attribute; uint32_t SigIndex; }; ``` Currently the attribute field is not used and reserved for future use, and always 0. And that this class contains `SigIndex` as its property is a little weird in the place, because the tag type's signature index is not an inherent property of a tag but rather a reference to another section that changes after linking. This makes tag handling in the linker also weird that tag-related methods are taking both `WasmTagType` and `WasmSignature` even though `WasmTagType` contains a signature index. This is because the signature index changes in linking so it doesn't have any info at this point. This instead moves `SigIndex` to `struct WasmTag` itself, as we did for `struct WasmFunction` in D111104. In this CL, in lib/MC and lib/Object, this now treats tag types in the same way as function types. Also in YAML, this removes `struct Tag`, because now it only contains the tag index. Also tags set `SigIndex` in `WasmImport` union, as functions do. I think this makes things simpler and makes tag handling more in line with function handling. These two shares similar properties in that both of them have signatures, but they are kind of nominal so having the same signature doesn't mean they are the same element. Also a drive-by fix: the reserved 'attirubute' part's encoding changed from uleb32 to uint8 a while ago. This was fixed in lib/MC and lib/Object but not in YAML. This doesn't change object files because the field's value is always 0 and its encoding is the same for the both encoding. This is effectively NFC; I didn't mark it as such just because it changed YAML test results. Reviewed By: sbc100, tlively Differential Revision: https://reviews.llvm.org/D111086
2021-10-02 10:07:41 +08:00
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