summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuri Iozzelli <y.iozzelli@gmail.com>2022-02-25 16:36:35 +0100
committerGitHub <noreply@github.com>2022-02-25 15:36:35 +0000
commit1f59b65d8befc8512ff9045bb371ea5ec379a78c (patch)
tree349621c048c7716a746d3361c8ec6219d32f0250
parent08cf71aa180674432750a02581f1e214c310042d (diff)
downloadwabt-1f59b65d8befc8512ff9045bb371ea5ec379a78c.tar.gz
wabt-1f59b65d8befc8512ff9045bb371ea5ec379a78c.tar.bz2
wabt-1f59b65d8befc8512ff9045bb371ea5ec379a78c.zip
Add initial support for code metadata (#1840)
See https://github.com/WebAssembly/tool-conventions/blob/main/CodeMetadata.md for the specification. In particular this pr implements the following: - Parsing code metadata sections in BinaryReader, providing appropriate callbacks that a BinaryReaderDelegate can implement: - BinaryReaderObjdump: show the sections in a human-readable form - BinaryReaderIr: add code metadata in the IR as expressions - Parsing code metadata annotations in text format, adding them in the IR like the BinaryReaderIR does - Writing the code metadata present in the IR in the proper sections when converting IR to binary - Support in wasm-decompiler for showing code metadata as comments in the pseudo-code All the features have corresponding tests. Support for code metadata is gated through the --enable-code-metadata feature. For reading/writing in the text format, --enable-annotations is also required. Missing features: Support for function-level code metadata (offset 0) Extensive validation in validator.cc (like making sure that all metadata instances are at the same code offset of an instruction)
-rw-r--r--src/binary-reader-ir.cc94
-rw-r--r--src/binary-reader-logging.cc20
-rw-r--r--src/binary-reader-logging.h7
-rw-r--r--src/binary-reader-nop.h15
-rw-r--r--src/binary-reader-objdump.cc29
-rw-r--r--src/binary-reader.cc58
-rw-r--r--src/binary-reader.h10
-rw-r--r--src/binary-writer.cc88
-rw-r--r--src/binary.h1
-rw-r--r--src/c-writer.cc3
-rw-r--r--src/decompiler.cc6
-rw-r--r--src/expr-visitor.cc4
-rw-r--r--src/expr-visitor.h2
-rw-r--r--src/feature.def1
-rw-r--r--src/ir-util.cc1
-rw-r--r--src/ir.cc1
-rw-r--r--src/ir.h15
-rw-r--r--src/validator.cc5
-rw-r--r--src/wast-parser.cc46
-rw-r--r--src/wast-parser.h1
-rw-r--r--src/wat-writer.cc11
-rw-r--r--test/binary/bad-code-metadata-function-count.txt65
-rw-r--r--test/binary/bad-code-metadata-function-duplicate.txt67
-rw-r--r--test/binary/bad-code-metadata-function-index.txt62
-rw-r--r--test/binary/bad-code-metadata-function-out-of-order.txt82
-rw-r--r--test/binary/bad-code-metadata-instance-count.txt65
-rw-r--r--test/binary/bad-code-metadata-instance-duplicate.txt68
-rw-r--r--test/binary/bad-code-metadata-instance-out-of-order.txt68
-rw-r--r--test/binary/code-metadata-section.txt61
-rw-r--r--test/decompile/code-metadata.txt17
-rw-r--r--test/dump/code-metadata.txt43
-rw-r--r--test/help/spectest-interp.txt1
-rw-r--r--test/help/wasm-interp.txt1
-rw-r--r--test/help/wasm-opcodecnt.txt1
-rw-r--r--test/help/wasm-validate.txt1
-rw-r--r--test/help/wasm2wat.txt1
-rw-r--r--test/help/wast2json.txt1
-rw-r--r--test/help/wat-desugar.txt1
-rw-r--r--test/help/wat2wasm.txt1
-rw-r--r--test/roundtrip/code-metadata.txt8
-rwxr-xr-xtest/run-roundtrip.py6
41 files changed, 1032 insertions, 6 deletions
diff --git a/src/binary-reader-ir.cc b/src/binary-reader-ir.cc
index fbb1b091..ff3eafab 100644
--- a/src/binary-reader-ir.cc
+++ b/src/binary-reader-ir.cc
@@ -21,6 +21,7 @@
#include <cstdarg>
#include <cstdint>
#include <cstdio>
+#include <deque>
#include <vector>
#include "src/binary-reader-nop.h"
@@ -43,6 +44,52 @@ struct LabelNode {
LabelNode::LabelNode(LabelType label_type, ExprList* exprs, Expr* context)
: label_type(label_type), exprs(exprs), context(context) {}
+class CodeMetadataExprQueue {
+ private:
+ struct Entry {
+ Func* func;
+ std::deque<std::unique_ptr<CodeMetadataExpr>> func_queue;
+ Entry(Func* f) : func(f) {}
+ };
+ std::deque<Entry> entries;
+
+ public:
+ CodeMetadataExprQueue() {}
+ void push_func(Func* f) { entries.emplace_back(f); }
+ void push_metadata(std::unique_ptr<CodeMetadataExpr> meta) {
+ assert(!entries.empty());
+ entries.back().func_queue.push_back(std::move(meta));
+ }
+
+ std::unique_ptr<CodeMetadataExpr> pop_match(Func* f, Offset offset) {
+ std::unique_ptr<CodeMetadataExpr> ret;
+ if (entries.empty()) {
+ return ret;
+ }
+
+ auto& current_entry = entries.front();
+
+ if (current_entry.func != f)
+ return ret;
+ if (current_entry.func_queue.empty()) {
+ entries.pop_front();
+ return ret;
+ }
+
+ auto& current_metadata = current_entry.func_queue.front();
+ if (current_metadata->loc.offset + current_entry.func->loc.offset !=
+ offset) {
+ return ret;
+ }
+
+ current_metadata->loc = Location(offset);
+ ret = std::move(current_metadata);
+ current_entry.func_queue.pop_front();
+
+ return ret;
+ }
+};
+
class BinaryReaderIR : public BinaryReaderNop {
public:
BinaryReaderIR(Module* out_module, const char* filename, Errors* errors);
@@ -115,6 +162,7 @@ class BinaryReaderIR : public BinaryReaderNop {
Result BeginFunctionBody(Index index, Offset size) override;
Result OnLocalDecl(Index decl_index, Index count, Type type) override;
+ Result OnOpcode(Opcode opcode) override;
Result OnAtomicLoadExpr(Opcode opcode,
Address alignment_log2,
Address offset) override;
@@ -276,6 +324,12 @@ class BinaryReaderIR : public BinaryReaderNop {
Result OnSectionSymbol(Index index,
uint32_t flags,
Index section_index) override;
+ /* Code Metadata sections */
+ Result BeginCodeMetadataSection(std::string_view name, Offset size) override;
+ Result OnCodeMetadataFuncCount(Index count) override;
+ Result OnCodeMetadataCount(Index function_index, Index count) override;
+ Result OnCodeMetadata(Offset offset, const void* data, Address size) override;
+
Result OnTagSymbol(Index index,
uint32_t flags,
std::string_view name,
@@ -318,6 +372,9 @@ class BinaryReaderIR : public BinaryReaderNop {
Func* current_func_ = nullptr;
std::vector<LabelNode> label_stack_;
const char* filename_;
+
+ CodeMetadataExprQueue code_metadata_queue_;
+ std::string_view current_metadata_name_;
};
BinaryReaderIR::BinaryReaderIR(Module* out_module,
@@ -660,6 +717,7 @@ Result BinaryReaderIR::OnFunctionBodyCount(Index count) {
Result BinaryReaderIR::BeginFunctionBody(Index index, Offset size) {
current_func_ = module_->funcs[index];
+ current_func_->loc = GetLocation();
PushLabel(LabelType::Func, &current_func_->exprs);
return Result::Ok;
}
@@ -669,6 +727,15 @@ Result BinaryReaderIR::OnLocalDecl(Index decl_index, Index count, Type type) {
return Result::Ok;
}
+Result BinaryReaderIR::OnOpcode(Opcode opcode) {
+ std::unique_ptr<CodeMetadataExpr> metadata =
+ code_metadata_queue_.pop_match(current_func_, GetLocation().offset - 1);
+ if (metadata) {
+ return AppendExpr(std::move(metadata));
+ }
+ return Result::Ok;
+}
+
Result BinaryReaderIR::OnAtomicLoadExpr(Opcode opcode,
Address alignment_log2,
Address offset) {
@@ -1447,6 +1514,33 @@ Result BinaryReaderIR::OnLocalNameLocalCount(Index index, Index count) {
return Result::Ok;
}
+Result BinaryReaderIR::BeginCodeMetadataSection(std::string_view name,
+ Offset size) {
+ current_metadata_name_ = name;
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnCodeMetadataFuncCount(Index count) {
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnCodeMetadataCount(Index function_index, Index count) {
+ code_metadata_queue_.push_func(module_->funcs[function_index]);
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnCodeMetadata(Offset offset,
+ const void* data,
+ Address size) {
+ std::vector<uint8_t> data_(static_cast<const uint8_t*>(data),
+ static_cast<const uint8_t*>(data) + size);
+ auto meta =
+ MakeUnique<CodeMetadataExpr>(current_metadata_name_, std::move(data_));
+ meta->loc.offset = offset;
+ code_metadata_queue_.push_metadata(std::move(meta));
+ return Result::Ok;
+}
+
Result BinaryReaderIR::OnLocalName(Index func_index,
Index local_index,
std::string_view name) {
diff --git a/src/binary-reader-logging.cc b/src/binary-reader-logging.cc
index 2e7cb41c..13c68852 100644
--- a/src/binary-reader-logging.cc
+++ b/src/binary-reader-logging.cc
@@ -638,6 +638,22 @@ Result BinaryReaderLogging::OnComdatEntry(ComdatType kind, Index index) {
return reader_->OnComdatEntry(kind, index);
}
+Result BinaryReaderLogging::BeginCodeMetadataSection(std::string_view name,
+ Offset size) {
+ LOGF("BeginCodeMetadataSection('" PRIstringview "', size:%" PRIzd ")\n",
+ WABT_PRINTF_STRING_VIEW_ARG(name), size);
+ Indent();
+ return reader_->BeginCodeMetadataSection(name, size);
+}
+Result BinaryReaderLogging::OnCodeMetadata(Offset code_offset,
+ const void* data,
+ Address size) {
+ std::string_view content(static_cast<const char*>(data), size);
+ LOGF("OnCodeMetadata(offset: %" PRIzd ", data: \"" PRIstringview "\")\n",
+ code_offset, WABT_PRINTF_STRING_VIEW_ARG(content));
+ return reader_->OnCodeMetadata(code_offset, data, size);
+}
+
#define DEFINE_BEGIN(name) \
Result BinaryReaderLogging::name(Offset size) { \
LOGF(#name "(%" PRIzd ")\n", size); \
@@ -892,6 +908,10 @@ DEFINE_INDEX(OnTagCount);
DEFINE_INDEX_INDEX(OnTagType, "index", "sig_index")
DEFINE_END(EndTagSection);
+DEFINE_INDEX(OnCodeMetadataFuncCount);
+DEFINE_INDEX_INDEX(OnCodeMetadataCount, "func_index", "count");
+DEFINE_END(EndCodeMetadataSection);
+
// We don't need to log these (the individual opcodes are logged instead), but
// we still need to forward the calls.
Result BinaryReaderLogging::OnOpcode(Opcode opcode) {
diff --git a/src/binary-reader-logging.h b/src/binary-reader-logging.h
index 740da63f..51cd5985 100644
--- a/src/binary-reader-logging.h
+++ b/src/binary-reader-logging.h
@@ -386,6 +386,13 @@ class BinaryReaderLogging : public BinaryReaderDelegate {
Result OnTagType(Index index, Index sig_index) override;
Result EndTagSection() override;
+ /* Code Metadata sections */
+ Result BeginCodeMetadataSection(std::string_view name, Offset size) override;
+ Result OnCodeMetadataFuncCount(Index count) override;
+ Result OnCodeMetadataCount(Index function_index, Index count) override;
+ Result OnCodeMetadata(Offset offset, const void* data, Address size) override;
+ Result EndCodeMetadataSection() override;
+
private:
void Indent();
void Dedent();
diff --git a/src/binary-reader-nop.h b/src/binary-reader-nop.h
index 4adde26a..ff78aa7f 100644
--- a/src/binary-reader-nop.h
+++ b/src/binary-reader-nop.h
@@ -462,6 +462,21 @@ class BinaryReaderNop : public BinaryReaderDelegate {
Result OnTagType(Index index, Index sig_index) override { return Result::Ok; }
Result EndTagSection() override { return Result::Ok; }
+ /* Code Metadata sections */
+ Result BeginCodeMetadataSection(std::string_view name, Offset size) override {
+ return Result::Ok;
+ }
+ Result OnCodeMetadataFuncCount(Index count) override { return Result::Ok; }
+ Result OnCodeMetadataCount(Index function_index, Index count) override {
+ return Result::Ok;
+ }
+ Result OnCodeMetadata(Offset offset,
+ const void* data,
+ Address size) override {
+ return Result::Ok;
+ }
+ Result EndCodeMetadataSection() override { return Result::Ok; }
+
/* Dylink section */
Result BeginDylinkSection(Offset size) override { return Result::Ok; }
Result OnDylinkInfo(uint32_t mem_size,
diff --git a/src/binary-reader-objdump.cc b/src/binary-reader-objdump.cc
index deb1167f..cb682698 100644
--- a/src/binary-reader-objdump.cc
+++ b/src/binary-reader-objdump.cc
@@ -1139,6 +1139,10 @@ class BinaryReaderObjdump : public BinaryReaderObjdumpBase {
Result OnF32ConstExpr(uint32_t value) override;
Result OnF64ConstExpr(uint64_t value) override;
Result OnGlobalGetExpr(Index global_index) override;
+ Result OnCodeMetadataCount(Index function_index, Index count) override;
+ Result OnCodeMetadata(Offset code_offset,
+ const void* data,
+ Address size) override;
private:
Result InitExprToConstOffset(const InitExpr& expr, uint64_t* out_offset);
@@ -2192,6 +2196,31 @@ Result BinaryReaderObjdump::OnTagType(Index index, Index sig_index) {
return Result::Ok;
}
+Result BinaryReaderObjdump::OnCodeMetadataCount(Index function_index,
+ Index count) {
+ if (!ShouldPrintDetails()) {
+ return Result::Ok;
+ }
+ printf(" - func[%" PRIindex "]", function_index);
+ auto name = GetFunctionName(function_index);
+ if (!name.empty()) {
+ printf(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ printf(":\n");
+ return Result::Ok;
+}
+Result BinaryReaderObjdump::OnCodeMetadata(Offset code_offset,
+ const void* data,
+ Address size) {
+ if (!ShouldPrintDetails()) {
+ return Result::Ok;
+ }
+ printf(" - meta[%" PRIzx "]:\n", code_offset);
+
+ out_stream_->WriteMemoryDump(data, size, 0, PrintChars::Yes, " - ");
+ return Result::Ok;
+}
+
} // end anonymous namespace
std::string_view ObjdumpNames::Get(Index index) const {
diff --git a/src/binary-reader.cc b/src/binary-reader.cc
index 0cef458d..28f47b64 100644
--- a/src/binary-reader.cc
+++ b/src/binary-reader.cc
@@ -140,6 +140,8 @@ class BinaryReader {
Result ReadDylink0Section(Offset section_size) WABT_WARN_UNUSED;
Result ReadTargetFeaturesSections(Offset section_size) WABT_WARN_UNUSED;
Result ReadLinkingSection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadCodeMetadataSection(std::string_view name,
+ Offset section_size) WABT_WARN_UNUSED;
Result ReadCustomSection(Index section_index,
Offset section_size) WABT_WARN_UNUSED;
Result ReadTypeSection(Offset section_size) WABT_WARN_UNUSED;
@@ -2258,6 +2260,57 @@ Result BinaryReader::ReadTagSection(Offset section_size) {
return Result::Ok;
}
+Result BinaryReader::ReadCodeMetadataSection(std::string_view name,
+ Offset section_size) {
+ CALLBACK(BeginCodeMetadataSection, name, section_size);
+
+ Index num_funcions;
+ CHECK_RESULT(ReadCount(&num_funcions, "function count"));
+ CALLBACK(OnCodeMetadataFuncCount, num_funcions);
+
+ Index last_function_index = kInvalidIndex;
+ for (Index i = 0; i < num_funcions; ++i) {
+ Index function_index;
+ CHECK_RESULT(ReadCount(&function_index, "function index"));
+ ERROR_UNLESS(function_index >= num_func_imports_,
+ "function import can't have metadata (got %" PRIindex ")",
+ function_index);
+ ERROR_UNLESS(function_index < NumTotalFuncs(),
+ "invalid function index: %" PRIindex, function_index);
+ ERROR_UNLESS(function_index != last_function_index,
+ "duplicate function index: %" PRIindex, function_index);
+ ERROR_UNLESS(last_function_index == kInvalidIndex ||
+ function_index > last_function_index,
+ "function index out of order: %" PRIindex, function_index);
+ last_function_index = function_index;
+
+ Index num_metadata;
+ CHECK_RESULT(ReadCount(&num_metadata, "metadata instances count"));
+
+ CALLBACK(OnCodeMetadataCount, function_index, num_metadata);
+
+ Offset last_code_offset = kInvalidOffset;
+ for (Index j = 0; j < num_metadata; ++j) {
+ Offset code_offset;
+ CHECK_RESULT(ReadOffset(&code_offset, "code offset"));
+ ERROR_UNLESS(code_offset != last_code_offset,
+ "duplicate code offset: %" PRIzx, code_offset);
+ ERROR_UNLESS(
+ last_code_offset == kInvalidOffset || code_offset > last_code_offset,
+ "code offset out of order: %" PRIzx, code_offset);
+ last_code_offset = code_offset;
+
+ Address data_size;
+ const void* data;
+ CHECK_RESULT(ReadBytes(&data, &data_size, "instance data"));
+ CALLBACK(OnCodeMetadata, code_offset, data, data_size);
+ }
+ }
+
+ CALLBACK(EndCodeMetadataSection);
+ return Result::Ok;
+}
+
Result BinaryReader::ReadCustomSection(Index section_index,
Offset section_size) {
std::string_view section_name;
@@ -2280,6 +2333,11 @@ Result BinaryReader::ReadCustomSection(Index section_index,
CHECK_RESULT(ReadTargetFeaturesSections(section_size));
} else if (section_name == WABT_BINARY_SECTION_LINKING) {
CHECK_RESULT(ReadLinkingSection(section_size));
+ } else if (options_.features.code_metadata_enabled() &&
+ section_name.find(WABT_BINARY_SECTION_CODE_METADATA) == 0) {
+ std::string_view metadata_name = section_name;
+ metadata_name.remove_prefix(sizeof(WABT_BINARY_SECTION_CODE_METADATA) - 1);
+ CHECK_RESULT(ReadCodeMetadataSection(metadata_name, section_size));
} else {
// This is an unknown custom section, skip it.
state_.offset = read_end_;
diff --git a/src/binary-reader.h b/src/binary-reader.h
index 978ce2f5..4e068ecb 100644
--- a/src/binary-reader.h
+++ b/src/binary-reader.h
@@ -461,6 +461,16 @@ class BinaryReaderDelegate {
virtual Result OnTagType(Index index, Index sig_index) = 0;
virtual Result EndTagSection() = 0;
+ /* Code Metadata sections */
+ virtual Result BeginCodeMetadataSection(std::string_view name,
+ Offset size) = 0;
+ virtual Result OnCodeMetadataFuncCount(Index count) = 0;
+ virtual Result OnCodeMetadataCount(Index function_index, Index count) = 0;
+ virtual Result OnCodeMetadata(Offset offset,
+ const void* data,
+ Address size) = 0;
+ virtual Result EndCodeMetadataSection() = 0;
+
const State* state = nullptr;
};
diff --git a/src/binary-writer.cc b/src/binary-writer.cc
index 76d0b765..00beede6 100644
--- a/src/binary-writer.cc
+++ b/src/binary-writer.cc
@@ -29,6 +29,7 @@
#include "src/binary.h"
#include "src/cast.h"
+#include "src/expr-visitor.h"
#include "src/ir.h"
#include "src/leb128.h"
#include "src/stream.h"
@@ -355,6 +356,23 @@ class SymbolTable {
}
};
+struct CodeMetadata {
+ Offset offset;
+ std::vector<uint8_t> data;
+ CodeMetadata(Offset offset, std::vector<uint8_t> data)
+ : offset(offset), data(std::move(data)) {}
+};
+struct FuncCodeMetadata {
+ Index func_idx;
+ std::vector<CodeMetadata> entries;
+ FuncCodeMetadata(Index func_idx) : func_idx(func_idx) {}
+};
+struct CodeMetadataSection {
+ std::vector<FuncCodeMetadata> entries;
+};
+typedef std::unordered_map<std::string_view, CodeMetadataSection>
+ CodeMetadataSections;
+
class BinaryWriter {
WABT_DISALLOW_COPY_AND_ASSIGN(BinaryWriter);
@@ -413,6 +431,7 @@ class BinaryWriter {
void WriteLinkingSection();
template <typename T>
void WriteNames(const std::vector<T*>& elems, NameSectionSubsection type);
+ void WriteCodeMetadataSections();
Stream* stream_;
const WriteBinaryOptions& options_;
@@ -438,6 +457,10 @@ class BinaryWriter {
size_t data_count_start_ = 0;
size_t data_count_end_ = 0;
bool has_data_segment_instruction_ = false;
+
+ CodeMetadataSections code_metadata_sections_;
+ Offset cur_func_start_offset_;
+ Index cur_func_index_;
};
static uint8_t log2_u32(uint32_t x) {
@@ -1105,6 +1128,17 @@ void BinaryWriter::WriteExpr(const Func* func, const Expr* expr) {
case ExprType::Unreachable:
WriteOpcode(stream_, Opcode::Unreachable);
break;
+ case ExprType::CodeMetadata: {
+ auto* meta_expr = cast<CodeMetadataExpr>(expr);
+ auto& s = code_metadata_sections_[meta_expr->name];
+ if (s.entries.empty() || s.entries.back().func_idx != cur_func_index_) {
+ s.entries.emplace_back(cur_func_index_);
+ }
+ auto& a = s.entries.back();
+ Offset code_offset = stream_->offset() - cur_func_start_offset_;
+ a.entries.emplace_back(code_offset, meta_expr->data);
+ break;
+ }
}
}
@@ -1575,13 +1609,15 @@ Result BinaryWriter::WriteModule() {
WriteU32Leb128(stream_, num_funcs, "num functions");
for (size_t i = 0; i < num_funcs; ++i) {
+ cur_func_index_ = i + module_->num_func_imports;
WriteHeader("function body", i);
- const Func* func = module_->funcs[i + module_->num_func_imports];
+ const Func* func = module_->funcs[cur_func_index_];
/* TODO(binji): better guess of the size of the function body section */
const Offset leb_size_guess = 1;
Offset body_size_offset =
WriteU32Leb128Space(leb_size_guess, "func body size (guess)");
+ cur_func_start_offset_ = stream_->offset();
WriteFunc(func);
auto func_start_offset = body_size_offset - last_section_payload_offset_;
auto func_end_offset = stream_->offset() - last_section_payload_offset_;
@@ -1610,6 +1646,7 @@ Result BinaryWriter::WriteModule() {
assert(data_count_end_ == code_start_);
assert(last_section_type_ == BinarySection::Code);
stream_->MoveData(data_count_start_, data_count_end_, size);
+ code_start_ = data_count_start_;
}
stream_->Truncate(data_count_start_ + size);
@@ -1625,6 +1662,8 @@ Result BinaryWriter::WriteModule() {
}
}
+ WriteCodeMetadataSections();
+
if (module_->data_segments.size()) {
BeginKnownSection(BinarySection::Data);
WriteU32Leb128(stream_, module_->data_segments.size(), "num data segments");
@@ -1719,6 +1758,53 @@ Result BinaryWriter::WriteModule() {
return stream_->result();
}
+void BinaryWriter::WriteCodeMetadataSections() {
+ if (code_metadata_sections_.empty())
+ return;
+
+ section_count_ -= 1;
+ // We have to increment the code section's index; adjust anything
+ // that might have captured it.
+ for (RelocSection& section : reloc_sections_) {
+ if (section.section_index == section_count_) {
+ assert(last_section_type_ == BinarySection::Code);
+ section.section_index += code_metadata_sections_.size();
+ }
+ }
+
+ MemoryStream tmp_stream;
+ Stream* main_stream = stream_;
+ stream_ = &tmp_stream;
+ for (auto& s : code_metadata_sections_) {
+ std::string name = "metadata.code.";
+ name.append(s.first);
+ auto& section = s.second;
+ BeginCustomSection(name.c_str());
+ WriteU32Leb128(stream_, section.entries.size(), "function count");
+ for (auto& f : section.entries) {
+ WriteU32Leb128WithReloc(f.func_idx, "function index",
+ RelocType::FuncIndexLEB);
+ WriteU32Leb128(stream_, f.entries.size(), "instances count");
+ for (auto& a : f.entries) {
+ WriteU32Leb128(stream_, a.offset, "code offset");
+ WriteU32Leb128(stream_, a.data.size(), "data length");
+ stream_->WriteData(a.data.data(), a.data.size(), "data",
+ PrintChars::Yes);
+ }
+ }
+ EndSection();
+ }
+ stream_ = main_stream;
+ auto buf = tmp_stream.ReleaseOutputBuffer();
+ stream_->MoveData(code_start_ + buf->data.size(), code_start_,
+ stream_->offset() - code_start_);
+ stream_->WriteDataAt(code_start_, buf->data.data(), buf->data.size());
+ stream_->AddOffset(buf->data.size());
+ code_start_ += buf->data.size();
+ section_count_ += 1;
+ last_section_type_ = BinarySection::Code;
+}
+
} // end anonymous namespace
Result WriteBinaryModule(Stream* stream,
diff --git a/src/binary.h b/src/binary.h
index bd0a3de7..39e0f201 100644
--- a/src/binary.h
+++ b/src/binary.h
@@ -34,6 +34,7 @@
#define WABT_BINARY_SECTION_TARGET_FEATURES "target_features"
#define WABT_BINARY_SECTION_DYLINK "dylink"
#define WABT_BINARY_SECTION_DYLINK0 "dylink.0"
+#define WABT_BINARY_SECTION_CODE_METADATA "metadata.code."
#define WABT_FOREACH_BINARY_SECTION(V) \
V(Custom, custom, 0) \
diff --git a/src/c-writer.cc b/src/c-writer.cc
index c1336671..388d8168 100644
--- a/src/c-writer.cc
+++ b/src/c-writer.cc
@@ -1498,6 +1498,9 @@ void CWriter::Write(const ExprList& exprs) {
break;
}
+ case ExprType::CodeMetadata:
+ break;
+
case ExprType::Compare:
Write(*cast<CompareExpr>(&expr));
break;
diff --git a/src/decompiler.cc b/src/decompiler.cc
index 1ddaedc3..91272301 100644
--- a/src/decompiler.cc
+++ b/src/decompiler.cc
@@ -604,6 +604,12 @@ struct Decompiler {
ts += "](";
return WrapChild(args[0], ts, ")", Precedence::Atomic);
}
+ case ExprType::CodeMetadata: {
+ auto cme = cast<CodeMetadataExpr>(n.e);
+ std::string c = "// @metadata.code." + cme->name + " ";
+ c += BinaryToString(cme->data);
+ return Value{{std::move(c)}, Precedence::None};
+ }
default: {
// Everything that looks like a function call.
std::string name;
diff --git a/src/expr-visitor.cc b/src/expr-visitor.cc
index d346f3b7..e7fee9bf 100644
--- a/src/expr-visitor.cc
+++ b/src/expr-visitor.cc
@@ -219,6 +219,10 @@ Result ExprVisitor::HandleDefaultState(Expr* expr) {
CHECK_RESULT(delegate_->OnCallRefExpr(cast<CallRefExpr>(expr)));
break;
+ case ExprType::CodeMetadata:
+ CHECK_RESULT(delegate_->OnCodeMetadataExpr(cast<CodeMetadataExpr>(expr)));
+ break;
+
case ExprType::Compare:
CHECK_RESULT(delegate_->OnCompareExpr(cast<CompareExpr>(expr)));
break;
diff --git a/src/expr-visitor.h b/src/expr-visitor.h
index ab7dc9e4..e00c487d 100644
--- a/src/expr-visitor.h
+++ b/src/expr-visitor.h
@@ -76,6 +76,7 @@ class ExprVisitor::Delegate {
virtual Result OnCallExpr(CallExpr*) = 0;
virtual Result OnCallIndirectExpr(CallIndirectExpr*) = 0;
virtual Result OnCallRefExpr(CallRefExpr*) = 0;
+ virtual Result OnCodeMetadataExpr(CodeMetadataExpr*) = 0;
virtual Result OnCompareExpr(CompareExpr*) = 0;
virtual Result OnConstExpr(ConstExpr*) = 0;
virtual Result OnConvertExpr(ConvertExpr*) = 0;
@@ -149,6 +150,7 @@ class ExprVisitor::DelegateNop : public ExprVisitor::Delegate {
Result OnCallExpr(CallExpr*) override { return Result::Ok; }
Result OnCallIndirectExpr(CallIndirectExpr*) override { return Result::Ok; }
Result OnCallRefExpr(CallRefExpr*) override { return Result::Ok; }
+ Result OnCodeMetadataExpr(CodeMetadataExpr*) override { return Result::Ok; }
Result OnCompareExpr(CompareExpr*) override { return Result::Ok; }
Result OnConstExpr(ConstExpr*) override { return Result::Ok; }
Result OnConvertExpr(ConvertExpr*) override { return Result::Ok; }
diff --git a/src/feature.def b/src/feature.def
index 63f920df..26b1db79 100644
--- a/src/feature.def
+++ b/src/feature.def
@@ -34,6 +34,7 @@ WABT_FEATURE(tail_call, "tail-call", false, "Tail-call
WABT_FEATURE(bulk_memory, "bulk-memory", true, "Bulk-memory operations")
WABT_FEATURE(reference_types, "reference-types", true, "Reference types (externref)")
WABT_FEATURE(annotations, "annotations", false, "Custom annotation syntax")
+WABT_FEATURE(code_metadata, "code-metadata", false, "Code metadata")
WABT_FEATURE(gc, "gc", false, "Garbage collection")
WABT_FEATURE(memory64, "memory64", false, "64-bit memory")
WABT_FEATURE(multi_memory, "multi-memory", false, "Multi-memory")
diff --git a/src/ir-util.cc b/src/ir-util.cc
index 959f0937..099ee8c3 100644
--- a/src/ir-util.cc
+++ b/src/ir-util.cc
@@ -165,6 +165,7 @@ ModuleContext::Arities ModuleContext::GetExprArity(const Expr& expr) const {
case ExprType::DataDrop:
case ExprType::ElemDrop:
case ExprType::AtomicFence:
+ case ExprType::CodeMetadata:
return {0, 0};
case ExprType::MemoryInit:
diff --git a/src/ir.cc b/src/ir.cc
index 82bead38..aed789f1 100644
--- a/src/ir.cc
+++ b/src/ir.cc
@@ -40,6 +40,7 @@ const char* ExprTypeName[] = {
"Call",
"CallIndirect",
"CallRef",
+ "CodeMetadata",
"Compare",
"Const",
"Convert",
diff --git a/src/ir.h b/src/ir.h
index 5974f64f..01a2bf57 100644
--- a/src/ir.h
+++ b/src/ir.h
@@ -320,6 +320,7 @@ enum class ExprType {
Call,
CallIndirect,
CallRef,
+ CodeMetadata,
Compare,
Const,
Convert,
@@ -631,6 +632,19 @@ class CallIndirectExpr : public ExprMixin<ExprType::CallIndirect> {
Var table;
};
+class CodeMetadataExpr : public ExprMixin<ExprType::CodeMetadata> {
+ public:
+ explicit CodeMetadataExpr(std::string_view name,
+ std::vector<uint8_t> data,
+ const Location& loc = Location())
+ : ExprMixin<ExprType::CodeMetadata>(loc),
+ name(std::move(name)),
+ data(std::move(data)) {}
+
+ std::string_view name;
+ std::vector<uint8_t> data;
+};
+
class ReturnCallIndirectExpr : public ExprMixin<ExprType::ReturnCallIndirect> {
public:
explicit ReturnCallIndirectExpr(const Location& loc = Location())
@@ -846,6 +860,7 @@ struct Func {
LocalTypes local_types;
BindingHash bindings;
ExprList exprs;
+ Location loc;
};
struct Global {
diff --git a/src/validator.cc b/src/validator.cc
index f662b3b0..b3f231bd 100644
--- a/src/validator.cc
+++ b/src/validator.cc
@@ -94,6 +94,7 @@ class Validator : public ExprVisitor::Delegate {
Result OnCallExpr(CallExpr*) override;
Result OnCallIndirectExpr(CallIndirectExpr*) override;
Result OnCallRefExpr(CallRefExpr*) override;
+ Result OnCodeMetadataExpr(CodeMetadataExpr*) override;
Result OnCompareExpr(CompareExpr*) override;
Result OnConstExpr(ConstExpr*) override;
Result OnConvertExpr(ConvertExpr*) override;
@@ -286,6 +287,10 @@ Result Validator::OnCallRefExpr(CallRefExpr* expr) {
return Result::Error;
}
+Result Validator::OnCodeMetadataExpr(CodeMetadataExpr* expr) {
+ return Result::Ok;
+}
+
Result Validator::OnCompareExpr(CompareExpr* expr) {
result_ |= validator_.OnCompare(expr->loc, expr->opcode);
return Result::Ok;
diff --git a/src/wast-parser.cc b/src/wast-parser.cc
index 99406786..f07ee8b1 100644
--- a/src/wast-parser.cc
+++ b/src/wast-parser.cc
@@ -194,6 +194,10 @@ bool IsInstr(TokenTypePair pair) {
return IsPlainOrBlockInstr(pair[0]) || IsExpr(pair);
}
+bool IsLparAnn(TokenTypePair pair) {
+ return pair[0] == TokenType::LparAnn;
+}
+
bool IsCatch(TokenType token_type) {
return token_type == TokenType::Catch || token_type == TokenType::CatchAll;
}
@@ -551,12 +555,18 @@ TokenType WastParser::Peek(size_t n) {
if (cur.token_type() != TokenType::LparAnn) {
tokens_.push_back(cur);
} else {
- // Custom annotation. For now, discard until matching Rpar.
+ // Custom annotation. For now, discard until matching Rpar, unless it is
+ // a code metadata annotation. In that case, we know how to parse it.
if (!options_->features.annotations_enabled()) {
Error(cur.loc, "annotations not enabled: %s", cur.to_string().c_str());
tokens_.push_back(Token(cur.loc, TokenType::Invalid));
continue;
}
+ if (options_->features.code_metadata_enabled() &&
+ cur.text().find("metadata.code.") == 0) {
+ tokens_.push_back(cur);
+ continue;
+ }
int indent = 1;
while (indent > 0) {
cur = lexer_->GetToken(this);
@@ -1826,11 +1836,22 @@ Result WastParser::ParseResultList(
Result WastParser::ParseInstrList(ExprList* exprs) {
WABT_TRACE(ParseInstrList);
ExprList new_exprs;
- while (IsInstr(PeekPair())) {
- if (Succeeded(ParseInstr(&new_exprs))) {
- exprs->splice(exprs->end(), new_exprs);
+ while (true) {
+ auto pair = PeekPair();
+ if (IsInstr(pair)) {
+ if (Succeeded(ParseInstr(&new_exprs))) {
+ exprs->splice(exprs->end(), new_exprs);
+ } else {
+ CHECK_RESULT(Synchronize(IsInstr));
+ }
+ } else if (IsLparAnn(pair)) {
+ if (Succeeded(ParseCodeMetadataAnnotation(&new_exprs))) {
+ exprs->splice(exprs->end(), new_exprs);
+ } else {
+ CHECK_RESULT(Synchronize(IsLparAnn));
+ }
} else {
- CHECK_RESULT(Synchronize(IsInstr));
+ break;
}
}
return Result::Ok;
@@ -1866,6 +1887,21 @@ Result WastParser::ParseInstr(ExprList* exprs) {
}
}
+Result WastParser::ParseCodeMetadataAnnotation(ExprList* exprs) {
+ WABT_TRACE(ParseCodeMetadataAnnotation);
+ Token tk = Consume();
+ std::string_view name = tk.text();
+ name.remove_prefix(sizeof("metadata.code.") - 1);
+ std::string data_text;
+ CHECK_RESULT(ParseQuotedText(&data_text));
+ std::vector<uint8_t> data(data_text.begin(), data_text.end());
+ exprs->push_back(MakeUnique<CodeMetadataExpr>(name, std::move(data)));
+ TokenType rpar = Peek();
+ assert(rpar == TokenType::Rpar);
+ Consume();
+ return Result::Ok;
+}
+
template <typename T>
Result WastParser::ParsePlainInstrVar(Location loc,
std::unique_ptr<Expr>* out_expr) {
diff --git a/src/wast-parser.h b/src/wast-parser.h
index 8c799b7f..b3ac652f 100644
--- a/src/wast-parser.h
+++ b/src/wast-parser.h
@@ -181,6 +181,7 @@ class WastParser {
Result ParseInstrList(ExprList*);
Result ParseTerminatingInstrList(ExprList*);
Result ParseInstr(ExprList*);
+ Result ParseCodeMetadataAnnotation(ExprList*);
Result ParsePlainInstr(std::unique_ptr<Expr>*);
Result ParseF32(Const*, ConstType type);
Result ParseF64(Const*, ConstType type);
diff --git a/src/wat-writer.cc b/src/wat-writer.cc
index fd4a5f43..45350e08 100644
--- a/src/wat-writer.cc
+++ b/src/wat-writer.cc
@@ -557,6 +557,7 @@ class WatWriter::ExprVisitorDelegate : public ExprVisitor::Delegate {
Result OnCallExpr(CallExpr*) override;
Result OnCallIndirectExpr(CallIndirectExpr*) override;
Result OnCallRefExpr(CallRefExpr*) override;
+ Result OnCodeMetadataExpr(CodeMetadataExpr*) override;
Result OnCompareExpr(CompareExpr*) override;
Result OnConstExpr(ConstExpr*) override;
Result OnConvertExpr(ConvertExpr*) override;
@@ -680,6 +681,16 @@ Result WatWriter::ExprVisitorDelegate::OnCallRefExpr(CallRefExpr* expr) {
return Result::Ok;
}
+Result WatWriter::ExprVisitorDelegate::OnCodeMetadataExpr(
+ CodeMetadataExpr* expr) {
+ writer_->WriteOpen("@metadata.code.", NextChar::None);
+ writer_->WriteDataWithNextChar(expr->name.data(), expr->name.size());
+ writer_->WritePutc(' ');
+ writer_->WriteQuotedData(expr->data.data(), expr->data.size());
+ writer_->WriteCloseSpace();
+ return Result::Ok;
+}
+
Result WatWriter::ExprVisitorDelegate::OnCompareExpr(CompareExpr* expr) {
writer_->WritePutsNewline(expr->opcode.GetName());
return Result::Ok;
diff --git a/test/binary/bad-code-metadata-function-count.txt b/test/binary/bad-code-metadata-function-count.txt
new file mode 100644
index 00000000..50da843a
--- /dev/null
+++ b/test/binary/bad-code-metadata-function-count.txt
@@ -0,0 +1,65 @@
+;;; TOOL: run-objdump-gen-wasm
+;;; ARGS1: -x
+magic
+version
+section(TYPE) {
+ count[1]
+ function params[0] results[1] i32
+}
+section(FUNCTION) {
+ count[1]
+ type[0]
+}
+section(MEMORY) {
+ count[1]
+ has_max[0]
+ initial[0]
+}
+section("metadata.code.test") {
+ function_count[2]
+ function_index[0]
+ ann_count[1]
+ ann_offset[1]
+ ann_data_size[1]
+ ann_data[1]
+}
+
+section(CODE) {
+ count[1]
+ func {
+ local_decls[0]
+ i32.const 1
+ return
+ }
+}
+
+(;; STDERR ;;;
+0000033: warning: unable to read u32 leb128: function index
+;;; STDERR ;;)
+(;; STDOUT ;;;
+
+bad-code-metadata-function-count.wasm: file format wasm 0x1
+
+Section Details:
+
+Type[1]:
+ - type[0] () -> i32
+Function[1]:
+ - func[0] sig=0
+Memory[1]:
+ - memory[0] pages: initial=0
+Custom:
+ - name: "metadata.code.test"
+ - func[0]:
+ - meta[1]:
+ - 0000000: 01 .
+Code[1]:
+ - func[0] size=5
+
+Code Disassembly:
+
+000037 func[0]:
+ 000038: 41 01 | i32.const 1
+ 00003a: 0f | return
+ 00003b: 0b | end
+;;; STDOUT ;;)
diff --git a/test/binary/bad-code-metadata-function-duplicate.txt b/test/binary/bad-code-metadata-function-duplicate.txt
new file mode 100644
index 00000000..4173ece2
--- /dev/null
+++ b/test/binary/bad-code-metadata-function-duplicate.txt
@@ -0,0 +1,67 @@
+;;; TOOL: run-objdump-gen-wasm
+;;; ARGS1: -x
+magic
+version
+section(TYPE) {
+ count[1]
+ function params[0] results[1] i32
+}
+section(FUNCTION) {
+ count[1]
+ type[0]
+}
+section(MEMORY) {
+ count[1]
+ has_max[0]
+ initial[0]
+}
+section("metadata.code.test") {
+ function_count[2]
+ function_index[0]
+ ann_count[1]
+ ann_offset[1]
+ ann_data_size[1]
+ ann_data[1]
+ function_index[0]
+ ann_count[0]
+}
+
+section(CODE) {
+ count[1]
+ func {
+ local_decls[0]
+ i32.const 1
+ return
+ }
+}
+
+(;; STDERR ;;;
+0000034: warning: duplicate function index: 0
+;;; STDERR ;;)
+(;; STDOUT ;;;
+
+bad-code-metadata-function-duplicate.wasm: file format wasm 0x1
+
+Section Details:
+
+Type[1]:
+ - type[0] () -> i32
+Function[1]:
+ - func[0] sig=0
+Memory[1]:
+ - memory[0] pages: initial=0
+Custom:
+ - name: "metadata.code.test"
+ - func[0]:
+ - meta[1]:
+ - 0000000: 01 .
+Code[1]:
+ - func[0] size=5
+
+Code Disassembly:
+
+000039 func[0]:
+ 00003a: 41 01 | i32.const 1
+ 00003c: 0f | return
+ 00003d: 0b | end
+;;; STDOUT ;;)
diff --git a/test/binary/bad-code-metadata-function-index.txt b/test/binary/bad-code-metadata-function-index.txt
new file mode 100644
index 00000000..48bd19c6
--- /dev/null
+++ b/test/binary/bad-code-metadata-function-index.txt
@@ -0,0 +1,62 @@
+;;; TOOL: run-objdump-gen-wasm
+;;; ARGS1: -x
+magic
+version
+section(TYPE) {
+ count[1]
+ function params[0] results[1] i32
+}
+section(FUNCTION) {
+ count[1]
+ type[0]
+}
+section(MEMORY) {
+ count[1]
+ has_max[0]
+ initial[0]
+}
+section("metadata.code.test") {
+ function_count[1]
+ function_index[2]
+ ann_count[1]
+ ann_offset[1]
+ ann_data_size[1]
+ ann_data[1]
+}
+
+section(CODE) {
+ count[1]
+ func {
+ local_decls[0]
+ i32.const 1
+ return
+ }
+}
+
+(;; STDERR ;;;
+000002f: warning: invalid function index: 2
+;;; STDERR ;;)
+(;; STDOUT ;;;
+
+bad-code-metadata-function-index.wasm: file format wasm 0x1
+
+Section Details:
+
+Type[1]:
+ - type[0] () -> i32
+Function[1]:
+ - func[0] sig=0
+Memory[1]:
+ - memory[0] pages: initial=0
+Custom:
+ - name: "metadata.code.test"
+Code[1]:
+ - func[0] size=5
+
+Code Disassembly:
+
+000037 func[0]:
+ 000038: 41 01 | i32.const 1
+ 00003a: 0f | return
+ 00003b: 0b | end
+;;; STDOUT ;;)
diff --git a/test/binary/bad-code-metadata-function-out-of-order.txt b/test/binary/bad-code-metadata-function-out-of-order.txt
new file mode 100644
index 00000000..b0c05890
--- /dev/null
+++ b/test/binary/bad-code-metadata-function-out-of-order.txt
@@ -0,0 +1,82 @@
+;;; TOOL: run-objdump-gen-wasm
+;;; ARGS1: -x
+magic
+version
+section(TYPE) {
+ count[1]
+ function params[0] results[1] i32
+}
+section(FUNCTION) {
+ count[2]
+ type[0]
+ type[0]
+}
+section(MEMORY) {
+ count[1]
+ has_max[0]
+ initial[0]
+}
+section("metadata.code.test") {
+ function_count[2]
+ function_index[1]
+ ann_count[1]
+ ann_offset[1]
+ ann_data_size[1]
+ ann_data[1]
+ function_index[0]
+ ann_count[1]
+ ann_offset[1]
+ ann_data_size[1]
+ ann_data[1]
+}
+
+section(CODE) {
+ count[2]
+ func {
+ local_decls[0]
+ i32.const 1
+ return
+ }
+ func {
+ local_decls[0]
+ i32.const 1
+ return
+ }
+}
+
+(;; STDERR ;;;
+0000035: warning: function index out of order: 0
+;;; STDERR ;;)
+(;; STDOUT ;;;
+
+bad-code-metadata-function-out-of-order.wasm: file format wasm 0x1
+
+Section Details:
+
+Type[1]:
+ - type[0] () -> i32
+Function[2]:
+ - func[0] sig=0
+ - func[1] sig=0
+Memory[1]:
+ - memory[0] pages: initial=0
+Custom:
+ - name: "metadata.code.test"
+ - func[1]:
+ - meta[1]:
+ - 0000000: 01 .
+Code[2]:
+ - func[0] size=5
+ - func[1] size=5
+
+Code Disassembly:
+
+00003d func[0]:
+ 00003e: 41 01 | i32.const 1
+ 000040: 0f | return
+ 000041: 0b | end
+000043 func[1]:
+ 000044: 41 01 | i32.const 1
+ 000046: 0f | return
+ 000047: 0b | end
+;;; STDOUT ;;)
diff --git a/test/binary/bad-code-metadata-instance-count.txt b/test/binary/bad-code-metadata-instance-count.txt
new file mode 100644
index 00000000..c8be5895
--- /dev/null
+++ b/test/binary/bad-code-metadata-instance-count.txt
@@ -0,0 +1,65 @@
+;;; TOOL: run-objdump-gen-wasm
+;;; ARGS1: -x
+magic
+version
+section(TYPE) {
+ count[1]
+ function params[0] results[1] i32
+}
+section(FUNCTION) {
+ count[1]
+ type[0]
+}
+section(MEMORY) {
+ count[1]
+ has_max[0]
+ initial[0]
+}
+section("metadata.code.test") {
+ function_count[1]
+ function_index[0]
+ ann_count[2]
+ ann_offset[1]
+ ann_data_size[1]
+ ann_data[1]
+}
+
+section(CODE) {
+ count[1]
+ func {
+ local_decls[0]
+ i32.const 1
+ return
+ }
+}
+
+(;; STDERR ;;;
+0000033: warning: unable to read u32 leb128: code offset
+;;; STDERR ;;)
+(;; STDOUT ;;;
+
+bad-code-metadata-instance-count.wasm: file format wasm 0x1
+
+Section Details:
+
+Type[1]:
+ - type[0] () -> i32
+Function[1]:
+ - func[0] sig=0
+Memory[1]:
+ - memory[0] pages: initial=0
+Custom:
+ - name: "metadata.code.test"
+ - func[0]:
+ - meta[1]:
+ - 0000000: 01 .
+Code[1]:
+ - func[0] size=5
+
+Code Disassembly:
+
+000037 func[0]:
+ 000038: 41 01 | i32.const 1
+ 00003a: 0f | return
+ 00003b: 0b | end
+;;; STDOUT ;;)
diff --git a/test/binary/bad-code-metadata-instance-duplicate.txt b/test/binary/bad-code-metadata-instance-duplicate.txt
new file mode 100644
index 00000000..95723713
--- /dev/null
+++ b/test/binary/bad-code-metadata-instance-duplicate.txt
@@ -0,0 +1,68 @@
+;;; TOOL: run-objdump-gen-wasm
+;;; ARGS1: -x
+magic
+version
+section(TYPE) {
+ count[1]
+ function params[0] results[1] i32
+}
+section(FUNCTION) {
+ count[1]
+ type[0]
+}
+section(MEMORY) {
+ count[1]
+ has_max[0]
+ initial[0]
+}
+section("metadata.code.test") {
+ function_count[1]
+ function_index[0]
+ ann_count[2]
+ ann_offset[1]
+ ann_data_size[1]
+ ann_data[1]
+ ann_offset[1]
+ ann_data_size[1]
+ ann_data[1]
+}
+
+section(CODE) {
+ count[1]
+ func {
+ local_decls[0]
+ i32.const 1
+ return
+ }
+}
+
+(;; STDERR ;;;
+0000034: warning: duplicate code offset: 1
+;;; STDERR ;;)
+(;; STDOUT ;;;
+
+bad-code-metadata-instance-duplicate.wasm: file format wasm 0x1
+
+Section Details:
+
+Type[1]:
+ - type[0] () -> i32
+Function[1]:
+ - func[0] sig=0
+Memory[1]:
+ - memory[0] pages: initial=0
+Custom:
+ - name: "metadata.code.test"
+ - func[0]:
+ - meta[1]:
+ - 0000000: 01 .
+Code[1]:
+ - func[0] size=5
+
+Code Disassembly:
+
+00003a func[0]:
+ 00003b: 41 01 | i32.const 1
+ 00003d: 0f | return
+ 00003e: 0b | end
+;;; STDOUT ;;)
diff --git a/test/binary/bad-code-metadata-instance-out-of-order.txt b/test/binary/bad-code-metadata-instance-out-of-order.txt
new file mode 100644
index 00000000..4efb85d8
--- /dev/null
+++ b/test/binary/bad-code-metadata-instance-out-of-order.txt
@@ -0,0 +1,68 @@
+;;; TOOL: run-objdump-gen-wasm
+;;; ARGS1: -x
+magic
+version
+section(TYPE) {
+ count[1]
+ function params[0] results[1] i32
+}
+section(FUNCTION) {
+ count[1]
+ type[0]
+}
+section(MEMORY) {
+ count[1]
+ has_max[0]
+ initial[0]
+}
+section("metadata.code.test") {
+ function_count[1]
+ function_index[0]
+ ann_count[2]
+ ann_offset[3]
+ ann_data_size[1]
+ ann_data[1]
+ ann_offset[1]
+ ann_data_size[1]
+ ann_data[1]
+}
+
+section(CODE) {
+ count[1]
+ func {
+ local_decls[0]
+ i32.const 1
+ return
+ }
+}
+
+(;; STDERR ;;;
+0000034: warning: code offset out of order: 1
+;;; STDERR ;;)
+(;; STDOUT ;;;
+
+bad-code-metadata-instance-out-of-order.wasm: file format wasm 0x1
+
+Section Details:
+
+Type[1]:
+ - type[0] () -> i32
+Function[1]:
+ - func[0] sig=0
+Memory[1]:
+ - memory[0] pages: initial=0
+Custom:
+ - name: "metadata.code.test"
+ - func[0]:
+ - meta[3]:
+ - 0000000: 01 .
+Code[1]:
+ - func[0] size=5
+
+Code Disassembly:
+
+00003a func[0]:
+ 00003b: 41 01 | i32.const 1
+ 00003d: 0f | return
+ 00003e: 0b | end
+;;; STDOUT ;;)
diff --git a/test/binary/code-metadata-section.txt b/test/binary/code-metadata-section.txt
new file mode 100644
index 00000000..9625f602
--- /dev/null
+++ b/test/binary/code-metadata-section.txt
@@ -0,0 +1,61 @@
+;;; TOOL: run-objdump-gen-wasm
+;;; ARGS: -x
+magic
+version
+section(TYPE) {
+ count[1]
+ function params[0] results[1] i32
+}
+section(FUNCTION) {
+ count[1]
+ type[0]
+}
+section(MEMORY) {
+ count[1]
+ has_max[0]
+ initial[0]
+}
+section("metadata.code.test") {
+ function_count[1]
+ function_index[0]
+ ann_count[1]
+ ann_offset[1]
+ ann_data_size[1]
+ ann_data[1]
+}
+
+section(CODE) {
+ count[1]
+ func {
+ local_decls[0]
+ i32.const 1
+ return
+ }
+}
+(;; STDOUT ;;;
+
+code-metadata-section.wasm: file format wasm 0x1
+
+Section Details:
+
+Type[1]:
+ - type[0] () -> i32
+Function[1]:
+ - func[0] sig=0
+Memory[1]:
+ - memory[0] pages: initial=0
+Custom:
+ - name: "metadata.code.test"
+ - func[0]:
+ - meta[1]:
+ - 0000000: 01 .
+Code[1]:
+ - func[0] size=5
+
+Code Disassembly:
+
+000037 func[0]:
+ 000038: 41 01 | i32.const 1
+ 00003a: 0f | return
+ 00003b: 0b | end
+;;; STDOUT ;;)
diff --git a/test/decompile/code-metadata.txt b/test/decompile/code-metadata.txt
new file mode 100644
index 00000000..1efd9d65
--- /dev/null
+++ b/test/decompile/code-metadata.txt
@@ -0,0 +1,17 @@
+;;; TOOL: run-wasm-decompile
+
+(module
+ (func $f (param i32) (result i32)
+ i32.const 1234
+ local.get 0
+ (@metadata.code.test "aa\01a") i32.add
+ return))
+
+(;; STDOUT ;;;
+function f_a(a:int):int {
+ let t0 = a;
+ // @metadata.code.test "aa\01a";
+ return 1234 + t0;
+}
+
+;;; STDOUT ;;)
diff --git a/test/dump/code-metadata.txt b/test/dump/code-metadata.txt
new file mode 100644
index 00000000..1b2254d7
--- /dev/null
+++ b/test/dump/code-metadata.txt
@@ -0,0 +1,43 @@
+;;; TOOL: run-objdump
+;;; ARGS0: --enable-annotations --enable-code-metadata
+;;; ARGS1: --headers --details
+(module
+ (func $f (param i32) (result i32)
+ i32.const 1234
+ local.get 0
+ (@metadata.code.test "aa\01a") i32.add
+ return))
+(;; STDOUT ;;;
+
+code-metadata.wasm: file format wasm 0x1
+
+Sections:
+
+ Type start=0x0000000a end=0x00000010 (size=0x00000006) count: 1
+ Function start=0x00000012 end=0x00000014 (size=0x00000002) count: 1
+ Custom start=0x00000016 end=0x00000032 (size=0x0000001c) "metadata.code.test"
+ Code start=0x00000034 end=0x0000003f (size=0x0000000b) count: 1
+
+Section Details:
+
+Type[1]:
+ - type[0] (i32) -> i32
+Function[1]:
+ - func[0] sig=0
+Custom:
+ - name: "metadata.code.test"
+ - func[0]:
+ - meta[6]:
+ - 0000000: 6161 0161 aa.a
+Code[1]:
+ - func[0] size=9
+
+Code Disassembly:
+
+000036 func[0]:
+ 000037: 41 d2 09 | i32.const 1234
+ 00003a: 20 00 | local.get 0
+ 00003c: 6a | i32.add
+ 00003d: 0f | return
+ 00003e: 0b | end
+;;; STDOUT ;;)
diff --git a/test/help/spectest-interp.txt b/test/help/spectest-interp.txt
index 225a3938..bbb68061 100644
--- a/test/help/spectest-interp.txt
+++ b/test/help/spectest-interp.txt
@@ -25,6 +25,7 @@ options:
--disable-bulk-memory Disable Bulk-memory operations
--disable-reference-types Disable Reference types (externref)
--enable-annotations Enable Custom annotation syntax
+ --enable-code-metadata Enable Code metadata
--enable-gc Enable Garbage collection
--enable-memory64 Enable 64-bit memory
--enable-multi-memory Enable Multi-memory
diff --git a/test/help/wasm-interp.txt b/test/help/wasm-interp.txt
index efbe0409..f6dc26b7 100644
--- a/test/help/wasm-interp.txt
+++ b/test/help/wasm-interp.txt
@@ -36,6 +36,7 @@ options:
--disable-bulk-memory Disable Bulk-memory operations
--disable-reference-types Disable Reference types (externref)
--enable-annotations Enable Custom annotation syntax
+ --enable-code-metadata Enable Code metadata
--enable-gc Enable Garbage collection
--enable-memory64 Enable 64-bit memory
--enable-multi-memory Enable Multi-memory
diff --git a/test/help/wasm-opcodecnt.txt b/test/help/wasm-opcodecnt.txt
index eff83b99..2a0606b9 100644
--- a/test/help/wasm-opcodecnt.txt
+++ b/test/help/wasm-opcodecnt.txt
@@ -26,6 +26,7 @@ options:
--disable-bulk-memory Disable Bulk-memory operations
--disable-reference-types Disable Reference types (externref)
--enable-annotations Enable Custom annotation syntax
+ --enable-code-metadata Enable Code metadata
--enable-gc Enable Garbage collection
--enable-memory64 Enable 64-bit memory
--enable-multi-memory Enable Multi-memory
diff --git a/test/help/wasm-validate.txt b/test/help/wasm-validate.txt
index 3f76d737..4ceeb329 100644
--- a/test/help/wasm-validate.txt
+++ b/test/help/wasm-validate.txt
@@ -25,6 +25,7 @@ options:
--disable-bulk-memory Disable Bulk-memory operations
--disable-reference-types Disable Reference types (externref)
--enable-annotations Enable Custom annotation syntax
+ --enable-code-metadata Enable Code metadata
--enable-gc Enable Garbage collection
--enable-memory64 Enable 64-bit memory
--enable-multi-memory Enable Multi-memory
diff --git a/test/help/wasm2wat.txt b/test/help/wasm2wat.txt
index 29155532..a2b78d31 100644
--- a/test/help/wasm2wat.txt
+++ b/test/help/wasm2wat.txt
@@ -31,6 +31,7 @@ options:
--disable-bulk-memory Disable Bulk-memory operations
--disable-reference-types Disable Reference types (externref)
--enable-annotations Enable Custom annotation syntax
+ --enable-code-metadata Enable Code metadata
--enable-gc Enable Garbage collection
--enable-memory64 Enable 64-bit memory
--enable-multi-memory Enable Multi-memory
diff --git a/test/help/wast2json.txt b/test/help/wast2json.txt
index 90278883..95fa3a3e 100644
--- a/test/help/wast2json.txt
+++ b/test/help/wast2json.txt
@@ -28,6 +28,7 @@ options:
--disable-bulk-memory Disable Bulk-memory operations
--disable-reference-types Disable Reference types (externref)
--enable-annotations Enable Custom annotation syntax
+ --enable-code-metadata Enable Code metadata
--enable-gc Enable Garbage collection
--enable-memory64 Enable 64-bit memory
--enable-multi-memory Enable Multi-memory
diff --git a/test/help/wat-desugar.txt b/test/help/wat-desugar.txt
index 38f6356c..268fd600 100644
--- a/test/help/wat-desugar.txt
+++ b/test/help/wat-desugar.txt
@@ -35,6 +35,7 @@ options:
--disable-bulk-memory Disable Bulk-memory operations
--disable-reference-types Disable Reference types (externref)
--enable-annotations Enable Custom annotation syntax
+ --enable-code-metadata Enable Code metadata
--enable-gc Enable Garbage collection
--enable-memory64 Enable 64-bit memory
--enable-multi-memory Enable Multi-memory
diff --git a/test/help/wat2wasm.txt b/test/help/wat2wasm.txt
index 71ff482c..2bba6093 100644
--- a/test/help/wat2wasm.txt
+++ b/test/help/wat2wasm.txt
@@ -35,6 +35,7 @@ options:
--disable-bulk-memory Disable Bulk-memory operations
--disable-reference-types Disable Reference types (externref)
--enable-annotations Enable Custom annotation syntax
+ --enable-code-metadata Enable Code metadata
--enable-gc Enable Garbage collection
--enable-memory64 Enable 64-bit memory
--enable-multi-memory Enable Multi-memory
diff --git a/test/roundtrip/code-metadata.txt b/test/roundtrip/code-metadata.txt
new file mode 100644
index 00000000..1526f212
--- /dev/null
+++ b/test/roundtrip/code-metadata.txt
@@ -0,0 +1,8 @@
+;;; TOOL: run-roundtrip
+;;; ARGS: --enable-annotations --enable-code-metadata
+(module
+ (func $f (param i32) (result i32)
+ i32.const 1234
+ local.get 0
+ (@metadata.code.test "aa\01a") i32.add
+ return))
diff --git a/test/run-roundtrip.py b/test/run-roundtrip.py
index ee765537..bf7b23cb 100755
--- a/test/run-roundtrip.py
+++ b/test/run-roundtrip.py
@@ -124,6 +124,8 @@ def main(args):
parser.add_argument('--disable-reference-types', action='store_true')
parser.add_argument('--enable-memory64', action='store_true')
parser.add_argument('--enable-multi-memory', action='store_true')
+ parser.add_argument('--enable-annotations', action='store_true')
+ parser.add_argument('--enable-code-metadata', action='store_true')
parser.add_argument('--inline-exports', action='store_true')
parser.add_argument('--inline-imports', action='store_true')
parser.add_argument('--reloc', action='store_true')
@@ -146,6 +148,8 @@ def main(args):
'--disable-reference-types': options.disable_reference_types,
'--enable-memory64': options.enable_memory64,
'--enable-multi-memory': options.enable_multi_memory,
+ '--enable-annotations': options.enable_annotations,
+ '--enable-code-metadata': options.enable_code_metadata,
'--reloc': options.reloc,
'--no-check': options.no_check,
})
@@ -166,6 +170,8 @@ def main(args):
'--enable-threads': options.enable_threads,
'--enable-memory64': options.enable_memory64,
'--enable-multi-memory': options.enable_multi_memory,
+ '--enable-annotations': options.enable_annotations,
+ '--enable-code-metadata': options.enable_code_metadata,
'--inline-exports': options.inline_exports,
'--inline-imports': options.inline_imports,
'--no-debug-names': not options.debug_names,