summaryrefslogtreecommitdiffstats
path: root/third_party/wasm2c/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/wasm2c/src
parentInitial commit. (diff)
downloadfirefox-upstream/124.0.1.tar.xz
firefox-upstream/124.0.1.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/wasm2c/src')
-rw-r--r--third_party/wasm2c/src/apply-names.cc581
-rw-r--r--third_party/wasm2c/src/binary-reader-ir.cc1775
-rw-r--r--third_party/wasm2c/src/binary-reader-logging.cc971
-rw-r--r--third_party/wasm2c/src/binary-reader-objdump.cc2408
-rw-r--r--third_party/wasm2c/src/binary-reader-opcnt.cc314
-rw-r--r--third_party/wasm2c/src/binary-reader.cc3027
-rw-r--r--third_party/wasm2c/src/binary-writer-spec.cc672
-rw-r--r--third_party/wasm2c/src/binary-writer.cc1798
-rw-r--r--third_party/wasm2c/src/binary.cc68
-rw-r--r--third_party/wasm2c/src/binding-hash.cc91
-rw-r--r--third_party/wasm2c/src/c-writer.cc5295
-rw-r--r--third_party/wasm2c/src/color.cc78
-rw-r--r--third_party/wasm2c/src/common.cc148
-rw-r--r--third_party/wasm2c/src/config.cc162
-rw-r--r--third_party/wasm2c/src/config.h.in332
-rw-r--r--third_party/wasm2c/src/decompiler.cc857
-rw-r--r--third_party/wasm2c/src/emscripten-exports.txt72
-rw-r--r--third_party/wasm2c/src/emscripten-helpers.cc403
-rw-r--r--third_party/wasm2c/src/error-formatter.cc127
-rw-r--r--third_party/wasm2c/src/expr-visitor.cc472
-rw-r--r--third_party/wasm2c/src/feature.cc56
-rw-r--r--third_party/wasm2c/src/filenames.cc57
-rw-r--r--third_party/wasm2c/src/generate-names.cc433
-rw-r--r--third_party/wasm2c/src/ir-util.cc271
-rw-r--r--third_party/wasm2c/src/ir.cc701
-rw-r--r--third_party/wasm2c/src/leb128.cc361
-rw-r--r--third_party/wasm2c/src/lexer-keywords.txt609
-rw-r--r--third_party/wasm2c/src/lexer-source-line-finder.cc152
-rw-r--r--third_party/wasm2c/src/lexer-source.cc67
-rw-r--r--third_party/wasm2c/src/literal.cc829
-rw-r--r--third_party/wasm2c/src/opcode-code-table.c41
-rw-r--r--third_party/wasm2c/src/opcode.cc429
-rw-r--r--third_party/wasm2c/src/option-parser.cc351
-rw-r--r--third_party/wasm2c/src/prebuilt/.clang-format2
-rw-r--r--third_party/wasm2c/src/prebuilt/lexer-keywords.cc1796
-rw-r--r--third_party/wasm2c/src/prebuilt/wasm2c_header_bottom.cc7
-rw-r--r--third_party/wasm2c/src/prebuilt/wasm2c_header_top.cc37
-rw-r--r--third_party/wasm2c/src/prebuilt/wasm2c_simd_source_declarations.cc224
-rw-r--r--third_party/wasm2c/src/prebuilt/wasm2c_source_declarations.cc1110
-rw-r--r--third_party/wasm2c/src/prebuilt/wasm2c_source_includes.cc33
-rw-r--r--third_party/wasm2c/src/resolve-names.cc649
-rw-r--r--third_party/wasm2c/src/sha256.cc48
-rw-r--r--third_party/wasm2c/src/shared-validator.cc1203
-rw-r--r--third_party/wasm2c/src/stream.cc330
-rw-r--r--third_party/wasm2c/src/test-binary-reader.cc75
-rw-r--r--third_party/wasm2c/src/test-circular-array.cc284
-rw-r--r--third_party/wasm2c/src/test-filenames.cc61
-rw-r--r--third_party/wasm2c/src/test-hexfloat.cc264
-rw-r--r--third_party/wasm2c/src/test-interp.cc723
-rw-r--r--third_party/wasm2c/src/test-intrusive-list.cc583
-rw-r--r--third_party/wasm2c/src/test-literal.cc838
-rw-r--r--third_party/wasm2c/src/test-option-parser.cc180
-rw-r--r--third_party/wasm2c/src/test-utf8.cc167
-rw-r--r--third_party/wasm2c/src/test-wast-parser.cc88
-rw-r--r--third_party/wasm2c/src/token.cc99
-rw-r--r--third_party/wasm2c/src/tools/wasm2c.cc216
-rw-r--r--third_party/wasm2c/src/tracing.cc71
-rw-r--r--third_party/wasm2c/src/type-checker.cc989
-rw-r--r--third_party/wasm2c/src/utf8.cc106
-rw-r--r--third_party/wasm2c/src/validator.cc1086
-rw-r--r--third_party/wasm2c/src/wabt.post.js387
-rw-r--r--third_party/wasm2c/src/wast-lexer.cc626
-rw-r--r--third_party/wasm2c/src/wast-parser.cc3616
-rw-r--r--third_party/wasm2c/src/wat-writer.cc1789
64 files changed, 41695 insertions, 0 deletions
diff --git a/third_party/wasm2c/src/apply-names.cc b/third_party/wasm2c/src/apply-names.cc
new file mode 100644
index 0000000000..506ab1fe71
--- /dev/null
+++ b/third_party/wasm2c/src/apply-names.cc
@@ -0,0 +1,581 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/apply-names.h"
+
+#include <cassert>
+#include <cstdio>
+#include <string_view>
+#include <vector>
+
+#include "wabt/cast.h"
+#include "wabt/expr-visitor.h"
+#include "wabt/ir.h"
+
+namespace wabt {
+
+namespace {
+
+class NameApplier : public ExprVisitor::DelegateNop {
+ public:
+ NameApplier();
+
+ Result VisitModule(Module* module);
+
+ // Implementation of ExprVisitor::DelegateNop.
+ Result BeginBlockExpr(BlockExpr*) override;
+ Result EndBlockExpr(BlockExpr*) override;
+ Result OnBrExpr(BrExpr*) override;
+ Result OnBrIfExpr(BrIfExpr*) override;
+ Result OnBrTableExpr(BrTableExpr*) override;
+ Result OnCallExpr(CallExpr*) override;
+ Result OnRefFuncExpr(RefFuncExpr*) override;
+ Result OnCallIndirectExpr(CallIndirectExpr*) override;
+ Result OnReturnCallExpr(ReturnCallExpr*) override;
+ Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr*) override;
+ Result OnGlobalGetExpr(GlobalGetExpr*) override;
+ Result OnGlobalSetExpr(GlobalSetExpr*) override;
+ Result BeginIfExpr(IfExpr*) override;
+ Result EndIfExpr(IfExpr*) override;
+ Result OnLoadExpr(LoadExpr*) override;
+ Result OnLocalGetExpr(LocalGetExpr*) override;
+ Result OnLocalSetExpr(LocalSetExpr*) override;
+ Result OnLocalTeeExpr(LocalTeeExpr*) override;
+ Result BeginLoopExpr(LoopExpr*) override;
+ Result EndLoopExpr(LoopExpr*) override;
+ Result OnMemoryCopyExpr(MemoryCopyExpr*) override;
+ Result OnDataDropExpr(DataDropExpr*) override;
+ Result OnMemoryFillExpr(MemoryFillExpr*) override;
+ Result OnMemoryGrowExpr(MemoryGrowExpr*) override;
+ Result OnMemoryInitExpr(MemoryInitExpr*) override;
+ Result OnMemorySizeExpr(MemorySizeExpr*) override;
+ Result OnElemDropExpr(ElemDropExpr*) override;
+ Result OnTableCopyExpr(TableCopyExpr*) override;
+ Result OnTableInitExpr(TableInitExpr*) override;
+ Result OnTableGetExpr(TableGetExpr*) override;
+ Result OnTableSetExpr(TableSetExpr*) override;
+ Result OnTableGrowExpr(TableGrowExpr*) override;
+ Result OnTableSizeExpr(TableSizeExpr*) override;
+ Result OnTableFillExpr(TableFillExpr*) override;
+ Result OnStoreExpr(StoreExpr*) override;
+ Result BeginTryExpr(TryExpr*) override;
+ Result EndTryExpr(TryExpr*) override;
+ Result OnCatchExpr(TryExpr*, Catch*) override;
+ Result OnDelegateExpr(TryExpr*) override;
+ Result OnThrowExpr(ThrowExpr*) override;
+ Result OnRethrowExpr(RethrowExpr*) override;
+ Result OnSimdLoadLaneExpr(SimdLoadLaneExpr*) override;
+ Result OnSimdStoreLaneExpr(SimdStoreLaneExpr*) override;
+
+ private:
+ void PushLabel(const std::string& label);
+ void PopLabel();
+ std::string_view FindLabelByVar(Var* var);
+ void UseNameForVar(std::string_view name, Var* var);
+ Result UseNameForFuncTypeVar(Var* var);
+ Result UseNameForFuncVar(Var* var);
+ Result UseNameForGlobalVar(Var* var);
+ Result UseNameForTableVar(Var* var);
+ Result UseNameForMemoryVar(Var* var);
+ Result UseNameForTagVar(Var* var);
+ Result UseNameForDataSegmentVar(Var* var);
+ Result UseNameForElemSegmentVar(Var* var);
+ Result UseNameForParamAndLocalVar(Func* func, Var* var);
+ Result VisitFunc(Index func_index, Func* func);
+ Result VisitGlobal(Global* global);
+ Result VisitTag(Tag* tag);
+ Result VisitExport(Index export_index, Export* export_);
+ Result VisitElemSegment(Index elem_segment_index, ElemSegment* segment);
+ Result VisitDataSegment(Index data_segment_index, DataSegment* segment);
+ Result VisitStart(Var* start_var);
+
+ Module* module_ = nullptr;
+ Func* current_func_ = nullptr;
+ ExprVisitor visitor_;
+ std::vector<std::string> param_and_local_index_to_name_;
+ std::vector<std::string> labels_;
+};
+
+NameApplier::NameApplier() : visitor_(this) {}
+
+void NameApplier::PushLabel(const std::string& label) {
+ labels_.push_back(label);
+}
+
+void NameApplier::PopLabel() {
+ labels_.pop_back();
+}
+
+std::string_view NameApplier::FindLabelByVar(Var* var) {
+ if (var->is_name()) {
+ for (int i = labels_.size() - 1; i >= 0; --i) {
+ const std::string& label = labels_[i];
+ if (label == var->name()) {
+ return label;
+ }
+ }
+ return std::string_view();
+ } else {
+ if (var->index() >= labels_.size()) {
+ return std::string_view();
+ }
+ return labels_[labels_.size() - 1 - var->index()];
+ }
+}
+
+void NameApplier::UseNameForVar(std::string_view name, Var* var) {
+ if (var->is_name()) {
+ assert(name == var->name());
+ return;
+ }
+
+ if (!name.empty()) {
+ var->set_name(name);
+ }
+}
+
+Result NameApplier::UseNameForFuncTypeVar(Var* var) {
+ FuncType* func_type = module_->GetFuncType(*var);
+ if (!func_type) {
+ return Result::Error;
+ }
+ UseNameForVar(func_type->name, var);
+ return Result::Ok;
+}
+
+Result NameApplier::UseNameForFuncVar(Var* var) {
+ Func* func = module_->GetFunc(*var);
+ if (!func) {
+ return Result::Error;
+ }
+ UseNameForVar(func->name, var);
+ return Result::Ok;
+}
+
+Result NameApplier::UseNameForGlobalVar(Var* var) {
+ Global* global = module_->GetGlobal(*var);
+ if (!global) {
+ return Result::Error;
+ }
+ UseNameForVar(global->name, var);
+ return Result::Ok;
+}
+
+Result NameApplier::UseNameForTableVar(Var* var) {
+ Table* table = module_->GetTable(*var);
+ if (!table) {
+ return Result::Error;
+ }
+ UseNameForVar(table->name, var);
+ return Result::Ok;
+}
+
+Result NameApplier::UseNameForMemoryVar(Var* var) {
+ Memory* memory = module_->GetMemory(*var);
+ if (!memory) {
+ return Result::Error;
+ }
+ UseNameForVar(memory->name, var);
+ return Result::Ok;
+}
+
+Result NameApplier::UseNameForTagVar(Var* var) {
+ Tag* tag = module_->GetTag(*var);
+ if (!tag) {
+ return Result::Error;
+ }
+ UseNameForVar(tag->name, var);
+ return Result::Ok;
+}
+
+Result NameApplier::UseNameForDataSegmentVar(Var* var) {
+ DataSegment* data_segment = module_->GetDataSegment(*var);
+ if (!data_segment) {
+ return Result::Error;
+ }
+ UseNameForVar(data_segment->name, var);
+ return Result::Ok;
+}
+
+Result NameApplier::UseNameForElemSegmentVar(Var* var) {
+ ElemSegment* elem_segment = module_->GetElemSegment(*var);
+ if (!elem_segment) {
+ return Result::Error;
+ }
+ UseNameForVar(elem_segment->name, var);
+ return Result::Ok;
+}
+
+Result NameApplier::UseNameForParamAndLocalVar(Func* func, Var* var) {
+ Index local_index = func->GetLocalIndex(*var);
+ if (local_index >= func->GetNumParamsAndLocals()) {
+ return Result::Error;
+ }
+
+ std::string name = param_and_local_index_to_name_[local_index];
+ if (var->is_name()) {
+ assert(name == var->name());
+ return Result::Ok;
+ }
+
+ if (!name.empty()) {
+ var->set_name(name);
+ }
+ return Result::Ok;
+}
+
+Result NameApplier::BeginBlockExpr(BlockExpr* expr) {
+ PushLabel(expr->block.label);
+ return Result::Ok;
+}
+
+Result NameApplier::EndBlockExpr(BlockExpr* expr) {
+ PopLabel();
+ return Result::Ok;
+}
+
+Result NameApplier::BeginLoopExpr(LoopExpr* expr) {
+ PushLabel(expr->block.label);
+ return Result::Ok;
+}
+
+Result NameApplier::EndLoopExpr(LoopExpr* expr) {
+ PopLabel();
+ return Result::Ok;
+}
+
+Result NameApplier::OnDataDropExpr(DataDropExpr* expr) {
+ CHECK_RESULT(UseNameForDataSegmentVar(&expr->var));
+ return Result::Ok;
+}
+
+Result NameApplier::OnMemoryCopyExpr(MemoryCopyExpr* expr) {
+ CHECK_RESULT(UseNameForMemoryVar(&expr->srcmemidx));
+ CHECK_RESULT(UseNameForMemoryVar(&expr->destmemidx));
+ return Result::Ok;
+}
+
+Result NameApplier::OnMemoryFillExpr(MemoryFillExpr* expr) {
+ CHECK_RESULT(UseNameForMemoryVar(&expr->memidx));
+ return Result::Ok;
+}
+
+Result NameApplier::OnMemoryGrowExpr(MemoryGrowExpr* expr) {
+ CHECK_RESULT(UseNameForMemoryVar(&expr->memidx));
+ return Result::Ok;
+}
+
+Result NameApplier::OnMemoryInitExpr(MemoryInitExpr* expr) {
+ CHECK_RESULT(UseNameForDataSegmentVar(&expr->var));
+ CHECK_RESULT(UseNameForMemoryVar(&expr->memidx));
+ return Result::Ok;
+}
+
+Result NameApplier::OnMemorySizeExpr(MemorySizeExpr* expr) {
+ CHECK_RESULT(UseNameForMemoryVar(&expr->memidx));
+ return Result::Ok;
+}
+
+Result NameApplier::OnElemDropExpr(ElemDropExpr* expr) {
+ CHECK_RESULT(UseNameForElemSegmentVar(&expr->var));
+ return Result::Ok;
+}
+
+Result NameApplier::OnTableCopyExpr(TableCopyExpr* expr) {
+ CHECK_RESULT(UseNameForTableVar(&expr->dst_table));
+ CHECK_RESULT(UseNameForTableVar(&expr->src_table));
+ return Result::Ok;
+}
+
+Result NameApplier::OnTableInitExpr(TableInitExpr* expr) {
+ CHECK_RESULT(UseNameForElemSegmentVar(&expr->segment_index));
+ CHECK_RESULT(UseNameForTableVar(&expr->table_index));
+ return Result::Ok;
+}
+
+Result NameApplier::OnTableGetExpr(TableGetExpr* expr) {
+ CHECK_RESULT(UseNameForTableVar(&expr->var));
+ return Result::Ok;
+}
+
+Result NameApplier::OnTableSetExpr(TableSetExpr* expr) {
+ CHECK_RESULT(UseNameForTableVar(&expr->var));
+ return Result::Ok;
+}
+
+Result NameApplier::OnTableGrowExpr(TableGrowExpr* expr) {
+ CHECK_RESULT(UseNameForTableVar(&expr->var));
+ return Result::Ok;
+}
+
+Result NameApplier::OnTableSizeExpr(TableSizeExpr* expr) {
+ CHECK_RESULT(UseNameForTableVar(&expr->var));
+ return Result::Ok;
+}
+
+Result NameApplier::OnTableFillExpr(TableFillExpr* expr) {
+ CHECK_RESULT(UseNameForTableVar(&expr->var));
+ return Result::Ok;
+}
+
+Result NameApplier::OnStoreExpr(StoreExpr* expr) {
+ CHECK_RESULT(UseNameForMemoryVar(&expr->memidx));
+ return Result::Ok;
+}
+
+Result NameApplier::OnBrExpr(BrExpr* expr) {
+ std::string_view label = FindLabelByVar(&expr->var);
+ UseNameForVar(label, &expr->var);
+ return Result::Ok;
+}
+
+Result NameApplier::OnBrIfExpr(BrIfExpr* expr) {
+ std::string_view label = FindLabelByVar(&expr->var);
+ UseNameForVar(label, &expr->var);
+ return Result::Ok;
+}
+
+Result NameApplier::OnBrTableExpr(BrTableExpr* expr) {
+ for (Var& target : expr->targets) {
+ std::string_view label = FindLabelByVar(&target);
+ UseNameForVar(label, &target);
+ }
+
+ std::string_view label = FindLabelByVar(&expr->default_target);
+ UseNameForVar(label, &expr->default_target);
+ return Result::Ok;
+}
+
+Result NameApplier::BeginTryExpr(TryExpr* expr) {
+ PushLabel(expr->block.label);
+ return Result::Ok;
+}
+
+Result NameApplier::EndTryExpr(TryExpr*) {
+ PopLabel();
+ return Result::Ok;
+}
+
+Result NameApplier::OnCatchExpr(TryExpr*, Catch* expr) {
+ if (!expr->IsCatchAll()) {
+ CHECK_RESULT(UseNameForTagVar(&expr->var));
+ }
+ return Result::Ok;
+}
+
+Result NameApplier::OnDelegateExpr(TryExpr* expr) {
+ PopLabel();
+ std::string_view label = FindLabelByVar(&expr->delegate_target);
+ UseNameForVar(label, &expr->delegate_target);
+ return Result::Ok;
+}
+
+Result NameApplier::OnThrowExpr(ThrowExpr* expr) {
+ CHECK_RESULT(UseNameForTagVar(&expr->var));
+ return Result::Ok;
+}
+
+Result NameApplier::OnRethrowExpr(RethrowExpr* expr) {
+ std::string_view label = FindLabelByVar(&expr->var);
+ UseNameForVar(label, &expr->var);
+ return Result::Ok;
+}
+
+Result NameApplier::OnCallExpr(CallExpr* expr) {
+ CHECK_RESULT(UseNameForFuncVar(&expr->var));
+ return Result::Ok;
+}
+
+Result NameApplier::OnRefFuncExpr(RefFuncExpr* expr) {
+ CHECK_RESULT(UseNameForFuncVar(&expr->var));
+ return Result::Ok;
+}
+
+Result NameApplier::OnCallIndirectExpr(CallIndirectExpr* expr) {
+ if (expr->decl.has_func_type) {
+ CHECK_RESULT(UseNameForFuncTypeVar(&expr->decl.type_var));
+ }
+ CHECK_RESULT(UseNameForTableVar(&expr->table));
+ return Result::Ok;
+}
+
+Result NameApplier::OnReturnCallExpr(ReturnCallExpr* expr) {
+ CHECK_RESULT(UseNameForFuncVar(&expr->var));
+ return Result::Ok;
+}
+
+Result NameApplier::OnReturnCallIndirectExpr(ReturnCallIndirectExpr* expr) {
+ if (expr->decl.has_func_type) {
+ CHECK_RESULT(UseNameForFuncTypeVar(&expr->decl.type_var));
+ }
+ CHECK_RESULT(UseNameForTableVar(&expr->table));
+ return Result::Ok;
+}
+
+Result NameApplier::OnGlobalGetExpr(GlobalGetExpr* expr) {
+ CHECK_RESULT(UseNameForGlobalVar(&expr->var));
+ return Result::Ok;
+}
+
+Result NameApplier::OnLocalGetExpr(LocalGetExpr* expr) {
+ CHECK_RESULT(UseNameForParamAndLocalVar(current_func_, &expr->var));
+ return Result::Ok;
+}
+
+Result NameApplier::BeginIfExpr(IfExpr* expr) {
+ PushLabel(expr->true_.label);
+ return Result::Ok;
+}
+
+Result NameApplier::EndIfExpr(IfExpr* expr) {
+ PopLabel();
+ return Result::Ok;
+}
+
+Result NameApplier::OnLoadExpr(LoadExpr* expr) {
+ CHECK_RESULT(UseNameForMemoryVar(&expr->memidx));
+ return Result::Ok;
+}
+
+Result NameApplier::OnGlobalSetExpr(GlobalSetExpr* expr) {
+ CHECK_RESULT(UseNameForGlobalVar(&expr->var));
+ return Result::Ok;
+}
+
+Result NameApplier::OnLocalSetExpr(LocalSetExpr* expr) {
+ CHECK_RESULT(UseNameForParamAndLocalVar(current_func_, &expr->var));
+ return Result::Ok;
+}
+
+Result NameApplier::OnLocalTeeExpr(LocalTeeExpr* expr) {
+ CHECK_RESULT(UseNameForParamAndLocalVar(current_func_, &expr->var));
+ return Result::Ok;
+}
+
+Result NameApplier::OnSimdLoadLaneExpr(SimdLoadLaneExpr* expr) {
+ CHECK_RESULT(UseNameForMemoryVar(&expr->memidx));
+ return Result::Ok;
+}
+
+Result NameApplier::OnSimdStoreLaneExpr(SimdStoreLaneExpr* expr) {
+ CHECK_RESULT(UseNameForMemoryVar(&expr->memidx));
+ return Result::Ok;
+}
+
+Result NameApplier::VisitFunc(Index func_index, Func* func) {
+ current_func_ = func;
+ if (func->decl.has_func_type) {
+ CHECK_RESULT(UseNameForFuncTypeVar(&func->decl.type_var));
+ }
+
+ MakeTypeBindingReverseMapping(func->GetNumParamsAndLocals(), func->bindings,
+ &param_and_local_index_to_name_);
+
+ CHECK_RESULT(visitor_.VisitFunc(func));
+ current_func_ = nullptr;
+ return Result::Ok;
+}
+
+Result NameApplier::VisitGlobal(Global* global) {
+ CHECK_RESULT(visitor_.VisitExprList(global->init_expr));
+ return Result::Ok;
+}
+
+Result NameApplier::VisitTag(Tag* tag) {
+ if (tag->decl.has_func_type) {
+ CHECK_RESULT(UseNameForFuncTypeVar(&tag->decl.type_var));
+ }
+ return Result::Ok;
+}
+
+Result NameApplier::VisitExport(Index export_index, Export* export_) {
+ switch (export_->kind) {
+ case ExternalKind::Func:
+ UseNameForFuncVar(&export_->var);
+ break;
+
+ case ExternalKind::Table:
+ UseNameForTableVar(&export_->var);
+ break;
+
+ case ExternalKind::Memory:
+ UseNameForMemoryVar(&export_->var);
+ break;
+
+ case ExternalKind::Global:
+ UseNameForGlobalVar(&export_->var);
+ break;
+
+ case ExternalKind::Tag:
+ UseNameForTagVar(&export_->var);
+ break;
+ }
+ return Result::Ok;
+}
+
+Result NameApplier::VisitElemSegment(Index elem_segment_index,
+ ElemSegment* segment) {
+ CHECK_RESULT(UseNameForTableVar(&segment->table_var));
+ CHECK_RESULT(visitor_.VisitExprList(segment->offset));
+ for (ExprList& elem_expr : segment->elem_exprs) {
+ Expr* expr = &elem_expr.front();
+ if (expr->type() == ExprType::RefFunc) {
+ CHECK_RESULT(UseNameForFuncVar(&cast<RefFuncExpr>(expr)->var));
+ }
+ }
+ return Result::Ok;
+}
+
+Result NameApplier::VisitDataSegment(Index data_segment_index,
+ DataSegment* segment) {
+ CHECK_RESULT(UseNameForMemoryVar(&segment->memory_var));
+ CHECK_RESULT(visitor_.VisitExprList(segment->offset));
+ return Result::Ok;
+}
+
+Result NameApplier::VisitStart(Var* start_var) {
+ CHECK_RESULT(UseNameForFuncVar(start_var));
+ return Result::Ok;
+}
+
+Result NameApplier::VisitModule(Module* module) {
+ module_ = module;
+ for (size_t i = 0; i < module->funcs.size(); ++i)
+ CHECK_RESULT(VisitFunc(i, module->funcs[i]));
+ for (size_t i = 0; i < module->globals.size(); ++i)
+ CHECK_RESULT(VisitGlobal(module->globals[i]));
+ for (size_t i = 0; i < module->tags.size(); ++i)
+ CHECK_RESULT(VisitTag(module->tags[i]));
+ for (size_t i = 0; i < module->exports.size(); ++i)
+ CHECK_RESULT(VisitExport(i, module->exports[i]));
+ for (size_t i = 0; i < module->elem_segments.size(); ++i)
+ CHECK_RESULT(VisitElemSegment(i, module->elem_segments[i]));
+ for (size_t i = 0; i < module->data_segments.size(); ++i)
+ CHECK_RESULT(VisitDataSegment(i, module->data_segments[i]));
+ for (size_t i = 0; i < module->starts.size(); ++i)
+ CHECK_RESULT(VisitStart(module->starts[i]));
+ module_ = nullptr;
+ return Result::Ok;
+}
+
+} // end anonymous namespace
+
+Result ApplyNames(Module* module) {
+ NameApplier applier;
+ return applier.VisitModule(module);
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/binary-reader-ir.cc b/third_party/wasm2c/src/binary-reader-ir.cc
new file mode 100644
index 0000000000..5736e3021e
--- /dev/null
+++ b/third_party/wasm2c/src/binary-reader-ir.cc
@@ -0,0 +1,1775 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/binary-reader-ir.h"
+
+#include <cassert>
+#include <cinttypes>
+#include <cstdarg>
+#include <cstdint>
+#include <cstdio>
+#include <deque>
+#include <vector>
+
+#include "wabt/binary-reader-nop.h"
+#include "wabt/cast.h"
+#include "wabt/common.h"
+#include "wabt/ir.h"
+
+namespace wabt {
+
+namespace {
+
+struct LabelNode {
+ LabelNode(LabelType, ExprList* exprs, Expr* context = nullptr);
+
+ LabelType label_type;
+ ExprList* exprs;
+ Expr* context;
+};
+
+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 {
+ static constexpr size_t kMaxNestingDepth = 16384; // max depth of label stack
+ static constexpr size_t kMaxFunctionLocals = 50000; // matches V8
+ static constexpr size_t kMaxFunctionParams = 1000; // matches V8
+ static constexpr size_t kMaxFunctionResults = 1000; // matches V8
+
+ public:
+ BinaryReaderIR(Module* out_module, const char* filename, Errors* errors);
+
+ bool OnError(const Error&) override;
+
+ Result OnTypeCount(Index count) override;
+ Result OnFuncType(Index index,
+ Index param_count,
+ Type* param_types,
+ Index result_count,
+ Type* result_types) override;
+ Result OnStructType(Index index, Index field_count, TypeMut* fields) override;
+ Result OnArrayType(Index index, TypeMut field) override;
+
+ Result OnImportCount(Index count) override;
+ Result OnImportFunc(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index func_index,
+ Index sig_index) override;
+ Result OnImportTable(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index table_index,
+ Type elem_type,
+ const Limits* elem_limits) override;
+ Result OnImportMemory(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index memory_index,
+ const Limits* page_limits) override;
+ Result OnImportGlobal(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index global_index,
+ Type type,
+ bool mutable_) override;
+ Result OnImportTag(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index tag_index,
+ Index sig_index) override;
+
+ Result OnFunctionCount(Index count) override;
+ Result OnFunction(Index index, Index sig_index) override;
+
+ Result OnTableCount(Index count) override;
+ Result OnTable(Index index,
+ Type elem_type,
+ const Limits* elem_limits) override;
+
+ Result OnMemoryCount(Index count) override;
+ Result OnMemory(Index index, const Limits* limits) override;
+
+ Result OnGlobalCount(Index count) override;
+ Result BeginGlobal(Index index, Type type, bool mutable_) override;
+ Result BeginGlobalInitExpr(Index index) override;
+ Result EndGlobalInitExpr(Index index) override;
+
+ Result OnExportCount(Index count) override;
+ Result OnExport(Index index,
+ ExternalKind kind,
+ Index item_index,
+ std::string_view name) override;
+
+ Result OnStartFunction(Index func_index) override;
+
+ Result OnFunctionBodyCount(Index count) override;
+ 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,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnAtomicStoreExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnAtomicRmwExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnAtomicRmwCmpxchgExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnAtomicWaitExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnAtomicFenceExpr(uint32_t consistency_model) override;
+ Result OnAtomicNotifyExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnBinaryExpr(Opcode opcode) override;
+ Result OnBlockExpr(Type sig_type) override;
+ Result OnBrExpr(Index depth) override;
+ Result OnBrIfExpr(Index depth) override;
+ Result OnBrTableExpr(Index num_targets,
+ Index* target_depths,
+ Index default_target_depth) override;
+ Result OnCallExpr(Index func_index) override;
+ Result OnCatchExpr(Index tag_index) override;
+ Result OnCatchAllExpr() override;
+ Result OnCallIndirectExpr(Index sig_index, Index table_index) override;
+ Result OnCallRefExpr() override;
+ Result OnReturnCallExpr(Index func_index) override;
+ Result OnReturnCallIndirectExpr(Index sig_index, Index table_index) override;
+ Result OnCompareExpr(Opcode opcode) override;
+ Result OnConvertExpr(Opcode opcode) override;
+ Result OnDelegateExpr(Index depth) override;
+ Result OnDropExpr() override;
+ Result OnElseExpr() override;
+ Result OnEndExpr() override;
+ Result OnF32ConstExpr(uint32_t value_bits) override;
+ Result OnF64ConstExpr(uint64_t value_bits) override;
+ Result OnV128ConstExpr(v128 value_bits) override;
+ Result OnGlobalGetExpr(Index global_index) override;
+ Result OnGlobalSetExpr(Index global_index) override;
+ Result OnI32ConstExpr(uint32_t value) override;
+ Result OnI64ConstExpr(uint64_t value) override;
+ Result OnIfExpr(Type sig_type) override;
+ Result OnLoadExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnLocalGetExpr(Index local_index) override;
+ Result OnLocalSetExpr(Index local_index) override;
+ Result OnLocalTeeExpr(Index local_index) override;
+ Result OnLoopExpr(Type sig_type) override;
+ Result OnMemoryCopyExpr(Index srcmemidx, Index destmemidx) override;
+ Result OnDataDropExpr(Index segment_index) override;
+ Result OnMemoryFillExpr(Index memidx) override;
+ Result OnMemoryGrowExpr(Index memidx) override;
+ Result OnMemoryInitExpr(Index segment_index, Index memidx) override;
+ Result OnMemorySizeExpr(Index memidx) override;
+ Result OnTableCopyExpr(Index dst_index, Index src_index) override;
+ Result OnElemDropExpr(Index segment_index) override;
+ Result OnTableInitExpr(Index segment_index, Index table_index) override;
+ Result OnTableGetExpr(Index table_index) override;
+ Result OnTableSetExpr(Index table_index) override;
+ Result OnTableGrowExpr(Index table_index) override;
+ Result OnTableSizeExpr(Index table_index) override;
+ Result OnTableFillExpr(Index table_index) override;
+ Result OnRefFuncExpr(Index func_index) override;
+ Result OnRefNullExpr(Type type) override;
+ Result OnRefIsNullExpr() override;
+ Result OnNopExpr() override;
+ Result OnRethrowExpr(Index depth) override;
+ Result OnReturnExpr() override;
+ Result OnSelectExpr(Index result_count, Type* result_types) override;
+ Result OnStoreExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnThrowExpr(Index tag_index) override;
+ Result OnTryExpr(Type sig_type) override;
+ Result OnUnaryExpr(Opcode opcode) override;
+ Result OnTernaryExpr(Opcode opcode) override;
+ Result OnUnreachableExpr() override;
+ Result EndFunctionBody(Index index) override;
+ Result OnSimdLaneOpExpr(Opcode opcode, uint64_t value) override;
+ Result OnSimdLoadLaneExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset,
+ uint64_t value) override;
+ Result OnSimdStoreLaneExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset,
+ uint64_t value) override;
+ Result OnSimdShuffleOpExpr(Opcode opcode, v128 value) override;
+ Result OnLoadSplatExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnLoadZeroExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) override;
+
+ Result OnElemSegmentCount(Index count) override;
+ Result BeginElemSegment(Index index,
+ Index table_index,
+ uint8_t flags) override;
+ Result BeginElemSegmentInitExpr(Index index) override;
+ Result EndElemSegmentInitExpr(Index index) override;
+ Result OnElemSegmentElemType(Index index, Type elem_type) override;
+ Result OnElemSegmentElemExprCount(Index index, Index count) override;
+ Result OnElemSegmentElemExpr_RefNull(Index segment_index, Type type) override;
+ Result OnElemSegmentElemExpr_RefFunc(Index segment_index,
+ Index func_index) override;
+
+ Result OnDataSegmentCount(Index count) override;
+ Result BeginDataSegment(Index index,
+ Index memory_index,
+ uint8_t flags) override;
+ Result BeginDataSegmentInitExpr(Index index) override;
+ Result EndDataSegmentInitExpr(Index index) override;
+ Result OnDataSegmentData(Index index,
+ const void* data,
+ Address size) override;
+
+ Result OnModuleName(std::string_view module_name) override;
+ Result OnFunctionNamesCount(Index num_functions) override;
+ Result OnFunctionName(Index function_index,
+ std::string_view function_name) override;
+ Result OnLocalNameLocalCount(Index function_index, Index num_locals) override;
+ Result OnLocalName(Index function_index,
+ Index local_index,
+ std::string_view local_name) override;
+ Result OnNameEntry(NameSectionSubsection type,
+ Index index,
+ std::string_view name) override;
+
+ Result BeginTagSection(Offset size) override { return Result::Ok; }
+ Result OnTagCount(Index count) override { return Result::Ok; }
+ Result OnTagType(Index index, Index sig_index) override;
+ Result EndTagSection() override { return Result::Ok; }
+
+ Result OnDataSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index segment,
+ uint32_t offset,
+ uint32_t size) override;
+ Result OnFunctionSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index func_index) override;
+ Result OnGlobalSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index global_index) override;
+ 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,
+ Index tag_index) override;
+ Result OnTableSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index table_index) override;
+
+ private:
+ Location GetLocation() const;
+ void PrintError(const char* format, ...);
+ Result PushLabel(LabelType label_type,
+ ExprList* first,
+ Expr* context = nullptr);
+ Result BeginInitExpr(ExprList* init_expr);
+ Result EndInitExpr();
+ Result PopLabel();
+ Result GetLabelAt(LabelNode** label, Index depth);
+ Result TopLabel(LabelNode** label);
+ Result TopLabelExpr(LabelNode** label, Expr** expr);
+ Result AppendExpr(std::unique_ptr<Expr> expr);
+ Result AppendCatch(Catch&& catch_);
+ void SetFuncDeclaration(FuncDeclaration* decl, Var var);
+ void SetBlockDeclaration(BlockDeclaration* decl, Type sig_type);
+ Result SetMemoryName(Index index, std::string_view name);
+ Result SetTableName(Index index, std::string_view name);
+ Result SetFunctionName(Index index, std::string_view name);
+ Result SetTypeName(Index index, std::string_view name);
+ Result SetGlobalName(Index index, std::string_view name);
+ Result SetDataSegmentName(Index index, std::string_view name);
+ Result SetElemSegmentName(Index index, std::string_view name);
+ Result SetTagName(Index index, std::string_view name);
+
+ std::string GetUniqueName(BindingHash* bindings,
+ const std::string& original_name);
+
+ Errors* errors_ = nullptr;
+ Module* module_ = nullptr;
+
+ 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,
+ const char* filename,
+ Errors* errors)
+ : errors_(errors), module_(out_module), filename_(filename) {}
+
+Location BinaryReaderIR::GetLocation() const {
+ Location loc;
+ loc.filename = filename_;
+ loc.offset = state->offset;
+ return loc;
+}
+
+void WABT_PRINTF_FORMAT(2, 3) BinaryReaderIR::PrintError(const char* format,
+ ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ errors_->emplace_back(ErrorLevel::Error, Location(kInvalidOffset), buffer);
+}
+
+Result BinaryReaderIR::PushLabel(LabelType label_type,
+ ExprList* first,
+ Expr* context) {
+ if (label_stack_.size() >= kMaxNestingDepth) {
+ PrintError("label stack exceeds max nesting depth");
+ return Result::Error;
+ }
+ label_stack_.emplace_back(label_type, first, context);
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::PopLabel() {
+ if (label_stack_.size() == 0) {
+ PrintError("popping empty label stack");
+ return Result::Error;
+ }
+
+ label_stack_.pop_back();
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::GetLabelAt(LabelNode** label, Index depth) {
+ if (depth >= label_stack_.size()) {
+ PrintError("accessing stack depth: %" PRIindex " >= max: %" PRIzd, depth,
+ label_stack_.size());
+ return Result::Error;
+ }
+
+ *label = &label_stack_[label_stack_.size() - depth - 1];
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::TopLabel(LabelNode** label) {
+ return GetLabelAt(label, 0);
+}
+
+Result BinaryReaderIR::TopLabelExpr(LabelNode** label, Expr** expr) {
+ CHECK_RESULT(TopLabel(label));
+ LabelNode* parent_label;
+ CHECK_RESULT(GetLabelAt(&parent_label, 1));
+ if (parent_label->exprs->empty()) {
+ PrintError("TopLabelExpr: parent label has empty expr list");
+ return Result::Error;
+ }
+ *expr = &parent_label->exprs->back();
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::AppendExpr(std::unique_ptr<Expr> expr) {
+ expr->loc = GetLocation();
+ LabelNode* label;
+ CHECK_RESULT(TopLabel(&label));
+ label->exprs->push_back(std::move(expr));
+ return Result::Ok;
+}
+
+void BinaryReaderIR::SetFuncDeclaration(FuncDeclaration* decl, Var var) {
+ decl->has_func_type = true;
+ decl->type_var = var;
+ if (auto* func_type = module_->GetFuncType(var)) {
+ decl->sig = func_type->sig;
+ }
+}
+
+void BinaryReaderIR::SetBlockDeclaration(BlockDeclaration* decl,
+ Type sig_type) {
+ if (sig_type.IsIndex()) {
+ Index type_index = sig_type.GetIndex();
+ SetFuncDeclaration(decl, Var(type_index, GetLocation()));
+ } else {
+ decl->has_func_type = false;
+ decl->sig.param_types.clear();
+ decl->sig.result_types = sig_type.GetInlineVector();
+ }
+}
+
+std::string BinaryReaderIR::GetUniqueName(BindingHash* bindings,
+ const std::string& orig_name) {
+ int counter = 1;
+ std::string unique_name = orig_name;
+ while (bindings->count(unique_name) != 0) {
+ unique_name = orig_name + "." + std::to_string(counter++);
+ }
+ return unique_name;
+}
+
+bool BinaryReaderIR::OnError(const Error& error) {
+ errors_->push_back(error);
+ return true;
+}
+
+Result BinaryReaderIR::OnTypeCount(Index count) {
+ WABT_TRY
+ module_->types.reserve(count);
+ WABT_CATCH_BAD_ALLOC
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnFuncType(Index index,
+ Index param_count,
+ Type* param_types,
+ Index result_count,
+ Type* result_types) {
+ if (param_count > kMaxFunctionParams) {
+ PrintError("FuncType param count exceeds maximum value");
+ return Result::Error;
+ }
+
+ if (result_count > kMaxFunctionResults) {
+ PrintError("FuncType result count exceeds maximum value");
+ return Result::Error;
+ }
+
+ auto field = std::make_unique<TypeModuleField>(GetLocation());
+ auto func_type = std::make_unique<FuncType>();
+ func_type->sig.param_types.assign(param_types, param_types + param_count);
+ func_type->sig.result_types.assign(result_types, result_types + result_count);
+
+ module_->features_used.simd |=
+ std::any_of(func_type->sig.param_types.begin(),
+ func_type->sig.param_types.end(),
+ [](auto x) { return x == Type::V128; }) ||
+ std::any_of(func_type->sig.result_types.begin(),
+ func_type->sig.result_types.end(),
+ [](auto x) { return x == Type::V128; });
+
+ field->type = std::move(func_type);
+ module_->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnStructType(Index index,
+ Index field_count,
+ TypeMut* fields) {
+ auto field = std::make_unique<TypeModuleField>(GetLocation());
+ auto struct_type = std::make_unique<StructType>();
+ struct_type->fields.resize(field_count);
+ for (Index i = 0; i < field_count; ++i) {
+ struct_type->fields[i].type = fields[i].type;
+ struct_type->fields[i].mutable_ = fields[i].mutable_;
+ module_->features_used.simd |= (fields[i].type == Type::V128);
+ }
+ field->type = std::move(struct_type);
+ module_->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnArrayType(Index index, TypeMut type_mut) {
+ auto field = std::make_unique<TypeModuleField>(GetLocation());
+ auto array_type = std::make_unique<ArrayType>();
+ array_type->field.type = type_mut.type;
+ array_type->field.mutable_ = type_mut.mutable_;
+ module_->features_used.simd |= (type_mut.type == Type::V128);
+ field->type = std::move(array_type);
+ module_->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnImportCount(Index count) {
+ WABT_TRY
+ module_->imports.reserve(count);
+ WABT_CATCH_BAD_ALLOC
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnImportFunc(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index func_index,
+ Index sig_index) {
+ auto import = std::make_unique<FuncImport>();
+ import->module_name = module_name;
+ import->field_name = field_name;
+ SetFuncDeclaration(&import->func.decl, Var(sig_index, GetLocation()));
+ module_->AppendField(
+ std::make_unique<ImportModuleField>(std::move(import), GetLocation()));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnImportTable(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index table_index,
+ Type elem_type,
+ const Limits* elem_limits) {
+ auto import = std::make_unique<TableImport>();
+ import->module_name = module_name;
+ import->field_name = field_name;
+ import->table.elem_limits = *elem_limits;
+ import->table.elem_type = elem_type;
+ module_->AppendField(
+ std::make_unique<ImportModuleField>(std::move(import), GetLocation()));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnImportMemory(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index memory_index,
+ const Limits* page_limits) {
+ auto import = std::make_unique<MemoryImport>();
+ import->module_name = module_name;
+ import->field_name = field_name;
+ import->memory.page_limits = *page_limits;
+ module_->AppendField(
+ std::make_unique<ImportModuleField>(std::move(import), GetLocation()));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnImportGlobal(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index global_index,
+ Type type,
+ bool mutable_) {
+ auto import = std::make_unique<GlobalImport>();
+ import->module_name = module_name;
+ import->field_name = field_name;
+ import->global.type = type;
+ import->global.mutable_ = mutable_;
+ module_->AppendField(
+ std::make_unique<ImportModuleField>(std::move(import), GetLocation()));
+ module_->features_used.simd |= (type == Type::V128);
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnImportTag(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index tag_index,
+ Index sig_index) {
+ auto import = std::make_unique<TagImport>();
+ import->module_name = module_name;
+ import->field_name = field_name;
+ SetFuncDeclaration(&import->tag.decl, Var(sig_index, GetLocation()));
+ module_->AppendField(
+ std::make_unique<ImportModuleField>(std::move(import), GetLocation()));
+ module_->features_used.exceptions = true;
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnFunctionCount(Index count) {
+ WABT_TRY
+ module_->funcs.reserve(module_->num_func_imports + count);
+ WABT_CATCH_BAD_ALLOC
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnFunction(Index index, Index sig_index) {
+ auto field = std::make_unique<FuncModuleField>(GetLocation());
+ Func& func = field->func;
+ SetFuncDeclaration(&func.decl, Var(sig_index, GetLocation()));
+ module_->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnTableCount(Index count) {
+ WABT_TRY
+ module_->tables.reserve(module_->num_table_imports + count);
+ WABT_CATCH_BAD_ALLOC
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnTable(Index index,
+ Type elem_type,
+ const Limits* elem_limits) {
+ auto field = std::make_unique<TableModuleField>(GetLocation());
+ Table& table = field->table;
+ table.elem_limits = *elem_limits;
+ table.elem_type = elem_type;
+ module_->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnMemoryCount(Index count) {
+ WABT_TRY
+ module_->memories.reserve(module_->num_memory_imports + count);
+ WABT_CATCH_BAD_ALLOC
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnMemory(Index index, const Limits* page_limits) {
+ auto field = std::make_unique<MemoryModuleField>(GetLocation());
+ Memory& memory = field->memory;
+ memory.page_limits = *page_limits;
+ module_->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnGlobalCount(Index count) {
+ WABT_TRY
+ module_->globals.reserve(module_->num_global_imports + count);
+ WABT_CATCH_BAD_ALLOC
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::BeginGlobal(Index index, Type type, bool mutable_) {
+ auto field = std::make_unique<GlobalModuleField>(GetLocation());
+ Global& global = field->global;
+ global.type = type;
+ global.mutable_ = mutable_;
+ module_->AppendField(std::move(field));
+ module_->features_used.simd |= (type == Type::V128);
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::BeginGlobalInitExpr(Index index) {
+ assert(index == module_->globals.size() - 1);
+ Global* global = module_->globals[index];
+ return BeginInitExpr(&global->init_expr);
+}
+
+Result BinaryReaderIR::EndGlobalInitExpr(Index index) {
+ return EndInitExpr();
+}
+
+Result BinaryReaderIR::OnExportCount(Index count) {
+ WABT_TRY
+ module_->exports.reserve(count);
+ WABT_CATCH_BAD_ALLOC
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnExport(Index index,
+ ExternalKind kind,
+ Index item_index,
+ std::string_view name) {
+ auto field = std::make_unique<ExportModuleField>(GetLocation());
+ Export& export_ = field->export_;
+ export_.name = name;
+ export_.var = Var(item_index, GetLocation());
+ export_.kind = kind;
+ module_->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnStartFunction(Index func_index) {
+ Var start(func_index, GetLocation());
+ module_->AppendField(
+ std::make_unique<StartModuleField>(start, GetLocation()));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnFunctionBodyCount(Index count) {
+ // Can hit this case on a malformed module if we don't stop on first error.
+ if (module_->num_func_imports + count != module_->funcs.size()) {
+ PrintError(
+ "number of imported func + func count in code section does not match "
+ "actual number of funcs in module");
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::BeginFunctionBody(Index index, Offset size) {
+ current_func_ = module_->funcs[index];
+ current_func_->loc = GetLocation();
+ return PushLabel(LabelType::Func, &current_func_->exprs);
+}
+
+Result BinaryReaderIR::OnLocalDecl(Index decl_index, Index count, Type type) {
+ current_func_->local_types.AppendDecl(type, count);
+
+ if (current_func_->GetNumLocals() > kMaxFunctionLocals) {
+ PrintError("function local count exceeds maximum value");
+ return Result::Error;
+ }
+
+ module_->features_used.simd |= (type == Type::V128);
+ 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));
+ }
+ module_->features_used.simd |= (opcode.GetResultType() == Type::V128);
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnAtomicLoadExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(std::make_unique<AtomicLoadExpr>(
+ opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnAtomicStoreExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(std::make_unique<AtomicStoreExpr>(
+ opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnAtomicRmwExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(std::make_unique<AtomicRmwExpr>(
+ opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnAtomicRmwCmpxchgExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(std::make_unique<AtomicRmwCmpxchgExpr>(
+ opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnAtomicWaitExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(std::make_unique<AtomicWaitExpr>(
+ opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnAtomicFenceExpr(uint32_t consistency_model) {
+ return AppendExpr(std::make_unique<AtomicFenceExpr>(consistency_model));
+}
+
+Result BinaryReaderIR::OnAtomicNotifyExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(std::make_unique<AtomicNotifyExpr>(
+ opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnBinaryExpr(Opcode opcode) {
+ return AppendExpr(std::make_unique<BinaryExpr>(opcode));
+}
+
+Result BinaryReaderIR::OnBlockExpr(Type sig_type) {
+ auto expr = std::make_unique<BlockExpr>();
+ SetBlockDeclaration(&expr->block.decl, sig_type);
+ ExprList* expr_list = &expr->block.exprs;
+ CHECK_RESULT(AppendExpr(std::move(expr)));
+ return PushLabel(LabelType::Block, expr_list);
+}
+
+Result BinaryReaderIR::OnBrExpr(Index depth) {
+ return AppendExpr(std::make_unique<BrExpr>(Var(depth, GetLocation())));
+}
+
+Result BinaryReaderIR::OnBrIfExpr(Index depth) {
+ return AppendExpr(std::make_unique<BrIfExpr>(Var(depth, GetLocation())));
+}
+
+Result BinaryReaderIR::OnBrTableExpr(Index num_targets,
+ Index* target_depths,
+ Index default_target_depth) {
+ auto expr = std::make_unique<BrTableExpr>();
+ expr->default_target = Var(default_target_depth, GetLocation());
+ expr->targets.resize(num_targets);
+ for (Index i = 0; i < num_targets; ++i) {
+ expr->targets[i] = Var(target_depths[i], GetLocation());
+ }
+ return AppendExpr(std::move(expr));
+}
+
+Result BinaryReaderIR::OnCallExpr(Index func_index) {
+ return AppendExpr(std::make_unique<CallExpr>(Var(func_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnCallIndirectExpr(Index sig_index, Index table_index) {
+ auto expr = std::make_unique<CallIndirectExpr>();
+ SetFuncDeclaration(&expr->decl, Var(sig_index, GetLocation()));
+ expr->table = Var(table_index, GetLocation());
+ return AppendExpr(std::move(expr));
+}
+
+Result BinaryReaderIR::OnCallRefExpr() {
+ return AppendExpr(std::make_unique<CallRefExpr>());
+}
+
+Result BinaryReaderIR::OnReturnCallExpr(Index func_index) {
+ return AppendExpr(
+ std::make_unique<ReturnCallExpr>(Var(func_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnReturnCallIndirectExpr(Index sig_index,
+ Index table_index) {
+ auto expr = std::make_unique<ReturnCallIndirectExpr>();
+ SetFuncDeclaration(&expr->decl, Var(sig_index, GetLocation()));
+ expr->table = Var(table_index, GetLocation());
+ return AppendExpr(std::move(expr));
+}
+
+Result BinaryReaderIR::OnCompareExpr(Opcode opcode) {
+ return AppendExpr(std::make_unique<CompareExpr>(opcode));
+}
+
+Result BinaryReaderIR::OnConvertExpr(Opcode opcode) {
+ return AppendExpr(std::make_unique<ConvertExpr>(opcode));
+}
+
+Result BinaryReaderIR::OnDropExpr() {
+ return AppendExpr(std::make_unique<DropExpr>());
+}
+
+Result BinaryReaderIR::OnElseExpr() {
+ LabelNode* label;
+ Expr* expr;
+ CHECK_RESULT(TopLabelExpr(&label, &expr));
+
+ if (label->label_type == LabelType::If) {
+ auto* if_expr = cast<IfExpr>(expr);
+ if_expr->true_.end_loc = GetLocation();
+ label->exprs = &if_expr->false_;
+ label->label_type = LabelType::Else;
+ } else {
+ PrintError("else expression without matching if");
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnEndExpr() {
+ if (label_stack_.size() > 1) {
+ LabelNode* label;
+ Expr* expr;
+ CHECK_RESULT(TopLabelExpr(&label, &expr));
+ switch (label->label_type) {
+ case LabelType::Block:
+ cast<BlockExpr>(expr)->block.end_loc = GetLocation();
+ break;
+ case LabelType::Loop:
+ cast<LoopExpr>(expr)->block.end_loc = GetLocation();
+ break;
+ case LabelType::If:
+ cast<IfExpr>(expr)->true_.end_loc = GetLocation();
+ break;
+ case LabelType::Else:
+ cast<IfExpr>(expr)->false_end_loc = GetLocation();
+ break;
+ case LabelType::Try:
+ cast<TryExpr>(expr)->block.end_loc = GetLocation();
+ break;
+
+ case LabelType::InitExpr:
+ case LabelType::Func:
+ case LabelType::Catch:
+ break;
+ }
+ }
+
+ return PopLabel();
+}
+
+Result BinaryReaderIR::OnF32ConstExpr(uint32_t value_bits) {
+ return AppendExpr(
+ std::make_unique<ConstExpr>(Const::F32(value_bits, GetLocation())));
+}
+
+Result BinaryReaderIR::OnF64ConstExpr(uint64_t value_bits) {
+ return AppendExpr(
+ std::make_unique<ConstExpr>(Const::F64(value_bits, GetLocation())));
+}
+
+Result BinaryReaderIR::OnV128ConstExpr(v128 value_bits) {
+ return AppendExpr(
+ std::make_unique<ConstExpr>(Const::V128(value_bits, GetLocation())));
+}
+
+Result BinaryReaderIR::OnGlobalGetExpr(Index global_index) {
+ return AppendExpr(
+ std::make_unique<GlobalGetExpr>(Var(global_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnLocalGetExpr(Index local_index) {
+ return AppendExpr(
+ std::make_unique<LocalGetExpr>(Var(local_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnI32ConstExpr(uint32_t value) {
+ return AppendExpr(
+ std::make_unique<ConstExpr>(Const::I32(value, GetLocation())));
+}
+
+Result BinaryReaderIR::OnI64ConstExpr(uint64_t value) {
+ return AppendExpr(
+ std::make_unique<ConstExpr>(Const::I64(value, GetLocation())));
+}
+
+Result BinaryReaderIR::OnIfExpr(Type sig_type) {
+ auto expr = std::make_unique<IfExpr>();
+ SetBlockDeclaration(&expr->true_.decl, sig_type);
+ ExprList* expr_list = &expr->true_.exprs;
+ CHECK_RESULT(AppendExpr(std::move(expr)));
+ return PushLabel(LabelType::If, expr_list);
+}
+
+Result BinaryReaderIR::OnLoadExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(std::make_unique<LoadExpr>(
+ opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnLoopExpr(Type sig_type) {
+ auto expr = std::make_unique<LoopExpr>();
+ SetBlockDeclaration(&expr->block.decl, sig_type);
+ ExprList* expr_list = &expr->block.exprs;
+ CHECK_RESULT(AppendExpr(std::move(expr)));
+ return PushLabel(LabelType::Loop, expr_list);
+}
+
+Result BinaryReaderIR::OnMemoryCopyExpr(Index srcmemidx, Index destmemidx) {
+ return AppendExpr(std::make_unique<MemoryCopyExpr>(
+ Var(srcmemidx, GetLocation()), Var(destmemidx, GetLocation())));
+}
+
+Result BinaryReaderIR::OnDataDropExpr(Index segment) {
+ return AppendExpr(
+ std::make_unique<DataDropExpr>(Var(segment, GetLocation())));
+}
+
+Result BinaryReaderIR::OnMemoryFillExpr(Index memidx) {
+ return AppendExpr(
+ std::make_unique<MemoryFillExpr>(Var(memidx, GetLocation())));
+}
+
+Result BinaryReaderIR::OnMemoryGrowExpr(Index memidx) {
+ return AppendExpr(
+ std::make_unique<MemoryGrowExpr>(Var(memidx, GetLocation())));
+}
+
+Result BinaryReaderIR::OnMemoryInitExpr(Index segment, Index memidx) {
+ return AppendExpr(std::make_unique<MemoryInitExpr>(
+ Var(segment, GetLocation()), Var(memidx, GetLocation())));
+}
+
+Result BinaryReaderIR::OnMemorySizeExpr(Index memidx) {
+ return AppendExpr(
+ std::make_unique<MemorySizeExpr>(Var(memidx, GetLocation())));
+}
+
+Result BinaryReaderIR::OnTableCopyExpr(Index dst_index, Index src_index) {
+ return AppendExpr(std::make_unique<TableCopyExpr>(
+ Var(dst_index, GetLocation()), Var(src_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnElemDropExpr(Index segment) {
+ return AppendExpr(
+ std::make_unique<ElemDropExpr>(Var(segment, GetLocation())));
+}
+
+Result BinaryReaderIR::OnTableInitExpr(Index segment, Index table_index) {
+ return AppendExpr(std::make_unique<TableInitExpr>(
+ Var(segment, GetLocation()), Var(table_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnTableGetExpr(Index table_index) {
+ return AppendExpr(
+ std::make_unique<TableGetExpr>(Var(table_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnTableSetExpr(Index table_index) {
+ return AppendExpr(
+ std::make_unique<TableSetExpr>(Var(table_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnTableGrowExpr(Index table_index) {
+ return AppendExpr(
+ std::make_unique<TableGrowExpr>(Var(table_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnTableSizeExpr(Index table_index) {
+ return AppendExpr(
+ std::make_unique<TableSizeExpr>(Var(table_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnTableFillExpr(Index table_index) {
+ return AppendExpr(
+ std::make_unique<TableFillExpr>(Var(table_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnRefFuncExpr(Index func_index) {
+ return AppendExpr(
+ std::make_unique<RefFuncExpr>(Var(func_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnRefNullExpr(Type type) {
+ return AppendExpr(std::make_unique<RefNullExpr>(type));
+}
+
+Result BinaryReaderIR::OnRefIsNullExpr() {
+ return AppendExpr(std::make_unique<RefIsNullExpr>());
+}
+
+Result BinaryReaderIR::OnNopExpr() {
+ return AppendExpr(std::make_unique<NopExpr>());
+}
+
+Result BinaryReaderIR::OnRethrowExpr(Index depth) {
+ return AppendExpr(std::make_unique<RethrowExpr>(Var(depth, GetLocation())));
+}
+
+Result BinaryReaderIR::OnReturnExpr() {
+ return AppendExpr(std::make_unique<ReturnExpr>());
+}
+
+Result BinaryReaderIR::OnSelectExpr(Index result_count, Type* result_types) {
+ TypeVector results;
+ results.assign(result_types, result_types + result_count);
+ return AppendExpr(std::make_unique<SelectExpr>(results));
+}
+
+Result BinaryReaderIR::OnGlobalSetExpr(Index global_index) {
+ return AppendExpr(
+ std::make_unique<GlobalSetExpr>(Var(global_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnLocalSetExpr(Index local_index) {
+ return AppendExpr(
+ std::make_unique<LocalSetExpr>(Var(local_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnStoreExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(std::make_unique<StoreExpr>(
+ opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnThrowExpr(Index tag_index) {
+ return AppendExpr(std::make_unique<ThrowExpr>(Var(tag_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnLocalTeeExpr(Index local_index) {
+ return AppendExpr(
+ std::make_unique<LocalTeeExpr>(Var(local_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnTryExpr(Type sig_type) {
+ auto expr_ptr = std::make_unique<TryExpr>();
+ // Save expr so it can be used below, after expr_ptr has been moved.
+ TryExpr* expr = expr_ptr.get();
+ ExprList* expr_list = &expr->block.exprs;
+ SetBlockDeclaration(&expr->block.decl, sig_type);
+ CHECK_RESULT(AppendExpr(std::move(expr_ptr)));
+ module_->features_used.exceptions = true;
+ return PushLabel(LabelType::Try, expr_list, expr);
+}
+
+Result BinaryReaderIR::AppendCatch(Catch&& catch_) {
+ LabelNode* label = nullptr;
+ CHECK_RESULT(TopLabel(&label));
+
+ if (label->label_type != LabelType::Try) {
+ PrintError("catch not inside try block");
+ return Result::Error;
+ }
+
+ auto* try_ = cast<TryExpr>(label->context);
+
+ if (catch_.IsCatchAll() && !try_->catches.empty() &&
+ try_->catches.back().IsCatchAll()) {
+ PrintError("only one catch_all allowed in try block");
+ return Result::Error;
+ }
+
+ if (try_->kind == TryKind::Plain) {
+ try_->kind = TryKind::Catch;
+ } else if (try_->kind != TryKind::Catch) {
+ PrintError("catch not allowed in try-delegate");
+ return Result::Error;
+ }
+
+ try_->catches.push_back(std::move(catch_));
+ label->exprs = &try_->catches.back().exprs;
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnCatchExpr(Index except_index) {
+ return AppendCatch(Catch(Var(except_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnCatchAllExpr() {
+ return AppendCatch(Catch(GetLocation()));
+}
+
+Result BinaryReaderIR::OnDelegateExpr(Index depth) {
+ LabelNode* label = nullptr;
+ CHECK_RESULT(TopLabel(&label));
+
+ if (label->label_type != LabelType::Try) {
+ PrintError("delegate not inside try block");
+ return Result::Error;
+ }
+
+ auto* try_ = cast<TryExpr>(label->context);
+
+ if (try_->kind == TryKind::Plain) {
+ try_->kind = TryKind::Delegate;
+ } else if (try_->kind != TryKind::Delegate) {
+ PrintError("delegate not allowed in try-catch");
+ return Result::Error;
+ }
+
+ try_->delegate_target = Var(depth, GetLocation());
+
+ PopLabel();
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnUnaryExpr(Opcode opcode) {
+ return AppendExpr(std::make_unique<UnaryExpr>(opcode));
+}
+
+Result BinaryReaderIR::OnTernaryExpr(Opcode opcode) {
+ return AppendExpr(std::make_unique<TernaryExpr>(opcode));
+}
+
+Result BinaryReaderIR::OnUnreachableExpr() {
+ return AppendExpr(std::make_unique<UnreachableExpr>());
+}
+
+Result BinaryReaderIR::EndFunctionBody(Index index) {
+ current_func_ = nullptr;
+ if (!label_stack_.empty()) {
+ PrintError("function %" PRIindex " missing end marker", index);
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnSimdLaneOpExpr(Opcode opcode, uint64_t value) {
+ return AppendExpr(std::make_unique<SimdLaneOpExpr>(opcode, value));
+}
+
+Result BinaryReaderIR::OnSimdLoadLaneExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset,
+ uint64_t value) {
+ return AppendExpr(std::make_unique<SimdLoadLaneExpr>(
+ opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset, value));
+}
+
+Result BinaryReaderIR::OnSimdStoreLaneExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset,
+ uint64_t value) {
+ return AppendExpr(std::make_unique<SimdStoreLaneExpr>(
+ opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset, value));
+}
+
+Result BinaryReaderIR::OnSimdShuffleOpExpr(Opcode opcode, v128 value) {
+ return AppendExpr(std::make_unique<SimdShuffleOpExpr>(opcode, value));
+}
+
+Result BinaryReaderIR::OnLoadSplatExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(std::make_unique<LoadSplatExpr>(
+ opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnLoadZeroExpr(Opcode opcode,
+ Index memidx,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(std::make_unique<LoadZeroExpr>(
+ opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnElemSegmentCount(Index count) {
+ WABT_TRY
+ module_->elem_segments.reserve(count);
+ WABT_CATCH_BAD_ALLOC
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::BeginElemSegment(Index index,
+ Index table_index,
+ uint8_t flags) {
+ auto field = std::make_unique<ElemSegmentModuleField>(GetLocation());
+ ElemSegment& elem_segment = field->elem_segment;
+ elem_segment.table_var = Var(table_index, GetLocation());
+ if ((flags & SegDeclared) == SegDeclared) {
+ elem_segment.kind = SegmentKind::Declared;
+ } else if ((flags & SegPassive) == SegPassive) {
+ elem_segment.kind = SegmentKind::Passive;
+ } else {
+ elem_segment.kind = SegmentKind::Active;
+ }
+ module_->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::BeginInitExpr(ExprList* expr) {
+ return PushLabel(LabelType::InitExpr, expr);
+}
+
+Result BinaryReaderIR::BeginElemSegmentInitExpr(Index index) {
+ assert(index == module_->elem_segments.size() - 1);
+ ElemSegment* segment = module_->elem_segments[index];
+ return BeginInitExpr(&segment->offset);
+}
+
+Result BinaryReaderIR::EndInitExpr() {
+ if (!label_stack_.empty()) {
+ PrintError("init expression missing end marker");
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::EndElemSegmentInitExpr(Index index) {
+ return EndInitExpr();
+}
+
+Result BinaryReaderIR::OnElemSegmentElemType(Index index, Type elem_type) {
+ assert(index == module_->elem_segments.size() - 1);
+ ElemSegment* segment = module_->elem_segments[index];
+ segment->elem_type = elem_type;
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnElemSegmentElemExprCount(Index index, Index count) {
+ assert(index == module_->elem_segments.size() - 1);
+ ElemSegment* segment = module_->elem_segments[index];
+ WABT_TRY
+ segment->elem_exprs.reserve(count);
+ WABT_CATCH_BAD_ALLOC
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnElemSegmentElemExpr_RefNull(Index segment_index,
+ Type type) {
+ assert(segment_index == module_->elem_segments.size() - 1);
+ ElemSegment* segment = module_->elem_segments[segment_index];
+ Location loc = GetLocation();
+ ExprList init_expr;
+ init_expr.push_back(std::make_unique<RefNullExpr>(type, loc));
+ segment->elem_exprs.push_back(std::move(init_expr));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnElemSegmentElemExpr_RefFunc(Index segment_index,
+ Index func_index) {
+ assert(segment_index == module_->elem_segments.size() - 1);
+ ElemSegment* segment = module_->elem_segments[segment_index];
+ Location loc = GetLocation();
+ ExprList init_expr;
+ init_expr.push_back(std::make_unique<RefFuncExpr>(Var(func_index, loc), loc));
+ segment->elem_exprs.push_back(std::move(init_expr));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnDataSegmentCount(Index count) {
+ WABT_TRY
+ module_->data_segments.reserve(count);
+ WABT_CATCH_BAD_ALLOC
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::BeginDataSegment(Index index,
+ Index memory_index,
+ uint8_t flags) {
+ auto field = std::make_unique<DataSegmentModuleField>(GetLocation());
+ DataSegment& data_segment = field->data_segment;
+ data_segment.memory_var = Var(memory_index, GetLocation());
+ if ((flags & SegPassive) == SegPassive) {
+ data_segment.kind = SegmentKind::Passive;
+ } else {
+ data_segment.kind = SegmentKind::Active;
+ }
+ module_->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::BeginDataSegmentInitExpr(Index index) {
+ assert(index == module_->data_segments.size() - 1);
+ DataSegment* segment = module_->data_segments[index];
+ return BeginInitExpr(&segment->offset);
+}
+
+Result BinaryReaderIR::EndDataSegmentInitExpr(Index index) {
+ return EndInitExpr();
+}
+
+Result BinaryReaderIR::OnDataSegmentData(Index index,
+ const void* data,
+ Address size) {
+ assert(index == module_->data_segments.size() - 1);
+ DataSegment* segment = module_->data_segments[index];
+ segment->data.resize(size);
+ if (size > 0) {
+ memcpy(segment->data.data(), data, size);
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnFunctionNamesCount(Index count) {
+ if (count > module_->funcs.size()) {
+ PrintError("expected function name count (%" PRIindex
+ ") <= function count (%" PRIzd ")",
+ count, module_->funcs.size());
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+static std::string MakeDollarName(std::string_view name) {
+ return std::string("$") + std::string(name);
+}
+
+Result BinaryReaderIR::OnModuleName(std::string_view name) {
+ if (name.empty()) {
+ return Result::Ok;
+ }
+
+ module_->name = MakeDollarName(name);
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::SetGlobalName(Index index, std::string_view name) {
+ if (name.empty()) {
+ return Result::Ok;
+ }
+ if (index >= module_->globals.size()) {
+ PrintError("invalid global index: %" PRIindex, index);
+ return Result::Error;
+ }
+ Global* glob = module_->globals[index];
+ std::string dollar_name =
+ GetUniqueName(&module_->global_bindings, MakeDollarName(name));
+ glob->name = dollar_name;
+ module_->global_bindings.emplace(dollar_name, Binding(index));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::SetFunctionName(Index index, std::string_view name) {
+ if (name.empty()) {
+ return Result::Ok;
+ }
+ if (index >= module_->funcs.size()) {
+ PrintError("invalid function index: %" PRIindex, index);
+ return Result::Error;
+ }
+ Func* func = module_->funcs[index];
+ std::string dollar_name =
+ GetUniqueName(&module_->func_bindings, MakeDollarName(name));
+ func->name = dollar_name;
+ module_->func_bindings.emplace(dollar_name, Binding(index));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::SetTypeName(Index index, std::string_view name) {
+ if (name.empty()) {
+ return Result::Ok;
+ }
+ if (index >= module_->types.size()) {
+ PrintError("invalid type index: %" PRIindex, index);
+ return Result::Error;
+ }
+ TypeEntry* type = module_->types[index];
+ std::string dollar_name =
+ GetUniqueName(&module_->type_bindings, MakeDollarName(name));
+ type->name = dollar_name;
+ module_->type_bindings.emplace(dollar_name, Binding(index));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::SetTableName(Index index, std::string_view name) {
+ if (name.empty()) {
+ return Result::Ok;
+ }
+ if (index >= module_->tables.size()) {
+ PrintError("invalid table index: %" PRIindex, index);
+ return Result::Error;
+ }
+ Table* table = module_->tables[index];
+ std::string dollar_name =
+ GetUniqueName(&module_->table_bindings, MakeDollarName(name));
+ table->name = dollar_name;
+ module_->table_bindings.emplace(dollar_name, Binding(index));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::SetDataSegmentName(Index index, std::string_view name) {
+ if (name.empty()) {
+ return Result::Ok;
+ }
+ if (index >= module_->data_segments.size()) {
+ PrintError("invalid data segment index: %" PRIindex, index);
+ return Result::Error;
+ }
+ DataSegment* segment = module_->data_segments[index];
+ std::string dollar_name =
+ GetUniqueName(&module_->data_segment_bindings, MakeDollarName(name));
+ segment->name = dollar_name;
+ module_->data_segment_bindings.emplace(dollar_name, Binding(index));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::SetElemSegmentName(Index index, std::string_view name) {
+ if (name.empty()) {
+ return Result::Ok;
+ }
+ if (index >= module_->elem_segments.size()) {
+ PrintError("invalid elem segment index: %" PRIindex, index);
+ return Result::Error;
+ }
+ ElemSegment* segment = module_->elem_segments[index];
+ std::string dollar_name =
+ GetUniqueName(&module_->elem_segment_bindings, MakeDollarName(name));
+ segment->name = dollar_name;
+ module_->elem_segment_bindings.emplace(dollar_name, Binding(index));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::SetMemoryName(Index index, std::string_view name) {
+ if (name.empty()) {
+ return Result::Ok;
+ }
+ if (index >= module_->memories.size()) {
+ PrintError("invalid memory index: %" PRIindex, index);
+ return Result::Error;
+ }
+ Memory* memory = module_->memories[index];
+ std::string dollar_name =
+ GetUniqueName(&module_->memory_bindings, MakeDollarName(name));
+ memory->name = dollar_name;
+ module_->memory_bindings.emplace(dollar_name, Binding(index));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::SetTagName(Index index, std::string_view name) {
+ if (name.empty()) {
+ return Result::Ok;
+ }
+ if (index >= module_->tags.size()) {
+ PrintError("invalid tag index: %" PRIindex, index);
+ return Result::Error;
+ }
+ Tag* tag = module_->tags[index];
+ std::string dollar_name =
+ GetUniqueName(&module_->tag_bindings, MakeDollarName(name));
+ tag->name = dollar_name;
+ module_->tag_bindings.emplace(dollar_name, Binding(index));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnFunctionName(Index index, std::string_view name) {
+ return SetFunctionName(index, name);
+}
+
+Result BinaryReaderIR::OnNameEntry(NameSectionSubsection type,
+ Index index,
+ std::string_view name) {
+ switch (type) {
+ // TODO(sbc): remove OnFunctionName in favor of just using
+ // OnNameEntry so that this works
+ case NameSectionSubsection::Function:
+ case NameSectionSubsection::Local:
+ case NameSectionSubsection::Module:
+ case NameSectionSubsection::Label:
+ break;
+ case NameSectionSubsection::Type:
+ SetTypeName(index, name);
+ break;
+ case NameSectionSubsection::Tag:
+ SetTagName(index, name);
+ break;
+ case NameSectionSubsection::Global:
+ SetGlobalName(index, name);
+ break;
+ case NameSectionSubsection::Table:
+ SetTableName(index, name);
+ break;
+ case NameSectionSubsection::DataSegment:
+ SetDataSegmentName(index, name);
+ break;
+ case NameSectionSubsection::Memory:
+ SetMemoryName(index, name);
+ break;
+ case NameSectionSubsection::ElemSegment:
+ SetElemSegmentName(index, name);
+ break;
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnLocalNameLocalCount(Index index, Index count) {
+ assert(index < module_->funcs.size());
+ Func* func = module_->funcs[index];
+ Index num_params_and_locals = func->GetNumParamsAndLocals();
+ if (count > num_params_and_locals) {
+ PrintError("expected local name count (%" PRIindex
+ ") <= local count (%" PRIindex ")",
+ count, num_params_and_locals);
+ return Result::Error;
+ }
+ 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 = std::make_unique<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) {
+ if (name.empty()) {
+ return Result::Ok;
+ }
+
+ Func* func = module_->funcs[func_index];
+ func->bindings.emplace(GetUniqueName(&func->bindings, MakeDollarName(name)),
+ Binding(local_index));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnTagType(Index index, Index sig_index) {
+ auto field = std::make_unique<TagModuleField>(GetLocation());
+ Tag& tag = field->tag;
+ SetFuncDeclaration(&tag.decl, Var(sig_index, GetLocation()));
+ module_->AppendField(std::move(field));
+ module_->features_used.exceptions = true;
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnDataSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index segment,
+ uint32_t offset,
+ uint32_t size) {
+ if (name.empty()) {
+ return Result::Ok;
+ }
+ if (flags & WABT_SYMBOL_FLAG_UNDEFINED) {
+ // Refers to data in another file, `segment` not valid.
+ return Result::Ok;
+ }
+ if (offset) {
+ // If it is pointing into the data segment, then it's not really naming
+ // the whole segment.
+ return Result::Ok;
+ }
+ if (segment >= module_->data_segments.size()) {
+ PrintError("invalid data segment index: %" PRIindex, segment);
+ return Result::Error;
+ }
+ DataSegment* seg = module_->data_segments[segment];
+ std::string dollar_name =
+ GetUniqueName(&module_->data_segment_bindings, MakeDollarName(name));
+ seg->name = dollar_name;
+ module_->data_segment_bindings.emplace(dollar_name, Binding(segment));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnFunctionSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index func_index) {
+ if (name.empty()) {
+ return Result::Ok;
+ }
+ if (func_index >= module_->funcs.size()) {
+ PrintError("invalid function index: %" PRIindex, func_index);
+ return Result::Error;
+ }
+ Func* func = module_->funcs[func_index];
+ if (!func->name.empty()) {
+ // The name section has already named this function.
+ return Result::Ok;
+ }
+ std::string dollar_name =
+ GetUniqueName(&module_->func_bindings, MakeDollarName(name));
+ func->name = dollar_name;
+ module_->func_bindings.emplace(dollar_name, Binding(func_index));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnGlobalSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index global_index) {
+ return SetGlobalName(global_index, name);
+}
+
+Result BinaryReaderIR::OnSectionSymbol(Index index,
+ uint32_t flags,
+ Index section_index) {
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnTagSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index tag_index) {
+ if (name.empty()) {
+ return Result::Ok;
+ }
+ if (tag_index >= module_->tags.size()) {
+ PrintError("invalid tag index: %" PRIindex, tag_index);
+ return Result::Error;
+ }
+ Tag* tag = module_->tags[tag_index];
+ std::string dollar_name =
+ GetUniqueName(&module_->tag_bindings, MakeDollarName(name));
+ tag->name = dollar_name;
+ module_->tag_bindings.emplace(dollar_name, Binding(tag_index));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnTableSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index table_index) {
+ return SetTableName(table_index, name);
+}
+
+} // end anonymous namespace
+
+Result ReadBinaryIr(const char* filename,
+ const void* data,
+ size_t size,
+ const ReadBinaryOptions& options,
+ Errors* errors,
+ Module* out_module) {
+ BinaryReaderIR reader(out_module, filename, errors);
+ return ReadBinary(data, size, &reader, options);
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/binary-reader-logging.cc b/third_party/wasm2c/src/binary-reader-logging.cc
new file mode 100644
index 0000000000..83a14e9ff7
--- /dev/null
+++ b/third_party/wasm2c/src/binary-reader-logging.cc
@@ -0,0 +1,971 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/binary-reader-logging.h"
+
+#include <cinttypes>
+
+#include "wabt/stream.h"
+
+namespace wabt {
+
+#define INDENT_SIZE 2
+
+#define LOGF_NOINDENT(...) stream_->Writef(__VA_ARGS__)
+
+#define LOGF(...) \
+ do { \
+ WriteIndent(); \
+ LOGF_NOINDENT(__VA_ARGS__); \
+ } while (0)
+
+namespace {
+
+void SPrintLimits(char* dst, size_t size, const Limits* limits) {
+ int result;
+ if (limits->has_max) {
+ result = wabt_snprintf(dst, size, "initial: %" PRIu64 ", max: %" PRIu64,
+ limits->initial, limits->max);
+ } else {
+ result = wabt_snprintf(dst, size, "initial: %" PRIu64, limits->initial);
+ }
+ WABT_USE(result);
+ assert(static_cast<size_t>(result) < size);
+}
+
+} // end anonymous namespace
+
+BinaryReaderLogging::BinaryReaderLogging(Stream* stream,
+ BinaryReaderDelegate* forward)
+ : stream_(stream), reader_(forward), indent_(0) {}
+
+void BinaryReaderLogging::Indent() {
+ indent_ += INDENT_SIZE;
+}
+
+void BinaryReaderLogging::Dedent() {
+ indent_ -= INDENT_SIZE;
+ assert(indent_ >= 0);
+}
+
+void BinaryReaderLogging::WriteIndent() {
+ static char s_indent[] =
+ " "
+ " ";
+ static const size_t s_indent_len = sizeof(s_indent) - 1;
+ size_t i = indent_;
+ while (i > s_indent_len) {
+ stream_->WriteData(s_indent, s_indent_len);
+ i -= s_indent_len;
+ }
+ if (i > 0) {
+ stream_->WriteData(s_indent, indent_);
+ }
+}
+
+void BinaryReaderLogging::LogType(Type type) {
+ if (type.IsIndex()) {
+ LOGF_NOINDENT("typeidx[%d]", type.GetIndex());
+ } else {
+ LOGF_NOINDENT("%s", type.GetName().c_str());
+ }
+}
+
+void BinaryReaderLogging::LogTypes(Index type_count, Type* types) {
+ LOGF_NOINDENT("[");
+ for (Index i = 0; i < type_count; ++i) {
+ LogType(types[i]);
+ if (i != type_count - 1) {
+ LOGF_NOINDENT(", ");
+ }
+ }
+ LOGF_NOINDENT("]");
+}
+
+void BinaryReaderLogging::LogTypes(TypeVector& types) {
+ LogTypes(types.size(), types.data());
+}
+
+void BinaryReaderLogging::LogField(TypeMut field) {
+ if (field.mutable_) {
+ LOGF_NOINDENT("(mut ");
+ }
+ LogType(field.type);
+ if (field.mutable_) {
+ LOGF_NOINDENT(")");
+ }
+}
+
+bool BinaryReaderLogging::OnError(const Error& error) {
+ return reader_->OnError(error);
+}
+
+void BinaryReaderLogging::OnSetState(const State* s) {
+ BinaryReaderDelegate::OnSetState(s);
+ reader_->OnSetState(s);
+}
+
+Result BinaryReaderLogging::BeginModule(uint32_t version) {
+ LOGF("BeginModule(version: %u)\n", version);
+ Indent();
+ return reader_->BeginModule(version);
+}
+
+Result BinaryReaderLogging::BeginSection(Index section_index,
+ BinarySection section_type,
+ Offset size) {
+ return reader_->BeginSection(section_index, section_type, size);
+}
+
+Result BinaryReaderLogging::BeginCustomSection(Index section_index,
+ Offset size,
+ std::string_view section_name) {
+ LOGF("BeginCustomSection('" PRIstringview "', size: %" PRIzd ")\n",
+ WABT_PRINTF_STRING_VIEW_ARG(section_name), size);
+ Indent();
+ return reader_->BeginCustomSection(section_index, size, section_name);
+}
+
+Result BinaryReaderLogging::OnFuncType(Index index,
+ Index param_count,
+ Type* param_types,
+ Index result_count,
+ Type* result_types) {
+ LOGF("OnFuncType(index: %" PRIindex ", params: ", index);
+ LogTypes(param_count, param_types);
+ LOGF_NOINDENT(", results: ");
+ LogTypes(result_count, result_types);
+ LOGF_NOINDENT(")\n");
+ return reader_->OnFuncType(index, param_count, param_types, result_count,
+ result_types);
+}
+
+Result BinaryReaderLogging::OnStructType(Index index,
+ Index field_count,
+ TypeMut* fields) {
+ LOGF("OnStructType(index: %" PRIindex ", fields: ", index);
+ LOGF_NOINDENT("[");
+ for (Index i = 0; i < field_count; ++i) {
+ LogField(fields[i]);
+ if (i != field_count - 1) {
+ LOGF_NOINDENT(", ");
+ }
+ }
+ LOGF_NOINDENT("])\n");
+ return reader_->OnStructType(index, field_count, fields);
+}
+
+Result BinaryReaderLogging::OnArrayType(Index index, TypeMut field) {
+ LOGF("OnArrayType(index: %" PRIindex ", field: ", index);
+ LogField(field);
+ LOGF_NOINDENT(")\n");
+ return reader_->OnArrayType(index, field);
+}
+
+Result BinaryReaderLogging::OnImport(Index index,
+ ExternalKind kind,
+ std::string_view module_name,
+ std::string_view field_name) {
+ LOGF("OnImport(index: %" PRIindex ", kind: %s, module: \"" PRIstringview
+ "\", field: \"" PRIstringview "\")\n",
+ index, GetKindName(kind), WABT_PRINTF_STRING_VIEW_ARG(module_name),
+ WABT_PRINTF_STRING_VIEW_ARG(field_name));
+ return reader_->OnImport(index, kind, module_name, field_name);
+}
+
+Result BinaryReaderLogging::OnImportFunc(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index func_index,
+ Index sig_index) {
+ LOGF("OnImportFunc(import_index: %" PRIindex ", func_index: %" PRIindex
+ ", sig_index: %" PRIindex ")\n",
+ import_index, func_index, sig_index);
+ return reader_->OnImportFunc(import_index, module_name, field_name,
+ func_index, sig_index);
+}
+
+Result BinaryReaderLogging::OnImportTable(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index table_index,
+ Type elem_type,
+ const Limits* elem_limits) {
+ char buf[100];
+ SPrintLimits(buf, sizeof(buf), elem_limits);
+ LOGF("OnImportTable(import_index: %" PRIindex ", table_index: %" PRIindex
+ ", elem_type: %s, %s)\n",
+ import_index, table_index, elem_type.GetName().c_str(), buf);
+ return reader_->OnImportTable(import_index, module_name, field_name,
+ table_index, elem_type, elem_limits);
+}
+
+Result BinaryReaderLogging::OnImportMemory(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index memory_index,
+ const Limits* page_limits) {
+ char buf[100];
+ SPrintLimits(buf, sizeof(buf), page_limits);
+ LOGF("OnImportMemory(import_index: %" PRIindex ", memory_index: %" PRIindex
+ ", %s)\n",
+ import_index, memory_index, buf);
+ return reader_->OnImportMemory(import_index, module_name, field_name,
+ memory_index, page_limits);
+}
+
+Result BinaryReaderLogging::OnImportGlobal(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index global_index,
+ Type type,
+ bool mutable_) {
+ LOGF("OnImportGlobal(import_index: %" PRIindex ", global_index: %" PRIindex
+ ", type: %s, mutable: "
+ "%s)\n",
+ import_index, global_index, type.GetName().c_str(),
+ mutable_ ? "true" : "false");
+ return reader_->OnImportGlobal(import_index, module_name, field_name,
+ global_index, type, mutable_);
+}
+
+Result BinaryReaderLogging::OnImportTag(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index tag_index,
+ Index sig_index) {
+ LOGF("OnImportTag(import_index: %" PRIindex ", tag_index: %" PRIindex
+ ", sig_index: %" PRIindex ")\n",
+ import_index, tag_index, sig_index);
+ return reader_->OnImportTag(import_index, module_name, field_name, tag_index,
+ sig_index);
+}
+
+Result BinaryReaderLogging::OnTable(Index index,
+ Type elem_type,
+ const Limits* elem_limits) {
+ char buf[100];
+ SPrintLimits(buf, sizeof(buf), elem_limits);
+ LOGF("OnTable(index: %" PRIindex ", elem_type: %s, %s)\n", index,
+ elem_type.GetName().c_str(), buf);
+ return reader_->OnTable(index, elem_type, elem_limits);
+}
+
+Result BinaryReaderLogging::OnMemory(Index index, const Limits* page_limits) {
+ char buf[100];
+ SPrintLimits(buf, sizeof(buf), page_limits);
+ LOGF("OnMemory(index: %" PRIindex ", %s)\n", index, buf);
+ return reader_->OnMemory(index, page_limits);
+}
+
+Result BinaryReaderLogging::BeginGlobal(Index index, Type type, bool mutable_) {
+ LOGF("BeginGlobal(index: %" PRIindex ", type: %s, mutable: %s)\n", index,
+ type.GetName().c_str(), mutable_ ? "true" : "false");
+ return reader_->BeginGlobal(index, type, mutable_);
+}
+
+Result BinaryReaderLogging::OnExport(Index index,
+ ExternalKind kind,
+ Index item_index,
+ std::string_view name) {
+ LOGF("OnExport(index: %" PRIindex ", kind: %s, item_index: %" PRIindex
+ ", name: \"" PRIstringview "\")\n",
+ index, GetKindName(kind), item_index, WABT_PRINTF_STRING_VIEW_ARG(name));
+ return reader_->OnExport(index, kind, item_index, name);
+}
+
+Result BinaryReaderLogging::BeginFunctionBody(Index value, Offset size) {
+ LOGF("BeginFunctionBody(%" PRIindex ", size:%" PRIzd ")\n", value, size);
+ return reader_->BeginFunctionBody(value, size);
+}
+
+Result BinaryReaderLogging::OnLocalDecl(Index decl_index,
+ Index count,
+ Type type) {
+ LOGF("OnLocalDecl(index: %" PRIindex ", count: %" PRIindex ", type: %s)\n",
+ decl_index, count, type.GetName().c_str());
+ return reader_->OnLocalDecl(decl_index, count, type);
+}
+
+Result BinaryReaderLogging::OnBlockExpr(Type sig_type) {
+ LOGF("OnBlockExpr(sig: ");
+ LogType(sig_type);
+ LOGF_NOINDENT(")\n");
+ return reader_->OnBlockExpr(sig_type);
+}
+
+Result BinaryReaderLogging::OnBrExpr(Index depth) {
+ LOGF("OnBrExpr(depth: %" PRIindex ")\n", depth);
+ return reader_->OnBrExpr(depth);
+}
+
+Result BinaryReaderLogging::OnBrIfExpr(Index depth) {
+ LOGF("OnBrIfExpr(depth: %" PRIindex ")\n", depth);
+ return reader_->OnBrIfExpr(depth);
+}
+
+Result BinaryReaderLogging::OnBrTableExpr(Index num_targets,
+ Index* target_depths,
+ Index default_target_depth) {
+ LOGF("OnBrTableExpr(num_targets: %" PRIindex ", depths: [", num_targets);
+ for (Index i = 0; i < num_targets; ++i) {
+ LOGF_NOINDENT("%" PRIindex, target_depths[i]);
+ if (i != num_targets - 1) {
+ LOGF_NOINDENT(", ");
+ }
+ }
+ LOGF_NOINDENT("], default: %" PRIindex ")\n", default_target_depth);
+ return reader_->OnBrTableExpr(num_targets, target_depths,
+ default_target_depth);
+}
+
+Result BinaryReaderLogging::OnF32ConstExpr(uint32_t value_bits) {
+ float value;
+ memcpy(&value, &value_bits, sizeof(value));
+ LOGF("OnF32ConstExpr(%g (0x%08x))\n", value, value_bits);
+ return reader_->OnF32ConstExpr(value_bits);
+}
+
+Result BinaryReaderLogging::OnF64ConstExpr(uint64_t value_bits) {
+ double value;
+ memcpy(&value, &value_bits, sizeof(value));
+ LOGF("OnF64ConstExpr(%g (0x%016" PRIx64 "))\n", value, value_bits);
+ return reader_->OnF64ConstExpr(value_bits);
+}
+
+Result BinaryReaderLogging::OnV128ConstExpr(v128 value_bits) {
+ LOGF("OnV128ConstExpr(0x%08x 0x%08x 0x%08x 0x%08x)\n", value_bits.u32(0),
+ value_bits.u32(1), value_bits.u32(2), value_bits.u32(3));
+ return reader_->OnV128ConstExpr(value_bits);
+}
+
+Result BinaryReaderLogging::OnI32ConstExpr(uint32_t value) {
+ LOGF("OnI32ConstExpr(%u (0x%x))\n", value, value);
+ return reader_->OnI32ConstExpr(value);
+}
+
+Result BinaryReaderLogging::OnI64ConstExpr(uint64_t value) {
+ LOGF("OnI64ConstExpr(%" PRIu64 " (0x%" PRIx64 "))\n", value, value);
+ return reader_->OnI64ConstExpr(value);
+}
+
+Result BinaryReaderLogging::OnIfExpr(Type sig_type) {
+ LOGF("OnIfExpr(sig: ");
+ LogType(sig_type);
+ LOGF_NOINDENT(")\n");
+ return reader_->OnIfExpr(sig_type);
+}
+
+Result BinaryReaderLogging::OnLoopExpr(Type sig_type) {
+ LOGF("OnLoopExpr(sig: ");
+ LogType(sig_type);
+ LOGF_NOINDENT(")\n");
+ return reader_->OnLoopExpr(sig_type);
+}
+
+Result BinaryReaderLogging::OnSelectExpr(Index result_count,
+ Type* result_types) {
+ LOGF("OnSelectExpr(return_type: ");
+ LogTypes(result_count, result_types);
+ LOGF_NOINDENT(")\n");
+ return reader_->OnSelectExpr(result_count, result_types);
+}
+
+Result BinaryReaderLogging::OnTryExpr(Type sig_type) {
+ LOGF("OnTryExpr(sig: ");
+ LogType(sig_type);
+ LOGF_NOINDENT(")\n");
+ return reader_->OnTryExpr(sig_type);
+}
+
+Result BinaryReaderLogging::OnSimdLaneOpExpr(Opcode opcode, uint64_t value) {
+ LOGF("OnSimdLaneOpExpr (lane: %" PRIu64 ")\n", value);
+ return reader_->OnSimdLaneOpExpr(opcode, value);
+}
+
+Result BinaryReaderLogging::OnSimdShuffleOpExpr(Opcode opcode, v128 value) {
+ LOGF("OnSimdShuffleOpExpr (lane: 0x%08x %08x %08x %08x)\n", value.u32(0),
+ value.u32(1), value.u32(2), value.u32(3));
+ return reader_->OnSimdShuffleOpExpr(opcode, value);
+}
+
+Result BinaryReaderLogging::BeginElemSegment(Index index,
+ Index table_index,
+ uint8_t flags) {
+ LOGF("BeginElemSegment(index: %" PRIindex ", table_index: %" PRIindex
+ ", flags: %d)\n",
+ index, table_index, flags);
+ return reader_->BeginElemSegment(index, table_index, flags);
+}
+
+Result BinaryReaderLogging::OnElemSegmentElemType(Index index, Type elem_type) {
+ LOGF("OnElemSegmentElemType(index: %" PRIindex ", type: %s)\n", index,
+ elem_type.GetName().c_str());
+ return reader_->OnElemSegmentElemType(index, elem_type);
+}
+
+Result BinaryReaderLogging::OnDataSegmentData(Index index,
+ const void* data,
+ Address size) {
+ LOGF("OnDataSegmentData(index:%" PRIindex ", size:%" PRIaddress ")\n", index,
+ size);
+ return reader_->OnDataSegmentData(index, data, size);
+}
+
+Result BinaryReaderLogging::OnModuleNameSubsection(Index index,
+ uint32_t name_type,
+ Offset subsection_size) {
+ LOGF("OnModuleNameSubsection(index:%" PRIindex ", nametype:%u, size:%" PRIzd
+ ")\n",
+ index, name_type, subsection_size);
+ return reader_->OnModuleNameSubsection(index, name_type, subsection_size);
+}
+
+Result BinaryReaderLogging::OnModuleName(std::string_view name) {
+ LOGF("OnModuleName(name: \"" PRIstringview "\")\n",
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ return reader_->OnModuleName(name);
+}
+
+Result BinaryReaderLogging::OnFunctionNameSubsection(Index index,
+ uint32_t name_type,
+ Offset subsection_size) {
+ LOGF("OnFunctionNameSubsection(index:%" PRIindex ", nametype:%u, size:%" PRIzd
+ ")\n",
+ index, name_type, subsection_size);
+ return reader_->OnFunctionNameSubsection(index, name_type, subsection_size);
+}
+
+Result BinaryReaderLogging::OnFunctionName(Index index, std::string_view name) {
+ LOGF("OnFunctionName(index: %" PRIindex ", name: \"" PRIstringview "\")\n",
+ index, WABT_PRINTF_STRING_VIEW_ARG(name));
+ return reader_->OnFunctionName(index, name);
+}
+
+Result BinaryReaderLogging::OnLocalNameSubsection(Index index,
+ uint32_t name_type,
+ Offset subsection_size) {
+ LOGF("OnLocalNameSubsection(index:%" PRIindex ", nametype:%u, size:%" PRIzd
+ ")\n",
+ index, name_type, subsection_size);
+ return reader_->OnLocalNameSubsection(index, name_type, subsection_size);
+}
+
+Result BinaryReaderLogging::OnLocalName(Index func_index,
+ Index local_index,
+ std::string_view name) {
+ LOGF("OnLocalName(func_index: %" PRIindex ", local_index: %" PRIindex
+ ", name: \"" PRIstringview "\")\n",
+ func_index, local_index, WABT_PRINTF_STRING_VIEW_ARG(name));
+ return reader_->OnLocalName(func_index, local_index, name);
+}
+
+Result BinaryReaderLogging::OnNameSubsection(
+ Index index,
+ NameSectionSubsection subsection_type,
+ Offset subsection_size) {
+ LOGF("OnNameSubsection(index: %" PRIindex ", type: %s, size:%" PRIzd ")\n",
+ index, GetNameSectionSubsectionName(subsection_type), subsection_size);
+ return reader_->OnNameSubsection(index, subsection_type, subsection_size);
+}
+
+Result BinaryReaderLogging::OnNameEntry(NameSectionSubsection type,
+ Index index,
+ std::string_view name) {
+ LOGF("OnNameEntry(type: %s, index: %" PRIindex ", name: \"" PRIstringview
+ "\")\n",
+ GetNameSectionSubsectionName(type), index,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ return reader_->OnNameEntry(type, index, name);
+}
+
+Result BinaryReaderLogging::OnDylinkInfo(uint32_t mem_size,
+ uint32_t mem_align,
+ uint32_t table_size,
+ uint32_t table_align) {
+ LOGF(
+ "OnDylinkInfo(mem_size: %u, mem_align: %u, table_size: %u, table_align: "
+ "%u)\n",
+ mem_size, mem_align, table_size, table_align);
+ return reader_->OnDylinkInfo(mem_size, mem_align, table_size, table_align);
+}
+
+Result BinaryReaderLogging::OnDylinkNeeded(std::string_view so_name) {
+ LOGF("OnDylinkNeeded(name: " PRIstringview ")\n",
+ WABT_PRINTF_STRING_VIEW_ARG(so_name));
+ return reader_->OnDylinkNeeded(so_name);
+}
+
+Result BinaryReaderLogging::OnDylinkExport(std::string_view name,
+ uint32_t flags) {
+ LOGF("OnDylinkExport(name: " PRIstringview ", flags: 0x%x)\n",
+ WABT_PRINTF_STRING_VIEW_ARG(name), flags);
+ return reader_->OnDylinkExport(name, flags);
+}
+
+Result BinaryReaderLogging::OnDylinkImport(std::string_view module,
+ std::string_view name,
+ uint32_t flags) {
+ LOGF("OnDylinkImport(module: " PRIstringview ", name: " PRIstringview
+ ", flags: 0x%x)\n",
+ WABT_PRINTF_STRING_VIEW_ARG(module), WABT_PRINTF_STRING_VIEW_ARG(name),
+ flags);
+ return reader_->OnDylinkImport(module, name, flags);
+}
+
+Result BinaryReaderLogging::OnRelocCount(Index count, Index section_index) {
+ LOGF("OnRelocCount(count: %" PRIindex ", section: %" PRIindex ")\n", count,
+ section_index);
+ return reader_->OnRelocCount(count, section_index);
+}
+
+Result BinaryReaderLogging::OnReloc(RelocType type,
+ Offset offset,
+ Index index,
+ uint32_t addend) {
+ int32_t signed_addend = static_cast<int32_t>(addend);
+ LOGF("OnReloc(type: %s, offset: %" PRIzd ", index: %" PRIindex
+ ", addend: %d)\n",
+ GetRelocTypeName(type), offset, index, signed_addend);
+ return reader_->OnReloc(type, offset, index, addend);
+}
+
+Result BinaryReaderLogging::OnFeature(uint8_t prefix, std::string_view name) {
+ LOGF("OnFeature(prefix: '%c', name: '" PRIstringview "')\n", prefix,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ return reader_->OnFeature(prefix, name);
+}
+
+Result BinaryReaderLogging::OnDataSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index segment,
+ uint32_t offset,
+ uint32_t size) {
+ LOGF("OnDataSymbol(name: " PRIstringview " flags: 0x%x)\n",
+ WABT_PRINTF_STRING_VIEW_ARG(name), flags);
+ return reader_->OnDataSymbol(index, flags, name, segment, offset, size);
+}
+
+Result BinaryReaderLogging::OnFunctionSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index func_index) {
+ LOGF("OnFunctionSymbol(name: " PRIstringview " flags: 0x%x index: %" PRIindex
+ ")\n",
+ WABT_PRINTF_STRING_VIEW_ARG(name), flags, func_index);
+ return reader_->OnFunctionSymbol(index, flags, name, func_index);
+}
+
+Result BinaryReaderLogging::OnGlobalSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index global_index) {
+ LOGF("OnGlobalSymbol(name: " PRIstringview " flags: 0x%x index: %" PRIindex
+ ")\n",
+ WABT_PRINTF_STRING_VIEW_ARG(name), flags, global_index);
+ return reader_->OnGlobalSymbol(index, flags, name, global_index);
+}
+
+Result BinaryReaderLogging::OnSectionSymbol(Index index,
+ uint32_t flags,
+ Index section_index) {
+ LOGF("OnSectionSymbol(flags: 0x%x index: %" PRIindex ")\n", flags,
+ section_index);
+ return reader_->OnSectionSymbol(index, flags, section_index);
+}
+
+Result BinaryReaderLogging::OnTagSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index tag_index) {
+ LOGF("OnTagSymbol(name: " PRIstringview " flags: 0x%x index: %" PRIindex
+ ")\n",
+ WABT_PRINTF_STRING_VIEW_ARG(name), flags, tag_index);
+ return reader_->OnTagSymbol(index, flags, name, tag_index);
+}
+
+Result BinaryReaderLogging::OnTableSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index table_index) {
+ LOGF("OnTableSymbol(name: " PRIstringview " flags: 0x%x index: %" PRIindex
+ ")\n",
+ WABT_PRINTF_STRING_VIEW_ARG(name), flags, table_index);
+ return reader_->OnTableSymbol(index, flags, name, table_index);
+}
+
+Result BinaryReaderLogging::OnSegmentInfo(Index index,
+ std::string_view name,
+ Address alignment,
+ uint32_t flags) {
+ LOGF("OnSegmentInfo(%d name: " PRIstringview ", alignment: %" PRIaddress
+ ", flags: 0x%x)\n",
+ index, WABT_PRINTF_STRING_VIEW_ARG(name), alignment, flags);
+ return reader_->OnSegmentInfo(index, name, alignment, flags);
+}
+
+Result BinaryReaderLogging::OnInitFunction(uint32_t priority,
+ Index symbol_index) {
+ LOGF("OnInitFunction(%d priority: %d)\n", symbol_index, priority);
+ return reader_->OnInitFunction(priority, symbol_index);
+}
+
+Result BinaryReaderLogging::OnComdatBegin(std::string_view name,
+ uint32_t flags,
+ Index count) {
+ LOGF("OnComdatBegin(" PRIstringview ", flags: %d, count: %" PRIindex ")\n",
+ WABT_PRINTF_STRING_VIEW_ARG(name), flags, count);
+ return reader_->OnComdatBegin(name, flags, count);
+}
+
+Result BinaryReaderLogging::OnComdatEntry(ComdatType kind, Index index) {
+ LOGF("OnComdatEntry(kind: %d, index: %" PRIindex ")\n",
+ static_cast<int>(kind), 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); \
+ Indent(); \
+ return reader_->name(size); \
+ }
+
+#define DEFINE_END(name) \
+ Result BinaryReaderLogging::name() { \
+ Dedent(); \
+ LOGF(#name "\n"); \
+ return reader_->name(); \
+ }
+
+#define DEFINE_INDEX(name) \
+ Result BinaryReaderLogging::name(Index value) { \
+ LOGF(#name "(%" PRIindex ")\n", value); \
+ return reader_->name(value); \
+ }
+
+#define DEFINE_TYPE(name) \
+ Result BinaryReaderLogging::name(Type type) { \
+ LOGF(#name "(%s)\n", type.GetName().c_str()); \
+ return reader_->name(type); \
+ }
+
+#define DEFINE_INDEX_DESC(name, desc) \
+ Result BinaryReaderLogging::name(Index value) { \
+ LOGF(#name "(" desc ": %" PRIindex ")\n", value); \
+ return reader_->name(value); \
+ }
+
+#define DEFINE_INDEX_TYPE(name) \
+ Result BinaryReaderLogging::name(Index value, Type type) { \
+ LOGF(#name "(index: %" PRIindex ", type: %s)\n", value, \
+ type.GetName().c_str()); \
+ return reader_->name(value, type); \
+ }
+
+#define DEFINE_INDEX_INDEX(name, desc0, desc1) \
+ Result BinaryReaderLogging::name(Index value0, Index value1) { \
+ LOGF(#name "(" desc0 ": %" PRIindex ", " desc1 ": %" PRIindex ")\n", \
+ value0, value1); \
+ return reader_->name(value0, value1); \
+ }
+
+#define DEFINE_INDEX_INDEX_U8(name, desc0, desc1, desc2) \
+ Result BinaryReaderLogging::name(Index value0, Index value1, \
+ uint8_t value2) { \
+ LOGF(#name "(" desc0 ": %" PRIindex ", " desc1 ": %" PRIindex ", " desc2 \
+ ": %d)\n", \
+ value0, value1, value2); \
+ return reader_->name(value0, value1, value2); \
+ }
+
+#define DEFINE_OPCODE(name) \
+ Result BinaryReaderLogging::name(Opcode opcode) { \
+ LOGF(#name "(\"%s\" (%u))\n", opcode.GetName(), opcode.GetCode()); \
+ return reader_->name(opcode); \
+ }
+
+#define DEFINE_LOAD_STORE_OPCODE(name) \
+ Result BinaryReaderLogging::name(Opcode opcode, Index memidx, \
+ Address alignment_log2, Address offset) { \
+ LOGF(#name "(opcode: \"%s\" (%u), memidx: %" PRIindex \
+ ", align log2: %" PRIaddress ", offset: %" PRIaddress ")\n", \
+ opcode.GetName(), opcode.GetCode(), memidx, alignment_log2, offset); \
+ return reader_->name(opcode, memidx, alignment_log2, offset); \
+ }
+
+#define DEFINE_SIMD_LOAD_STORE_LANE_OPCODE(name) \
+ Result BinaryReaderLogging::name(Opcode opcode, Index memidx, \
+ Address alignment_log2, Address offset, \
+ uint64_t value) { \
+ LOGF(#name "(opcode: \"%s\" (%u), memidx: %" PRIindex \
+ ", align log2: %" PRIaddress ", offset: %" PRIaddress \
+ ", lane: %" PRIu64 ")\n", \
+ opcode.GetName(), opcode.GetCode(), memidx, alignment_log2, offset, \
+ value); \
+ return reader_->name(opcode, memidx, alignment_log2, offset, value); \
+ }
+
+#define DEFINE0(name) \
+ Result BinaryReaderLogging::name() { \
+ LOGF(#name "\n"); \
+ return reader_->name(); \
+ }
+
+DEFINE_END(EndModule)
+
+DEFINE_END(EndCustomSection)
+
+DEFINE_BEGIN(BeginTypeSection)
+DEFINE_INDEX(OnTypeCount)
+DEFINE_END(EndTypeSection)
+
+DEFINE_BEGIN(BeginImportSection)
+DEFINE_INDEX(OnImportCount)
+DEFINE_END(EndImportSection)
+
+DEFINE_BEGIN(BeginFunctionSection)
+DEFINE_INDEX(OnFunctionCount)
+DEFINE_INDEX_INDEX(OnFunction, "index", "sig_index")
+DEFINE_END(EndFunctionSection)
+
+DEFINE_BEGIN(BeginTableSection)
+DEFINE_INDEX(OnTableCount)
+DEFINE_END(EndTableSection)
+
+DEFINE_BEGIN(BeginMemorySection)
+DEFINE_INDEX(OnMemoryCount)
+DEFINE_END(EndMemorySection)
+
+DEFINE_BEGIN(BeginGlobalSection)
+DEFINE_INDEX(OnGlobalCount)
+DEFINE_INDEX(BeginGlobalInitExpr)
+DEFINE_INDEX(EndGlobalInitExpr)
+DEFINE_INDEX(EndGlobal)
+DEFINE_END(EndGlobalSection)
+
+DEFINE_BEGIN(BeginExportSection)
+DEFINE_INDEX(OnExportCount)
+DEFINE_END(EndExportSection)
+
+DEFINE_BEGIN(BeginStartSection)
+DEFINE_INDEX(OnStartFunction)
+DEFINE_END(EndStartSection)
+
+DEFINE_BEGIN(BeginCodeSection)
+DEFINE_INDEX(OnFunctionBodyCount)
+DEFINE_INDEX(EndFunctionBody)
+DEFINE_INDEX(OnLocalDeclCount)
+DEFINE_LOAD_STORE_OPCODE(OnAtomicLoadExpr);
+DEFINE_LOAD_STORE_OPCODE(OnAtomicRmwExpr);
+DEFINE_LOAD_STORE_OPCODE(OnAtomicRmwCmpxchgExpr);
+DEFINE_LOAD_STORE_OPCODE(OnAtomicStoreExpr);
+DEFINE_LOAD_STORE_OPCODE(OnAtomicWaitExpr);
+DEFINE_INDEX_DESC(OnAtomicFenceExpr, "consistency_model");
+DEFINE_LOAD_STORE_OPCODE(OnAtomicNotifyExpr);
+DEFINE_OPCODE(OnBinaryExpr)
+DEFINE_INDEX_DESC(OnCallExpr, "func_index")
+DEFINE_INDEX_INDEX(OnCallIndirectExpr, "sig_index", "table_index")
+DEFINE0(OnCallRefExpr)
+DEFINE_INDEX_DESC(OnCatchExpr, "tag_index");
+DEFINE0(OnCatchAllExpr);
+DEFINE_OPCODE(OnCompareExpr)
+DEFINE_OPCODE(OnConvertExpr)
+DEFINE_INDEX_DESC(OnDelegateExpr, "depth");
+DEFINE0(OnDropExpr)
+DEFINE0(OnElseExpr)
+DEFINE0(OnEndExpr)
+DEFINE_INDEX_DESC(OnGlobalGetExpr, "index")
+DEFINE_INDEX_DESC(OnGlobalSetExpr, "index")
+DEFINE_LOAD_STORE_OPCODE(OnLoadExpr);
+DEFINE_INDEX_DESC(OnLocalGetExpr, "index")
+DEFINE_INDEX_DESC(OnLocalSetExpr, "index")
+DEFINE_INDEX_DESC(OnLocalTeeExpr, "index")
+DEFINE_INDEX_INDEX(OnMemoryCopyExpr, "src_memory_index", "dest_memory_index")
+DEFINE_INDEX(OnDataDropExpr)
+DEFINE_INDEX(OnMemoryFillExpr)
+DEFINE_INDEX(OnMemoryGrowExpr)
+DEFINE_INDEX_INDEX(OnMemoryInitExpr, "segment_index", "memory_index")
+DEFINE_INDEX(OnMemorySizeExpr)
+DEFINE_INDEX_INDEX(OnTableCopyExpr, "dst_index", "src_index")
+DEFINE_INDEX(OnElemDropExpr)
+DEFINE_INDEX_INDEX(OnTableInitExpr, "segment_index", "table_index")
+DEFINE_INDEX(OnTableSetExpr)
+DEFINE_INDEX(OnTableGetExpr)
+DEFINE_INDEX(OnTableGrowExpr)
+DEFINE_INDEX(OnTableSizeExpr)
+DEFINE_INDEX_DESC(OnTableFillExpr, "table index")
+DEFINE_INDEX(OnRefFuncExpr)
+DEFINE_TYPE(OnRefNullExpr)
+DEFINE0(OnRefIsNullExpr)
+DEFINE0(OnNopExpr)
+DEFINE_INDEX_DESC(OnRethrowExpr, "depth");
+DEFINE_INDEX_DESC(OnReturnCallExpr, "func_index")
+
+DEFINE_INDEX_INDEX(OnReturnCallIndirectExpr, "sig_index", "table_index")
+DEFINE0(OnReturnExpr)
+DEFINE_LOAD_STORE_OPCODE(OnLoadSplatExpr);
+DEFINE_LOAD_STORE_OPCODE(OnLoadZeroExpr);
+DEFINE_LOAD_STORE_OPCODE(OnStoreExpr);
+DEFINE_INDEX_DESC(OnThrowExpr, "tag_index")
+DEFINE0(OnUnreachableExpr)
+DEFINE_OPCODE(OnUnaryExpr)
+DEFINE_OPCODE(OnTernaryExpr)
+DEFINE_SIMD_LOAD_STORE_LANE_OPCODE(OnSimdLoadLaneExpr);
+DEFINE_SIMD_LOAD_STORE_LANE_OPCODE(OnSimdStoreLaneExpr);
+DEFINE_END(EndCodeSection)
+
+DEFINE_BEGIN(BeginElemSection)
+DEFINE_INDEX(OnElemSegmentCount)
+DEFINE_INDEX(BeginElemSegmentInitExpr)
+DEFINE_INDEX(EndElemSegmentInitExpr)
+DEFINE_INDEX_INDEX(OnElemSegmentElemExprCount, "index", "count")
+DEFINE_INDEX_TYPE(OnElemSegmentElemExpr_RefNull)
+DEFINE_INDEX_INDEX(OnElemSegmentElemExpr_RefFunc, "index", "func_index")
+DEFINE_INDEX(EndElemSegment)
+DEFINE_END(EndElemSection)
+
+DEFINE_BEGIN(BeginDataSection)
+DEFINE_INDEX(OnDataSegmentCount)
+DEFINE_INDEX_INDEX_U8(BeginDataSegment, "index", "memory_index", "flags")
+DEFINE_INDEX(BeginDataSegmentInitExpr)
+DEFINE_INDEX(EndDataSegmentInitExpr)
+DEFINE_INDEX(EndDataSegment)
+DEFINE_END(EndDataSection)
+
+DEFINE_BEGIN(BeginDataCountSection)
+DEFINE_INDEX(OnDataCount)
+DEFINE_END(EndDataCountSection)
+
+DEFINE_BEGIN(BeginNamesSection)
+DEFINE_INDEX(OnFunctionNamesCount)
+DEFINE_INDEX(OnLocalNameFunctionCount)
+DEFINE_INDEX_INDEX(OnLocalNameLocalCount, "index", "count")
+DEFINE_INDEX(OnNameCount);
+DEFINE_END(EndNamesSection)
+
+DEFINE_BEGIN(BeginRelocSection)
+DEFINE_END(EndRelocSection)
+
+DEFINE_BEGIN(BeginDylinkSection)
+DEFINE_INDEX(OnDylinkNeededCount)
+DEFINE_INDEX(OnDylinkExportCount)
+DEFINE_INDEX(OnDylinkImportCount)
+DEFINE_END(EndDylinkSection)
+
+DEFINE_BEGIN(BeginTargetFeaturesSection)
+DEFINE_INDEX(OnFeatureCount)
+DEFINE_END(EndTargetFeaturesSection)
+
+DEFINE_BEGIN(BeginLinkingSection)
+DEFINE_INDEX(OnSymbolCount)
+DEFINE_INDEX(OnSegmentInfoCount)
+DEFINE_INDEX(OnInitFunctionCount)
+DEFINE_INDEX(OnComdatCount)
+DEFINE_END(EndLinkingSection)
+
+DEFINE_BEGIN(BeginTagSection);
+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) {
+ return reader_->OnOpcode(opcode);
+}
+
+Result BinaryReaderLogging::OnOpcodeBare() {
+ return reader_->OnOpcodeBare();
+}
+
+Result BinaryReaderLogging::OnOpcodeIndex(Index value) {
+ return reader_->OnOpcodeIndex(value);
+}
+
+Result BinaryReaderLogging::OnOpcodeIndexIndex(Index value, Index value2) {
+ return reader_->OnOpcodeIndexIndex(value, value2);
+}
+
+Result BinaryReaderLogging::OnOpcodeUint32(uint32_t value) {
+ return reader_->OnOpcodeUint32(value);
+}
+
+Result BinaryReaderLogging::OnOpcodeUint32Uint32(uint32_t value,
+ uint32_t value2) {
+ return reader_->OnOpcodeUint32Uint32(value, value2);
+}
+
+Result BinaryReaderLogging::OnOpcodeUint32Uint32Uint32(uint32_t value,
+ uint32_t value2,
+ uint32_t value3) {
+ return reader_->OnOpcodeUint32Uint32Uint32(value, value2, value3);
+}
+
+Result BinaryReaderLogging::OnOpcodeUint32Uint32Uint32Uint32(uint32_t value,
+ uint32_t value2,
+ uint32_t value3,
+ uint32_t value4) {
+ return reader_->OnOpcodeUint32Uint32Uint32Uint32(value, value2, value3,
+ value4);
+}
+
+Result BinaryReaderLogging::OnOpcodeUint64(uint64_t value) {
+ return reader_->OnOpcodeUint64(value);
+}
+
+Result BinaryReaderLogging::OnOpcodeF32(uint32_t value) {
+ return reader_->OnOpcodeF32(value);
+}
+
+Result BinaryReaderLogging::OnOpcodeF64(uint64_t value) {
+ return reader_->OnOpcodeF64(value);
+}
+
+Result BinaryReaderLogging::OnOpcodeV128(v128 value) {
+ return reader_->OnOpcodeV128(value);
+}
+
+Result BinaryReaderLogging::OnOpcodeBlockSig(Type sig_type) {
+ return reader_->OnOpcodeBlockSig(sig_type);
+}
+
+Result BinaryReaderLogging::OnOpcodeType(Type type) {
+ return reader_->OnOpcodeType(type);
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/binary-reader-objdump.cc b/third_party/wasm2c/src/binary-reader-objdump.cc
new file mode 100644
index 0000000000..96ece0689d
--- /dev/null
+++ b/third_party/wasm2c/src/binary-reader-objdump.cc
@@ -0,0 +1,2408 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/binary-reader-objdump.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cinttypes>
+#include <cstdio>
+#include <cstring>
+#include <vector>
+
+#if HAVE_STRCASECMP
+#include <strings.h>
+#endif
+
+#include "wabt/binary-reader-nop.h"
+#include "wabt/filenames.h"
+#include "wabt/literal.h"
+#include "wabt/string-util.h"
+
+namespace wabt {
+
+namespace {
+
+class BinaryReaderObjdumpBase : public BinaryReaderNop {
+ public:
+ BinaryReaderObjdumpBase(const uint8_t* data,
+ size_t size,
+ ObjdumpOptions* options,
+ ObjdumpState* state);
+
+ bool OnError(const Error&) override;
+
+ Result BeginModule(uint32_t version) override;
+ Result BeginSection(Index section_index,
+ BinarySection section_type,
+ Offset size) override;
+
+ Result OnOpcode(Opcode Opcode) override;
+ Result OnRelocCount(Index count, Index section_index) override;
+
+ protected:
+ std::string_view GetTypeName(Index index) const;
+ std::string_view GetFunctionName(Index index) const;
+ std::string_view GetGlobalName(Index index) const;
+ std::string_view GetLocalName(Index function_index, Index local_index) const;
+ std::string_view GetSectionName(Index index) const;
+ std::string_view GetTagName(Index index) const;
+ std::string_view GetSymbolName(Index index) const;
+ std::string_view GetSegmentName(Index index) const;
+ std::string_view GetTableName(Index index) const;
+ void PrintRelocation(const Reloc& reloc, Offset offset) const;
+ Offset GetPrintOffset(Offset offset) const;
+ Offset GetSectionStart(BinarySection section_code) const {
+ return section_starts_[static_cast<size_t>(section_code)];
+ }
+
+ ObjdumpOptions* options_;
+ ObjdumpState* objdump_state_;
+ const uint8_t* data_;
+ size_t size_;
+ bool print_details_ = false;
+ BinarySection reloc_section_ = BinarySection::Invalid;
+ Offset section_starts_[kBinarySectionCount];
+ // Map of section index to section type
+ std::vector<BinarySection> section_types_;
+ bool section_found_ = false;
+ std::string module_name_;
+ Opcode current_opcode = Opcode::Unreachable;
+
+ std::unique_ptr<FileStream> err_stream_;
+};
+
+BinaryReaderObjdumpBase::BinaryReaderObjdumpBase(const uint8_t* data,
+ size_t size,
+ ObjdumpOptions* options,
+ ObjdumpState* objdump_state)
+ : options_(options),
+ objdump_state_(objdump_state),
+ data_(data),
+ size_(size),
+ err_stream_(FileStream::CreateStderr()) {
+ ZeroMemory(section_starts_);
+}
+
+Result BinaryReaderObjdumpBase::BeginSection(Index section_index,
+ BinarySection section_code,
+ Offset size) {
+ section_starts_[static_cast<size_t>(section_code)] = state->offset;
+ section_types_.push_back(section_code);
+ return Result::Ok;
+}
+
+bool BinaryReaderObjdumpBase::OnError(const Error&) {
+ // Tell the BinaryReader that this error is "handled" for all passes other
+ // than the prepass. When the error is handled the default message will be
+ // suppressed.
+ return options_->mode != ObjdumpMode::Prepass;
+}
+
+Result BinaryReaderObjdumpBase::BeginModule(uint32_t version) {
+ switch (options_->mode) {
+ case ObjdumpMode::Headers:
+ printf("\n");
+ printf("Sections:\n\n");
+ break;
+ case ObjdumpMode::Details:
+ printf("\n");
+ printf("Section Details:\n\n");
+ break;
+ case ObjdumpMode::Disassemble:
+ printf("\n");
+ printf("Code Disassembly:\n\n");
+ break;
+ case ObjdumpMode::Prepass: {
+ std::string_view basename = GetBasename(options_->filename);
+ if (basename == "-") {
+ basename = "<stdin>";
+ }
+ printf("%s:\tfile format wasm %#x\n", std::string(basename).c_str(),
+ version);
+ break;
+ }
+ case ObjdumpMode::RawData:
+ break;
+ }
+
+ return Result::Ok;
+}
+
+std::string_view BinaryReaderObjdumpBase::GetTypeName(Index index) const {
+ return objdump_state_->type_names.Get(index);
+}
+
+std::string_view BinaryReaderObjdumpBase::GetFunctionName(Index index) const {
+ return objdump_state_->function_names.Get(index);
+}
+
+std::string_view BinaryReaderObjdumpBase::GetGlobalName(Index index) const {
+ return objdump_state_->global_names.Get(index);
+}
+
+std::string_view BinaryReaderObjdumpBase::GetLocalName(
+ Index function_index,
+ Index local_index) const {
+ return objdump_state_->local_names.Get(function_index, local_index);
+}
+
+std::string_view BinaryReaderObjdumpBase::GetSectionName(Index index) const {
+ return objdump_state_->section_names.Get(index);
+}
+
+std::string_view BinaryReaderObjdumpBase::GetTagName(Index index) const {
+ return objdump_state_->tag_names.Get(index);
+}
+
+std::string_view BinaryReaderObjdumpBase::GetSegmentName(Index index) const {
+ return objdump_state_->segment_names.Get(index);
+}
+
+std::string_view BinaryReaderObjdumpBase::GetTableName(Index index) const {
+ return objdump_state_->table_names.Get(index);
+}
+
+std::string_view BinaryReaderObjdumpBase::GetSymbolName(
+ Index symbol_index) const {
+ if (symbol_index >= objdump_state_->symtab.size())
+ return "<illegal_symbol_index>";
+ ObjdumpSymbol& sym = objdump_state_->symtab[symbol_index];
+ switch (sym.kind) {
+ case SymbolType::Function:
+ return GetFunctionName(sym.index);
+ case SymbolType::Data:
+ return sym.name;
+ case SymbolType::Global:
+ return GetGlobalName(sym.index);
+ case SymbolType::Section:
+ return GetSectionName(sym.index);
+ case SymbolType::Tag:
+ return GetTagName(sym.index);
+ case SymbolType::Table:
+ return GetTableName(sym.index);
+ }
+ WABT_UNREACHABLE;
+}
+
+void BinaryReaderObjdumpBase::PrintRelocation(const Reloc& reloc,
+ Offset offset) const {
+ printf(" %06" PRIzx ": %-18s %" PRIindex, offset,
+ GetRelocTypeName(reloc.type), reloc.index);
+ if (reloc.addend) {
+ printf(" + %d", reloc.addend);
+ }
+ if (reloc.type != RelocType::TypeIndexLEB) {
+ printf(" <" PRIstringview ">",
+ WABT_PRINTF_STRING_VIEW_ARG(GetSymbolName(reloc.index)));
+ }
+ printf("\n");
+}
+
+Offset BinaryReaderObjdumpBase::GetPrintOffset(Offset offset) const {
+ return options_->section_offsets
+ ? offset - GetSectionStart(BinarySection::Code)
+ : offset;
+}
+
+Result BinaryReaderObjdumpBase::OnOpcode(Opcode opcode) {
+ current_opcode = opcode;
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpBase::OnRelocCount(Index count, Index section_index) {
+ if (section_index >= section_types_.size()) {
+ err_stream_->Writef("invalid relocation section index: %" PRIindex "\n",
+ section_index);
+ reloc_section_ = BinarySection::Invalid;
+ return Result::Error;
+ }
+ reloc_section_ = section_types_[section_index];
+ return Result::Ok;
+}
+
+class BinaryReaderObjdumpPrepass : public BinaryReaderObjdumpBase {
+ public:
+ using BinaryReaderObjdumpBase::BinaryReaderObjdumpBase;
+
+ Result BeginSection(Index section_index,
+ BinarySection section_code,
+ Offset size) override {
+ BinaryReaderObjdumpBase::BeginSection(section_index, section_code, size);
+ if (section_code != BinarySection::Custom) {
+ objdump_state_->section_names.Set(section_index,
+ wabt::GetSectionName(section_code));
+ }
+ return Result::Ok;
+ }
+
+ Result BeginCustomSection(Index section_index,
+ Offset size,
+ std::string_view section_name) override {
+ objdump_state_->section_names.Set(section_index, section_name);
+ return Result::Ok;
+ }
+
+ Result OnFunctionName(Index index, std::string_view name) override {
+ SetFunctionName(index, name);
+ return Result::Ok;
+ }
+
+ Result OnFuncType(Index index,
+ Index param_count,
+ Type* param_types,
+ Index result_count,
+ Type* result_types) override {
+ objdump_state_->function_param_counts[index] = param_count;
+ return Result::Ok;
+ }
+
+ Result OnNameEntry(NameSectionSubsection type,
+ Index index,
+ std::string_view name) override {
+ switch (type) {
+ // TODO(sbc): remove OnFunctionName in favor of just using
+ // OnNameEntry so that this works
+ /*
+ case NameSectionSubsection::Function:
+ SetFunctionName(index, name);
+ break;
+ */
+ case NameSectionSubsection::Type:
+ SetTypeName(index, name);
+ break;
+ case NameSectionSubsection::Global:
+ SetGlobalName(index, name);
+ break;
+ case NameSectionSubsection::Table:
+ SetTableName(index, name);
+ break;
+ case NameSectionSubsection::DataSegment:
+ SetSegmentName(index, name);
+ break;
+ case NameSectionSubsection::Tag:
+ SetTagName(index, name);
+ break;
+ default:
+ break;
+ }
+ return Result::Ok;
+ }
+
+ Result OnLocalName(Index function_index,
+ Index local_index,
+ std::string_view local_name) override {
+ SetLocalName(function_index, local_index, local_name);
+ return Result::Ok;
+ }
+
+ Result OnSymbolCount(Index count) override {
+ objdump_state_->symtab.resize(count);
+ return Result::Ok;
+ }
+
+ Result OnDataSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index segment,
+ uint32_t offset,
+ uint32_t size) override {
+ objdump_state_->symtab[index] = {SymbolType::Data, std::string(name), 0};
+ return Result::Ok;
+ }
+
+ Result OnFunctionSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index func_index) override {
+ if (!name.empty()) {
+ SetFunctionName(func_index, name);
+ }
+ objdump_state_->symtab[index] = {SymbolType::Function, std::string(name),
+ func_index};
+ return Result::Ok;
+ }
+
+ Result OnGlobalSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index global_index) override {
+ if (!name.empty()) {
+ SetGlobalName(global_index, name);
+ }
+ objdump_state_->symtab[index] = {SymbolType::Global, std::string(name),
+ global_index};
+ return Result::Ok;
+ }
+
+ Result OnSectionSymbol(Index index,
+ uint32_t flags,
+ Index section_index) override {
+ objdump_state_->symtab[index] = {SymbolType::Section,
+ std::string(GetSectionName(section_index)),
+ section_index};
+ return Result::Ok;
+ }
+
+ Result OnTagSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index tag_index) override {
+ if (!name.empty()) {
+ SetTagName(tag_index, name);
+ }
+ objdump_state_->symtab[index] = {SymbolType::Tag, std::string(name),
+ tag_index};
+ return Result::Ok;
+ }
+
+ Result OnTableSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index table_index) override {
+ if (!name.empty()) {
+ SetTableName(table_index, name);
+ }
+ objdump_state_->symtab[index] = {SymbolType::Table, std::string(name),
+ table_index};
+ return Result::Ok;
+ }
+
+ Result OnImportFunc(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index func_index,
+ Index sig_index) override {
+ SetFunctionName(func_index, module_name + "." + field_name);
+ return Result::Ok;
+ }
+
+ Result OnImportTag(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index tag_index,
+ Index sig_index) override {
+ SetTagName(tag_index, module_name + "." + field_name);
+ return Result::Ok;
+ }
+
+ Result OnImportGlobal(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index global_index,
+ Type type,
+ bool mutable_) override {
+ SetGlobalName(global_index, module_name + "." + field_name);
+ return Result::Ok;
+ }
+
+ Result OnImportTable(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index table_index,
+ Type elem_type,
+ const Limits* elem_limits) override {
+ SetTableName(table_index, module_name + "." + field_name);
+ return Result::Ok;
+ }
+
+ Result OnExport(Index index,
+ ExternalKind kind,
+ Index item_index,
+ std::string_view name) override {
+ if (kind == ExternalKind::Func) {
+ SetFunctionName(item_index, name);
+ } else if (kind == ExternalKind::Global) {
+ SetGlobalName(item_index, name);
+ }
+ return Result::Ok;
+ }
+
+ Result OnReloc(RelocType type,
+ Offset offset,
+ Index index,
+ uint32_t addend) override;
+
+ Result OnModuleName(std::string_view name) override {
+ if (options_->mode == ObjdumpMode::Prepass) {
+ printf("module name: <" PRIstringview ">\n",
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ return Result::Ok;
+ }
+
+ Result OnSegmentInfo(Index index,
+ std::string_view name,
+ Address alignment_log2,
+ uint32_t flags) override {
+ SetSegmentName(index, name);
+ return Result::Ok;
+ }
+
+ protected:
+ void SetTypeName(Index index, std::string_view name);
+ void SetFunctionName(Index index, std::string_view name);
+ void SetGlobalName(Index index, std::string_view name);
+ void SetLocalName(Index function_index,
+ Index local_index,
+ std::string_view name);
+ void SetTagName(Index index, std::string_view name);
+ void SetTableName(Index index, std::string_view name);
+ void SetSegmentName(Index index, std::string_view name);
+};
+
+void BinaryReaderObjdumpPrepass::SetTypeName(Index index,
+ std::string_view name) {
+ objdump_state_->type_names.Set(index, name);
+}
+
+void BinaryReaderObjdumpPrepass::SetFunctionName(Index index,
+ std::string_view name) {
+ objdump_state_->function_names.Set(index, name);
+}
+
+void BinaryReaderObjdumpPrepass::SetGlobalName(Index index,
+ std::string_view name) {
+ objdump_state_->global_names.Set(index, name);
+}
+
+void BinaryReaderObjdumpPrepass::SetLocalName(Index function_index,
+ Index local_index,
+ std::string_view name) {
+ objdump_state_->local_names.Set(function_index, local_index, name);
+}
+
+void BinaryReaderObjdumpPrepass::SetTagName(Index index,
+ std::string_view name) {
+ objdump_state_->tag_names.Set(index, name);
+}
+
+void BinaryReaderObjdumpPrepass::SetTableName(Index index,
+ std::string_view name) {
+ objdump_state_->table_names.Set(index, name);
+}
+
+void BinaryReaderObjdumpPrepass::SetSegmentName(Index index,
+ std::string_view name) {
+ objdump_state_->segment_names.Set(index, name);
+}
+
+Result BinaryReaderObjdumpPrepass::OnReloc(RelocType type,
+ Offset offset,
+ Index index,
+ uint32_t addend) {
+ BinaryReaderObjdumpBase::OnReloc(type, offset, index, addend);
+ if (reloc_section_ == BinarySection::Code) {
+ objdump_state_->code_relocations.emplace_back(type, offset, index, addend);
+ } else if (reloc_section_ == BinarySection::Data) {
+ objdump_state_->data_relocations.emplace_back(type, offset, index, addend);
+ }
+ return Result::Ok;
+}
+
+class BinaryReaderObjdumpDisassemble : public BinaryReaderObjdumpBase {
+ public:
+ using BinaryReaderObjdumpBase::BinaryReaderObjdumpBase;
+
+ std::string BlockSigToString(Type type) const;
+
+ Result BeginFunctionBody(Index index, Offset size) override;
+ Result EndFunctionBody(Index index) override;
+
+ Result OnLocalDeclCount(Index count) override;
+ Result OnLocalDecl(Index decl_index, Index count, Type type) override;
+
+ Result OnOpcode(Opcode Opcode) override;
+ Result OnOpcodeBare() override;
+ Result OnOpcodeIndex(Index value) override;
+ Result OnOpcodeIndexIndex(Index value, Index value2) override;
+ Result OnOpcodeUint32(uint32_t value) override;
+ Result OnOpcodeUint32Uint32(uint32_t value, uint32_t value2) override;
+ Result OnCallIndirectExpr(uint32_t sig_indix, uint32_t table_index) override;
+ Result OnOpcodeUint32Uint32Uint32(uint32_t value,
+ uint32_t value2,
+ uint32_t value3) override;
+ Result OnOpcodeUint32Uint32Uint32Uint32(uint32_t value,
+ uint32_t value2,
+ uint32_t value3,
+ uint32_t value4) override;
+ Result OnOpcodeUint64(uint64_t value) override;
+ Result OnOpcodeF32(uint32_t value) override;
+ Result OnOpcodeF64(uint64_t value) override;
+ Result OnOpcodeV128(v128 value) override;
+ Result OnOpcodeBlockSig(Type sig_type) override;
+ Result OnOpcodeType(Type type) override;
+
+ Result OnBrTableExpr(Index num_targets,
+ Index* target_depths,
+ Index default_target_depth) override;
+ Result OnDelegateExpr(Index) override;
+ Result OnEndExpr() override;
+
+ private:
+ void LogOpcode(const char* fmt, ...);
+
+ Offset current_opcode_offset = 0;
+ Offset last_opcode_end = 0;
+ int indent_level = 0;
+ Index next_reloc = 0;
+ Index current_function_index = 0;
+ Index local_index_ = 0;
+ bool in_function_body = false;
+ bool skip_next_opcode_ = false;
+};
+
+std::string BinaryReaderObjdumpDisassemble::BlockSigToString(Type type) const {
+ if (type.IsIndex()) {
+ return StringPrintf("type[%d]", type.GetIndex());
+ } else if (type == Type::Void) {
+ return "";
+ } else {
+ return type.GetName();
+ }
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcode(Opcode opcode) {
+ BinaryReaderObjdumpBase::OnOpcode(opcode);
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ if (options_->debug) {
+ const char* opcode_name = opcode.GetName();
+ err_stream_->Writef("on_opcode: %#" PRIzx ": %s\n", state->offset,
+ opcode_name);
+ }
+
+ if (last_opcode_end) {
+ // Takes care of cases where opcode's bytes was a non-canonical leb128
+ // encoding. In this case, opcode.GetLength() under-reports the length,
+ // since it canonicalizes the opcode.
+ if (state->offset < last_opcode_end + opcode.GetLength()) {
+ Opcode missing_opcode = Opcode::FromCode(data_[last_opcode_end]);
+ const char* opcode_name = missing_opcode.GetName();
+ fprintf(stderr,
+ "error: %#" PRIzx " missing opcode callback at %#" PRIzx
+ " (%#02x=%s)\n",
+ state->offset, last_opcode_end + 1, data_[last_opcode_end],
+ opcode_name);
+ return Result::Error;
+ }
+ }
+
+ current_opcode_offset = state->offset;
+ return Result::Ok;
+}
+
+#define IMMEDIATE_OCTET_COUNT 9
+
+Result BinaryReaderObjdumpDisassemble::OnLocalDeclCount(Index count) {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ current_opcode_offset = state->offset;
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnLocalDecl(Index decl_index,
+ Index count,
+ Type type) {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ Offset offset = current_opcode_offset;
+ size_t data_size = state->offset - offset;
+
+ printf(" %06" PRIzx ":", GetPrintOffset(offset));
+ for (size_t i = 0; i < data_size && i < IMMEDIATE_OCTET_COUNT;
+ i++, offset++) {
+ printf(" %02x", data_[offset]);
+ }
+ for (size_t i = data_size; i < IMMEDIATE_OCTET_COUNT; i++) {
+ printf(" ");
+ }
+ printf(" | local[%" PRIindex, local_index_);
+
+ if (count != 1) {
+ printf("..%" PRIindex "", local_index_ + count - 1);
+ }
+ local_index_ += count;
+
+ printf("] type=%s\n", type.GetName().c_str());
+
+ last_opcode_end = current_opcode_offset + data_size;
+ current_opcode_offset = last_opcode_end;
+
+ return Result::Ok;
+}
+
+void BinaryReaderObjdumpDisassemble::LogOpcode(const char* fmt, ...) {
+ // BinaryReaderObjdumpDisassemble is only used to disassembly function bodies
+ // so this should never be called for instructions outside of function bodies
+ // (i.e. init expresions).
+ assert(in_function_body);
+ if (skip_next_opcode_) {
+ skip_next_opcode_ = false;
+ return;
+ }
+ const Offset immediate_len = state->offset - current_opcode_offset;
+ const Offset opcode_size = current_opcode.GetLength();
+ const Offset total_size = opcode_size + immediate_len;
+ // current_opcode_offset has already read past this opcode; rewind it by the
+ // size of this opcode, which may be more than one byte.
+ Offset offset = current_opcode_offset - opcode_size;
+ const Offset offset_end = offset + total_size;
+
+ bool first_line = true;
+ while (offset < offset_end) {
+ // Print bytes, but only display a maximum of IMMEDIATE_OCTET_COUNT on each
+ // line.
+ printf(" %06" PRIzx ":", GetPrintOffset(offset));
+ size_t i;
+ for (i = 0; offset < offset_end && i < IMMEDIATE_OCTET_COUNT;
+ ++i, ++offset) {
+ printf(" %02x", data_[offset]);
+ }
+ // Fill the rest of the remaining space with spaces.
+ for (; i < IMMEDIATE_OCTET_COUNT; ++i) {
+ printf(" ");
+ }
+ printf(" | ");
+
+ if (first_line) {
+ first_line = false;
+
+ // Print disassembly.
+ int indent_level = this->indent_level;
+ switch (current_opcode) {
+ case Opcode::Else:
+ case Opcode::Catch:
+ case Opcode::CatchAll:
+ indent_level--;
+ break;
+ default:
+ break;
+ }
+ for (int j = 0; j < indent_level; j++) {
+ printf(" ");
+ }
+
+ const char* opcode_name = current_opcode.GetName();
+ printf("%s", opcode_name);
+ if (fmt) {
+ printf(" ");
+ va_list args;
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+ }
+ }
+
+ printf("\n");
+ }
+
+ last_opcode_end = state->offset;
+
+ // Print relocation after then full (potentially multi-line) instruction.
+ if (options_->relocs &&
+ next_reloc < objdump_state_->code_relocations.size()) {
+ const Reloc& reloc = objdump_state_->code_relocations[next_reloc];
+ Offset code_start = GetSectionStart(BinarySection::Code);
+ Offset abs_offset = code_start + reloc.offset;
+ if (last_opcode_end > abs_offset) {
+ PrintRelocation(reloc, abs_offset);
+ next_reloc++;
+ }
+ }
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeBare() {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ LogOpcode(0, nullptr);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeIndex(Index value) {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ std::string_view name;
+ if (current_opcode == Opcode::Call &&
+ !(name = GetFunctionName(value)).empty()) {
+ LogOpcode("%d <" PRIstringview ">", value,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ } else if (current_opcode == Opcode::Throw &&
+ !(name = GetTagName(value)).empty()) {
+ LogOpcode("%d <" PRIstringview ">", value,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ } else if ((current_opcode == Opcode::GlobalGet ||
+ current_opcode == Opcode::GlobalSet) &&
+ !(name = GetGlobalName(value)).empty()) {
+ LogOpcode("%d <" PRIstringview ">", value,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ } else if ((current_opcode == Opcode::LocalGet ||
+ current_opcode == Opcode::LocalSet) &&
+ !(name = GetLocalName(current_function_index, value)).empty()) {
+ LogOpcode("%d <" PRIstringview ">", value,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ } else {
+ LogOpcode("%d", value);
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeIndexIndex(Index value,
+ Index value2) {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ LogOpcode("%" PRIindex " %" PRIindex, value, value2);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32(uint32_t value) {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ std::string_view name;
+ if (current_opcode == Opcode::DataDrop &&
+ !(name = GetSegmentName(value)).empty()) {
+ LogOpcode("%d <" PRIstringview ">", value,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ } else {
+ LogOpcode("%u", value);
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32Uint32(uint32_t value,
+ uint32_t value2) {
+ if (!in_function_body)
+ return Result::Ok;
+ std::string_view name;
+ if (current_opcode == Opcode::MemoryInit &&
+ !(name = GetSegmentName(value)).empty()) {
+ LogOpcode("%u %u <" PRIstringview ">", value, value2,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ } else {
+ LogOpcode("%u %u", value, value2);
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnCallIndirectExpr(
+ uint32_t sig_index,
+ uint32_t table_index) {
+ std::string_view table_name = GetTableName(table_index);
+ std::string_view type_name = GetTypeName(sig_index);
+ if (!type_name.empty() && !table_name.empty()) {
+ LogOpcode("%u <" PRIstringview "> (type %u <" PRIstringview ">)",
+ table_index, WABT_PRINTF_STRING_VIEW_ARG(table_name), sig_index,
+ WABT_PRINTF_STRING_VIEW_ARG(type_name));
+ } else if (!table_name.empty()) {
+ LogOpcode("%u <" PRIstringview "> (type %u)", table_index,
+ WABT_PRINTF_STRING_VIEW_ARG(table_name), sig_index);
+ } else if (!type_name.empty()) {
+ LogOpcode("%u (type %u <" PRIstringview ">)", table_index, sig_index,
+ WABT_PRINTF_STRING_VIEW_ARG(type_name));
+ } else {
+ LogOpcode("%u (type %u)", table_index, sig_index);
+ }
+ skip_next_opcode_ = true;
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32Uint32Uint32(
+ uint32_t value,
+ uint32_t value2,
+ uint32_t value3) {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ LogOpcode("%u %u %u", value, value2, value3);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32Uint32Uint32Uint32(
+ uint32_t value,
+ uint32_t value2,
+ uint32_t value3,
+ uint32_t value4) {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ LogOpcode("%u %u %u %u", value, value2, value3, value4);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeUint64(uint64_t value) {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ LogOpcode("%" PRId64, value);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeF32(uint32_t value) {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ char buffer[WABT_MAX_FLOAT_HEX];
+ WriteFloatHex(buffer, sizeof(buffer), value);
+ LogOpcode(buffer);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeF64(uint64_t value) {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ char buffer[WABT_MAX_DOUBLE_HEX];
+ WriteDoubleHex(buffer, sizeof(buffer), value);
+ LogOpcode(buffer);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeV128(v128 value) {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ // v128 is always dumped as i32x4:
+ LogOpcode("0x%08x 0x%08x 0x%08x 0x%08x", value.u32(0), value.u32(1),
+ value.u32(2), value.u32(3));
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeType(Type type) {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ if (current_opcode == Opcode::SelectT) {
+ LogOpcode(type.GetName().c_str());
+ } else {
+ LogOpcode(type.GetRefKindName());
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnBrTableExpr(
+ Index num_targets,
+ Index* target_depths,
+ Index default_target_depth) {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+
+ std::string buffer = std::string();
+ for (Index i = 0; i < num_targets; i++) {
+ buffer.append(std::to_string(target_depths[i])).append(" ");
+ }
+ buffer.append(std::to_string(default_target_depth));
+
+ LogOpcode("%s", buffer.c_str());
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnDelegateExpr(Index depth) {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ // Because `delegate` ends the block we need to dedent here, and
+ // we don't need to dedent it in LogOpcode.
+ if (indent_level > 0) {
+ indent_level--;
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnEndExpr() {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ if (indent_level > 0) {
+ indent_level--;
+ }
+ LogOpcode(0, nullptr);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::BeginFunctionBody(Index index,
+ Offset size) {
+ printf("%06" PRIzx " func[%" PRIindex "]", GetPrintOffset(state->offset),
+ index);
+ auto name = GetFunctionName(index);
+ if (!name.empty()) {
+ printf(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ printf(":\n");
+
+ last_opcode_end = 0;
+ in_function_body = true;
+ current_function_index = index;
+ local_index_ = objdump_state_->function_param_counts[index];
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::EndFunctionBody(Index index) {
+ assert(in_function_body);
+ in_function_body = false;
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeBlockSig(Type sig_type) {
+ if (!in_function_body) {
+ return Result::Ok;
+ }
+ if (sig_type != Type::Void) {
+ LogOpcode("%s", BlockSigToString(sig_type).c_str());
+ } else {
+ LogOpcode(nullptr);
+ }
+ indent_level++;
+ return Result::Ok;
+}
+
+enum class InitExprType {
+ I32,
+ F32,
+ I64,
+ F64,
+ V128,
+ Global,
+ FuncRef,
+ // TODO: There isn't a nullref anymore, this just represents ref.null of some
+ // type T.
+ NullRef,
+};
+
+struct InitInst {
+ Opcode opcode;
+ union {
+ Index index;
+ uint32_t i32;
+ uint32_t f32;
+ uint64_t i64;
+ uint64_t f64;
+ v128 v128_v;
+ Type type;
+ } imm;
+};
+
+struct InitExpr {
+ InitExprType type;
+ std::vector<InitInst> insts;
+};
+
+class BinaryReaderObjdump : public BinaryReaderObjdumpBase {
+ public:
+ BinaryReaderObjdump(const uint8_t* data,
+ size_t size,
+ ObjdumpOptions* options,
+ ObjdumpState* state);
+
+ Result EndModule() override;
+ Result BeginSection(Index section_index,
+ BinarySection section_type,
+ Offset size) override;
+ Result BeginCustomSection(Index section_index,
+ Offset size,
+ std::string_view section_name) override;
+
+ Result OnTypeCount(Index count) override;
+ Result OnFuncType(Index index,
+ Index param_count,
+ Type* param_types,
+ Index result_count,
+ Type* result_types) override;
+ Result OnStructType(Index index, Index field_count, TypeMut* fields) override;
+ Result OnArrayType(Index index, TypeMut field) override;
+
+ Result OnImportCount(Index count) override;
+ Result OnImportFunc(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index func_index,
+ Index sig_index) override;
+ Result OnImportTable(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index table_index,
+ Type elem_type,
+ const Limits* elem_limits) override;
+ Result OnImportMemory(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index memory_index,
+ const Limits* page_limits) override;
+ Result OnImportGlobal(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index global_index,
+ Type type,
+ bool mutable_) override;
+ Result OnImportTag(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index tag_index,
+ Index sig_index) override;
+
+ Result OnFunctionCount(Index count) override;
+ Result OnFunction(Index index, Index sig_index) override;
+
+ Result OnTableCount(Index count) override;
+ Result OnTable(Index index,
+ Type elem_type,
+ const Limits* elem_limits) override;
+
+ Result OnMemoryCount(Index count) override;
+ Result OnMemory(Index index, const Limits* limits) override;
+
+ Result OnGlobalCount(Index count) override;
+ Result BeginGlobal(Index index, Type type, bool mutable_) override;
+
+ Result OnExportCount(Index count) override;
+ Result OnExport(Index index,
+ ExternalKind kind,
+ Index item_index,
+ std::string_view name) override;
+
+ Result OnStartFunction(Index func_index) override;
+ Result OnDataCount(Index count) override;
+
+ Result OnFunctionBodyCount(Index count) override;
+ Result BeginFunctionBody(Index index, Offset size) override;
+
+ Result OnElemSegmentCount(Index count) override;
+ Result BeginElemSegment(Index index,
+ Index table_index,
+ uint8_t flags) override;
+ Result OnElemSegmentElemType(Index index, Type elem_type) override;
+ Result OnElemSegmentElemExprCount(Index index, Index count) override;
+ Result OnElemSegmentElemExpr_RefNull(Index segment_index, Type type) override;
+ Result OnElemSegmentElemExpr_RefFunc(Index segment_index,
+ Index func_index) override;
+
+ void BeginInitExpr() { current_init_expr_.insts.clear(); }
+
+ Result BeginElemSegmentInitExpr(Index index) override {
+ reading_elem_init_expr_ = true;
+ BeginInitExpr();
+ return Result::Ok;
+ }
+
+ Result EndElemSegmentInitExpr(Index index) override { return EndInitExpr(); }
+
+ Result BeginDataSegmentInitExpr(Index index) override {
+ reading_data_init_expr_ = true;
+ BeginInitExpr();
+ return Result::Ok;
+ }
+
+ Result EndDataSegmentInitExpr(Index index) override { return EndInitExpr(); }
+
+ Result BeginGlobalInitExpr(Index index) override {
+ reading_global_init_expr_ = true;
+ BeginInitExpr();
+ return Result::Ok;
+ }
+
+ Result EndGlobalInitExpr(Index index) override { return EndInitExpr(); }
+
+ Result OnDataSegmentCount(Index count) override;
+ Result BeginDataSegment(Index index,
+ Index memory_index,
+ uint8_t flags) override;
+ Result OnDataSegmentData(Index index,
+ const void* data,
+ Address size) override;
+
+ Result OnModuleName(std::string_view name) override;
+ Result OnFunctionName(Index function_index,
+ std::string_view function_name) override;
+ Result OnLocalName(Index function_index,
+ Index local_index,
+ std::string_view local_name) override;
+ Result OnNameEntry(NameSectionSubsection type,
+ Index index,
+ std::string_view name) override;
+
+ Result OnDylinkInfo(uint32_t mem_size,
+ uint32_t mem_align_log2,
+ uint32_t table_size,
+ uint32_t table_align_log2) override;
+ Result OnDylinkNeededCount(Index count) override;
+ Result OnDylinkNeeded(std::string_view so_name) override;
+ Result OnDylinkImportCount(Index count) override;
+ Result OnDylinkExportCount(Index count) override;
+ Result OnDylinkImport(std::string_view module,
+ std::string_view name,
+ uint32_t flags) override;
+ Result OnDylinkExport(std::string_view name, uint32_t flags) override;
+
+ Result OnRelocCount(Index count, Index section_index) override;
+ Result OnReloc(RelocType type,
+ Offset offset,
+ Index index,
+ uint32_t addend) override;
+
+ Result OnFeature(uint8_t prefix, std::string_view name) override;
+
+ Result OnSymbolCount(Index count) override;
+ Result OnDataSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index segment,
+ uint32_t offset,
+ uint32_t size) override;
+ Result OnFunctionSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index func_index) override;
+ Result OnGlobalSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index global_index) override;
+ Result OnSectionSymbol(Index index,
+ uint32_t flags,
+ Index section_index) override;
+ Result OnTagSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index tag_index) override;
+ Result OnTableSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index table_index) override;
+ Result OnSegmentInfoCount(Index count) override;
+ Result OnSegmentInfo(Index index,
+ std::string_view name,
+ Address alignment_log2,
+ uint32_t flags) override;
+ Result OnInitFunctionCount(Index count) override;
+ Result OnInitFunction(uint32_t priority, Index symbol_index) override;
+ Result OnComdatCount(Index count) override;
+ Result OnComdatBegin(std::string_view name,
+ uint32_t flags,
+ Index count) override;
+ Result OnComdatEntry(ComdatType kind, Index index) override;
+
+ Result OnTagCount(Index count) override;
+ Result OnTagType(Index index, Index sig_index) override;
+
+ Result OnOpcode(Opcode Opcode) override;
+ Result OnI32ConstExpr(uint32_t value) override;
+ Result OnI64ConstExpr(uint64_t value) override;
+ 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 EndInitExpr();
+ bool ShouldPrintDetails();
+ void PrintDetails(const char* fmt, ...);
+ Result PrintSymbolFlags(uint32_t flags);
+ Result PrintSegmentFlags(uint32_t flags);
+ void PrintInitExpr(const InitExpr& expr, bool as_unsigned = false);
+ Result OnCount(Index count);
+
+ std::unique_ptr<FileStream> out_stream_;
+ Index elem_index_ = 0;
+ Index table_index_ = 0;
+ Index next_data_reloc_ = 0;
+ bool reading_elem_init_expr_ = false;
+ bool reading_data_init_expr_ = false;
+ bool reading_global_init_expr_ = false;
+ InitExpr current_init_expr_;
+ uint8_t data_flags_ = 0;
+ uint8_t elem_flags_ = 0;
+ Index data_mem_index_ = 0;
+ uint64_t data_offset_ = 0;
+ uint64_t elem_offset_ = 0;
+
+ bool ReadingInitExpr() {
+ return reading_elem_init_expr_ || reading_data_init_expr_ ||
+ reading_global_init_expr_;
+ }
+};
+
+BinaryReaderObjdump::BinaryReaderObjdump(const uint8_t* data,
+ size_t size,
+ ObjdumpOptions* options,
+ ObjdumpState* objdump_state)
+ : BinaryReaderObjdumpBase(data, size, options, objdump_state),
+ out_stream_(FileStream::CreateStdout()) {}
+
+Result BinaryReaderObjdump::BeginCustomSection(Index section_index,
+ Offset size,
+ std::string_view section_name) {
+ PrintDetails(" - name: \"" PRIstringview "\"\n",
+ WABT_PRINTF_STRING_VIEW_ARG(section_name));
+ if (options_->mode == ObjdumpMode::Headers) {
+ printf("\"" PRIstringview "\"\n",
+ WABT_PRINTF_STRING_VIEW_ARG(section_name));
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::BeginSection(Index section_index,
+ BinarySection section_code,
+ Offset size) {
+ BinaryReaderObjdumpBase::BeginSection(section_index, section_code, size);
+
+ // |section_name| and |match_name| are identical for known sections. For
+ // custom sections, |section_name| is "Custom", but |match_name| is the name
+ // of the custom section.
+ const char* section_name = wabt::GetSectionName(section_code);
+ std::string match_name(GetSectionName(section_index));
+
+ bool section_match = !options_->section_name ||
+ !strcasecmp(options_->section_name, match_name.c_str());
+ if (section_match) {
+ section_found_ = true;
+ }
+
+ switch (options_->mode) {
+ case ObjdumpMode::Headers:
+ printf("%9s start=%#010" PRIzx " end=%#010" PRIzx " (size=%#010" PRIoffset
+ ") ",
+ section_name, state->offset, state->offset + size, size);
+ break;
+ case ObjdumpMode::Details:
+ if (section_match) {
+ printf("%s", section_name);
+ // All known section types except the Start and DataCount sections have
+ // a count in which case this line gets completed in OnCount().
+ if (section_code == BinarySection::Start ||
+ section_code == BinarySection::DataCount ||
+ section_code == BinarySection::Custom) {
+ printf(":\n");
+ }
+ print_details_ = true;
+ } else {
+ print_details_ = false;
+ }
+ break;
+ case ObjdumpMode::RawData:
+ if (section_match) {
+ printf("\nContents of section %s:\n", section_name);
+ out_stream_->WriteMemoryDump(data_ + state->offset, size, state->offset,
+ PrintChars::Yes);
+ }
+ break;
+ case ObjdumpMode::Prepass:
+ case ObjdumpMode::Disassemble:
+ break;
+ }
+ return Result::Ok;
+}
+
+bool BinaryReaderObjdump::ShouldPrintDetails() {
+ if (options_->mode != ObjdumpMode::Details) {
+ return false;
+ }
+ return print_details_;
+}
+
+void WABT_PRINTF_FORMAT(2, 3) BinaryReaderObjdump::PrintDetails(const char* fmt,
+ ...) {
+ if (!ShouldPrintDetails()) {
+ return;
+ }
+ va_list args;
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+}
+
+Result BinaryReaderObjdump::OnCount(Index count) {
+ if (options_->mode == ObjdumpMode::Headers) {
+ printf("count: %" PRIindex "\n", count);
+ } else if (options_->mode == ObjdumpMode::Details && print_details_) {
+ printf("[%" PRIindex "]:\n", count);
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::EndModule() {
+ if (options_->section_name && !section_found_) {
+ err_stream_->Writef("Section not found: %s\n", options_->section_name);
+ return Result::Error;
+ }
+
+ if (options_->relocs && ShouldPrintDetails()) {
+ if (next_data_reloc_ != objdump_state_->data_relocations.size()) {
+ err_stream_->Writef("Data reloctions outside of segments!:\n");
+ for (size_t i = next_data_reloc_;
+ i < objdump_state_->data_relocations.size(); i++) {
+ const Reloc& reloc = objdump_state_->data_relocations[i];
+ PrintRelocation(reloc, reloc.offset);
+ }
+
+ return Result::Error;
+ }
+ }
+
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnTypeCount(Index count) {
+ return OnCount(count);
+}
+
+Result BinaryReaderObjdump::OnFuncType(Index index,
+ Index param_count,
+ Type* param_types,
+ Index result_count,
+ Type* result_types) {
+ if (!ShouldPrintDetails()) {
+ return Result::Ok;
+ }
+ printf(" - type[%" PRIindex "] (", index);
+ for (Index i = 0; i < param_count; i++) {
+ if (i != 0) {
+ printf(", ");
+ }
+ printf("%s", param_types[i].GetName().c_str());
+ }
+ printf(") -> ");
+ switch (result_count) {
+ case 0:
+ printf("nil");
+ break;
+ case 1:
+ printf("%s", result_types[0].GetName().c_str());
+ break;
+ default:
+ printf("(");
+ for (Index i = 0; i < result_count; i++) {
+ if (i != 0) {
+ printf(", ");
+ }
+ printf("%s", result_types[i].GetName().c_str());
+ }
+ printf(")");
+ break;
+ }
+ printf("\n");
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnStructType(Index index,
+ Index field_count,
+ TypeMut* fields) {
+ if (!ShouldPrintDetails()) {
+ return Result::Ok;
+ }
+ printf(" - type[%" PRIindex "] (struct", index);
+ for (Index i = 0; i < field_count; i++) {
+ if (fields[i].mutable_) {
+ printf(" (mut");
+ }
+ printf(" %s", fields[i].type.GetName().c_str());
+ if (fields[i].mutable_) {
+ printf(")");
+ }
+ }
+ printf(")\n");
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnArrayType(Index index, TypeMut field) {
+ if (!ShouldPrintDetails()) {
+ return Result::Ok;
+ }
+ printf(" - type[%" PRIindex "] (array", index);
+ if (field.mutable_) {
+ printf(" (mut");
+ }
+ printf(" %s", field.type.GetName().c_str());
+ if (field.mutable_) {
+ printf(")");
+ }
+ printf(")\n");
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnFunctionCount(Index count) {
+ return OnCount(count);
+}
+
+Result BinaryReaderObjdump::OnFunction(Index index, Index sig_index) {
+ PrintDetails(" - func[%" PRIindex "] sig=%" PRIindex, index, sig_index);
+ auto name = GetFunctionName(index);
+ if (!name.empty()) {
+ PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ PrintDetails("\n");
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnFunctionBodyCount(Index count) {
+ return OnCount(count);
+}
+
+Result BinaryReaderObjdump::BeginFunctionBody(Index index, Offset size) {
+ PrintDetails(" - func[%" PRIindex "] size=%" PRIzd, index, size);
+ auto name = GetFunctionName(index);
+ if (!name.empty()) {
+ PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ PrintDetails("\n");
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnStartFunction(Index func_index) {
+ if (options_->mode == ObjdumpMode::Headers) {
+ printf("start: %" PRIindex "\n", func_index);
+ } else {
+ PrintDetails(" - start function: %" PRIindex, func_index);
+ auto name = GetFunctionName(func_index);
+ if (!name.empty()) {
+ PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ PrintDetails("\n");
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnDataCount(Index count) {
+ if (options_->mode == ObjdumpMode::Headers) {
+ printf("count: %" PRIindex "\n", count);
+ } else {
+ PrintDetails(" - data count: %" PRIindex "\n", count);
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnImportCount(Index count) {
+ return OnCount(count);
+}
+
+Result BinaryReaderObjdump::OnImportFunc(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index func_index,
+ Index sig_index) {
+ PrintDetails(" - func[%" PRIindex "] sig=%" PRIindex, func_index, sig_index);
+ auto name = GetFunctionName(func_index);
+ if (!name.empty()) {
+ PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ PrintDetails(" <- " PRIstringview "." PRIstringview "\n",
+ WABT_PRINTF_STRING_VIEW_ARG(module_name),
+ WABT_PRINTF_STRING_VIEW_ARG(field_name));
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnImportTable(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index table_index,
+ Type elem_type,
+ const Limits* elem_limits) {
+ PrintDetails(" - table[%" PRIindex "] type=%s initial=%" PRId64, table_index,
+ elem_type.GetName().c_str(), elem_limits->initial);
+ if (elem_limits->has_max) {
+ PrintDetails(" max=%" PRId64, elem_limits->max);
+ }
+ PrintDetails(" <- " PRIstringview "." PRIstringview "\n",
+ WABT_PRINTF_STRING_VIEW_ARG(module_name),
+ WABT_PRINTF_STRING_VIEW_ARG(field_name));
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnImportMemory(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index memory_index,
+ const Limits* page_limits) {
+ PrintDetails(" - memory[%" PRIindex "] pages: initial=%" PRId64, memory_index,
+ page_limits->initial);
+ if (page_limits->has_max) {
+ PrintDetails(" max=%" PRId64, page_limits->max);
+ }
+ if (page_limits->is_shared) {
+ PrintDetails(" shared");
+ }
+ if (page_limits->is_64) {
+ PrintDetails(" i64");
+ }
+ PrintDetails(" <- " PRIstringview "." PRIstringview "\n",
+ WABT_PRINTF_STRING_VIEW_ARG(module_name),
+ WABT_PRINTF_STRING_VIEW_ARG(field_name));
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnImportGlobal(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index global_index,
+ Type type,
+ bool mutable_) {
+ PrintDetails(" - global[%" PRIindex "] %s mutable=%d", global_index,
+ type.GetName().c_str(), mutable_);
+ PrintDetails(" <- " PRIstringview "." PRIstringview "\n",
+ WABT_PRINTF_STRING_VIEW_ARG(module_name),
+ WABT_PRINTF_STRING_VIEW_ARG(field_name));
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnImportTag(Index import_index,
+ std::string_view module_name,
+ std::string_view field_name,
+ Index tag_index,
+ Index sig_index) {
+ PrintDetails(" - tag[%" PRIindex "] sig=%" PRIindex, tag_index, sig_index);
+ auto name = GetTagName(tag_index);
+ if (!name.empty()) {
+ PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ PrintDetails(" <- " PRIstringview "." PRIstringview "\n",
+ WABT_PRINTF_STRING_VIEW_ARG(module_name),
+ WABT_PRINTF_STRING_VIEW_ARG(field_name));
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnMemoryCount(Index count) {
+ return OnCount(count);
+}
+
+Result BinaryReaderObjdump::OnMemory(Index index, const Limits* page_limits) {
+ PrintDetails(" - memory[%" PRIindex "] pages: initial=%" PRId64, index,
+ page_limits->initial);
+ if (page_limits->has_max) {
+ PrintDetails(" max=%" PRId64, page_limits->max);
+ }
+ if (page_limits->is_shared) {
+ PrintDetails(" shared");
+ }
+ if (page_limits->is_64) {
+ PrintDetails(" i64");
+ }
+ PrintDetails("\n");
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnTableCount(Index count) {
+ return OnCount(count);
+}
+
+Result BinaryReaderObjdump::OnTable(Index index,
+ Type elem_type,
+ const Limits* elem_limits) {
+ PrintDetails(" - table[%" PRIindex "] type=%s initial=%" PRId64, index,
+ elem_type.GetName().c_str(), elem_limits->initial);
+ if (elem_limits->has_max) {
+ PrintDetails(" max=%" PRId64, elem_limits->max);
+ }
+ auto name = GetTableName(index);
+ if (!name.empty()) {
+ PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ PrintDetails("\n");
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnExportCount(Index count) {
+ return OnCount(count);
+}
+
+Result BinaryReaderObjdump::OnExport(Index index,
+ ExternalKind kind,
+ Index item_index,
+ std::string_view name) {
+ PrintDetails(" - %s[%" PRIindex "]", GetKindName(kind), item_index);
+ if (kind == ExternalKind::Func) {
+ auto name = GetFunctionName(item_index);
+ if (!name.empty()) {
+ PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ }
+
+ PrintDetails(" -> \"" PRIstringview "\"\n",
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnElemSegmentElemExpr_RefNull(Index segment_index,
+ Type type) {
+ PrintDetails(" - elem[%" PRIu64 "] = ref.null %s\n",
+ elem_offset_ + elem_index_, type.GetName().c_str());
+ elem_index_++;
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnElemSegmentElemExpr_RefFunc(Index segment_index,
+ Index func_index) {
+ PrintDetails(" - elem[%" PRIu64 "] = func[%" PRIindex "]",
+ elem_offset_ + elem_index_, func_index);
+ auto name = GetFunctionName(func_index);
+ if (!name.empty()) {
+ PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ PrintDetails("\n");
+ elem_index_++;
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnElemSegmentCount(Index count) {
+ return OnCount(count);
+}
+
+Result BinaryReaderObjdump::BeginElemSegment(Index index,
+ Index table_index,
+ uint8_t flags) {
+ table_index_ = table_index;
+ elem_index_ = 0;
+ elem_flags_ = flags;
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnElemSegmentElemType(Index index, Type elem_type) {
+ // TODO: Add support for this.
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnElemSegmentElemExprCount(Index index,
+ Index count) {
+ PrintDetails(" - segment[%" PRIindex "] flags=%d table=%" PRIindex
+ " count=%" PRIindex,
+ index, elem_flags_, table_index_, count);
+ if (elem_flags_ & SegPassive) {
+ PrintDetails("\n");
+ } else {
+ PrintInitExpr(current_init_expr_, /*as_unsigned=*/true);
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnGlobalCount(Index count) {
+ return OnCount(count);
+}
+
+Result BinaryReaderObjdump::BeginGlobal(Index index, Type type, bool mutable_) {
+ PrintDetails(" - global[%" PRIindex "] %s mutable=%d", index,
+ type.GetName().c_str(), mutable_);
+ std::string_view name = GetGlobalName(index);
+ if (!name.empty()) {
+ PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ return Result::Ok;
+}
+
+void BinaryReaderObjdump::PrintInitExpr(const InitExpr& expr,
+ bool as_unsigned) {
+ assert(expr.insts.size() > 0);
+
+ // We have two different way to print init expressions. One for
+ // extended expressions involving more than one instruction, and
+ // a short form for the more traditional single instruction form.
+ if (expr.insts.size() > 1) {
+ PrintDetails(" - init (");
+ bool first = true;
+ for (auto& inst : expr.insts) {
+ if (!first) {
+ PrintDetails(", ");
+ }
+ first = false;
+ PrintDetails("%s", inst.opcode.GetName());
+ switch (inst.opcode) {
+ case Opcode::I32Const:
+ PrintDetails(" %d", inst.imm.i32);
+ break;
+ case Opcode::I64Const:
+ PrintDetails(" %" PRId64, inst.imm.i64);
+ break;
+ case Opcode::F32Const: {
+ char buffer[WABT_MAX_FLOAT_HEX];
+ WriteFloatHex(buffer, sizeof(buffer), inst.imm.f32);
+ PrintDetails(" %s\n", buffer);
+ break;
+ }
+ case Opcode::F64Const: {
+ char buffer[WABT_MAX_DOUBLE_HEX];
+ WriteDoubleHex(buffer, sizeof(buffer), inst.imm.f64);
+ PrintDetails(" %s\n", buffer);
+ break;
+ }
+ case Opcode::GlobalGet: {
+ PrintDetails(" %" PRIindex, inst.imm.index);
+ std::string_view name = GetGlobalName(inst.imm.index);
+ if (!name.empty()) {
+ PrintDetails(" <" PRIstringview ">",
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ PrintDetails(")\n");
+ return;
+ }
+
+ switch (expr.type) {
+ case InitExprType::I32:
+ if (as_unsigned) {
+ PrintDetails(" - init i32=%u\n", expr.insts[0].imm.i32);
+ } else {
+ PrintDetails(" - init i32=%d\n", expr.insts[0].imm.i32);
+ }
+ break;
+ case InitExprType::I64:
+ if (as_unsigned) {
+ PrintDetails(" - init i64=%" PRIu64 "\n", expr.insts[0].imm.i64);
+ } else {
+ PrintDetails(" - init i64=%" PRId64 "\n", expr.insts[0].imm.i64);
+ }
+ break;
+ case InitExprType::F64: {
+ char buffer[WABT_MAX_DOUBLE_HEX];
+ WriteDoubleHex(buffer, sizeof(buffer), expr.insts[0].imm.f64);
+ PrintDetails(" - init f64=%s\n", buffer);
+ break;
+ }
+ case InitExprType::F32: {
+ char buffer[WABT_MAX_FLOAT_HEX];
+ WriteFloatHex(buffer, sizeof(buffer), expr.insts[0].imm.f32);
+ PrintDetails(" - init f32=%s\n", buffer);
+ break;
+ }
+ case InitExprType::V128: {
+ PrintDetails(
+ " - init v128=0x%08x 0x%08x 0x%08x 0x%08x \n",
+ expr.insts[0].imm.v128_v.u32(0), expr.insts[0].imm.v128_v.u32(1),
+ expr.insts[0].imm.v128_v.u32(2), expr.insts[0].imm.v128_v.u32(3));
+ break;
+ }
+ case InitExprType::Global: {
+ PrintDetails(" - init global=%" PRIindex, expr.insts[0].imm.index);
+ std::string_view name = GetGlobalName(expr.insts[0].imm.index);
+ if (!name.empty()) {
+ PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ PrintDetails("\n");
+ break;
+ }
+ case InitExprType::FuncRef: {
+ PrintDetails(" - init ref.func:%" PRIindex, expr.insts[0].imm.index);
+ std::string_view name = GetFunctionName(expr.insts[0].imm.index);
+ if (!name.empty()) {
+ PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ PrintDetails("\n");
+ break;
+ }
+ case InitExprType::NullRef:
+ PrintDetails(" - init null\n");
+ break;
+ break;
+ }
+}
+
+static void InitExprToConstOffset(const InitExpr& expr, uint64_t* out_offset) {
+ if (expr.insts.size() == 1) {
+ switch (expr.type) {
+ case InitExprType::I32:
+ *out_offset = expr.insts[0].imm.i32;
+ break;
+ case InitExprType::I64:
+ *out_offset = expr.insts[0].imm.i64;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+Result BinaryReaderObjdump::EndInitExpr() {
+ if (reading_data_init_expr_) {
+ reading_data_init_expr_ = false;
+ InitExprToConstOffset(current_init_expr_, &data_offset_);
+ } else if (reading_elem_init_expr_) {
+ reading_elem_init_expr_ = false;
+ InitExprToConstOffset(current_init_expr_, &elem_offset_);
+ } else if (reading_global_init_expr_) {
+ reading_global_init_expr_ = false;
+ PrintInitExpr(current_init_expr_);
+ } else {
+ WABT_UNREACHABLE;
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnI32ConstExpr(uint32_t value) {
+ if (ReadingInitExpr()) {
+ current_init_expr_.type = InitExprType::I32;
+ current_init_expr_.insts.back().imm.i32 = value;
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnI64ConstExpr(uint64_t value) {
+ if (ReadingInitExpr()) {
+ current_init_expr_.type = InitExprType::I64;
+ current_init_expr_.insts.back().imm.i64 = value;
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnF32ConstExpr(uint32_t value) {
+ if (ReadingInitExpr()) {
+ current_init_expr_.type = InitExprType::F32;
+ current_init_expr_.insts.back().imm.f32 = value;
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnF64ConstExpr(uint64_t value) {
+ if (ReadingInitExpr()) {
+ current_init_expr_.type = InitExprType::F64;
+ current_init_expr_.insts.back().imm.f64 = value;
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnOpcode(Opcode opcode) {
+ BinaryReaderObjdumpBase::OnOpcode(opcode);
+ if (ReadingInitExpr() && opcode != Opcode::End) {
+ InitInst i;
+ i.opcode = current_opcode;
+ current_init_expr_.insts.push_back(i);
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnGlobalGetExpr(Index global_index) {
+ if (ReadingInitExpr()) {
+ current_init_expr_.type = InitExprType::Global;
+ current_init_expr_.insts.back().imm.index = global_index;
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnModuleName(std::string_view name) {
+ PrintDetails(" - module <" PRIstringview ">\n",
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnFunctionName(Index index, std::string_view name) {
+ PrintDetails(" - func[%" PRIindex "] <" PRIstringview ">\n", index,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnNameEntry(NameSectionSubsection type,
+ Index index,
+ std::string_view name) {
+ PrintDetails(" - %s[%" PRIindex "] <" PRIstringview ">\n",
+ GetNameSectionSubsectionName(type), index,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnLocalName(Index func_index,
+ Index local_index,
+ std::string_view name) {
+ if (!name.empty()) {
+ PrintDetails(" - func[%" PRIindex "] local[%" PRIindex "] <" PRIstringview
+ ">\n",
+ func_index, local_index, WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnDataSegmentCount(Index count) {
+ return OnCount(count);
+}
+
+Result BinaryReaderObjdump::BeginDataSegment(Index index,
+ Index memory_index,
+ uint8_t flags) {
+ data_mem_index_ = memory_index;
+ data_flags_ = flags;
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnDataSegmentData(Index index,
+ const void* src_data,
+ Address size) {
+ if (!ShouldPrintDetails()) {
+ return Result::Ok;
+ }
+
+ PrintDetails(" - segment[%" PRIindex "]", index);
+ auto name = GetSegmentName(index);
+ if (!name.empty()) {
+ PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ if (data_flags_ & SegPassive) {
+ PrintDetails(" passive");
+ } else {
+ PrintDetails(" memory=%" PRIindex, data_mem_index_);
+ }
+ PrintDetails(" size=%" PRIaddress, size);
+ if (data_flags_ & SegPassive) {
+ PrintDetails("\n");
+ } else {
+ PrintInitExpr(current_init_expr_, /*as_unsigned=*/true);
+ }
+
+ out_stream_->WriteMemoryDump(src_data, size, data_offset_, PrintChars::Yes,
+ " - ");
+
+ // Print relocations from this segment.
+ if (!options_->relocs) {
+ return Result::Ok;
+ }
+
+ Offset data_start = GetSectionStart(BinarySection::Data);
+ Offset segment_start = state->offset - size;
+ Offset segment_offset = segment_start - data_start;
+ while (next_data_reloc_ < objdump_state_->data_relocations.size()) {
+ const Reloc& reloc = objdump_state_->data_relocations[next_data_reloc_];
+ Offset abs_offset = data_start + reloc.offset;
+ if (abs_offset > state->offset) {
+ break;
+ }
+ PrintRelocation(reloc, reloc.offset - segment_offset + data_offset_);
+ next_data_reloc_++;
+ }
+
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnDylinkInfo(uint32_t mem_size,
+ uint32_t mem_align_log2,
+ uint32_t table_size,
+ uint32_t table_align_log2) {
+ PrintDetails(" - mem_size : %u\n", mem_size);
+ PrintDetails(" - mem_p2align : %u\n", mem_align_log2);
+ PrintDetails(" - table_size : %u\n", table_size);
+ PrintDetails(" - table_p2align: %u\n", table_align_log2);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnDylinkNeededCount(Index count) {
+ if (count) {
+ PrintDetails(" - needed_dynlibs[%u]:\n", count);
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnDylinkImportCount(Index count) {
+ PrintDetails(" - imports[%u]:\n", count);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnDylinkExportCount(Index count) {
+ PrintDetails(" - exports[%u]:\n", count);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnDylinkExport(std::string_view name,
+ uint32_t flags) {
+ PrintDetails(" - " PRIstringview, WABT_PRINTF_STRING_VIEW_ARG(name));
+ return PrintSymbolFlags(flags);
+}
+
+Result BinaryReaderObjdump::OnDylinkImport(std::string_view module,
+ std::string_view name,
+ uint32_t flags) {
+ PrintDetails(" - " PRIstringview "." PRIstringview,
+ WABT_PRINTF_STRING_VIEW_ARG(module),
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ return PrintSymbolFlags(flags);
+}
+
+Result BinaryReaderObjdump::OnDylinkNeeded(std::string_view so_name) {
+ PrintDetails(" - " PRIstringview "\n", WABT_PRINTF_STRING_VIEW_ARG(so_name));
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnRelocCount(Index count, Index section_index) {
+ BinaryReaderObjdumpBase::OnRelocCount(count, section_index);
+ PrintDetails(" - relocations for section: %d (" PRIstringview ") [%d]\n",
+ section_index,
+ WABT_PRINTF_STRING_VIEW_ARG(GetSectionName(section_index)),
+ count);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnReloc(RelocType type,
+ Offset offset,
+ Index index,
+ uint32_t addend) {
+ Offset total_offset = GetSectionStart(reloc_section_) + offset;
+ PrintDetails(" - %-18s offset=%#08" PRIoffset "(file=%#08" PRIoffset ") ",
+ GetRelocTypeName(type), offset, total_offset);
+ if (type == RelocType::TypeIndexLEB) {
+ PrintDetails("type=%" PRIindex, index);
+ } else {
+ PrintDetails("symbol=%" PRIindex " <" PRIstringview ">", index,
+ WABT_PRINTF_STRING_VIEW_ARG(GetSymbolName(index)));
+ }
+
+ if (addend) {
+ int32_t signed_addend = static_cast<int32_t>(addend);
+ if (signed_addend < 0) {
+ PrintDetails("-");
+ signed_addend = -signed_addend;
+ } else {
+ PrintDetails("+");
+ }
+ PrintDetails("%#x", signed_addend);
+ }
+ PrintDetails("\n");
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnFeature(uint8_t prefix, std::string_view name) {
+ PrintDetails(" - [%c] " PRIstringview "\n", prefix,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnSymbolCount(Index count) {
+ PrintDetails(" - symbol table [count=%d]\n", count);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::PrintSymbolFlags(uint32_t flags) {
+ if (flags > WABT_SYMBOL_FLAG_MAX) {
+ err_stream_->Writef("Unknown symbols flags: %x\n", flags);
+ return Result::Error;
+ }
+
+ const char* binding_name = nullptr;
+ SymbolBinding binding =
+ static_cast<SymbolBinding>(flags & WABT_SYMBOL_MASK_BINDING);
+ switch (binding) {
+ case SymbolBinding::Global:
+ binding_name = "global";
+ break;
+ case SymbolBinding::Local:
+ binding_name = "local";
+ break;
+ case SymbolBinding::Weak:
+ binding_name = "weak";
+ break;
+ }
+ flags &= ~WABT_SYMBOL_MASK_BINDING;
+
+ const char* vis_name = nullptr;
+ SymbolVisibility vis =
+ static_cast<SymbolVisibility>(flags & WABT_SYMBOL_MASK_VISIBILITY);
+ switch (vis) {
+ case SymbolVisibility::Hidden:
+ vis_name = "hidden";
+ break;
+ case SymbolVisibility::Default:
+ vis_name = "default";
+ break;
+ }
+ flags &= ~WABT_SYMBOL_MASK_VISIBILITY;
+
+ PrintDetails(" [");
+ if (flags & WABT_SYMBOL_FLAG_UNDEFINED) {
+ PrintDetails(" undefined");
+ flags &= ~WABT_SYMBOL_FLAG_UNDEFINED;
+ }
+ if (flags & WABT_SYMBOL_FLAG_EXPORTED) {
+ PrintDetails(" exported");
+ flags &= ~WABT_SYMBOL_FLAG_EXPORTED;
+ }
+ if (flags & WABT_SYMBOL_FLAG_EXPLICIT_NAME) {
+ PrintDetails(" explicit_name");
+ flags &= ~WABT_SYMBOL_FLAG_EXPLICIT_NAME;
+ }
+ if (flags & WABT_SYMBOL_FLAG_NO_STRIP) {
+ PrintDetails(" no_strip");
+ flags &= ~WABT_SYMBOL_FLAG_NO_STRIP;
+ }
+ if (flags & WABT_SYMBOL_FLAG_TLS) {
+ PrintDetails(" tls");
+ flags &= ~WABT_SYMBOL_FLAG_TLS;
+ }
+ if (flags != 0) {
+ PrintDetails(" unknown_flags=%#x", flags);
+ }
+ PrintDetails(" binding=%s vis=%s ]\n", binding_name, vis_name);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::PrintSegmentFlags(uint32_t flags) {
+ if (flags > WABT_SYMBOL_FLAG_MAX) {
+ err_stream_->Writef("Unknown symbols flags: %x\n", flags);
+ return Result::Error;
+ }
+ PrintDetails(" [");
+ if (flags & WABT_SEGMENT_FLAG_STRINGS) {
+ PrintDetails(" STRINGS");
+ flags &= ~WABT_SEGMENT_FLAG_STRINGS;
+ }
+ if (flags & WABT_SEGMENT_FLAG_TLS) {
+ PrintDetails(" TLS");
+ flags &= ~WABT_SEGMENT_FLAG_TLS;
+ }
+ if (flags != 0) {
+ PrintDetails(" unknown_flags=%#x", flags);
+ }
+ PrintDetails(" ]\n");
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnDataSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index segment,
+ uint32_t offset,
+ uint32_t size) {
+ PrintDetails(" - %d: D <" PRIstringview ">", index,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ if (!(flags & WABT_SYMBOL_FLAG_UNDEFINED))
+ PrintDetails(" segment=%" PRIindex " offset=%d size=%d", segment, offset,
+ size);
+ return PrintSymbolFlags(flags);
+}
+
+Result BinaryReaderObjdump::OnFunctionSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index func_index) {
+ if (name.empty()) {
+ name = GetFunctionName(func_index);
+ }
+ PrintDetails(" - %d: F <" PRIstringview "> func=%" PRIindex, index,
+ WABT_PRINTF_STRING_VIEW_ARG(name), func_index);
+ return PrintSymbolFlags(flags);
+}
+
+Result BinaryReaderObjdump::OnGlobalSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index global_index) {
+ if (name.empty()) {
+ name = GetGlobalName(global_index);
+ }
+ PrintDetails(" - %d: G <" PRIstringview "> global=%" PRIindex, index,
+ WABT_PRINTF_STRING_VIEW_ARG(name), global_index);
+ return PrintSymbolFlags(flags);
+}
+
+Result BinaryReaderObjdump::OnSectionSymbol(Index index,
+ uint32_t flags,
+ Index section_index) {
+ auto sym_name = GetSectionName(section_index);
+ assert(!sym_name.empty());
+ PrintDetails(" - %d: S <" PRIstringview "> section=%" PRIindex, index,
+ WABT_PRINTF_STRING_VIEW_ARG(sym_name), section_index);
+ return PrintSymbolFlags(flags);
+}
+
+Result BinaryReaderObjdump::OnTagSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index tag_index) {
+ if (name.empty()) {
+ name = GetTagName(tag_index);
+ }
+ PrintDetails(" - %d: E <" PRIstringview "> tag=%" PRIindex, index,
+ WABT_PRINTF_STRING_VIEW_ARG(name), tag_index);
+ return PrintSymbolFlags(flags);
+}
+
+Result BinaryReaderObjdump::OnTableSymbol(Index index,
+ uint32_t flags,
+ std::string_view name,
+ Index table_index) {
+ if (name.empty()) {
+ name = GetTableName(table_index);
+ }
+ PrintDetails(" - %d: T <" PRIstringview "> table=%" PRIindex, index,
+ WABT_PRINTF_STRING_VIEW_ARG(name), table_index);
+ return PrintSymbolFlags(flags);
+}
+
+Result BinaryReaderObjdump::OnSegmentInfoCount(Index count) {
+ PrintDetails(" - segment info [count=%d]\n", count);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnSegmentInfo(Index index,
+ std::string_view name,
+ Address alignment_log2,
+ uint32_t flags) {
+ PrintDetails(" - %d: " PRIstringview " p2align=%" PRIaddress, index,
+ WABT_PRINTF_STRING_VIEW_ARG(name), alignment_log2);
+ return PrintSegmentFlags(flags);
+}
+
+Result BinaryReaderObjdump::OnInitFunctionCount(Index count) {
+ PrintDetails(" - init functions [count=%d]\n", count);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnInitFunction(uint32_t priority,
+ Index symbol_index) {
+ PrintDetails(" - %d: priority=%d", symbol_index, priority);
+ auto name = GetSymbolName(symbol_index);
+ if (!name.empty()) {
+ PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ PrintDetails("\n");
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnComdatCount(Index count) {
+ PrintDetails(" - comdat groups [count=%d]\n", count);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnComdatBegin(std::string_view name,
+ uint32_t flags,
+ Index count) {
+ PrintDetails(" - " PRIstringview ": [count=%d]\n",
+ WABT_PRINTF_STRING_VIEW_ARG(name), count);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnComdatEntry(ComdatType kind, Index index) {
+ switch (kind) {
+ case ComdatType::Data: {
+ PrintDetails(" - segment[%" PRIindex "]", index);
+ auto name = GetSegmentName(index);
+ if (!name.empty()) {
+ PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ break;
+ }
+ case ComdatType::Function: {
+ PrintDetails(" - func[%" PRIindex "]", index);
+ auto name = GetFunctionName(index);
+ if (!name.empty()) {
+ PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ break;
+ }
+ }
+ PrintDetails("\n");
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnTagCount(Index count) {
+ return OnCount(count);
+}
+
+Result BinaryReaderObjdump::OnTagType(Index index, Index sig_index) {
+ if (!ShouldPrintDetails()) {
+ return Result::Ok;
+ }
+ printf(" - tag[%" PRIindex "] sig=%" PRIindex "\n", 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 {
+ auto iter = names.find(index);
+ if (iter == names.end())
+ return std::string_view();
+ return iter->second;
+}
+
+void ObjdumpNames::Set(Index index, std::string_view name) {
+ names[index] = std::string(name);
+}
+
+std::string_view ObjdumpLocalNames::Get(Index function_index,
+ Index local_index) const {
+ auto iter = names.find(std::pair<Index, Index>(function_index, local_index));
+ if (iter == names.end())
+ return std::string_view();
+ return iter->second;
+}
+
+void ObjdumpLocalNames::Set(Index function_index,
+ Index local_index,
+ std::string_view name) {
+ names[std::pair<Index, Index>(function_index, local_index)] =
+ std::string(name);
+}
+
+Result ReadBinaryObjdump(const uint8_t* data,
+ size_t size,
+ ObjdumpOptions* options,
+ ObjdumpState* state) {
+ Features features;
+ features.EnableAll();
+ const bool kReadDebugNames = true;
+ const bool kStopOnFirstError = false;
+ const bool kFailOnCustomSectionError = false;
+ ReadBinaryOptions read_options(features, options->log_stream, kReadDebugNames,
+ kStopOnFirstError, kFailOnCustomSectionError);
+
+ switch (options->mode) {
+ case ObjdumpMode::Prepass: {
+ read_options.skip_function_bodies = true;
+ BinaryReaderObjdumpPrepass reader(data, size, options, state);
+ return ReadBinary(data, size, &reader, read_options);
+ }
+ case ObjdumpMode::Disassemble: {
+ BinaryReaderObjdumpDisassemble reader(data, size, options, state);
+ return ReadBinary(data, size, &reader, read_options);
+ }
+ default: {
+ read_options.skip_function_bodies = true;
+ BinaryReaderObjdump reader(data, size, options, state);
+ return ReadBinary(data, size, &reader, read_options);
+ }
+ }
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/binary-reader-opcnt.cc b/third_party/wasm2c/src/binary-reader-opcnt.cc
new file mode 100644
index 0000000000..3177dbb7eb
--- /dev/null
+++ b/third_party/wasm2c/src/binary-reader-opcnt.cc
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/binary-reader-opcnt.h"
+
+#include <cassert>
+#include <cinttypes>
+#include <cstdarg>
+#include <cstdint>
+#include <cstdio>
+
+#include "wabt/binary-reader-nop.h"
+#include "wabt/common.h"
+#include "wabt/literal.h"
+#include "wabt/stream.h"
+
+namespace wabt {
+
+OpcodeInfo::OpcodeInfo(Opcode opcode, Kind kind)
+ : opcode_(opcode), kind_(kind) {}
+
+template <typename T>
+OpcodeInfo::OpcodeInfo(Opcode opcode, Kind kind, T* data, size_t count)
+ : OpcodeInfo(opcode, kind) {
+ if (count > 0) {
+ data_.resize(sizeof(T) * count);
+ memcpy(data_.data(), data, data_.size());
+ }
+}
+
+template <typename T>
+OpcodeInfo::OpcodeInfo(Opcode opcode, Kind kind, T* data, size_t count, T extra)
+ : OpcodeInfo(opcode, kind, data, count) {
+ data_.resize(data_.size() + sizeof(T));
+ memcpy(data_.data() + data_.size() - sizeof(T), &extra, sizeof(T));
+}
+
+template <typename T>
+std::pair<const T*, size_t> OpcodeInfo::GetDataArray() const {
+ if (data_.empty()) {
+ return std::pair<const T*, size_t>(nullptr, 0);
+ }
+
+ assert(data_.size() % sizeof(T) == 0);
+ return std::make_pair(reinterpret_cast<const T*>(data_.data()),
+ data_.size() / sizeof(T));
+}
+
+template <typename T>
+const T* OpcodeInfo::GetData(size_t expected_size) const {
+ auto [data, size] = GetDataArray<T>();
+ assert(size == expected_size);
+ return data;
+}
+
+template <typename T, typename F>
+void OpcodeInfo::WriteArray(Stream& stream, F&& write_func) {
+ auto [data, size] = GetDataArray<T>();
+ for (size_t i = 0; i < size; ++i) {
+ // Write an initial space (to separate from the opcode name) first, then
+ // comma-separate.
+ stream.Writef("%s", i == 0 ? " " : ", ");
+ write_func(data[i]);
+ }
+}
+
+void OpcodeInfo::Write(Stream& stream) {
+ stream.Writef("%s", opcode_.GetName());
+
+ switch (kind_) {
+ case Kind::Bare:
+ break;
+
+ case Kind::Uint32:
+ stream.Writef(" %u (0x%x)", *GetData<uint32_t>(), *GetData<uint32_t>());
+ break;
+
+ case Kind::Uint64:
+ stream.Writef(" %" PRIu64 " (0x%" PRIx64 ")", *GetData<uint64_t>(),
+ *GetData<uint64_t>());
+ break;
+
+ case Kind::Index:
+ stream.Writef(" %" PRIindex, *GetData<Index>());
+ break;
+
+ case Kind::Float32: {
+ stream.Writef(" %g", *GetData<float>());
+ char buffer[WABT_MAX_FLOAT_HEX + 1];
+ WriteFloatHex(buffer, sizeof(buffer), *GetData<uint32_t>());
+ stream.Writef(" (%s)", buffer);
+ break;
+ }
+
+ case Kind::Float64: {
+ stream.Writef(" %g", *GetData<double>());
+ char buffer[WABT_MAX_DOUBLE_HEX + 1];
+ WriteDoubleHex(buffer, sizeof(buffer), *GetData<uint64_t>());
+ stream.Writef(" (%s)", buffer);
+ break;
+ }
+
+ case Kind::V128: {
+ auto data = *GetData<v128>();
+ auto l0 = data.u32(0);
+ auto l1 = data.u32(1);
+ auto l2 = data.u32(2);
+ auto l3 = data.u32(3);
+ stream.Writef(" %u %u %u %u (0x%x 0x%x 0x%x 0x%x)", l0, l1, l2, l3, l0,
+ l1, l2, l3);
+ break;
+ }
+
+ case Kind::Uint32Uint32:
+ case Kind::Uint32Uint32Uint32:
+ case Kind::Uint32Uint32Uint32Uint32:
+ WriteArray<uint32_t>(
+ stream, [&stream](uint32_t value) { stream.Writef("%u", value); });
+ break;
+
+ case Kind::BlockSig: {
+ auto type = *GetData<Type>();
+ if (type.IsIndex()) {
+ stream.Writef(" type:%d", type.GetIndex());
+ } else if (type != Type::Void) {
+ stream.Writef(" %s", type.GetName().c_str());
+ }
+ break;
+ }
+
+ case Kind::BrTable: {
+ WriteArray<Index>(stream, [&stream](Index index) {
+ stream.Writef("%" PRIindex, index);
+ });
+ break;
+ }
+ }
+}
+
+bool operator==(const OpcodeInfo& lhs, const OpcodeInfo& rhs) {
+ return lhs.opcode_ == rhs.opcode_ && lhs.kind_ == rhs.kind_ &&
+ lhs.data_ == rhs.data_;
+}
+
+bool operator!=(const OpcodeInfo& lhs, const OpcodeInfo& rhs) {
+ return !(lhs == rhs);
+}
+
+bool operator<(const OpcodeInfo& lhs, const OpcodeInfo& rhs) {
+ if (lhs.opcode_ < rhs.opcode_) {
+ return true;
+ }
+ if (lhs.opcode_ > rhs.opcode_) {
+ return false;
+ }
+ if (lhs.kind_ < rhs.kind_) {
+ return true;
+ }
+ if (lhs.kind_ > rhs.kind_) {
+ return false;
+ }
+ if (lhs.data_ < rhs.data_) {
+ return true;
+ }
+ if (lhs.data_ > rhs.data_) {
+ return false;
+ }
+ return false;
+}
+
+bool operator<=(const OpcodeInfo& lhs, const OpcodeInfo& rhs) {
+ return lhs < rhs || lhs == rhs;
+}
+
+bool operator>(const OpcodeInfo& lhs, const OpcodeInfo& rhs) {
+ return !(lhs <= rhs);
+}
+
+bool operator>=(const OpcodeInfo& lhs, const OpcodeInfo& rhs) {
+ return !(lhs < rhs);
+}
+
+namespace {
+
+class BinaryReaderOpcnt : public BinaryReaderNop {
+ public:
+ explicit BinaryReaderOpcnt(OpcodeInfoCounts* counts);
+
+ Result OnOpcode(Opcode opcode) override;
+ Result OnOpcodeBare() override;
+ Result OnOpcodeUint32(uint32_t value) override;
+ Result OnOpcodeIndex(Index value) override;
+ Result OnOpcodeUint32Uint32(uint32_t value, uint32_t value2) override;
+ Result OnOpcodeUint32Uint32Uint32(uint32_t value,
+ uint32_t value2,
+ uint32_t value3) override;
+ Result OnOpcodeUint64(uint64_t value) override;
+ Result OnOpcodeF32(uint32_t value) override;
+ Result OnOpcodeF64(uint64_t value) override;
+ Result OnOpcodeV128(v128 value) override;
+ Result OnOpcodeBlockSig(Type sig_type) override;
+ Result OnBrTableExpr(Index num_targets,
+ Index* target_depths,
+ Index default_target_depth) override;
+ Result OnEndExpr() override;
+
+ private:
+ template <typename... Args>
+ Result Emplace(Args&&... args);
+
+ OpcodeInfoCounts* opcode_counts_;
+ Opcode current_opcode_;
+};
+
+template <typename... Args>
+Result BinaryReaderOpcnt::Emplace(Args&&... args) {
+ auto pair = opcode_counts_->emplace(
+ std::piecewise_construct, std::make_tuple(std::forward<Args>(args)...),
+ std::make_tuple(0));
+
+ auto& count = pair.first->second;
+ count++;
+ return Result::Ok;
+}
+
+BinaryReaderOpcnt::BinaryReaderOpcnt(OpcodeInfoCounts* counts)
+ : opcode_counts_(counts) {}
+
+Result BinaryReaderOpcnt::OnOpcode(Opcode opcode) {
+ current_opcode_ = opcode;
+ return Result::Ok;
+}
+
+Result BinaryReaderOpcnt::OnOpcodeBare() {
+ return Emplace(current_opcode_, OpcodeInfo::Kind::Bare);
+}
+
+Result BinaryReaderOpcnt::OnOpcodeUint32(uint32_t value) {
+ return Emplace(current_opcode_, OpcodeInfo::Kind::Uint32, &value);
+}
+
+Result BinaryReaderOpcnt::OnOpcodeIndex(Index value) {
+ return Emplace(current_opcode_, OpcodeInfo::Kind::Index, &value);
+}
+
+Result BinaryReaderOpcnt::OnOpcodeUint32Uint32(uint32_t value0,
+ uint32_t value1) {
+ uint32_t array[2] = {value0, value1};
+ return Emplace(current_opcode_, OpcodeInfo::Kind::Uint32Uint32, array, 2);
+}
+
+Result BinaryReaderOpcnt::OnOpcodeUint32Uint32Uint32(uint32_t value0,
+ uint32_t value1,
+ uint32_t value2) {
+ uint32_t array[3] = {value0, value1, value2};
+ return Emplace(current_opcode_, OpcodeInfo::Kind::Uint32Uint32Uint32, array,
+ 3);
+}
+
+Result BinaryReaderOpcnt::OnOpcodeUint64(uint64_t value) {
+ return Emplace(current_opcode_, OpcodeInfo::Kind::Uint64, &value);
+}
+
+Result BinaryReaderOpcnt::OnOpcodeF32(uint32_t value) {
+ return Emplace(current_opcode_, OpcodeInfo::Kind::Float32, &value);
+}
+
+Result BinaryReaderOpcnt::OnOpcodeF64(uint64_t value) {
+ return Emplace(current_opcode_, OpcodeInfo::Kind::Float64, &value);
+}
+
+Result BinaryReaderOpcnt::OnOpcodeV128(v128 value) {
+ return Emplace(current_opcode_, OpcodeInfo::Kind::V128, &value);
+}
+
+Result BinaryReaderOpcnt::OnOpcodeBlockSig(Type sig_type) {
+ return Emplace(current_opcode_, OpcodeInfo::Kind::BlockSig, &sig_type);
+}
+
+Result BinaryReaderOpcnt::OnBrTableExpr(Index num_targets,
+ Index* target_depths,
+ Index default_target_depth) {
+ return Emplace(current_opcode_, OpcodeInfo::Kind::BrTable, target_depths,
+ num_targets, default_target_depth);
+}
+
+Result BinaryReaderOpcnt::OnEndExpr() {
+ return Emplace(Opcode::End, OpcodeInfo::Kind::Bare);
+}
+
+} // end anonymous namespace
+
+Result ReadBinaryOpcnt(const void* data,
+ size_t size,
+ const ReadBinaryOptions& options,
+ OpcodeInfoCounts* counts) {
+ BinaryReaderOpcnt reader(counts);
+ return ReadBinary(data, size, &reader, options);
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/binary-reader.cc b/third_party/wasm2c/src/binary-reader.cc
new file mode 100644
index 0000000000..8e86383db4
--- /dev/null
+++ b/third_party/wasm2c/src/binary-reader.cc
@@ -0,0 +1,3027 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/binary-reader.h"
+
+#include <cassert>
+#include <cinttypes>
+#include <cstdarg>
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+#include <vector>
+
+#include "wabt/config.h"
+
+#include "wabt/binary-reader-logging.h"
+#include "wabt/binary.h"
+#include "wabt/leb128.h"
+#include "wabt/stream.h"
+#include "wabt/utf8.h"
+
+#if HAVE_ALLOCA
+#include <alloca.h>
+#endif
+
+#define ERROR_IF(expr, ...) \
+ do { \
+ if (expr) { \
+ PrintError(__VA_ARGS__); \
+ return Result::Error; \
+ } \
+ } while (0)
+
+#define ERROR_UNLESS(expr, ...) ERROR_IF(!(expr), __VA_ARGS__)
+
+#define ERROR_UNLESS_OPCODE_ENABLED(opcode) \
+ do { \
+ if (!opcode.IsEnabled(options_.features)) { \
+ return ReportUnexpectedOpcode(opcode); \
+ } \
+ } while (0)
+
+#define CALLBACK0(member) \
+ ERROR_UNLESS(Succeeded(delegate_->member()), #member " callback failed")
+
+#define CALLBACK(member, ...) \
+ ERROR_UNLESS(Succeeded(delegate_->member(__VA_ARGS__)), \
+ #member " callback failed")
+
+namespace wabt {
+
+namespace {
+
+class BinaryReader {
+ public:
+ struct ReadModuleOptions {
+ bool stop_on_first_error;
+ };
+
+ BinaryReader(const void* data,
+ size_t size,
+ BinaryReaderDelegate* delegate,
+ const ReadBinaryOptions& options);
+
+ Result ReadModule(const ReadModuleOptions& options);
+
+ private:
+ template <typename T, T BinaryReader::*member>
+ struct ValueRestoreGuard {
+ explicit ValueRestoreGuard(BinaryReader* this_)
+ : this_(this_), previous_value_(this_->*member) {}
+ ~ValueRestoreGuard() { this_->*member = previous_value_; }
+
+ BinaryReader* this_;
+ T previous_value_;
+ };
+
+ struct ReadSectionsOptions {
+ bool stop_on_first_error;
+ };
+
+ void WABT_PRINTF_FORMAT(2, 3) PrintError(const char* format, ...);
+ [[nodiscard]] Result ReadOpcode(Opcode* out_value, const char* desc);
+ template <typename T>
+ [[nodiscard]] Result ReadT(T* out_value,
+ const char* type_name,
+ const char* desc);
+ [[nodiscard]] Result ReadU8(uint8_t* out_value, const char* desc);
+ [[nodiscard]] Result ReadU32(uint32_t* out_value, const char* desc);
+ [[nodiscard]] Result ReadF32(uint32_t* out_value, const char* desc);
+ [[nodiscard]] Result ReadF64(uint64_t* out_value, const char* desc);
+ [[nodiscard]] Result ReadV128(v128* out_value, const char* desc);
+ [[nodiscard]] Result ReadU32Leb128(uint32_t* out_value, const char* desc);
+ [[nodiscard]] Result ReadU64Leb128(uint64_t* out_value, const char* desc);
+ [[nodiscard]] Result ReadS32Leb128(uint32_t* out_value, const char* desc);
+ [[nodiscard]] Result ReadS64Leb128(uint64_t* out_value, const char* desc);
+ [[nodiscard]] Result ReadType(Type* out_value, const char* desc);
+ [[nodiscard]] Result ReadRefType(Type* out_value, const char* desc);
+ [[nodiscard]] Result ReadExternalKind(ExternalKind* out_value,
+ const char* desc);
+ [[nodiscard]] Result ReadStr(std::string_view* out_str, const char* desc);
+ [[nodiscard]] Result ReadBytes(const void** out_data,
+ Address* out_data_size,
+ const char* desc);
+ [[nodiscard]] Result ReadIndex(Index* index, const char* desc);
+ [[nodiscard]] Result ReadOffset(Offset* offset, const char* desc);
+ [[nodiscard]] Result ReadAlignment(Address* align_log2, const char* desc);
+ [[nodiscard]] Result ReadMemidx(Index* memidx, const char* desc);
+ [[nodiscard]] Result ReadMemLocation(Address* alignment_log2,
+ Index* memidx,
+ Address* offset,
+ const char* desc_align,
+ const char* desc_memidx,
+ const char* desc_offset,
+ uint8_t* lane_val = nullptr);
+ [[nodiscard]] Result CallbackMemLocation(const Address* alignment_log2,
+ const Index* memidx,
+ const Address* offset,
+ const uint8_t* lane_val = nullptr);
+ [[nodiscard]] Result ReadCount(Index* index, const char* desc);
+ [[nodiscard]] Result ReadField(TypeMut* out_value);
+
+ bool IsConcreteType(Type);
+ bool IsBlockType(Type);
+
+ Index NumTotalFuncs();
+
+ [[nodiscard]] Result ReadInitExpr(Index index);
+ [[nodiscard]] Result ReadTable(Type* out_elem_type, Limits* out_elem_limits);
+ [[nodiscard]] Result ReadMemory(Limits* out_page_limits);
+ [[nodiscard]] Result ReadGlobalHeader(Type* out_type, bool* out_mutable);
+ [[nodiscard]] Result ReadTagType(Index* out_sig_index);
+ [[nodiscard]] Result ReadAddress(Address* out_value,
+ Index memory,
+ const char* desc);
+ [[nodiscard]] Result ReadFunctionBody(Offset end_offset);
+ // ReadInstructions either until and END instruction, or until
+ // the given end_offset.
+ [[nodiscard]] Result ReadInstructions(bool stop_on_end,
+ Offset end_offset,
+ Opcode* final_opcode);
+ [[nodiscard]] Result ReadNameSection(Offset section_size);
+ [[nodiscard]] Result ReadRelocSection(Offset section_size);
+ [[nodiscard]] Result ReadDylinkSection(Offset section_size);
+ [[nodiscard]] Result ReadDylink0Section(Offset section_size);
+ [[nodiscard]] Result ReadTargetFeaturesSections(Offset section_size);
+ [[nodiscard]] Result ReadLinkingSection(Offset section_size);
+ [[nodiscard]] Result ReadCodeMetadataSection(std::string_view name,
+ Offset section_size);
+ [[nodiscard]] Result ReadCustomSection(Index section_index,
+ Offset section_size);
+ [[nodiscard]] Result ReadTypeSection(Offset section_size);
+ [[nodiscard]] Result ReadImportSection(Offset section_size);
+ [[nodiscard]] Result ReadFunctionSection(Offset section_size);
+ [[nodiscard]] Result ReadTableSection(Offset section_size);
+ [[nodiscard]] Result ReadMemorySection(Offset section_size);
+ [[nodiscard]] Result ReadGlobalSection(Offset section_size);
+ [[nodiscard]] Result ReadExportSection(Offset section_size);
+ [[nodiscard]] Result ReadStartSection(Offset section_size);
+ [[nodiscard]] Result ReadElemSection(Offset section_size);
+ [[nodiscard]] Result ReadCodeSection(Offset section_size);
+ [[nodiscard]] Result ReadDataSection(Offset section_size);
+ [[nodiscard]] Result ReadDataCountSection(Offset section_size);
+ [[nodiscard]] Result ReadTagSection(Offset section_size);
+ [[nodiscard]] Result ReadSections(const ReadSectionsOptions& options);
+ Result ReportUnexpectedOpcode(Opcode opcode, const char* message = nullptr);
+
+ size_t read_end_ = 0; // Either the section end or data_size.
+ BinaryReaderDelegate::State state_;
+ BinaryReaderLogging logging_delegate_;
+ BinaryReaderDelegate* delegate_ = nullptr;
+ TypeVector param_types_;
+ TypeVector result_types_;
+ TypeMutVector fields_;
+ std::vector<Index> target_depths_;
+ const ReadBinaryOptions& options_;
+ BinarySection last_known_section_ = BinarySection::Invalid;
+ bool did_read_names_section_ = false;
+ bool reading_custom_section_ = false;
+ Index num_func_imports_ = 0;
+ Index num_table_imports_ = 0;
+ Index num_memory_imports_ = 0;
+ Index num_global_imports_ = 0;
+ Index num_tag_imports_ = 0;
+ Index num_function_signatures_ = 0;
+ Index num_function_bodies_ = 0;
+ Index data_count_ = kInvalidIndex;
+ std::vector<Limits> memories;
+
+ using ReadEndRestoreGuard =
+ ValueRestoreGuard<size_t, &BinaryReader::read_end_>;
+};
+
+BinaryReader::BinaryReader(const void* data,
+ size_t size,
+ BinaryReaderDelegate* delegate,
+ const ReadBinaryOptions& options)
+ : read_end_(size),
+ state_(static_cast<const uint8_t*>(data), size),
+ logging_delegate_(options.log_stream, delegate),
+ delegate_(options.log_stream ? &logging_delegate_ : delegate),
+ options_(options),
+ last_known_section_(BinarySection::Invalid) {
+ delegate->OnSetState(&state_);
+}
+
+void WABT_PRINTF_FORMAT(2, 3) BinaryReader::PrintError(const char* format,
+ ...) {
+ ErrorLevel error_level =
+ reading_custom_section_ && !options_.fail_on_custom_section_error
+ ? ErrorLevel::Warning
+ : ErrorLevel::Error;
+
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ Error error(error_level, Location(state_.offset), buffer);
+ bool handled = delegate_->OnError(error);
+
+ if (!handled) {
+ // Not great to just print, but we don't want to eat the error either.
+ fprintf(stderr, "%07" PRIzx ": %s: %s\n", state_.offset,
+ GetErrorLevelName(error_level), buffer);
+ }
+}
+
+Result BinaryReader::ReportUnexpectedOpcode(Opcode opcode, const char* where) {
+ std::string message = "unexpected opcode";
+ if (where) {
+ message += ' ';
+ message += where;
+ }
+
+ message += ":";
+
+ std::vector<uint8_t> bytes = opcode.GetBytes();
+ assert(bytes.size() > 0);
+
+ for (uint8_t byte : bytes) {
+ message += StringPrintf(" 0x%x", byte);
+ }
+
+ PrintError("%s", message.c_str());
+ return Result::Error;
+}
+
+Result BinaryReader::ReadOpcode(Opcode* out_value, const char* desc) {
+ uint8_t value = 0;
+ CHECK_RESULT(ReadU8(&value, desc));
+
+ if (Opcode::IsPrefixByte(value)) {
+ uint32_t code;
+ CHECK_RESULT(ReadU32Leb128(&code, desc));
+ *out_value = Opcode::FromCode(value, code);
+ } else {
+ *out_value = Opcode::FromCode(value);
+ }
+ return Result::Ok;
+}
+
+template <typename T>
+Result BinaryReader::ReadT(T* out_value,
+ const char* type_name,
+ const char* desc) {
+ if (state_.offset + sizeof(T) > read_end_) {
+ PrintError("unable to read %s: %s", type_name, desc);
+ return Result::Error;
+ }
+#if WABT_BIG_ENDIAN
+ uint8_t tmp[sizeof(T)];
+ memcpy(tmp, state_.data + state_.offset, sizeof(tmp));
+ SwapBytesSized(tmp, sizeof(tmp));
+ memcpy(out_value, tmp, sizeof(T));
+#else
+ memcpy(out_value, state_.data + state_.offset, sizeof(T));
+#endif
+ state_.offset += sizeof(T);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadU8(uint8_t* out_value, const char* desc) {
+ return ReadT(out_value, "uint8_t", desc);
+}
+
+Result BinaryReader::ReadU32(uint32_t* out_value, const char* desc) {
+ return ReadT(out_value, "uint32_t", desc);
+}
+
+Result BinaryReader::ReadF32(uint32_t* out_value, const char* desc) {
+ return ReadT(out_value, "float", desc);
+}
+
+Result BinaryReader::ReadF64(uint64_t* out_value, const char* desc) {
+ return ReadT(out_value, "double", desc);
+}
+
+Result BinaryReader::ReadV128(v128* out_value, const char* desc) {
+ return ReadT(out_value, "v128", desc);
+}
+
+Result BinaryReader::ReadU32Leb128(uint32_t* out_value, const char* desc) {
+ const uint8_t* p = state_.data + state_.offset;
+ const uint8_t* end = state_.data + read_end_;
+ size_t bytes_read = wabt::ReadU32Leb128(p, end, out_value);
+ ERROR_UNLESS(bytes_read > 0, "unable to read u32 leb128: %s", desc);
+ state_.offset += bytes_read;
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadU64Leb128(uint64_t* out_value, const char* desc) {
+ const uint8_t* p = state_.data + state_.offset;
+ const uint8_t* end = state_.data + read_end_;
+ size_t bytes_read = wabt::ReadU64Leb128(p, end, out_value);
+ ERROR_UNLESS(bytes_read > 0, "unable to read u64 leb128: %s", desc);
+ state_.offset += bytes_read;
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadS32Leb128(uint32_t* out_value, const char* desc) {
+ const uint8_t* p = state_.data + state_.offset;
+ const uint8_t* end = state_.data + read_end_;
+ size_t bytes_read = wabt::ReadS32Leb128(p, end, out_value);
+ ERROR_UNLESS(bytes_read > 0, "unable to read i32 leb128: %s", desc);
+ state_.offset += bytes_read;
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadS64Leb128(uint64_t* out_value, const char* desc) {
+ const uint8_t* p = state_.data + state_.offset;
+ const uint8_t* end = state_.data + read_end_;
+ size_t bytes_read = wabt::ReadS64Leb128(p, end, out_value);
+ ERROR_UNLESS(bytes_read > 0, "unable to read i64 leb128: %s", desc);
+ state_.offset += bytes_read;
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadType(Type* out_value, const char* desc) {
+ uint32_t type = 0;
+ CHECK_RESULT(ReadS32Leb128(&type, desc));
+ if (static_cast<Type::Enum>(type) == Type::Reference) {
+ uint32_t heap_type = 0;
+ CHECK_RESULT(ReadS32Leb128(&heap_type, desc));
+ *out_value = Type(Type::Reference, heap_type);
+ } else {
+ *out_value = static_cast<Type>(type);
+ }
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadRefType(Type* out_value, const char* desc) {
+ uint32_t type = 0;
+ CHECK_RESULT(ReadS32Leb128(&type, desc));
+ *out_value = static_cast<Type>(type);
+ ERROR_UNLESS(out_value->IsRef(), "%s must be a reference type", desc);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadExternalKind(ExternalKind* out_value,
+ const char* desc) {
+ uint8_t value = 0;
+ CHECK_RESULT(ReadU8(&value, desc));
+ ERROR_UNLESS(value < kExternalKindCount, "invalid export external kind: %d",
+ value);
+ *out_value = static_cast<ExternalKind>(value);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadStr(std::string_view* out_str, const char* desc) {
+ uint32_t str_len = 0;
+ CHECK_RESULT(ReadU32Leb128(&str_len, "string length"));
+
+ ERROR_UNLESS(state_.offset + str_len <= read_end_,
+ "unable to read string: %s", desc);
+
+ *out_str = std::string_view(
+ reinterpret_cast<const char*>(state_.data) + state_.offset, str_len);
+ state_.offset += str_len;
+
+ ERROR_UNLESS(IsValidUtf8(out_str->data(), out_str->length()),
+ "invalid utf-8 encoding: %s", desc);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadBytes(const void** out_data,
+ Address* out_data_size,
+ const char* desc) {
+ uint32_t data_size = 0;
+ CHECK_RESULT(ReadU32Leb128(&data_size, "data size"));
+
+ ERROR_UNLESS(state_.offset + data_size <= read_end_,
+ "unable to read data: %s", desc);
+
+ *out_data = static_cast<const uint8_t*>(state_.data) + state_.offset;
+ *out_data_size = data_size;
+ state_.offset += data_size;
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadIndex(Index* index, const char* desc) {
+ uint32_t value;
+ CHECK_RESULT(ReadU32Leb128(&value, desc));
+ *index = value;
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadOffset(Offset* offset, const char* desc) {
+ uint32_t value;
+ CHECK_RESULT(ReadU32Leb128(&value, desc));
+ *offset = value;
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadAlignment(Address* alignment_log2, const char* desc) {
+ uint32_t value;
+ CHECK_RESULT(ReadU32Leb128(&value, desc));
+ if (value >= 128 ||
+ (value >= 32 && !options_.features.multi_memory_enabled())) {
+ PrintError("invalid %s: %u", desc, value);
+ return Result::Error;
+ }
+ *alignment_log2 = value;
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadMemidx(Index* memidx, const char* desc) {
+ CHECK_RESULT(ReadIndex(memidx, desc));
+ ERROR_UNLESS(*memidx < memories.size(), "memory index %u out of range",
+ *memidx);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadMemLocation(Address* alignment_log2,
+ Index* memidx,
+ Address* offset,
+ const char* desc_align,
+ const char* desc_memidx,
+ const char* desc_offset,
+ uint8_t* lane_val) {
+ CHECK_RESULT(ReadAlignment(alignment_log2, desc_align));
+ *memidx = 0;
+ if (*alignment_log2 >> 6) {
+ ERROR_IF(!options_.features.multi_memory_enabled(),
+ "multi_memory not allowed");
+ *alignment_log2 = *alignment_log2 & ((1 << 6) - 1);
+ CHECK_RESULT(ReadMemidx(memidx, desc_memidx));
+ }
+ CHECK_RESULT(ReadAddress(offset, 0, desc_offset));
+
+ if (lane_val) {
+ CHECK_RESULT(ReadU8(lane_val, "Lane idx"));
+ }
+
+ return Result::Ok;
+}
+
+Result BinaryReader::CallbackMemLocation(const Address* alignment_log2,
+ const Index* memidx,
+ const Address* offset,
+ const uint8_t* lane_val) {
+ if (lane_val) {
+ if (*memidx) {
+ CALLBACK(OnOpcodeUint32Uint32Uint32Uint32, *alignment_log2, *memidx,
+ *offset, *lane_val);
+ } else {
+ CALLBACK(OnOpcodeUint32Uint32Uint32, *alignment_log2, *offset, *lane_val);
+ }
+ } else {
+ if (*memidx) {
+ CALLBACK(OnOpcodeUint32Uint32Uint32, *alignment_log2, *memidx, *offset);
+ } else {
+ CALLBACK(OnOpcodeUint32Uint32, *alignment_log2, *offset);
+ }
+ }
+
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadCount(Index* count, const char* desc) {
+ CHECK_RESULT(ReadIndex(count, desc));
+
+ // This check assumes that each item follows in this section, and takes at
+ // least 1 byte. It's possible that this check passes but reading fails
+ // later. It is still useful to check here, though, because it early-outs
+ // when an erroneous large count is used, before allocating memory for it.
+ size_t section_remaining = read_end_ - state_.offset;
+ if (*count > section_remaining) {
+ PrintError("invalid %s %" PRIindex ", only %" PRIzd
+ " bytes left in section",
+ desc, *count, section_remaining);
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadField(TypeMut* out_value) {
+ // TODO: Reuse for global header too?
+ Type field_type;
+ CHECK_RESULT(ReadType(&field_type, "field type"));
+ ERROR_UNLESS(IsConcreteType(field_type),
+ "expected valid field type (got " PRItypecode ")",
+ WABT_PRINTF_TYPE_CODE(field_type));
+
+ uint8_t mutable_ = 0;
+ CHECK_RESULT(ReadU8(&mutable_, "field mutability"));
+ ERROR_UNLESS(mutable_ <= 1, "field mutability must be 0 or 1");
+ out_value->type = field_type;
+ out_value->mutable_ = mutable_;
+ return Result::Ok;
+}
+
+bool BinaryReader::IsConcreteType(Type type) {
+ switch (type) {
+ case Type::I32:
+ case Type::I64:
+ case Type::F32:
+ case Type::F64:
+ return true;
+
+ case Type::V128:
+ return options_.features.simd_enabled();
+
+ case Type::FuncRef:
+ case Type::ExternRef:
+ return options_.features.reference_types_enabled();
+
+ case Type::Reference:
+ return options_.features.function_references_enabled();
+
+ default:
+ return false;
+ }
+}
+
+bool BinaryReader::IsBlockType(Type type) {
+ if (IsConcreteType(type) || type == Type::Void) {
+ return true;
+ }
+
+ if (!(options_.features.multi_value_enabled() && type.IsIndex())) {
+ return false;
+ }
+
+ return true;
+}
+
+Index BinaryReader::NumTotalFuncs() {
+ return num_func_imports_ + num_function_signatures_;
+}
+
+Result BinaryReader::ReadInitExpr(Index index) {
+ // Read instructions until END opcode is reached.
+ Opcode final_opcode(Opcode::Invalid);
+ CHECK_RESULT(
+ ReadInstructions(/*stop_on_end=*/true, read_end_, &final_opcode));
+ ERROR_UNLESS(state_.offset <= read_end_,
+ "init expression longer than given size");
+ ERROR_UNLESS(final_opcode == Opcode::End,
+ "init expression must end with END opcode");
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadTable(Type* out_elem_type, Limits* out_elem_limits) {
+ CHECK_RESULT(ReadRefType(out_elem_type, "table elem type"));
+
+ uint8_t flags;
+ uint32_t initial;
+ uint32_t max = 0;
+ CHECK_RESULT(ReadU8(&flags, "table flags"));
+ bool has_max = flags & WABT_BINARY_LIMITS_HAS_MAX_FLAG;
+ bool is_shared = flags & WABT_BINARY_LIMITS_IS_SHARED_FLAG;
+ bool is_64 = flags & WABT_BINARY_LIMITS_IS_64_FLAG;
+ const uint8_t unknown_flags = flags & ~WABT_BINARY_LIMITS_ALL_FLAGS;
+ ERROR_IF(is_shared, "tables may not be shared");
+ ERROR_IF(is_64, "tables may not be 64-bit");
+ ERROR_UNLESS(unknown_flags == 0, "malformed table limits flag: %d", flags);
+ CHECK_RESULT(ReadU32Leb128(&initial, "table initial elem count"));
+ if (has_max) {
+ CHECK_RESULT(ReadU32Leb128(&max, "table max elem count"));
+ }
+
+ out_elem_limits->has_max = has_max;
+ out_elem_limits->initial = initial;
+ out_elem_limits->max = max;
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadMemory(Limits* out_page_limits) {
+ uint8_t flags;
+ uint64_t initial;
+ uint64_t max = 0;
+ CHECK_RESULT(ReadU8(&flags, "memory flags"));
+ bool has_max = flags & WABT_BINARY_LIMITS_HAS_MAX_FLAG;
+ bool is_shared = flags & WABT_BINARY_LIMITS_IS_SHARED_FLAG;
+ bool is_64 = flags & WABT_BINARY_LIMITS_IS_64_FLAG;
+ const uint8_t unknown_flags = flags & ~WABT_BINARY_LIMITS_ALL_FLAGS;
+ ERROR_UNLESS(unknown_flags == 0, "malformed memory limits flag: %d", flags);
+ ERROR_IF(is_shared && !options_.features.threads_enabled(),
+ "memory may not be shared: threads not allowed");
+ ERROR_IF(is_64 && !options_.features.memory64_enabled(),
+ "memory64 not allowed");
+ if (is_64) {
+ CHECK_RESULT(ReadU64Leb128(&initial, "memory initial page count"));
+ if (has_max) {
+ CHECK_RESULT(ReadU64Leb128(&max, "memory max page count"));
+ }
+ } else {
+ uint32_t initial32;
+ CHECK_RESULT(ReadU32Leb128(&initial32, "memory initial page count"));
+ initial = initial32;
+ if (has_max) {
+ uint32_t max32;
+ CHECK_RESULT(ReadU32Leb128(&max32, "memory max page count"));
+ max = max32;
+ }
+ }
+
+ out_page_limits->has_max = has_max;
+ out_page_limits->is_shared = is_shared;
+ out_page_limits->is_64 = is_64;
+ out_page_limits->initial = initial;
+ out_page_limits->max = max;
+
+ // Have to keep a copy of these, to know how to interpret load/stores.
+ memories.push_back(*out_page_limits);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadGlobalHeader(Type* out_type, bool* out_mutable) {
+ Type global_type = Type::Void;
+ uint8_t mutable_ = 0;
+ CHECK_RESULT(ReadType(&global_type, "global type"));
+ ERROR_UNLESS(IsConcreteType(global_type), "invalid global type: %#x",
+ static_cast<int>(global_type));
+
+ CHECK_RESULT(ReadU8(&mutable_, "global mutability"));
+ ERROR_UNLESS(mutable_ <= 1, "global mutability must be 0 or 1");
+
+ *out_type = global_type;
+ *out_mutable = mutable_;
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadAddress(Address* out_value,
+ Index memory,
+ const char* desc) {
+ ERROR_UNLESS(memory < memories.size(),
+ "load/store memory %u out of range %zu", memory,
+ memories.size());
+ if (memories[memory].is_64) {
+ return ReadU64Leb128(out_value, desc);
+ } else {
+ uint32_t val;
+ Result res = ReadU32Leb128(&val, desc);
+ *out_value = val;
+ return res;
+ }
+}
+
+Result BinaryReader::ReadFunctionBody(Offset end_offset) {
+ Opcode final_opcode(Opcode::Invalid);
+ CHECK_RESULT(
+ ReadInstructions(/*stop_on_end=*/false, end_offset, &final_opcode));
+ ERROR_UNLESS(state_.offset == end_offset,
+ "function body longer than given size");
+ ERROR_UNLESS(final_opcode == Opcode::End,
+ "function body must end with END opcode");
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadInstructions(bool stop_on_end,
+ Offset end_offset,
+ Opcode* final_opcode) {
+ while (state_.offset < end_offset) {
+ Opcode opcode;
+ CHECK_RESULT(ReadOpcode(&opcode, "opcode"));
+ CALLBACK(OnOpcode, opcode);
+ ERROR_UNLESS_OPCODE_ENABLED(opcode);
+ if (final_opcode) {
+ *final_opcode = opcode;
+ }
+
+ switch (opcode) {
+ case Opcode::Unreachable:
+ CALLBACK0(OnUnreachableExpr);
+ CALLBACK0(OnOpcodeBare);
+ break;
+
+ case Opcode::Block: {
+ Type sig_type;
+ CHECK_RESULT(ReadType(&sig_type, "block signature type"));
+ ERROR_UNLESS(IsBlockType(sig_type),
+ "expected valid block signature type");
+ CALLBACK(OnBlockExpr, sig_type);
+ CALLBACK(OnOpcodeBlockSig, sig_type);
+ break;
+ }
+
+ case Opcode::Loop: {
+ Type sig_type;
+ CHECK_RESULT(ReadType(&sig_type, "loop signature type"));
+ ERROR_UNLESS(IsBlockType(sig_type),
+ "expected valid block signature type");
+ CALLBACK(OnLoopExpr, sig_type);
+ CALLBACK(OnOpcodeBlockSig, sig_type);
+ break;
+ }
+
+ case Opcode::If: {
+ Type sig_type;
+ CHECK_RESULT(ReadType(&sig_type, "if signature type"));
+ ERROR_UNLESS(IsBlockType(sig_type),
+ "expected valid block signature type");
+ CALLBACK(OnIfExpr, sig_type);
+ CALLBACK(OnOpcodeBlockSig, sig_type);
+ break;
+ }
+
+ case Opcode::Else:
+ CALLBACK0(OnElseExpr);
+ CALLBACK0(OnOpcodeBare);
+ break;
+
+ case Opcode::SelectT: {
+ Index num_results;
+ CHECK_RESULT(ReadCount(&num_results, "num result types"));
+
+ result_types_.resize(num_results);
+ for (Index i = 0; i < num_results; ++i) {
+ Type result_type;
+ CHECK_RESULT(ReadType(&result_type, "select result type"));
+ ERROR_UNLESS(IsConcreteType(result_type),
+ "expected valid select result type (got " PRItypecode
+ ")",
+ WABT_PRINTF_TYPE_CODE(result_type));
+ result_types_[i] = result_type;
+ }
+
+ if (num_results) {
+ CALLBACK(OnSelectExpr, num_results, result_types_.data());
+ CALLBACK(OnOpcodeType, result_types_[0]);
+ } else {
+ CALLBACK(OnSelectExpr, 0, NULL);
+ CALLBACK0(OnOpcodeBare);
+ }
+ break;
+ }
+
+ case Opcode::Select:
+ CALLBACK(OnSelectExpr, 0, nullptr);
+ CALLBACK0(OnOpcodeBare);
+ break;
+
+ case Opcode::Br: {
+ Index depth;
+ CHECK_RESULT(ReadIndex(&depth, "br depth"));
+ CALLBACK(OnBrExpr, depth);
+ CALLBACK(OnOpcodeIndex, depth);
+ break;
+ }
+
+ case Opcode::BrIf: {
+ Index depth;
+ CHECK_RESULT(ReadIndex(&depth, "br_if depth"));
+ CALLBACK(OnBrIfExpr, depth);
+ CALLBACK(OnOpcodeIndex, depth);
+ break;
+ }
+
+ case Opcode::BrTable: {
+ Index num_targets;
+ CHECK_RESULT(ReadCount(&num_targets, "br_table target count"));
+ target_depths_.resize(num_targets);
+
+ for (Index i = 0; i < num_targets; ++i) {
+ Index target_depth;
+ CHECK_RESULT(ReadIndex(&target_depth, "br_table target depth"));
+ target_depths_[i] = target_depth;
+ }
+
+ Index default_target_depth;
+ CHECK_RESULT(
+ ReadIndex(&default_target_depth, "br_table default target depth"));
+
+ Index* target_depths = num_targets ? target_depths_.data() : nullptr;
+
+ CALLBACK(OnBrTableExpr, num_targets, target_depths,
+ default_target_depth);
+ break;
+ }
+
+ case Opcode::Return:
+ CALLBACK0(OnReturnExpr);
+ CALLBACK0(OnOpcodeBare);
+ break;
+
+ case Opcode::Nop:
+ CALLBACK0(OnNopExpr);
+ CALLBACK0(OnOpcodeBare);
+ break;
+
+ case Opcode::Drop:
+ CALLBACK0(OnDropExpr);
+ CALLBACK0(OnOpcodeBare);
+ break;
+
+ case Opcode::End:
+ CALLBACK0(OnEndExpr);
+ if (stop_on_end) {
+ return Result::Ok;
+ }
+ break;
+
+ case Opcode::I32Const: {
+ uint32_t value;
+ CHECK_RESULT(ReadS32Leb128(&value, "i32.const value"));
+ CALLBACK(OnI32ConstExpr, value);
+ CALLBACK(OnOpcodeUint32, value);
+ break;
+ }
+
+ case Opcode::I64Const: {
+ uint64_t value;
+ CHECK_RESULT(ReadS64Leb128(&value, "i64.const value"));
+ CALLBACK(OnI64ConstExpr, value);
+ CALLBACK(OnOpcodeUint64, value);
+ break;
+ }
+
+ case Opcode::F32Const: {
+ uint32_t value_bits = 0;
+ CHECK_RESULT(ReadF32(&value_bits, "f32.const value"));
+ CALLBACK(OnF32ConstExpr, value_bits);
+ CALLBACK(OnOpcodeF32, value_bits);
+ break;
+ }
+
+ case Opcode::F64Const: {
+ uint64_t value_bits = 0;
+ CHECK_RESULT(ReadF64(&value_bits, "f64.const value"));
+ CALLBACK(OnF64ConstExpr, value_bits);
+ CALLBACK(OnOpcodeF64, value_bits);
+ break;
+ }
+
+ case Opcode::V128Const: {
+ v128 value_bits;
+ ZeroMemory(value_bits);
+ CHECK_RESULT(ReadV128(&value_bits, "v128.const value"));
+ CALLBACK(OnV128ConstExpr, value_bits);
+ CALLBACK(OnOpcodeV128, value_bits);
+ break;
+ }
+
+ case Opcode::GlobalGet: {
+ Index global_index;
+ CHECK_RESULT(ReadIndex(&global_index, "global.get global index"));
+ CALLBACK(OnGlobalGetExpr, global_index);
+ CALLBACK(OnOpcodeIndex, global_index);
+ break;
+ }
+
+ case Opcode::LocalGet: {
+ Index local_index;
+ CHECK_RESULT(ReadIndex(&local_index, "local.get local index"));
+ CALLBACK(OnLocalGetExpr, local_index);
+ CALLBACK(OnOpcodeIndex, local_index);
+ break;
+ }
+
+ case Opcode::GlobalSet: {
+ Index global_index;
+ CHECK_RESULT(ReadIndex(&global_index, "global.set global index"));
+ CALLBACK(OnGlobalSetExpr, global_index);
+ CALLBACK(OnOpcodeIndex, global_index);
+ break;
+ }
+
+ case Opcode::LocalSet: {
+ Index local_index;
+ CHECK_RESULT(ReadIndex(&local_index, "local.set local index"));
+ CALLBACK(OnLocalSetExpr, local_index);
+ CALLBACK(OnOpcodeIndex, local_index);
+ break;
+ }
+
+ case Opcode::Call: {
+ Index func_index;
+ CHECK_RESULT(ReadIndex(&func_index, "call function index"));
+ CALLBACK(OnCallExpr, func_index);
+ CALLBACK(OnOpcodeIndex, func_index);
+ break;
+ }
+
+ case Opcode::CallIndirect: {
+ Index sig_index;
+ CHECK_RESULT(ReadIndex(&sig_index, "call_indirect signature index"));
+ Index table_index = 0;
+ if (options_.features.reference_types_enabled()) {
+ CHECK_RESULT(ReadIndex(&table_index, "call_indirect table index"));
+ } else {
+ uint8_t reserved;
+ CHECK_RESULT(ReadU8(&reserved, "call_indirect reserved"));
+ ERROR_UNLESS(reserved == 0, "call_indirect reserved value must be 0");
+ }
+ CALLBACK(OnCallIndirectExpr, sig_index, table_index);
+ CALLBACK(OnOpcodeUint32Uint32, sig_index, table_index);
+ break;
+ }
+
+ case Opcode::ReturnCall: {
+ Index func_index;
+ CHECK_RESULT(ReadIndex(&func_index, "return_call"));
+ CALLBACK(OnReturnCallExpr, func_index);
+ CALLBACK(OnOpcodeIndex, func_index);
+ break;
+ }
+
+ case Opcode::ReturnCallIndirect: {
+ Index sig_index;
+ CHECK_RESULT(ReadIndex(&sig_index, "return_call_indirect"));
+ Index table_index = 0;
+ if (options_.features.reference_types_enabled()) {
+ CHECK_RESULT(
+ ReadIndex(&table_index, "return_call_indirect table index"));
+ } else {
+ uint8_t reserved;
+ CHECK_RESULT(ReadU8(&reserved, "return_call_indirect reserved"));
+ ERROR_UNLESS(reserved == 0,
+ "return_call_indirect reserved value must be 0");
+ }
+ CALLBACK(OnReturnCallIndirectExpr, sig_index, table_index);
+ CALLBACK(OnOpcodeUint32Uint32, sig_index, table_index);
+ break;
+ }
+
+ case Opcode::LocalTee: {
+ Index local_index;
+ CHECK_RESULT(ReadIndex(&local_index, "local.tee local index"));
+ CALLBACK(OnLocalTeeExpr, local_index);
+ CALLBACK(OnOpcodeIndex, local_index);
+ break;
+ }
+
+ case Opcode::I32Load8S:
+ case Opcode::I32Load8U:
+ case Opcode::I32Load16S:
+ case Opcode::I32Load16U:
+ case Opcode::I64Load8S:
+ case Opcode::I64Load8U:
+ case Opcode::I64Load16S:
+ case Opcode::I64Load16U:
+ case Opcode::I64Load32S:
+ case Opcode::I64Load32U:
+ case Opcode::I32Load:
+ case Opcode::I64Load:
+ case Opcode::F32Load:
+ case Opcode::F64Load:
+ case Opcode::V128Load:
+ case Opcode::V128Load8X8S:
+ case Opcode::V128Load8X8U:
+ case Opcode::V128Load16X4S:
+ case Opcode::V128Load16X4U:
+ case Opcode::V128Load32X2S:
+ case Opcode::V128Load32X2U: {
+ Address alignment_log2;
+ Index memidx;
+ Address offset;
+ CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
+ "load alignment", "load memidx",
+ "load offset"));
+ CALLBACK(OnLoadExpr, opcode, memidx, alignment_log2, offset);
+ CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
+ break;
+ }
+
+ case Opcode::I32Store8:
+ case Opcode::I32Store16:
+ case Opcode::I64Store8:
+ case Opcode::I64Store16:
+ case Opcode::I64Store32:
+ case Opcode::I32Store:
+ case Opcode::I64Store:
+ case Opcode::F32Store:
+ case Opcode::F64Store:
+ case Opcode::V128Store: {
+ Address alignment_log2;
+ Index memidx;
+ Address offset;
+ CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
+ "store alignment", "store memidx",
+ "store offset"));
+ CALLBACK(OnStoreExpr, opcode, memidx, alignment_log2, offset);
+ CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
+ break;
+ }
+
+ case Opcode::MemorySize: {
+ Index memidx = 0;
+ if (!options_.features.multi_memory_enabled()) {
+ uint8_t reserved;
+ CHECK_RESULT(ReadU8(&reserved, "memory.size reserved"));
+ ERROR_UNLESS(reserved == 0, "memory.size reserved value must be 0");
+ } else {
+ CHECK_RESULT(ReadMemidx(&memidx, "memory.size memidx"));
+ }
+ CALLBACK(OnMemorySizeExpr, memidx);
+ CALLBACK(OnOpcodeUint32, memidx);
+ break;
+ }
+
+ case Opcode::MemoryGrow: {
+ Index memidx = 0;
+ if (!options_.features.multi_memory_enabled()) {
+ uint8_t reserved;
+ CHECK_RESULT(ReadU8(&reserved, "memory.grow reserved"));
+ ERROR_UNLESS(reserved == 0, "memory.grow reserved value must be 0");
+ } else {
+ CHECK_RESULT(ReadMemidx(&memidx, "memory.grow memidx"));
+ }
+ CALLBACK(OnMemoryGrowExpr, memidx);
+ CALLBACK(OnOpcodeUint32, memidx);
+ break;
+ }
+
+ case Opcode::I32Add:
+ case Opcode::I32Sub:
+ case Opcode::I32Mul:
+ case Opcode::I32DivS:
+ case Opcode::I32DivU:
+ case Opcode::I32RemS:
+ case Opcode::I32RemU:
+ case Opcode::I32And:
+ case Opcode::I32Or:
+ case Opcode::I32Xor:
+ case Opcode::I32Shl:
+ case Opcode::I32ShrU:
+ case Opcode::I32ShrS:
+ case Opcode::I32Rotr:
+ case Opcode::I32Rotl:
+ case Opcode::I64Add:
+ case Opcode::I64Sub:
+ case Opcode::I64Mul:
+ case Opcode::I64DivS:
+ case Opcode::I64DivU:
+ case Opcode::I64RemS:
+ case Opcode::I64RemU:
+ case Opcode::I64And:
+ case Opcode::I64Or:
+ case Opcode::I64Xor:
+ case Opcode::I64Shl:
+ case Opcode::I64ShrU:
+ case Opcode::I64ShrS:
+ case Opcode::I64Rotr:
+ case Opcode::I64Rotl:
+ case Opcode::F32Add:
+ case Opcode::F32Sub:
+ case Opcode::F32Mul:
+ case Opcode::F32Div:
+ case Opcode::F32Min:
+ case Opcode::F32Max:
+ case Opcode::F32Copysign:
+ case Opcode::F64Add:
+ case Opcode::F64Sub:
+ case Opcode::F64Mul:
+ case Opcode::F64Div:
+ case Opcode::F64Min:
+ case Opcode::F64Max:
+ case Opcode::F64Copysign:
+ case Opcode::I8X16Add:
+ case Opcode::I16X8Add:
+ case Opcode::I32X4Add:
+ case Opcode::I64X2Add:
+ case Opcode::I8X16Sub:
+ case Opcode::I16X8Sub:
+ case Opcode::I32X4Sub:
+ case Opcode::I64X2Sub:
+ case Opcode::I16X8Mul:
+ case Opcode::I32X4Mul:
+ case Opcode::I64X2Mul:
+ case Opcode::I8X16AddSatS:
+ case Opcode::I8X16AddSatU:
+ case Opcode::I16X8AddSatS:
+ case Opcode::I16X8AddSatU:
+ case Opcode::I8X16SubSatS:
+ case Opcode::I8X16SubSatU:
+ case Opcode::I16X8SubSatS:
+ case Opcode::I16X8SubSatU:
+ case Opcode::I8X16MinS:
+ case Opcode::I16X8MinS:
+ case Opcode::I32X4MinS:
+ case Opcode::I8X16MinU:
+ case Opcode::I16X8MinU:
+ case Opcode::I32X4MinU:
+ case Opcode::I8X16MaxS:
+ case Opcode::I16X8MaxS:
+ case Opcode::I32X4MaxS:
+ case Opcode::I8X16MaxU:
+ case Opcode::I16X8MaxU:
+ case Opcode::I32X4MaxU:
+ case Opcode::I8X16Shl:
+ case Opcode::I16X8Shl:
+ case Opcode::I32X4Shl:
+ case Opcode::I64X2Shl:
+ case Opcode::I8X16ShrS:
+ case Opcode::I8X16ShrU:
+ case Opcode::I16X8ShrS:
+ case Opcode::I16X8ShrU:
+ case Opcode::I32X4ShrS:
+ case Opcode::I32X4ShrU:
+ case Opcode::I64X2ShrS:
+ case Opcode::I64X2ShrU:
+ case Opcode::V128And:
+ case Opcode::V128Or:
+ case Opcode::V128Xor:
+ case Opcode::F32X4Min:
+ case Opcode::F32X4PMin:
+ case Opcode::F64X2Min:
+ case Opcode::F64X2PMin:
+ case Opcode::F32X4Max:
+ case Opcode::F32X4PMax:
+ case Opcode::F64X2Max:
+ case Opcode::F64X2PMax:
+ case Opcode::F32X4Add:
+ case Opcode::F64X2Add:
+ case Opcode::F32X4Sub:
+ case Opcode::F64X2Sub:
+ case Opcode::F32X4Div:
+ case Opcode::F64X2Div:
+ case Opcode::F32X4Mul:
+ case Opcode::F64X2Mul:
+ case Opcode::I8X16Swizzle:
+ case Opcode::I8X16NarrowI16X8S:
+ case Opcode::I8X16NarrowI16X8U:
+ case Opcode::I16X8NarrowI32X4S:
+ case Opcode::I16X8NarrowI32X4U:
+ case Opcode::V128Andnot:
+ case Opcode::I8X16AvgrU:
+ case Opcode::I16X8AvgrU:
+ case Opcode::I16X8ExtmulLowI8X16S:
+ case Opcode::I16X8ExtmulHighI8X16S:
+ case Opcode::I16X8ExtmulLowI8X16U:
+ case Opcode::I16X8ExtmulHighI8X16U:
+ case Opcode::I32X4ExtmulLowI16X8S:
+ case Opcode::I32X4ExtmulHighI16X8S:
+ case Opcode::I32X4ExtmulLowI16X8U:
+ case Opcode::I32X4ExtmulHighI16X8U:
+ case Opcode::I64X2ExtmulLowI32X4S:
+ case Opcode::I64X2ExtmulHighI32X4S:
+ case Opcode::I64X2ExtmulLowI32X4U:
+ case Opcode::I64X2ExtmulHighI32X4U:
+ case Opcode::I16X8Q15mulrSatS:
+ case Opcode::I32X4DotI16X8S:
+ case Opcode::I8X16RelaxedSwizzle:
+ case Opcode::F32X4RelaxedMin:
+ case Opcode::F32X4RelaxedMax:
+ case Opcode::F64X2RelaxedMin:
+ case Opcode::F64X2RelaxedMax:
+ case Opcode::I16X8RelaxedQ15mulrS:
+ case Opcode::I16X8DotI8X16I7X16S:
+ CALLBACK(OnBinaryExpr, opcode);
+ CALLBACK0(OnOpcodeBare);
+ break;
+
+ case Opcode::I32Eq:
+ case Opcode::I32Ne:
+ case Opcode::I32LtS:
+ case Opcode::I32LeS:
+ case Opcode::I32LtU:
+ case Opcode::I32LeU:
+ case Opcode::I32GtS:
+ case Opcode::I32GeS:
+ case Opcode::I32GtU:
+ case Opcode::I32GeU:
+ case Opcode::I64Eq:
+ case Opcode::I64Ne:
+ case Opcode::I64LtS:
+ case Opcode::I64LeS:
+ case Opcode::I64LtU:
+ case Opcode::I64LeU:
+ case Opcode::I64GtS:
+ case Opcode::I64GeS:
+ case Opcode::I64GtU:
+ case Opcode::I64GeU:
+ case Opcode::F32Eq:
+ case Opcode::F32Ne:
+ case Opcode::F32Lt:
+ case Opcode::F32Le:
+ case Opcode::F32Gt:
+ case Opcode::F32Ge:
+ case Opcode::F64Eq:
+ case Opcode::F64Ne:
+ case Opcode::F64Lt:
+ case Opcode::F64Le:
+ case Opcode::F64Gt:
+ case Opcode::F64Ge:
+ case Opcode::I8X16Eq:
+ case Opcode::I16X8Eq:
+ case Opcode::I32X4Eq:
+ case Opcode::I64X2Eq:
+ case Opcode::F32X4Eq:
+ case Opcode::F64X2Eq:
+ case Opcode::I8X16Ne:
+ case Opcode::I16X8Ne:
+ case Opcode::I32X4Ne:
+ case Opcode::I64X2Ne:
+ case Opcode::F32X4Ne:
+ case Opcode::F64X2Ne:
+ case Opcode::I8X16LtS:
+ case Opcode::I8X16LtU:
+ case Opcode::I16X8LtS:
+ case Opcode::I16X8LtU:
+ case Opcode::I32X4LtS:
+ case Opcode::I32X4LtU:
+ case Opcode::I64X2LtS:
+ case Opcode::F32X4Lt:
+ case Opcode::F64X2Lt:
+ case Opcode::I8X16LeS:
+ case Opcode::I8X16LeU:
+ case Opcode::I16X8LeS:
+ case Opcode::I16X8LeU:
+ case Opcode::I32X4LeS:
+ case Opcode::I32X4LeU:
+ case Opcode::I64X2LeS:
+ case Opcode::F32X4Le:
+ case Opcode::F64X2Le:
+ case Opcode::I8X16GtS:
+ case Opcode::I8X16GtU:
+ case Opcode::I16X8GtS:
+ case Opcode::I16X8GtU:
+ case Opcode::I32X4GtS:
+ case Opcode::I32X4GtU:
+ case Opcode::I64X2GtS:
+ case Opcode::F32X4Gt:
+ case Opcode::F64X2Gt:
+ case Opcode::I8X16GeS:
+ case Opcode::I8X16GeU:
+ case Opcode::I16X8GeS:
+ case Opcode::I16X8GeU:
+ case Opcode::I32X4GeS:
+ case Opcode::I32X4GeU:
+ case Opcode::I64X2GeS:
+ case Opcode::F32X4Ge:
+ case Opcode::F64X2Ge:
+ CALLBACK(OnCompareExpr, opcode);
+ CALLBACK0(OnOpcodeBare);
+ break;
+
+ case Opcode::I32Clz:
+ case Opcode::I32Ctz:
+ case Opcode::I32Popcnt:
+ case Opcode::I64Clz:
+ case Opcode::I64Ctz:
+ case Opcode::I64Popcnt:
+ case Opcode::F32Abs:
+ case Opcode::F32Neg:
+ case Opcode::F32Ceil:
+ case Opcode::F32Floor:
+ case Opcode::F32Trunc:
+ case Opcode::F32Nearest:
+ case Opcode::F32Sqrt:
+ case Opcode::F64Abs:
+ case Opcode::F64Neg:
+ case Opcode::F64Ceil:
+ case Opcode::F64Floor:
+ case Opcode::F64Trunc:
+ case Opcode::F64Nearest:
+ case Opcode::F64Sqrt:
+ case Opcode::I8X16Splat:
+ case Opcode::I16X8Splat:
+ case Opcode::I32X4Splat:
+ case Opcode::I64X2Splat:
+ case Opcode::F32X4Splat:
+ case Opcode::F64X2Splat:
+ case Opcode::I8X16Neg:
+ case Opcode::I16X8Neg:
+ case Opcode::I32X4Neg:
+ case Opcode::I64X2Neg:
+ case Opcode::V128Not:
+ case Opcode::V128AnyTrue:
+ case Opcode::I8X16Bitmask:
+ case Opcode::I16X8Bitmask:
+ case Opcode::I32X4Bitmask:
+ case Opcode::I64X2Bitmask:
+ case Opcode::I8X16AllTrue:
+ case Opcode::I16X8AllTrue:
+ case Opcode::I32X4AllTrue:
+ case Opcode::I64X2AllTrue:
+ case Opcode::F32X4Ceil:
+ case Opcode::F64X2Ceil:
+ case Opcode::F32X4Floor:
+ case Opcode::F64X2Floor:
+ case Opcode::F32X4Trunc:
+ case Opcode::F64X2Trunc:
+ case Opcode::F32X4Nearest:
+ case Opcode::F64X2Nearest:
+ case Opcode::F32X4Neg:
+ case Opcode::F64X2Neg:
+ case Opcode::F32X4Abs:
+ case Opcode::F64X2Abs:
+ case Opcode::F32X4Sqrt:
+ case Opcode::F64X2Sqrt:
+ case Opcode::I16X8ExtendLowI8X16S:
+ case Opcode::I16X8ExtendHighI8X16S:
+ case Opcode::I16X8ExtendLowI8X16U:
+ case Opcode::I16X8ExtendHighI8X16U:
+ case Opcode::I32X4ExtendLowI16X8S:
+ case Opcode::I32X4ExtendHighI16X8S:
+ case Opcode::I32X4ExtendLowI16X8U:
+ case Opcode::I32X4ExtendHighI16X8U:
+ case Opcode::I64X2ExtendLowI32X4S:
+ case Opcode::I64X2ExtendHighI32X4S:
+ case Opcode::I64X2ExtendLowI32X4U:
+ case Opcode::I64X2ExtendHighI32X4U:
+ case Opcode::I8X16Abs:
+ case Opcode::I16X8Abs:
+ case Opcode::I32X4Abs:
+ case Opcode::I64X2Abs:
+ case Opcode::I8X16Popcnt:
+ case Opcode::I16X8ExtaddPairwiseI8X16S:
+ case Opcode::I16X8ExtaddPairwiseI8X16U:
+ case Opcode::I32X4ExtaddPairwiseI16X8S:
+ case Opcode::I32X4ExtaddPairwiseI16X8U:
+ case Opcode::I32X4RelaxedTruncF32X4S:
+ case Opcode::I32X4RelaxedTruncF32X4U:
+ case Opcode::I32X4RelaxedTruncF64X2SZero:
+ case Opcode::I32X4RelaxedTruncF64X2UZero:
+ CALLBACK(OnUnaryExpr, opcode);
+ CALLBACK0(OnOpcodeBare);
+ break;
+
+ case Opcode::V128BitSelect:
+ case Opcode::F32X4RelaxedMadd:
+ case Opcode::F32X4RelaxedNmadd:
+ case Opcode::F64X2RelaxedMadd:
+ case Opcode::F64X2RelaxedNmadd:
+ case Opcode::I8X16RelaxedLaneSelect:
+ case Opcode::I16X8RelaxedLaneSelect:
+ case Opcode::I32X4RelaxedLaneSelect:
+ case Opcode::I64X2RelaxedLaneSelect:
+ case Opcode::I32X4DotI8X16I7X16AddS:
+ CALLBACK(OnTernaryExpr, opcode);
+ CALLBACK0(OnOpcodeBare);
+ break;
+
+ case Opcode::I8X16ExtractLaneS:
+ case Opcode::I8X16ExtractLaneU:
+ case Opcode::I16X8ExtractLaneS:
+ case Opcode::I16X8ExtractLaneU:
+ case Opcode::I32X4ExtractLane:
+ case Opcode::I64X2ExtractLane:
+ case Opcode::F32X4ExtractLane:
+ case Opcode::F64X2ExtractLane:
+ case Opcode::I8X16ReplaceLane:
+ case Opcode::I16X8ReplaceLane:
+ case Opcode::I32X4ReplaceLane:
+ case Opcode::I64X2ReplaceLane:
+ case Opcode::F32X4ReplaceLane:
+ case Opcode::F64X2ReplaceLane: {
+ uint8_t lane_val;
+ CHECK_RESULT(ReadU8(&lane_val, "Lane idx"));
+ CALLBACK(OnSimdLaneOpExpr, opcode, lane_val);
+ CALLBACK(OnOpcodeUint64, lane_val);
+ break;
+ }
+
+ case Opcode::I8X16Shuffle: {
+ v128 value;
+ CHECK_RESULT(ReadV128(&value, "Lane idx [16]"));
+ CALLBACK(OnSimdShuffleOpExpr, opcode, value);
+ CALLBACK(OnOpcodeV128, value);
+ break;
+ }
+
+ case Opcode::V128Load8Splat:
+ case Opcode::V128Load16Splat:
+ case Opcode::V128Load32Splat:
+ case Opcode::V128Load64Splat: {
+ Address alignment_log2;
+ Index memidx;
+ Address offset;
+ CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
+ "load alignment", "load memidx",
+ "load offset"));
+ CALLBACK(OnLoadSplatExpr, opcode, memidx, alignment_log2, offset);
+ CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
+ break;
+ }
+ case Opcode::V128Load8Lane:
+ case Opcode::V128Load16Lane:
+ case Opcode::V128Load32Lane:
+ case Opcode::V128Load64Lane: {
+ Address alignment_log2;
+ Index memidx;
+ Address offset;
+ uint8_t lane_val;
+ CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
+ "load alignment", "load memidx",
+ "load offset", &lane_val));
+ CALLBACK(OnSimdLoadLaneExpr, opcode, memidx, alignment_log2, offset,
+ lane_val);
+ CHECK_RESULT(
+ CallbackMemLocation(&alignment_log2, &memidx, &offset, &lane_val));
+ break;
+ }
+ case Opcode::V128Store8Lane:
+ case Opcode::V128Store16Lane:
+ case Opcode::V128Store32Lane:
+ case Opcode::V128Store64Lane: {
+ Address alignment_log2;
+ Index memidx;
+ Address offset;
+ uint8_t lane_val;
+ CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
+ "store alignment", "store memidx",
+ "store offset", &lane_val));
+ CALLBACK(OnSimdStoreLaneExpr, opcode, memidx, alignment_log2, offset,
+ lane_val);
+ CHECK_RESULT(
+ CallbackMemLocation(&alignment_log2, &memidx, &offset, &lane_val));
+ break;
+ }
+ case Opcode::V128Load32Zero:
+ case Opcode::V128Load64Zero: {
+ Address alignment_log2;
+ Index memidx;
+ Address offset;
+ CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
+ "load alignment", "load memidx",
+ "load offset"));
+ CALLBACK(OnLoadZeroExpr, opcode, memidx, alignment_log2, offset);
+ CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
+ break;
+ }
+ case Opcode::I32TruncF32S:
+ case Opcode::I32TruncF64S:
+ case Opcode::I32TruncF32U:
+ case Opcode::I32TruncF64U:
+ case Opcode::I32WrapI64:
+ case Opcode::I64TruncF32S:
+ case Opcode::I64TruncF64S:
+ case Opcode::I64TruncF32U:
+ case Opcode::I64TruncF64U:
+ case Opcode::I64ExtendI32S:
+ case Opcode::I64ExtendI32U:
+ case Opcode::F32ConvertI32S:
+ case Opcode::F32ConvertI32U:
+ case Opcode::F32ConvertI64S:
+ case Opcode::F32ConvertI64U:
+ case Opcode::F32DemoteF64:
+ case Opcode::F32ReinterpretI32:
+ case Opcode::F64ConvertI32S:
+ case Opcode::F64ConvertI32U:
+ case Opcode::F64ConvertI64S:
+ case Opcode::F64ConvertI64U:
+ case Opcode::F64PromoteF32:
+ case Opcode::F64ReinterpretI64:
+ case Opcode::I32ReinterpretF32:
+ case Opcode::I64ReinterpretF64:
+ case Opcode::I32Eqz:
+ case Opcode::I64Eqz:
+ case Opcode::F32X4ConvertI32X4S:
+ case Opcode::F32X4ConvertI32X4U:
+ case Opcode::I32X4TruncSatF32X4S:
+ case Opcode::I32X4TruncSatF32X4U:
+ case Opcode::F32X4DemoteF64X2Zero:
+ case Opcode::F64X2PromoteLowF32X4:
+ case Opcode::I32X4TruncSatF64X2SZero:
+ case Opcode::I32X4TruncSatF64X2UZero:
+ case Opcode::F64X2ConvertLowI32X4S:
+ case Opcode::F64X2ConvertLowI32X4U:
+ CALLBACK(OnConvertExpr, opcode);
+ CALLBACK0(OnOpcodeBare);
+ break;
+
+ case Opcode::Try: {
+ Type sig_type;
+ CHECK_RESULT(ReadType(&sig_type, "try signature type"));
+ ERROR_UNLESS(IsBlockType(sig_type),
+ "expected valid block signature type");
+ CALLBACK(OnTryExpr, sig_type);
+ CALLBACK(OnOpcodeBlockSig, sig_type);
+ break;
+ }
+
+ case Opcode::Catch: {
+ Index index;
+ CHECK_RESULT(ReadIndex(&index, "tag index"));
+ CALLBACK(OnCatchExpr, index);
+ CALLBACK(OnOpcodeIndex, index);
+ break;
+ }
+
+ case Opcode::CatchAll: {
+ CALLBACK(OnCatchAllExpr);
+ CALLBACK(OnOpcodeBare);
+ break;
+ }
+
+ case Opcode::Delegate: {
+ Index index;
+ CHECK_RESULT(ReadIndex(&index, "depth"));
+ CALLBACK(OnDelegateExpr, index);
+ CALLBACK(OnOpcodeIndex, index);
+ break;
+ }
+
+ case Opcode::Rethrow: {
+ Index depth;
+ CHECK_RESULT(ReadIndex(&depth, "catch depth"));
+ CALLBACK(OnRethrowExpr, depth);
+ CALLBACK(OnOpcodeIndex, depth);
+ break;
+ }
+
+ case Opcode::Throw: {
+ Index index;
+ CHECK_RESULT(ReadIndex(&index, "tag index"));
+ CALLBACK(OnThrowExpr, index);
+ CALLBACK(OnOpcodeIndex, index);
+ break;
+ }
+
+ case Opcode::I32Extend8S:
+ case Opcode::I32Extend16S:
+ case Opcode::I64Extend8S:
+ case Opcode::I64Extend16S:
+ case Opcode::I64Extend32S:
+ CALLBACK(OnUnaryExpr, opcode);
+ CALLBACK0(OnOpcodeBare);
+ break;
+
+ case Opcode::I32TruncSatF32S:
+ case Opcode::I32TruncSatF32U:
+ case Opcode::I32TruncSatF64S:
+ case Opcode::I32TruncSatF64U:
+ case Opcode::I64TruncSatF32S:
+ case Opcode::I64TruncSatF32U:
+ case Opcode::I64TruncSatF64S:
+ case Opcode::I64TruncSatF64U:
+ CALLBACK(OnConvertExpr, opcode);
+ CALLBACK0(OnOpcodeBare);
+ break;
+
+ case Opcode::MemoryAtomicNotify: {
+ Address alignment_log2;
+ Index memidx;
+ Address offset;
+ CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
+ "notify alignment", "notify memidx",
+ "notify offset"));
+ CALLBACK(OnAtomicNotifyExpr, opcode, memidx, alignment_log2, offset);
+ CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
+ break;
+ }
+
+ case Opcode::MemoryAtomicWait32:
+ case Opcode::MemoryAtomicWait64: {
+ Address alignment_log2;
+ Index memidx;
+ Address offset;
+ CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
+ "wait alignment", "wait memidx",
+ "wait offset"));
+ CALLBACK(OnAtomicWaitExpr, opcode, memidx, alignment_log2, offset);
+ CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
+ break;
+ }
+
+ case Opcode::AtomicFence: {
+ uint8_t consistency_model;
+ CHECK_RESULT(ReadU8(&consistency_model, "consistency model"));
+ ERROR_UNLESS(consistency_model == 0,
+ "atomic.fence consistency model must be 0");
+ CALLBACK(OnAtomicFenceExpr, consistency_model);
+ CALLBACK(OnOpcodeUint32, consistency_model);
+ break;
+ }
+
+ case Opcode::I32AtomicLoad8U:
+ case Opcode::I32AtomicLoad16U:
+ case Opcode::I64AtomicLoad8U:
+ case Opcode::I64AtomicLoad16U:
+ case Opcode::I64AtomicLoad32U:
+ case Opcode::I32AtomicLoad:
+ case Opcode::I64AtomicLoad: {
+ Address alignment_log2;
+ Index memidx;
+ Address offset;
+ CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
+ "load alignment", "load memidx",
+ "load offset"));
+ CALLBACK(OnAtomicLoadExpr, opcode, memidx, alignment_log2, offset);
+ CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
+ break;
+ }
+
+ case Opcode::I32AtomicStore8:
+ case Opcode::I32AtomicStore16:
+ case Opcode::I64AtomicStore8:
+ case Opcode::I64AtomicStore16:
+ case Opcode::I64AtomicStore32:
+ case Opcode::I32AtomicStore:
+ case Opcode::I64AtomicStore: {
+ Address alignment_log2;
+ Index memidx;
+ Address offset;
+ CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
+ "store alignment", "store memidx",
+ "store offset"));
+ CALLBACK(OnAtomicStoreExpr, opcode, memidx, alignment_log2, offset);
+ CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
+ break;
+ }
+
+ case Opcode::I32AtomicRmwAdd:
+ case Opcode::I64AtomicRmwAdd:
+ case Opcode::I32AtomicRmw8AddU:
+ case Opcode::I32AtomicRmw16AddU:
+ case Opcode::I64AtomicRmw8AddU:
+ case Opcode::I64AtomicRmw16AddU:
+ case Opcode::I64AtomicRmw32AddU:
+ case Opcode::I32AtomicRmwSub:
+ case Opcode::I64AtomicRmwSub:
+ case Opcode::I32AtomicRmw8SubU:
+ case Opcode::I32AtomicRmw16SubU:
+ case Opcode::I64AtomicRmw8SubU:
+ case Opcode::I64AtomicRmw16SubU:
+ case Opcode::I64AtomicRmw32SubU:
+ case Opcode::I32AtomicRmwAnd:
+ case Opcode::I64AtomicRmwAnd:
+ case Opcode::I32AtomicRmw8AndU:
+ case Opcode::I32AtomicRmw16AndU:
+ case Opcode::I64AtomicRmw8AndU:
+ case Opcode::I64AtomicRmw16AndU:
+ case Opcode::I64AtomicRmw32AndU:
+ case Opcode::I32AtomicRmwOr:
+ case Opcode::I64AtomicRmwOr:
+ case Opcode::I32AtomicRmw8OrU:
+ case Opcode::I32AtomicRmw16OrU:
+ case Opcode::I64AtomicRmw8OrU:
+ case Opcode::I64AtomicRmw16OrU:
+ case Opcode::I64AtomicRmw32OrU:
+ case Opcode::I32AtomicRmwXor:
+ case Opcode::I64AtomicRmwXor:
+ case Opcode::I32AtomicRmw8XorU:
+ case Opcode::I32AtomicRmw16XorU:
+ case Opcode::I64AtomicRmw8XorU:
+ case Opcode::I64AtomicRmw16XorU:
+ case Opcode::I64AtomicRmw32XorU:
+ case Opcode::I32AtomicRmwXchg:
+ case Opcode::I64AtomicRmwXchg:
+ case Opcode::I32AtomicRmw8XchgU:
+ case Opcode::I32AtomicRmw16XchgU:
+ case Opcode::I64AtomicRmw8XchgU:
+ case Opcode::I64AtomicRmw16XchgU:
+ case Opcode::I64AtomicRmw32XchgU: {
+ Address alignment_log2;
+ Index memidx;
+ Address offset;
+ CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
+ "memory alignment", "memory memidx",
+ "memory offset"));
+ CALLBACK(OnAtomicRmwExpr, opcode, memidx, alignment_log2, offset);
+ CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
+ break;
+ }
+
+ case Opcode::I32AtomicRmwCmpxchg:
+ case Opcode::I64AtomicRmwCmpxchg:
+ case Opcode::I32AtomicRmw8CmpxchgU:
+ case Opcode::I32AtomicRmw16CmpxchgU:
+ case Opcode::I64AtomicRmw8CmpxchgU:
+ case Opcode::I64AtomicRmw16CmpxchgU:
+ case Opcode::I64AtomicRmw32CmpxchgU: {
+ Address alignment_log2;
+ Index memidx;
+ Address offset;
+ CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
+ "memory alignment", "memory memidx",
+ "memory offset"));
+ CALLBACK(OnAtomicRmwCmpxchgExpr, opcode, memidx, alignment_log2,
+ offset);
+ CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
+ break;
+ }
+
+ case Opcode::TableInit: {
+ Index segment;
+ CHECK_RESULT(ReadIndex(&segment, "elem segment index"));
+ Index table_index;
+ CHECK_RESULT(ReadIndex(&table_index, "reserved table index"));
+ CALLBACK(OnTableInitExpr, segment, table_index);
+ CALLBACK(OnOpcodeUint32Uint32, segment, table_index);
+ break;
+ }
+
+ case Opcode::MemoryInit: {
+ Index segment;
+ ERROR_IF(data_count_ == kInvalidIndex,
+ "memory.init requires data count section");
+ CHECK_RESULT(ReadIndex(&segment, "elem segment index"));
+ Index memidx = 0;
+ if (!options_.features.multi_memory_enabled()) {
+ uint8_t reserved;
+ CHECK_RESULT(ReadU8(&reserved, "reserved memory index"));
+ ERROR_UNLESS(reserved == 0, "reserved value must be 0");
+ } else {
+ CHECK_RESULT(ReadMemidx(&memidx, "memory.init memidx"));
+ }
+ CALLBACK(OnMemoryInitExpr, segment, memidx);
+ CALLBACK(OnOpcodeUint32Uint32, segment, memidx);
+ break;
+ }
+
+ case Opcode::DataDrop:
+ ERROR_IF(data_count_ == kInvalidIndex,
+ "data.drop requires data count section");
+ [[fallthrough]];
+ case Opcode::ElemDrop: {
+ Index segment;
+ CHECK_RESULT(ReadIndex(&segment, "segment index"));
+ if (opcode == Opcode::DataDrop) {
+ CALLBACK(OnDataDropExpr, segment);
+ } else {
+ CALLBACK(OnElemDropExpr, segment);
+ }
+ CALLBACK(OnOpcodeUint32, segment);
+ break;
+ }
+
+ case Opcode::MemoryFill: {
+ Index memidx = 0;
+ if (!options_.features.multi_memory_enabled()) {
+ uint8_t reserved;
+ CHECK_RESULT(ReadU8(&reserved, "memory.fill reserved"));
+ ERROR_UNLESS(reserved == 0, "memory.fill reserved value must be 0");
+ } else {
+ CHECK_RESULT(ReadMemidx(&memidx, "memory.fill memidx"));
+ }
+ CALLBACK(OnMemoryFillExpr, memidx);
+ CALLBACK(OnOpcodeUint32, memidx);
+ break;
+ }
+
+ case Opcode::MemoryCopy: {
+ Index srcmemidx = 0;
+ Index destmemidx = 0;
+ if (!options_.features.multi_memory_enabled()) {
+ uint8_t reserved;
+ CHECK_RESULT(ReadU8(&reserved, "reserved memory index"));
+ ERROR_UNLESS(reserved == 0, "reserved value must be 0");
+ CHECK_RESULT(ReadU8(&reserved, "reserved memory index"));
+ ERROR_UNLESS(reserved == 0, "reserved value must be 0");
+ } else {
+ CHECK_RESULT(ReadMemidx(&srcmemidx, "memory.copy srcmemidx"));
+ CHECK_RESULT(ReadMemidx(&destmemidx, "memory.copy destmemindex"));
+ }
+ CALLBACK(OnMemoryCopyExpr, srcmemidx, destmemidx);
+ CALLBACK(OnOpcodeUint32Uint32, srcmemidx, destmemidx);
+ break;
+ }
+
+ case Opcode::TableCopy: {
+ Index table_dst;
+ Index table_src;
+ CHECK_RESULT(ReadIndex(&table_dst, "reserved table index"));
+ CHECK_RESULT(ReadIndex(&table_src, "table src"));
+ CALLBACK(OnTableCopyExpr, table_dst, table_src);
+ CALLBACK(OnOpcodeUint32Uint32, table_dst, table_src);
+ break;
+ }
+
+ case Opcode::TableGet: {
+ Index table;
+ CHECK_RESULT(ReadIndex(&table, "table index"));
+ CALLBACK(OnTableGetExpr, table);
+ CALLBACK(OnOpcodeUint32, table);
+ break;
+ }
+
+ case Opcode::TableSet: {
+ Index table;
+ CHECK_RESULT(ReadIndex(&table, "table index"));
+ CALLBACK(OnTableSetExpr, table);
+ CALLBACK(OnOpcodeUint32, table);
+ break;
+ }
+
+ case Opcode::TableGrow: {
+ Index table;
+ CHECK_RESULT(ReadIndex(&table, "table index"));
+ CALLBACK(OnTableGrowExpr, table);
+ CALLBACK(OnOpcodeUint32, table);
+ break;
+ }
+
+ case Opcode::TableSize: {
+ Index table;
+ CHECK_RESULT(ReadIndex(&table, "table index"));
+ CALLBACK(OnTableSizeExpr, table);
+ CALLBACK(OnOpcodeUint32, table);
+ break;
+ }
+
+ case Opcode::TableFill: {
+ Index table;
+ CHECK_RESULT(ReadIndex(&table, "table index"));
+ CALLBACK(OnTableFillExpr, table);
+ CALLBACK(OnOpcodeUint32, table);
+ break;
+ }
+
+ case Opcode::RefFunc: {
+ Index func;
+ CHECK_RESULT(ReadIndex(&func, "func index"));
+ CALLBACK(OnRefFuncExpr, func);
+ CALLBACK(OnOpcodeUint32, func);
+ break;
+ }
+
+ case Opcode::RefNull: {
+ Type type;
+ CHECK_RESULT(ReadRefType(&type, "ref.null type"));
+ CALLBACK(OnRefNullExpr, type);
+ CALLBACK(OnOpcodeType, type);
+ break;
+ }
+
+ case Opcode::RefIsNull:
+ CALLBACK(OnRefIsNullExpr);
+ CALLBACK(OnOpcodeBare);
+ break;
+
+ case Opcode::CallRef:
+ CALLBACK(OnCallRefExpr);
+ CALLBACK(OnOpcodeBare);
+ break;
+
+ default:
+ return ReportUnexpectedOpcode(opcode);
+ }
+ }
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadNameSection(Offset section_size) {
+ CALLBACK(BeginNamesSection, section_size);
+ Index i = 0;
+ uint32_t previous_subsection_type = 0;
+ while (state_.offset < read_end_) {
+ uint32_t name_type;
+ Offset subsection_size;
+ CHECK_RESULT(ReadU32Leb128(&name_type, "name type"));
+ if (i != 0) {
+ ERROR_UNLESS(name_type != previous_subsection_type,
+ "duplicate sub-section");
+ ERROR_UNLESS(name_type >= previous_subsection_type,
+ "out-of-order sub-section");
+ }
+ previous_subsection_type = name_type;
+ CHECK_RESULT(ReadOffset(&subsection_size, "subsection size"));
+ size_t subsection_end = state_.offset + subsection_size;
+ ERROR_UNLESS(subsection_end <= read_end_,
+ "invalid sub-section size: extends past end");
+ ReadEndRestoreGuard guard(this);
+ read_end_ = subsection_end;
+
+ NameSectionSubsection type = static_cast<NameSectionSubsection>(name_type);
+ if (type <= NameSectionSubsection::Last) {
+ CALLBACK(OnNameSubsection, i, type, subsection_size);
+ }
+
+ switch (type) {
+ case NameSectionSubsection::Module:
+ CALLBACK(OnModuleNameSubsection, i, name_type, subsection_size);
+ if (subsection_size) {
+ std::string_view name;
+ CHECK_RESULT(ReadStr(&name, "module name"));
+ CALLBACK(OnModuleName, name);
+ }
+ break;
+ case NameSectionSubsection::Function:
+ CALLBACK(OnFunctionNameSubsection, i, name_type, subsection_size);
+ if (subsection_size) {
+ Index num_names;
+ CHECK_RESULT(ReadCount(&num_names, "name count"));
+ CALLBACK(OnFunctionNamesCount, num_names);
+ Index last_function_index = kInvalidIndex;
+
+ for (Index j = 0; j < num_names; ++j) {
+ Index function_index;
+ std::string_view function_name;
+
+ CHECK_RESULT(ReadIndex(&function_index, "function index"));
+ ERROR_UNLESS(function_index != last_function_index,
+ "duplicate function name: %u", function_index);
+ ERROR_UNLESS(last_function_index == kInvalidIndex ||
+ function_index > last_function_index,
+ "function index out of order: %u", function_index);
+ last_function_index = function_index;
+ ERROR_UNLESS(function_index < NumTotalFuncs(),
+ "invalid function index: %" PRIindex, function_index);
+ CHECK_RESULT(ReadStr(&function_name, "function name"));
+ CALLBACK(OnFunctionName, function_index, function_name);
+ }
+ }
+ break;
+ case NameSectionSubsection::Local:
+ CALLBACK(OnLocalNameSubsection, i, name_type, subsection_size);
+ if (subsection_size) {
+ Index num_funcs;
+ CHECK_RESULT(ReadCount(&num_funcs, "function count"));
+ CALLBACK(OnLocalNameFunctionCount, num_funcs);
+ Index last_function_index = kInvalidIndex;
+ for (Index j = 0; j < num_funcs; ++j) {
+ Index function_index;
+ CHECK_RESULT(ReadIndex(&function_index, "function index"));
+ ERROR_UNLESS(function_index < NumTotalFuncs(),
+ "invalid function index: %u", function_index);
+ ERROR_UNLESS(last_function_index == kInvalidIndex ||
+ function_index > last_function_index,
+ "locals function index out of order: %u",
+ function_index);
+ last_function_index = function_index;
+ Index num_locals;
+ CHECK_RESULT(ReadCount(&num_locals, "local count"));
+ CALLBACK(OnLocalNameLocalCount, function_index, num_locals);
+ Index last_local_index = kInvalidIndex;
+ for (Index k = 0; k < num_locals; ++k) {
+ Index local_index;
+ std::string_view local_name;
+
+ CHECK_RESULT(ReadIndex(&local_index, "named index"));
+ ERROR_UNLESS(local_index != last_local_index,
+ "duplicate local index: %u", local_index);
+ ERROR_UNLESS(last_local_index == kInvalidIndex ||
+ local_index > last_local_index,
+ "local index out of order: %u", local_index);
+ last_local_index = local_index;
+ CHECK_RESULT(ReadStr(&local_name, "name"));
+ CALLBACK(OnLocalName, function_index, local_index, local_name);
+ }
+ }
+ }
+ break;
+ case NameSectionSubsection::Label:
+ // TODO(sbc): Implement label names. These are slightly more complicated
+ // since they refer to offsets in the code section / instruction stream.
+ state_.offset = subsection_end;
+ break;
+ case NameSectionSubsection::Type:
+ case NameSectionSubsection::Table:
+ case NameSectionSubsection::Memory:
+ case NameSectionSubsection::Global:
+ case NameSectionSubsection::ElemSegment:
+ case NameSectionSubsection::DataSegment:
+ case NameSectionSubsection::Tag:
+ if (subsection_size) {
+ Index num_names;
+ CHECK_RESULT(ReadCount(&num_names, "name count"));
+ CALLBACK(OnNameCount, num_names);
+ for (Index j = 0; j < num_names; ++j) {
+ Index index;
+ std::string_view name;
+
+ CHECK_RESULT(ReadIndex(&index, "index"));
+ CHECK_RESULT(ReadStr(&name, "name"));
+ CALLBACK(OnNameEntry, type, index, name);
+ }
+ }
+ state_.offset = subsection_end;
+ break;
+ default:
+ // Unknown subsection, skip it.
+ state_.offset = subsection_end;
+ break;
+ }
+ ++i;
+ ERROR_UNLESS(state_.offset == subsection_end,
+ "unfinished sub-section (expected end: 0x%" PRIzx ")",
+ subsection_end);
+ }
+ CALLBACK0(EndNamesSection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadRelocSection(Offset section_size) {
+ CALLBACK(BeginRelocSection, section_size);
+ uint32_t section_index;
+ CHECK_RESULT(ReadU32Leb128(&section_index, "section index"));
+ Index num_relocs;
+ CHECK_RESULT(ReadCount(&num_relocs, "relocation count"));
+ CALLBACK(OnRelocCount, num_relocs, section_index);
+ for (Index i = 0; i < num_relocs; ++i) {
+ Offset offset;
+ Index index;
+ uint32_t reloc_type, addend = 0;
+ CHECK_RESULT(ReadU32Leb128(&reloc_type, "relocation type"));
+ CHECK_RESULT(ReadOffset(&offset, "offset"));
+ CHECK_RESULT(ReadIndex(&index, "index"));
+ RelocType type = static_cast<RelocType>(reloc_type);
+ switch (type) {
+ case RelocType::MemoryAddressLEB:
+ case RelocType::MemoryAddressLEB64:
+ case RelocType::MemoryAddressSLEB:
+ case RelocType::MemoryAddressSLEB64:
+ case RelocType::MemoryAddressRelSLEB:
+ case RelocType::MemoryAddressRelSLEB64:
+ case RelocType::MemoryAddressI32:
+ case RelocType::MemoryAddressI64:
+ case RelocType::FunctionOffsetI32:
+ case RelocType::SectionOffsetI32:
+ case RelocType::MemoryAddressTLSSLEB:
+ case RelocType::MemoryAddressTLSI32:
+ CHECK_RESULT(ReadS32Leb128(&addend, "addend"));
+ break;
+
+ case RelocType::FuncIndexLEB:
+ case RelocType::TableIndexSLEB:
+ case RelocType::TableIndexSLEB64:
+ case RelocType::TableIndexI32:
+ case RelocType::TableIndexI64:
+ case RelocType::TypeIndexLEB:
+ case RelocType::GlobalIndexLEB:
+ case RelocType::GlobalIndexI32:
+ case RelocType::TagIndexLEB:
+ case RelocType::TableIndexRelSLEB:
+ case RelocType::TableNumberLEB:
+ break;
+
+ default:
+ PrintError("unknown reloc type: %s", GetRelocTypeName(type));
+ return Result::Error;
+ }
+ CALLBACK(OnReloc, type, offset, index, addend);
+ }
+ CALLBACK0(EndRelocSection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadDylink0Section(Offset section_size) {
+ CALLBACK(BeginDylinkSection, section_size);
+
+ while (state_.offset < read_end_) {
+ uint32_t dylink_type;
+ Offset subsection_size;
+ CHECK_RESULT(ReadU32Leb128(&dylink_type, "type"));
+ CHECK_RESULT(ReadOffset(&subsection_size, "subsection size"));
+ size_t subsection_end = state_.offset + subsection_size;
+ ERROR_UNLESS(subsection_end <= read_end_,
+ "invalid sub-section size: extends past end");
+ ReadEndRestoreGuard guard(this);
+ read_end_ = subsection_end;
+
+ uint32_t count;
+ switch (static_cast<DylinkEntryType>(dylink_type)) {
+ case DylinkEntryType::MemInfo: {
+ uint32_t mem_size;
+ uint32_t mem_align;
+ uint32_t table_size;
+ uint32_t table_align;
+
+ CHECK_RESULT(ReadU32Leb128(&mem_size, "mem_size"));
+ CHECK_RESULT(ReadU32Leb128(&mem_align, "mem_align"));
+ CHECK_RESULT(ReadU32Leb128(&table_size, "table_size"));
+ CHECK_RESULT(ReadU32Leb128(&table_align, "table_align"));
+ CALLBACK(OnDylinkInfo, mem_size, mem_align, table_size, table_align);
+ break;
+ }
+ case DylinkEntryType::Needed:
+ CHECK_RESULT(ReadU32Leb128(&count, "needed_dynlibs"));
+ CALLBACK(OnDylinkNeededCount, count);
+ while (count--) {
+ std::string_view so_name;
+ CHECK_RESULT(ReadStr(&so_name, "dylib so_name"));
+ CALLBACK(OnDylinkNeeded, so_name);
+ }
+ break;
+ case DylinkEntryType::ImportInfo:
+ CHECK_RESULT(ReadU32Leb128(&count, "count"));
+ CALLBACK(OnDylinkImportCount, count);
+ for (Index i = 0; i < count; ++i) {
+ uint32_t flags = 0;
+ std::string_view module;
+ std::string_view field;
+ CHECK_RESULT(ReadStr(&module, "module"));
+ CHECK_RESULT(ReadStr(&field, "field"));
+ CHECK_RESULT(ReadU32Leb128(&flags, "flags"));
+ CALLBACK(OnDylinkImport, module, field, flags);
+ }
+ break;
+ case DylinkEntryType::ExportInfo:
+ CHECK_RESULT(ReadU32Leb128(&count, "count"));
+ CALLBACK(OnDylinkExportCount, count);
+ for (Index i = 0; i < count; ++i) {
+ uint32_t flags = 0;
+ std::string_view name;
+ CHECK_RESULT(ReadStr(&name, "name"));
+ CHECK_RESULT(ReadU32Leb128(&flags, "flags"));
+ CALLBACK(OnDylinkExport, name, flags);
+ }
+ break;
+ default:
+ // Unknown subsection, skip it.
+ state_.offset = subsection_end;
+ break;
+ }
+ ERROR_UNLESS(state_.offset == subsection_end,
+ "unfinished sub-section (expected end: 0x%" PRIzx ")",
+ subsection_end);
+ }
+
+ CALLBACK0(EndDylinkSection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadDylinkSection(Offset section_size) {
+ CALLBACK(BeginDylinkSection, section_size);
+ uint32_t mem_size;
+ uint32_t mem_align;
+ uint32_t table_size;
+ uint32_t table_align;
+
+ CHECK_RESULT(ReadU32Leb128(&mem_size, "mem_size"));
+ CHECK_RESULT(ReadU32Leb128(&mem_align, "mem_align"));
+ CHECK_RESULT(ReadU32Leb128(&table_size, "table_size"));
+ CHECK_RESULT(ReadU32Leb128(&table_align, "table_align"));
+ CALLBACK(OnDylinkInfo, mem_size, mem_align, table_size, table_align);
+
+ uint32_t count;
+ CHECK_RESULT(ReadU32Leb128(&count, "needed_dynlibs"));
+ CALLBACK(OnDylinkNeededCount, count);
+ while (count--) {
+ std::string_view so_name;
+ CHECK_RESULT(ReadStr(&so_name, "dylib so_name"));
+ CALLBACK(OnDylinkNeeded, so_name);
+ }
+
+ CALLBACK0(EndDylinkSection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadTargetFeaturesSections(Offset section_size) {
+ CALLBACK(BeginTargetFeaturesSection, section_size);
+ uint32_t count;
+ CHECK_RESULT(ReadU32Leb128(&count, "sym count"));
+ CALLBACK(OnFeatureCount, count);
+ while (count--) {
+ uint8_t prefix;
+ std::string_view name;
+ CHECK_RESULT(ReadU8(&prefix, "prefix"));
+ CHECK_RESULT(ReadStr(&name, "feature name"));
+ CALLBACK(OnFeature, prefix, name);
+ }
+ CALLBACK0(EndTargetFeaturesSection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadLinkingSection(Offset section_size) {
+ CALLBACK(BeginLinkingSection, section_size);
+ uint32_t version;
+ CHECK_RESULT(ReadU32Leb128(&version, "version"));
+ ERROR_UNLESS(version == 2, "invalid linking metadata version: %u", version);
+ while (state_.offset < read_end_) {
+ uint32_t linking_type;
+ Offset subsection_size;
+ CHECK_RESULT(ReadU32Leb128(&linking_type, "type"));
+ CHECK_RESULT(ReadOffset(&subsection_size, "subsection size"));
+ size_t subsection_end = state_.offset + subsection_size;
+ ERROR_UNLESS(subsection_end <= read_end_,
+ "invalid sub-section size: extends past end");
+ ReadEndRestoreGuard guard(this);
+ read_end_ = subsection_end;
+
+ uint32_t count;
+ switch (static_cast<LinkingEntryType>(linking_type)) {
+ case LinkingEntryType::SymbolTable:
+ CHECK_RESULT(ReadU32Leb128(&count, "sym count"));
+ CALLBACK(OnSymbolCount, count);
+ for (Index i = 0; i < count; ++i) {
+ std::string_view name;
+ uint32_t flags = 0;
+ uint32_t kind = 0;
+ CHECK_RESULT(ReadU32Leb128(&kind, "sym type"));
+ CHECK_RESULT(ReadU32Leb128(&flags, "sym flags"));
+ SymbolType sym_type = static_cast<SymbolType>(kind);
+ switch (sym_type) {
+ case SymbolType::Function:
+ case SymbolType::Global:
+ case SymbolType::Tag:
+ case SymbolType::Table: {
+ uint32_t index = 0;
+ CHECK_RESULT(ReadU32Leb128(&index, "index"));
+ if ((flags & WABT_SYMBOL_FLAG_UNDEFINED) == 0 ||
+ (flags & WABT_SYMBOL_FLAG_EXPLICIT_NAME) != 0)
+ CHECK_RESULT(ReadStr(&name, "symbol name"));
+ switch (sym_type) {
+ case SymbolType::Function:
+ CALLBACK(OnFunctionSymbol, i, flags, name, index);
+ break;
+ case SymbolType::Global:
+ CALLBACK(OnGlobalSymbol, i, flags, name, index);
+ break;
+ case SymbolType::Tag:
+ CALLBACK(OnTagSymbol, i, flags, name, index);
+ break;
+ case SymbolType::Table:
+ CALLBACK(OnTableSymbol, i, flags, name, index);
+ break;
+ default:
+ WABT_UNREACHABLE;
+ }
+ break;
+ }
+ case SymbolType::Data: {
+ uint32_t segment = 0;
+ uint32_t offset = 0;
+ uint32_t size = 0;
+ CHECK_RESULT(ReadStr(&name, "symbol name"));
+ if ((flags & WABT_SYMBOL_FLAG_UNDEFINED) == 0) {
+ CHECK_RESULT(ReadU32Leb128(&segment, "segment"));
+ CHECK_RESULT(ReadU32Leb128(&offset, "offset"));
+ CHECK_RESULT(ReadU32Leb128(&size, "size"));
+ }
+ CALLBACK(OnDataSymbol, i, flags, name, segment, offset, size);
+ break;
+ }
+ case SymbolType::Section: {
+ uint32_t index = 0;
+ CHECK_RESULT(ReadU32Leb128(&index, "index"));
+ CALLBACK(OnSectionSymbol, i, flags, index);
+ break;
+ }
+ }
+ }
+ break;
+ case LinkingEntryType::SegmentInfo:
+ CHECK_RESULT(ReadU32Leb128(&count, "info count"));
+ CALLBACK(OnSegmentInfoCount, count);
+ for (Index i = 0; i < count; i++) {
+ std::string_view name;
+ Address alignment_log2;
+ uint32_t flags;
+ CHECK_RESULT(ReadStr(&name, "segment name"));
+ CHECK_RESULT(ReadAlignment(&alignment_log2, "segment alignment"));
+ CHECK_RESULT(ReadU32Leb128(&flags, "segment flags"));
+ CALLBACK(OnSegmentInfo, i, name, alignment_log2, flags);
+ }
+ break;
+ case LinkingEntryType::InitFunctions:
+ CHECK_RESULT(ReadU32Leb128(&count, "info count"));
+ CALLBACK(OnInitFunctionCount, count);
+ while (count--) {
+ uint32_t priority;
+ uint32_t symbol;
+ CHECK_RESULT(ReadU32Leb128(&priority, "priority"));
+ CHECK_RESULT(ReadU32Leb128(&symbol, "symbol index"));
+ CALLBACK(OnInitFunction, priority, symbol);
+ }
+ break;
+ case LinkingEntryType::ComdatInfo:
+ CHECK_RESULT(ReadU32Leb128(&count, "count"));
+ CALLBACK(OnComdatCount, count);
+ while (count--) {
+ uint32_t flags;
+ uint32_t entry_count;
+ std::string_view name;
+ CHECK_RESULT(ReadStr(&name, "comdat name"));
+ CHECK_RESULT(ReadU32Leb128(&flags, "flags"));
+ CHECK_RESULT(ReadU32Leb128(&entry_count, "entry count"));
+ CALLBACK(OnComdatBegin, name, flags, entry_count);
+ while (entry_count--) {
+ uint32_t kind;
+ uint32_t index;
+ CHECK_RESULT(ReadU32Leb128(&kind, "kind"));
+ CHECK_RESULT(ReadU32Leb128(&index, "index"));
+ ComdatType comdat_type = static_cast<ComdatType>(kind);
+ CALLBACK(OnComdatEntry, comdat_type, index);
+ }
+ }
+ break;
+ default:
+ // Unknown subsection, skip it.
+ state_.offset = subsection_end;
+ break;
+ }
+ ERROR_UNLESS(state_.offset == subsection_end,
+ "unfinished sub-section (expected end: 0x%" PRIzx ")",
+ subsection_end);
+ }
+ CALLBACK0(EndLinkingSection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadTagType(Index* out_sig_index) {
+ uint8_t attribute;
+ CHECK_RESULT(ReadU8(&attribute, "tag attribute"));
+ ERROR_UNLESS(attribute == 0, "tag attribute must be 0");
+ CHECK_RESULT(ReadIndex(out_sig_index, "tag signature index"));
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadTagSection(Offset section_size) {
+ CALLBACK(BeginTagSection, section_size);
+ Index num_tags;
+ CHECK_RESULT(ReadCount(&num_tags, "tag count"));
+ CALLBACK(OnTagCount, num_tags);
+
+ for (Index i = 0; i < num_tags; ++i) {
+ Index tag_index = num_tag_imports_ + i;
+ Index sig_index;
+ CHECK_RESULT(ReadTagType(&sig_index));
+ CALLBACK(OnTagType, tag_index, sig_index);
+ }
+
+ CALLBACK(EndTagSection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadCodeMetadataSection(std::string_view name,
+ Offset section_size) {
+ CALLBACK(BeginCodeMetadataSection, name, section_size);
+
+ Index num_functions;
+ CHECK_RESULT(ReadCount(&num_functions, "function count"));
+ CALLBACK(OnCodeMetadataFuncCount, num_functions);
+
+ Index last_function_index = kInvalidIndex;
+ for (Index i = 0; i < num_functions; ++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;
+ CHECK_RESULT(ReadStr(&section_name, "section name"));
+ CALLBACK(BeginCustomSection, section_index, section_size, section_name);
+ ValueRestoreGuard<bool, &BinaryReader::reading_custom_section_> guard(this);
+ reading_custom_section_ = true;
+
+ if (options_.read_debug_names && section_name == WABT_BINARY_SECTION_NAME) {
+ CHECK_RESULT(ReadNameSection(section_size));
+ did_read_names_section_ = true;
+ } else if (section_name == WABT_BINARY_SECTION_DYLINK0) {
+ CHECK_RESULT(ReadDylink0Section(section_size));
+ } else if (section_name == WABT_BINARY_SECTION_DYLINK) {
+ CHECK_RESULT(ReadDylinkSection(section_size));
+ } else if (section_name.rfind(WABT_BINARY_SECTION_RELOC, 0) == 0) {
+ // Reloc sections always begin with "reloc."
+ CHECK_RESULT(ReadRelocSection(section_size));
+ } else if (section_name == WABT_BINARY_SECTION_TARGET_FEATURES) {
+ 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_;
+ }
+ CALLBACK0(EndCustomSection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadTypeSection(Offset section_size) {
+ CALLBACK(BeginTypeSection, section_size);
+ Index num_signatures;
+ CHECK_RESULT(ReadCount(&num_signatures, "type count"));
+ CALLBACK(OnTypeCount, num_signatures);
+
+ for (Index i = 0; i < num_signatures; ++i) {
+ Type form;
+ if (options_.features.gc_enabled()) {
+ CHECK_RESULT(ReadType(&form, "type form"));
+ } else {
+ uint8_t type;
+ CHECK_RESULT(ReadU8(&type, "type form"));
+ ERROR_UNLESS(type == 0x60, "unexpected type form (got %#x)", type);
+ form = Type::Func;
+ }
+
+ switch (form) {
+ case Type::Func: {
+ Index num_params;
+ CHECK_RESULT(ReadCount(&num_params, "function param count"));
+
+ param_types_.resize(num_params);
+
+ for (Index j = 0; j < num_params; ++j) {
+ Type param_type;
+ CHECK_RESULT(ReadType(&param_type, "function param type"));
+ ERROR_UNLESS(IsConcreteType(param_type),
+ "expected valid param type (got " PRItypecode ")",
+ WABT_PRINTF_TYPE_CODE(param_type));
+ param_types_[j] = param_type;
+ }
+
+ Index num_results;
+ CHECK_RESULT(ReadCount(&num_results, "function result count"));
+
+ result_types_.resize(num_results);
+
+ for (Index j = 0; j < num_results; ++j) {
+ Type result_type;
+ CHECK_RESULT(ReadType(&result_type, "function result type"));
+ ERROR_UNLESS(IsConcreteType(result_type),
+ "expected valid result type (got " PRItypecode ")",
+ WABT_PRINTF_TYPE_CODE(result_type));
+ result_types_[j] = result_type;
+ }
+
+ Type* param_types = num_params ? param_types_.data() : nullptr;
+ Type* result_types = num_results ? result_types_.data() : nullptr;
+
+ CALLBACK(OnFuncType, i, num_params, param_types, num_results,
+ result_types);
+ break;
+ }
+
+ case Type::Struct: {
+ ERROR_UNLESS(options_.features.gc_enabled(),
+ "invalid type form: struct not allowed");
+ Index num_fields;
+ CHECK_RESULT(ReadCount(&num_fields, "field count"));
+
+ fields_.resize(num_fields);
+ for (Index j = 0; j < num_fields; ++j) {
+ CHECK_RESULT(ReadField(&fields_[j]));
+ }
+
+ CALLBACK(OnStructType, i, fields_.size(), fields_.data());
+ break;
+ }
+
+ case Type::Array: {
+ ERROR_UNLESS(options_.features.gc_enabled(),
+ "invalid type form: array not allowed");
+
+ TypeMut field;
+ CHECK_RESULT(ReadField(&field));
+ CALLBACK(OnArrayType, i, field);
+ break;
+ };
+
+ default:
+ PrintError("unexpected type form (got " PRItypecode ")",
+ WABT_PRINTF_TYPE_CODE(form));
+ return Result::Error;
+ }
+ }
+ CALLBACK0(EndTypeSection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadImportSection(Offset section_size) {
+ CALLBACK(BeginImportSection, section_size);
+ Index num_imports;
+ CHECK_RESULT(ReadCount(&num_imports, "import count"));
+ CALLBACK(OnImportCount, num_imports);
+ for (Index i = 0; i < num_imports; ++i) {
+ std::string_view module_name;
+ CHECK_RESULT(ReadStr(&module_name, "import module name"));
+ std::string_view field_name;
+ CHECK_RESULT(ReadStr(&field_name, "import field name"));
+
+ uint8_t kind;
+ CHECK_RESULT(ReadU8(&kind, "import kind"));
+ CALLBACK(OnImport, i, static_cast<ExternalKind>(kind), module_name,
+ field_name);
+ switch (static_cast<ExternalKind>(kind)) {
+ case ExternalKind::Func: {
+ Index sig_index;
+ CHECK_RESULT(ReadIndex(&sig_index, "import signature index"));
+ CALLBACK(OnImportFunc, i, module_name, field_name, num_func_imports_,
+ sig_index);
+ num_func_imports_++;
+ break;
+ }
+
+ case ExternalKind::Table: {
+ Type elem_type;
+ Limits elem_limits;
+ CHECK_RESULT(ReadTable(&elem_type, &elem_limits));
+ CALLBACK(OnImportTable, i, module_name, field_name, num_table_imports_,
+ elem_type, &elem_limits);
+ num_table_imports_++;
+ break;
+ }
+
+ case ExternalKind::Memory: {
+ Limits page_limits;
+ CHECK_RESULT(ReadMemory(&page_limits));
+ CALLBACK(OnImportMemory, i, module_name, field_name,
+ num_memory_imports_, &page_limits);
+ num_memory_imports_++;
+ break;
+ }
+
+ case ExternalKind::Global: {
+ Type type;
+ bool mutable_;
+ CHECK_RESULT(ReadGlobalHeader(&type, &mutable_));
+ CALLBACK(OnImportGlobal, i, module_name, field_name,
+ num_global_imports_, type, mutable_);
+ num_global_imports_++;
+ break;
+ }
+
+ case ExternalKind::Tag: {
+ ERROR_UNLESS(options_.features.exceptions_enabled(),
+ "invalid import tag kind: exceptions not allowed");
+ Index sig_index;
+ CHECK_RESULT(ReadTagType(&sig_index));
+ CALLBACK(OnImportTag, i, module_name, field_name, num_tag_imports_,
+ sig_index);
+ num_tag_imports_++;
+ break;
+ }
+
+ default:
+ PrintError("malformed import kind: %d", kind);
+ return Result::Error;
+ }
+ }
+
+ CALLBACK0(EndImportSection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadFunctionSection(Offset section_size) {
+ CALLBACK(BeginFunctionSection, section_size);
+ CHECK_RESULT(
+ ReadCount(&num_function_signatures_, "function signature count"));
+ CALLBACK(OnFunctionCount, num_function_signatures_);
+ for (Index i = 0; i < num_function_signatures_; ++i) {
+ Index func_index = num_func_imports_ + i;
+ Index sig_index;
+ CHECK_RESULT(ReadIndex(&sig_index, "function signature index"));
+ CALLBACK(OnFunction, func_index, sig_index);
+ }
+ CALLBACK0(EndFunctionSection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadTableSection(Offset section_size) {
+ CALLBACK(BeginTableSection, section_size);
+ Index num_tables;
+ CHECK_RESULT(ReadCount(&num_tables, "table count"));
+ CALLBACK(OnTableCount, num_tables);
+ for (Index i = 0; i < num_tables; ++i) {
+ Index table_index = num_table_imports_ + i;
+ Type elem_type;
+ Limits elem_limits;
+ CHECK_RESULT(ReadTable(&elem_type, &elem_limits));
+ CALLBACK(OnTable, table_index, elem_type, &elem_limits);
+ }
+ CALLBACK0(EndTableSection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadMemorySection(Offset section_size) {
+ CALLBACK(BeginMemorySection, section_size);
+ Index num_memories;
+ CHECK_RESULT(ReadCount(&num_memories, "memory count"));
+ CALLBACK(OnMemoryCount, num_memories);
+ for (Index i = 0; i < num_memories; ++i) {
+ Index memory_index = num_memory_imports_ + i;
+ Limits page_limits;
+ CHECK_RESULT(ReadMemory(&page_limits));
+ CALLBACK(OnMemory, memory_index, &page_limits);
+ }
+ CALLBACK0(EndMemorySection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadGlobalSection(Offset section_size) {
+ CALLBACK(BeginGlobalSection, section_size);
+ Index num_globals;
+ CHECK_RESULT(ReadCount(&num_globals, "global count"));
+ CALLBACK(OnGlobalCount, num_globals);
+ for (Index i = 0; i < num_globals; ++i) {
+ Index global_index = num_global_imports_ + i;
+ Type global_type;
+ bool mutable_;
+ CHECK_RESULT(ReadGlobalHeader(&global_type, &mutable_));
+ CALLBACK(BeginGlobal, global_index, global_type, mutable_);
+ CALLBACK(BeginGlobalInitExpr, global_index);
+ CHECK_RESULT(ReadInitExpr(global_index));
+ CALLBACK(EndGlobalInitExpr, global_index);
+ CALLBACK(EndGlobal, global_index);
+ }
+ CALLBACK0(EndGlobalSection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadExportSection(Offset section_size) {
+ CALLBACK(BeginExportSection, section_size);
+ Index num_exports;
+ CHECK_RESULT(ReadCount(&num_exports, "export count"));
+ CALLBACK(OnExportCount, num_exports);
+ for (Index i = 0; i < num_exports; ++i) {
+ std::string_view name;
+ CHECK_RESULT(ReadStr(&name, "export item name"));
+
+ ExternalKind kind;
+ CHECK_RESULT(ReadExternalKind(&kind, "export kind"));
+
+ Index item_index;
+ CHECK_RESULT(ReadIndex(&item_index, "export item index"));
+ if (kind == ExternalKind::Tag) {
+ ERROR_UNLESS(options_.features.exceptions_enabled(),
+ "invalid export tag kind: exceptions not allowed");
+ }
+
+ CALLBACK(OnExport, i, static_cast<ExternalKind>(kind), item_index, name);
+ }
+ CALLBACK0(EndExportSection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadStartSection(Offset section_size) {
+ CALLBACK(BeginStartSection, section_size);
+ Index func_index;
+ CHECK_RESULT(ReadIndex(&func_index, "start function index"));
+ CALLBACK(OnStartFunction, func_index);
+ CALLBACK0(EndStartSection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadElemSection(Offset section_size) {
+ CALLBACK(BeginElemSection, section_size);
+ Index num_elem_segments;
+ CHECK_RESULT(ReadCount(&num_elem_segments, "elem segment count"));
+ CALLBACK(OnElemSegmentCount, num_elem_segments);
+ for (Index i = 0; i < num_elem_segments; ++i) {
+ uint32_t flags;
+ CHECK_RESULT(ReadU32Leb128(&flags, "elem segment flags"));
+ ERROR_IF(flags > SegFlagMax, "invalid elem segment flags: %#x", flags);
+ Index table_index(0);
+ if ((flags & (SegPassive | SegExplicitIndex)) == SegExplicitIndex) {
+ CHECK_RESULT(ReadIndex(&table_index, "elem segment table index"));
+ }
+ Type elem_type = Type::FuncRef;
+
+ CALLBACK(BeginElemSegment, i, table_index, flags);
+
+ if (!(flags & SegPassive)) {
+ CALLBACK(BeginElemSegmentInitExpr, i);
+ CHECK_RESULT(ReadInitExpr(i));
+ CALLBACK(EndElemSegmentInitExpr, i);
+ }
+
+ // For backwards compat we support not declaring the element kind.
+ if (flags & (SegPassive | SegExplicitIndex)) {
+ if (flags & SegUseElemExprs) {
+ CHECK_RESULT(ReadRefType(&elem_type, "table elem type"));
+ } else {
+ ExternalKind kind;
+ CHECK_RESULT(ReadExternalKind(&kind, "export kind"));
+ ERROR_UNLESS(kind == ExternalKind::Func,
+ "segment elem type must be func (%s)",
+ elem_type.GetName().c_str());
+ elem_type = Type::FuncRef;
+ }
+ }
+
+ CALLBACK(OnElemSegmentElemType, i, elem_type);
+
+ Index num_elem_exprs;
+ CHECK_RESULT(ReadCount(&num_elem_exprs, "elem count"));
+
+ CALLBACK(OnElemSegmentElemExprCount, i, num_elem_exprs);
+ for (Index j = 0; j < num_elem_exprs; ++j) {
+ if (flags & SegUseElemExprs) {
+ Opcode opcode;
+ CHECK_RESULT(ReadOpcode(&opcode, "elem expr opcode"));
+ if (opcode == Opcode::RefNull) {
+ Type type;
+ CHECK_RESULT(ReadRefType(&type, "elem expr ref.null type"));
+ CALLBACK(OnElemSegmentElemExpr_RefNull, i, type);
+ } else if (opcode == Opcode::RefFunc) {
+ Index func_index;
+ CHECK_RESULT(ReadIndex(&func_index, "elem expr func index"));
+ CALLBACK(OnElemSegmentElemExpr_RefFunc, i, func_index);
+ } else {
+ PrintError(
+ "expected ref.null or ref.func in passive element segment");
+ }
+ CHECK_RESULT(ReadOpcode(&opcode, "opcode"));
+ ERROR_UNLESS(opcode == Opcode::End,
+ "expected END opcode after element expression");
+ } else {
+ Index func_index;
+ CHECK_RESULT(ReadIndex(&func_index, "elem expr func index"));
+ CALLBACK(OnElemSegmentElemExpr_RefFunc, i, func_index);
+ }
+ }
+ CALLBACK(EndElemSegment, i);
+ }
+ CALLBACK0(EndElemSection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadCodeSection(Offset section_size) {
+ CALLBACK(BeginCodeSection, section_size);
+ CHECK_RESULT(ReadCount(&num_function_bodies_, "function body count"));
+ ERROR_UNLESS(num_function_signatures_ == num_function_bodies_,
+ "function signature count != function body count");
+ CALLBACK(OnFunctionBodyCount, num_function_bodies_);
+ for (Index i = 0; i < num_function_bodies_; ++i) {
+ Index func_index = num_func_imports_ + i;
+ Offset func_offset = state_.offset;
+ state_.offset = func_offset;
+ uint32_t body_size;
+ CHECK_RESULT(ReadU32Leb128(&body_size, "function body size"));
+ Offset body_start_offset = state_.offset;
+ Offset end_offset = body_start_offset + body_size;
+ CALLBACK(BeginFunctionBody, func_index, body_size);
+
+ uint64_t total_locals = 0;
+ Index num_local_decls;
+ CHECK_RESULT(ReadCount(&num_local_decls, "local declaration count"));
+ CALLBACK(OnLocalDeclCount, num_local_decls);
+ for (Index k = 0; k < num_local_decls; ++k) {
+ Index num_local_types;
+ CHECK_RESULT(ReadIndex(&num_local_types, "local type count"));
+ total_locals += num_local_types;
+ ERROR_UNLESS(total_locals <= UINT32_MAX, "local count must be <= 0x%x",
+ UINT32_MAX);
+ Type local_type;
+ CHECK_RESULT(ReadType(&local_type, "local type"));
+ ERROR_UNLESS(IsConcreteType(local_type), "expected valid local type");
+ CALLBACK(OnLocalDecl, k, num_local_types, local_type);
+ }
+
+ if (options_.skip_function_bodies) {
+ state_.offset = end_offset;
+ } else {
+ CHECK_RESULT(ReadFunctionBody(end_offset));
+ }
+
+ CALLBACK(EndFunctionBody, func_index);
+ }
+ CALLBACK0(EndCodeSection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadDataSection(Offset section_size) {
+ CALLBACK(BeginDataSection, section_size);
+ Index num_data_segments;
+ CHECK_RESULT(ReadCount(&num_data_segments, "data segment count"));
+ CALLBACK(OnDataSegmentCount, num_data_segments);
+ // If the DataCount section is not present, then data_count_ will be invalid.
+ ERROR_UNLESS(data_count_ == kInvalidIndex || data_count_ == num_data_segments,
+ "data segment count does not equal count in DataCount section");
+ for (Index i = 0; i < num_data_segments; ++i) {
+ uint32_t flags;
+ CHECK_RESULT(ReadU32Leb128(&flags, "data segment flags"));
+ ERROR_IF(flags != 0 && !options_.features.bulk_memory_enabled(),
+ "invalid memory index %d: bulk memory not allowed", flags);
+ ERROR_IF(flags > SegFlagMax, "invalid data segment flags: %#x", flags);
+ Index memory_index(0);
+ if (flags & SegExplicitIndex) {
+ CHECK_RESULT(ReadIndex(&memory_index, "data segment memory index"));
+ }
+ CALLBACK(BeginDataSegment, i, memory_index, flags);
+ if (!(flags & SegPassive)) {
+ ERROR_UNLESS(memories.size() > 0, "no memory to copy data to");
+ CALLBACK(BeginDataSegmentInitExpr, i);
+ CHECK_RESULT(ReadInitExpr(i));
+ CALLBACK(EndDataSegmentInitExpr, i);
+ }
+
+ Address data_size;
+ const void* data;
+ CHECK_RESULT(ReadBytes(&data, &data_size, "data segment data"));
+ CALLBACK(OnDataSegmentData, i, data, data_size);
+ CALLBACK(EndDataSegment, i);
+ }
+ CALLBACK0(EndDataSection);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadDataCountSection(Offset section_size) {
+ CALLBACK(BeginDataCountSection, section_size);
+ Index data_count;
+ CHECK_RESULT(ReadIndex(&data_count, "data count"));
+ CALLBACK(OnDataCount, data_count);
+ CALLBACK0(EndDataCountSection);
+ data_count_ = data_count;
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadSections(const ReadSectionsOptions& options) {
+ Result result = Result::Ok;
+ Index section_index = 0;
+ bool seen_section_code[static_cast<int>(BinarySection::Last) + 1] = {false};
+
+ for (; state_.offset < state_.size; ++section_index) {
+ uint8_t section_code;
+ Offset section_size;
+ CHECK_RESULT(ReadU8(&section_code, "section code"));
+ CHECK_RESULT(ReadOffset(&section_size, "section size"));
+ ReadEndRestoreGuard guard(this);
+ read_end_ = state_.offset + section_size;
+ if (section_code >= kBinarySectionCount) {
+ PrintError("invalid section code: %u", section_code);
+ if (options.stop_on_first_error) {
+ return Result::Error;
+ }
+ // If we don't have to stop on first error, continue reading
+ // sections, because although we could not understand the
+ // current section, we can continue and correctly parse
+ // subsequent sections, so we can give back as much information
+ // as we can understand.
+ result = Result::Error;
+ state_.offset = read_end_;
+ continue;
+ }
+
+ BinarySection section = static_cast<BinarySection>(section_code);
+ if (section != BinarySection::Custom) {
+ if (seen_section_code[section_code]) {
+ PrintError("multiple %s sections", GetSectionName(section));
+ return Result::Error;
+ }
+ seen_section_code[section_code] = true;
+ }
+
+ ERROR_UNLESS(read_end_ <= state_.size,
+ "invalid section size: extends past end");
+
+ ERROR_UNLESS(
+ last_known_section_ == BinarySection::Invalid ||
+ section == BinarySection::Custom ||
+ GetSectionOrder(section) > GetSectionOrder(last_known_section_),
+ "section %s out of order", GetSectionName(section));
+
+ ERROR_UNLESS(!did_read_names_section_ || section == BinarySection::Custom,
+ "%s section can not occur after Name section",
+ GetSectionName(section));
+
+ CALLBACK(BeginSection, section_index, section, section_size);
+
+ bool stop_on_first_error = options_.stop_on_first_error;
+ Result section_result = Result::Error;
+ switch (section) {
+ case BinarySection::Custom:
+ section_result = ReadCustomSection(section_index, section_size);
+ if (options_.fail_on_custom_section_error) {
+ result |= section_result;
+ } else {
+ stop_on_first_error = false;
+ }
+ break;
+ case BinarySection::Type:
+ section_result = ReadTypeSection(section_size);
+ result |= section_result;
+ break;
+ case BinarySection::Import:
+ section_result = ReadImportSection(section_size);
+ result |= section_result;
+ break;
+ case BinarySection::Function:
+ section_result = ReadFunctionSection(section_size);
+ result |= section_result;
+ break;
+ case BinarySection::Table:
+ section_result = ReadTableSection(section_size);
+ result |= section_result;
+ break;
+ case BinarySection::Memory:
+ section_result = ReadMemorySection(section_size);
+ result |= section_result;
+ break;
+ case BinarySection::Global:
+ section_result = ReadGlobalSection(section_size);
+ result |= section_result;
+ break;
+ case BinarySection::Export:
+ section_result = ReadExportSection(section_size);
+ result |= section_result;
+ break;
+ case BinarySection::Start:
+ section_result = ReadStartSection(section_size);
+ result |= section_result;
+ break;
+ case BinarySection::Elem:
+ section_result = ReadElemSection(section_size);
+ result |= section_result;
+ break;
+ case BinarySection::Code:
+ section_result = ReadCodeSection(section_size);
+ result |= section_result;
+ break;
+ case BinarySection::Data:
+ section_result = ReadDataSection(section_size);
+ result |= section_result;
+ break;
+ case BinarySection::Tag:
+ ERROR_UNLESS(options_.features.exceptions_enabled(),
+ "invalid section code: %u",
+ static_cast<unsigned int>(section));
+ section_result = ReadTagSection(section_size);
+ result |= section_result;
+ break;
+ case BinarySection::DataCount:
+ ERROR_UNLESS(options_.features.bulk_memory_enabled(),
+ "invalid section code: %u",
+ static_cast<unsigned int>(section));
+ section_result = ReadDataCountSection(section_size);
+ result |= section_result;
+ break;
+ case BinarySection::Invalid:
+ WABT_UNREACHABLE;
+ }
+
+ if (Succeeded(section_result) && state_.offset != read_end_) {
+ PrintError("unfinished section (expected end: 0x%" PRIzx ")", read_end_);
+ section_result = Result::Error;
+ result |= section_result;
+ }
+
+ if (Failed(section_result)) {
+ if (stop_on_first_error) {
+ return Result::Error;
+ }
+
+ // If we're continuing after failing to read this section, move the
+ // offset to the expected section end. This way we may be able to read
+ // further sections.
+ state_.offset = read_end_;
+ }
+
+ if (section != BinarySection::Custom) {
+ last_known_section_ = section;
+ }
+ }
+
+ return result;
+}
+
+Result BinaryReader::ReadModule(const ReadModuleOptions& options) {
+ uint32_t magic = 0;
+ CHECK_RESULT(ReadU32(&magic, "magic"));
+ ERROR_UNLESS(magic == WABT_BINARY_MAGIC, "bad magic value");
+ uint32_t version = 0;
+ CHECK_RESULT(ReadU32(&version, "version"));
+ ERROR_UNLESS(version == WABT_BINARY_VERSION,
+ "bad wasm file version: %#x (expected %#x)", version,
+ WABT_BINARY_VERSION);
+
+ CALLBACK(BeginModule, version);
+ CHECK_RESULT(ReadSections(ReadSectionsOptions{options.stop_on_first_error}));
+ // This is checked in ReadCodeSection, but it must be checked at the end too,
+ // in case the code section was omitted.
+ ERROR_UNLESS(num_function_signatures_ == num_function_bodies_,
+ "function signature count != function body count");
+ CALLBACK0(EndModule);
+
+ return Result::Ok;
+}
+
+} // end anonymous namespace
+
+Result ReadBinary(const void* data,
+ size_t size,
+ BinaryReaderDelegate* delegate,
+ const ReadBinaryOptions& options) {
+ BinaryReader reader(data, size, delegate, options);
+ return reader.ReadModule(
+ BinaryReader::ReadModuleOptions{options.stop_on_first_error});
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/binary-writer-spec.cc b/third_party/wasm2c/src/binary-writer-spec.cc
new file mode 100644
index 0000000000..3cb6674de3
--- /dev/null
+++ b/third_party/wasm2c/src/binary-writer-spec.cc
@@ -0,0 +1,672 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/binary-writer-spec.h"
+
+#include <cassert>
+#include <cinttypes>
+#include <string_view>
+
+#include "wabt/config.h"
+
+#include "wabt/binary-writer.h"
+#include "wabt/binary.h"
+#include "wabt/cast.h"
+#include "wabt/filenames.h"
+#include "wabt/ir.h"
+#include "wabt/literal.h"
+#include "wabt/stream.h"
+
+namespace wabt {
+
+namespace {
+
+class BinaryWriterSpec {
+ public:
+ BinaryWriterSpec(Stream* json_stream,
+ WriteBinarySpecStreamFactory module_stream_factory,
+ std::string_view source_filename,
+ std::string_view module_filename_noext,
+ const WriteBinaryOptions& options);
+
+ Result WriteScript(const Script& script);
+
+ private:
+ std::string GetModuleFilename(const char* extension);
+ void WriteString(const char* s);
+ void WriteKey(const char* key);
+ void WriteSeparator();
+ void WriteEscapedString(std::string_view);
+ void WriteCommandType(const Command& command);
+ void WriteLocation(const Location& loc);
+ void WriteVar(const Var& var);
+ void WriteTypeObject(Type type);
+ void WriteF32(uint32_t, ExpectedNan);
+ void WriteF64(uint64_t, ExpectedNan);
+ void WriteRefBits(uintptr_t ref_bits);
+ void WriteConst(const Const& const_);
+ void WriteConstVector(const ConstVector& consts);
+ void WriteAction(const Action& action);
+ void WriteActionResultType(const Action& action);
+ void WriteModule(std::string_view filename, const Module& module);
+ void WriteScriptModule(std::string_view filename,
+ const ScriptModule& script_module);
+ void WriteInvalidModule(const ScriptModule& module, std::string_view text);
+ void WriteCommands();
+
+ const Script* script_ = nullptr;
+ Stream* json_stream_ = nullptr;
+ WriteBinarySpecStreamFactory module_stream_factory_;
+ std::string source_filename_;
+ std::string module_filename_noext_;
+ const WriteBinaryOptions& options_;
+ Result result_ = Result::Ok;
+ size_t num_modules_ = 0;
+};
+
+BinaryWriterSpec::BinaryWriterSpec(
+ Stream* json_stream,
+ WriteBinarySpecStreamFactory module_stream_factory,
+ std::string_view source_filename,
+ std::string_view module_filename_noext,
+ const WriteBinaryOptions& options)
+ : json_stream_(json_stream),
+ module_stream_factory_(module_stream_factory),
+ source_filename_(source_filename),
+ module_filename_noext_(module_filename_noext),
+ options_(options) {}
+
+std::string BinaryWriterSpec::GetModuleFilename(const char* extension) {
+ std::string result = module_filename_noext_;
+ result += '.';
+ result += std::to_string(num_modules_);
+ result += extension;
+ ConvertBackslashToSlash(&result);
+ return result;
+}
+
+void BinaryWriterSpec::WriteString(const char* s) {
+ json_stream_->Writef("\"%s\"", s);
+}
+
+void BinaryWriterSpec::WriteKey(const char* key) {
+ json_stream_->Writef("\"%s\": ", key);
+}
+
+void BinaryWriterSpec::WriteSeparator() {
+ json_stream_->Writef(", ");
+}
+
+void BinaryWriterSpec::WriteEscapedString(std::string_view s) {
+ json_stream_->WriteChar('"');
+ for (size_t i = 0; i < s.length(); ++i) {
+ uint8_t c = s[i];
+ if (c < 0x20 || c == '\\' || c == '"') {
+ json_stream_->Writef("\\u%04x", c);
+ } else {
+ json_stream_->WriteChar(c);
+ }
+ }
+ json_stream_->WriteChar('"');
+}
+
+void BinaryWriterSpec::WriteCommandType(const Command& command) {
+ static const char* s_command_names[] = {
+ "module",
+ "module",
+ "action",
+ "register",
+ "assert_malformed",
+ "assert_invalid",
+ "assert_unlinkable",
+ "assert_uninstantiable",
+ "assert_return",
+ "assert_trap",
+ "assert_exhaustion",
+ "assert_exception",
+ };
+ WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(s_command_names) == kCommandTypeCount);
+
+ WriteKey("type");
+ assert(s_command_names[static_cast<size_t>(command.type)]);
+ WriteString(s_command_names[static_cast<size_t>(command.type)]);
+}
+
+void BinaryWriterSpec::WriteLocation(const Location& loc) {
+ WriteKey("line");
+ json_stream_->Writef("%d", loc.line);
+}
+
+void BinaryWriterSpec::WriteVar(const Var& var) {
+ if (var.is_index()) {
+ json_stream_->Writef("\"%" PRIindex "\"", var.index());
+ } else {
+ WriteEscapedString(var.name());
+ }
+}
+
+void BinaryWriterSpec::WriteTypeObject(Type type) {
+ json_stream_->Writef("{");
+ WriteKey("type");
+ WriteString(type.GetName().c_str());
+ json_stream_->Writef("}");
+}
+
+void BinaryWriterSpec::WriteF32(uint32_t f32_bits, ExpectedNan expected) {
+ switch (expected) {
+ case ExpectedNan::None:
+ json_stream_->Writef("\"%u\"", f32_bits);
+ break;
+
+ case ExpectedNan::Arithmetic:
+ WriteString("nan:arithmetic");
+ break;
+
+ case ExpectedNan::Canonical:
+ WriteString("nan:canonical");
+ break;
+ }
+}
+
+void BinaryWriterSpec::WriteF64(uint64_t f64_bits, ExpectedNan expected) {
+ switch (expected) {
+ case ExpectedNan::None:
+ json_stream_->Writef("\"%" PRIu64 "\"", f64_bits);
+ break;
+
+ case ExpectedNan::Arithmetic:
+ WriteString("nan:arithmetic");
+ break;
+
+ case ExpectedNan::Canonical:
+ WriteString("nan:canonical");
+ break;
+ }
+}
+
+void BinaryWriterSpec::WriteRefBits(uintptr_t ref_bits) {
+ if (ref_bits == Const::kRefNullBits) {
+ json_stream_->Writef("\"null\"");
+ } else {
+ json_stream_->Writef("\"%" PRIuPTR "\"", ref_bits);
+ }
+}
+
+void BinaryWriterSpec::WriteConst(const Const& const_) {
+ json_stream_->Writef("{");
+ WriteKey("type");
+
+ /* Always write the values as strings, even though they may be representable
+ * as JSON numbers. This way the formatting is consistent. */
+ switch (const_.type()) {
+ case Type::I32:
+ WriteString("i32");
+ WriteSeparator();
+ WriteKey("value");
+ json_stream_->Writef("\"%u\"", const_.u32());
+ break;
+
+ case Type::I64:
+ WriteString("i64");
+ WriteSeparator();
+ WriteKey("value");
+ json_stream_->Writef("\"%" PRIu64 "\"", const_.u64());
+ break;
+
+ case Type::F32:
+ WriteString("f32");
+ WriteSeparator();
+ WriteKey("value");
+ WriteF32(const_.f32_bits(), const_.expected_nan());
+ break;
+
+ case Type::F64:
+ WriteString("f64");
+ WriteSeparator();
+ WriteKey("value");
+ WriteF64(const_.f64_bits(), const_.expected_nan());
+ break;
+
+ case Type::FuncRef: {
+ WriteString("funcref");
+ WriteSeparator();
+ WriteKey("value");
+ WriteRefBits(const_.ref_bits());
+ break;
+ }
+
+ case Type::ExternRef: {
+ WriteString("externref");
+ WriteSeparator();
+ WriteKey("value");
+ WriteRefBits(const_.ref_bits());
+ break;
+ }
+
+ case Type::V128: {
+ WriteString("v128");
+ WriteSeparator();
+ WriteKey("lane_type");
+ WriteString(const_.lane_type().GetName().c_str());
+ WriteSeparator();
+ WriteKey("value");
+ json_stream_->Writef("[");
+
+ for (int lane = 0; lane < const_.lane_count(); ++lane) {
+ switch (const_.lane_type()) {
+ case Type::I8:
+ json_stream_->Writef("\"%u\"", const_.v128_lane<uint8_t>(lane));
+ break;
+
+ case Type::I16:
+ json_stream_->Writef("\"%u\"", const_.v128_lane<uint16_t>(lane));
+ break;
+
+ case Type::I32:
+ json_stream_->Writef("\"%u\"", const_.v128_lane<uint32_t>(lane));
+ break;
+
+ case Type::I64:
+ json_stream_->Writef("\"%" PRIu64 "\"",
+ const_.v128_lane<uint64_t>(lane));
+ break;
+
+ case Type::F32:
+ WriteF32(const_.v128_lane<uint32_t>(lane),
+ const_.expected_nan(lane));
+ break;
+
+ case Type::F64:
+ WriteF64(const_.v128_lane<uint64_t>(lane),
+ const_.expected_nan(lane));
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ if (lane != const_.lane_count() - 1) {
+ WriteSeparator();
+ }
+ }
+
+ json_stream_->Writef("]");
+
+ break;
+ }
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ json_stream_->Writef("}");
+}
+
+void BinaryWriterSpec::WriteConstVector(const ConstVector& consts) {
+ json_stream_->Writef("[");
+ for (size_t i = 0; i < consts.size(); ++i) {
+ const Const& const_ = consts[i];
+ WriteConst(const_);
+ if (i != consts.size() - 1) {
+ WriteSeparator();
+ }
+ }
+ json_stream_->Writef("]");
+}
+
+void BinaryWriterSpec::WriteAction(const Action& action) {
+ WriteKey("action");
+ json_stream_->Writef("{");
+ WriteKey("type");
+ if (action.type() == ActionType::Invoke) {
+ WriteString("invoke");
+ } else {
+ assert(action.type() == ActionType::Get);
+ WriteString("get");
+ }
+ WriteSeparator();
+ if (action.module_var.is_name()) {
+ WriteKey("module");
+ WriteVar(action.module_var);
+ WriteSeparator();
+ }
+ if (action.type() == ActionType::Invoke) {
+ WriteKey("field");
+ WriteEscapedString(action.name);
+ WriteSeparator();
+ WriteKey("args");
+ WriteConstVector(cast<InvokeAction>(&action)->args);
+ } else {
+ WriteKey("field");
+ WriteEscapedString(action.name);
+ }
+ json_stream_->Writef("}");
+}
+
+void BinaryWriterSpec::WriteActionResultType(const Action& action) {
+ const Module* module = script_->GetModule(action.module_var);
+ const Export* export_;
+ json_stream_->Writef("[");
+ switch (action.type()) {
+ case ActionType::Invoke: {
+ export_ = module->GetExport(action.name);
+ assert(export_->kind == ExternalKind::Func);
+ const Func* func = module->GetFunc(export_->var);
+ Index num_results = func->GetNumResults();
+ for (Index i = 0; i < num_results; ++i)
+ WriteTypeObject(func->GetResultType(i));
+ break;
+ }
+
+ case ActionType::Get: {
+ export_ = module->GetExport(action.name);
+ assert(export_->kind == ExternalKind::Global);
+ const Global* global = module->GetGlobal(export_->var);
+ WriteTypeObject(global->type);
+ break;
+ }
+ }
+ json_stream_->Writef("]");
+}
+
+void BinaryWriterSpec::WriteModule(std::string_view filename,
+ const Module& module) {
+ result_ |=
+ WriteBinaryModule(module_stream_factory_(filename), &module, options_);
+}
+
+void BinaryWriterSpec::WriteScriptModule(std::string_view filename,
+ const ScriptModule& script_module) {
+ switch (script_module.type()) {
+ case ScriptModuleType::Text:
+ WriteModule(filename, cast<TextScriptModule>(&script_module)->module);
+ break;
+
+ case ScriptModuleType::Binary:
+ module_stream_factory_(filename)->WriteData(
+ cast<BinaryScriptModule>(&script_module)->data, "");
+ break;
+
+ case ScriptModuleType::Quoted:
+ module_stream_factory_(filename)->WriteData(
+ cast<QuotedScriptModule>(&script_module)->data, "");
+ break;
+ }
+}
+
+void BinaryWriterSpec::WriteInvalidModule(const ScriptModule& module,
+ std::string_view text) {
+ const char* extension = "";
+ const char* module_type = "";
+ switch (module.type()) {
+ case ScriptModuleType::Text:
+ extension = kWasmExtension;
+ module_type = "binary";
+ break;
+
+ case ScriptModuleType::Binary:
+ extension = kWasmExtension;
+ module_type = "binary";
+ break;
+
+ case ScriptModuleType::Quoted:
+ extension = kWatExtension;
+ module_type = "text";
+ break;
+ }
+
+ WriteLocation(module.location());
+ WriteSeparator();
+ std::string filename = GetModuleFilename(extension);
+ WriteKey("filename");
+ WriteEscapedString(GetBasename(filename));
+ WriteSeparator();
+ WriteKey("text");
+ WriteEscapedString(text);
+ WriteSeparator();
+ WriteKey("module_type");
+ WriteString(module_type);
+ WriteScriptModule(filename, module);
+}
+
+void BinaryWriterSpec::WriteCommands() {
+ json_stream_->Writef("{\"source_filename\": ");
+ WriteEscapedString(source_filename_);
+ json_stream_->Writef(",\n \"commands\": [\n");
+ Index last_module_index = kInvalidIndex;
+ for (size_t i = 0; i < script_->commands.size(); ++i) {
+ const Command* command = script_->commands[i].get();
+
+ if (i != 0) {
+ WriteSeparator();
+ json_stream_->Writef("\n");
+ }
+
+ json_stream_->Writef(" {");
+ WriteCommandType(*command);
+ WriteSeparator();
+
+ switch (command->type) {
+ case CommandType::Module: {
+ const Module& module = cast<ModuleCommand>(command)->module;
+ std::string filename = GetModuleFilename(kWasmExtension);
+ WriteLocation(module.loc);
+ WriteSeparator();
+ if (!module.name.empty()) {
+ WriteKey("name");
+ WriteEscapedString(module.name);
+ WriteSeparator();
+ }
+ WriteKey("filename");
+ WriteEscapedString(GetBasename(filename));
+ WriteModule(filename, module);
+ num_modules_++;
+ last_module_index = i;
+ break;
+ }
+
+ case CommandType::ScriptModule: {
+ auto* script_module_command = cast<ScriptModuleCommand>(command);
+ const auto& module = script_module_command->module;
+ std::string filename = GetModuleFilename(kWasmExtension);
+ WriteLocation(module.loc);
+ WriteSeparator();
+ if (!module.name.empty()) {
+ WriteKey("name");
+ WriteEscapedString(module.name);
+ WriteSeparator();
+ }
+ WriteKey("filename");
+ WriteEscapedString(GetBasename(filename));
+ WriteScriptModule(filename, *script_module_command->script_module);
+ num_modules_++;
+ last_module_index = i;
+ break;
+ }
+
+ case CommandType::Action: {
+ const Action& action = *cast<ActionCommand>(command)->action;
+ WriteLocation(action.loc);
+ WriteSeparator();
+ WriteAction(action);
+ WriteSeparator();
+ WriteKey("expected");
+ WriteActionResultType(action);
+ break;
+ }
+
+ case CommandType::Register: {
+ auto* register_command = cast<RegisterCommand>(command);
+ const Var& var = register_command->var;
+ WriteLocation(var.loc);
+ WriteSeparator();
+ if (var.is_name()) {
+ WriteKey("name");
+ WriteVar(var);
+ WriteSeparator();
+ } else {
+ /* If we're not registering by name, then we should only be
+ * registering the last module. */
+ WABT_USE(last_module_index);
+ assert(var.index() == last_module_index);
+ }
+ WriteKey("as");
+ WriteEscapedString(register_command->module_name);
+ break;
+ }
+
+ case CommandType::AssertMalformed: {
+ auto* assert_malformed_command = cast<AssertMalformedCommand>(command);
+ WriteInvalidModule(*assert_malformed_command->module,
+ assert_malformed_command->text);
+ num_modules_++;
+ break;
+ }
+
+ case CommandType::AssertInvalid: {
+ auto* assert_invalid_command = cast<AssertInvalidCommand>(command);
+ WriteInvalidModule(*assert_invalid_command->module,
+ assert_invalid_command->text);
+ num_modules_++;
+ break;
+ }
+
+ case CommandType::AssertUnlinkable: {
+ auto* assert_unlinkable_command =
+ cast<AssertUnlinkableCommand>(command);
+ WriteInvalidModule(*assert_unlinkable_command->module,
+ assert_unlinkable_command->text);
+ num_modules_++;
+ break;
+ }
+
+ case CommandType::AssertUninstantiable: {
+ auto* assert_uninstantiable_command =
+ cast<AssertUninstantiableCommand>(command);
+ WriteInvalidModule(*assert_uninstantiable_command->module,
+ assert_uninstantiable_command->text);
+ num_modules_++;
+ break;
+ }
+
+ case CommandType::AssertReturn: {
+ auto* assert_return_command = cast<AssertReturnCommand>(command);
+ WriteLocation(assert_return_command->action->loc);
+ WriteSeparator();
+ WriteAction(*assert_return_command->action);
+ WriteSeparator();
+ const Expectation* expectation = assert_return_command->expected.get();
+ switch (expectation->type()) {
+ case ExpectationType::Values:
+ WriteKey("expected");
+ break;
+
+ case ExpectationType::Either:
+ WriteKey("either");
+ break;
+ }
+ WriteConstVector(expectation->expected);
+ break;
+ }
+
+ case CommandType::AssertTrap: {
+ auto* assert_trap_command = cast<AssertTrapCommand>(command);
+ WriteLocation(assert_trap_command->action->loc);
+ WriteSeparator();
+ WriteAction(*assert_trap_command->action);
+ WriteSeparator();
+ WriteKey("text");
+ WriteEscapedString(assert_trap_command->text);
+ WriteSeparator();
+ WriteKey("expected");
+ WriteActionResultType(*assert_trap_command->action);
+ break;
+ }
+
+ case CommandType::AssertExhaustion: {
+ auto* assert_exhaustion_command =
+ cast<AssertExhaustionCommand>(command);
+ WriteLocation(assert_exhaustion_command->action->loc);
+ WriteSeparator();
+ WriteAction(*assert_exhaustion_command->action);
+ WriteSeparator();
+ WriteKey("text");
+ WriteEscapedString(assert_exhaustion_command->text);
+ WriteSeparator();
+ WriteKey("expected");
+ WriteActionResultType(*assert_exhaustion_command->action);
+ break;
+ }
+
+ case CommandType::AssertException: {
+ auto* assert_exception_command = cast<AssertExceptionCommand>(command);
+ WriteLocation(assert_exception_command->action->loc);
+ WriteSeparator();
+ WriteAction(*assert_exception_command->action);
+ WriteSeparator();
+ WriteKey("expected");
+ WriteActionResultType(*assert_exception_command->action);
+ break;
+ }
+ }
+
+ json_stream_->Writef("}");
+ }
+ json_stream_->Writef("]}\n");
+}
+
+Result BinaryWriterSpec::WriteScript(const Script& script) {
+ script_ = &script;
+ WriteCommands();
+ return result_;
+}
+
+} // end anonymous namespace
+
+Result WriteBinarySpecScript(Stream* json_stream,
+ WriteBinarySpecStreamFactory module_stream_factory,
+ Script* script,
+ std::string_view source_filename,
+ std::string_view module_filename_noext,
+ const WriteBinaryOptions& options) {
+ BinaryWriterSpec binary_writer_spec(json_stream, module_stream_factory,
+ source_filename, module_filename_noext,
+ options);
+ return binary_writer_spec.WriteScript(*script);
+}
+
+Result WriteBinarySpecScript(
+ Stream* json_stream,
+ Script* script,
+ std::string_view source_filename,
+ std::string_view module_filename_noext,
+ const WriteBinaryOptions& options,
+ std::vector<FilenameMemoryStreamPair>* out_module_streams,
+ Stream* log_stream) {
+ WriteBinarySpecStreamFactory module_stream_factory =
+ [&](std::string_view filename) {
+ out_module_streams->emplace_back(
+ filename, std::make_unique<MemoryStream>(log_stream));
+ return out_module_streams->back().stream.get();
+ };
+
+ BinaryWriterSpec binary_writer_spec(json_stream, module_stream_factory,
+ source_filename, module_filename_noext,
+ options);
+ return binary_writer_spec.WriteScript(*script);
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/binary-writer.cc b/third_party/wasm2c/src/binary-writer.cc
new file mode 100644
index 0000000000..2eb2884684
--- /dev/null
+++ b/third_party/wasm2c/src/binary-writer.cc
@@ -0,0 +1,1798 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/binary-writer.h"
+
+#include <cassert>
+#include <cmath>
+#include <cstdarg>
+#include <cstdint>
+#include <cstdio>
+#include <set>
+#include <string_view>
+#include <vector>
+
+#include "wabt/config.h"
+
+#include "wabt/binary.h"
+#include "wabt/cast.h"
+#include "wabt/expr-visitor.h"
+#include "wabt/ir.h"
+#include "wabt/leb128.h"
+#include "wabt/stream.h"
+
+#define PRINT_HEADER_NO_INDEX -1
+#define MAX_U32_LEB128_BYTES 5
+
+namespace wabt {
+
+void WriteStr(Stream* stream,
+ std::string_view s,
+ const char* desc,
+ PrintChars print_chars) {
+ WriteU32Leb128(stream, s.length(), "string length");
+ stream->WriteData(s.data(), s.length(), desc, print_chars);
+}
+
+void WriteOpcode(Stream* stream, Opcode opcode) {
+ if (opcode.HasPrefix()) {
+ stream->WriteU8(opcode.GetPrefix(), "prefix");
+ WriteU32Leb128(stream, opcode.GetCode(), opcode.GetName());
+ } else {
+ stream->WriteU8(opcode.GetCode(), opcode.GetName());
+ }
+}
+
+void WriteType(Stream* stream, Type type, const char* desc) {
+ WriteS32Leb128(stream, type, desc ? desc : type.GetName().c_str());
+ if (type.IsReferenceWithIndex()) {
+ WriteS32Leb128(stream, type.GetReferenceIndex(),
+ desc ? desc : type.GetName().c_str());
+ }
+}
+
+void WriteLimits(Stream* stream, const Limits* limits) {
+ uint32_t flags = limits->has_max ? WABT_BINARY_LIMITS_HAS_MAX_FLAG : 0;
+ flags |= limits->is_shared ? WABT_BINARY_LIMITS_IS_SHARED_FLAG : 0;
+ flags |= limits->is_64 ? WABT_BINARY_LIMITS_IS_64_FLAG : 0;
+ WriteU32Leb128(stream, flags, "limits: flags");
+ if (limits->is_64) {
+ WriteU64Leb128(stream, limits->initial, "limits: initial");
+ if (limits->has_max) {
+ WriteU64Leb128(stream, limits->max, "limits: max");
+ }
+ } else {
+ WriteU32Leb128(stream, limits->initial, "limits: initial");
+ if (limits->has_max) {
+ WriteU32Leb128(stream, limits->max, "limits: max");
+ }
+ }
+}
+
+void WriteDebugName(Stream* stream, std::string_view name, const char* desc) {
+ std::string_view stripped_name = name;
+ if (!stripped_name.empty()) {
+ // Strip leading $ from name
+ assert(stripped_name.front() == '$');
+ stripped_name.remove_prefix(1);
+ }
+ WriteStr(stream, stripped_name, desc, PrintChars::Yes);
+}
+
+namespace {
+
+/* TODO(binji): better leb size guess. Some sections we know will only be 1
+ byte, but others we can be fairly certain will be larger. */
+constexpr size_t LEB_SECTION_SIZE_GUESS = 1;
+
+#define ALLOC_FAILURE \
+ fprintf(stderr, "%s:%d: allocation failed\n", __FILE__, __LINE__)
+
+struct RelocSection {
+ RelocSection(const char* name, Index index)
+ : name(name), section_index(index) {}
+
+ const char* name;
+ Index section_index;
+ std::vector<Reloc> relocations;
+};
+
+class Symbol {
+ public:
+ struct Function {
+ static const SymbolType type = SymbolType::Function;
+ Index index;
+ };
+ struct Data {
+ static const SymbolType type = SymbolType::Data;
+ Index index;
+ Offset offset;
+ Address size;
+ };
+ struct Global {
+ static const SymbolType type = SymbolType::Global;
+ Index index;
+ };
+ struct Section {
+ static const SymbolType type = SymbolType::Section;
+ Index section;
+ };
+ struct Tag {
+ static const SymbolType type = SymbolType::Tag;
+ Index index;
+ };
+ struct Table {
+ static const SymbolType type = SymbolType::Table;
+ Index index;
+ };
+
+ private:
+ SymbolType type_;
+ std::string_view name_;
+ uint8_t flags_;
+ union {
+ Function function_;
+ Data data_;
+ Global global_;
+ Section section_;
+ Tag tag_;
+ Table table_;
+ };
+
+ public:
+ Symbol(const std::string_view& name, uint8_t flags, const Function& f)
+ : type_(Function::type), name_(name), flags_(flags), function_(f) {}
+ Symbol(const std::string_view& name, uint8_t flags, const Data& d)
+ : type_(Data::type), name_(name), flags_(flags), data_(d) {}
+ Symbol(const std::string_view& name, uint8_t flags, const Global& g)
+ : type_(Global::type), name_(name), flags_(flags), global_(g) {}
+ Symbol(const std::string_view& name, uint8_t flags, const Section& s)
+ : type_(Section::type), name_(name), flags_(flags), section_(s) {}
+ Symbol(const std::string_view& name, uint8_t flags, const Tag& e)
+ : type_(Tag::type), name_(name), flags_(flags), tag_(e) {}
+ Symbol(const std::string_view& name, uint8_t flags, const Table& t)
+ : type_(Table::type), name_(name), flags_(flags), table_(t) {}
+
+ SymbolType type() const { return type_; }
+ const std::string_view& name() const { return name_; }
+ uint8_t flags() const { return flags_; }
+
+ SymbolVisibility visibility() const {
+ return static_cast<SymbolVisibility>(flags() & WABT_SYMBOL_MASK_VISIBILITY);
+ }
+ SymbolBinding binding() const {
+ return static_cast<SymbolBinding>(flags() & WABT_SYMBOL_MASK_BINDING);
+ }
+ bool undefined() const { return flags() & WABT_SYMBOL_FLAG_UNDEFINED; }
+ bool defined() const { return !undefined(); }
+ bool exported() const { return flags() & WABT_SYMBOL_FLAG_EXPORTED; }
+ bool explicit_name() const {
+ return flags() & WABT_SYMBOL_FLAG_EXPLICIT_NAME;
+ }
+ bool no_strip() const { return flags() & WABT_SYMBOL_FLAG_NO_STRIP; }
+
+ bool IsFunction() const { return type() == Function::type; }
+ bool IsData() const { return type() == Data::type; }
+ bool IsGlobal() const { return type() == Global::type; }
+ bool IsSection() const { return type() == Section::type; }
+ bool IsTag() const { return type() == Tag::type; }
+ bool IsTable() const { return type() == Table::type; }
+
+ const Function& AsFunction() const {
+ assert(IsFunction());
+ return function_;
+ }
+ const Data& AsData() const {
+ assert(IsData());
+ return data_;
+ }
+ const Global& AsGlobal() const {
+ assert(IsGlobal());
+ return global_;
+ }
+ const Section& AsSection() const {
+ assert(IsSection());
+ return section_;
+ }
+ const Tag& AsTag() const {
+ assert(IsTag());
+ return tag_;
+ }
+ const Table& AsTable() const {
+ assert(IsTable());
+ return table_;
+ }
+};
+
+class SymbolTable {
+ WABT_DISALLOW_COPY_AND_ASSIGN(SymbolTable);
+
+ std::vector<Symbol> symbols_;
+
+ std::vector<Index> functions_;
+ std::vector<Index> tables_;
+ std::vector<Index> globals_;
+
+ std::set<std::string_view> seen_names_;
+
+ Result EnsureUnique(const std::string_view& name) {
+ if (seen_names_.count(name)) {
+ fprintf(stderr,
+ "error: duplicate symbol when writing relocatable "
+ "binary: %s\n",
+ &name[0]);
+ return Result::Error;
+ }
+ seen_names_.insert(name);
+ return Result::Ok;
+ };
+
+ template <typename T>
+ Result AddSymbol(std::vector<Index>* map,
+ std::string_view name,
+ bool imported,
+ bool exported,
+ T&& sym) {
+ uint8_t flags = 0;
+ if (imported) {
+ flags |= WABT_SYMBOL_FLAG_UNDEFINED;
+ // Wabt currently has no way for a user to explicitly specify the name of
+ // an import, so never set the EXPLICIT_NAME flag, and ignore any display
+ // name fabricated by wabt.
+ name = std::string_view();
+ } else {
+ if (name.empty()) {
+ // Definitions without a name are local.
+ flags |= uint8_t(SymbolBinding::Local);
+ flags |= uint8_t(SymbolVisibility::Hidden);
+ } else {
+ // Otherwise, strip the dollar off the name; a definition $foo is
+ // available for linking as "foo".
+ assert(name[0] == '$');
+ name.remove_prefix(1);
+ }
+
+ if (exported) {
+ CHECK_RESULT(EnsureUnique(name));
+ flags |= uint8_t(SymbolVisibility::Hidden);
+ flags |= WABT_SYMBOL_FLAG_NO_STRIP;
+ }
+ }
+ if (exported) {
+ flags |= WABT_SYMBOL_FLAG_EXPORTED;
+ }
+
+ map->push_back(symbols_.size());
+ symbols_.emplace_back(name, flags, sym);
+ return Result::Ok;
+ };
+
+ Index SymbolIndex(const std::vector<Index>& table, Index index) const {
+ // For well-formed modules, an index into (e.g.) functions_ will always be
+ // within bounds; the out-of-bounds case here is just to allow --relocatable
+ // to write known-invalid modules.
+ return index < table.size() ? table[index] : kInvalidIndex;
+ }
+
+ public:
+ SymbolTable() {}
+
+ Result Populate(const Module* module) {
+ std::set<Index> exported_funcs;
+ std::set<Index> exported_globals;
+ std::set<Index> exported_tags;
+ std::set<Index> exported_tables;
+
+ for (const Export* export_ : module->exports) {
+ switch (export_->kind) {
+ case ExternalKind::Func:
+ exported_funcs.insert(module->GetFuncIndex(export_->var));
+ break;
+ case ExternalKind::Table:
+ exported_tables.insert(module->GetTableIndex(export_->var));
+ break;
+ case ExternalKind::Memory:
+ break;
+ case ExternalKind::Global:
+ exported_globals.insert(module->GetGlobalIndex(export_->var));
+ break;
+ case ExternalKind::Tag:
+ exported_tags.insert(module->GetTagIndex(export_->var));
+ break;
+ }
+ }
+
+ // We currently only create symbol table entries for function, table, and
+ // global symbols.
+ for (size_t i = 0; i < module->funcs.size(); ++i) {
+ const Func* func = module->funcs[i];
+ bool imported = i < module->num_func_imports;
+ bool exported = exported_funcs.count(i);
+ CHECK_RESULT(AddSymbol(&functions_, func->name, imported, exported,
+ Symbol::Function{Index(i)}));
+ }
+
+ for (size_t i = 0; i < module->tables.size(); ++i) {
+ const Table* table = module->tables[i];
+ bool imported = i < module->num_table_imports;
+ bool exported = exported_tables.count(i);
+ CHECK_RESULT(AddSymbol(&tables_, table->name, imported, exported,
+ Symbol::Table{Index(i)}));
+ }
+
+ for (size_t i = 0; i < module->globals.size(); ++i) {
+ const Global* global = module->globals[i];
+ bool imported = i < module->num_global_imports;
+ bool exported = exported_globals.count(i);
+ CHECK_RESULT(AddSymbol(&globals_, global->name, imported, exported,
+ Symbol::Global{Index(i)}));
+ }
+
+ return Result::Ok;
+ }
+
+ const std::vector<Symbol>& symbols() const { return symbols_; }
+ Index FunctionSymbolIndex(Index index) const {
+ return SymbolIndex(functions_, index);
+ }
+ Index TableSymbolIndex(Index index) const {
+ return SymbolIndex(tables_, index);
+ }
+ Index GlobalSymbolIndex(Index index) const {
+ return SymbolIndex(globals_, index);
+ }
+};
+
+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;
+};
+using CodeMetadataSections =
+ std::unordered_map<std::string_view, CodeMetadataSection>;
+
+class BinaryWriter {
+ WABT_DISALLOW_COPY_AND_ASSIGN(BinaryWriter);
+
+ public:
+ BinaryWriter(Stream*,
+ const WriteBinaryOptions& options,
+ const Module* module);
+
+ Result WriteModule();
+
+ private:
+ void WriteHeader(const char* name, int index);
+ Offset WriteU32Leb128Space(Offset leb_size_guess, const char* desc);
+ Offset WriteFixupU32Leb128Size(Offset offset,
+ Offset leb_size_guess,
+ const char* desc);
+ void BeginKnownSection(BinarySection section_code);
+ void BeginCustomSection(const char* name);
+ void WriteSectionHeader(const char* desc, BinarySection section_code);
+ void EndSection();
+ void BeginSubsection(const char* name);
+ void EndSubsection();
+ Index GetLabelVarDepth(const Var* var);
+ Index GetTagVarDepth(const Var* var);
+ Index GetLocalIndex(const Func* func, const Var& var);
+ Index GetSymbolIndex(RelocType reloc_type, Index index);
+ void AddReloc(RelocType reloc_type, Index index);
+ void WriteBlockDecl(const BlockDeclaration& decl);
+ void WriteU32Leb128WithReloc(Index index,
+ const char* desc,
+ RelocType reloc_type);
+ void WriteS32Leb128WithReloc(int32_t value,
+ const char* desc,
+ RelocType reloc_type);
+ void WriteTableNumberWithReloc(Index table_number, const char* desc);
+ template <typename T>
+ void WriteLoadStoreExpr(const Func* func, const Expr* expr, const char* desc);
+ template <typename T>
+ void WriteSimdLoadStoreLaneExpr(const Func* func,
+ const Expr* expr,
+ const char* desc);
+ void WriteExpr(const Func* func, const Expr* expr);
+ void WriteExprList(const Func* func, const ExprList& exprs);
+ void WriteInitExpr(const ExprList& expr);
+ void WriteFuncLocals(const Func* func, const LocalTypes& local_types);
+ void WriteFunc(const Func* func);
+ void WriteTable(const Table* table);
+ void WriteMemory(const Memory* memory);
+ void WriteGlobalHeader(const Global* global);
+ void WriteTagType(const Tag* tag);
+ void WriteRelocSection(const RelocSection* reloc_section);
+ void WriteLinkingSection();
+ template <typename T>
+ void WriteNames(const std::vector<T*>& elems, NameSectionSubsection type);
+ void WriteCodeMetadataSections();
+
+ Stream* stream_;
+ const WriteBinaryOptions& options_;
+ const Module* module_;
+
+ SymbolTable symtab_;
+ std::vector<RelocSection> reloc_sections_;
+ RelocSection* current_reloc_section_ = nullptr;
+
+ Index section_count_ = 0;
+ size_t last_section_offset_ = 0;
+ size_t last_section_leb_size_guess_ = 0;
+ BinarySection last_section_type_ = BinarySection::Invalid;
+ size_t last_section_payload_offset_ = 0;
+
+ size_t last_subsection_offset_ = 0;
+ size_t last_subsection_leb_size_guess_ = 0;
+ size_t last_subsection_payload_offset_ = 0;
+
+ // Information about the data count section, so it can be removed if it is
+ // not needed, and relocs relative to the code section patched up.
+ size_t code_start_ = 0;
+ 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) {
+ uint8_t result = 0;
+ while (x > 1) {
+ x >>= 1;
+ result++;
+ }
+ return result;
+}
+
+BinaryWriter::BinaryWriter(Stream* stream,
+ const WriteBinaryOptions& options,
+ const Module* module)
+ : stream_(stream), options_(options), module_(module) {}
+
+void BinaryWriter::WriteHeader(const char* name, int index) {
+ if (stream_->has_log_stream()) {
+ if (index == PRINT_HEADER_NO_INDEX) {
+ stream_->log_stream().Writef("; %s\n", name);
+ } else {
+ stream_->log_stream().Writef("; %s %d\n", name, index);
+ }
+ }
+}
+
+/* returns offset of leb128 */
+Offset BinaryWriter::WriteU32Leb128Space(Offset leb_size_guess,
+ const char* desc) {
+ assert(leb_size_guess <= MAX_U32_LEB128_BYTES);
+ uint8_t data[MAX_U32_LEB128_BYTES] = {0};
+ Offset result = stream_->offset();
+ Offset bytes_to_write =
+ options_.canonicalize_lebs ? leb_size_guess : MAX_U32_LEB128_BYTES;
+ stream_->WriteData(data, bytes_to_write, desc);
+ return result;
+}
+
+Offset BinaryWriter::WriteFixupU32Leb128Size(Offset offset,
+ Offset leb_size_guess,
+ const char* desc) {
+ if (options_.canonicalize_lebs) {
+ Offset size = stream_->offset() - offset - leb_size_guess;
+ Offset leb_size = U32Leb128Length(size);
+ Offset delta = leb_size - leb_size_guess;
+ if (delta != 0) {
+ Offset src_offset = offset + leb_size_guess;
+ Offset dst_offset = offset + leb_size;
+ stream_->MoveData(dst_offset, src_offset, size);
+ }
+ WriteU32Leb128At(stream_, offset, size, desc);
+ stream_->AddOffset(delta);
+ return delta;
+ } else {
+ Offset size = stream_->offset() - offset - MAX_U32_LEB128_BYTES;
+ WriteFixedU32Leb128At(stream_, offset, size, desc);
+ return 0;
+ }
+}
+
+void BinaryWriter::WriteBlockDecl(const BlockDeclaration& decl) {
+ if (decl.sig.GetNumParams() == 0 && decl.sig.GetNumResults() <= 1) {
+ if (decl.sig.GetNumResults() == 0) {
+ WriteType(stream_, Type::Void);
+ } else if (decl.sig.GetNumResults() == 1) {
+ WriteType(stream_, decl.sig.GetResultType(0));
+ }
+ return;
+ }
+
+ Index index = decl.has_func_type ? module_->GetFuncTypeIndex(decl.type_var)
+ : module_->GetFuncTypeIndex(decl.sig);
+ assert(index != kInvalidIndex);
+ WriteS32Leb128WithReloc(index, "block type function index",
+ RelocType::TypeIndexLEB);
+}
+
+void BinaryWriter::WriteSectionHeader(const char* desc,
+ BinarySection section_code) {
+ assert(last_section_leb_size_guess_ == 0);
+ WriteHeader(desc, PRINT_HEADER_NO_INDEX);
+ stream_->WriteU8Enum(section_code, "section code");
+ last_section_type_ = section_code;
+ last_section_leb_size_guess_ = LEB_SECTION_SIZE_GUESS;
+ last_section_offset_ =
+ WriteU32Leb128Space(LEB_SECTION_SIZE_GUESS, "section size (guess)");
+ last_section_payload_offset_ = stream_->offset();
+}
+
+void BinaryWriter::BeginKnownSection(BinarySection section_code) {
+ char desc[100];
+ wabt_snprintf(desc, sizeof(desc), "section \"%s\" (%u)",
+ GetSectionName(section_code),
+ static_cast<unsigned>(section_code));
+ WriteSectionHeader(desc, section_code);
+}
+
+void BinaryWriter::BeginCustomSection(const char* name) {
+ char desc[100];
+ wabt_snprintf(desc, sizeof(desc), "section \"%s\"", name);
+ WriteSectionHeader(desc, BinarySection::Custom);
+ WriteStr(stream_, name, "custom section name", PrintChars::Yes);
+}
+
+void BinaryWriter::EndSection() {
+ assert(last_section_leb_size_guess_ != 0);
+ WriteFixupU32Leb128Size(last_section_offset_, last_section_leb_size_guess_,
+ "FIXUP section size");
+ last_section_leb_size_guess_ = 0;
+ section_count_++;
+}
+
+void BinaryWriter::BeginSubsection(const char* name) {
+ assert(last_subsection_leb_size_guess_ == 0);
+ last_subsection_leb_size_guess_ = LEB_SECTION_SIZE_GUESS;
+ last_subsection_offset_ =
+ WriteU32Leb128Space(LEB_SECTION_SIZE_GUESS, "subsection size (guess)");
+ last_subsection_payload_offset_ = stream_->offset();
+}
+
+void BinaryWriter::EndSubsection() {
+ assert(last_subsection_leb_size_guess_ != 0);
+ WriteFixupU32Leb128Size(last_subsection_offset_,
+ last_subsection_leb_size_guess_,
+ "FIXUP subsection size");
+ last_subsection_leb_size_guess_ = 0;
+}
+
+Index BinaryWriter::GetLabelVarDepth(const Var* var) {
+ return var->index();
+}
+
+Index BinaryWriter::GetTagVarDepth(const Var* var) {
+ return var->index();
+}
+
+Index BinaryWriter::GetSymbolIndex(RelocType reloc_type, Index index) {
+ switch (reloc_type) {
+ case RelocType::FuncIndexLEB:
+ return symtab_.FunctionSymbolIndex(index);
+ case RelocType::TableNumberLEB:
+ return symtab_.TableSymbolIndex(index);
+ case RelocType::GlobalIndexLEB:
+ return symtab_.GlobalSymbolIndex(index);
+ case RelocType::TypeIndexLEB:
+ // Type indexes don't create entries in the symbol table; instead their
+ // index is used directly.
+ return index;
+ default:
+ fprintf(stderr, "warning: unsupported relocation type: %s\n",
+ GetRelocTypeName(reloc_type));
+ return kInvalidIndex;
+ }
+}
+
+void BinaryWriter::AddReloc(RelocType reloc_type, Index index) {
+ // Add a new reloc section if needed
+ if (!current_reloc_section_ ||
+ current_reloc_section_->section_index != section_count_) {
+ reloc_sections_.emplace_back(GetSectionName(last_section_type_),
+ section_count_);
+ current_reloc_section_ = &reloc_sections_.back();
+ }
+
+ // Add a new relocation to the curent reloc section
+ size_t offset = stream_->offset() - last_section_payload_offset_;
+ Index symbol_index = GetSymbolIndex(reloc_type, index);
+ if (symbol_index == kInvalidIndex) {
+ // The file is invalid, for example a reference to function 42 where only 10
+ // functions are defined. The user must have already passed --no-check, so
+ // no extra warning here is needed.
+ return;
+ }
+ current_reloc_section_->relocations.emplace_back(reloc_type, offset,
+ symbol_index);
+}
+
+void BinaryWriter::WriteU32Leb128WithReloc(Index index,
+ const char* desc,
+ RelocType reloc_type) {
+ if (options_.relocatable) {
+ AddReloc(reloc_type, index);
+ WriteFixedU32Leb128(stream_, index, desc);
+ } else {
+ WriteU32Leb128(stream_, index, desc);
+ }
+}
+
+void BinaryWriter::WriteS32Leb128WithReloc(int32_t value,
+ const char* desc,
+ RelocType reloc_type) {
+ if (options_.relocatable) {
+ AddReloc(reloc_type, value);
+ WriteFixedS32Leb128(stream_, value, desc);
+ } else {
+ WriteS32Leb128(stream_, value, desc);
+ }
+}
+
+void BinaryWriter::WriteTableNumberWithReloc(Index value, const char* desc) {
+ // Unless reference types are enabled, all references to tables refer to table
+ // 0, so no relocs need be emitted when making relocatable binaries.
+ if (options_.relocatable && options_.features.reference_types_enabled()) {
+ AddReloc(RelocType::TableNumberLEB, value);
+ WriteFixedS32Leb128(stream_, value, desc);
+ } else {
+ WriteS32Leb128(stream_, value, desc);
+ }
+}
+
+Index BinaryWriter::GetLocalIndex(const Func* func, const Var& var) {
+ // func can be nullptr when using local.get/local.set/local.tee in an
+ // init_expr.
+ if (func) {
+ return func->GetLocalIndex(var);
+ } else if (var.is_index()) {
+ return var.index();
+ } else {
+ return kInvalidIndex;
+ }
+}
+
+// TODO(binji): Rename this, it is used for more than loads/stores now.
+template <typename T>
+void BinaryWriter::WriteLoadStoreExpr(const Func* func,
+ const Expr* expr,
+ const char* desc) {
+ auto* typed_expr = cast<T>(expr);
+ WriteOpcode(stream_, typed_expr->opcode);
+ Address align = typed_expr->opcode.GetAlignment(typed_expr->align);
+ Index memidx = module_->GetMemoryIndex(typed_expr->memidx);
+ if (memidx != 0) {
+ stream_->WriteU8(log2_u32(align) | (1 << 6), "alignment");
+ WriteU32Leb128(stream_, memidx, "memidx");
+ } else {
+ stream_->WriteU8(log2_u32(align), "alignment");
+ }
+ WriteU32Leb128(stream_, typed_expr->offset, desc);
+}
+
+template <typename T>
+void BinaryWriter::WriteSimdLoadStoreLaneExpr(const Func* func,
+ const Expr* expr,
+ const char* desc) {
+ WriteLoadStoreExpr<T>(func, expr, desc);
+ auto* typed_expr = cast<T>(expr);
+ stream_->WriteU8(static_cast<uint8_t>(typed_expr->val), "Simd Lane literal");
+}
+
+void BinaryWriter::WriteExpr(const Func* func, const Expr* expr) {
+ switch (expr->type()) {
+ case ExprType::AtomicLoad:
+ WriteLoadStoreExpr<AtomicLoadExpr>(func, expr, "memory offset");
+ break;
+ case ExprType::AtomicRmw:
+ WriteLoadStoreExpr<AtomicRmwExpr>(func, expr, "memory offset");
+ break;
+ case ExprType::AtomicRmwCmpxchg:
+ WriteLoadStoreExpr<AtomicRmwCmpxchgExpr>(func, expr, "memory offset");
+ break;
+ case ExprType::AtomicStore:
+ WriteLoadStoreExpr<AtomicStoreExpr>(func, expr, "memory offset");
+ break;
+ case ExprType::AtomicWait:
+ WriteLoadStoreExpr<AtomicWaitExpr>(func, expr, "memory offset");
+ break;
+ case ExprType::AtomicFence: {
+ auto* fence_expr = cast<AtomicFenceExpr>(expr);
+ WriteOpcode(stream_, Opcode::AtomicFence);
+ WriteU32Leb128(stream_, fence_expr->consistency_model,
+ "consistency model");
+ break;
+ }
+ case ExprType::AtomicNotify:
+ WriteLoadStoreExpr<AtomicNotifyExpr>(func, expr, "memory offset");
+ break;
+ case ExprType::Binary:
+ WriteOpcode(stream_, cast<BinaryExpr>(expr)->opcode);
+ break;
+ case ExprType::Block:
+ WriteOpcode(stream_, Opcode::Block);
+ WriteBlockDecl(cast<BlockExpr>(expr)->block.decl);
+ WriteExprList(func, cast<BlockExpr>(expr)->block.exprs);
+ WriteOpcode(stream_, Opcode::End);
+ break;
+ case ExprType::Br:
+ WriteOpcode(stream_, Opcode::Br);
+ WriteU32Leb128(stream_, GetLabelVarDepth(&cast<BrExpr>(expr)->var),
+ "break depth");
+ break;
+ case ExprType::BrIf:
+ WriteOpcode(stream_, Opcode::BrIf);
+ WriteU32Leb128(stream_, GetLabelVarDepth(&cast<BrIfExpr>(expr)->var),
+ "break depth");
+ break;
+ case ExprType::BrTable: {
+ auto* br_table_expr = cast<BrTableExpr>(expr);
+ WriteOpcode(stream_, Opcode::BrTable);
+ WriteU32Leb128(stream_, br_table_expr->targets.size(), "num targets");
+ Index depth;
+ for (const Var& var : br_table_expr->targets) {
+ depth = GetLabelVarDepth(&var);
+ WriteU32Leb128(stream_, depth, "break depth");
+ }
+ depth = GetLabelVarDepth(&br_table_expr->default_target);
+ WriteU32Leb128(stream_, depth, "break depth for default");
+ break;
+ }
+ case ExprType::Call: {
+ Index index = module_->GetFuncIndex(cast<CallExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::Call);
+ WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB);
+ break;
+ }
+ case ExprType::ReturnCall: {
+ Index index = module_->GetFuncIndex(cast<ReturnCallExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::ReturnCall);
+ WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB);
+ break;
+ }
+ case ExprType::CallIndirect: {
+ Index sig_index =
+ module_->GetFuncTypeIndex(cast<CallIndirectExpr>(expr)->decl);
+ Index table_index =
+ module_->GetTableIndex(cast<CallIndirectExpr>(expr)->table);
+ WriteOpcode(stream_, Opcode::CallIndirect);
+ WriteU32Leb128WithReloc(sig_index, "signature index",
+ RelocType::TypeIndexLEB);
+ WriteTableNumberWithReloc(table_index, "table index");
+ break;
+ }
+ case ExprType::CallRef: {
+ WriteOpcode(stream_, Opcode::CallRef);
+ break;
+ }
+ case ExprType::ReturnCallIndirect: {
+ Index sig_index =
+ module_->GetFuncTypeIndex(cast<ReturnCallIndirectExpr>(expr)->decl);
+ Index table_index =
+ module_->GetTableIndex(cast<ReturnCallIndirectExpr>(expr)->table);
+ WriteOpcode(stream_, Opcode::ReturnCallIndirect);
+ WriteU32Leb128WithReloc(sig_index, "signature index",
+ RelocType::TypeIndexLEB);
+ WriteTableNumberWithReloc(table_index, "table index");
+ break;
+ }
+ case ExprType::Compare:
+ WriteOpcode(stream_, cast<CompareExpr>(expr)->opcode);
+ break;
+ case ExprType::Const: {
+ const Const& const_ = cast<ConstExpr>(expr)->const_;
+ switch (const_.type()) {
+ case Type::I32: {
+ WriteOpcode(stream_, Opcode::I32Const);
+ WriteS32Leb128(stream_, const_.u32(), "i32 literal");
+ break;
+ }
+ case Type::I64:
+ WriteOpcode(stream_, Opcode::I64Const);
+ WriteS64Leb128(stream_, const_.u64(), "i64 literal");
+ break;
+ case Type::F32:
+ WriteOpcode(stream_, Opcode::F32Const);
+ stream_->WriteU32(const_.f32_bits(), "f32 literal");
+ break;
+ case Type::F64:
+ WriteOpcode(stream_, Opcode::F64Const);
+ stream_->WriteU64(const_.f64_bits(), "f64 literal");
+ break;
+ case Type::V128:
+ WriteOpcode(stream_, Opcode::V128Const);
+ stream_->WriteU128(const_.vec128(), "v128 literal");
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ }
+ case ExprType::Convert:
+ WriteOpcode(stream_, cast<ConvertExpr>(expr)->opcode);
+ break;
+ case ExprType::Drop:
+ WriteOpcode(stream_, Opcode::Drop);
+ break;
+ case ExprType::GlobalGet: {
+ Index index = module_->GetGlobalIndex(cast<GlobalGetExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::GlobalGet);
+ WriteU32Leb128WithReloc(index, "global index", RelocType::GlobalIndexLEB);
+ break;
+ }
+ case ExprType::GlobalSet: {
+ Index index = module_->GetGlobalIndex(cast<GlobalSetExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::GlobalSet);
+ WriteU32Leb128WithReloc(index, "global index", RelocType::GlobalIndexLEB);
+ break;
+ }
+ case ExprType::If: {
+ auto* if_expr = cast<IfExpr>(expr);
+ WriteOpcode(stream_, Opcode::If);
+ WriteBlockDecl(if_expr->true_.decl);
+ WriteExprList(func, if_expr->true_.exprs);
+ if (!if_expr->false_.empty()) {
+ WriteOpcode(stream_, Opcode::Else);
+ WriteExprList(func, if_expr->false_);
+ }
+ WriteOpcode(stream_, Opcode::End);
+ break;
+ }
+ case ExprType::Load:
+ WriteLoadStoreExpr<LoadExpr>(func, expr, "load offset");
+ break;
+ case ExprType::LocalGet: {
+ Index index = GetLocalIndex(func, cast<LocalGetExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::LocalGet);
+ WriteU32Leb128(stream_, index, "local index");
+ break;
+ }
+ case ExprType::LocalSet: {
+ Index index = GetLocalIndex(func, cast<LocalSetExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::LocalSet);
+ WriteU32Leb128(stream_, index, "local index");
+ break;
+ }
+ case ExprType::LocalTee: {
+ Index index = GetLocalIndex(func, cast<LocalTeeExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::LocalTee);
+ WriteU32Leb128(stream_, index, "local index");
+ break;
+ }
+ case ExprType::Loop:
+ WriteOpcode(stream_, Opcode::Loop);
+ WriteBlockDecl(cast<LoopExpr>(expr)->block.decl);
+ WriteExprList(func, cast<LoopExpr>(expr)->block.exprs);
+ WriteOpcode(stream_, Opcode::End);
+ break;
+ case ExprType::MemoryCopy: {
+ Index srcmemidx =
+ module_->GetMemoryIndex(cast<MemoryCopyExpr>(expr)->srcmemidx);
+ Index destmemidx =
+ module_->GetMemoryIndex(cast<MemoryCopyExpr>(expr)->destmemidx);
+ WriteOpcode(stream_, Opcode::MemoryCopy);
+ WriteU32Leb128(stream_, srcmemidx, "memory.copy srcmemidx");
+ WriteU32Leb128(stream_, destmemidx, "memory.copy destmemidx");
+ break;
+ }
+ case ExprType::DataDrop: {
+ Index index = module_->GetDataSegmentIndex(cast<DataDropExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::DataDrop);
+ WriteU32Leb128(stream_, index, "data.drop segment");
+ has_data_segment_instruction_ = true;
+ break;
+ }
+ case ExprType::MemoryFill: {
+ Index memidx =
+ module_->GetMemoryIndex(cast<MemoryFillExpr>(expr)->memidx);
+ WriteOpcode(stream_, Opcode::MemoryFill);
+ WriteU32Leb128(stream_, memidx, "memory.fill memidx");
+ break;
+ }
+ case ExprType::MemoryGrow: {
+ Index memidx =
+ module_->GetMemoryIndex(cast<MemoryGrowExpr>(expr)->memidx);
+ WriteOpcode(stream_, Opcode::MemoryGrow);
+ WriteU32Leb128(stream_, memidx, "memory.grow memidx");
+ break;
+ }
+ case ExprType::MemoryInit: {
+ Index index =
+ module_->GetDataSegmentIndex(cast<MemoryInitExpr>(expr)->var);
+ Index memidx =
+ module_->GetMemoryIndex(cast<MemoryInitExpr>(expr)->memidx);
+ WriteOpcode(stream_, Opcode::MemoryInit);
+ WriteU32Leb128(stream_, index, "memory.init segment");
+ WriteU32Leb128(stream_, memidx, "memory.init memidx");
+ has_data_segment_instruction_ = true;
+ break;
+ }
+ case ExprType::MemorySize: {
+ Index memidx =
+ module_->GetMemoryIndex(cast<MemorySizeExpr>(expr)->memidx);
+ WriteOpcode(stream_, Opcode::MemorySize);
+ WriteU32Leb128(stream_, memidx, "memory.size memidx");
+ break;
+ }
+ case ExprType::TableCopy: {
+ auto* copy_expr = cast<TableCopyExpr>(expr);
+ Index dst = module_->GetTableIndex(copy_expr->dst_table);
+ Index src = module_->GetTableIndex(copy_expr->src_table);
+ WriteOpcode(stream_, Opcode::TableCopy);
+ WriteTableNumberWithReloc(dst, "table.copy dst_table");
+ WriteTableNumberWithReloc(src, "table.copy src_table");
+ break;
+ }
+ case ExprType::ElemDrop: {
+ Index index = module_->GetElemSegmentIndex(cast<ElemDropExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::ElemDrop);
+ WriteU32Leb128(stream_, index, "elem.drop segment");
+ break;
+ }
+ case ExprType::TableInit: {
+ auto* init_expr = cast<TableInitExpr>(expr);
+ Index table_index = module_->GetTableIndex(init_expr->table_index);
+ Index segment_index =
+ module_->GetElemSegmentIndex(init_expr->segment_index);
+ WriteOpcode(stream_, Opcode::TableInit);
+ WriteU32Leb128(stream_, segment_index, "table.init segment");
+ WriteTableNumberWithReloc(table_index, "table.init table");
+ break;
+ }
+ case ExprType::TableGet: {
+ Index index = module_->GetTableIndex(cast<TableGetExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::TableGet);
+ WriteTableNumberWithReloc(index, "table.get table index");
+ break;
+ }
+ case ExprType::TableSet: {
+ Index index = module_->GetTableIndex(cast<TableSetExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::TableSet);
+ WriteTableNumberWithReloc(index, "table.set table index");
+ break;
+ }
+ case ExprType::TableGrow: {
+ Index index = module_->GetTableIndex(cast<TableGrowExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::TableGrow);
+ WriteTableNumberWithReloc(index, "table.grow table index");
+ break;
+ }
+ case ExprType::TableSize: {
+ Index index = module_->GetTableIndex(cast<TableSizeExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::TableSize);
+ WriteTableNumberWithReloc(index, "table.size table index");
+ break;
+ }
+ case ExprType::TableFill: {
+ Index index = module_->GetTableIndex(cast<TableFillExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::TableFill);
+ WriteTableNumberWithReloc(index, "table.fill table index");
+ break;
+ }
+ case ExprType::RefFunc: {
+ WriteOpcode(stream_, Opcode::RefFunc);
+ Index index = module_->GetFuncIndex(cast<RefFuncExpr>(expr)->var);
+ WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB);
+ break;
+ }
+ case ExprType::RefNull: {
+ WriteOpcode(stream_, Opcode::RefNull);
+ WriteType(stream_, cast<RefNullExpr>(expr)->type, "ref.null type");
+ break;
+ }
+ case ExprType::RefIsNull:
+ WriteOpcode(stream_, Opcode::RefIsNull);
+ break;
+ case ExprType::Nop:
+ WriteOpcode(stream_, Opcode::Nop);
+ break;
+ case ExprType::Rethrow:
+ WriteOpcode(stream_, Opcode::Rethrow);
+ WriteU32Leb128(stream_, GetLabelVarDepth(&cast<RethrowExpr>(expr)->var),
+ "rethrow depth");
+ break;
+ case ExprType::Return:
+ WriteOpcode(stream_, Opcode::Return);
+ break;
+ case ExprType::Select: {
+ auto* select_expr = cast<SelectExpr>(expr);
+ if (select_expr->result_type.empty()) {
+ WriteOpcode(stream_, Opcode::Select);
+ } else {
+ WriteOpcode(stream_, Opcode::SelectT);
+ WriteU32Leb128(stream_, select_expr->result_type.size(),
+ "num result types");
+ for (Type t : select_expr->result_type) {
+ WriteType(stream_, t, "result type");
+ }
+ }
+ break;
+ }
+ case ExprType::Store:
+ WriteLoadStoreExpr<StoreExpr>(func, expr, "store offset");
+ break;
+ case ExprType::Throw:
+ WriteOpcode(stream_, Opcode::Throw);
+ WriteU32Leb128(stream_, GetTagVarDepth(&cast<ThrowExpr>(expr)->var),
+ "throw tag");
+ break;
+ case ExprType::Try: {
+ auto* try_expr = cast<TryExpr>(expr);
+ WriteOpcode(stream_, Opcode::Try);
+ WriteBlockDecl(try_expr->block.decl);
+ WriteExprList(func, try_expr->block.exprs);
+ switch (try_expr->kind) {
+ case TryKind::Catch:
+ for (const Catch& catch_ : try_expr->catches) {
+ if (catch_.IsCatchAll()) {
+ WriteOpcode(stream_, Opcode::CatchAll);
+ } else {
+ WriteOpcode(stream_, Opcode::Catch);
+ WriteU32Leb128(stream_, GetTagVarDepth(&catch_.var), "catch tag");
+ }
+ WriteExprList(func, catch_.exprs);
+ }
+ WriteOpcode(stream_, Opcode::End);
+ break;
+ case TryKind::Delegate:
+ WriteOpcode(stream_, Opcode::Delegate);
+ WriteU32Leb128(stream_, GetLabelVarDepth(&try_expr->delegate_target),
+ "delegate depth");
+ break;
+ case TryKind::Plain:
+ WriteOpcode(stream_, Opcode::End);
+ break;
+ }
+ break;
+ }
+ case ExprType::Unary:
+ WriteOpcode(stream_, cast<UnaryExpr>(expr)->opcode);
+ break;
+ case ExprType::Ternary:
+ WriteOpcode(stream_, cast<TernaryExpr>(expr)->opcode);
+ break;
+ case ExprType::SimdLaneOp: {
+ const Opcode opcode = cast<SimdLaneOpExpr>(expr)->opcode;
+ WriteOpcode(stream_, opcode);
+ stream_->WriteU8(static_cast<uint8_t>(cast<SimdLaneOpExpr>(expr)->val),
+ "Simd Lane literal");
+ break;
+ }
+ case ExprType::SimdLoadLane: {
+ WriteSimdLoadStoreLaneExpr<SimdLoadLaneExpr>(func, expr, "load offset");
+ break;
+ }
+ case ExprType::SimdStoreLane: {
+ WriteSimdLoadStoreLaneExpr<SimdStoreLaneExpr>(func, expr, "store offset");
+ break;
+ }
+ case ExprType::SimdShuffleOp: {
+ const Opcode opcode = cast<SimdShuffleOpExpr>(expr)->opcode;
+ WriteOpcode(stream_, opcode);
+ stream_->WriteU128(cast<SimdShuffleOpExpr>(expr)->val,
+ "Simd Lane[16] literal");
+ break;
+ }
+ case ExprType::LoadSplat:
+ WriteLoadStoreExpr<LoadSplatExpr>(func, expr, "load offset");
+ break;
+ case ExprType::LoadZero:
+ WriteLoadStoreExpr<LoadZeroExpr>(func, expr, "load offset");
+ break;
+ 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;
+ }
+ }
+}
+
+void BinaryWriter::WriteExprList(const Func* func, const ExprList& exprs) {
+ for (const Expr& expr : exprs) {
+ WriteExpr(func, &expr);
+ }
+}
+
+void BinaryWriter::WriteInitExpr(const ExprList& expr) {
+ WriteExprList(nullptr, expr);
+ WriteOpcode(stream_, Opcode::End);
+}
+
+void BinaryWriter::WriteFuncLocals(const Func* func,
+ const LocalTypes& local_types) {
+ if (local_types.size() == 0) {
+ WriteU32Leb128(stream_, 0, "local decl count");
+ return;
+ }
+
+ Index local_decl_count = local_types.decls().size();
+ WriteU32Leb128(stream_, local_decl_count, "local decl count");
+ for (auto decl : local_types.decls()) {
+ WriteU32Leb128(stream_, decl.second, "local type count");
+ WriteType(stream_, decl.first);
+ }
+}
+
+void BinaryWriter::WriteFunc(const Func* func) {
+ WriteFuncLocals(func, func->local_types);
+ WriteExprList(func, func->exprs);
+ WriteOpcode(stream_, Opcode::End);
+}
+
+void BinaryWriter::WriteTable(const Table* table) {
+ WriteType(stream_, table->elem_type);
+ WriteLimits(stream_, &table->elem_limits);
+}
+
+void BinaryWriter::WriteMemory(const Memory* memory) {
+ WriteLimits(stream_, &memory->page_limits);
+}
+
+void BinaryWriter::WriteGlobalHeader(const Global* global) {
+ WriteType(stream_, global->type);
+ stream_->WriteU8(global->mutable_, "global mutability");
+}
+
+void BinaryWriter::WriteTagType(const Tag* tag) {
+ stream_->WriteU8(0, "tag attribute");
+ WriteU32Leb128(stream_, module_->GetFuncTypeIndex(tag->decl),
+ "tag signature index");
+}
+
+void BinaryWriter::WriteRelocSection(const RelocSection* reloc_section) {
+ char section_name[128];
+ wabt_snprintf(section_name, sizeof(section_name), "%s.%s",
+ WABT_BINARY_SECTION_RELOC, reloc_section->name);
+ BeginCustomSection(section_name);
+ WriteU32Leb128(stream_, reloc_section->section_index, "reloc section index");
+ const std::vector<Reloc>& relocs = reloc_section->relocations;
+ WriteU32Leb128(stream_, relocs.size(), "num relocs");
+
+ for (const Reloc& reloc : relocs) {
+ WriteU32Leb128(stream_, reloc.type, "reloc type");
+ WriteU32Leb128(stream_, reloc.offset, "reloc offset");
+ WriteU32Leb128(stream_, reloc.index, "reloc index");
+ switch (reloc.type) {
+ case RelocType::MemoryAddressLEB:
+ case RelocType::MemoryAddressLEB64:
+ case RelocType::MemoryAddressSLEB:
+ case RelocType::MemoryAddressSLEB64:
+ case RelocType::MemoryAddressRelSLEB:
+ case RelocType::MemoryAddressRelSLEB64:
+ case RelocType::MemoryAddressI32:
+ case RelocType::MemoryAddressI64:
+ case RelocType::FunctionOffsetI32:
+ case RelocType::SectionOffsetI32:
+ case RelocType::MemoryAddressTLSSLEB:
+ case RelocType::MemoryAddressTLSI32:
+ WriteU32Leb128(stream_, reloc.addend, "reloc addend");
+ break;
+ case RelocType::FuncIndexLEB:
+ case RelocType::TableIndexSLEB:
+ case RelocType::TableIndexSLEB64:
+ case RelocType::TableIndexI32:
+ case RelocType::TableIndexI64:
+ case RelocType::TypeIndexLEB:
+ case RelocType::GlobalIndexLEB:
+ case RelocType::TagIndexLEB:
+ case RelocType::TableIndexRelSLEB:
+ case RelocType::TableNumberLEB:
+ break;
+ default:
+ fprintf(stderr, "warning: unsupported relocation type: %s\n",
+ GetRelocTypeName(reloc.type));
+ }
+ }
+
+ EndSection();
+}
+
+void BinaryWriter::WriteLinkingSection() {
+ BeginCustomSection(WABT_BINARY_SECTION_LINKING);
+ WriteU32Leb128(stream_, 2, "metadata version");
+ const std::vector<Symbol>& symbols = symtab_.symbols();
+ if (symbols.size()) {
+ stream_->WriteU8Enum(LinkingEntryType::SymbolTable, "symbol table");
+ BeginSubsection("symbol table");
+ WriteU32Leb128(stream_, symbols.size(), "num symbols");
+
+ for (const Symbol& sym : symbols) {
+ stream_->WriteU8Enum(sym.type(), "symbol type");
+ WriteU32Leb128(stream_, sym.flags(), "symbol flags");
+ switch (sym.type()) {
+ case SymbolType::Function:
+ WriteU32Leb128(stream_, sym.AsFunction().index, "function index");
+ if (sym.defined() || sym.explicit_name()) {
+ WriteStr(stream_, sym.name(), "function name", PrintChars::Yes);
+ }
+ break;
+ case SymbolType::Data:
+ WriteStr(stream_, sym.name(), "data name", PrintChars::Yes);
+ if (sym.defined()) {
+ WriteU32Leb128(stream_, sym.AsData().index, "data index");
+ WriteU32Leb128(stream_, sym.AsData().offset, "data offset");
+ WriteU32Leb128(stream_, sym.AsData().size, "data size");
+ }
+ break;
+ case SymbolType::Global:
+ WriteU32Leb128(stream_, sym.AsGlobal().index, "global index");
+ if (sym.defined() || sym.explicit_name()) {
+ WriteStr(stream_, sym.name(), "global name", PrintChars::Yes);
+ }
+ break;
+ case SymbolType::Section:
+ WriteU32Leb128(stream_, sym.AsSection().section, "section index");
+ break;
+ case SymbolType::Tag:
+ WriteU32Leb128(stream_, sym.AsTag().index, "tag index");
+ if (sym.defined() || sym.explicit_name()) {
+ WriteStr(stream_, sym.name(), "tag name", PrintChars::Yes);
+ }
+ break;
+ case SymbolType::Table:
+ WriteU32Leb128(stream_, sym.AsTable().index, "table index");
+ if (sym.defined() || sym.explicit_name()) {
+ WriteStr(stream_, sym.name(), "table name", PrintChars::Yes);
+ }
+ break;
+ }
+ }
+ EndSubsection();
+ }
+ EndSection();
+}
+
+template <typename T>
+void BinaryWriter::WriteNames(const std::vector<T*>& elems,
+ NameSectionSubsection type) {
+ size_t num_named_elems = 0;
+ for (const T* elem : elems) {
+ if (!elem->name.empty()) {
+ num_named_elems++;
+ }
+ }
+
+ if (!num_named_elems) {
+ return;
+ }
+
+ WriteU32Leb128(stream_, type, "name subsection type");
+ BeginSubsection("name subsection");
+
+ char desc[100];
+ WriteU32Leb128(stream_, num_named_elems, "num names");
+ for (size_t i = 0; i < elems.size(); ++i) {
+ const T* elem = elems[i];
+ if (elem->name.empty()) {
+ continue;
+ }
+ WriteU32Leb128(stream_, i, "elem index");
+ wabt_snprintf(desc, sizeof(desc), "elem name %" PRIzd, i);
+ WriteDebugName(stream_, elem->name, desc);
+ }
+ EndSubsection();
+}
+
+Result BinaryWriter::WriteModule() {
+ stream_->WriteU32(WABT_BINARY_MAGIC, "WASM_BINARY_MAGIC");
+ stream_->WriteU32(WABT_BINARY_VERSION, "WASM_BINARY_VERSION");
+
+ if (options_.relocatable) {
+ CHECK_RESULT(symtab_.Populate(module_));
+ }
+
+ if (module_->types.size()) {
+ BeginKnownSection(BinarySection::Type);
+ WriteU32Leb128(stream_, module_->types.size(), "num types");
+ for (size_t i = 0; i < module_->types.size(); ++i) {
+ const TypeEntry* type = module_->types[i];
+ switch (type->kind()) {
+ case TypeEntryKind::Func: {
+ const FuncType* func_type = cast<FuncType>(type);
+ const FuncSignature* sig = &func_type->sig;
+ WriteHeader("func type", i);
+ WriteType(stream_, Type::Func);
+
+ Index num_params = sig->param_types.size();
+ Index num_results = sig->result_types.size();
+ WriteU32Leb128(stream_, num_params, "num params");
+ for (size_t j = 0; j < num_params; ++j) {
+ WriteType(stream_, sig->param_types[j]);
+ }
+
+ WriteU32Leb128(stream_, num_results, "num results");
+ for (size_t j = 0; j < num_results; ++j) {
+ WriteType(stream_, sig->result_types[j]);
+ }
+ break;
+ }
+
+ case TypeEntryKind::Struct: {
+ const StructType* struct_type = cast<StructType>(type);
+ WriteHeader("struct type", i);
+ WriteType(stream_, Type::Struct);
+ Index num_fields = struct_type->fields.size();
+ WriteU32Leb128(stream_, num_fields, "num fields");
+ for (size_t j = 0; j < num_fields; ++j) {
+ const Field& field = struct_type->fields[j];
+ WriteType(stream_, field.type);
+ stream_->WriteU8(field.mutable_, "field mutability");
+ }
+ break;
+ }
+
+ case TypeEntryKind::Array: {
+ const ArrayType* array_type = cast<ArrayType>(type);
+ WriteHeader("array type", i);
+ WriteType(stream_, Type::Array);
+ WriteType(stream_, array_type->field.type);
+ stream_->WriteU8(array_type->field.mutable_, "field mutability");
+ break;
+ }
+ }
+ }
+ EndSection();
+ }
+
+ if (module_->imports.size()) {
+ BeginKnownSection(BinarySection::Import);
+ WriteU32Leb128(stream_, module_->imports.size(), "num imports");
+
+ for (size_t i = 0; i < module_->imports.size(); ++i) {
+ const Import* import = module_->imports[i];
+ WriteHeader("import header", i);
+ WriteStr(stream_, import->module_name, "import module name",
+ PrintChars::Yes);
+ WriteStr(stream_, import->field_name, "import field name",
+ PrintChars::Yes);
+ stream_->WriteU8Enum(import->kind(), "import kind");
+ switch (import->kind()) {
+ case ExternalKind::Func:
+ WriteU32Leb128(
+ stream_,
+ module_->GetFuncTypeIndex(cast<FuncImport>(import)->func.decl),
+ "import signature index");
+ break;
+
+ case ExternalKind::Table:
+ WriteTable(&cast<TableImport>(import)->table);
+ break;
+
+ case ExternalKind::Memory:
+ WriteMemory(&cast<MemoryImport>(import)->memory);
+ break;
+
+ case ExternalKind::Global:
+ WriteGlobalHeader(&cast<GlobalImport>(import)->global);
+ break;
+
+ case ExternalKind::Tag:
+ WriteTagType(&cast<TagImport>(import)->tag);
+ break;
+ }
+ }
+ EndSection();
+ }
+
+ assert(module_->funcs.size() >= module_->num_func_imports);
+ Index num_funcs = module_->funcs.size() - module_->num_func_imports;
+ if (num_funcs) {
+ BeginKnownSection(BinarySection::Function);
+ WriteU32Leb128(stream_, num_funcs, "num functions");
+
+ for (size_t i = 0; i < num_funcs; ++i) {
+ const Func* func = module_->funcs[i + module_->num_func_imports];
+ char desc[100];
+ wabt_snprintf(desc, sizeof(desc), "function %" PRIzd " signature index",
+ i);
+ WriteU32Leb128(stream_, module_->GetFuncTypeIndex(func->decl), desc);
+ }
+ EndSection();
+ }
+
+ assert(module_->tables.size() >= module_->num_table_imports);
+ Index num_tables = module_->tables.size() - module_->num_table_imports;
+ if (num_tables) {
+ BeginKnownSection(BinarySection::Table);
+ WriteU32Leb128(stream_, num_tables, "num tables");
+ for (size_t i = 0; i < num_tables; ++i) {
+ const Table* table = module_->tables[i + module_->num_table_imports];
+ WriteHeader("table", i);
+ WriteTable(table);
+ }
+ EndSection();
+ }
+
+ assert(module_->memories.size() >= module_->num_memory_imports);
+ Index num_memories = module_->memories.size() - module_->num_memory_imports;
+ if (num_memories) {
+ BeginKnownSection(BinarySection::Memory);
+ WriteU32Leb128(stream_, num_memories, "num memories");
+ for (size_t i = 0; i < num_memories; ++i) {
+ const Memory* memory = module_->memories[i + module_->num_memory_imports];
+ WriteHeader("memory", i);
+ WriteMemory(memory);
+ }
+ EndSection();
+ }
+
+ assert(module_->tags.size() >= module_->num_tag_imports);
+ Index num_tags = module_->tags.size() - module_->num_tag_imports;
+ if (num_tags) {
+ BeginKnownSection(BinarySection::Tag);
+ WriteU32Leb128(stream_, num_tags, "tag count");
+ for (size_t i = 0; i < num_tags; ++i) {
+ WriteHeader("tag", i);
+ const Tag* tag = module_->tags[i + module_->num_tag_imports];
+ WriteTagType(tag);
+ }
+ EndSection();
+ }
+
+ assert(module_->globals.size() >= module_->num_global_imports);
+ Index num_globals = module_->globals.size() - module_->num_global_imports;
+ if (num_globals) {
+ BeginKnownSection(BinarySection::Global);
+ WriteU32Leb128(stream_, num_globals, "num globals");
+
+ for (size_t i = 0; i < num_globals; ++i) {
+ const Global* global = module_->globals[i + module_->num_global_imports];
+ WriteGlobalHeader(global);
+ WriteInitExpr(global->init_expr);
+ }
+ EndSection();
+ }
+
+ if (module_->exports.size()) {
+ BeginKnownSection(BinarySection::Export);
+ WriteU32Leb128(stream_, module_->exports.size(), "num exports");
+
+ for (const Export* export_ : module_->exports) {
+ WriteStr(stream_, export_->name, "export name", PrintChars::Yes);
+ stream_->WriteU8Enum(export_->kind, "export kind");
+ switch (export_->kind) {
+ case ExternalKind::Func: {
+ Index index = module_->GetFuncIndex(export_->var);
+ WriteU32Leb128(stream_, index, "export func index");
+ break;
+ }
+ case ExternalKind::Table: {
+ Index index = module_->GetTableIndex(export_->var);
+ WriteU32Leb128(stream_, index, "export table index");
+ break;
+ }
+ case ExternalKind::Memory: {
+ Index index = module_->GetMemoryIndex(export_->var);
+ WriteU32Leb128(stream_, index, "export memory index");
+ break;
+ }
+ case ExternalKind::Global: {
+ Index index = module_->GetGlobalIndex(export_->var);
+ WriteU32Leb128(stream_, index, "export global index");
+ break;
+ }
+ case ExternalKind::Tag: {
+ Index index = module_->GetTagIndex(export_->var);
+ WriteU32Leb128(stream_, index, "export tag index");
+ break;
+ }
+ }
+ }
+ EndSection();
+ }
+
+ if (module_->starts.size()) {
+ Index start_func_index = module_->GetFuncIndex(*module_->starts[0]);
+ if (start_func_index != kInvalidIndex) {
+ BeginKnownSection(BinarySection::Start);
+ WriteU32Leb128(stream_, start_func_index, "start func index");
+ EndSection();
+ }
+ }
+
+ if (module_->elem_segments.size()) {
+ BeginKnownSection(BinarySection::Elem);
+ WriteU32Leb128(stream_, module_->elem_segments.size(), "num elem segments");
+ for (size_t i = 0; i < module_->elem_segments.size(); ++i) {
+ ElemSegment* segment = module_->elem_segments[i];
+ WriteHeader("elem segment header", i);
+ // 1. flags
+ uint8_t flags = segment->GetFlags(module_);
+ stream_->WriteU8(flags, "segment flags");
+ // 2. optional target table
+ if (flags & SegExplicitIndex && segment->kind != SegmentKind::Declared) {
+ WriteU32Leb128(stream_, module_->GetTableIndex(segment->table_var),
+ "table index");
+ }
+ // 3. optional target location within the table (active segments only)
+ if (!(flags & SegPassive)) {
+ WriteInitExpr(segment->offset);
+ }
+ // 4. type of item in the following list (omitted for "legacy" segments)
+ if (flags & (SegPassive | SegExplicitIndex)) {
+ if (flags & SegUseElemExprs) {
+ WriteType(stream_, segment->elem_type, "elem expr list type");
+ } else {
+ stream_->WriteU8Enum(ExternalKind::Func, "elem list type");
+ }
+ }
+ // 5. actual list of elements (with extern indexes or elem expr's)
+ // preceeded by length
+ WriteU32Leb128(stream_, segment->elem_exprs.size(), "num elems");
+ if (flags & SegUseElemExprs) {
+ for (const ExprList& elem_expr : segment->elem_exprs) {
+ WriteInitExpr(elem_expr);
+ }
+ } else {
+ for (const ExprList& elem_expr : segment->elem_exprs) {
+ assert(elem_expr.size() == 1);
+ const Expr* expr = &elem_expr.front();
+ assert(expr->type() == ExprType::RefFunc);
+ WriteU32Leb128(stream_,
+ module_->GetFuncIndex(cast<RefFuncExpr>(expr)->var),
+ "elem function index");
+ }
+ }
+ }
+ EndSection();
+ }
+
+ if (options_.features.bulk_memory_enabled() &&
+ module_->data_segments.size()) {
+ // Keep track of the data count section offset so it can be removed if
+ // it isn't needed.
+ data_count_start_ = stream_->offset();
+ BeginKnownSection(BinarySection::DataCount);
+ WriteU32Leb128(stream_, module_->data_segments.size(), "data count");
+ EndSection();
+ data_count_end_ = stream_->offset();
+ }
+
+ if (num_funcs) {
+ code_start_ = stream_->offset();
+ BeginKnownSection(BinarySection::Code);
+ 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[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_;
+ auto delta = WriteFixupU32Leb128Size(body_size_offset, leb_size_guess,
+ "FIXUP func body size");
+ if (current_reloc_section_ && delta != 0) {
+ for (Reloc& reloc : current_reloc_section_->relocations) {
+ if (reloc.offset >= func_start_offset &&
+ reloc.offset <= func_end_offset) {
+ reloc.offset += delta;
+ }
+ }
+ }
+ }
+ EndSection();
+ }
+
+ // Remove the DataCount section if there are no instructions that require it.
+ if (options_.features.bulk_memory_enabled() &&
+ module_->data_segments.size() && !has_data_segment_instruction_) {
+ Offset size = stream_->offset() - data_count_end_;
+ if (size) {
+ // If the DataCount section was followed by anything, assert that it's
+ // only the Code section. This limits the amount of fixing-up that we
+ // need to do.
+ 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);
+
+ --section_count_;
+
+ // We just effectively decremented 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;
+ }
+ }
+ }
+
+ WriteCodeMetadataSections();
+
+ if (module_->data_segments.size()) {
+ BeginKnownSection(BinarySection::Data);
+ WriteU32Leb128(stream_, module_->data_segments.size(), "num data segments");
+ for (size_t i = 0; i < module_->data_segments.size(); ++i) {
+ const DataSegment* segment = module_->data_segments[i];
+ WriteHeader("data segment header", i);
+ uint8_t flags = segment->GetFlags(module_);
+ stream_->WriteU8(flags, "segment flags");
+ if (!(flags & SegPassive)) {
+ if (options_.features.multi_memory_enabled() &&
+ (flags & SegExplicitIndex)) {
+ WriteU32Leb128(stream_, module_->GetMemoryIndex(segment->memory_var),
+ "memidx");
+ } else {
+ assert(module_->GetMemoryIndex(segment->memory_var) == 0);
+ }
+ WriteInitExpr(segment->offset);
+ }
+ WriteU32Leb128(stream_, segment->data.size(), "data segment size");
+ WriteHeader("data segment data", i);
+ stream_->WriteData(segment->data, "data segment data");
+ }
+ EndSection();
+ }
+
+ if (options_.write_debug_names) {
+ std::vector<std::string> index_to_name;
+
+ char desc[100];
+ BeginCustomSection(WABT_BINARY_SECTION_NAME);
+
+ if (!module_->name.empty()) {
+ WriteU32Leb128(stream_, NameSectionSubsection::Module,
+ "module name type");
+ BeginSubsection("module name subsection");
+ WriteDebugName(stream_, module_->name, "module name");
+ EndSubsection();
+ }
+
+ WriteNames<Func>(module_->funcs, NameSectionSubsection::Function);
+
+ WriteU32Leb128(stream_, 2, "local name type");
+
+ BeginSubsection("local name subsection");
+ WriteU32Leb128(stream_, module_->funcs.size(), "num functions");
+ for (size_t i = 0; i < module_->funcs.size(); ++i) {
+ const Func* func = module_->funcs[i];
+ Index num_params_and_locals = func->GetNumParamsAndLocals();
+ MakeTypeBindingReverseMapping(num_params_and_locals, func->bindings,
+ &index_to_name);
+ Index num_named = 0;
+ for (auto s : index_to_name) {
+ if (!s.empty()) {
+ num_named++;
+ }
+ }
+
+ WriteU32Leb128(stream_, i, "function index");
+ WriteU32Leb128(stream_, num_named, "num locals");
+
+ for (size_t j = 0; j < num_params_and_locals; ++j) {
+ const std::string& name = index_to_name[j];
+ if (!name.empty()) {
+ wabt_snprintf(desc, sizeof(desc), "local name %" PRIzd, j);
+ WriteU32Leb128(stream_, j, "local index");
+ WriteDebugName(stream_, name, desc);
+ }
+ }
+ }
+ EndSubsection();
+
+ WriteNames<TypeEntry>(module_->types, NameSectionSubsection::Type);
+ WriteNames<Table>(module_->tables, NameSectionSubsection::Table);
+ WriteNames<Memory>(module_->memories, NameSectionSubsection::Memory);
+ WriteNames<Global>(module_->globals, NameSectionSubsection::Global);
+ WriteNames<ElemSegment>(module_->elem_segments,
+ NameSectionSubsection::ElemSegment);
+ WriteNames<DataSegment>(module_->data_segments,
+ NameSectionSubsection::DataSegment);
+ WriteNames<Tag>(module_->tags, NameSectionSubsection::Tag);
+
+ EndSection();
+ }
+
+ if (options_.relocatable) {
+ WriteLinkingSection();
+ for (RelocSection& section : reloc_sections_) {
+ WriteRelocSection(&section);
+ }
+ }
+
+ 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,
+ const Module* module,
+ const WriteBinaryOptions& options) {
+ BinaryWriter binary_writer(stream, options, module);
+ return binary_writer.WriteModule();
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/binary.cc b/third_party/wasm2c/src/binary.cc
new file mode 100644
index 0000000000..d4938a1939
--- /dev/null
+++ b/third_party/wasm2c/src/binary.cc
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/binary.h"
+
+namespace wabt {
+
+BinarySectionOrder GetSectionOrder(BinarySection sec) {
+ switch (sec) {
+#define V(Name, name, code) \
+ case BinarySection::Name: \
+ return BinarySectionOrder::Name;
+ WABT_FOREACH_BINARY_SECTION(V)
+#undef V
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+const char* GetSectionName(BinarySection sec) {
+ switch (sec) {
+#define V(Name, name, code) \
+ case BinarySection::Name: \
+ return #Name;
+ WABT_FOREACH_BINARY_SECTION(V)
+#undef V
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+// clang-format off
+const char* NameSubsectionName[] = {
+ "module",
+ "function",
+ "local",
+ "label",
+ "type",
+ "table",
+ "memory",
+ "global",
+ "elemseg",
+ "dataseg",
+ "tag",
+};
+// clang-format on
+
+const char* GetNameSectionSubsectionName(NameSectionSubsection subsec) {
+ static_assert(WABT_ENUM_COUNT(NameSectionSubsection) ==
+ WABT_ARRAY_SIZE(NameSubsectionName),
+ "Malformed ExprTypeName array");
+ return NameSubsectionName[size_t(subsec)];
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/binding-hash.cc b/third_party/wasm2c/src/binding-hash.cc
new file mode 100644
index 0000000000..cd1b5f5a3e
--- /dev/null
+++ b/third_party/wasm2c/src/binding-hash.cc
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/binding-hash.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "wabt/ir.h"
+
+namespace wabt {
+
+void BindingHash::FindDuplicates(DuplicateCallback callback) const {
+ if (size() > 0) {
+ ValueTypeVector duplicates;
+ CreateDuplicatesVector(&duplicates);
+ SortDuplicatesVectorByLocation(&duplicates);
+ CallCallbacks(duplicates, callback);
+ }
+}
+
+Index BindingHash::FindIndex(const Var& var) const {
+ if (var.is_name()) {
+ return FindIndex(var.name());
+ }
+ return var.index();
+}
+
+void BindingHash::CreateDuplicatesVector(
+ ValueTypeVector* out_duplicates) const {
+ // This relies on the fact that in an unordered_multimap, all values with the
+ // same key are adjacent in iteration order.
+ auto first = begin();
+ bool is_first = true;
+ for (auto iter = std::next(first); iter != end(); ++iter) {
+ if (first->first == iter->first) {
+ if (is_first) {
+ out_duplicates->push_back(&*first);
+ }
+ out_duplicates->push_back(&*iter);
+ is_first = false;
+ } else {
+ is_first = true;
+ first = iter;
+ }
+ }
+}
+
+void BindingHash::SortDuplicatesVectorByLocation(
+ ValueTypeVector* duplicates) const {
+ std::sort(
+ duplicates->begin(), duplicates->end(),
+ [](const value_type* lhs, const value_type* rhs) -> bool {
+ return lhs->second.loc.line < rhs->second.loc.line ||
+ (lhs->second.loc.line == rhs->second.loc.line &&
+ lhs->second.loc.first_column < rhs->second.loc.first_column);
+ });
+}
+
+void BindingHash::CallCallbacks(const ValueTypeVector& duplicates,
+ DuplicateCallback callback) const {
+ // Loop through all duplicates in order, and call callback with first
+ // occurrence.
+ for (auto iter = duplicates.begin(), end = duplicates.end(); iter != end;
+ ++iter) {
+ auto first = std::find_if(duplicates.begin(), duplicates.end(),
+ [iter](const value_type* x) -> bool {
+ return x->first == (*iter)->first;
+ });
+ if (first == iter) {
+ continue;
+ }
+ assert(first != duplicates.end());
+ callback(**first, **iter);
+ }
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/c-writer.cc b/third_party/wasm2c/src/c-writer.cc
new file mode 100644
index 0000000000..2405c170ae
--- /dev/null
+++ b/third_party/wasm2c/src/c-writer.cc
@@ -0,0 +1,5295 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/c-writer.h"
+
+#include <cctype>
+#include <cinttypes>
+#include <iterator>
+#include <limits>
+#include <map>
+#include <set>
+#include <string_view>
+#include <vector>
+
+#include "wabt/cast.h"
+#include "wabt/common.h"
+#include "wabt/ir.h"
+#include "wabt/literal.h"
+#include "wabt/sha256.h"
+#include "wabt/stream.h"
+#include "wabt/string-util.h"
+
+#define INDENT_SIZE 2
+
+#define UNIMPLEMENTED(x) printf("unimplemented: %s\n", (x)), abort()
+
+// code to be inserted into the generated output
+extern const char* s_header_top;
+extern const char* s_header_bottom;
+extern const char* s_source_includes;
+extern const char* s_source_declarations;
+extern const char* s_simd_source_declarations;
+
+namespace wabt {
+
+namespace {
+
+struct Label {
+ Label(LabelType label_type,
+ const std::string& name,
+ const TypeVector& sig,
+ size_t type_stack_size,
+ size_t try_catch_stack_size,
+ bool used = false)
+ : label_type(label_type),
+ name(name),
+ sig(sig),
+ type_stack_size(type_stack_size),
+ try_catch_stack_size(try_catch_stack_size),
+ used(used) {}
+
+ bool HasValue() const { return !sig.empty(); }
+
+ LabelType label_type;
+ const std::string& name;
+ const TypeVector& sig;
+ size_t type_stack_size;
+ size_t try_catch_stack_size;
+ bool used = false;
+};
+
+struct LocalName {
+ explicit LocalName(const std::string& name) : name(name) {}
+ const std::string& name;
+};
+
+struct ParamName : LocalName {
+ using LocalName::LocalName;
+ ParamName(const Var& var) : LocalName(var.name()) {}
+};
+
+struct LabelName : LocalName {
+ using LocalName::LocalName;
+};
+
+struct GlobalName {
+ GlobalName(ModuleFieldType type, const std::string& name)
+ : type(type), name(name) {}
+ ModuleFieldType type;
+ const std::string& name;
+};
+
+struct ExternalPtr : GlobalName {
+ using GlobalName::GlobalName;
+};
+
+struct ExternalRef : GlobalName {
+ using GlobalName::GlobalName;
+};
+
+struct ExternalInstancePtr : GlobalName {
+ using GlobalName::GlobalName;
+};
+
+struct ExternalInstanceRef : GlobalName {
+ using GlobalName::GlobalName;
+};
+
+struct GotoLabel {
+ explicit GotoLabel(const Var& var) : var(var) {}
+ const Var& var;
+};
+
+struct LabelDecl {
+ explicit LabelDecl(const std::string& name) : name(name) {}
+ std::string name;
+};
+
+struct GlobalInstanceVar {
+ explicit GlobalInstanceVar(const Var& var) : var(var) {}
+ const Var& var;
+};
+
+struct StackVar {
+ explicit StackVar(Index index, Type type = Type::Any)
+ : index(index), type(type) {}
+ Index index;
+ Type type;
+};
+
+struct TypeEnum {
+ explicit TypeEnum(Type type) : type(type) {}
+ Type type;
+};
+
+struct SignedType {
+ explicit SignedType(Type type) : type(type) {}
+ Type type;
+};
+
+struct ResultType {
+ explicit ResultType(const TypeVector& types) : types(types) {}
+ const TypeVector& types;
+};
+
+struct TryCatchLabel {
+ TryCatchLabel(const std::string& name, size_t try_catch_stack_size)
+ : name(name), try_catch_stack_size(try_catch_stack_size), used(false) {}
+ std::string name;
+ size_t try_catch_stack_size;
+ bool used;
+};
+
+struct FuncTypeExpr {
+ const FuncType* func_type;
+ FuncTypeExpr(const FuncType* f) : func_type(f) {}
+};
+
+struct Newline {};
+struct OpenBrace {};
+struct CloseBrace {};
+
+int GetShiftMask(Type type) {
+ // clang-format off
+ switch (type) {
+ case Type::I32: return 31;
+ case Type::I64: return 63;
+ default: WABT_UNREACHABLE; return 0;
+ }
+ // clang-format on
+}
+
+/*
+ * This function is the default behavior for name_to_output_file_index_. For
+ * single .c output, this function returns a vector filled with 0. For multiple
+ * .c outputs, this function sorts all non-imported functions in the module by
+ * their names, and then divides all non-imported functions into equal-sized
+ * buckets (# of non-imported functions / # of .c outputs) based on the sorting.
+ */
+static std::vector<size_t> default_name_to_output_file_index(
+ std::vector<Func*>::const_iterator func_begin,
+ std::vector<Func*>::const_iterator func_end,
+ size_t num_imports,
+ size_t num_streams) {
+ std::vector<size_t> result;
+ result.resize(std::distance(func_begin, func_end));
+ if (num_streams == 1) {
+ return result;
+ }
+
+ std::map<std::string, Index> sorted_functions;
+ size_t non_imported_funcs = result.size() - num_imports;
+ size_t bucket_size = non_imported_funcs / num_streams +
+ (non_imported_funcs % num_streams ? 1 : 0);
+ Index func_index = 0;
+ for (auto func = func_begin; func != func_end; func++) {
+ sorted_functions.insert({(*func)->name, func_index});
+ ++func_index;
+ }
+ Index sorted_func_index = 0;
+ for (const auto& [func_name, index] : sorted_functions) {
+ bool is_import = index < num_imports;
+ if (!is_import) {
+ result.at(index) = sorted_func_index / bucket_size;
+ ++sorted_func_index;
+ }
+ }
+ return result;
+}
+
+class CWriter {
+ public:
+ CWriter(std::vector<Stream*>&& c_streams,
+ Stream* h_stream,
+ Stream* h_impl_stream,
+ const char* header_name,
+ const char* header_impl_name,
+ const WriteCOptions& options)
+ : options_(options),
+ c_streams_(std::move(c_streams)),
+ h_stream_(h_stream),
+ h_impl_stream_(h_impl_stream),
+ header_name_(header_name),
+ header_impl_name_(header_impl_name) {
+ module_prefix_ = MangleModuleName(options_.module_name);
+ if (c_streams_.size() != 1 && options.name_to_output_file_index) {
+ name_to_output_file_index_ = options.name_to_output_file_index;
+ } else {
+ name_to_output_file_index_ = default_name_to_output_file_index;
+ }
+ }
+
+ Result WriteModule(const Module&);
+
+ private:
+ using SymbolSet = std::set<std::string>;
+ using SymbolMap = std::map<std::string, std::string>;
+ using StackTypePair = std::pair<Index, Type>;
+ using StackVarSymbolMap = std::map<StackTypePair, std::string>;
+
+ void WriteCHeader();
+ void WriteCSource();
+
+ size_t MarkTypeStack() const;
+ void ResetTypeStack(size_t mark);
+ Type StackType(Index) const;
+ void PushType(Type);
+ void PushTypes(const TypeVector&);
+ void DropTypes(size_t count);
+
+ void PushLabel(LabelType,
+ const std::string& name,
+ const FuncSignature&,
+ bool used = false);
+ const Label* FindLabel(const Var& var, bool mark_used = true);
+ bool IsTopLabelUsed() const;
+ void PopLabel();
+
+ static constexpr char MangleType(Type);
+ static constexpr char MangleField(ModuleFieldType);
+ static std::string MangleMultivalueTypes(const TypeVector&);
+ static std::string MangleTagTypes(const TypeVector&);
+ static std::string Mangle(std::string_view name, bool double_underscores);
+ static std::string MangleName(std::string_view);
+ static std::string MangleModuleName(std::string_view);
+ std::string ExportName(std::string_view module_name,
+ std::string_view export_name);
+ std::string ExportName(std::string_view export_name);
+ std::string ModuleInstanceTypeName() const;
+ static std::string ModuleInstanceTypeName(std::string_view module_name);
+ void ClaimName(SymbolSet& set,
+ SymbolMap& map,
+ char type_suffix,
+ std::string_view wasm_name,
+ const std::string& c_name);
+ std::string FindUniqueName(SymbolSet& set, std::string_view proposed_name);
+ std::string ClaimUniqueName(SymbolSet& set,
+ SymbolMap& map,
+ char type_suffix,
+ std::string_view wasm_name,
+ const std::string& proposed_c_name);
+ void DefineImportName(const Import* import,
+ std::string_view module_name,
+ std::string_view field_name);
+ void ReserveExportNames();
+ void ReserveExportName(std::string_view);
+ std::string DefineImportedModuleInstanceName(std::string_view name);
+ std::string DefineInstanceMemberName(ModuleFieldType, std::string_view);
+ std::string DefineGlobalScopeName(ModuleFieldType, std::string_view);
+ std::string DefineLocalScopeName(std::string_view name, bool is_label);
+ std::string DefineParamName(std::string_view);
+ std::string DefineLabelName(std::string_view);
+ std::string DefineStackVarName(Index, Type, std::string_view);
+
+ static void SerializeFuncType(const FuncType&, std::string&);
+
+ std::string GetGlobalName(ModuleFieldType, const std::string&) const;
+ std::string GetLocalName(const std::string&, bool is_label) const;
+
+ void Indent(int size = INDENT_SIZE);
+ void Dedent(int size = INDENT_SIZE);
+ void WriteIndent();
+ void WriteData(const char* src, size_t size);
+ void Writef(const char* format, ...);
+
+ template <typename T, typename U, typename... Args>
+ void Write(T&& t, U&& u, Args&&... args) {
+ Write(std::forward<T>(t));
+ Write(std::forward<U>(u));
+ Write(std::forward<Args>(args)...);
+ }
+
+ static const char* GetReferenceTypeName(const Type& type);
+ static const char* GetReferenceNullValue(const Type& type);
+ static const char* GetCTypeName(const Type& type);
+
+ const char* InternalSymbolScope() const;
+
+ enum class CWriterPhase {
+ Declarations,
+ Definitions,
+ };
+
+ void Write() {}
+ void Write(Newline);
+ void Write(OpenBrace);
+ void Write(CloseBrace);
+ void Write(uint64_t);
+ void Write(std::string_view);
+ void Write(const ParamName&);
+ void Write(const LabelName&);
+ void Write(const GlobalName&);
+ void Write(const ExternalPtr&);
+ void Write(const ExternalRef&);
+ void Write(const ExternalInstancePtr&);
+ void Write(const ExternalInstanceRef&);
+ void Write(Type);
+ void Write(SignedType);
+ void Write(TypeEnum);
+ void Write(const GotoLabel&);
+ void Write(const LabelDecl&);
+ void Write(const GlobalInstanceVar&);
+ void Write(const StackVar&);
+ void Write(const ResultType&);
+ void Write(const Const&);
+ void WriteInitExpr(const ExprList&);
+ void WriteInitExprTerminal(const Expr*);
+ std::string GenerateHeaderGuard() const;
+ void WriteSourceTop();
+ void WriteMultiCTop();
+ void WriteMultiCTopEmpty();
+ void WriteMultivalueTypes();
+ void WriteTagTypes();
+ void WriteFuncTypeDecls();
+ void WriteFuncTypes();
+ void Write(const FuncTypeExpr&);
+ void WriteTagDecls();
+ void WriteTags();
+ void ComputeUniqueImports();
+ void BeginInstance();
+ void WriteImports();
+ void WriteFuncDeclarations();
+ void WriteFuncDeclaration(const FuncDeclaration&, const std::string&);
+ void WriteImportFuncDeclaration(const FuncDeclaration&,
+ const std::string& module_name,
+ const std::string&);
+ void WriteCallIndirectFuncDeclaration(const FuncDeclaration&,
+ const std::string&);
+ void ComputeSimdScope();
+ void WriteHeaderIncludes();
+ void WriteV128Decl();
+ void WriteModuleInstance();
+ void WriteGlobals();
+ void WriteGlobal(const Global&, const std::string&);
+ void WriteGlobalPtr(const Global&, const std::string&);
+ void WriteMemories();
+ void WriteMemory(const std::string&);
+ void WriteMemoryPtr(const std::string&);
+ void WriteTables();
+ void WriteTable(const std::string&, const wabt::Type&);
+ void WriteTablePtr(const std::string&, const Table&);
+ void WriteTableType(const wabt::Type&);
+ void WriteDataInstances();
+ void WriteElemInstances();
+ void WriteGlobalInitializers();
+ void WriteDataInitializerDecls();
+ void WriteDataInitializers();
+ void WriteElemInitializerDecls();
+ void WriteElemInitializers();
+ void WriteElemTableInit(bool, const ElemSegment*, const Table*);
+ void WriteExports(CWriterPhase);
+ void WriteInitDecl();
+ void WriteFreeDecl();
+ void WriteGetFuncTypeDecl();
+ void WriteInit();
+ void WriteFree();
+ void WriteGetFuncType();
+ void WriteInitInstanceImport();
+ void WriteImportProperties(CWriterPhase);
+ void WriteFuncs();
+ void Write(const Func&);
+ void WriteParamsAndLocals();
+ void WriteParams(const std::vector<std::string>& index_to_name);
+ void WriteParamSymbols(const std::vector<std::string>& index_to_name);
+ void WriteParamTypes(const FuncDeclaration& decl);
+ void WriteLocals(const std::vector<std::string>& index_to_name);
+ void WriteStackVarDeclarations();
+ void Write(const ExprList&);
+
+ enum class AssignOp {
+ Disallowed,
+ Allowed,
+ };
+
+ void WriteSimpleUnaryExpr(Opcode, const char* op);
+ void WriteInfixBinaryExpr(Opcode,
+ const char* op,
+ AssignOp = AssignOp::Allowed);
+ void WritePrefixBinaryExpr(Opcode, const char* op);
+ void WriteSignedBinaryExpr(Opcode, const char* op);
+ void Write(const BinaryExpr&);
+ void Write(const CompareExpr&);
+ void Write(const ConvertExpr&);
+ void Write(const LoadExpr&);
+ void Write(const StoreExpr&);
+ void Write(const UnaryExpr&);
+ void Write(const TernaryExpr&);
+ void Write(const SimdLaneOpExpr&);
+ void Write(const SimdLoadLaneExpr&);
+ void Write(const SimdStoreLaneExpr&);
+ void Write(const SimdShuffleOpExpr&);
+ void Write(const LoadSplatExpr&);
+ void Write(const LoadZeroExpr&);
+ void Write(const Block&);
+
+ size_t BeginTry(const TryExpr& tryexpr);
+ void WriteTryCatch(const TryExpr& tryexpr);
+ void WriteTryDelegate(const TryExpr& tryexpr);
+ void Write(const Catch& c);
+ void WriteThrow();
+
+ void PushTryCatch(const std::string& name);
+ void PopTryCatch();
+
+ void PushFuncSection(std::string_view include_condition = "");
+
+ const WriteCOptions& options_;
+ const Module* module_ = nullptr;
+ const Func* func_ = nullptr;
+ Stream* stream_ = nullptr;
+ std::vector<Stream*> c_streams_;
+ Stream* h_stream_ = nullptr;
+ Stream* h_impl_stream_ = nullptr;
+ std::string header_name_;
+ std::string header_impl_name_;
+ Result result_ = Result::Ok;
+ int indent_ = 0;
+ bool should_write_indent_next_ = false;
+ int consecutive_newline_count_ = 0;
+
+ SymbolMap global_sym_map_;
+ SymbolMap local_sym_map_;
+ SymbolMap import_module_sym_map_;
+ StackVarSymbolMap stack_var_sym_map_;
+ SymbolSet global_syms_;
+ SymbolSet local_syms_;
+ SymbolSet import_syms_;
+ TypeVector type_stack_;
+ std::vector<Label> label_stack_;
+ std::vector<TryCatchLabel> try_catch_stack_;
+ std::string module_prefix_;
+
+ std::vector<const Import*> unique_imports_;
+ SymbolSet import_module_set_; // modules that are imported from
+ SymbolSet import_func_module_set_; // modules that funcs are imported from
+
+ std::vector<std::pair<std::string, MemoryStream>> func_sections_;
+ SymbolSet func_includes_;
+
+ std::vector<std::string> unique_func_type_names_;
+
+ std::function<std::vector<size_t>(std::vector<Func*>::const_iterator,
+ std::vector<Func*>::const_iterator,
+ size_t,
+ size_t)>
+ name_to_output_file_index_;
+
+ bool simd_used_in_header_;
+};
+
+// TODO: if WABT begins supporting debug names for labels,
+// will need to avoid conflict between a label named "$Bfunc" and
+// the implicit func label
+static constexpr char kImplicitFuncLabel[] = "$Bfunc";
+
+// These should be greater than any ModuleFieldType (used for MangleField).
+static constexpr char kParamSuffix =
+ 'a' + static_cast<char>(ModuleFieldType::Tag) + 1;
+static constexpr char kLabelSuffix = kParamSuffix + 1;
+
+static constexpr char kGlobalSymbolPrefix[] = "w2c_";
+static constexpr char kLocalSymbolPrefix[] = "var_";
+static constexpr char kAdminSymbolPrefix[] = "wasm2c_";
+
+size_t CWriter::MarkTypeStack() const {
+ return type_stack_.size();
+}
+
+void CWriter::ResetTypeStack(size_t mark) {
+ assert(mark <= type_stack_.size());
+ type_stack_.erase(type_stack_.begin() + mark, type_stack_.end());
+}
+
+Type CWriter::StackType(Index index) const {
+ assert(index < type_stack_.size());
+ return *(type_stack_.rbegin() + index);
+}
+
+void CWriter::PushType(Type type) {
+ type_stack_.push_back(type);
+}
+
+void CWriter::PushTypes(const TypeVector& types) {
+ type_stack_.insert(type_stack_.end(), types.begin(), types.end());
+}
+
+void CWriter::DropTypes(size_t count) {
+ assert(count <= type_stack_.size());
+ type_stack_.erase(type_stack_.end() - count, type_stack_.end());
+}
+
+void CWriter::PushLabel(LabelType label_type,
+ const std::string& name,
+ const FuncSignature& sig,
+ bool used) {
+ if (label_type == LabelType::Loop)
+ label_stack_.emplace_back(label_type, name, sig.param_types,
+ type_stack_.size(), try_catch_stack_.size(),
+ used);
+ else
+ label_stack_.emplace_back(label_type, name, sig.result_types,
+ type_stack_.size(), try_catch_stack_.size(),
+ used);
+}
+
+const Label* CWriter::FindLabel(const Var& var, bool mark_used) {
+ Label* label = nullptr;
+
+ if (var.is_index()) {
+ // We've generated names for all labels, so we should only be using an
+ // index when branching to the implicit function label, which can't be
+ // named.
+ assert(var.index() + 1 == label_stack_.size());
+ label = &label_stack_[0];
+ } else {
+ assert(var.is_name());
+ for (Index i = label_stack_.size(); i > 0; --i) {
+ label = &label_stack_[i - 1];
+ if (label->name == var.name())
+ break;
+ }
+ }
+
+ assert(label);
+ if (mark_used) {
+ label->used = true;
+ }
+ return label;
+}
+
+bool CWriter::IsTopLabelUsed() const {
+ assert(!label_stack_.empty());
+ return label_stack_.back().used;
+}
+
+void CWriter::PopLabel() {
+ label_stack_.pop_back();
+}
+
+// static
+constexpr char CWriter::MangleType(Type type) {
+ // clang-format off
+ switch (type) {
+ case Type::I32: return 'i';
+ case Type::I64: return 'j';
+ case Type::F32: return 'f';
+ case Type::F64: return 'd';
+ case Type::V128: return 'o';
+ case Type::FuncRef: return 'r';
+ case Type::ExternRef: return 'e';
+ default:
+ WABT_UNREACHABLE;
+ }
+ // clang-format on
+}
+
+// static
+constexpr char CWriter::MangleField(ModuleFieldType type) {
+ assert(static_cast<std::underlying_type<ModuleFieldType>::type>(type) <
+ std::numeric_limits<char>::max());
+ return 'a' + static_cast<char>(type);
+}
+
+// remove risky characters for pasting into a C-style comment
+static std::string SanitizeForComment(std::string_view str) {
+ std::string result;
+
+ for (const uint8_t ch : str) {
+ // escape control chars, DEL, >7-bit chars, trigraphs, and end of comment
+ if (ch < ' ' || ch > '~' || ch == '?' || ch == '/') {
+ result += "\\" + StringPrintf("%02X", ch);
+ } else {
+ result += ch;
+ }
+ }
+
+ return result;
+}
+
+// static
+std::string CWriter::MangleMultivalueTypes(const TypeVector& types) {
+ assert(types.size() >= 2);
+ std::string result = "wasm_multi_";
+ for (auto type : types) {
+ result += MangleType(type);
+ }
+ return result;
+}
+
+// static
+std::string CWriter::MangleTagTypes(const TypeVector& types) {
+ assert(types.size() >= 2);
+ std::string result = "wasm_tag_";
+ for (auto type : types) {
+ result += MangleType(type);
+ }
+ return result;
+}
+
+/* The C symbol for an export from this module. */
+std::string CWriter::ExportName(std::string_view export_name) {
+ return kGlobalSymbolPrefix + module_prefix_ + '_' + MangleName(export_name);
+}
+
+/* The C symbol for an export from an arbitrary module. */
+// static
+std::string CWriter::ExportName(std::string_view module_name,
+ std::string_view export_name) {
+ return kGlobalSymbolPrefix + MangleModuleName(module_name) + '_' +
+ MangleName(export_name);
+}
+
+/* The type name of an instance of this module. */
+std::string CWriter::ModuleInstanceTypeName() const {
+ return kGlobalSymbolPrefix + module_prefix_;
+}
+
+/* The type name of an instance of an arbitrary module. */
+// static
+std::string CWriter::ModuleInstanceTypeName(std::string_view module_name) {
+ return kGlobalSymbolPrefix + MangleModuleName(module_name);
+}
+
+/*
+ * Hardcoded "C"-locale versions of isalpha/isdigit/isalnum/isxdigit for use
+ * in CWriter::Mangle(). We don't use the standard isalpha/isdigit/isalnum
+ * because the caller might have changed the current locale.
+ */
+static bool internal_isalpha(uint8_t ch) {
+ return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
+}
+
+static bool internal_isdigit(uint8_t ch) {
+ return (ch >= '0' && ch <= '9');
+}
+
+static bool internal_isalnum(uint8_t ch) {
+ return internal_isalpha(ch) || internal_isdigit(ch);
+}
+
+static bool internal_ishexdigit(uint8_t ch) {
+ return internal_isdigit(ch) || (ch >= 'A' && ch <= 'F'); // capitals only
+}
+
+// static
+std::string CWriter::Mangle(std::string_view name, bool double_underscores) {
+ /*
+ * Name mangling transforms arbitrary Wasm names into "safe" C names
+ * in a deterministic way. To avoid collisions, distinct Wasm names must be
+ * transformed into distinct C names.
+ *
+ * The rules implemented here are:
+ * 1) any hex digit ('A' through 'F') that follows the sequence "0x"
+ * is escaped
+ * 2) any underscore at the beginning, at the end, or following another
+ * underscore, is escaped
+ * 3) if double_underscores is set, underscores are replaced with
+ * two underscores.
+ * 4) otherwise, any alphanumeric character is kept as-is,
+ * and any other character is escaped
+ *
+ * "Escaped" means the character is represented with the sequence "0xAB",
+ * where A B are hex digits ('0'-'9' or 'A'-'F') representing the character's
+ * numeric value.
+ *
+ * Module names are mangled with double_underscores=true to prevent
+ * collisions between, e.g., a module "alfa" with export
+ * "bravo_charlie" vs. a module "alfa_bravo" with export "charlie".
+ */
+
+ enum State { Any, Zero, ZeroX, ZeroXHexDigit } state{Any};
+ bool last_was_underscore = false;
+
+ std::string result;
+ auto append_escaped = [&](const uint8_t ch) {
+ result += "0x" + StringPrintf("%02X", ch);
+ last_was_underscore = false;
+ state = Any;
+ };
+
+ auto append_verbatim = [&](const uint8_t ch) {
+ result += ch;
+ last_was_underscore = (ch == '_');
+ };
+
+ for (auto it = name.begin(); it != name.end(); ++it) {
+ const uint8_t ch = *it;
+ switch (state) {
+ case Any:
+ state = (ch == '0') ? Zero : Any;
+ break;
+ case Zero:
+ state = (ch == 'x') ? ZeroX : Any;
+ break;
+ case ZeroX:
+ state = internal_ishexdigit(ch) ? ZeroXHexDigit : Any;
+ break;
+ case ZeroXHexDigit:
+ WABT_UNREACHABLE;
+ break;
+ }
+
+ /* rule 1 */
+ if (state == ZeroXHexDigit) {
+ append_escaped(ch);
+ continue;
+ }
+
+ /* rule 2 */
+ if ((ch == '_') && ((it == name.begin()) || (std::next(it) == name.end()) ||
+ last_was_underscore)) {
+ append_escaped(ch);
+ continue;
+ }
+
+ /* rule 3 */
+ if (double_underscores && ch == '_') {
+ append_verbatim(ch);
+ append_verbatim(ch);
+ continue;
+ }
+
+ /* rule 4 */
+ if (internal_isalnum(ch) || (ch == '_')) {
+ append_verbatim(ch);
+ } else {
+ append_escaped(ch);
+ }
+ }
+
+ return result;
+}
+
+// static
+std::string CWriter::MangleName(std::string_view name) {
+ return Mangle(name, false);
+}
+
+// static
+std::string CWriter::MangleModuleName(std::string_view name) {
+ return Mangle(name, true);
+}
+
+/*
+ * Allocate a C symbol (must be unused) in the SymbolSet,
+ * and a mapping from the Wasm name (tagged with
+ * the index space of the name) to that C symbol.
+ */
+void CWriter::ClaimName(SymbolSet& set,
+ SymbolMap& map,
+ char type_suffix,
+ std::string_view wasm_name,
+ const std::string& c_name) {
+ const std::string type_tagged_wasm_name =
+ std::string(wasm_name) + type_suffix;
+
+ [[maybe_unused]] bool success;
+ success = set.insert(c_name).second;
+ assert(success);
+
+ success = map.emplace(type_tagged_wasm_name, c_name).second;
+ assert(success);
+}
+
+/*
+ * Make a proposed C symbol unique in a given symbol set by appending
+ * an integer to the symbol if necessary.
+ */
+std::string CWriter::FindUniqueName(SymbolSet& set,
+ std::string_view proposed_name) {
+ std::string unique{proposed_name};
+ if (set.find(unique) != set.end()) {
+ std::string base = unique + "_";
+ size_t count = 0;
+ do {
+ unique = base + std::to_string(count++);
+ } while (set.find(unique) != set.end());
+ }
+ return unique;
+}
+
+/*
+ * Find a unique C symbol in the symbol set and claim it (mapping the
+ * type-tagged Wasm name to it).
+ */
+std::string CWriter::ClaimUniqueName(SymbolSet& set,
+ SymbolMap& map,
+ char type_suffix,
+ std::string_view wasm_name,
+ const std::string& proposed_c_name) {
+ const std::string unique = FindUniqueName(set, proposed_c_name);
+ ClaimName(set, map, type_suffix, wasm_name, unique);
+ return unique;
+}
+
+std::string_view StripLeadingDollar(std::string_view name) {
+ assert(!name.empty());
+ assert(name.front() == '$');
+ name.remove_prefix(1);
+ return name;
+}
+
+void CWriter::DefineImportName(const Import* import,
+ std::string_view module,
+ std::string_view field_name) {
+ std::string name;
+ ModuleFieldType type{};
+
+ switch (import->kind()) {
+ case ExternalKind::Func:
+ type = ModuleFieldType::Func;
+ name = cast<FuncImport>(import)->func.name;
+ break;
+ case ExternalKind::Tag:
+ type = ModuleFieldType::Tag;
+ name = cast<TagImport>(import)->tag.name;
+ break;
+ case ExternalKind::Global:
+ type = ModuleFieldType::Global;
+ name = cast<GlobalImport>(import)->global.name;
+ break;
+ case ExternalKind::Memory:
+ type = ModuleFieldType::Memory;
+ name = cast<MemoryImport>(import)->memory.name;
+ break;
+ case ExternalKind::Table:
+ type = ModuleFieldType::Table;
+ name = cast<TableImport>(import)->table.name;
+ break;
+ }
+
+ import_syms_.insert(name);
+ import_module_sym_map_.emplace(name, import->module_name);
+
+ const std::string mangled = ExportName(module, field_name);
+ global_syms_.erase(mangled); // duplicate imports are allowed
+ ClaimName(global_syms_, global_sym_map_, MangleField(type), name, mangled);
+}
+
+/*
+ * Reserve a C symbol for the public name of a module's export. The
+ * format of these is "w2c_" + the module prefix + "_" + the mangled
+ * export name. Reserving the symbol prevents internal functions and
+ * other names from shadowing/overlapping the exports.
+ */
+void CWriter::ReserveExportName(std::string_view name) {
+ ClaimName(global_syms_, global_sym_map_, MangleField(ModuleFieldType::Export),
+ name, ExportName(name));
+}
+
+/*
+ * Names for functions, function types, tags, and segments are globally unique
+ * across modules (formatted the same as an export, as "w2c_" + module prefix +
+ * "_" + the name, made unique if necessary).
+ */
+std::string CWriter::DefineGlobalScopeName(ModuleFieldType type,
+ std::string_view name) {
+ return ClaimUniqueName(global_syms_, global_sym_map_, MangleField(type), name,
+ ExportName(StripLeadingDollar(name)));
+}
+
+std::string CWriter::GetGlobalName(ModuleFieldType type,
+ const std::string& name) const {
+ std::string mangled = name + MangleField(type);
+ assert(global_sym_map_.count(mangled) == 1);
+ return global_sym_map_.at(mangled);
+}
+
+/* Names for params, locals, and stack vars are formatted as "var_" + name. */
+std::string CWriter::DefineLocalScopeName(std::string_view name,
+ bool is_label) {
+ return ClaimUniqueName(
+ local_syms_, local_sym_map_, is_label ? kLabelSuffix : kParamSuffix, name,
+ kLocalSymbolPrefix + MangleName(StripLeadingDollar(name)));
+}
+
+std::string CWriter::GetLocalName(const std::string& name,
+ bool is_label) const {
+ std::string mangled = name + (is_label ? kLabelSuffix : kParamSuffix);
+ assert(local_sym_map_.count(mangled) == 1);
+ return local_sym_map_.at(mangled);
+}
+
+std::string CWriter::DefineParamName(std::string_view name) {
+ return DefineLocalScopeName(name, false);
+}
+
+std::string CWriter::DefineLabelName(std::string_view name) {
+ return DefineLocalScopeName(name, true);
+}
+
+std::string CWriter::DefineStackVarName(Index index,
+ Type type,
+ std::string_view name) {
+ std::string unique =
+ FindUniqueName(local_syms_, kLocalSymbolPrefix + MangleName(name));
+ StackTypePair stp = {index, type};
+ [[maybe_unused]] bool success =
+ stack_var_sym_map_.emplace(stp, unique).second;
+ assert(success);
+ return unique;
+}
+
+/*
+ * Members of the module instance (globals, tables, and memories) are formatted
+ * as "w2c_" + the mangled name of the element (made unique if necessary).
+ */
+std::string CWriter::DefineInstanceMemberName(ModuleFieldType type,
+ std::string_view name) {
+ return ClaimUniqueName(
+ global_syms_, global_sym_map_, MangleField(type), name,
+ kGlobalSymbolPrefix + MangleName(StripLeadingDollar(name)));
+}
+
+/*
+ * The name of a module-instance member that points to the originating
+ * instance of an imported function is formatted as "w2c_" + originating
+ * module prefix + "_instance".
+ */
+std::string CWriter::DefineImportedModuleInstanceName(std::string_view name) {
+ return ClaimUniqueName(global_syms_, global_sym_map_,
+ MangleField(ModuleFieldType::Import), name,
+ ExportName(name, "instance"));
+}
+
+void CWriter::Indent(int size) {
+ indent_ += size;
+}
+
+void CWriter::Dedent(int size) {
+ indent_ -= size;
+ assert(indent_ >= 0);
+}
+
+void CWriter::WriteIndent() {
+ static char s_indent[] =
+ " "
+ " ";
+ static size_t s_indent_len = sizeof(s_indent) - 1;
+ size_t to_write = indent_;
+ while (to_write >= s_indent_len) {
+ stream_->WriteData(s_indent, s_indent_len);
+ to_write -= s_indent_len;
+ }
+ if (to_write > 0) {
+ stream_->WriteData(s_indent, to_write);
+ }
+}
+
+void CWriter::WriteData(const char* src, size_t size) {
+ if (should_write_indent_next_) {
+ WriteIndent();
+ should_write_indent_next_ = false;
+ }
+ if (size > 0 && src[0] != '\n') {
+ consecutive_newline_count_ = 0;
+ }
+ stream_->WriteData(src, size);
+}
+
+void WABT_PRINTF_FORMAT(2, 3) CWriter::Writef(const char* format, ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ WriteData(buffer, length);
+}
+
+void CWriter::Write(Newline) {
+ // Allow maximum one blank line between sections
+ if (consecutive_newline_count_ < 2) {
+ Write("\n");
+ consecutive_newline_count_++;
+ }
+ should_write_indent_next_ = true;
+}
+
+void CWriter::Write(OpenBrace) {
+ Write("{");
+ Indent();
+ Write(Newline());
+}
+
+void CWriter::Write(CloseBrace) {
+ Dedent();
+ Write("}");
+}
+
+void CWriter::Write(uint64_t val) {
+ Writef("%" PRIu64, val);
+}
+
+void CWriter::Write(std::string_view s) {
+ WriteData(s.data(), s.size());
+}
+
+void CWriter::Write(const ParamName& name) {
+ Write(GetLocalName(name.name, false));
+}
+
+void CWriter::Write(const LabelName& name) {
+ Write(GetLocalName(name.name, true));
+}
+
+void CWriter::Write(const GlobalName& name) {
+ Write(GetGlobalName(name.type, name.name));
+}
+
+void CWriter::Write(const ExternalPtr& name) {
+ bool is_import = import_syms_.count(name.name) != 0;
+ if (!is_import) {
+ Write("&");
+ }
+ Write(GlobalName(name));
+}
+
+void CWriter::Write(const ExternalInstancePtr& name) {
+ bool is_import = import_syms_.count(name.name) != 0;
+ if (!is_import) {
+ Write("&");
+ }
+ Write("instance->", GlobalName(name));
+}
+
+void CWriter::Write(const ExternalRef& name) {
+ bool is_import = import_syms_.count(name.name) != 0;
+ if (is_import) {
+ Write("(*", GlobalName(name), ")");
+ } else {
+ Write(GlobalName(name));
+ }
+}
+
+void CWriter::Write(const ExternalInstanceRef& name) {
+ bool is_import = import_syms_.count(name.name) != 0;
+ if (is_import) {
+ Write("(*instance->", GlobalName(name), ")");
+ } else {
+ Write("instance->", GlobalName(name));
+ }
+}
+
+void CWriter::Write(const GotoLabel& goto_label) {
+ const Label* label = FindLabel(goto_label.var);
+ if (label->HasValue()) {
+ size_t amount = label->sig.size();
+ assert(type_stack_.size() >= label->type_stack_size);
+ assert(type_stack_.size() >= amount);
+ assert(type_stack_.size() - amount >= label->type_stack_size);
+ Index offset = type_stack_.size() - label->type_stack_size - amount;
+ if (offset != 0) {
+ for (Index i = 0; i < amount; ++i) {
+ Write(StackVar(amount - i - 1 + offset, label->sig[i]), " = ",
+ StackVar(amount - i - 1), "; ");
+ }
+ }
+ }
+
+ assert(try_catch_stack_.size() >= label->try_catch_stack_size);
+
+ if (try_catch_stack_.size() != label->try_catch_stack_size) {
+ const std::string& name =
+ try_catch_stack_.at(label->try_catch_stack_size).name;
+
+ Write("wasm_rt_set_unwind_target(", name, "_outer_target);", Newline());
+ }
+
+ if (goto_label.var.is_name()) {
+ Write("goto ", LabelName(goto_label.var.name()), ";");
+ } else {
+ // We've generated names for all labels, so we should only be using an
+ // index when branching to the implicit function label, which can't be
+ // named.
+ Write("goto ", LabelName(kImplicitFuncLabel), ";");
+ }
+}
+
+void CWriter::Write(const LabelDecl& label) {
+ if (IsTopLabelUsed())
+ Write(label.name, ":;", Newline());
+}
+
+void CWriter::Write(const GlobalInstanceVar& var) {
+ assert(var.var.is_name());
+ Write(ExternalInstanceRef(ModuleFieldType::Global, var.var.name()));
+}
+
+void CWriter::Write(const StackVar& sv) {
+ Index index = type_stack_.size() - 1 - sv.index;
+ Type type = sv.type;
+ if (type == Type::Any) {
+ assert(index < type_stack_.size());
+ type = type_stack_[index];
+ }
+
+ StackTypePair stp = {index, type};
+ auto iter = stack_var_sym_map_.find(stp);
+ if (iter == stack_var_sym_map_.end()) {
+ std::string name = MangleType(type) + std::to_string(index);
+ Write(DefineStackVarName(index, type, name));
+ } else {
+ Write(iter->second);
+ }
+}
+
+// static
+const char* CWriter::GetCTypeName(const Type& type) {
+ // clang-format off
+ switch (type) {
+ case Type::I32: return "u32";
+ case Type::I64: return "u64";
+ case Type::F32: return "f32";
+ case Type::F64: return "f64";
+ case Type::V128: return "v128";
+ case Type::FuncRef: return "wasm_rt_funcref_t";
+ case Type::ExternRef: return "wasm_rt_externref_t";
+ default:
+ WABT_UNREACHABLE;
+ }
+ // clang-format on
+}
+
+void CWriter::Write(Type type) {
+ Write(GetCTypeName(type));
+}
+
+void CWriter::Write(TypeEnum type) {
+ // clang-format off
+ switch (type.type) {
+ case Type::I32: Write("WASM_RT_I32"); break;
+ case Type::I64: Write("WASM_RT_I64"); break;
+ case Type::F32: Write("WASM_RT_F32"); break;
+ case Type::F64: Write("WASM_RT_F64"); break;
+ case Type::V128: Write("WASM_RT_V128"); break;
+ case Type::FuncRef: Write("WASM_RT_FUNCREF"); break;
+ case Type::ExternRef: Write("WASM_RT_EXTERNREF"); break;
+ default:
+ WABT_UNREACHABLE;
+ }
+ // clang-format on
+}
+
+void CWriter::Write(SignedType type) {
+ // clang-format off
+ switch (type.type) {
+ case Type::I32: Write("s32"); break;
+ case Type::I64: Write("s64"); break;
+ default:
+ WABT_UNREACHABLE;
+ }
+ // clang-format on
+}
+
+void CWriter::Write(const ResultType& rt) {
+ if (rt.types.empty()) {
+ Write("void");
+ } else if (rt.types.size() == 1) {
+ Write(rt.types[0]);
+ } else {
+ Write("struct ", MangleMultivalueTypes(rt.types));
+ }
+}
+
+void CWriter::Write(const Const& const_) {
+ switch (const_.type()) {
+ case Type::I32:
+ Writef("%uu", static_cast<int32_t>(const_.u32()));
+ break;
+
+ case Type::I64:
+ Writef("%" PRIu64 "ull", static_cast<int64_t>(const_.u64()));
+ break;
+
+ case Type::F32: {
+ uint32_t f32_bits = const_.f32_bits();
+ // TODO(binji): Share with similar float info in interp.cc and literal.cc
+ if ((f32_bits & 0x7f800000u) == 0x7f800000u) {
+ const char* sign = (f32_bits & 0x80000000) ? "-" : "";
+ uint32_t significand = f32_bits & 0x7fffffu;
+ if (significand == 0) {
+ // Infinity.
+ Writef("%sINFINITY", sign);
+ } else {
+ // Nan.
+ Writef("f32_reinterpret_i32(0x%08x) /* %snan:0x%06x */", f32_bits,
+ sign, significand);
+ }
+ } else if (f32_bits == 0x80000000) {
+ // Negative zero. Special-cased so it isn't written as -0 below.
+ Writef("-0.f");
+ } else {
+ Writef("%.9g", Bitcast<float>(f32_bits));
+ }
+ break;
+ }
+
+ case Type::F64: {
+ uint64_t f64_bits = const_.f64_bits();
+ // TODO(binji): Share with similar float info in interp.cc and literal.cc
+ if ((f64_bits & 0x7ff0000000000000ull) == 0x7ff0000000000000ull) {
+ const char* sign = (f64_bits & 0x8000000000000000ull) ? "-" : "";
+ uint64_t significand = f64_bits & 0xfffffffffffffull;
+ if (significand == 0) {
+ // Infinity.
+ Writef("%sINFINITY", sign);
+ } else {
+ // Nan.
+ Writef("f64_reinterpret_i64(0x%016" PRIx64 ") /* %snan:0x%013" PRIx64
+ " */",
+ f64_bits, sign, significand);
+ }
+ } else if (f64_bits == 0x8000000000000000ull) {
+ // Negative zero. Special-cased so it isn't written as -0 below.
+ Writef("-0.0");
+ } else {
+ Writef("%.17g", Bitcast<double>(f64_bits));
+ }
+ break;
+ }
+ case Type::V128: {
+ Writef("simde_wasm_i32x4_const(0x%08x, 0x%08x, 0x%08x, 0x%08x)",
+ const_.vec128().u32(0), const_.vec128().u32(1),
+ const_.vec128().u32(2), const_.vec128().u32(3));
+ break;
+ }
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::WriteInitDecl() {
+ Write("void ", kAdminSymbolPrefix, module_prefix_, "_instantiate(",
+ ModuleInstanceTypeName(), "*");
+ for (const auto& import_module_name : import_module_set_) {
+ Write(", struct ", ModuleInstanceTypeName(import_module_name), "*");
+ }
+ Write(");", Newline());
+}
+
+void CWriter::WriteFreeDecl() {
+ Write("void ", kAdminSymbolPrefix, module_prefix_, "_free(",
+ ModuleInstanceTypeName(), "*);", Newline());
+}
+
+void CWriter::WriteGetFuncTypeDecl() {
+ Write("wasm_rt_func_type_t ", kAdminSymbolPrefix, module_prefix_,
+ "_get_func_type(uint32_t param_count, uint32_t result_count, ...);",
+ Newline());
+}
+
+void CWriter::WriteInitExpr(const ExprList& expr_list) {
+ if (expr_list.empty()) {
+ WABT_UNREACHABLE;
+ }
+
+ std::vector<std::string> mini_stack;
+
+ for (const auto& expr : expr_list) {
+ if (expr.type() == ExprType::Binary) {
+ // Extended const expressions include at least one binary op.
+ // This builds a C expression from the operands.
+ if (mini_stack.size() < 2) {
+ WABT_UNREACHABLE;
+ }
+
+ const auto binexpr = cast<BinaryExpr>(&expr);
+ char op;
+ switch (binexpr->opcode) {
+ case Opcode::I32Add:
+ case Opcode::I64Add:
+ case Opcode::F32Add:
+ case Opcode::F64Add:
+ op = '+';
+ break;
+
+ case Opcode::I32Sub:
+ case Opcode::I64Sub:
+ case Opcode::F32Sub:
+ case Opcode::F64Sub:
+ op = '-';
+ break;
+
+ case Opcode::I32Mul:
+ case Opcode::I64Mul:
+ case Opcode::F32Mul:
+ case Opcode::F64Mul:
+ op = '*';
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ std::string combination =
+ "((" + std::string(GetCTypeName(binexpr->opcode.GetParamType1())) +
+ ")" + mini_stack.at(mini_stack.size() - 2) + ")" + op + "((" +
+ std::string(GetCTypeName(binexpr->opcode.GetParamType2())) + ")" +
+ mini_stack.at(mini_stack.size() - 1) + ")";
+ mini_stack.resize(mini_stack.size() - 2);
+ mini_stack.push_back(std::move(combination));
+ } else {
+ // Leaf node (nullary const expression)
+ Stream* existing_stream = stream_;
+ MemoryStream terminal_stream;
+ stream_ = &terminal_stream;
+ WriteInitExprTerminal(&expr);
+ const auto& buf = terminal_stream.output_buffer();
+ mini_stack.emplace_back(reinterpret_cast<const char*>(buf.data.data()),
+ buf.data.size());
+ stream_ = existing_stream;
+ }
+ }
+
+ if (mini_stack.size() != 1) {
+ WABT_UNREACHABLE;
+ }
+
+ Write(mini_stack.front());
+}
+
+void CWriter::WriteInitExprTerminal(const Expr* expr) {
+ switch (expr->type()) {
+ case ExprType::Const:
+ Write(cast<ConstExpr>(expr)->const_);
+ break;
+
+ case ExprType::GlobalGet:
+ Write(GlobalInstanceVar(cast<GlobalGetExpr>(expr)->var));
+ break;
+
+ case ExprType::RefFunc: {
+ const Func* func = module_->GetFunc(cast<RefFuncExpr>(expr)->var);
+ const FuncDeclaration& decl = func->decl;
+
+ assert(decl.has_func_type);
+ const FuncType* func_type = module_->GetFuncType(decl.type_var);
+
+ Write("(wasm_rt_funcref_t){", FuncTypeExpr(func_type), ", ",
+ "(wasm_rt_function_ptr_t)",
+ ExternalPtr(ModuleFieldType::Func, func->name), ", ");
+
+ bool is_import = import_module_sym_map_.count(func->name) != 0;
+ if (is_import) {
+ Write("instance->", GlobalName(ModuleFieldType::Import,
+ import_module_sym_map_[func->name]));
+ } else {
+ Write("instance");
+ }
+
+ Write("};", Newline());
+ } break;
+
+ case ExprType::RefNull:
+ Write(GetReferenceNullValue(cast<RefNullExpr>(expr)->type));
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+std::string CWriter::GenerateHeaderGuard() const {
+ std::string result;
+ for (char c : header_name_) {
+ if (isalnum(c) || c == '_') {
+ result += toupper(c);
+ } else {
+ result += '_';
+ }
+ }
+ result += "_GENERATED_";
+ return result;
+}
+
+void CWriter::WriteSourceTop() {
+ Write(s_source_includes);
+ Write(Newline(), "#include \"", header_name_, "\"", Newline());
+ Write(s_source_declarations, Newline());
+
+ if (module_->features_used.simd) {
+ if (!simd_used_in_header_) {
+ WriteV128Decl();
+ }
+ Write(s_simd_source_declarations);
+ }
+}
+
+void CWriter::WriteMultiCTop() {
+ if (c_streams_.size() > 1) {
+ assert(header_impl_name_.size() > 0);
+ Write("/* Automatically generated by wasm2c */", Newline());
+ Write("#include \"", header_impl_name_, "\"", Newline());
+ }
+}
+
+void CWriter::WriteMultiCTopEmpty() {
+ for (auto& stream : c_streams_) {
+ if (stream->offset() == 0) {
+ stream_ = stream;
+ Write("/* Empty wasm2c generated file */\n");
+ Write("typedef int dummy_def;");
+ }
+ }
+}
+
+void CWriter::WriteMultivalueTypes() {
+ for (TypeEntry* type : module_->types) {
+ FuncType* func_type = cast<FuncType>(type);
+ Index num_results = func_type->GetNumResults();
+ if (num_results <= 1) {
+ continue;
+ }
+ std::string name = MangleMultivalueTypes(func_type->sig.result_types);
+ // these ifndefs are actually to support importing multiple modules
+ // incidentally they also mean we don't have to bother with deduplication
+ Write("#ifndef ", name, Newline());
+ Write("#define ", name, " ", name, Newline());
+ Write("struct ", name, " ", OpenBrace());
+ for (Index i = 0; i < num_results; ++i) {
+ Type type = func_type->GetResultType(i);
+ Write(type);
+ Writef(" %c%d;", MangleType(type), i);
+ Write(Newline());
+ }
+ Write(CloseBrace(), ";", Newline(), "#endif /* ", name, " */", Newline());
+ }
+}
+
+void CWriter::WriteTagTypes() {
+ for (const Tag* tag : module_->tags) {
+ const FuncDeclaration& tag_type = tag->decl;
+ Index num_params = tag_type.GetNumParams();
+ if (num_params <= 1) {
+ continue;
+ }
+ const std::string name = MangleTagTypes(tag_type.sig.param_types);
+ // use same method as WriteMultivalueTypes
+ Write("#ifndef ", name, Newline());
+ Write("#define ", name, " ", name, Newline());
+ Write("struct ", name, " ", OpenBrace());
+ for (Index i = 0; i < num_params; ++i) {
+ Type type = tag_type.GetParamType(i);
+ Write(type);
+ Writef(" %c%d;", MangleType(type), i);
+ Write(Newline());
+ }
+ Write(CloseBrace(), ";", Newline(), "#endif /* ", name, " */", Newline());
+ }
+}
+
+void CWriter::WriteFuncTypeDecls() {
+ if (module_->types.empty()) {
+ return;
+ }
+
+ Write(Newline());
+
+ std::string serialized_type;
+ for (const TypeEntry* type : module_->types) {
+ const std::string name =
+ DefineGlobalScopeName(ModuleFieldType::Type, type->name);
+
+ if (c_streams_.size() > 1) {
+ Write("FUNC_TYPE_DECL_EXTERN_T(", name, ");", Newline());
+ }
+ }
+}
+
+void CWriter::WriteFuncTypes() {
+ if (module_->types.empty()) {
+ return;
+ }
+
+ Write(Newline());
+
+ std::unordered_map<std::string, std::string> type_hash;
+
+ std::string serialized_type;
+ for (const TypeEntry* type : module_->types) {
+ const std::string name = GetGlobalName(ModuleFieldType::Type, type->name);
+ SerializeFuncType(*cast<FuncType>(type), serialized_type);
+
+ auto prior_type = type_hash.find(serialized_type);
+ if (prior_type != type_hash.end()) {
+ /* duplicate function type */
+ unique_func_type_names_.push_back(prior_type->second);
+ } else {
+ unique_func_type_names_.push_back(name);
+ type_hash.emplace(serialized_type, name);
+ if (c_streams_.size() > 1) {
+ Write("FUNC_TYPE_EXTERN_T(");
+ } else {
+ Write("FUNC_TYPE_T(");
+ }
+ Write(name, ") = \"");
+ for (uint8_t x : serialized_type) {
+ Writef("\\x%02x", x);
+ }
+ Write("\";", Newline());
+ }
+ }
+}
+
+void CWriter::Write(const FuncTypeExpr& expr) {
+ Index func_type_index = module_->GetFuncTypeIndex(expr.func_type->sig);
+ Write(unique_func_type_names_.at(func_type_index));
+}
+
+// static
+void CWriter::SerializeFuncType(const FuncType& func_type,
+ std::string& serialized_type) {
+ unsigned int len = func_type.GetNumParams() + func_type.GetNumResults() + 1;
+
+ char* const mangled_signature = static_cast<char*>(alloca(len));
+ char* next_byte = mangled_signature;
+
+ // step 1: serialize each param type
+ for (Index i = 0; i < func_type.GetNumParams(); ++i) {
+ *next_byte++ = MangleType(func_type.GetParamType(i));
+ }
+
+ // step 2: separate params and results with a space
+ *next_byte++ = ' ';
+
+ // step 3: serialize each result type
+ for (Index i = 0; i < func_type.GetNumResults(); ++i) {
+ *next_byte++ = MangleType(func_type.GetResultType(i));
+ }
+
+ assert(next_byte - mangled_signature == len);
+
+ // step 4: SHA-256 the whole string
+ sha256({mangled_signature, len}, serialized_type);
+}
+
+void CWriter::WriteTagDecls() {
+ Index tag_index = 0;
+ for (const Tag* tag : module_->tags) {
+ bool is_import = tag_index < module_->num_tag_imports;
+ if (!is_import) {
+ // Tags are identified and compared solely by their (unique) address.
+ // The data stored in this variable is never read.
+ if (tag_index == module_->num_tag_imports) {
+ Write(Newline());
+ Write("typedef char wasm_tag_placeholder_t;", Newline());
+ }
+ DefineGlobalScopeName(ModuleFieldType::Tag, tag->name);
+ if (c_streams_.size() > 1) {
+ Write("extern const wasm_tag_placeholder_t ",
+ GlobalName(ModuleFieldType::Tag, tag->name), ";", Newline());
+ }
+ }
+ tag_index++;
+ }
+}
+
+void CWriter::WriteTags() {
+ Write(Newline());
+ Index tag_index = 0;
+ for (const Tag* tag : module_->tags) {
+ bool is_import = tag_index < module_->num_tag_imports;
+ if (!is_import) {
+ Write(InternalSymbolScope(), "const wasm_tag_placeholder_t ",
+ GlobalName(ModuleFieldType::Tag, tag->name), ";", Newline());
+ }
+ tag_index++;
+ }
+}
+
+void CWriter::ComputeUniqueImports() {
+ using modname_name_pair = std::pair<std::string, std::string>;
+ std::map<modname_name_pair, const Import*> import_map;
+ for (const Import* import : module_->imports) {
+ // After emplacing, the returned bool says whether the insert happened;
+ // i.e., was there already an import with the same modname and name?
+ // If there was, make sure it was at least the same kind of import.
+ const auto iterator_and_insertion_bool = import_map.emplace(
+ modname_name_pair(import->module_name, import->field_name), import);
+ if (!iterator_and_insertion_bool.second) {
+ if (iterator_and_insertion_bool.first->second->kind() != import->kind()) {
+ UNIMPLEMENTED("contradictory import declaration");
+ } else {
+ fprintf(stderr, "warning: duplicate import declaration \"%s\" \"%s\"\n",
+ import->module_name.c_str(), import->field_name.c_str());
+ }
+ }
+ import_module_set_.insert(import->module_name);
+ if (import->kind() == ExternalKind::Func) {
+ import_func_module_set_.insert(import->module_name);
+ }
+ }
+
+ for (const auto& node : import_map) {
+ unique_imports_.push_back(node.second);
+ }
+}
+
+void CWriter::BeginInstance() {
+ if (module_->imports.empty()) {
+ Write("typedef struct ", ModuleInstanceTypeName(), " ", OpenBrace());
+ return;
+ }
+
+ ComputeUniqueImports();
+
+ // define names of per-instance imports
+ for (const Import* import : module_->imports) {
+ DefineImportName(import, import->module_name, import->field_name);
+ }
+
+ // Forward declaring module instance types
+ for (const auto& import_module : import_module_set_) {
+ DefineImportedModuleInstanceName(import_module);
+ Write("struct ", ModuleInstanceTypeName(import_module), ";", Newline());
+ }
+
+ // Forward declaring module imports
+ for (const Import* import : unique_imports_) {
+ if ((import->kind() == ExternalKind::Func) ||
+ (import->kind() == ExternalKind::Tag)) {
+ continue;
+ }
+
+ Write("extern ");
+ switch (import->kind()) {
+ case ExternalKind::Global: {
+ const Global& global = cast<GlobalImport>(import)->global;
+ Write(global.type);
+ break;
+ }
+
+ case ExternalKind::Memory: {
+ Write("wasm_rt_memory_t");
+ break;
+ }
+
+ case ExternalKind::Table:
+ WriteTableType(cast<TableImport>(import)->table.elem_type);
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+ Write("* ", ExportName(import->module_name, import->field_name), "(struct ",
+ ModuleInstanceTypeName(import->module_name), "*);", Newline());
+ }
+ Write(Newline());
+
+ // Add pointers to module instances that any func is imported from,
+ // so that imported functions can be given their own module instances
+ // when invoked
+ Write("typedef struct ", ModuleInstanceTypeName(), " ", OpenBrace());
+ for (const auto& import_module : import_func_module_set_) {
+ Write("struct ", ModuleInstanceTypeName(import_module), "* ",
+ GlobalName(ModuleFieldType::Import, import_module), ";", Newline());
+ }
+
+ for (const Import* import : unique_imports_) {
+ if ((import->kind() == ExternalKind::Func) ||
+ (import->kind() == ExternalKind::Tag)) {
+ continue;
+ }
+
+ Write("/* import: '", SanitizeForComment(import->module_name), "' '",
+ SanitizeForComment(import->field_name), "' */", Newline());
+
+ switch (import->kind()) {
+ case ExternalKind::Global:
+ WriteGlobal(cast<GlobalImport>(import)->global,
+ std::string("*") +
+ ExportName(import->module_name, import->field_name));
+ break;
+
+ case ExternalKind::Memory:
+ WriteMemory(std::string("*") +
+ ExportName(import->module_name, import->field_name));
+ break;
+
+ case ExternalKind::Table: {
+ const Table& table = cast<TableImport>(import)->table;
+ WriteTable(std::string("*") +
+ ExportName(import->module_name, import->field_name),
+ table.elem_type);
+ } break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ Write(Newline());
+ }
+}
+
+// Write module-wide imports (funcs & tags), which aren't tied to an instance.
+void CWriter::WriteImports() {
+ if (unique_imports_.empty())
+ return;
+
+ Write(Newline());
+
+ for (const Import* import : unique_imports_) {
+ if (import->kind() == ExternalKind::Func) {
+ Write(Newline(), "/* import: '", SanitizeForComment(import->module_name),
+ "' '", SanitizeForComment(import->field_name), "' */", Newline());
+ const Func& func = cast<FuncImport>(import)->func;
+ WriteImportFuncDeclaration(
+ func.decl, import->module_name,
+ ExportName(import->module_name, import->field_name));
+ Write(";");
+ Write(Newline());
+ } else if (import->kind() == ExternalKind::Tag) {
+ Write(Newline(), "/* import: '", SanitizeForComment(import->module_name),
+ "' '", SanitizeForComment(import->field_name), "' */", Newline());
+ Write("extern const wasm_rt_tag_t ",
+ ExportName(import->module_name, import->field_name), ";",
+ Newline());
+ }
+ }
+}
+
+void CWriter::WriteFuncDeclarations() {
+ if (module_->funcs.size() == module_->num_func_imports)
+ return;
+
+ Write(Newline());
+
+ Index func_index = 0;
+ for (const Func* func : module_->funcs) {
+ bool is_import = func_index < module_->num_func_imports;
+ if (!is_import) {
+ Write(InternalSymbolScope());
+ WriteFuncDeclaration(
+ func->decl, DefineGlobalScopeName(ModuleFieldType::Func, func->name));
+ Write(";", Newline());
+ }
+ ++func_index;
+ }
+}
+
+void CWriter::WriteFuncDeclaration(const FuncDeclaration& decl,
+ const std::string& name) {
+ Write(ResultType(decl.sig.result_types), " ", name, "(");
+ Write(ModuleInstanceTypeName(), "*");
+ WriteParamTypes(decl);
+ Write(")");
+}
+
+void CWriter::WriteImportFuncDeclaration(const FuncDeclaration& decl,
+ const std::string& module_name,
+ const std::string& name) {
+ Write(ResultType(decl.sig.result_types), " ", name, "(");
+ Write("struct ", ModuleInstanceTypeName(module_name), "*");
+ WriteParamTypes(decl);
+ Write(")");
+}
+
+void CWriter::WriteCallIndirectFuncDeclaration(const FuncDeclaration& decl,
+ const std::string& name) {
+ Write(ResultType(decl.sig.result_types), " ", name, "(void*");
+ WriteParamTypes(decl);
+ Write(")");
+}
+
+static bool func_uses_simd(const FuncSignature& sig) {
+ return std::any_of(sig.param_types.begin(), sig.param_types.end(),
+ [](auto x) { return x == Type::V128; }) ||
+ std::any_of(sig.result_types.begin(), sig.result_types.end(),
+ [](auto x) { return x == Type::V128; });
+}
+
+void CWriter::ComputeSimdScope() {
+ simd_used_in_header_ =
+ module_->features_used.simd &&
+ (std::any_of(module_->globals.begin(), module_->globals.end(),
+ [](const auto& x) { return x->type == Type::V128; }) ||
+ std::any_of(module_->imports.begin(), module_->imports.end(),
+ [](const auto& x) {
+ return x->kind() == ExternalKind::Func &&
+ func_uses_simd(cast<FuncImport>(x)->func.decl.sig);
+ }) ||
+ std::any_of(module_->exports.begin(), module_->exports.end(),
+ [&](const auto& x) {
+ return x->kind == ExternalKind::Func &&
+ func_uses_simd(module_->GetFunc(x->var)->decl.sig);
+ }));
+}
+
+void CWriter::WriteHeaderIncludes() {
+ Write("#include \"wasm-rt.h\"", Newline());
+
+ if (module_->features_used.exceptions) {
+ Write("#include \"wasm-rt-exceptions.h\"", Newline(), Newline());
+ }
+
+ if (simd_used_in_header_) {
+ WriteV128Decl();
+ }
+
+ Write(Newline());
+}
+
+void CWriter::WriteV128Decl() {
+ Write("#include <simde/wasm/simd128.h>", Newline(), Newline());
+ Write("#ifndef WASM_RT_SIMD_TYPE_DEFINED", Newline(),
+ "#define WASM_RT_SIMD_TYPE_DEFINED", Newline(),
+ "typedef simde_v128_t v128;", Newline(), "#endif", Newline(),
+ Newline());
+}
+
+void CWriter::WriteModuleInstance() {
+ BeginInstance();
+ WriteGlobals();
+ WriteMemories();
+ WriteTables();
+ WriteDataInstances();
+ WriteElemInstances();
+
+ // C forbids an empty struct
+ if (module_->globals.empty() && module_->memories.empty() &&
+ module_->tables.empty() && import_func_module_set_.empty()) {
+ Write("char dummy_member;", Newline());
+ }
+
+ Write(CloseBrace(), " ", ModuleInstanceTypeName(), ";", Newline());
+ Write(Newline());
+}
+
+void CWriter::WriteGlobals() {
+ Index global_index = 0;
+ if (module_->globals.size() != module_->num_global_imports) {
+ for (const Global* global : module_->globals) {
+ bool is_import = global_index < module_->num_global_imports;
+ if (!is_import) {
+ WriteGlobal(*global, DefineInstanceMemberName(ModuleFieldType::Global,
+ global->name));
+ Write(Newline());
+ }
+ ++global_index;
+ }
+ }
+}
+
+void CWriter::WriteGlobal(const Global& global, const std::string& name) {
+ Write(global.type, " ", name, ";");
+}
+
+void CWriter::WriteGlobalPtr(const Global& global, const std::string& name) {
+ Write(global.type, "* ", name, "(", ModuleInstanceTypeName(), "* instance)");
+}
+
+void CWriter::WriteMemories() {
+ if (module_->memories.size() == module_->num_memory_imports)
+ return;
+
+ Index memory_index = 0;
+ for (const Memory* memory : module_->memories) {
+ bool is_import = memory_index < module_->num_memory_imports;
+ if (!is_import) {
+ WriteMemory(
+ DefineInstanceMemberName(ModuleFieldType::Memory, memory->name));
+ Write(Newline());
+ }
+ ++memory_index;
+ }
+}
+
+void CWriter::WriteMemory(const std::string& name) {
+ Write("wasm_rt_memory_t ", name, ";");
+}
+
+void CWriter::WriteMemoryPtr(const std::string& name) {
+ Write("wasm_rt_memory_t* ", name, "(", ModuleInstanceTypeName(),
+ "* instance)");
+}
+
+void CWriter::WriteTables() {
+ if (module_->tables.size() == module_->num_table_imports) {
+ return;
+ }
+
+ Index table_index = 0;
+ for (const Table* table : module_->tables) {
+ bool is_import = table_index < module_->num_table_imports;
+ if (!is_import) {
+ WriteTable(DefineInstanceMemberName(ModuleFieldType::Table, table->name),
+ table->elem_type);
+ Write(Newline());
+ }
+ ++table_index;
+ }
+}
+
+void CWriter::WriteTable(const std::string& name, const wabt::Type& type) {
+ WriteTableType(type);
+ Write(" ", name, ";");
+}
+
+void CWriter::WriteTablePtr(const std::string& name, const Table& table) {
+ WriteTableType(table.elem_type);
+ Write("* ", name, "(", ModuleInstanceTypeName(), "* instance)");
+}
+
+void CWriter::WriteTableType(const wabt::Type& type) {
+ Write("wasm_rt_", GetReferenceTypeName(type), "_table_t");
+}
+
+void CWriter::WriteGlobalInitializers() {
+ if (module_->globals.empty())
+ return;
+
+ Write(Newline(), "static void init_globals(", ModuleInstanceTypeName(),
+ "* instance) ", OpenBrace());
+ Index global_index = 0;
+ for (const Global* global : module_->globals) {
+ bool is_import = global_index < module_->num_global_imports;
+ if (!is_import) {
+ assert(!global->init_expr.empty());
+ Write(ExternalInstanceRef(ModuleFieldType::Global, global->name), " = ");
+ WriteInitExpr(global->init_expr);
+ Write(";", Newline());
+ }
+ ++global_index;
+ }
+ Write(CloseBrace(), Newline());
+}
+
+static inline bool is_droppable(const DataSegment* data_segment) {
+ return (data_segment->kind == SegmentKind::Passive) &&
+ (!data_segment->data.empty());
+}
+
+static inline bool is_droppable(const ElemSegment* elem_segment) {
+ return (elem_segment->kind == SegmentKind::Passive) &&
+ (!elem_segment->elem_exprs.empty());
+}
+
+void CWriter::WriteDataInstances() {
+ for (const DataSegment* data_segment : module_->data_segments) {
+ std::string name =
+ DefineGlobalScopeName(ModuleFieldType::DataSegment, data_segment->name);
+ if (is_droppable(data_segment)) {
+ Write("bool ", "data_segment_dropped_", name, " : 1;", Newline());
+ }
+ }
+}
+
+void CWriter::WriteDataInitializerDecls() {
+ if (module_->memories.empty()) {
+ return;
+ }
+
+ for (const DataSegment* data_segment : module_->data_segments) {
+ if (data_segment->data.empty()) {
+ continue;
+ }
+
+ if (c_streams_.size() > 1) {
+ Write(Newline(), "extern const u8 data_segment_data_",
+ GlobalName(ModuleFieldType::DataSegment, data_segment->name), "[];",
+ Newline());
+ }
+ }
+}
+
+void CWriter::WriteDataInitializers() {
+ if (module_->memories.empty()) {
+ return;
+ }
+
+ for (const DataSegment* data_segment : module_->data_segments) {
+ if (data_segment->data.empty()) {
+ continue;
+ }
+
+ Write(Newline(), InternalSymbolScope(), "const u8 data_segment_data_",
+ GlobalName(ModuleFieldType::DataSegment, data_segment->name),
+ "[] = ", OpenBrace());
+ size_t i = 0;
+ for (uint8_t x : data_segment->data) {
+ Writef("0x%02x, ", x);
+ if ((++i % 12) == 0)
+ Write(Newline());
+ }
+ if (i > 0)
+ Write(Newline());
+ Write(CloseBrace(), ";", Newline());
+ }
+
+ Write(Newline(), "static void init_memories(", ModuleInstanceTypeName(),
+ "* instance) ", OpenBrace());
+ if (module_->memories.size() > module_->num_memory_imports) {
+ Index memory_idx = module_->num_memory_imports;
+ for (Index i = memory_idx; i < module_->memories.size(); i++) {
+ const Memory* memory = module_->memories[i];
+ uint64_t max;
+ if (memory->page_limits.has_max) {
+ max = memory->page_limits.max;
+ } else {
+ max = memory->page_limits.is_64 ? (static_cast<uint64_t>(1) << 48)
+ : 65536;
+ }
+ Write("wasm_rt_allocate_memory(",
+ ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", ",
+ memory->page_limits.initial, ", ", max, ", ",
+ memory->page_limits.is_64, ");", Newline());
+ }
+ }
+
+ for (const DataSegment* data_segment : module_->data_segments) {
+ if (data_segment->kind != SegmentKind::Active) {
+ continue;
+ }
+ const Memory* memory =
+ module_->memories[module_->GetMemoryIndex(data_segment->memory_var)];
+ Write("LOAD_DATA(",
+ ExternalInstanceRef(ModuleFieldType::Memory, memory->name), ", ");
+ WriteInitExpr(data_segment->offset);
+ if (data_segment->data.empty()) {
+ Write(", NULL, 0");
+ } else {
+ Write(", data_segment_data_",
+ GlobalName(ModuleFieldType::DataSegment, data_segment->name), ", ",
+ data_segment->data.size());
+ }
+ Write(");", Newline());
+ }
+
+ Write(CloseBrace(), Newline());
+
+ if (!module_->data_segments.empty()) {
+ Write(Newline(), "static void init_data_instances(",
+ ModuleInstanceTypeName(), " *instance) ", OpenBrace());
+
+ for (const DataSegment* data_segment : module_->data_segments) {
+ if (is_droppable(data_segment)) {
+ Write("instance->data_segment_dropped_",
+ GlobalName(ModuleFieldType::DataSegment, data_segment->name),
+ " = false;", Newline());
+ }
+ }
+
+ Write(CloseBrace(), Newline());
+ }
+}
+
+void CWriter::WriteElemInstances() {
+ for (const ElemSegment* elem_segment : module_->elem_segments) {
+ std::string name =
+ DefineGlobalScopeName(ModuleFieldType::ElemSegment, elem_segment->name);
+ if (is_droppable(elem_segment)) {
+ Write("bool ", "elem_segment_dropped_", name, " : 1;", Newline());
+ }
+ }
+}
+
+void CWriter::WriteElemInitializerDecls() {
+ if (module_->tables.empty()) {
+ return;
+ }
+
+ for (const ElemSegment* elem_segment : module_->elem_segments) {
+ if (elem_segment->elem_exprs.empty()) {
+ continue;
+ }
+
+ if (elem_segment->elem_type == Type::ExternRef) {
+ // no need to store externref elem initializers because only
+ // ref.null is possible
+ continue;
+ }
+
+ if (c_streams_.size() > 1) {
+ Write(Newline(),
+ "extern const wasm_elem_segment_expr_t elem_segment_exprs_",
+ GlobalName(ModuleFieldType::ElemSegment, elem_segment->name), "[];",
+ Newline());
+ }
+ }
+}
+
+void CWriter::WriteElemInitializers() {
+ if (module_->tables.empty()) {
+ return;
+ }
+
+ for (const ElemSegment* elem_segment : module_->elem_segments) {
+ if (elem_segment->elem_exprs.empty()) {
+ continue;
+ }
+
+ if (elem_segment->elem_type == Type::ExternRef) {
+ // no need to store externref elem initializers because only
+ // ref.null is possible
+ continue;
+ }
+
+ Write(Newline(), InternalSymbolScope(),
+ "const wasm_elem_segment_expr_t elem_segment_exprs_",
+ GlobalName(ModuleFieldType::ElemSegment, elem_segment->name),
+ "[] = ", OpenBrace());
+
+ for (const ExprList& elem_expr : elem_segment->elem_exprs) {
+ assert(elem_expr.size() == 1);
+ const Expr& expr = elem_expr.front();
+ switch (expr.type()) {
+ case ExprType::RefFunc: {
+ const Func* func = module_->GetFunc(cast<RefFuncExpr>(&expr)->var);
+ const FuncType* func_type = module_->GetFuncType(func->decl.type_var);
+ Write("{", FuncTypeExpr(func_type), ", (wasm_rt_function_ptr_t)",
+ ExternalPtr(ModuleFieldType::Func, func->name), ", ");
+ const bool is_import = import_module_sym_map_.count(func->name) != 0;
+ if (is_import) {
+ Write("offsetof(", ModuleInstanceTypeName(), ", ",
+ GlobalName(ModuleFieldType::Import,
+ import_module_sym_map_[func->name]),
+ ")");
+ } else {
+ Write("0");
+ }
+ Write("},", Newline());
+ } break;
+ case ExprType::RefNull:
+ Write("{NULL, NULL, 0},", Newline());
+ break;
+ default:
+ WABT_UNREACHABLE;
+ }
+ }
+ Write(CloseBrace(), ";", Newline());
+ }
+
+ Write(Newline(), "static void init_tables(", ModuleInstanceTypeName(),
+ "* instance) ", OpenBrace());
+
+ if (module_->tables.size() > module_->num_table_imports) {
+ Index table_idx = module_->num_table_imports;
+ for (Index i = table_idx; i < module_->tables.size(); i++) {
+ const Table* table = module_->tables[i];
+ uint32_t max =
+ table->elem_limits.has_max ? table->elem_limits.max : UINT32_MAX;
+ Write("wasm_rt_allocate_", GetReferenceTypeName(table->elem_type),
+ "_table(", ExternalInstancePtr(ModuleFieldType::Table, table->name),
+ ", ", table->elem_limits.initial, ", ", max, ");", Newline());
+ }
+ }
+
+ for (const ElemSegment* elem_segment : module_->elem_segments) {
+ if (elem_segment->kind != SegmentKind::Active) {
+ continue;
+ }
+
+ const Table* table = module_->GetTable(elem_segment->table_var);
+
+ WriteElemTableInit(true, elem_segment, table);
+ }
+
+ Write(CloseBrace(), Newline());
+
+ if (!module_->elem_segments.empty()) {
+ Write(Newline(), "static void init_elem_instances(",
+ ModuleInstanceTypeName(), " *instance) ", OpenBrace());
+
+ for (const ElemSegment* elem_segment : module_->elem_segments) {
+ if (is_droppable(elem_segment)) {
+ Write("instance->elem_segment_dropped_",
+ GlobalName(ModuleFieldType::ElemSegment, elem_segment->name),
+ " = false;", Newline());
+ }
+ }
+
+ Write(CloseBrace(), Newline());
+ }
+}
+
+void CWriter::WriteElemTableInit(bool active_initialization,
+ const ElemSegment* src_segment,
+ const Table* dst_table) {
+ assert(dst_table->elem_type == Type::FuncRef ||
+ dst_table->elem_type == Type::ExternRef);
+ assert(dst_table->elem_type == src_segment->elem_type);
+
+ Write(GetReferenceTypeName(dst_table->elem_type), "_table_init(",
+ ExternalInstancePtr(ModuleFieldType::Table, dst_table->name), ", ");
+
+ // elem segment exprs needed only for funcref tables
+ // because externref tables can only be initialized with ref.null
+ if (dst_table->elem_type == Type::FuncRef) {
+ if (src_segment->elem_exprs.empty()) {
+ Write("NULL, ");
+ } else {
+ Write("elem_segment_exprs_",
+ GlobalName(ModuleFieldType::ElemSegment, src_segment->name), ", ");
+ }
+ }
+
+ // src_size, dest_addr, src_addr, N
+ if (active_initialization) {
+ Write(src_segment->elem_exprs.size(), ", ");
+ WriteInitExpr(src_segment->offset);
+ Write(", 0, ", src_segment->elem_exprs.size());
+ } else {
+ if (is_droppable(src_segment)) {
+ Write("(instance->elem_segment_dropped_",
+ GlobalName(ModuleFieldType::ElemSegment, src_segment->name),
+ " ? 0 : ", src_segment->elem_exprs.size(), "), ");
+ } else {
+ Write("0, ");
+ }
+ Write(StackVar(2), ", ", StackVar(1), ", ", StackVar(0));
+ }
+
+ if (dst_table->elem_type == Type::FuncRef) {
+ Write(", instance");
+ }
+
+ Write(");", Newline());
+}
+
+void CWriter::WriteExports(CWriterPhase kind) {
+ if (module_->exports.empty())
+ return;
+
+ for (const Export* export_ : module_->exports) {
+ Write(Newline(), "/* export: '", SanitizeForComment(export_->name), "' */",
+ Newline());
+
+ const std::string mangled_name = ExportName(export_->name);
+ std::string internal_name;
+ std::vector<std::string> index_to_name;
+
+ switch (export_->kind) {
+ case ExternalKind::Func: {
+ const Func* func = module_->GetFunc(export_->var);
+ internal_name = func->name;
+ if (kind == CWriterPhase::Declarations) {
+ WriteFuncDeclaration(func->decl, mangled_name);
+ } else {
+ func_ = func;
+ local_syms_ = global_syms_;
+ local_sym_map_.clear();
+ stack_var_sym_map_.clear();
+ Write(ResultType(func_->decl.sig.result_types), " ", mangled_name,
+ "(");
+ MakeTypeBindingReverseMapping(func_->GetNumParamsAndLocals(),
+ func_->bindings, &index_to_name);
+ WriteParams(index_to_name);
+ }
+ break;
+ }
+
+ case ExternalKind::Global: {
+ const Global* global = module_->GetGlobal(export_->var);
+ internal_name = global->name;
+ WriteGlobalPtr(*global, mangled_name);
+ break;
+ }
+
+ case ExternalKind::Memory: {
+ const Memory* memory = module_->GetMemory(export_->var);
+ internal_name = memory->name;
+ WriteMemoryPtr(mangled_name);
+ break;
+ }
+
+ case ExternalKind::Table: {
+ const Table* table = module_->GetTable(export_->var);
+ internal_name = table->name;
+ WriteTablePtr(mangled_name, *table);
+ break;
+ }
+
+ case ExternalKind::Tag: {
+ const Tag* tag = module_->GetTag(export_->var);
+ internal_name = tag->name;
+ if (kind == CWriterPhase::Declarations) {
+ Write("extern ");
+ }
+ Write("const wasm_rt_tag_t ", mangled_name);
+ break;
+ }
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ if (kind == CWriterPhase::Declarations) {
+ Write(";", Newline());
+ continue;
+ }
+
+ Write(" ");
+ switch (export_->kind) {
+ case ExternalKind::Func: {
+ Write(OpenBrace());
+ Write("return ", ExternalRef(ModuleFieldType::Func, internal_name),
+ "(");
+
+ bool is_import = import_module_sym_map_.count(internal_name) != 0;
+ if (is_import) {
+ Write("instance->",
+ GlobalName(ModuleFieldType::Import,
+ import_module_sym_map_[internal_name]));
+ } else {
+ Write("instance");
+ }
+ WriteParamSymbols(index_to_name);
+ Write(CloseBrace(), Newline());
+
+ local_sym_map_.clear();
+ stack_var_sym_map_.clear();
+ func_ = nullptr;
+ break;
+ }
+
+ case ExternalKind::Global:
+ Write(OpenBrace());
+ Write("return ",
+ ExternalInstancePtr(ModuleFieldType::Global, internal_name), ";",
+ Newline());
+ Write(CloseBrace(), Newline());
+ break;
+
+ case ExternalKind::Memory:
+ Write(OpenBrace());
+ Write("return ",
+ ExternalInstancePtr(ModuleFieldType::Memory, internal_name), ";",
+ Newline());
+ Write(CloseBrace(), Newline());
+ break;
+
+ case ExternalKind::Table:
+ Write(OpenBrace());
+ Write("return ",
+ ExternalInstancePtr(ModuleFieldType::Table, internal_name), ";",
+ Newline());
+ Write(CloseBrace(), Newline());
+ break;
+
+ case ExternalKind::Tag:
+ Write("= ", ExternalPtr(ModuleFieldType::Tag, internal_name), ";",
+ Newline());
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+ }
+}
+
+void CWriter::WriteInit() {
+ Write(Newline(), "void ", kAdminSymbolPrefix, module_prefix_, "_instantiate(",
+ ModuleInstanceTypeName(), "* instance");
+ for (const auto& import_module_name : import_module_set_) {
+ Write(", struct ", ModuleInstanceTypeName(import_module_name), "* ",
+ GlobalName(ModuleFieldType::Import, import_module_name));
+ }
+ Write(") ", OpenBrace());
+
+ Write("assert(wasm_rt_is_initialized());", Newline());
+
+ if (!import_module_set_.empty()) {
+ Write("init_instance_import(instance");
+ for (const auto& import_module_name : import_module_set_) {
+ Write(", ", GlobalName(ModuleFieldType::Import, import_module_name));
+ }
+ Write(");", Newline());
+ }
+
+ if (!module_->globals.empty()) {
+ Write("init_globals(instance);", Newline());
+ }
+ if (!module_->tables.empty()) {
+ Write("init_tables(instance);", Newline());
+ }
+ if (!module_->memories.empty()) {
+ Write("init_memories(instance);", Newline());
+ }
+ if (!module_->tables.empty() && !module_->elem_segments.empty()) {
+ Write("init_elem_instances(instance);", Newline());
+ }
+ if (!module_->memories.empty() && !module_->data_segments.empty()) {
+ Write("init_data_instances(instance);", Newline());
+ }
+
+ for (Var* var : module_->starts) {
+ Write(ExternalRef(ModuleFieldType::Func, module_->GetFunc(*var)->name));
+ bool is_import =
+ import_module_sym_map_.count(module_->GetFunc(*var)->name) != 0;
+ if (is_import) {
+ Write("(instance->",
+ GlobalName(ModuleFieldType::Import,
+ import_module_sym_map_[module_->GetFunc(*var)->name]),
+ ");");
+ } else {
+ Write("(instance);");
+ }
+ Write(Newline());
+ }
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteGetFuncType() {
+ Write(Newline(), "wasm_rt_func_type_t ", kAdminSymbolPrefix, module_prefix_,
+ "_get_func_type(uint32_t param_count, uint32_t result_count, "
+ "...) ",
+ OpenBrace());
+
+ Write("va_list args;", Newline());
+
+ for (const TypeEntry* type : module_->types) {
+ const FuncType* func_type = cast<FuncType>(type);
+ const FuncSignature& signature = func_type->sig;
+
+ Write(Newline(), "if (param_count == ", signature.GetNumParams(),
+ " && result_count == ", signature.GetNumResults(), ") ", OpenBrace());
+ Write("va_start(args, result_count);", Newline());
+ Write("if (true");
+ for (const auto& t : signature.param_types) {
+ Write(" && va_arg(args, wasm_rt_type_t) == ", TypeEnum(t));
+ }
+ for (const auto& t : signature.result_types) {
+ Write(" && va_arg(args, wasm_rt_type_t) == ", TypeEnum(t));
+ }
+ Write(") ", OpenBrace());
+ Write("va_end(args);", Newline());
+ Write("return ", FuncTypeExpr(func_type), ";", Newline());
+ Write(CloseBrace(), Newline());
+ Write("va_end(args);", Newline());
+ Write(CloseBrace(), Newline());
+ }
+
+ Write(Newline(), "return NULL;", Newline());
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteInitInstanceImport() {
+ if (import_module_set_.empty())
+ return;
+
+ Write(Newline(), "static void init_instance_import(",
+ ModuleInstanceTypeName(), "* instance");
+ for (const auto& import_module_name : import_module_set_) {
+ Write(", struct ", ModuleInstanceTypeName(import_module_name), "* ",
+ GlobalName(ModuleFieldType::Import, import_module_name));
+ }
+ Write(")", OpenBrace());
+
+ for (const auto& import_module : import_func_module_set_) {
+ Write("instance->", GlobalName(ModuleFieldType::Import, import_module),
+ " = ", GlobalName(ModuleFieldType::Import, import_module), ";",
+ Newline());
+ }
+
+ for (const Import* import : unique_imports_) {
+ switch (import->kind()) {
+ case ExternalKind::Func:
+ case ExternalKind::Tag:
+ break;
+
+ case ExternalKind::Global:
+ case ExternalKind::Memory:
+ case ExternalKind::Table: {
+ Write("instance->", ExportName(import->module_name, import->field_name),
+ " = ", ExportName(import->module_name, import->field_name), "(",
+ GlobalName(ModuleFieldType::Import, import->module_name), ");",
+ Newline());
+ break;
+ }
+
+ default:
+ WABT_UNREACHABLE;
+ }
+ }
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteImportProperties(CWriterPhase kind) {
+ if (import_module_set_.empty())
+ return;
+
+ Write(Newline());
+
+ auto write_import_prop = [&](const Import* import, std::string prop,
+ std::string type, uint64_t value) {
+ if (kind == CWriterPhase::Declarations) {
+ Write("extern ");
+ }
+ Write("const ", type, " ", kAdminSymbolPrefix, module_prefix_, "_", prop,
+ "_", MangleModuleName(import->module_name), "_",
+ MangleName(import->field_name));
+ if (kind == CWriterPhase::Definitions) {
+ Write(" = ", value);
+ }
+
+ Write(";", Newline());
+ };
+
+ for (const Import* import : unique_imports_) {
+ if (import->kind() == ExternalKind::Memory) {
+ const Limits* limits = &(cast<MemoryImport>(import)->memory.page_limits);
+ // We use u64 so we can handle both 32-bit and 64-bit memories
+ const uint64_t default_max = limits->is_64
+ ? (static_cast<uint64_t>(1) << 48)
+ : (static_cast<uint64_t>(1) << 16);
+ write_import_prop(import, "min", "u64", limits->initial);
+ write_import_prop(import, "max", "u64",
+ limits->has_max ? limits->max : default_max);
+ write_import_prop(import, "is64", "u8", limits->is_64);
+ } else if (import->kind() == ExternalKind::Table) {
+ const Limits* limits = &(cast<TableImport>(import)->table.elem_limits);
+ const uint64_t default_max = std::numeric_limits<uint32_t>::max();
+ write_import_prop(import, "min", "u32", limits->initial);
+ write_import_prop(import, "max", "u32",
+ limits->has_max ? limits->max : default_max);
+ } else {
+ continue;
+ }
+ }
+}
+
+void CWriter::WriteFree() {
+ Write(Newline(), "void ", kAdminSymbolPrefix, module_prefix_, "_free(",
+ ModuleInstanceTypeName(), "* instance) ", OpenBrace());
+
+ {
+ Index table_index = 0;
+ for (const Table* table : module_->tables) {
+ bool is_import = table_index < module_->num_table_imports;
+ if (!is_import) {
+ Write("wasm_rt_free_", GetReferenceTypeName(table->elem_type),
+ "_table(",
+ ExternalInstancePtr(ModuleFieldType::Table, table->name), ");",
+ Newline());
+ }
+ ++table_index;
+ }
+ }
+
+ {
+ Index memory_index = 0;
+ for (const Memory* memory : module_->memories) {
+ bool is_import = memory_index < module_->num_memory_imports;
+ if (!is_import) {
+ Write("wasm_rt_free_memory(",
+ ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ");",
+ Newline());
+ }
+ ++memory_index;
+ }
+ }
+
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteFuncs() {
+ std::vector<size_t> c_stream_assignment =
+ name_to_output_file_index_(module_->funcs.begin(), module_->funcs.end(),
+ module_->num_func_imports, c_streams_.size());
+ Index func_index = 0;
+ for (const Func* func : module_->funcs) {
+ bool is_import = func_index < module_->num_func_imports;
+ if (!is_import) {
+ stream_ = c_streams_.at(c_stream_assignment.at(func_index));
+ Write(*func);
+ }
+ ++func_index;
+ }
+}
+
+void CWriter::PushFuncSection(std::string_view include_condition) {
+ func_sections_.emplace_back(include_condition, MemoryStream{});
+ stream_ = &func_sections_.back().second;
+}
+
+void CWriter::Write(const Func& func) {
+ func_ = &func;
+ local_syms_.clear();
+ local_sym_map_.clear();
+ stack_var_sym_map_.clear();
+ func_sections_.clear();
+ func_includes_.clear();
+
+ Stream* prev_stream = stream_;
+
+ /*
+ * If offset of stream_ is 0, this is the first time some function is written
+ * to this stream, then write multi c top.
+ */
+ if (stream_->offset() == 0) {
+ WriteMultiCTop();
+ }
+ Write(Newline());
+
+ PushFuncSection();
+ Write(ResultType(func.decl.sig.result_types), " ",
+ GlobalName(ModuleFieldType::Func, func.name), "(");
+ WriteParamsAndLocals();
+ Write("FUNC_PROLOGUE;", Newline());
+
+ PushFuncSection();
+
+ std::string label = DefineLabelName(kImplicitFuncLabel);
+ ResetTypeStack(0);
+ std::string empty; // Must not be temporary, since address is taken by Label.
+ PushLabel(LabelType::Func, empty, func.decl.sig);
+ Write(func.exprs, LabelDecl(label));
+ PopLabel();
+ ResetTypeStack(0);
+ PushTypes(func.decl.sig.result_types);
+ Write("FUNC_EPILOGUE;", Newline());
+
+ // Return the top of the stack implicitly.
+ Index num_results = func.GetNumResults();
+ if (num_results == 1) {
+ Write("return ", StackVar(0), ";", Newline());
+ } else if (num_results >= 2) {
+ Write(OpenBrace());
+ Write(ResultType(func.decl.sig.result_types), " tmp;", Newline());
+ for (Index i = 0; i < num_results; ++i) {
+ Type type = func.GetResultType(i);
+ Writef("tmp.%c%d = ", MangleType(type), i);
+ Write(StackVar(num_results - i - 1), ";", Newline());
+ }
+ Write("return tmp;", Newline());
+ Write(CloseBrace(), Newline());
+ }
+
+ stream_ = prev_stream;
+
+ for (size_t i = 0; i < func_sections_.size(); ++i) {
+ auto& [condition, stream] = func_sections_.at(i);
+ std::unique_ptr<OutputBuffer> buf = stream.ReleaseOutputBuffer();
+ if (condition.empty() || func_includes_.count(condition)) {
+ stream_->WriteData(buf->data.data(), buf->data.size());
+ }
+
+ if (i == 0) {
+ WriteStackVarDeclarations(); // these come immediately after section #0
+ // (return type/name/params/locals)
+ }
+ }
+
+ Write(CloseBrace(), Newline());
+
+ func_ = nullptr;
+}
+
+void CWriter::WriteParamsAndLocals() {
+ std::vector<std::string> index_to_name;
+ MakeTypeBindingReverseMapping(func_->GetNumParamsAndLocals(), func_->bindings,
+ &index_to_name);
+ WriteParams(index_to_name);
+ Write(" ", OpenBrace());
+ WriteLocals(index_to_name);
+}
+
+void CWriter::WriteParams(const std::vector<std::string>& index_to_name) {
+ Write(ModuleInstanceTypeName(), "* instance");
+ if (func_->GetNumParams() != 0) {
+ Indent(4);
+ for (Index i = 0; i < func_->GetNumParams(); ++i) {
+ Write(", ");
+ if (i != 0 && (i % 8) == 0) {
+ Write(Newline());
+ }
+ Write(func_->GetParamType(i), " ", DefineParamName(index_to_name[i]));
+ }
+ Dedent(4);
+ }
+ Write(")");
+}
+
+void CWriter::WriteParamSymbols(const std::vector<std::string>& index_to_name) {
+ if (func_->GetNumParams() != 0) {
+ Indent(4);
+ for (Index i = 0; i < func_->GetNumParams(); ++i) {
+ Write(", ");
+ if (i != 0 && (i % 8) == 0) {
+ Write(Newline());
+ }
+ Write(ParamName(index_to_name[i]));
+ }
+ Dedent(4);
+ }
+ Write(");", Newline());
+}
+
+void CWriter::WriteParamTypes(const FuncDeclaration& decl) {
+ if (decl.GetNumParams() != 0) {
+ for (Index i = 0; i < decl.GetNumParams(); ++i) {
+ Write(", ");
+ Write(decl.GetParamType(i));
+ }
+ }
+}
+
+void CWriter::WriteLocals(const std::vector<std::string>& index_to_name) {
+ Index num_params = func_->GetNumParams();
+ for (Type type : {Type::I32, Type::I64, Type::F32, Type::F64, Type::V128,
+ Type::FuncRef, Type::ExternRef}) {
+ Index local_index = 0;
+ size_t count = 0;
+ for (Type local_type : func_->local_types) {
+ if (local_type == type) {
+ if (count == 0) {
+ Write(type, " ");
+ Indent(4);
+ } else {
+ Write(", ");
+ if ((count % 8) == 0)
+ Write(Newline());
+ }
+
+ Write(DefineParamName(index_to_name[num_params + local_index]), " = ");
+ if (local_type == Type::FuncRef || local_type == Type::ExternRef) {
+ Write(GetReferenceNullValue(local_type));
+ } else if (local_type == Type::V128) {
+ Write("simde_wasm_i64x2_make(0, 0)");
+ } else {
+ Write("0");
+ }
+ ++count;
+ }
+ ++local_index;
+ }
+ if (count != 0) {
+ Dedent(4);
+ Write(";", Newline());
+ }
+ }
+}
+
+void CWriter::WriteStackVarDeclarations() {
+ for (Type type : {Type::I32, Type::I64, Type::F32, Type::F64, Type::V128,
+ Type::FuncRef, Type::ExternRef}) {
+ size_t count = 0;
+ for (const auto& [pair, name] : stack_var_sym_map_) {
+ Type stp_type = pair.second;
+
+ if (stp_type == type) {
+ if (count == 0) {
+ Write(type, " ");
+ Indent(4);
+ } else {
+ Write(", ");
+ if ((count % 8) == 0)
+ Write(Newline());
+ }
+
+ Write(name);
+ ++count;
+ }
+ }
+ if (count != 0) {
+ Dedent(4);
+ Write(";", Newline());
+ }
+ }
+}
+
+void CWriter::Write(const Block& block) {
+ std::string label = DefineLabelName(block.label);
+ DropTypes(block.decl.GetNumParams());
+ size_t mark = MarkTypeStack();
+ PushLabel(LabelType::Block, block.label, block.decl.sig);
+ PushTypes(block.decl.sig.param_types);
+ Write(block.exprs, LabelDecl(label));
+ ResetTypeStack(mark);
+ PopLabel();
+ PushTypes(block.decl.sig.result_types);
+}
+
+size_t CWriter::BeginTry(const TryExpr& tryexpr) {
+ Write(OpenBrace()); /* beginning of try-catch */
+ const std::string tlabel = DefineLabelName(tryexpr.block.label);
+ Write("WASM_RT_UNWIND_TARGET *", tlabel,
+ "_outer_target = wasm_rt_get_unwind_target();", Newline());
+ Write("WASM_RT_UNWIND_TARGET ", tlabel, "_unwind_target;", Newline());
+ Write("if (!wasm_rt_try(", tlabel, "_unwind_target)) ");
+ Write(OpenBrace()); /* beginning of try block */
+ DropTypes(tryexpr.block.decl.GetNumParams());
+ const size_t mark = MarkTypeStack();
+ PushLabel(LabelType::Try, tryexpr.block.label, tryexpr.block.decl.sig);
+ PushTypes(tryexpr.block.decl.sig.param_types);
+ Write("wasm_rt_set_unwind_target(&", tlabel, "_unwind_target);", Newline());
+ PushTryCatch(tlabel);
+ Write(tryexpr.block.exprs);
+ ResetTypeStack(mark);
+ Write("wasm_rt_set_unwind_target(", tlabel, "_outer_target);", Newline());
+ Write(CloseBrace()); /* end of try block */
+ Write(" else ", OpenBrace()); /* beginning of catch blocks or delegate */
+ assert(label_stack_.back().name == tryexpr.block.label);
+ assert(label_stack_.back().label_type == LabelType::Try);
+ label_stack_.back().label_type = LabelType::Catch;
+ if (try_catch_stack_.back().used) {
+ Write(tlabel, "_catch:;", Newline());
+ }
+
+ return mark;
+}
+
+void CWriter::WriteTryCatch(const TryExpr& tryexpr) {
+ const size_t mark = BeginTry(tryexpr);
+
+ /* exception has been thrown -- do we catch it? */
+
+ const LabelName tlabel = LabelName(tryexpr.block.label);
+
+ Write("wasm_rt_set_unwind_target(", tlabel, "_outer_target);", Newline());
+ PopTryCatch();
+
+ /* save the thrown exception to the stack if it might be rethrown later */
+ PushFuncSection(tryexpr.block.label);
+ Write("/* save exception ", tlabel, " for rethrow */", Newline());
+ Write("const wasm_rt_tag_t ", tlabel, "_tag = wasm_rt_exception_tag();",
+ Newline());
+ Write("uint32_t ", tlabel, "_size = wasm_rt_exception_size();", Newline());
+ Write("void *", tlabel, " = alloca(", tlabel, "_size);", Newline());
+ Write("wasm_rt_memcpy(", tlabel, ", wasm_rt_exception(), ", tlabel, "_size);",
+ Newline());
+ PushFuncSection();
+
+ assert(!tryexpr.catches.empty());
+ bool has_catch_all{};
+ for (auto it = tryexpr.catches.cbegin(); it != tryexpr.catches.cend(); ++it) {
+ if (it == tryexpr.catches.cbegin()) {
+ Write(Newline());
+ } else {
+ Write(" else ");
+ }
+ ResetTypeStack(mark);
+ Write(*it);
+ if (it->IsCatchAll()) {
+ has_catch_all = true;
+ break;
+ }
+ }
+ if (!has_catch_all) {
+ /* if not caught, rethrow */
+ Write(" else ", OpenBrace());
+ WriteThrow();
+ Write(CloseBrace(), Newline());
+ }
+ Write(CloseBrace(), Newline()); /* end of catch blocks */
+ Write(CloseBrace(), Newline()); /* end of try-catch */
+
+ ResetTypeStack(mark);
+ assert(!label_stack_.empty());
+ assert(label_stack_.back().name == tryexpr.block.label);
+ Write(LabelDecl(GetLocalName(tryexpr.block.label, true)));
+ PopLabel();
+ PushTypes(tryexpr.block.decl.sig.result_types);
+}
+
+void CWriter::Write(const Catch& c) {
+ if (c.IsCatchAll()) {
+ Write(c.exprs);
+ return;
+ }
+
+ Write("if (wasm_rt_exception_tag() == ",
+ ExternalPtr(ModuleFieldType::Tag, module_->GetTag(c.var)->name), ") ",
+ OpenBrace());
+
+ const Tag* tag = module_->GetTag(c.var);
+ const FuncDeclaration& tag_type = tag->decl;
+ const Index num_params = tag_type.GetNumParams();
+ if (num_params == 1) {
+ PushType(tag_type.GetParamType(0));
+ Write("wasm_rt_memcpy(&", StackVar(0), ", wasm_rt_exception(), sizeof(",
+ tag_type.GetParamType(0), "));", Newline());
+ } else if (num_params > 1) {
+ for (const auto& type : tag_type.sig.param_types) {
+ PushType(type);
+ }
+ Write(OpenBrace());
+ Write("struct ", MangleTagTypes(tag_type.sig.param_types), " tmp;",
+ Newline());
+ Write("wasm_rt_memcpy(&tmp, wasm_rt_exception(), sizeof(tmp));", Newline());
+ for (unsigned int i = 0; i < tag_type.sig.param_types.size(); ++i) {
+ Write(StackVar(i));
+ Writef(" = tmp.%c%d;", MangleType(tag_type.sig.param_types.at(i)), i);
+ Write(Newline());
+ }
+
+ Write(CloseBrace(), Newline());
+ }
+
+ Write(c.exprs);
+ Write(CloseBrace());
+}
+
+void CWriter::WriteThrow() {
+ if (try_catch_stack_.empty()) {
+ Write("wasm_rt_throw();", Newline());
+ } else {
+ Write("goto ", try_catch_stack_.back().name, "_catch;", Newline());
+ try_catch_stack_.back().used = true;
+ }
+}
+
+void CWriter::PushTryCatch(const std::string& name) {
+ try_catch_stack_.emplace_back(name, try_catch_stack_.size());
+}
+
+void CWriter::PopTryCatch() {
+ assert(!try_catch_stack_.empty());
+ try_catch_stack_.pop_back();
+}
+
+void CWriter::WriteTryDelegate(const TryExpr& tryexpr) {
+ const size_t mark = BeginTry(tryexpr);
+
+ /* exception has been thrown -- where do we delegate it? */
+
+ if (tryexpr.delegate_target.is_index()) {
+ /* must be the implicit function label */
+ assert(!try_catch_stack_.empty());
+ const std::string& unwind_name = try_catch_stack_.at(0).name;
+ Write("wasm_rt_set_unwind_target(", unwind_name, "_outer_target);",
+ Newline());
+
+ Write("wasm_rt_throw();", Newline());
+ } else {
+ const Label* label = FindLabel(tryexpr.delegate_target, false);
+
+ assert(try_catch_stack_.size() >= label->try_catch_stack_size);
+
+ if (label->label_type == LabelType::Try) {
+ Write("goto ", LabelName(label->name), "_catch;", Newline());
+ try_catch_stack_.at(label->try_catch_stack_size).used = true;
+ } else if (label->try_catch_stack_size == 0) {
+ assert(!try_catch_stack_.empty());
+ const std::string& unwind_name = try_catch_stack_.at(0).name;
+ Write("wasm_rt_set_unwind_target(", unwind_name, "_outer_target);",
+ Newline());
+
+ Write("wasm_rt_throw();", Newline());
+ } else {
+ const std::string label_target =
+ try_catch_stack_.at(label->try_catch_stack_size - 1).name + "_catch";
+ Write("goto ", label_target, ";", Newline());
+ try_catch_stack_.at(label->try_catch_stack_size - 1).used = true;
+ }
+ }
+
+ Write(CloseBrace(), Newline());
+ Write(CloseBrace(), Newline());
+
+ PopTryCatch();
+ ResetTypeStack(mark);
+ assert(!label_stack_.empty());
+ assert(label_stack_.back().name == tryexpr.block.label);
+ Write(LabelDecl(GetLocalName(tryexpr.block.label, true)));
+ PopLabel();
+ PushTypes(tryexpr.block.decl.sig.result_types);
+}
+
+void CWriter::Write(const ExprList& exprs) {
+ for (const Expr& expr : exprs) {
+ switch (expr.type()) {
+ case ExprType::Binary:
+ Write(*cast<BinaryExpr>(&expr));
+ break;
+
+ case ExprType::Block:
+ Write(cast<BlockExpr>(&expr)->block);
+ break;
+
+ case ExprType::Br:
+ Write(GotoLabel(cast<BrExpr>(&expr)->var), Newline());
+ // Stop processing this ExprList, since the following are unreachable.
+ return;
+
+ case ExprType::BrIf:
+ Write("if (", StackVar(0), ") {");
+ DropTypes(1);
+ Write(GotoLabel(cast<BrIfExpr>(&expr)->var), "}", Newline());
+ break;
+
+ case ExprType::BrTable: {
+ const auto* bt_expr = cast<BrTableExpr>(&expr);
+ Write("switch (", StackVar(0), ") ", OpenBrace());
+ DropTypes(1);
+ Index i = 0;
+ for (const Var& var : bt_expr->targets) {
+ Write("case ", i++, ": ", GotoLabel(var), Newline());
+ }
+ Write("default: ");
+ Write(GotoLabel(bt_expr->default_target), Newline(), CloseBrace(),
+ Newline());
+ // Stop processing this ExprList, since the following are unreachable.
+ return;
+ }
+
+ case ExprType::Call: {
+ const Var& var = cast<CallExpr>(&expr)->var;
+ const Func& func = *module_->GetFunc(var);
+ Index num_params = func.GetNumParams();
+ Index num_results = func.GetNumResults();
+ assert(type_stack_.size() >= num_params);
+ if (num_results > 1) {
+ Write(OpenBrace());
+ Write("struct ", MangleMultivalueTypes(func.decl.sig.result_types));
+ Write(" tmp = ");
+ } else if (num_results == 1) {
+ Write(StackVar(num_params - 1, func.GetResultType(0)), " = ");
+ }
+
+ assert(var.is_name());
+ Write(ExternalRef(ModuleFieldType::Func, var.name()), "(");
+ bool is_import = import_module_sym_map_.count(func.name) != 0;
+ if (is_import) {
+ Write("instance->", GlobalName(ModuleFieldType::Import,
+ import_module_sym_map_[func.name]));
+ } else {
+ Write("instance");
+ }
+ for (Index i = 0; i < num_params; ++i) {
+ Write(", ");
+ Write(StackVar(num_params - i - 1));
+ }
+ Write(");", Newline());
+ DropTypes(num_params);
+ if (num_results > 1) {
+ for (Index i = 0; i < num_results; ++i) {
+ Type type = func.GetResultType(i);
+ PushType(type);
+ Write(StackVar(0));
+ Writef(" = tmp.%c%d;", MangleType(type), i);
+ Write(Newline());
+ }
+ Write(CloseBrace(), Newline());
+ } else {
+ PushTypes(func.decl.sig.result_types);
+ }
+ break;
+ }
+
+ case ExprType::CallIndirect: {
+ const FuncDeclaration& decl = cast<CallIndirectExpr>(&expr)->decl;
+ Index num_params = decl.GetNumParams();
+ Index num_results = decl.GetNumResults();
+ assert(type_stack_.size() > num_params);
+ if (num_results > 1) {
+ Write(OpenBrace());
+ Write("struct ", MangleMultivalueTypes(decl.sig.result_types));
+ Write(" tmp = ");
+ } else if (num_results == 1) {
+ Write(StackVar(num_params, decl.GetResultType(0)), " = ");
+ }
+
+ const Table* table =
+ module_->GetTable(cast<CallIndirectExpr>(&expr)->table);
+
+ assert(decl.has_func_type);
+ const FuncType* func_type = module_->GetFuncType(decl.type_var);
+
+ Write("CALL_INDIRECT(",
+ ExternalInstanceRef(ModuleFieldType::Table, table->name), ", ");
+ WriteCallIndirectFuncDeclaration(decl, "(*)");
+ Write(", ", FuncTypeExpr(func_type), ", ", StackVar(0));
+ Write(", ", ExternalInstanceRef(ModuleFieldType::Table, table->name),
+ ".data[", StackVar(0), "].module_instance");
+ for (Index i = 0; i < num_params; ++i) {
+ Write(", ", StackVar(num_params - i));
+ }
+ Write(");", Newline());
+ DropTypes(num_params + 1);
+ if (num_results > 1) {
+ for (Index i = 0; i < num_results; ++i) {
+ Type type = decl.GetResultType(i);
+ PushType(type);
+ Write(StackVar(0));
+ Writef(" = tmp.%c%d;", MangleType(type), i);
+ Write(Newline());
+ }
+ Write(CloseBrace(), Newline());
+ } else {
+ PushTypes(decl.sig.result_types);
+ }
+ break;
+ }
+
+ case ExprType::CodeMetadata:
+ break;
+
+ case ExprType::Compare:
+ Write(*cast<CompareExpr>(&expr));
+ break;
+
+ case ExprType::Const: {
+ const Const& const_ = cast<ConstExpr>(&expr)->const_;
+ PushType(const_.type());
+ Write(StackVar(0), " = ", const_, ";", Newline());
+ break;
+ }
+
+ case ExprType::Convert:
+ Write(*cast<ConvertExpr>(&expr));
+ break;
+
+ case ExprType::Drop:
+ DropTypes(1);
+ break;
+
+ case ExprType::GlobalGet: {
+ const Var& var = cast<GlobalGetExpr>(&expr)->var;
+ PushType(module_->GetGlobal(var)->type);
+ Write(StackVar(0), " = ", GlobalInstanceVar(var), ";", Newline());
+ break;
+ }
+
+ case ExprType::GlobalSet: {
+ const Var& var = cast<GlobalSetExpr>(&expr)->var;
+ Write(GlobalInstanceVar(var), " = ", StackVar(0), ";", Newline());
+ DropTypes(1);
+ break;
+ }
+
+ case ExprType::If: {
+ const IfExpr& if_ = *cast<IfExpr>(&expr);
+ Write("if (", StackVar(0), ") ", OpenBrace());
+ DropTypes(1);
+ std::string label = DefineLabelName(if_.true_.label);
+ DropTypes(if_.true_.decl.GetNumParams());
+ size_t mark = MarkTypeStack();
+ PushLabel(LabelType::If, if_.true_.label, if_.true_.decl.sig);
+ PushTypes(if_.true_.decl.sig.param_types);
+ Write(if_.true_.exprs, CloseBrace());
+ if (!if_.false_.empty()) {
+ ResetTypeStack(mark);
+ PushTypes(if_.true_.decl.sig.param_types);
+ Write(" else ", OpenBrace(), if_.false_, CloseBrace());
+ }
+ ResetTypeStack(mark);
+ Write(Newline(), LabelDecl(label));
+ PopLabel();
+ PushTypes(if_.true_.decl.sig.result_types);
+ break;
+ }
+
+ case ExprType::Load:
+ Write(*cast<LoadExpr>(&expr));
+ break;
+
+ case ExprType::LocalGet: {
+ const Var& var = cast<LocalGetExpr>(&expr)->var;
+ PushType(func_->GetLocalType(var));
+ Write(StackVar(0), " = ", var, ";", Newline());
+ break;
+ }
+
+ case ExprType::LocalSet: {
+ const Var& var = cast<LocalSetExpr>(&expr)->var;
+ Write(var, " = ", StackVar(0), ";", Newline());
+ DropTypes(1);
+ break;
+ }
+
+ case ExprType::LocalTee: {
+ const Var& var = cast<LocalTeeExpr>(&expr)->var;
+ Write(var, " = ", StackVar(0), ";", Newline());
+ break;
+ }
+
+ case ExprType::Loop: {
+ const Block& block = cast<LoopExpr>(&expr)->block;
+ if (!block.exprs.empty()) {
+ Write(DefineLabelName(block.label), ": ");
+ Indent();
+ DropTypes(block.decl.GetNumParams());
+ size_t mark = MarkTypeStack();
+ PushLabel(LabelType::Loop, block.label, block.decl.sig);
+ PushTypes(block.decl.sig.param_types);
+ Write(Newline(), block.exprs);
+ ResetTypeStack(mark);
+ PopLabel();
+ PushTypes(block.decl.sig.result_types);
+ Dedent();
+ }
+ break;
+ }
+
+ case ExprType::MemoryFill: {
+ const auto inst = cast<MemoryFillExpr>(&expr);
+ Memory* memory =
+ module_->memories[module_->GetMemoryIndex(inst->memidx)];
+ Write("memory_fill(",
+ ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", ",
+ StackVar(2), ", ", StackVar(1), ", ", StackVar(0), ");",
+ Newline());
+ DropTypes(3);
+ } break;
+
+ case ExprType::MemoryCopy: {
+ const auto inst = cast<MemoryCopyExpr>(&expr);
+ Memory* dest_memory =
+ module_->memories[module_->GetMemoryIndex(inst->destmemidx)];
+ const Memory* src_memory = module_->GetMemory(inst->srcmemidx);
+ Write("memory_copy(",
+ ExternalInstancePtr(ModuleFieldType::Memory, dest_memory->name),
+ ", ",
+ ExternalInstancePtr(ModuleFieldType::Memory, src_memory->name),
+ ", ", StackVar(2), ", ", StackVar(1), ", ", StackVar(0), ");",
+ Newline());
+ DropTypes(3);
+ } break;
+
+ case ExprType::MemoryInit: {
+ const auto inst = cast<MemoryInitExpr>(&expr);
+ Memory* dest_memory =
+ module_->memories[module_->GetMemoryIndex(inst->memidx)];
+ const DataSegment* src_data = module_->GetDataSegment(inst->var);
+ Write("memory_init(",
+ ExternalInstancePtr(ModuleFieldType::Memory, dest_memory->name),
+ ", ");
+ if (src_data->data.empty()) {
+ Write("NULL, 0");
+ } else {
+ Write("data_segment_data_",
+ GlobalName(ModuleFieldType::DataSegment, src_data->name), ", ");
+ if (is_droppable(src_data)) {
+ Write("(", "instance->data_segment_dropped_",
+ GlobalName(ModuleFieldType::DataSegment, src_data->name),
+ " ? 0 : ", src_data->data.size(), ")");
+ } else {
+ Write("0");
+ }
+ }
+
+ Write(", ", StackVar(2), ", ", StackVar(1), ", ", StackVar(0), ");",
+ Newline());
+ DropTypes(3);
+ } break;
+
+ case ExprType::TableInit: {
+ const auto inst = cast<TableInitExpr>(&expr);
+ Table* dest_table =
+ module_->tables[module_->GetTableIndex(inst->table_index)];
+ const ElemSegment* src_segment =
+ module_->GetElemSegment(inst->segment_index);
+
+ WriteElemTableInit(false, src_segment, dest_table);
+ DropTypes(3);
+ } break;
+
+ case ExprType::DataDrop: {
+ const auto inst = cast<DataDropExpr>(&expr);
+ const DataSegment* data = module_->GetDataSegment(inst->var);
+ if (is_droppable(data)) {
+ Write("instance->data_segment_dropped_",
+ GlobalName(ModuleFieldType::DataSegment, data->name),
+ " = true;", Newline());
+ }
+ } break;
+
+ case ExprType::ElemDrop: {
+ const auto inst = cast<ElemDropExpr>(&expr);
+ const ElemSegment* seg = module_->GetElemSegment(inst->var);
+ if (is_droppable(seg)) {
+ Write("instance->elem_segment_dropped_",
+ GlobalName(ModuleFieldType::ElemSegment, seg->name), " = true;",
+ Newline());
+ }
+ } break;
+
+ case ExprType::TableCopy: {
+ const auto inst = cast<TableCopyExpr>(&expr);
+ Table* dest_table =
+ module_->tables[module_->GetTableIndex(inst->dst_table)];
+ const Table* src_table = module_->GetTable(inst->src_table);
+ if (dest_table->elem_type != src_table->elem_type) {
+ WABT_UNREACHABLE;
+ }
+
+ Write(
+ GetReferenceTypeName(dest_table->elem_type), "_table_copy(",
+ ExternalInstancePtr(ModuleFieldType::Table, dest_table->name), ", ",
+ ExternalInstancePtr(ModuleFieldType::Table, src_table->name), ", ",
+ StackVar(2), ", ", StackVar(1), ", ", StackVar(0), ");", Newline());
+ DropTypes(3);
+ } break;
+
+ case ExprType::TableGet: {
+ const Table* table = module_->GetTable(cast<TableGetExpr>(&expr)->var);
+ Write(StackVar(0, table->elem_type), " = ",
+ GetReferenceTypeName(table->elem_type), "_table_get(",
+ ExternalInstancePtr(ModuleFieldType::Table, table->name), ", ",
+ StackVar(0), ");", Newline());
+ DropTypes(1);
+ PushType(table->elem_type);
+ } break;
+
+ case ExprType::TableSet: {
+ const Table* table = module_->GetTable(cast<TableSetExpr>(&expr)->var);
+ Write(GetReferenceTypeName(table->elem_type), "_table_set(",
+ ExternalInstancePtr(ModuleFieldType::Table, table->name), ", ",
+ StackVar(1), ", ", StackVar(0), ");", Newline());
+ DropTypes(2);
+ } break;
+
+ case ExprType::TableGrow: {
+ const Table* table = module_->GetTable(cast<TableGrowExpr>(&expr)->var);
+ Write(StackVar(1, Type::I32), " = wasm_rt_grow_",
+ GetReferenceTypeName(table->elem_type), "_table(",
+ ExternalInstancePtr(ModuleFieldType::Table, table->name), ", ",
+ StackVar(0), ", ", StackVar(1), ");", Newline());
+ DropTypes(2);
+ PushType(Type::I32);
+ } break;
+
+ case ExprType::TableSize: {
+ const Table* table = module_->GetTable(cast<TableSizeExpr>(&expr)->var);
+
+ PushType(Type::I32);
+ Write(StackVar(0), " = ",
+ ExternalInstanceRef(ModuleFieldType::Table, table->name),
+ ".size;", Newline());
+ } break;
+
+ case ExprType::TableFill: {
+ const Table* table = module_->GetTable(cast<TableFillExpr>(&expr)->var);
+ Write(GetReferenceTypeName(table->elem_type), "_table_fill(",
+ ExternalInstancePtr(ModuleFieldType::Table, table->name), ", ",
+ StackVar(2), ", ", StackVar(1), ", ", StackVar(0), ");",
+ Newline());
+ DropTypes(3);
+ } break;
+
+ case ExprType::RefFunc: {
+ const Func* func = module_->GetFunc(cast<RefFuncExpr>(&expr)->var);
+ PushType(Type::FuncRef);
+ const FuncDeclaration& decl = func->decl;
+
+ assert(decl.has_func_type);
+ const FuncType* func_type = module_->GetFuncType(decl.type_var);
+
+ Write(StackVar(0), " = (wasm_rt_funcref_t){", FuncTypeExpr(func_type),
+ ", (wasm_rt_function_ptr_t)",
+ ExternalPtr(ModuleFieldType::Func, func->name), ", ");
+
+ bool is_import = import_module_sym_map_.count(func->name) != 0;
+ if (is_import) {
+ Write("instance->", GlobalName(ModuleFieldType::Import,
+ import_module_sym_map_[func->name]));
+ } else {
+ Write("instance");
+ }
+
+ Write("};", Newline());
+ } break;
+
+ case ExprType::RefNull:
+ PushType(cast<RefNullExpr>(&expr)->type);
+ Write(StackVar(0), " = ",
+ GetReferenceNullValue(cast<RefNullExpr>(&expr)->type), ";",
+ Newline());
+ break;
+
+ case ExprType::RefIsNull:
+ switch (StackType(0)) {
+ case Type::FuncRef:
+ Write(StackVar(0, Type::I32), " = (", StackVar(0), ".func == NULL",
+ ");", Newline());
+ break;
+ case Type::ExternRef:
+ Write(StackVar(0, Type::I32), " = (", StackVar(0),
+ " == ", GetReferenceNullValue(Type::ExternRef), ");",
+ Newline());
+ break;
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ DropTypes(1);
+ PushType(Type::I32);
+ break;
+
+ case ExprType::MemoryGrow: {
+ Memory* memory = module_->memories[module_->GetMemoryIndex(
+ cast<MemoryGrowExpr>(&expr)->memidx)];
+
+ Write(StackVar(0), " = wasm_rt_grow_memory(",
+ ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", ",
+ StackVar(0), ");", Newline());
+ break;
+ }
+
+ case ExprType::MemorySize: {
+ Memory* memory = module_->memories[module_->GetMemoryIndex(
+ cast<MemorySizeExpr>(&expr)->memidx)];
+
+ PushType(memory->page_limits.IndexType());
+ Write(StackVar(0), " = ",
+ ExternalInstanceRef(ModuleFieldType::Memory, memory->name),
+ ".pages;", Newline());
+ break;
+ }
+
+ case ExprType::Nop:
+ break;
+
+ case ExprType::Return:
+ // Goto the function label instead; this way we can do shared function
+ // cleanup code in one place.
+ Write(GotoLabel(Var(label_stack_.size() - 1, {})), Newline());
+ // Stop processing this ExprList, since the following are unreachable.
+ return;
+
+ case ExprType::Select: {
+ Type type = StackType(1);
+ Write(StackVar(2), " = ", StackVar(0), " ? ", StackVar(2), " : ",
+ StackVar(1), ";", Newline());
+ DropTypes(3);
+ PushType(type);
+ break;
+ }
+
+ case ExprType::Store:
+ Write(*cast<StoreExpr>(&expr));
+ break;
+
+ case ExprType::Unary:
+ Write(*cast<UnaryExpr>(&expr));
+ break;
+
+ case ExprType::Ternary:
+ Write(*cast<TernaryExpr>(&expr));
+ break;
+
+ case ExprType::SimdLaneOp: {
+ Write(*cast<SimdLaneOpExpr>(&expr));
+ break;
+ }
+
+ case ExprType::SimdLoadLane: {
+ Write(*cast<SimdLoadLaneExpr>(&expr));
+ break;
+ }
+
+ case ExprType::SimdStoreLane: {
+ Write(*cast<SimdStoreLaneExpr>(&expr));
+ break;
+ }
+
+ case ExprType::SimdShuffleOp: {
+ Write(*cast<SimdShuffleOpExpr>(&expr));
+ break;
+ }
+
+ case ExprType::LoadSplat:
+ Write(*cast<LoadSplatExpr>(&expr));
+ break;
+
+ case ExprType::LoadZero:
+ Write(*cast<LoadZeroExpr>(&expr));
+ break;
+
+ case ExprType::Unreachable:
+ Write("UNREACHABLE;", Newline());
+ return;
+
+ case ExprType::Throw: {
+ const Var& var = cast<ThrowExpr>(&expr)->var;
+ const Tag* tag = module_->GetTag(var);
+
+ Index num_params = tag->decl.GetNumParams();
+ if (num_params == 0) {
+ Write("wasm_rt_load_exception(",
+ ExternalPtr(ModuleFieldType::Tag, tag->name), ", 0, NULL);",
+ Newline());
+ } else if (num_params == 1) {
+ Write("wasm_rt_load_exception(",
+ ExternalPtr(ModuleFieldType::Tag, tag->name), ", sizeof(",
+ tag->decl.GetParamType(0), "), &", StackVar(0), ");",
+ Newline());
+ } else {
+ Write(OpenBrace());
+ Write("struct ", MangleTagTypes(tag->decl.sig.param_types));
+ Write(" tmp = {");
+ for (Index i = 0; i < num_params; ++i) {
+ Write(StackVar(i), ", ");
+ }
+ Write("};", Newline());
+ Write("wasm_rt_load_exception(",
+ ExternalPtr(ModuleFieldType::Tag, tag->name),
+ ", sizeof(tmp), &tmp);", Newline());
+ Write(CloseBrace(), Newline());
+ }
+
+ WriteThrow();
+ } break;
+
+ case ExprType::Rethrow: {
+ const RethrowExpr* rethrow = cast<RethrowExpr>(&expr);
+ assert(rethrow->var.is_name());
+ const LabelName ex{rethrow->var.name()};
+ func_includes_.insert(rethrow->var.name());
+ Write("wasm_rt_load_exception(", ex, "_tag, ", ex, "_size, ", ex, ");",
+ Newline());
+ WriteThrow();
+ } break;
+
+ case ExprType::Try: {
+ const TryExpr& tryexpr = *cast<TryExpr>(&expr);
+ switch (tryexpr.kind) {
+ case TryKind::Plain:
+ Write(tryexpr.block);
+ break;
+ case TryKind::Catch:
+ WriteTryCatch(tryexpr);
+ break;
+ case TryKind::Delegate:
+ WriteTryDelegate(tryexpr);
+ break;
+ }
+ } break;
+
+ case ExprType::AtomicLoad:
+ case ExprType::AtomicRmw:
+ case ExprType::AtomicRmwCmpxchg:
+ case ExprType::AtomicStore:
+ case ExprType::AtomicWait:
+ case ExprType::AtomicFence:
+ case ExprType::AtomicNotify:
+ case ExprType::ReturnCall:
+ case ExprType::ReturnCallIndirect:
+ case ExprType::CallRef:
+ UNIMPLEMENTED("...");
+ break;
+ }
+ }
+}
+
+void CWriter::WriteSimpleUnaryExpr(Opcode opcode, const char* op) {
+ Type result_type = opcode.GetResultType();
+ Write(StackVar(0, result_type), " = ", op, "(", StackVar(0), ");", Newline());
+ DropTypes(1);
+ PushType(opcode.GetResultType());
+}
+
+void CWriter::WriteInfixBinaryExpr(Opcode opcode,
+ const char* op,
+ AssignOp assign_op) {
+ Type result_type = opcode.GetResultType();
+ Write(StackVar(1, result_type));
+ if (assign_op == AssignOp::Allowed) {
+ Write(" ", op, "= ", StackVar(0));
+ } else {
+ Write(" = ", StackVar(1), " ", op, " ", StackVar(0));
+ }
+ Write(";", Newline());
+ DropTypes(2);
+ PushType(result_type);
+}
+
+void CWriter::WritePrefixBinaryExpr(Opcode opcode, const char* op) {
+ Type result_type = opcode.GetResultType();
+ Write(StackVar(1, result_type), " = ", op, "(", StackVar(1), ", ",
+ StackVar(0), ");", Newline());
+ DropTypes(2);
+ PushType(result_type);
+}
+
+void CWriter::WriteSignedBinaryExpr(Opcode opcode, const char* op) {
+ Type result_type = opcode.GetResultType();
+ Type type = opcode.GetParamType1();
+ assert(opcode.GetParamType2() == type);
+ Write(StackVar(1, result_type), " = (", type, ")((", SignedType(type), ")",
+ StackVar(1), " ", op, " (", SignedType(type), ")", StackVar(0), ");",
+ Newline());
+ DropTypes(2);
+ PushType(result_type);
+}
+
+void CWriter::Write(const BinaryExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::I32Add:
+ case Opcode::I64Add:
+ case Opcode::F32Add:
+ case Opcode::F64Add:
+ WriteInfixBinaryExpr(expr.opcode, "+");
+ break;
+
+ case Opcode::I32Sub:
+ case Opcode::I64Sub:
+ case Opcode::F32Sub:
+ case Opcode::F64Sub:
+ WriteInfixBinaryExpr(expr.opcode, "-");
+ break;
+
+ case Opcode::I32Mul:
+ case Opcode::I64Mul:
+ case Opcode::F32Mul:
+ case Opcode::F64Mul:
+ WriteInfixBinaryExpr(expr.opcode, "*");
+ break;
+
+ case Opcode::I32DivS:
+ WritePrefixBinaryExpr(expr.opcode, "I32_DIV_S");
+ break;
+
+ case Opcode::I64DivS:
+ WritePrefixBinaryExpr(expr.opcode, "I64_DIV_S");
+ break;
+
+ case Opcode::I32DivU:
+ case Opcode::I64DivU:
+ WritePrefixBinaryExpr(expr.opcode, "DIV_U");
+ break;
+
+ case Opcode::F32Div:
+ case Opcode::F64Div:
+ WriteInfixBinaryExpr(expr.opcode, "/");
+ break;
+
+ case Opcode::I32RemS:
+ WritePrefixBinaryExpr(expr.opcode, "I32_REM_S");
+ break;
+
+ case Opcode::I64RemS:
+ WritePrefixBinaryExpr(expr.opcode, "I64_REM_S");
+ break;
+
+ case Opcode::I32RemU:
+ case Opcode::I64RemU:
+ WritePrefixBinaryExpr(expr.opcode, "REM_U");
+ break;
+
+ case Opcode::I32And:
+ case Opcode::I64And:
+ WriteInfixBinaryExpr(expr.opcode, "&");
+ break;
+
+ case Opcode::I32Or:
+ case Opcode::I64Or:
+ WriteInfixBinaryExpr(expr.opcode, "|");
+ break;
+
+ case Opcode::I32Xor:
+ case Opcode::I64Xor:
+ WriteInfixBinaryExpr(expr.opcode, "^");
+ break;
+
+ case Opcode::I32Shl:
+ case Opcode::I64Shl:
+ Write(StackVar(1), " <<= (", StackVar(0), " & ",
+ GetShiftMask(expr.opcode.GetResultType()), ");", Newline());
+ DropTypes(1);
+ break;
+
+ case Opcode::I32ShrS:
+ case Opcode::I64ShrS: {
+ Type type = expr.opcode.GetResultType();
+ Write(StackVar(1), " = (", type, ")((", SignedType(type), ")",
+ StackVar(1), " >> (", StackVar(0), " & ", GetShiftMask(type), "));",
+ Newline());
+ DropTypes(1);
+ break;
+ }
+
+ case Opcode::I32ShrU:
+ case Opcode::I64ShrU:
+ Write(StackVar(1), " >>= (", StackVar(0), " & ",
+ GetShiftMask(expr.opcode.GetResultType()), ");", Newline());
+ DropTypes(1);
+ break;
+
+ case Opcode::I32Rotl:
+ WritePrefixBinaryExpr(expr.opcode, "I32_ROTL");
+ break;
+
+ case Opcode::I64Rotl:
+ WritePrefixBinaryExpr(expr.opcode, "I64_ROTL");
+ break;
+
+ case Opcode::I32Rotr:
+ WritePrefixBinaryExpr(expr.opcode, "I32_ROTR");
+ break;
+
+ case Opcode::I64Rotr:
+ WritePrefixBinaryExpr(expr.opcode, "I64_ROTR");
+ break;
+
+ case Opcode::F32Min:
+ case Opcode::F64Min:
+ WritePrefixBinaryExpr(expr.opcode, "FMIN");
+ break;
+
+ case Opcode::F32Max:
+ case Opcode::F64Max:
+ WritePrefixBinaryExpr(expr.opcode, "FMAX");
+ break;
+
+ case Opcode::F32Copysign:
+ WritePrefixBinaryExpr(expr.opcode, "copysignf");
+ break;
+
+ case Opcode::F64Copysign:
+ WritePrefixBinaryExpr(expr.opcode, "copysign");
+ break;
+
+ case Opcode::I8X16Add:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_add");
+ break;
+
+ case Opcode::I8X16AddSatS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_add_sat");
+ break;
+
+ case Opcode::I8X16AddSatU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_add_sat");
+ break;
+
+ case Opcode::I8X16AvgrU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_avgr");
+ break;
+
+ case Opcode::I8X16MaxS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_max");
+ break;
+
+ case Opcode::I8X16MaxU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_max");
+ break;
+
+ case Opcode::I8X16MinS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_min");
+ break;
+
+ case Opcode::I8X16MinU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_min");
+ break;
+
+ case Opcode::I8X16NarrowI16X8S:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_narrow_i16x8");
+ break;
+
+ case Opcode::I8X16NarrowI16X8U:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_narrow_i16x8");
+ break;
+
+ case Opcode::I8X16Shl:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_shl");
+ break;
+
+ case Opcode::I8X16ShrS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_shr");
+ break;
+
+ case Opcode::I8X16ShrU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_shr");
+ break;
+
+ case Opcode::I8X16Sub:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_sub");
+ break;
+
+ case Opcode::I8X16SubSatS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_sub_sat");
+ break;
+
+ case Opcode::I8X16SubSatU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_sub_sat");
+ break;
+
+ case Opcode::I8X16Swizzle:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_swizzle");
+ break;
+
+ case Opcode::I16X8Add:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_add");
+ break;
+
+ case Opcode::I16X8AvgrU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_avgr");
+ break;
+
+ case Opcode::I16X8AddSatS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_add_sat");
+ break;
+
+ case Opcode::I16X8AddSatU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_add_sat");
+ break;
+
+ case Opcode::I16X8ExtmulHighI8X16S:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_extmul_high_i8x16");
+ break;
+
+ case Opcode::I16X8ExtmulHighI8X16U:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_extmul_high_u8x16");
+ break;
+
+ case Opcode::I16X8ExtmulLowI8X16S:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_extmul_low_i8x16");
+ break;
+
+ case Opcode::I16X8ExtmulLowI8X16U:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_extmul_low_u8x16");
+ break;
+
+ case Opcode::I16X8MaxS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_max");
+ break;
+
+ case Opcode::I16X8MaxU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_max");
+ break;
+
+ case Opcode::I16X8MinS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_min");
+ break;
+
+ case Opcode::I16X8MinU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_min");
+ break;
+
+ case Opcode::I16X8Mul:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_mul");
+ break;
+
+ case Opcode::I16X8NarrowI32X4S:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_narrow_i32x4");
+ break;
+
+ case Opcode::I16X8NarrowI32X4U:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_narrow_i32x4");
+ break;
+
+ case Opcode::I16X8Q15mulrSatS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_q15mulr_sat");
+ break;
+
+ case Opcode::I16X8Shl:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_shl");
+ break;
+
+ case Opcode::I16X8ShrS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_shr");
+ break;
+
+ case Opcode::I16X8ShrU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_shr");
+ break;
+
+ case Opcode::I16X8Sub:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_sub");
+ break;
+
+ case Opcode::I16X8SubSatS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_sub_sat");
+ break;
+
+ case Opcode::I16X8SubSatU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_sub_sat");
+ break;
+
+ case Opcode::I32X4Add:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_add");
+ break;
+
+ case Opcode::I32X4DotI16X8S:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_dot_i16x8");
+ break;
+
+ case Opcode::I32X4ExtmulHighI16X8S:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_extmul_high_i16x8");
+ break;
+
+ case Opcode::I32X4ExtmulHighI16X8U:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u32x4_extmul_high_u16x8");
+ break;
+
+ case Opcode::I32X4ExtmulLowI16X8S:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_extmul_low_i16x8");
+ break;
+
+ case Opcode::I32X4ExtmulLowI16X8U:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u32x4_extmul_low_u16x8");
+ break;
+
+ case Opcode::I32X4MaxS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_max");
+ break;
+
+ case Opcode::I32X4MaxU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u32x4_max");
+ break;
+
+ case Opcode::I32X4MinS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_min");
+ break;
+
+ case Opcode::I32X4MinU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u32x4_min");
+ break;
+
+ case Opcode::I32X4Mul:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_mul");
+ break;
+
+ case Opcode::I32X4Shl:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_shl");
+ break;
+
+ case Opcode::I32X4ShrS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_shr");
+ break;
+
+ case Opcode::I32X4ShrU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u32x4_shr");
+ break;
+
+ case Opcode::I32X4Sub:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_sub");
+ break;
+
+ case Opcode::I64X2Add:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_add");
+ break;
+
+ case Opcode::I64X2ExtmulHighI32X4S:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_extmul_high_i32x4");
+ break;
+
+ case Opcode::I64X2ExtmulHighI32X4U:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u64x2_extmul_high_u32x4");
+ break;
+
+ case Opcode::I64X2ExtmulLowI32X4S:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_extmul_low_i32x4");
+ break;
+
+ case Opcode::I64X2ExtmulLowI32X4U:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u64x2_extmul_low_u32x4");
+ break;
+
+ case Opcode::I64X2Mul:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_mul");
+ break;
+
+ case Opcode::I64X2Shl:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_shl");
+ break;
+
+ case Opcode::I64X2ShrS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_shr");
+ break;
+
+ case Opcode::I64X2ShrU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u64x2_shr");
+ break;
+
+ case Opcode::I64X2Sub:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_sub");
+ break;
+
+ case Opcode::F32X4Add:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_add");
+ break;
+
+ case Opcode::F32X4Div:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_div");
+ break;
+
+ case Opcode::F32X4Max:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_max");
+ break;
+
+ case Opcode::F32X4Mul:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_mul");
+ break;
+
+ case Opcode::F32X4Min:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_min");
+ break;
+
+ case Opcode::F32X4PMax:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_pmax");
+ break;
+
+ case Opcode::F32X4PMin:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_pmin");
+ break;
+
+ case Opcode::F32X4Sub:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_sub");
+ break;
+
+ case Opcode::F64X2Add:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_add");
+ break;
+
+ case Opcode::F64X2Div:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_div");
+ break;
+
+ case Opcode::F64X2Max:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_max");
+ break;
+
+ case Opcode::F64X2Mul:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_mul");
+ break;
+
+ case Opcode::F64X2Min:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_min");
+ break;
+
+ case Opcode::F64X2PMax:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_pmax");
+ break;
+
+ case Opcode::F64X2PMin:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_pmin");
+ break;
+
+ case Opcode::F64X2Sub:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_sub");
+ break;
+
+ case Opcode::V128And:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_v128_and");
+ break;
+
+ case Opcode::V128Andnot:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_v128_andnot");
+ break;
+
+ case Opcode::V128Or:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_v128_or");
+ break;
+
+ case Opcode::V128Xor:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_v128_xor");
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const CompareExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::I32Eq:
+ case Opcode::I64Eq:
+ case Opcode::F32Eq:
+ case Opcode::F64Eq:
+ WriteInfixBinaryExpr(expr.opcode, "==", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32Ne:
+ case Opcode::I64Ne:
+ case Opcode::F32Ne:
+ case Opcode::F64Ne:
+ WriteInfixBinaryExpr(expr.opcode, "!=", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32LtS:
+ case Opcode::I64LtS:
+ WriteSignedBinaryExpr(expr.opcode, "<");
+ break;
+
+ case Opcode::I32LtU:
+ case Opcode::I64LtU:
+ case Opcode::F32Lt:
+ case Opcode::F64Lt:
+ WriteInfixBinaryExpr(expr.opcode, "<", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32LeS:
+ case Opcode::I64LeS:
+ WriteSignedBinaryExpr(expr.opcode, "<=");
+ break;
+
+ case Opcode::I32LeU:
+ case Opcode::I64LeU:
+ case Opcode::F32Le:
+ case Opcode::F64Le:
+ WriteInfixBinaryExpr(expr.opcode, "<=", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32GtS:
+ case Opcode::I64GtS:
+ WriteSignedBinaryExpr(expr.opcode, ">");
+ break;
+
+ case Opcode::I32GtU:
+ case Opcode::I64GtU:
+ case Opcode::F32Gt:
+ case Opcode::F64Gt:
+ WriteInfixBinaryExpr(expr.opcode, ">", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32GeS:
+ case Opcode::I64GeS:
+ WriteSignedBinaryExpr(expr.opcode, ">=");
+ break;
+
+ case Opcode::I32GeU:
+ case Opcode::I64GeU:
+ case Opcode::F32Ge:
+ case Opcode::F64Ge:
+ WriteInfixBinaryExpr(expr.opcode, ">=", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I8X16Eq:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_eq");
+ break;
+
+ case Opcode::I8X16GeS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_ge");
+ break;
+
+ case Opcode::I8X16GeU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_ge");
+ break;
+
+ case Opcode::I8X16GtS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_gt");
+ break;
+
+ case Opcode::I8X16GtU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_gt");
+ break;
+
+ case Opcode::I8X16LeS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_le");
+ break;
+
+ case Opcode::I8X16LeU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_le");
+ break;
+
+ case Opcode::I8X16LtS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_lt");
+ break;
+
+ case Opcode::I8X16LtU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_lt");
+ break;
+
+ case Opcode::I8X16Ne:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_ne");
+ break;
+
+ case Opcode::I16X8Eq:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_eq");
+ break;
+
+ case Opcode::I16X8GeS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_ge");
+ break;
+
+ case Opcode::I16X8GeU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_ge");
+ break;
+
+ case Opcode::I16X8GtS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_gt");
+ break;
+ case Opcode::I16X8GtU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_gt");
+ break;
+
+ case Opcode::I16X8LeS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_le");
+ break;
+
+ case Opcode::I16X8LeU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_le");
+ break;
+
+ case Opcode::I16X8LtS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_lt");
+ break;
+
+ case Opcode::I16X8LtU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_lt");
+ break;
+
+ case Opcode::I16X8Ne:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_ne");
+ break;
+
+ case Opcode::I32X4Eq:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_eq");
+ break;
+
+ case Opcode::I32X4GeS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_ge");
+ break;
+
+ case Opcode::I32X4GeU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u32x4_ge");
+ break;
+
+ case Opcode::I32X4GtS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_gt");
+ break;
+
+ case Opcode::I32X4GtU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u32x4_gt");
+ break;
+
+ case Opcode::I32X4LeS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_le");
+ break;
+
+ case Opcode::I32X4LeU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u32x4_le");
+ break;
+
+ case Opcode::I32X4LtS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_lt");
+ break;
+
+ case Opcode::I32X4LtU:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u32x4_lt");
+ break;
+
+ case Opcode::I32X4Ne:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_ne");
+ break;
+
+ case Opcode::I64X2Eq:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_eq");
+ break;
+
+ case Opcode::I64X2GeS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_ge");
+ break;
+
+ case Opcode::I64X2GtS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_gt");
+ break;
+
+ case Opcode::I64X2LeS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_le");
+ break;
+
+ case Opcode::I64X2LtS:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_lt");
+ break;
+
+ case Opcode::I64X2Ne:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_ne");
+ break;
+
+ case Opcode::F32X4Eq:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_eq");
+ break;
+
+ case Opcode::F32X4Ge:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_ge");
+ break;
+
+ case Opcode::F32X4Gt:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_gt");
+ break;
+
+ case Opcode::F32X4Le:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_le");
+ break;
+
+ case Opcode::F32X4Lt:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_lt");
+ break;
+
+ case Opcode::F32X4Ne:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_ne");
+ break;
+
+ case Opcode::F64X2Eq:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_eq");
+ break;
+
+ case Opcode::F64X2Ge:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_ge");
+ break;
+
+ case Opcode::F64X2Gt:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_gt");
+ break;
+
+ case Opcode::F64X2Le:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_le");
+ break;
+
+ case Opcode::F64X2Lt:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_lt");
+ break;
+
+ case Opcode::F64X2Ne:
+ WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_ne");
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const ConvertExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::I32Eqz:
+ case Opcode::I64Eqz:
+ WriteSimpleUnaryExpr(expr.opcode, "!");
+ break;
+
+ case Opcode::I64ExtendI32S:
+ WriteSimpleUnaryExpr(expr.opcode, "(u64)(s64)(s32)");
+ break;
+
+ case Opcode::I64ExtendI32U:
+ WriteSimpleUnaryExpr(expr.opcode, "(u64)");
+ break;
+
+ case Opcode::I32WrapI64:
+ WriteSimpleUnaryExpr(expr.opcode, "(u32)");
+ break;
+
+ case Opcode::I32TruncF32S:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_S_F32");
+ break;
+
+ case Opcode::I64TruncF32S:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_S_F32");
+ break;
+
+ case Opcode::I32TruncF64S:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_S_F64");
+ break;
+
+ case Opcode::I64TruncF64S:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_S_F64");
+ break;
+
+ case Opcode::I32TruncF32U:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_U_F32");
+ break;
+
+ case Opcode::I64TruncF32U:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_U_F32");
+ break;
+
+ case Opcode::I32TruncF64U:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_U_F64");
+ break;
+
+ case Opcode::I64TruncF64U:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_U_F64");
+ break;
+
+ case Opcode::I32TruncSatF32S:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_SAT_S_F32");
+ break;
+
+ case Opcode::I64TruncSatF32S:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_SAT_S_F32");
+ break;
+
+ case Opcode::I32TruncSatF64S:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_SAT_S_F64");
+ break;
+
+ case Opcode::I64TruncSatF64S:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_SAT_S_F64");
+ break;
+
+ case Opcode::I32TruncSatF32U:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_SAT_U_F32");
+ break;
+
+ case Opcode::I64TruncSatF32U:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_SAT_U_F32");
+ break;
+
+ case Opcode::I32TruncSatF64U:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_SAT_U_F64");
+ break;
+
+ case Opcode::I64TruncSatF64U:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_SAT_U_F64");
+ break;
+
+ case Opcode::F32ConvertI32S:
+ WriteSimpleUnaryExpr(expr.opcode, "(f32)(s32)");
+ break;
+
+ case Opcode::F32ConvertI64S:
+ WriteSimpleUnaryExpr(expr.opcode, "(f32)(s64)");
+ break;
+
+ case Opcode::F32ConvertI32U:
+ WriteSimpleUnaryExpr(expr.opcode, "(f32)");
+ break;
+
+ case Opcode::F32DemoteF64:
+ WriteSimpleUnaryExpr(expr.opcode, "(f32)wasm_quiet");
+ break;
+
+ case Opcode::F32ConvertI64U:
+ // TODO(binji): This needs to be handled specially (see
+ // wabt_convert_uint64_to_float).
+ WriteSimpleUnaryExpr(expr.opcode, "(f32)");
+ break;
+
+ case Opcode::F64ConvertI32S:
+ WriteSimpleUnaryExpr(expr.opcode, "(f64)(s32)");
+ break;
+
+ case Opcode::F64ConvertI64S:
+ WriteSimpleUnaryExpr(expr.opcode, "(f64)(s64)");
+ break;
+
+ case Opcode::F64ConvertI32U:
+ WriteSimpleUnaryExpr(expr.opcode, "(f64)");
+ break;
+
+ case Opcode::F64PromoteF32:
+ WriteSimpleUnaryExpr(expr.opcode, "(f64)wasm_quietf");
+ break;
+
+ case Opcode::F64ConvertI64U:
+ // TODO(binji): This needs to be handled specially (see
+ // wabt_convert_uint64_to_double).
+ WriteSimpleUnaryExpr(expr.opcode, "(f64)");
+ break;
+
+ case Opcode::F32ReinterpretI32:
+ WriteSimpleUnaryExpr(expr.opcode, "f32_reinterpret_i32");
+ break;
+
+ case Opcode::I32ReinterpretF32:
+ WriteSimpleUnaryExpr(expr.opcode, "i32_reinterpret_f32");
+ break;
+
+ case Opcode::F64ReinterpretI64:
+ WriteSimpleUnaryExpr(expr.opcode, "f64_reinterpret_i64");
+ break;
+
+ case Opcode::I64ReinterpretF64:
+ WriteSimpleUnaryExpr(expr.opcode, "i64_reinterpret_f64");
+ break;
+
+ case Opcode::I32X4TruncSatF32X4S:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i32x4_trunc_sat_f32x4");
+ break;
+
+ case Opcode::I32X4TruncSatF32X4U:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_u32x4_trunc_sat_f32x4");
+ break;
+
+ case Opcode::I32X4TruncSatF64X2SZero:
+ WriteSimpleUnaryExpr(expr.opcode,
+ "simde_wasm_i32x4_trunc_sat_f64x2_zero");
+ break;
+
+ case Opcode::I32X4TruncSatF64X2UZero:
+ WriteSimpleUnaryExpr(expr.opcode,
+ "simde_wasm_u32x4_trunc_sat_f64x2_zero");
+ break;
+
+ case Opcode::F32X4ConvertI32X4S:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_convert_i32x4");
+ break;
+
+ case Opcode::F32X4ConvertI32X4U:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_convert_u32x4");
+ break;
+
+ case Opcode::F32X4DemoteF64X2Zero:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_demote_f64x2_zero");
+ break;
+
+ case Opcode::F64X2ConvertLowI32X4S:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_convert_low_i32x4");
+ break;
+
+ case Opcode::F64X2ConvertLowI32X4U:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_convert_low_u32x4");
+ break;
+
+ case Opcode::F64X2PromoteLowF32X4:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_promote_low_f32x4");
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const LoadExpr& expr) {
+ const char* func = nullptr;
+ // clang-format off
+ switch (expr.opcode) {
+ case Opcode::I32Load: func = "i32_load"; break;
+ case Opcode::I64Load: func = "i64_load"; break;
+ case Opcode::F32Load: func = "f32_load"; break;
+ case Opcode::F64Load: func = "f64_load"; break;
+ case Opcode::I32Load8S: func = "i32_load8_s"; break;
+ case Opcode::I64Load8S: func = "i64_load8_s"; break;
+ case Opcode::I32Load8U: func = "i32_load8_u"; break;
+ case Opcode::I64Load8U: func = "i64_load8_u"; break;
+ case Opcode::I32Load16S: func = "i32_load16_s"; break;
+ case Opcode::I64Load16S: func = "i64_load16_s"; break;
+ case Opcode::I32Load16U: func = "i32_load16_u"; break;
+ case Opcode::I64Load16U: func = "i64_load16_u"; break;
+ case Opcode::I64Load32S: func = "i64_load32_s"; break;
+ case Opcode::I64Load32U: func = "i64_load32_u"; break;
+ case Opcode::V128Load: func = "v128_load"; break;
+ case Opcode::V128Load8X8S: func = "i16x8_load8x8"; break;
+ case Opcode::V128Load8X8U: func = "u16x8_load8x8"; break;
+ case Opcode::V128Load16X4S: func = "i32x4_load16x4"; break;
+ case Opcode::V128Load16X4U: func = "u32x4_load16x4"; break;
+ case Opcode::V128Load32X2S: func = "i64x2_load32x2"; break;
+ case Opcode::V128Load32X2U: func = "u64x2_load32x2"; break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+ // clang-format on
+
+ Memory* memory = module_->memories[module_->GetMemoryIndex(expr.memidx)];
+
+ Type result_type = expr.opcode.GetResultType();
+ Write(StackVar(0, result_type), " = ", func, "(",
+ ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", (u64)(",
+ StackVar(0), ")");
+ if (expr.offset != 0)
+ Write(" + ", expr.offset, "u");
+ Write(");", Newline());
+ DropTypes(1);
+ PushType(result_type);
+}
+
+void CWriter::Write(const StoreExpr& expr) {
+ const char* func = nullptr;
+ // clang-format off
+ switch (expr.opcode) {
+ case Opcode::I32Store: func = "i32_store"; break;
+ case Opcode::I64Store: func = "i64_store"; break;
+ case Opcode::F32Store: func = "f32_store"; break;
+ case Opcode::F64Store: func = "f64_store"; break;
+ case Opcode::I32Store8: func = "i32_store8"; break;
+ case Opcode::I64Store8: func = "i64_store8"; break;
+ case Opcode::I32Store16: func = "i32_store16"; break;
+ case Opcode::I64Store16: func = "i64_store16"; break;
+ case Opcode::I64Store32: func = "i64_store32"; break;
+ case Opcode::V128Store: func = "v128_store"; break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+ // clang-format on
+
+ Memory* memory = module_->memories[module_->GetMemoryIndex(expr.memidx)];
+
+ Write(func, "(", ExternalInstancePtr(ModuleFieldType::Memory, memory->name),
+ ", (u64)(", StackVar(1), ")");
+ if (expr.offset != 0)
+ Write(" + ", expr.offset);
+ Write(", ", StackVar(0), ");", Newline());
+ DropTypes(2);
+}
+
+void CWriter::Write(const UnaryExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::I32Clz:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_CLZ");
+ break;
+
+ case Opcode::I64Clz:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_CLZ");
+ break;
+
+ case Opcode::I32Ctz:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_CTZ");
+ break;
+
+ case Opcode::I64Ctz:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_CTZ");
+ break;
+
+ case Opcode::I32Popcnt:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_POPCNT");
+ break;
+
+ case Opcode::I64Popcnt:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_POPCNT");
+ break;
+
+ case Opcode::F32Neg:
+ case Opcode::F64Neg:
+ WriteSimpleUnaryExpr(expr.opcode, "-");
+ break;
+
+ case Opcode::F32Abs:
+ WriteSimpleUnaryExpr(expr.opcode, "wasm_fabsf");
+ break;
+
+ case Opcode::F64Abs:
+ WriteSimpleUnaryExpr(expr.opcode, "wasm_fabs");
+ break;
+
+ case Opcode::F32Sqrt:
+ WriteSimpleUnaryExpr(expr.opcode, "wasm_sqrtf");
+ break;
+
+ case Opcode::F64Sqrt:
+ WriteSimpleUnaryExpr(expr.opcode, "wasm_sqrt");
+ break;
+
+ case Opcode::F32Ceil:
+ WriteSimpleUnaryExpr(expr.opcode, "wasm_ceilf");
+ break;
+
+ case Opcode::F64Ceil:
+ WriteSimpleUnaryExpr(expr.opcode, "wasm_ceil");
+ break;
+
+ case Opcode::F32Floor:
+ WriteSimpleUnaryExpr(expr.opcode, "wasm_floorf");
+ break;
+
+ case Opcode::F64Floor:
+ WriteSimpleUnaryExpr(expr.opcode, "wasm_floor");
+ break;
+
+ case Opcode::F32Trunc:
+ WriteSimpleUnaryExpr(expr.opcode, "wasm_truncf");
+ break;
+
+ case Opcode::F64Trunc:
+ WriteSimpleUnaryExpr(expr.opcode, "wasm_trunc");
+ break;
+
+ case Opcode::F32Nearest:
+ WriteSimpleUnaryExpr(expr.opcode, "wasm_nearbyintf");
+ break;
+
+ case Opcode::F64Nearest:
+ WriteSimpleUnaryExpr(expr.opcode, "wasm_nearbyint");
+ break;
+
+ case Opcode::I32Extend8S:
+ WriteSimpleUnaryExpr(expr.opcode, "(u32)(s32)(s8)(u8)");
+ break;
+
+ case Opcode::I32Extend16S:
+ WriteSimpleUnaryExpr(expr.opcode, "(u32)(s32)(s16)(u16)");
+ break;
+
+ case Opcode::I64Extend8S:
+ WriteSimpleUnaryExpr(expr.opcode, "(u64)(s64)(s8)(u8)");
+ break;
+
+ case Opcode::I64Extend16S:
+ WriteSimpleUnaryExpr(expr.opcode, "(u64)(s64)(s16)(u16)");
+ break;
+
+ case Opcode::I64Extend32S:
+ WriteSimpleUnaryExpr(expr.opcode, "(u64)(s64)(s32)(u32)");
+ break;
+
+ case Opcode::I8X16Abs:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i8x16_abs");
+ break;
+
+ case Opcode::I8X16AllTrue:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i8x16_all_true");
+ break;
+
+ case Opcode::I8X16Bitmask:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i8x16_bitmask");
+ break;
+
+ case Opcode::I8X16Neg:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i8x16_neg");
+ break;
+
+ case Opcode::I8X16Popcnt:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i8x16_popcnt");
+ break;
+
+ case Opcode::I8X16Splat:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i8x16_splat");
+ break;
+
+ case Opcode::I16X8Abs:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i16x8_abs");
+ break;
+
+ case Opcode::I16X8AllTrue:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i16x8_all_true");
+ break;
+
+ case Opcode::I16X8Bitmask:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i16x8_bitmask");
+ break;
+
+ case Opcode::I16X8ExtaddPairwiseI8X16S:
+ WriteSimpleUnaryExpr(expr.opcode,
+ "simde_wasm_i16x8_extadd_pairwise_i8x16");
+ break;
+
+ case Opcode::I16X8ExtaddPairwiseI8X16U:
+ WriteSimpleUnaryExpr(expr.opcode,
+ "simde_wasm_u16x8_extadd_pairwise_u8x16");
+ break;
+
+ case Opcode::I16X8ExtendHighI8X16S:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i16x8_extend_high_i8x16");
+ break;
+
+ case Opcode::I16X8ExtendHighI8X16U:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_u16x8_extend_high_u8x16");
+ break;
+
+ case Opcode::I16X8ExtendLowI8X16S:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i16x8_extend_low_i8x16");
+ break;
+
+ case Opcode::I16X8ExtendLowI8X16U:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_u16x8_extend_low_u8x16");
+ break;
+
+ case Opcode::I16X8Neg:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i16x8_neg");
+ break;
+
+ case Opcode::I16X8Splat:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i16x8_splat");
+ break;
+
+ case Opcode::I32X4Abs:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i32x4_abs");
+ break;
+
+ case Opcode::I32X4AllTrue:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i32x4_all_true");
+ break;
+
+ case Opcode::I32X4Bitmask:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i32x4_bitmask");
+ break;
+
+ case Opcode::I32X4ExtaddPairwiseI16X8S:
+ WriteSimpleUnaryExpr(expr.opcode,
+ "simde_wasm_i32x4_extadd_pairwise_i16x8");
+ break;
+
+ case Opcode::I32X4ExtaddPairwiseI16X8U:
+ WriteSimpleUnaryExpr(expr.opcode,
+ "simde_wasm_u32x4_extadd_pairwise_u16x8");
+ break;
+
+ case Opcode::I32X4ExtendHighI16X8S:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i32x4_extend_high_i16x8");
+ break;
+
+ case Opcode::I32X4ExtendHighI16X8U:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_u32x4_extend_high_u16x8");
+ break;
+
+ case Opcode::I32X4ExtendLowI16X8S:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i32x4_extend_low_i16x8");
+ break;
+
+ case Opcode::I32X4ExtendLowI16X8U:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_u32x4_extend_low_u16x8");
+ break;
+
+ case Opcode::I32X4Neg:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i32x4_neg");
+ break;
+
+ case Opcode::I32X4Splat:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i32x4_splat");
+ break;
+
+ case Opcode::I64X2Abs:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i64x2_abs");
+ break;
+
+ case Opcode::I64X2AllTrue:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i64x2_all_true");
+ break;
+
+ case Opcode::I64X2Bitmask:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i64x2_bitmask");
+ break;
+
+ case Opcode::I64X2ExtendHighI32X4S:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i64x2_extend_high_i32x4");
+ break;
+
+ case Opcode::I64X2ExtendHighI32X4U:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_u64x2_extend_high_u32x4");
+ break;
+
+ case Opcode::I64X2ExtendLowI32X4S:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i64x2_extend_low_i32x4");
+ break;
+
+ case Opcode::I64X2ExtendLowI32X4U:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_u64x2_extend_low_u32x4");
+ break;
+
+ case Opcode::I64X2Neg:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i64x2_neg");
+ break;
+
+ case Opcode::I64X2Splat:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i64x2_splat");
+ break;
+
+ case Opcode::F32X4Abs:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_abs");
+ break;
+
+ case Opcode::F32X4Ceil:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_ceil");
+ break;
+
+ case Opcode::F32X4Floor:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_floor");
+ break;
+
+ case Opcode::F32X4Nearest:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_nearest");
+ break;
+
+ case Opcode::F32X4Neg:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_neg");
+ break;
+
+ case Opcode::F32X4Splat:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_splat");
+ break;
+
+ case Opcode::F32X4Sqrt:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_sqrt");
+ break;
+
+ case Opcode::F32X4Trunc:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_trunc");
+ break;
+
+ case Opcode::F64X2Abs:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_abs");
+ break;
+
+ case Opcode::F64X2Ceil:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_ceil");
+ break;
+
+ case Opcode::F64X2Floor:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_floor");
+ break;
+
+ case Opcode::F64X2Nearest:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_nearest");
+ break;
+
+ case Opcode::F64X2Neg:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_neg");
+ break;
+
+ case Opcode::F64X2Splat:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_splat");
+ break;
+
+ case Opcode::F64X2Sqrt:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_sqrt");
+ break;
+
+ case Opcode::F64X2Trunc:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_trunc");
+ break;
+
+ case Opcode::V128AnyTrue:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_v128_any_true");
+ break;
+
+ case Opcode::V128Not:
+ WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_v128_not");
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const TernaryExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::V128BitSelect: {
+ Type result_type = expr.opcode.GetResultType();
+ Write(StackVar(2, result_type), " = ", "simde_wasm_v128_bitselect", "(",
+ StackVar(2), ", ", StackVar(1), ", ", StackVar(0), ");", Newline());
+ DropTypes(3);
+ PushType(result_type);
+ break;
+ }
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const SimdLaneOpExpr& expr) {
+ Type result_type = expr.opcode.GetResultType();
+
+ switch (expr.opcode) {
+ case Opcode::I8X16ExtractLaneS: {
+ Write(StackVar(0, result_type), " = simde_wasm_i8x16_extract_lane(",
+ StackVar(0), ", ", expr.val, ");", Newline());
+ DropTypes(1);
+ break;
+ }
+ case Opcode::I8X16ExtractLaneU: {
+ Write(StackVar(0, result_type), " = simde_wasm_u8x16_extract_lane(",
+ StackVar(0), ", ", expr.val, ");", Newline());
+ DropTypes(1);
+ break;
+ }
+ case Opcode::I16X8ExtractLaneS: {
+ Write(StackVar(0, result_type), " = simde_wasm_i16x8_extract_lane(",
+ StackVar(0), ", ", expr.val, ");", Newline());
+ DropTypes(1);
+ break;
+ }
+ case Opcode::I16X8ExtractLaneU: {
+ Write(StackVar(0, result_type), " = simde_wasm_u16x8_extract_lane(",
+ StackVar(0), ", ", expr.val, ");", Newline());
+ DropTypes(1);
+ break;
+ }
+ case Opcode::I32X4ExtractLane: {
+ Write(StackVar(0, result_type), " = simde_wasm_i32x4_extract_lane(",
+ StackVar(0), ", ", expr.val, ");", Newline());
+ DropTypes(1);
+ break;
+ }
+ case Opcode::I64X2ExtractLane: {
+ Write(StackVar(0, result_type), " = simde_wasm_i64x2_extract_lane(",
+ StackVar(0), ", ", expr.val, ");", Newline());
+ DropTypes(1);
+ break;
+ }
+ case Opcode::F32X4ExtractLane: {
+ Write(StackVar(0, result_type), " = simde_wasm_f32x4_extract_lane(",
+ StackVar(0), ", ", expr.val, ");", Newline());
+ DropTypes(1);
+ break;
+ }
+ case Opcode::F64X2ExtractLane: {
+ Write(StackVar(0, result_type), " = simde_wasm_f64x2_extract_lane(",
+ StackVar(0), ", ", expr.val, ");", Newline());
+ DropTypes(1);
+ break;
+ }
+ case Opcode::I8X16ReplaceLane: {
+ Write(StackVar(1, result_type), " = simde_wasm_i8x16_replace_lane(",
+ StackVar(1), ", ", expr.val, ", ", StackVar(0), ");", Newline());
+ DropTypes(2);
+ break;
+ }
+ case Opcode::I16X8ReplaceLane: {
+ Write(StackVar(1, result_type), " = simde_wasm_i16x8_replace_lane(",
+ StackVar(1), ", ", expr.val, ", ", StackVar(0), ");", Newline());
+ DropTypes(2);
+ break;
+ }
+ case Opcode::I32X4ReplaceLane: {
+ Write(StackVar(1, result_type), " = simde_wasm_i32x4_replace_lane(",
+ StackVar(1), ", ", expr.val, ", ", StackVar(0), ");", Newline());
+ DropTypes(2);
+ break;
+ }
+ case Opcode::I64X2ReplaceLane: {
+ Write(StackVar(1, result_type), " = simde_wasm_i64x2_replace_lane(",
+ StackVar(1), ", ", expr.val, ", ", StackVar(0), ");", Newline());
+ DropTypes(2);
+ break;
+ }
+ case Opcode::F32X4ReplaceLane: {
+ Write(StackVar(1, result_type), " = simde_wasm_f32x4_replace_lane(",
+ StackVar(1), ", ", expr.val, ", ", StackVar(0), ");", Newline());
+ DropTypes(2);
+ break;
+ }
+ case Opcode::F64X2ReplaceLane: {
+ Write(StackVar(1, result_type), " = simde_wasm_f64x2_replace_lane(",
+ StackVar(1), ", ", expr.val, ", ", StackVar(0), ");", Newline());
+ DropTypes(2);
+ break;
+ }
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ PushType(result_type);
+}
+
+void CWriter::Write(const SimdLoadLaneExpr& expr) {
+ const char* func = nullptr;
+ // clang-format off
+ switch (expr.opcode) {
+ case Opcode::V128Load8Lane: func = "v128_load8_lane"; break;
+ case Opcode::V128Load16Lane: func = "v128_load16_lane"; break;
+ case Opcode::V128Load32Lane: func = "v128_load32_lane"; break;
+ case Opcode::V128Load64Lane: func = "v128_load64_lane"; break;
+ default:
+ WABT_UNREACHABLE;
+ }
+ // clang-format on
+ Memory* memory = module_->memories[module_->GetMemoryIndex(expr.memidx)];
+ Type result_type = expr.opcode.GetResultType();
+ Write(StackVar(1, result_type), " = ", func, expr.val, "(",
+ ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", (u64)(",
+ StackVar(1), ")");
+
+ if (expr.offset != 0)
+ Write(" + ", expr.offset, "u");
+ Write(", ", StackVar(0));
+ Write(");", Newline());
+
+ DropTypes(2);
+ PushType(result_type);
+}
+
+void CWriter::Write(const SimdStoreLaneExpr& expr) {
+ const char* func = nullptr;
+ // clang-format off
+ switch (expr.opcode) {
+ case Opcode::V128Store8Lane: func = "v128_store8_lane"; break;
+ case Opcode::V128Store16Lane: func = "v128_store16_lane"; break;
+ case Opcode::V128Store32Lane: func = "v128_store32_lane"; break;
+ case Opcode::V128Store64Lane: func = "v128_store64_lane"; break;
+ default:
+ WABT_UNREACHABLE;
+ }
+ // clang-format on
+ Memory* memory = module_->memories[module_->GetMemoryIndex(expr.memidx)];
+
+ Write(func, expr.val, "(",
+ ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", (u64)(",
+ StackVar(1), ")");
+
+ if (expr.offset != 0)
+ Write(" + ", expr.offset, "u");
+ Write(", ", StackVar(0));
+ Write(");", Newline());
+
+ DropTypes(2);
+}
+
+void CWriter::Write(const SimdShuffleOpExpr& expr) {
+ Type result_type = expr.opcode.GetResultType();
+ switch (expr.opcode) {
+ case Opcode::I8X16Shuffle: {
+ Write(StackVar(1, result_type), " = simde_wasm_i8x16_shuffle(",
+ StackVar(1), ", ", StackVar(0), ", ", expr.val.u8(0), ", ",
+ expr.val.u8(1), ", ", expr.val.u8(2), ", ", expr.val.u8(3), ", ",
+ expr.val.u8(4), ", ", expr.val.u8(5), ", ", expr.val.u8(6), ", ",
+ expr.val.u8(7), ", ", expr.val.u8(8), ", ", expr.val.u8(9), ", ",
+ expr.val.u8(10), ", ", expr.val.u8(11), ", ", expr.val.u8(12), ", ",
+ expr.val.u8(13), ", ", expr.val.u8(14), ", ", expr.val.u8(15), ");",
+ Newline());
+ DropTypes(2);
+ break;
+ }
+ default:
+ WABT_UNREACHABLE;
+ }
+ PushType(result_type);
+}
+
+void CWriter::Write(const LoadSplatExpr& expr) {
+ Memory* memory = module_->memories[module_->GetMemoryIndex(expr.memidx)];
+
+ const char* func = nullptr;
+ // clang-format off
+ switch (expr.opcode) {
+ case Opcode::V128Load8Splat: func = "v128_load8_splat"; break;
+ case Opcode::V128Load16Splat: func = "v128_load16_splat"; break;
+ case Opcode::V128Load32Splat: func = "v128_load32_splat"; break;
+ case Opcode::V128Load64Splat: func = "v128_load64_splat"; break;
+ default:
+ WABT_UNREACHABLE;
+ }
+ // clang-format on
+ Type result_type = expr.opcode.GetResultType();
+ Write(StackVar(0, result_type), " = ", func, "(",
+ ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", (u64)(",
+ StackVar(0), ")");
+ if (expr.offset != 0)
+ Write(" + ", expr.offset);
+ Write(");", Newline());
+
+ DropTypes(1);
+ PushType(result_type);
+}
+
+void CWriter::Write(const LoadZeroExpr& expr) {
+ Memory* memory = module_->memories[module_->GetMemoryIndex(expr.memidx)];
+
+ const char* func = nullptr;
+ // clang-format off
+ switch (expr.opcode) {
+ case Opcode::V128Load32Zero: func = "v128_load32_zero"; break;
+ case Opcode::V128Load64Zero: func = "v128_load64_zero"; break;
+ default:
+ WABT_UNREACHABLE;
+ }
+ // clang-format on
+
+ Type result_type = expr.opcode.GetResultType();
+ Write(StackVar(0, result_type), " = ", func, "(",
+ ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", (u64)(",
+ StackVar(0), ")");
+ if (expr.offset != 0)
+ Write(" + ", expr.offset);
+ Write(");", Newline());
+
+ DropTypes(1);
+ PushType(result_type);
+}
+
+void CWriter::ReserveExportNames() {
+ for (const Export* export_ : module_->exports) {
+ ReserveExportName(export_->name);
+ }
+}
+
+void CWriter::WriteCHeader() {
+ ReserveExportNames();
+
+ stream_ = h_stream_;
+ std::string guard = GenerateHeaderGuard();
+ Write("/* Automatically generated by wasm2c */", Newline());
+ Write("#ifndef ", guard, Newline());
+ Write("#define ", guard, Newline());
+ Write(Newline());
+ ComputeSimdScope();
+ WriteHeaderIncludes();
+ Write(s_header_top);
+ Write(Newline());
+ WriteModuleInstance();
+ WriteInitDecl();
+ WriteFreeDecl();
+ WriteGetFuncTypeDecl();
+ WriteMultivalueTypes();
+ WriteImports();
+ WriteImportProperties(CWriterPhase::Declarations);
+ WriteExports(CWriterPhase::Declarations);
+ Write(Newline());
+ Write(s_header_bottom);
+ Write(Newline(), "#endif /* ", guard, " */", Newline());
+}
+
+void CWriter::WriteCSource() {
+ /* Write the "top" to h_impl stream */
+ stream_ = h_impl_stream_;
+ Write("/* Automatically generated by wasm2c */", Newline());
+ WriteSourceTop();
+
+ /* Write module-wide declarations to impl header */
+ WriteFuncTypeDecls();
+ WriteTagTypes();
+ WriteTagDecls();
+ WriteFuncDeclarations();
+ WriteDataInitializerDecls();
+ WriteElemInitializerDecls();
+
+ /* Write the module-wide material to the first output stream */
+ stream_ = c_streams_.front();
+ WriteMultiCTop();
+ WriteFuncTypes();
+ WriteTags();
+ WriteGlobalInitializers();
+ WriteDataInitializers();
+ WriteElemInitializers();
+ WriteExports(CWriterPhase::Definitions);
+ WriteInitInstanceImport();
+ WriteImportProperties(CWriterPhase::Definitions);
+ WriteInit();
+ WriteFree();
+ WriteGetFuncType();
+
+ /* Write function bodies across the different output streams */
+ WriteFuncs();
+
+ /* For any empty .c output, write a dummy typedef to avoid gcc warning */
+ WriteMultiCTopEmpty();
+}
+
+Result CWriter::WriteModule(const Module& module) {
+ WABT_USE(options_);
+ module_ = &module;
+ WriteCHeader();
+ WriteCSource();
+ return result_;
+}
+
+// static
+const char* CWriter::GetReferenceTypeName(const Type& type) {
+ switch (type) {
+ case Type::FuncRef:
+ return "funcref";
+ case Type::ExternRef:
+ return "externref";
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+// static
+const char* CWriter::GetReferenceNullValue(const Type& type) {
+ switch (type) {
+ case Type::FuncRef:
+ return "wasm_rt_funcref_null_value";
+ case Type::ExternRef:
+ return "wasm_rt_externref_null_value";
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+const char* CWriter::InternalSymbolScope() const {
+ if (c_streams_.size() == 1) {
+ return "static ";
+ } else {
+ return "";
+ }
+}
+
+} // end anonymous namespace
+
+Result WriteC(std::vector<Stream*>&& c_streams,
+ Stream* h_stream,
+ Stream* h_impl_stream,
+ const char* header_name,
+ const char* header_impl_name,
+ const Module* module,
+ const WriteCOptions& options) {
+ CWriter c_writer(std::move(c_streams), h_stream, h_impl_stream, header_name,
+ header_impl_name, options);
+ return c_writer.WriteModule(*module);
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/color.cc b/third_party/wasm2c/src/color.cc
new file mode 100644
index 0000000000..66b8555ecc
--- /dev/null
+++ b/third_party/wasm2c/src/color.cc
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/color.h"
+
+#include <cstdlib>
+
+#include "wabt/common.h"
+
+#if HAVE_WIN32_VT100
+#include <io.h>
+#include <windows.h>
+#elif HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+namespace wabt {
+
+Color::Color(FILE* file, bool enabled) : file_(file) {
+ enabled_ = enabled && SupportsColor(file_);
+}
+
+// static
+bool Color::SupportsColor(FILE* file) {
+ char* force = getenv("FORCE_COLOR");
+ if (force) {
+ return atoi(force) != 0;
+ }
+
+#if HAVE_WIN32_VT100
+
+ HANDLE handle;
+ if (file == stdout) {
+ handle = GetStdHandle(STD_OUTPUT_HANDLE);
+ } else if (file == stderr) {
+ handle = GetStdHandle(STD_ERROR_HANDLE);
+ } else {
+ return false;
+ }
+ DWORD mode;
+ if (!_isatty(_fileno(file)) || !GetConsoleMode(handle, &mode) ||
+ !SetConsoleMode(handle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) {
+ return false;
+ }
+ return true;
+ // TODO(binji): Support older Windows by using SetConsoleTextAttribute?
+
+#elif HAVE_UNISTD_H
+
+ return isatty(fileno(file));
+
+#else
+
+ return false;
+
+#endif
+}
+
+void Color::WriteCode(const char* code) const {
+ if (enabled_) {
+ fputs(code, file_);
+ }
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/common.cc b/third_party/wasm2c/src/common.cc
new file mode 100644
index 0000000000..037b8304bd
--- /dev/null
+++ b/third_party/wasm2c/src/common.cc
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/common.h"
+
+#include <cassert>
+#include <cerrno>
+#include <climits>
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#if COMPILER_IS_MSVC
+#include <fcntl.h>
+#include <io.h>
+#include <stdlib.h>
+#define PATH_MAX _MAX_PATH
+#define stat _stat
+#define S_IFREG _S_IFREG
+#endif
+
+namespace wabt {
+
+Reloc::Reloc(RelocType type, Offset offset, Index index, int32_t addend)
+ : type(type), offset(offset), index(index), addend(addend) {}
+
+const char* g_kind_name[] = {"func", "table", "memory", "global", "tag"};
+WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(g_kind_name) == kExternalKindCount);
+
+const char* g_reloc_type_name[] = {
+ "R_WASM_FUNCTION_INDEX_LEB", "R_WASM_TABLE_INDEX_SLEB",
+ "R_WASM_TABLE_INDEX_I32", "R_WASM_MEMORY_ADDR_LEB",
+ "R_WASM_MEMORY_ADDR_SLEB", "R_WASM_MEMORY_ADDR_I32",
+ "R_WASM_TYPE_INDEX_LEB", "R_WASM_GLOBAL_INDEX_LEB",
+ "R_WASM_FUNCTION_OFFSET_I32", "R_WASM_SECTION_OFFSET_I32",
+ "R_WASM_TAG_INDEX_LEB", "R_WASM_MEMORY_ADDR_REL_SLEB",
+ "R_WASM_TABLE_INDEX_REL_SLEB", "R_WASM_GLOBAL_INDEX_I32",
+ "R_WASM_MEMORY_ADDR_LEB64", "R_WASM_MEMORY_ADDR_SLEB64",
+ "R_WASM_MEMORY_ADDR_I64", "R_WASM_MEMORY_ADDR_REL_SLEB64",
+ "R_WASM_TABLE_INDEX_SLEB64", "R_WASM_TABLE_INDEX_I64",
+ "R_WASM_TABLE_NUMBER_LEB", "R_WASM_MEMORY_ADDR_TLS_SLEB",
+ "R_WASM_MEMORY_ADDR_TLS_I32",
+};
+WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(g_reloc_type_name) == kRelocTypeCount);
+
+static Result ReadStdin(std::vector<uint8_t>* out_data) {
+ out_data->resize(0);
+ uint8_t buffer[4096];
+ while (true) {
+ size_t bytes_read = fread(buffer, 1, sizeof(buffer), stdin);
+ if (bytes_read == 0) {
+ if (ferror(stdin)) {
+ fprintf(stderr, "error reading from stdin: %s\n", strerror(errno));
+ return Result::Error;
+ }
+ return Result::Ok;
+ }
+ size_t old_size = out_data->size();
+ out_data->resize(old_size + bytes_read);
+ memcpy(out_data->data() + old_size, buffer, bytes_read);
+ }
+}
+
+Result ReadFile(std::string_view filename, std::vector<uint8_t>* out_data) {
+ std::string filename_str(filename);
+ const char* filename_cstr = filename_str.c_str();
+
+ if (filename == "-") {
+ return ReadStdin(out_data);
+ }
+
+ struct stat statbuf;
+ if (stat(filename_cstr, &statbuf) < 0) {
+ fprintf(stderr, "%s: %s\n", filename_cstr, strerror(errno));
+ return Result::Error;
+ }
+
+ if (!(statbuf.st_mode & S_IFREG)) {
+ fprintf(stderr, "%s: not a regular file\n", filename_cstr);
+ return Result::Error;
+ }
+
+ FILE* infile = fopen(filename_cstr, "rb");
+ if (!infile) {
+ fprintf(stderr, "%s: %s\n", filename_cstr, strerror(errno));
+ return Result::Error;
+ }
+
+ if (fseek(infile, 0, SEEK_END) < 0) {
+ perror("fseek to end failed");
+ fclose(infile);
+ return Result::Error;
+ }
+
+ long size = ftell(infile);
+ if (size < 0) {
+ perror("ftell failed");
+ fclose(infile);
+ return Result::Error;
+ }
+
+ if (fseek(infile, 0, SEEK_SET) < 0) {
+ perror("fseek to beginning failed");
+ fclose(infile);
+ return Result::Error;
+ }
+
+ out_data->resize(size);
+ if (size != 0 && fread(out_data->data(), size, 1, infile) != 1) {
+ fprintf(stderr, "%s: fread failed: %s\n", filename_cstr, strerror(errno));
+ fclose(infile);
+ return Result::Error;
+ }
+
+ fclose(infile);
+ return Result::Ok;
+}
+
+void InitStdio() {
+#if COMPILER_IS_MSVC
+ int result = _setmode(_fileno(stdout), _O_BINARY);
+ if (result == -1) {
+ perror("Cannot set mode binary to stdout");
+ }
+ result = _setmode(_fileno(stderr), _O_BINARY);
+ if (result == -1) {
+ perror("Cannot set mode binary to stderr");
+ }
+#endif
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/config.cc b/third_party/wasm2c/src/config.cc
new file mode 100644
index 0000000000..ccf80e68ea
--- /dev/null
+++ b/third_party/wasm2c/src/config.cc
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/config.h"
+
+#include <cstdarg>
+#include <cstdio>
+
+#if COMPILER_IS_MSVC && _M_X64
+#include <emmintrin.h>
+#elif COMPILER_IS_MSVC && _M_IX86
+#include <float.h>
+#endif
+
+/* c99-style vsnprintf for MSVC < 2015. See http://stackoverflow.com/a/8712996
+ using _snprintf or vsnprintf will not-properly null-terminate, and will return
+ -1 instead of the number of characters needed on overflow. */
+#if COMPILER_IS_MSVC
+int wabt_vsnprintf(char* str, size_t size, const char* format, va_list ap) {
+ int result = -1;
+ if (size != 0) {
+ result = _vsnprintf_s(str, size, _TRUNCATE, format, ap);
+ }
+ if (result == -1) {
+ result = _vscprintf(format, ap);
+ }
+ return result;
+}
+
+#if !HAVE_SNPRINTF
+int wabt_snprintf(char* str, size_t size, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ int result = wabt_vsnprintf(str, size, format, args);
+ va_end(args);
+ return result;
+}
+#endif
+#endif
+
+#if COMPILER_IS_MSVC && _M_IX86
+// Allow the following functions to change the floating-point environment (e.g.
+// update to 64-bit precision in the mantissa). This is only needed for x87
+// floats, which are only used on MSVC 32-bit.
+#pragma fenv_access(on)
+namespace {
+
+typedef unsigned int FPControl;
+
+FPControl Set64BitPrecisionControl() {
+ FPControl old_ctrl = _control87(0, 0);
+ _control87(_PC_64, _MCW_PC);
+ return old_ctrl;
+}
+
+void ResetPrecisionControl(FPControl old_ctrl) {
+ _control87(old_ctrl, _MCW_PC);
+}
+
+} // end of anonymous namespace
+#endif
+
+double wabt_convert_uint64_to_double(uint64_t x) {
+#if COMPILER_IS_MSVC && _M_X64
+ // MSVC on x64 generates uint64 -> float conversions but doesn't do
+ // round-to-nearest-ties-to-even, which is required by WebAssembly.
+ __m128d result = _mm_setzero_pd();
+ if (x & 0x8000000000000000ULL) {
+ result = _mm_cvtsi64_sd(result, (x >> 1) | (x & 1));
+ result = _mm_add_sd(result, result);
+ } else {
+ result = _mm_cvtsi64_sd(result, x);
+ }
+ return _mm_cvtsd_f64(result);
+#elif COMPILER_IS_MSVC && _M_IX86
+ // MSVC on x86 converts from i64 -> double -> float, which causes incorrect
+ // rounding. Using the x87 float stack instead preserves the correct
+ // rounding.
+ FPControl old_ctrl = Set64BitPrecisionControl();
+ static const double c = 18446744073709551616.0;
+ double result;
+ __asm fild x;
+ if (x & 0x8000000000000000ULL) {
+ __asm fadd c;
+ }
+ __asm fstp result;
+ ResetPrecisionControl(old_ctrl);
+ return result;
+#else
+ return static_cast<double>(x);
+#endif
+}
+
+float wabt_convert_uint64_to_float(uint64_t x) {
+#if COMPILER_IS_MSVC && _M_X64
+ // MSVC on x64 generates uint64 -> float conversions but doesn't do
+ // round-to-nearest-ties-to-even, which is required by WebAssembly.
+ __m128 result = _mm_setzero_ps();
+ if (x & 0x8000000000000000ULL) {
+ result = _mm_cvtsi64_ss(result, (x >> 1) | (x & 1));
+ result = _mm_add_ss(result, result);
+ } else {
+ result = _mm_cvtsi64_ss(result, x);
+ }
+ return _mm_cvtss_f32(result);
+#elif COMPILER_IS_MSVC && _M_IX86
+ // MSVC on x86 converts from i64 -> double -> float, which causes incorrect
+ // rounding. Using the x87 float stack instead preserves the correct
+ // rounding.
+ FPControl old_ctrl = Set64BitPrecisionControl();
+ static const float c = 18446744073709551616.0f;
+ float result;
+ __asm fild x;
+ if (x & 0x8000000000000000ULL) {
+ __asm fadd c;
+ }
+ __asm fstp result;
+ ResetPrecisionControl(old_ctrl);
+ return result;
+#else
+ return static_cast<float>(x);
+#endif
+}
+
+double wabt_convert_int64_to_double(int64_t x) {
+#if COMPILER_IS_MSVC && _M_IX86
+ double result;
+ __asm fild x;
+ __asm fstp result;
+ return result;
+#else
+ return static_cast<double>(x);
+#endif
+}
+
+float wabt_convert_int64_to_float(int64_t x) {
+#if COMPILER_IS_MSVC && _M_IX86
+ float result;
+ __asm fild x;
+ __asm fstp result;
+ return result;
+#else
+ return static_cast<float>(x);
+#endif
+}
+
+#if COMPILER_IS_MSVC && _M_IX86
+#pragma fenv_access(off)
+#endif
diff --git a/third_party/wasm2c/src/config.h.in b/third_party/wasm2c/src/config.h.in
new file mode 100644
index 0000000000..f7187c3228
--- /dev/null
+++ b/third_party/wasm2c/src/config.h.in
@@ -0,0 +1,332 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WABT_CONFIG_H_
+#define WABT_CONFIG_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#cmakedefine WABT_VERSION_STRING "@WABT_VERSION_STRING@"
+
+#cmakedefine WABT_DEBUG @WABT_DEBUG@
+
+/* TODO(binji): nice way to define these with WABT_ prefix? */
+
+/* Whether <alloca.h> is available */
+#cmakedefine01 HAVE_ALLOCA_H
+
+/* Whether <unistd.h> is available */
+#cmakedefine01 HAVE_UNISTD_H
+
+/* Whether snprintf is defined by stdio.h */
+#cmakedefine01 HAVE_SNPRINTF
+
+/* Whether ssize_t is defined by stddef.h */
+#cmakedefine01 HAVE_SSIZE_T
+
+/* Whether strcasecmp is defined by strings.h */
+#cmakedefine01 HAVE_STRCASECMP
+
+/* Whether ENABLE_VIRTUAL_TERMINAL_PROCESSING is defined by windows.h */
+#cmakedefine01 HAVE_WIN32_VT100
+
+/* Whether the target architecture is big endian */
+#cmakedefine01 WABT_BIG_ENDIAN
+
+/* Whether <openssl/sha.h> is available */
+#cmakedefine01 HAVE_OPENSSL_SHA_H
+
+#cmakedefine01 COMPILER_IS_CLANG
+#cmakedefine01 COMPILER_IS_GNU
+#cmakedefine01 COMPILER_IS_MSVC
+
+#cmakedefine01 WITH_EXCEPTIONS
+
+#define SIZEOF_SIZE_T @SIZEOF_SIZE_T@
+
+#if HAVE_ALLOCA_H
+#include <alloca.h>
+#elif COMPILER_IS_MSVC
+#include <malloc.h>
+#define alloca _alloca
+#elif defined(__MINGW32__)
+#include <malloc.h>
+#endif
+
+#if COMPILER_IS_CLANG || COMPILER_IS_GNU
+
+#define WABT_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#define WABT_LIKELY(x) __builtin_expect(!!(x), 1)
+
+#define WABT_VECTORCALL
+
+#if __MINGW32__
+// mingw defaults to printf format specifier being ms_printf (which doesn't
+// understand 'llu', etc.) We always want gnu_printf, and force mingw to always
+// use mingw_printf, mingw_vprintf, etc.
+#define WABT_PRINTF_FORMAT(format_arg, first_arg) \
+ __attribute__((format(gnu_printf, (format_arg), (first_arg))))
+#else
+#define WABT_PRINTF_FORMAT(format_arg, first_arg) \
+ __attribute__((format(printf, (format_arg), (first_arg))))
+#endif
+
+#ifdef __cplusplus
+#define WABT_STATIC_ASSERT(x) static_assert((x), #x)
+#else
+#define WABT_STATIC_ASSERT(x) _Static_assert((x), #x)
+#endif
+
+#elif COMPILER_IS_MSVC
+
+#include <intrin.h>
+#include <string.h>
+
+#define WABT_STATIC_ASSERT(x) _STATIC_ASSERT(x)
+#define WABT_UNLIKELY(x) (x)
+#define WABT_LIKELY(x) (x)
+#define WABT_PRINTF_FORMAT(format_arg, first_arg)
+
+#define WABT_VECTORCALL __vectorcall
+
+#else
+
+#error unknown compiler
+
+#endif
+
+#define WABT_UNREACHABLE abort()
+
+#ifdef __cplusplus
+
+namespace wabt {
+
+#if COMPILER_IS_CLANG || COMPILER_IS_GNU
+
+inline int Clz(unsigned x) { return x ? __builtin_clz(x) : sizeof(x) * 8; }
+inline int Clz(unsigned long x) { return x ? __builtin_clzl(x) : sizeof(x) * 8; }
+inline int Clz(unsigned long long x) { return x ? __builtin_clzll(x) : sizeof(x) * 8; }
+
+inline int Ctz(unsigned x) { return x ? __builtin_ctz(x) : sizeof(x) * 8; }
+inline int Ctz(unsigned long x) { return x ? __builtin_ctzl(x) : sizeof(x) * 8; }
+inline int Ctz(unsigned long long x) { return x ? __builtin_ctzll(x) : sizeof(x) * 8; }
+
+inline int Popcount(uint8_t x) { return __builtin_popcount(x); }
+inline int Popcount(unsigned x) { return __builtin_popcount(x); }
+inline int Popcount(unsigned long x) { return __builtin_popcountl(x); }
+inline int Popcount(unsigned long long x) { return __builtin_popcountll(x); }
+
+#elif COMPILER_IS_MSVC
+
+#if _M_IX86
+inline unsigned long LowDword(unsigned __int64 value) {
+ return (unsigned long)value;
+}
+
+inline unsigned long HighDword(unsigned __int64 value) {
+ unsigned long high;
+ memcpy(&high, (unsigned char*)&value + sizeof(high), sizeof(high));
+ return high;
+}
+#endif
+
+inline int Clz(unsigned long mask) {
+ if (mask == 0)
+ return 32;
+
+ unsigned long index;
+ _BitScanReverse(&index, mask);
+ return sizeof(unsigned long) * 8 - (index + 1);
+}
+
+inline int Clz(unsigned int mask) {
+ return Clz((unsigned long)mask);
+}
+
+inline int Clz(unsigned __int64 mask) {
+#if _M_X64 || _M_ARM64
+ if (mask == 0)
+ return 64;
+
+ unsigned long index;
+ _BitScanReverse64(&index, mask);
+ return sizeof(unsigned __int64) * 8 - (index + 1);
+#elif _M_IX86
+ int result = Clz(HighDword(mask));
+ if (result == 32)
+ result += Clz(LowDword(mask));
+
+ return result;
+#else
+#error unexpected architecture
+#endif
+}
+
+inline int Ctz(unsigned long mask) {
+ if (mask == 0)
+ return 32;
+
+ unsigned long index;
+ _BitScanForward(&index, mask);
+ return index;
+}
+
+inline int Ctz(unsigned int mask) {
+ return Ctz((unsigned long)mask);
+}
+
+inline int Ctz(unsigned __int64 mask) {
+#if _M_X64 || _M_ARM64
+ if (mask == 0)
+ return 64;
+
+ unsigned long index;
+ _BitScanForward64(&index, mask);
+ return index;
+#elif _M_IX86
+ int result = Ctz(LowDword(mask));
+ if (result == 32)
+ result += Ctz(HighDword(mask));
+
+ return result;
+#else
+#error unexpected architecture
+#endif
+}
+
+#if _M_ARM64
+//https://stackoverflow.com/a/70012905
+template <typename T>
+int BrianKernighanPopcount(T value) {
+ int count;
+ for(count = 0; value; count++)
+ {
+ value &= value - 1;
+ }
+ return count;
+}
+#endif
+
+inline int Popcount(unsigned long value) {
+ #if _M_X64 || _M_IX86
+ return __popcnt(value);
+#elif _M_ARM64
+ return BrianKernighanPopcount(value);
+#else
+#error unexpected architecture
+#endif
+}
+
+inline int Popcount(uint8_t value) {
+ return Popcount((unsigned long)value);
+}
+
+inline int Popcount(unsigned int value) {
+ return Popcount((unsigned long)value);
+}
+
+inline int Popcount(unsigned __int64 value) {
+#if _M_X64
+ return __popcnt64(value);
+#elif _M_IX86
+ return Popcount(HighDword(value)) + Popcount(LowDword(value));
+#elif _M_ARM64
+ return BrianKernighanPopcount(value);
+#else
+#error unexpected architecture
+#endif
+}
+
+#else
+
+#error unknown compiler
+
+#endif
+
+} // namespace wabt
+
+#if COMPILER_IS_MSVC
+
+/* print format specifier for size_t */
+#if SIZEOF_SIZE_T == 4
+#define PRIzd "d"
+#define PRIzx "x"
+#elif SIZEOF_SIZE_T == 8
+#define PRIzd "I64d"
+#define PRIzx "I64x"
+#else
+#error "weird sizeof size_t"
+#endif
+
+#elif COMPILER_IS_CLANG || COMPILER_IS_GNU
+
+/* print format specifier for size_t */
+#define PRIzd "zd"
+#define PRIzx "zx"
+
+#else
+
+#error unknown compiler
+
+#endif
+
+#if HAVE_SNPRINTF
+#define wabt_snprintf snprintf
+#elif COMPILER_IS_MSVC
+/* can't just use _snprintf because it doesn't always null terminate */
+#include <cstdarg>
+int wabt_snprintf(char* str, size_t size, const char* format, ...);
+#else
+#error no snprintf
+#endif
+
+#if COMPILER_IS_MSVC
+/* can't just use vsnprintf because it doesn't always null terminate */
+int wabt_vsnprintf(char* str, size_t size, const char* format, va_list ap);
+#else
+#define wabt_vsnprintf vsnprintf
+#endif
+
+#if !HAVE_SSIZE_T
+#if COMPILER_IS_MSVC
+/* define ssize_t identically to how LLVM does, to avoid conflicts if including both */
+#if defined(_WIN64)
+typedef signed __int64 ssize_t;
+#else
+typedef signed int ssize_t;
+#endif /* _WIN64 */
+#else
+typedef long ssize_t;
+#endif
+#endif
+
+#if !HAVE_STRCASECMP
+#if COMPILER_IS_MSVC
+#define strcasecmp _stricmp
+#else
+#error no strcasecmp
+#endif
+#endif
+
+double wabt_convert_uint64_to_double(uint64_t x);
+float wabt_convert_uint64_to_float(uint64_t x);
+double wabt_convert_int64_to_double(int64_t x);
+float wabt_convert_int64_to_float(int64_t x);
+
+#endif // __cplusplus
+
+#endif /* WABT_CONFIG_H_ */
diff --git a/third_party/wasm2c/src/decompiler.cc b/third_party/wasm2c/src/decompiler.cc
new file mode 100644
index 0000000000..dd8eb76701
--- /dev/null
+++ b/third_party/wasm2c/src/decompiler.cc
@@ -0,0 +1,857 @@
+/*
+ * Copyright 2019 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/decompiler.h"
+
+#include "wabt/decompiler-ast.h"
+#include "wabt/decompiler-ls.h"
+#include "wabt/decompiler-naming.h"
+
+#include "wabt/stream.h"
+
+#define WABT_TRACING 0
+#include "wabt/tracing.h"
+
+#include <inttypes.h>
+
+namespace wabt {
+
+struct Decompiler {
+ Decompiler(const Module& module, const DecompileOptions& options)
+ : mc(module), options(options) {}
+
+ // Sorted such that "greater precedence" is also the bigger enum val.
+ enum Precedence {
+ // Low precedence.
+ None, // precedence doesn't matter, since never nested.
+ Assign, // =
+ OtherBin, // min
+ Bit, // & |
+ Equal, // == != < > >= <=
+ Shift, // << >>
+ Add, // + -
+ Multiply, // * / %
+ If, // if{}
+ Indexing, // []
+ Atomic, // (), a, 1, a()
+ // High precedence.
+ };
+
+ // Anything besides these will get parentheses if used with equal precedence,
+ // for clarity.
+ bool Associative(Precedence p) {
+ return p == Precedence::Add || p == Precedence::Multiply;
+ }
+
+ struct Value {
+ std::vector<std::string> v;
+ // Lazily add bracketing only if the parent requires it.
+ // This is the precedence level of this value, for example, if this
+ // precedence is Add, and the parent is Multiply, bracketing is needed,
+ // but not if it is the reverse.
+ Precedence precedence;
+
+ Value(std::vector<std::string>&& v, Precedence p) : v(v), precedence(p) {}
+
+ size_t width() {
+ size_t w = 0;
+ for (auto& s : v) {
+ w = std::max(w, s.size());
+ }
+ return w;
+ }
+
+ // This value should really never be copied, only moved.
+ Value(Value&& rhs) = default;
+ Value(const Value& rhs) = delete;
+ Value& operator=(Value&& rhs) = default;
+ Value& operator=(const Value& rhs) = delete;
+ };
+
+ std::string to_string(double d) {
+ auto s = std::to_string(d);
+ // Remove redundant trailing '0's that to_string produces.
+ while (s.size() > 2 && s.back() == '0' && s[s.size() - 2] != '.')
+ s.pop_back();
+ return s;
+ }
+
+ std::string Indent(size_t amount) { return std::string(amount, ' '); }
+
+ std::string OpcodeToToken(Opcode opcode) {
+ std::string s = opcode.GetDecomp();
+ std::replace(s.begin(), s.end(), '.', '_');
+ return s;
+ }
+
+ void IndentValue(Value& val, size_t amount, std::string_view first_indent) {
+ auto indent = Indent(amount);
+ for (auto& stat : val.v) {
+ auto is = (&stat != &val.v[0] || first_indent.empty())
+ ? std::string_view(indent)
+ : first_indent;
+ stat.insert(0, is.data(), is.size());
+ }
+ }
+
+ Value WrapChild(Value& child,
+ std::string_view prefix,
+ std::string_view postfix,
+ Precedence precedence) {
+ auto width = prefix.size() + postfix.size() + child.width();
+ auto& v = child.v;
+ if (width < target_exp_width ||
+ (prefix.size() <= indent_amount && postfix.size() <= indent_amount)) {
+ if (v.size() == 1) {
+ // Fits in a single line.
+ v[0].insert(0, prefix.data(), prefix.size());
+ v[0].append(postfix.data(), postfix.size());
+ } else {
+ // Multiline, but with prefix on same line.
+ IndentValue(child, prefix.size(), prefix);
+ v.back().append(postfix.data(), postfix.size());
+ }
+ } else {
+ // Multiline with prefix on its own line.
+ IndentValue(child, indent_amount, {});
+ v.insert(v.begin(), std::string(prefix));
+ v.back().append(postfix.data(), postfix.size());
+ }
+ child.precedence = precedence;
+ return std::move(child);
+ }
+
+ void BracketIfNeeded(Value& val, Precedence parent_precedence) {
+ if (parent_precedence < val.precedence ||
+ (parent_precedence == val.precedence &&
+ Associative(parent_precedence))) {
+ return;
+ }
+ val = WrapChild(val, "(", ")", Precedence::Atomic);
+ }
+
+ Value WrapBinary(std::vector<Value>& args,
+ std::string_view infix,
+ bool indent_right,
+ Precedence precedence) {
+ assert(args.size() == 2);
+ auto& left = args[0];
+ auto& right = args[1];
+ BracketIfNeeded(left, precedence);
+ BracketIfNeeded(right, precedence);
+ auto width = infix.size() + left.width() + right.width() + 2;
+ if (width < target_exp_width && left.v.size() == 1 && right.v.size() == 1) {
+ return Value{{cat(left.v[0], " ", infix, " ", right.v[0])}, precedence};
+ } else {
+ Value bin{{}, precedence};
+ std::move(left.v.begin(), left.v.end(), std::back_inserter(bin.v));
+ bin.v.back().append(" ", 1);
+ bin.v.back().append(infix.data(), infix.size());
+ if (indent_right) {
+ IndentValue(right, indent_amount, {});
+ }
+ std::move(right.v.begin(), right.v.end(), std::back_inserter(bin.v));
+ return bin;
+ }
+ }
+
+ Value WrapNAry(std::vector<Value>& args,
+ std::string_view prefix,
+ std::string_view postfix,
+ Precedence precedence) {
+ size_t total_width = 0;
+ size_t max_width = 0;
+ bool multiline = false;
+ for (auto& child : args) {
+ auto w = child.width();
+ max_width = std::max(max_width, w);
+ total_width += w;
+ multiline = multiline || child.v.size() > 1;
+ }
+ if (!multiline &&
+ (total_width + prefix.size() + postfix.size() < target_exp_width ||
+ args.empty())) {
+ // Single line.
+ auto s = std::string(prefix);
+ for (auto& child : args) {
+ if (&child != &args[0])
+ s += ", ";
+ s += child.v[0];
+ }
+ s += postfix;
+ return Value{{std::move(s)}, precedence};
+ } else {
+ // Multi-line.
+ Value ml{{}, precedence};
+ auto ident_with_name = max_width + prefix.size() < target_exp_width;
+ size_t i = 0;
+ for (auto& child : args) {
+ IndentValue(child, ident_with_name ? prefix.size() : indent_amount,
+ !i && ident_with_name ? prefix : std::string_view{});
+ if (i < args.size() - 1) {
+ child.v.back() += ",";
+ }
+ std::move(child.v.begin(), child.v.end(), std::back_inserter(ml.v));
+ i++;
+ }
+ if (!ident_with_name) {
+ ml.v.insert(ml.v.begin(), std::string(prefix));
+ }
+ ml.v.back() += postfix;
+ return ml;
+ }
+ }
+
+ std::string_view VarName(std::string_view name) {
+ assert(!name.empty());
+ return name[0] == '$' ? name.substr(1) : name;
+ }
+
+ template <ExprType T>
+ Value Get(const VarExpr<T>& ve) {
+ return Value{{std::string(VarName(ve.var.name()))}, Precedence::Atomic};
+ }
+
+ template <ExprType T>
+ Value Set(Value& child, const VarExpr<T>& ve) {
+ return WrapChild(child, VarName(ve.var.name()) + " = ", "",
+ Precedence::Assign);
+ }
+
+ std::string TempVarName(Index n) {
+ // FIXME: this needs much better variable naming. Problem is, the code
+ // in generate-names.cc has allready run, its dictionaries deleted, so it
+ // is not easy to integrate with it.
+ return "t" + std::to_string(n);
+ }
+
+ std::string LocalDecl(const std::string& name, Type t) {
+ auto struc = lst.GenTypeDecl(name);
+ return cat(VarName(name), ":",
+ struc.empty() ? GetDecompTypeName(t) : struc);
+ }
+
+ bool ConstIntVal(const Expr* e, uint64_t& dest) {
+ dest = 0;
+ if (!e || e->type() != ExprType::Const) {
+ return false;
+ }
+ auto& c = cast<ConstExpr>(e)->const_;
+ if (c.type() != Type::I32 && c.type() != Type::I64) {
+ return false;
+ }
+ dest = c.type() == Type::I32 ? c.u32() : c.u64();
+ return true;
+ }
+
+ void LoadStore(Value& val,
+ const Node& addr_exp,
+ uint64_t offset,
+ Opcode opc,
+ Address align,
+ Type op_type) {
+ bool append_type = true;
+ auto access = lst.GenAccess(offset, addr_exp);
+ if (!access.empty()) {
+ if (access == "*") {
+ // The variable was declared as a typed pointer, so this access
+ // doesn't need a type.
+ append_type = false;
+ } else {
+ // We can do this load/store as a struct access.
+ BracketIfNeeded(val, Precedence::Indexing);
+ val.v.back() += "." + access;
+ return;
+ }
+ }
+ // Detect absolute addressing, which we try to turn into references to the
+ // data section when possible.
+ uint64_t abs_base;
+ if (ConstIntVal(addr_exp.e, abs_base)) {
+ // We don't care what part of the absolute address was stored where,
+ // 1[0] and 0[1] are the same.
+ abs_base += offset;
+ // FIXME: make this less expensive with a binary search or whatever.
+ for (auto dat : mc.module.data_segments) {
+ uint64_t dat_base;
+ if (dat->offset.size() == 1 &&
+ ConstIntVal(&dat->offset.front(), dat_base) &&
+ abs_base >= dat_base && abs_base < dat_base + dat->data.size()) {
+ // We are inside the range of this data segment!
+ // Turn expression into data_name[index]
+ val = Value{{dat->name}, Precedence::Atomic};
+ // The new offset is from the start of the data segment, instead of
+ // whatever it was.. this may be a different value from both the
+ // original const and offset!
+ offset = abs_base - dat_base;
+ }
+ }
+ }
+ // Do the load/store as a generalized indexing operation.
+ // The offset is divisible by the alignment in 99.99% of
+ // cases, but the spec doesn't guarantee it, so we must
+ // have a backup syntax.
+ auto index = offset % align == 0
+ ? std::to_string(offset / align)
+ : cat(std::to_string(offset), "@", std::to_string(align));
+ // Detect the very common case of (base + (index << 2))[0]:int etc.
+ // so we can instead do base[index]:int
+ // TODO: (index << 2) on the left of + occurs also.
+ // TODO: sadly this does not address cases where the shift amount > align.
+ // (which happens for arrays of structs or arrays of long (with align=4)).
+ // TODO: also very common is (v = base + (index << 2))[0]:int
+ if (addr_exp.etype == ExprType::Binary) {
+ auto& pe = *cast<BinaryExpr>(addr_exp.e);
+ auto& shift_exp = addr_exp.children[1];
+ if (pe.opcode == Opcode::I32Add && shift_exp.etype == ExprType::Binary) {
+ auto& se = *cast<BinaryExpr>(shift_exp.e);
+ auto& const_exp = shift_exp.children[1];
+ if (se.opcode == Opcode::I32Shl && const_exp.etype == ExprType::Const) {
+ auto& ce = *cast<ConstExpr>(const_exp.e);
+ if (ce.const_.type() == Type::I32 &&
+ (1ULL << ce.const_.u32()) == align) {
+ // Pfew, case detected :( Lets re-write this in Haskell.
+ // TODO: we're decompiling these twice.
+ // The thing to the left of << is going to be part of the index.
+ auto ival = DecompileExpr(shift_exp.children[0], &shift_exp);
+ if (ival.v.size() == 1) { // Don't bother if huge.
+ if (offset == 0) {
+ index = ival.v[0];
+ } else {
+ BracketIfNeeded(ival, Precedence::Add);
+ index = cat(ival.v[0], " + ", index);
+ }
+ // We're going to use the thing to the left of + as the new
+ // base address:
+ val = DecompileExpr(addr_exp.children[0], &addr_exp);
+ }
+ }
+ }
+ }
+ }
+ BracketIfNeeded(val, Precedence::Indexing);
+ val.v.back() += cat("[", index, "]");
+ if (append_type) {
+ val.v.back() += cat(":", GetDecompTypeName(GetMemoryType(op_type, opc)),
+ lst.GenAlign(align, opc));
+ }
+ val.precedence = Precedence::Indexing;
+ }
+
+ Value DecompileExpr(const Node& n, const Node* parent) {
+ std::vector<Value> args;
+ for (auto& c : n.children) {
+ args.push_back(DecompileExpr(c, &n));
+ }
+ // First deal with the specialized node types.
+ switch (n.ntype) {
+ case NodeType::FlushToVars: {
+ std::string decls = "let ";
+ for (Index i = 0; i < n.u.var_count; i++) {
+ if (i) {
+ decls += ", ";
+ }
+ decls += TempVarName(n.u.var_start + i);
+ }
+ decls += " = ";
+ return WrapNAry(args, decls, "", Precedence::Assign);
+ }
+ case NodeType::FlushedVar: {
+ return Value{{TempVarName(n.u.var_start)}, Precedence::Atomic};
+ }
+ case NodeType::Statements: {
+ Value stats{{}, Precedence::None};
+ for (size_t i = 0; i < n.children.size(); i++) {
+ auto& s = args[i].v.back();
+ if (s.back() != '}' && s.back() != ':') {
+ s += ';';
+ }
+ std::move(args[i].v.begin(), args[i].v.end(),
+ std::back_inserter(stats.v));
+ }
+ return stats;
+ }
+ case NodeType::EndReturn: {
+ return WrapNAry(args, "return ", "", Precedence::None);
+ }
+ case NodeType::Decl: {
+ cur_ast->vars_defined[n.u.var->name()].defined = true;
+ return Value{{"var " + LocalDecl(std::string(n.u.var->name()),
+ cur_func->GetLocalType(*n.u.var))},
+ Precedence::None};
+ }
+ case NodeType::DeclInit: {
+ if (cur_ast->vars_defined[n.u.var->name()].defined) {
+ // This has already been pre-declared, output as assign.
+ return WrapChild(args[0], cat(VarName(n.u.var->name()), " = "), "",
+ Precedence::None);
+ } else {
+ return WrapChild(args[0],
+ cat("var ",
+ LocalDecl(std::string(n.u.var->name()),
+ cur_func->GetLocalType(*n.u.var)),
+ " = "),
+ "", Precedence::None);
+ }
+ }
+ case NodeType::Expr:
+ // We're going to fall thru to the second switch to deal with ExprType.
+ break;
+ case NodeType::Uninitialized:
+ assert(false);
+ break;
+ }
+ // Existing ExprTypes.
+ switch (n.etype) {
+ case ExprType::Const: {
+ auto& c = cast<ConstExpr>(n.e)->const_;
+ switch (c.type()) {
+ case Type::I32:
+ return Value{{std::to_string(static_cast<int32_t>(c.u32()))},
+ Precedence::Atomic};
+ case Type::I64:
+ return Value{{std::to_string(static_cast<int64_t>(c.u64())) + "L"},
+ Precedence::Atomic};
+ case Type::F32: {
+ float f = Bitcast<float>(c.f32_bits());
+ return Value{{to_string(f) + "f"}, Precedence::Atomic};
+ }
+ case Type::F64: {
+ double d = Bitcast<double>(c.f64_bits());
+ return Value{{to_string(d)}, Precedence::Atomic};
+ }
+ case Type::V128:
+ return Value{{"V128"}, Precedence::Atomic}; // FIXME
+ default:
+ WABT_UNREACHABLE;
+ }
+ }
+ case ExprType::LocalGet: {
+ return Get(*cast<LocalGetExpr>(n.e));
+ }
+ case ExprType::GlobalGet: {
+ return Get(*cast<GlobalGetExpr>(n.e));
+ }
+ case ExprType::LocalSet: {
+ return Set(args[0], *cast<LocalSetExpr>(n.e));
+ }
+ case ExprType::GlobalSet: {
+ return Set(args[0], *cast<GlobalSetExpr>(n.e));
+ }
+ case ExprType::LocalTee: {
+ auto& te = *cast<LocalTeeExpr>(n.e);
+ return args.empty() ? Get(te) : Set(args[0], te);
+ }
+ case ExprType::Binary: {
+ auto& be = *cast<BinaryExpr>(n.e);
+ auto opcs = OpcodeToToken(be.opcode);
+ // TODO: Is this selection better done on Opcode values directly?
+ // What if new values get added and OtherBin doesn't make sense?
+ auto prec = Precedence::OtherBin;
+ if (opcs == "*" || opcs == "/" || opcs == "%") {
+ prec = Precedence::Multiply;
+ } else if (opcs == "+" || opcs == "-") {
+ prec = Precedence::Add;
+ } else if (opcs == "&" || opcs == "|" || opcs == "^") {
+ prec = Precedence::Bit;
+ } else if (opcs == "<<" || opcs == ">>") {
+ prec = Precedence::Shift;
+ }
+ return WrapBinary(args, opcs, false, prec);
+ }
+ case ExprType::Compare: {
+ auto& ce = *cast<CompareExpr>(n.e);
+ return WrapBinary(args, OpcodeToToken(ce.opcode), false,
+ Precedence::Equal);
+ }
+ case ExprType::Unary: {
+ auto& ue = *cast<UnaryExpr>(n.e);
+ // BracketIfNeeded(stack.back());
+ // TODO: also version without () depending on precedence.
+ return WrapChild(args[0], OpcodeToToken(ue.opcode) + "(", ")",
+ Precedence::Atomic);
+ }
+ case ExprType::Load: {
+ auto& le = *cast<LoadExpr>(n.e);
+ LoadStore(args[0], n.children[0], le.offset, le.opcode, le.align,
+ le.opcode.GetResultType());
+ return std::move(args[0]);
+ }
+ case ExprType::Store: {
+ auto& se = *cast<StoreExpr>(n.e);
+ LoadStore(args[0], n.children[0], se.offset, se.opcode, se.align,
+ se.opcode.GetParamType2());
+ return WrapBinary(args, "=", true, Precedence::Assign);
+ }
+ case ExprType::If: {
+ auto ife = cast<IfExpr>(n.e);
+ Value* elsep = nullptr;
+ if (!ife->false_.empty()) {
+ elsep = &args[2];
+ }
+ auto& thenp = args[1];
+ auto& ifs = args[0];
+ bool multiline = ifs.v.size() > 1 || thenp.v.size() > 1;
+ size_t width = ifs.width() + thenp.width();
+ if (elsep) {
+ width += elsep->width();
+ multiline = multiline || elsep->v.size() > 1;
+ }
+ multiline = multiline || width > target_exp_width;
+ if (multiline) {
+ auto if_start = std::string_view("if (");
+ IndentValue(ifs, if_start.size(), if_start);
+ ifs.v.back() += ") {";
+ IndentValue(thenp, indent_amount, {});
+ std::move(thenp.v.begin(), thenp.v.end(), std::back_inserter(ifs.v));
+ if (elsep) {
+ ifs.v.push_back("} else {");
+ IndentValue(*elsep, indent_amount, {});
+ std::move(elsep->v.begin(), elsep->v.end(),
+ std::back_inserter(ifs.v));
+ }
+ ifs.v.push_back("}");
+ ifs.precedence = Precedence::If;
+ return std::move(ifs);
+ } else {
+ auto s = cat("if (", ifs.v[0], ") { ", thenp.v[0], " }");
+ if (elsep)
+ s += cat(" else { ", elsep->v[0], " }");
+ return Value{{std::move(s)}, Precedence::If};
+ }
+ }
+ case ExprType::Block: {
+ auto& val = args[0];
+ val.v.push_back(
+ cat("label ", VarName(cast<BlockExpr>(n.e)->block.label), ":"));
+ // If this block is part of a larger statement scope, it doesn't
+ // need its own indenting, but if its part of an exp we wrap it in {}.
+ if (parent && parent->ntype != NodeType::Statements &&
+ parent->etype != ExprType::Block &&
+ parent->etype != ExprType::Loop &&
+ (parent->etype != ExprType::If || &parent->children[0] == &n)) {
+ IndentValue(val, indent_amount, {});
+ val.v.insert(val.v.begin(), "{");
+ val.v.push_back("}");
+ }
+ val.precedence = Precedence::Atomic;
+ return std::move(val);
+ }
+ case ExprType::Loop: {
+ auto& val = args[0];
+ auto& block = cast<LoopExpr>(n.e)->block;
+ IndentValue(val, indent_amount, {});
+ val.v.insert(val.v.begin(), cat("loop ", VarName(block.label), " {"));
+ val.v.push_back("}");
+ val.precedence = Precedence::Atomic;
+ return std::move(val);
+ }
+ case ExprType::Br: {
+ auto be = cast<BrExpr>(n.e);
+ return Value{{(n.u.lt == LabelType::Loop ? "continue " : "goto ") +
+ VarName(be->var.name())},
+ Precedence::None};
+ }
+ case ExprType::BrIf: {
+ auto bie = cast<BrIfExpr>(n.e);
+ auto jmp = n.u.lt == LabelType::Loop ? "continue" : "goto";
+ return WrapChild(args[0], "if (",
+ cat(") ", jmp, " ", VarName(bie->var.name())),
+ Precedence::None);
+ }
+ case ExprType::Return: {
+ return WrapNAry(args, "return ", "", Precedence::None);
+ }
+ case ExprType::Rethrow: {
+ return WrapNAry(args, "rethrow ", "", Precedence::None);
+ }
+ case ExprType::Drop: {
+ // Silent dropping of return values is very common, so currently
+ // don't output this.
+ return std::move(args[0]);
+ }
+ case ExprType::Nop: {
+ return Value{{"nop"}, Precedence::None};
+ }
+ case ExprType::Unreachable: {
+ return Value{{"unreachable"}, Precedence::None};
+ }
+ case ExprType::RefNull: {
+ return Value{{"null"}, Precedence::Atomic};
+ }
+ case ExprType::BrTable: {
+ auto bte = cast<BrTableExpr>(n.e);
+ std::string ts = "br_table[";
+ for (auto& v : bte->targets) {
+ ts += VarName(v.name());
+ ts += ", ";
+ }
+ ts += "..";
+ ts += VarName(bte->default_target.name());
+ 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;
+ auto precedence = Precedence::Atomic;
+ switch (n.etype) {
+ case ExprType::Call:
+ name = cast<CallExpr>(n.e)->var.name();
+ break;
+ case ExprType::ReturnCall:
+ name = "return_call " + cast<ReturnCallExpr>(n.e)->var.name();
+ precedence = Precedence::None;
+ break;
+ case ExprType::Convert:
+ name = std::string(OpcodeToToken(cast<ConvertExpr>(n.e)->opcode));
+ break;
+ case ExprType::Ternary:
+ name = std::string(OpcodeToToken(cast<TernaryExpr>(n.e)->opcode));
+ break;
+ case ExprType::Select:
+ // This one looks like it could be translated to "?:" style ternary,
+ // but the arguments are NOT lazy, and side effects definitely do
+ // occur in the branches. So it has no clear equivalent in C-syntax.
+ // To emphasize that all args are being evaluated in order, we
+ // leave it as a function call.
+ name = "select_if";
+ break;
+ case ExprType::MemoryGrow:
+ name = "memory_grow";
+ break;
+ case ExprType::MemorySize:
+ name = "memory_size";
+ break;
+ case ExprType::MemoryCopy:
+ name = "memory_copy";
+ break;
+ case ExprType::MemoryFill:
+ name = "memory_fill";
+ break;
+ case ExprType::RefIsNull:
+ name = "is_null";
+ break;
+ case ExprType::CallIndirect:
+ name = "call_indirect";
+ break;
+ case ExprType::ReturnCallIndirect:
+ name = "return_call call_indirect";
+ break;
+ default:
+ name = GetExprTypeName(n.etype);
+ break;
+ }
+ return WrapNAry(args, name + "(", ")", precedence);
+ }
+ }
+ }
+
+ bool CheckImportExport(std::string& s,
+ ExternalKind kind,
+ Index index,
+ std::string_view name) {
+ // Figure out if this thing is imported, exported, or neither.
+ auto is_import = mc.module.IsImport(kind, Var(index, Location()));
+ // TODO: is this the best way to check for export?
+ // FIXME: this doesn't work for functions that get renamed in some way,
+ // as the export has the original name..
+ auto xport = mc.module.GetExport(name);
+ auto is_export = xport && xport->kind == kind;
+ if (is_export)
+ s += "export ";
+ if (is_import)
+ s += "import ";
+ return is_import;
+ }
+
+ std::string InitExp(const ExprList& el) {
+ assert(!el.empty());
+ AST ast(mc, nullptr);
+ ast.Construct(el, 1, 0, false);
+ auto val = DecompileExpr(ast.exp_stack[0], nullptr);
+ assert(ast.exp_stack.size() == 1 && val.v.size() == 1);
+ return std::move(val.v[0]);
+ }
+
+ // FIXME: Merge with WatWriter::WriteQuotedData somehow.
+ std::string BinaryToString(const std::vector<uint8_t>& in) {
+ std::string s = "\"";
+ size_t line_start = 0;
+ static const char s_hexdigits[] = "0123456789abcdef";
+ for (auto c : in) {
+ if (c >= ' ' && c <= '~') {
+ s += c;
+ } else {
+ s += '\\';
+ s += s_hexdigits[c >> 4];
+ s += s_hexdigits[c & 0xf];
+ }
+ if (s.size() - line_start > target_exp_width) {
+ if (line_start == 0) {
+ s = " " + s;
+ }
+ s += "\"\n ";
+ line_start = s.size();
+ s += "\"";
+ }
+ }
+ s += '\"';
+ return s;
+ }
+
+ std::string Decompile() {
+ std::string s;
+ // Memories.
+ Index memory_index = 0;
+ for (auto m : mc.module.memories) {
+ auto is_import =
+ CheckImportExport(s, ExternalKind::Memory, memory_index, m->name);
+ s += cat("memory ", m->name);
+ if (!is_import) {
+ s += cat("(initial: ", std::to_string(m->page_limits.initial),
+ ", max: ", std::to_string(m->page_limits.max), ")");
+ }
+ s += ";\n";
+ memory_index++;
+ }
+ if (!mc.module.memories.empty())
+ s += "\n";
+
+ // Globals.
+ Index global_index = 0;
+ for (auto g : mc.module.globals) {
+ auto is_import =
+ CheckImportExport(s, ExternalKind::Global, global_index, g->name);
+ s += cat("global ", g->name, ":", GetDecompTypeName(g->type));
+ if (!is_import) {
+ s += cat(" = ", InitExp(g->init_expr));
+ }
+ s += ";\n";
+ global_index++;
+ }
+ if (!mc.module.globals.empty())
+ s += "\n";
+
+ // Tables.
+ Index table_index = 0;
+ for (auto tab : mc.module.tables) {
+ auto is_import =
+ CheckImportExport(s, ExternalKind::Table, table_index, tab->name);
+ s += cat("table ", tab->name, ":", GetDecompTypeName(tab->elem_type));
+ if (!is_import) {
+ s += cat("(min: ", std::to_string(tab->elem_limits.initial),
+ ", max: ", std::to_string(tab->elem_limits.max), ")");
+ }
+ s += ";\n";
+ table_index++;
+ }
+ if (!mc.module.tables.empty())
+ s += "\n";
+
+ // Data.
+ for (auto dat : mc.module.data_segments) {
+ s += cat("data ", dat->name, "(offset: ", InitExp(dat->offset), ") =");
+ auto ds = BinaryToString(dat->data);
+ if (ds.size() > target_exp_width / 2) {
+ s += "\n";
+ } else {
+ s += " ";
+ }
+ s += ds;
+ s += ";\n";
+ }
+ if (!mc.module.data_segments.empty())
+ s += "\n";
+
+ // Code.
+ Index func_index = 0;
+ for (auto f : mc.module.funcs) {
+ cur_func = f;
+ auto is_import =
+ CheckImportExport(s, ExternalKind::Func, func_index, f->name);
+ AST ast(mc, f);
+ cur_ast = &ast;
+ if (!is_import) {
+ ast.Construct(f->exprs, f->GetNumResults(), 0, true);
+ lst.Track(ast.exp_stack[0]);
+ lst.CheckLayouts();
+ }
+ s += cat("function ", f->name, "(");
+ for (Index i = 0; i < f->GetNumParams(); i++) {
+ if (i)
+ s += ", ";
+ auto t = f->GetParamType(i);
+ auto name = "$" + IndexToAlphaName(i);
+ s += LocalDecl(name, t);
+ }
+ s += ")";
+ if (f->GetNumResults()) {
+ if (f->GetNumResults() == 1) {
+ s += cat(":", GetDecompTypeName(f->GetResultType(0)));
+ } else {
+ s += ":(";
+ for (Index i = 0; i < f->GetNumResults(); i++) {
+ if (i)
+ s += ", ";
+ s += GetDecompTypeName(f->GetResultType(i));
+ }
+ s += ")";
+ }
+ }
+ if (is_import) {
+ s += ";";
+ } else {
+ s += " {\n";
+ auto val = DecompileExpr(ast.exp_stack[0], nullptr);
+ IndentValue(val, indent_amount, {});
+ for (auto& stat : val.v) {
+ s += stat;
+ s += "\n";
+ }
+ s += "}";
+ }
+ s += "\n\n";
+ mc.EndFunc();
+ lst.Clear();
+ func_index++;
+ cur_ast = nullptr;
+ cur_func = nullptr;
+ }
+ return s;
+ }
+
+ ModuleContext mc;
+ const DecompileOptions& options;
+ size_t indent_amount = 2;
+ size_t target_exp_width = 70;
+ const Func* cur_func = nullptr;
+ AST* cur_ast = nullptr;
+ LoadStoreTracking lst;
+};
+
+std::string Decompile(const Module& module, const DecompileOptions& options) {
+ Decompiler decompiler(module, options);
+ return decompiler.Decompile();
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/emscripten-exports.txt b/third_party/wasm2c/src/emscripten-exports.txt
new file mode 100644
index 0000000000..638673120a
--- /dev/null
+++ b/third_party/wasm2c/src/emscripten-exports.txt
@@ -0,0 +1,72 @@
+_free
+_malloc
+_wabt_annotations_enabled
+_wabt_apply_names_module
+_wabt_bulk_memory_enabled
+_wabt_code_metadata_enabled
+_wabt_destroy_errors
+_wabt_destroy_features
+_wabt_destroy_module
+_wabt_destroy_output_buffer
+_wabt_destroy_parse_wat_result
+_wabt_destroy_read_binary_result
+_wabt_destroy_wast_lexer
+_wabt_destroy_write_module_result
+_wabt_exceptions_enabled
+_wabt_extended_const_enabled
+_wabt_format_binary_errors
+_wabt_format_text_errors
+_wabt_function_references_enabled
+_wabt_gc_enabled
+_wabt_generate_names_module
+_wabt_memory64_enabled
+_wabt_multi_memory_enabled
+_wabt_multi_value_enabled
+_wabt_mutable_globals_enabled
+_wabt_new_errors
+_wabt_new_features
+_wabt_new_wast_buffer_lexer
+_wabt_output_buffer_get_data
+_wabt_output_buffer_get_size
+_wabt_parse_wast
+_wabt_parse_wast_result_get_result
+_wabt_parse_wast_result_release_module
+_wabt_parse_wat
+_wabt_parse_wat_result_get_result
+_wabt_parse_wat_result_release_module
+_wabt_read_binary
+_wabt_read_binary_result_get_result
+_wabt_read_binary_result_release_module
+_wabt_reference_types_enabled
+_wabt_relaxed_simd_enabled
+_wabt_sat_float_to_int_enabled
+_wabt_set_annotations_enabled
+_wabt_set_bulk_memory_enabled
+_wabt_set_code_metadata_enabled
+_wabt_set_exceptions_enabled
+_wabt_set_extended_const_enabled
+_wabt_set_function_references_enabled
+_wabt_set_gc_enabled
+_wabt_set_memory64_enabled
+_wabt_set_multi_memory_enabled
+_wabt_set_multi_value_enabled
+_wabt_set_mutable_globals_enabled
+_wabt_set_reference_types_enabled
+_wabt_set_relaxed_simd_enabled
+_wabt_set_sat_float_to_int_enabled
+_wabt_set_sign_extension_enabled
+_wabt_set_simd_enabled
+_wabt_set_tail_call_enabled
+_wabt_set_threads_enabled
+_wabt_sign_extension_enabled
+_wabt_simd_enabled
+_wabt_tail_call_enabled
+_wabt_threads_enabled
+_wabt_validate_module
+_wabt_validate_script
+_wabt_write_binary_module
+_wabt_write_binary_spec_script
+_wabt_write_module_result_get_result
+_wabt_write_module_result_release_log_output_buffer
+_wabt_write_module_result_release_output_buffer
+_wabt_write_text_module
diff --git a/third_party/wasm2c/src/emscripten-helpers.cc b/third_party/wasm2c/src/emscripten-helpers.cc
new file mode 100644
index 0000000000..127a8c458d
--- /dev/null
+++ b/third_party/wasm2c/src/emscripten-helpers.cc
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WABT_EMSCRIPTEN_HELPERS_H_
+#define WABT_EMSCRIPTEN_HELPERS_H_
+
+#include <cstddef>
+
+#include <algorithm>
+#include <iterator>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "wabt/apply-names.h"
+#include "wabt/binary-reader-ir.h"
+#include "wabt/binary-reader.h"
+#include "wabt/binary-writer-spec.h"
+#include "wabt/binary-writer.h"
+#include "wabt/common.h"
+#include "wabt/error-formatter.h"
+#include "wabt/feature.h"
+#include "wabt/filenames.h"
+#include "wabt/generate-names.h"
+#include "wabt/ir.h"
+#include "wabt/stream.h"
+#include "wabt/validator.h"
+#include "wabt/wast-lexer.h"
+#include "wabt/wast-parser.h"
+#include "wabt/wat-writer.h"
+
+using WabtOutputBufferPtr = std::unique_ptr<wabt::OutputBuffer>;
+using WabtFilenameOutputBufferPair =
+ std::pair<std::string, WabtOutputBufferPtr>;
+
+struct WabtParseWatResult {
+ wabt::Result result;
+ std::unique_ptr<wabt::Module> module;
+};
+
+struct WabtReadBinaryResult {
+ wabt::Result result;
+ std::unique_ptr<wabt::Module> module;
+};
+
+struct WabtWriteModuleResult {
+ wabt::Result result;
+ WabtOutputBufferPtr buffer;
+ WabtOutputBufferPtr log_buffer;
+};
+
+struct WabtWriteScriptResult {
+ wabt::Result result;
+ WabtOutputBufferPtr json_buffer;
+ WabtOutputBufferPtr log_buffer;
+ std::vector<WabtFilenameOutputBufferPair> module_buffers;
+};
+
+struct WabtParseWastResult {
+ wabt::Result result;
+ std::unique_ptr<wabt::Script> script;
+};
+
+extern "C" {
+
+wabt::Features* wabt_new_features(void) {
+ return new wabt::Features();
+}
+
+void wabt_destroy_features(wabt::Features* f) {
+ delete f;
+}
+
+#define WABT_FEATURE(variable, flag, default_, help) \
+ bool wabt_##variable##_enabled(wabt::Features* f) { \
+ return f->variable##_enabled(); \
+ } \
+ void wabt_set_##variable##_enabled(wabt::Features* f, int enabled) { \
+ f->set_##variable##_enabled(enabled); \
+ }
+#include "wabt/feature.def"
+#undef WABT_FEATURE
+
+wabt::WastLexer* wabt_new_wast_buffer_lexer(const char* filename,
+ const void* data,
+ size_t size,
+ wabt::Errors* errors) {
+ std::unique_ptr<wabt::WastLexer> lexer =
+ wabt::WastLexer::CreateBufferLexer(filename, data, size, errors);
+ return lexer.release();
+}
+
+WabtParseWatResult* wabt_parse_wat(wabt::WastLexer* lexer,
+ wabt::Features* features,
+ wabt::Errors* errors) {
+ wabt::WastParseOptions options(*features);
+ WabtParseWatResult* result = new WabtParseWatResult();
+ std::unique_ptr<wabt::Module> module;
+ result->result = wabt::ParseWatModule(lexer, &module, errors, &options);
+ result->module = std::move(module);
+ return result;
+}
+
+WabtParseWastResult* wabt_parse_wast(wabt::WastLexer* lexer,
+ wabt::Features* features,
+ wabt::Errors* errors) {
+ wabt::WastParseOptions options(*features);
+ WabtParseWastResult* result = new WabtParseWastResult();
+ std::unique_ptr<wabt::Script> script;
+ result->result = wabt::ParseWastScript(lexer, &script, errors, &options);
+ result->script = std::move(script);
+ return result;
+}
+
+WabtReadBinaryResult* wabt_read_binary(const void* data,
+ size_t size,
+ int read_debug_names,
+ wabt::Features* features,
+ wabt::Errors* errors) {
+ wabt::ReadBinaryOptions options;
+ options.features = *features;
+ options.read_debug_names = read_debug_names;
+
+ WabtReadBinaryResult* result = new WabtReadBinaryResult();
+ wabt::Module* module = new wabt::Module();
+ // TODO(binji): Pass through from wabt_read_binary parameter.
+ const char* filename = "<binary>";
+ result->result =
+ wabt::ReadBinaryIr(filename, data, size, options, errors, module);
+ result->module.reset(module);
+ return result;
+}
+
+wabt::Result::Enum wabt_validate_module(wabt::Module* module,
+ wabt::Features* features,
+ wabt::Errors* errors) {
+ wabt::ValidateOptions options;
+ options.features = *features;
+ return ValidateModule(module, errors, options);
+}
+
+wabt::Result::Enum wabt_validate_script(wabt::Script* script,
+ wabt::Features* features,
+ wabt::Errors* errors) {
+ wabt::ValidateOptions options;
+ options.features = *features;
+ return ValidateScript(script, errors, options);
+}
+
+WabtWriteScriptResult* wabt_write_binary_spec_script(
+ wabt::Script* script,
+ const char* source_filename,
+ const char* out_filename,
+ int log,
+ int canonicalize_lebs,
+ int relocatable,
+ int write_debug_names) {
+ wabt::MemoryStream log_stream;
+ wabt::MemoryStream* log_stream_p = log ? &log_stream : nullptr;
+
+ wabt::WriteBinaryOptions options;
+ options.canonicalize_lebs = canonicalize_lebs;
+ options.relocatable = relocatable;
+ options.write_debug_names = write_debug_names;
+
+ std::vector<wabt::FilenameMemoryStreamPair> module_streams;
+ wabt::MemoryStream json_stream(log_stream_p);
+
+ std::string module_filename_noext(
+ wabt::StripExtension(out_filename ? out_filename : source_filename));
+
+ WabtWriteScriptResult* result = new WabtWriteScriptResult();
+ result->result = WriteBinarySpecScript(&json_stream, script, source_filename,
+ module_filename_noext, options,
+ &module_streams, log_stream_p);
+
+ if (result->result == wabt::Result::Ok) {
+ result->json_buffer = json_stream.ReleaseOutputBuffer();
+ result->log_buffer = log ? log_stream.ReleaseOutputBuffer() : nullptr;
+ std::transform(module_streams.begin(), module_streams.end(),
+ std::back_inserter(result->module_buffers),
+ [](wabt::FilenameMemoryStreamPair& pair) {
+ return WabtFilenameOutputBufferPair(
+ pair.filename, pair.stream->ReleaseOutputBuffer());
+ });
+ }
+ return result;
+}
+
+wabt::Result::Enum wabt_apply_names_module(wabt::Module* module) {
+ return ApplyNames(module);
+}
+
+wabt::Result::Enum wabt_generate_names_module(wabt::Module* module) {
+ return GenerateNames(module);
+}
+
+WabtWriteModuleResult* wabt_write_binary_module(wabt::Module* module,
+ int log,
+ int canonicalize_lebs,
+ int relocatable,
+ int write_debug_names) {
+ wabt::MemoryStream log_stream;
+ wabt::WriteBinaryOptions options;
+ options.canonicalize_lebs = canonicalize_lebs;
+ options.relocatable = relocatable;
+ options.write_debug_names = write_debug_names;
+
+ wabt::MemoryStream stream(log ? &log_stream : nullptr);
+ WabtWriteModuleResult* result = new WabtWriteModuleResult();
+ result->result = WriteBinaryModule(&stream, module, options);
+ if (result->result == wabt::Result::Ok) {
+ result->buffer = stream.ReleaseOutputBuffer();
+ result->log_buffer = log ? log_stream.ReleaseOutputBuffer() : nullptr;
+ }
+ return result;
+}
+
+WabtWriteModuleResult* wabt_write_text_module(wabt::Module* module,
+ int fold_exprs,
+ int inline_export) {
+ wabt::WriteWatOptions options;
+ options.fold_exprs = fold_exprs;
+ options.inline_export = inline_export;
+
+ wabt::MemoryStream stream;
+ WabtWriteModuleResult* result = new WabtWriteModuleResult();
+ result->result = WriteWat(&stream, module, options);
+ if (result->result == wabt::Result::Ok) {
+ result->buffer = stream.ReleaseOutputBuffer();
+ }
+ return result;
+}
+
+void wabt_destroy_module(wabt::Module* module) {
+ delete module;
+}
+
+void wabt_destroy_wast_lexer(wabt::WastLexer* lexer) {
+ delete lexer;
+}
+
+// Errors
+wabt::Errors* wabt_new_errors(void) {
+ return new wabt::Errors();
+}
+
+wabt::OutputBuffer* wabt_format_text_errors(wabt::Errors* errors,
+ wabt::WastLexer* lexer) {
+ auto line_finder = lexer->MakeLineFinder();
+ std::string string_result = FormatErrorsToString(
+ *errors, wabt::Location::Type::Text, line_finder.get());
+
+ wabt::OutputBuffer* result = new wabt::OutputBuffer();
+ std::copy(string_result.begin(), string_result.end(),
+ std::back_inserter(result->data));
+ return result;
+}
+
+wabt::OutputBuffer* wabt_format_binary_errors(wabt::Errors* errors) {
+ std::string string_result =
+ FormatErrorsToString(*errors, wabt::Location::Type::Binary);
+
+ wabt::OutputBuffer* result = new wabt::OutputBuffer();
+ std::copy(string_result.begin(), string_result.end(),
+ std::back_inserter(result->data));
+ return result;
+}
+
+void wabt_destroy_errors(wabt::Errors* errors) {
+ delete errors;
+}
+
+// WabtParseWatResult
+wabt::Result::Enum wabt_parse_wat_result_get_result(
+ WabtParseWatResult* result) {
+ return result->result;
+}
+
+wabt::Module* wabt_parse_wat_result_release_module(WabtParseWatResult* result) {
+ return result->module.release();
+}
+
+void wabt_destroy_parse_wat_result(WabtParseWatResult* result) {
+ delete result;
+}
+
+// WabtParseWastResult
+wabt::Result::Enum wabt_parse_wast_result_get_result(
+ WabtParseWastResult* result) {
+ return result->result;
+}
+
+wabt::Script* wabt_parse_wast_result_release_module(
+ WabtParseWastResult* result) {
+ return result->script.release();
+}
+
+void wabt_destroy_parse_wast_result(WabtParseWastResult* result) {
+ delete result;
+}
+
+// WabtReadBinaryResult
+wabt::Result::Enum wabt_read_binary_result_get_result(
+ WabtReadBinaryResult* result) {
+ return result->result;
+}
+
+wabt::Module* wabt_read_binary_result_release_module(
+ WabtReadBinaryResult* result) {
+ return result->module.release();
+}
+
+void wabt_destroy_read_binary_result(WabtReadBinaryResult* result) {
+ delete result;
+}
+
+// WabtWriteModuleResult
+wabt::Result::Enum wabt_write_module_result_get_result(
+ WabtWriteModuleResult* result) {
+ return result->result;
+}
+
+wabt::OutputBuffer* wabt_write_module_result_release_output_buffer(
+ WabtWriteModuleResult* result) {
+ return result->buffer.release();
+}
+
+wabt::OutputBuffer* wabt_write_module_result_release_log_output_buffer(
+ WabtWriteModuleResult* result) {
+ return result->log_buffer.release();
+}
+
+void wabt_destroy_write_module_result(WabtWriteModuleResult* result) {
+ delete result;
+}
+
+// WabtWriteScriptResult
+wabt::Result::Enum wabt_write_script_result_get_result(
+ WabtWriteScriptResult* result) {
+ return result->result;
+}
+
+wabt::OutputBuffer* wabt_write_script_result_release_json_output_buffer(
+ WabtWriteScriptResult* result) {
+ return result->json_buffer.release();
+}
+
+wabt::OutputBuffer* wabt_write_script_result_release_log_output_buffer(
+ WabtWriteScriptResult* result) {
+ return result->log_buffer.release();
+}
+
+size_t wabt_write_script_result_get_module_count(
+ WabtWriteScriptResult* result) {
+ return result->module_buffers.size();
+}
+
+const char* wabt_write_script_result_get_module_filename(
+ WabtWriteScriptResult* result,
+ size_t index) {
+ return result->module_buffers[index].first.c_str();
+}
+
+wabt::OutputBuffer* wabt_write_script_result_release_module_output_buffer(
+ WabtWriteScriptResult* result,
+ size_t index) {
+ return result->module_buffers[index].second.release();
+}
+
+void wabt_destroy_write_script_result(WabtWriteScriptResult* result) {
+ delete result;
+}
+
+// wabt::OutputBuffer*
+const void* wabt_output_buffer_get_data(wabt::OutputBuffer* output_buffer) {
+ return output_buffer->data.data();
+}
+
+size_t wabt_output_buffer_get_size(wabt::OutputBuffer* output_buffer) {
+ return output_buffer->data.size();
+}
+
+void wabt_destroy_output_buffer(wabt::OutputBuffer* output_buffer) {
+ delete output_buffer;
+}
+
+} // extern "C"
+
+#endif /* WABT_EMSCRIPTEN_HELPERS_H_ */
diff --git a/third_party/wasm2c/src/error-formatter.cc b/third_party/wasm2c/src/error-formatter.cc
new file mode 100644
index 0000000000..9a14cfac25
--- /dev/null
+++ b/third_party/wasm2c/src/error-formatter.cc
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2018 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/error-formatter.h"
+
+namespace wabt {
+
+namespace {
+
+std::string FormatError(const Error& error,
+ Location::Type location_type,
+ const Color& color,
+ LexerSourceLineFinder* line_finder,
+ int source_line_max_length,
+ int indent) {
+ std::string indent_str(indent, ' ');
+ std::string result = indent_str;
+
+ result += color.MaybeBoldCode();
+
+ const Location& loc = error.loc;
+ if (!loc.filename.empty()) {
+ result += loc.filename;
+ result += ":";
+ }
+
+ if (location_type == Location::Type::Text) {
+ result += StringPrintf("%d:%d: ", loc.line, loc.first_column);
+ } else if (loc.offset != kInvalidOffset) {
+ result += StringPrintf("%07" PRIzx ": ", loc.offset);
+ }
+
+ result += color.MaybeRedCode();
+ result += GetErrorLevelName(error.error_level);
+ result += ": ";
+ result += color.MaybeDefaultCode();
+
+ result += error.message;
+ result += '\n';
+
+ LexerSourceLineFinder::SourceLine source_line;
+ if (line_finder) {
+ line_finder->GetSourceLine(loc, source_line_max_length, &source_line);
+ }
+
+ if (!source_line.line.empty()) {
+ result += indent_str;
+ result += source_line.line;
+ result += '\n';
+ result += indent_str;
+
+ size_t num_spaces = (loc.first_column - 1) - source_line.column_offset;
+ size_t num_carets = loc.last_column - loc.first_column;
+ num_carets = std::min(num_carets, source_line.line.size() - num_spaces);
+ num_carets = std::max<size_t>(num_carets, 1);
+ result.append(num_spaces, ' ');
+ result += color.MaybeBoldCode();
+ result += color.MaybeGreenCode();
+ result.append(num_carets, '^');
+ result += color.MaybeDefaultCode();
+ result += '\n';
+ }
+
+ return result;
+}
+
+} // End of anonymous namespace
+
+std::string FormatErrorsToString(const Errors& errors,
+ Location::Type location_type,
+ LexerSourceLineFinder* line_finder,
+ const Color& color,
+ const std::string& header,
+ PrintHeader print_header,
+ int source_line_max_length) {
+ std::string result;
+ for (const auto& error : errors) {
+ if (!header.empty()) {
+ switch (print_header) {
+ case PrintHeader::Never:
+ break;
+ case PrintHeader::Once:
+ print_header = PrintHeader::Never;
+ [[fallthrough]];
+ case PrintHeader::Always:
+ result += header;
+ result += ":\n";
+ break;
+ }
+ }
+
+ int indent = header.empty() ? 0 : 2;
+
+ result += FormatError(error, location_type, color, line_finder,
+ source_line_max_length, indent);
+ }
+ return result;
+}
+
+void FormatErrorsToFile(const Errors& errors,
+ Location::Type location_type,
+ LexerSourceLineFinder* line_finder,
+ FILE* file,
+ const std::string& header,
+ PrintHeader print_header,
+ int source_line_max_length) {
+ Color color(file);
+ std::string s =
+ FormatErrorsToString(errors, location_type, line_finder, color, header,
+ print_header, source_line_max_length);
+ fwrite(s.data(), 1, s.size(), file);
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/expr-visitor.cc b/third_party/wasm2c/src/expr-visitor.cc
new file mode 100644
index 0000000000..05cd2798ca
--- /dev/null
+++ b/third_party/wasm2c/src/expr-visitor.cc
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/expr-visitor.h"
+
+#include "wabt/cast.h"
+#include "wabt/ir.h"
+
+namespace wabt {
+
+ExprVisitor::ExprVisitor(Delegate* delegate) : delegate_(delegate) {}
+
+Result ExprVisitor::VisitExpr(Expr* root_expr) {
+ state_stack_.clear();
+ expr_stack_.clear();
+ expr_iter_stack_.clear();
+ catch_index_stack_.clear();
+
+ PushDefault(root_expr);
+
+ while (!state_stack_.empty()) {
+ State state = state_stack_.back();
+ auto* expr = expr_stack_.back();
+
+ switch (state) {
+ case State::Default:
+ PopDefault();
+ CHECK_RESULT(HandleDefaultState(expr));
+ break;
+
+ case State::Block: {
+ auto block_expr = cast<BlockExpr>(expr);
+ auto& iter = expr_iter_stack_.back();
+ if (iter != block_expr->block.exprs.end()) {
+ PushDefault(&*iter++);
+ } else {
+ CHECK_RESULT(delegate_->EndBlockExpr(block_expr));
+ PopExprlist();
+ }
+ break;
+ }
+
+ case State::IfTrue: {
+ auto if_expr = cast<IfExpr>(expr);
+ auto& iter = expr_iter_stack_.back();
+ if (iter != if_expr->true_.exprs.end()) {
+ PushDefault(&*iter++);
+ } else {
+ CHECK_RESULT(delegate_->AfterIfTrueExpr(if_expr));
+ PopExprlist();
+ PushExprlist(State::IfFalse, expr, if_expr->false_);
+ }
+ break;
+ }
+
+ case State::IfFalse: {
+ auto if_expr = cast<IfExpr>(expr);
+ auto& iter = expr_iter_stack_.back();
+ if (iter != if_expr->false_.end()) {
+ PushDefault(&*iter++);
+ } else {
+ CHECK_RESULT(delegate_->EndIfExpr(if_expr));
+ PopExprlist();
+ }
+ break;
+ }
+
+ case State::Loop: {
+ auto loop_expr = cast<LoopExpr>(expr);
+ auto& iter = expr_iter_stack_.back();
+ if (iter != loop_expr->block.exprs.end()) {
+ PushDefault(&*iter++);
+ } else {
+ CHECK_RESULT(delegate_->EndLoopExpr(loop_expr));
+ PopExprlist();
+ }
+ break;
+ }
+
+ case State::Try: {
+ auto try_expr = cast<TryExpr>(expr);
+ auto& iter = expr_iter_stack_.back();
+ if (iter != try_expr->block.exprs.end()) {
+ PushDefault(&*iter++);
+ } else {
+ PopExprlist();
+ switch (try_expr->kind) {
+ case TryKind::Catch:
+ if (!try_expr->catches.empty()) {
+ Catch& catch_ = try_expr->catches[0];
+ CHECK_RESULT(delegate_->OnCatchExpr(try_expr, &catch_));
+ PushCatch(expr, 0, catch_.exprs);
+ } else {
+ CHECK_RESULT(delegate_->EndTryExpr(try_expr));
+ }
+ break;
+ case TryKind::Delegate:
+ CHECK_RESULT(delegate_->OnDelegateExpr(try_expr));
+ break;
+ case TryKind::Plain:
+ CHECK_RESULT(delegate_->EndTryExpr(try_expr));
+ break;
+ }
+ }
+ break;
+ }
+
+ case State::Catch: {
+ auto try_expr = cast<TryExpr>(expr);
+ Index catch_index = catch_index_stack_.back();
+ auto& iter = expr_iter_stack_.back();
+ if (iter != try_expr->catches[catch_index].exprs.end()) {
+ PushDefault(&*iter++);
+ } else {
+ PopCatch();
+ catch_index++;
+ if (catch_index < try_expr->catches.size()) {
+ Catch& catch_ = try_expr->catches[catch_index];
+ CHECK_RESULT(delegate_->OnCatchExpr(try_expr, &catch_));
+ PushCatch(expr, catch_index, catch_.exprs);
+ } else {
+ CHECK_RESULT(delegate_->EndTryExpr(try_expr));
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ return Result::Ok;
+}
+
+Result ExprVisitor::VisitExprList(ExprList& exprs) {
+ for (Expr& expr : exprs)
+ CHECK_RESULT(VisitExpr(&expr));
+ return Result::Ok;
+}
+
+Result ExprVisitor::VisitFunc(Func* func) {
+ return VisitExprList(func->exprs);
+}
+
+Result ExprVisitor::HandleDefaultState(Expr* expr) {
+ switch (expr->type()) {
+ case ExprType::AtomicLoad:
+ CHECK_RESULT(delegate_->OnAtomicLoadExpr(cast<AtomicLoadExpr>(expr)));
+ break;
+
+ case ExprType::AtomicStore:
+ CHECK_RESULT(delegate_->OnAtomicStoreExpr(cast<AtomicStoreExpr>(expr)));
+ break;
+
+ case ExprType::AtomicRmw:
+ CHECK_RESULT(delegate_->OnAtomicRmwExpr(cast<AtomicRmwExpr>(expr)));
+ break;
+
+ case ExprType::AtomicRmwCmpxchg:
+ CHECK_RESULT(
+ delegate_->OnAtomicRmwCmpxchgExpr(cast<AtomicRmwCmpxchgExpr>(expr)));
+ break;
+
+ case ExprType::AtomicWait:
+ CHECK_RESULT(delegate_->OnAtomicWaitExpr(cast<AtomicWaitExpr>(expr)));
+ break;
+
+ case ExprType::AtomicFence:
+ CHECK_RESULT(delegate_->OnAtomicFenceExpr(cast<AtomicFenceExpr>(expr)));
+ break;
+
+ case ExprType::AtomicNotify:
+ CHECK_RESULT(delegate_->OnAtomicNotifyExpr(cast<AtomicNotifyExpr>(expr)));
+ break;
+
+ case ExprType::Binary:
+ CHECK_RESULT(delegate_->OnBinaryExpr(cast<BinaryExpr>(expr)));
+ break;
+
+ case ExprType::Block: {
+ auto block_expr = cast<BlockExpr>(expr);
+ CHECK_RESULT(delegate_->BeginBlockExpr(block_expr));
+ PushExprlist(State::Block, expr, block_expr->block.exprs);
+ break;
+ }
+
+ case ExprType::Br:
+ CHECK_RESULT(delegate_->OnBrExpr(cast<BrExpr>(expr)));
+ break;
+
+ case ExprType::BrIf:
+ CHECK_RESULT(delegate_->OnBrIfExpr(cast<BrIfExpr>(expr)));
+ break;
+
+ case ExprType::BrTable:
+ CHECK_RESULT(delegate_->OnBrTableExpr(cast<BrTableExpr>(expr)));
+ break;
+
+ case ExprType::Call:
+ CHECK_RESULT(delegate_->OnCallExpr(cast<CallExpr>(expr)));
+ break;
+
+ case ExprType::CallIndirect:
+ CHECK_RESULT(delegate_->OnCallIndirectExpr(cast<CallIndirectExpr>(expr)));
+ break;
+
+ case ExprType::CallRef:
+ 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;
+
+ case ExprType::Const:
+ CHECK_RESULT(delegate_->OnConstExpr(cast<ConstExpr>(expr)));
+ break;
+
+ case ExprType::Convert:
+ CHECK_RESULT(delegate_->OnConvertExpr(cast<ConvertExpr>(expr)));
+ break;
+
+ case ExprType::Drop:
+ CHECK_RESULT(delegate_->OnDropExpr(cast<DropExpr>(expr)));
+ break;
+
+ case ExprType::GlobalGet:
+ CHECK_RESULT(delegate_->OnGlobalGetExpr(cast<GlobalGetExpr>(expr)));
+ break;
+
+ case ExprType::GlobalSet:
+ CHECK_RESULT(delegate_->OnGlobalSetExpr(cast<GlobalSetExpr>(expr)));
+ break;
+
+ case ExprType::If: {
+ auto if_expr = cast<IfExpr>(expr);
+ CHECK_RESULT(delegate_->BeginIfExpr(if_expr));
+ PushExprlist(State::IfTrue, expr, if_expr->true_.exprs);
+ break;
+ }
+
+ case ExprType::Load:
+ CHECK_RESULT(delegate_->OnLoadExpr(cast<LoadExpr>(expr)));
+ break;
+
+ case ExprType::LoadSplat:
+ CHECK_RESULT(delegate_->OnLoadSplatExpr(cast<LoadSplatExpr>(expr)));
+ break;
+
+ case ExprType::LoadZero:
+ CHECK_RESULT(delegate_->OnLoadZeroExpr(cast<LoadZeroExpr>(expr)));
+ break;
+
+ case ExprType::LocalGet:
+ CHECK_RESULT(delegate_->OnLocalGetExpr(cast<LocalGetExpr>(expr)));
+ break;
+
+ case ExprType::LocalSet:
+ CHECK_RESULT(delegate_->OnLocalSetExpr(cast<LocalSetExpr>(expr)));
+ break;
+
+ case ExprType::LocalTee:
+ CHECK_RESULT(delegate_->OnLocalTeeExpr(cast<LocalTeeExpr>(expr)));
+ break;
+
+ case ExprType::Loop: {
+ auto loop_expr = cast<LoopExpr>(expr);
+ CHECK_RESULT(delegate_->BeginLoopExpr(loop_expr));
+ PushExprlist(State::Loop, expr, loop_expr->block.exprs);
+ break;
+ }
+
+ case ExprType::MemoryCopy:
+ CHECK_RESULT(delegate_->OnMemoryCopyExpr(cast<MemoryCopyExpr>(expr)));
+ break;
+
+ case ExprType::DataDrop:
+ CHECK_RESULT(delegate_->OnDataDropExpr(cast<DataDropExpr>(expr)));
+ break;
+
+ case ExprType::MemoryFill:
+ CHECK_RESULT(delegate_->OnMemoryFillExpr(cast<MemoryFillExpr>(expr)));
+ break;
+
+ case ExprType::MemoryGrow:
+ CHECK_RESULT(delegate_->OnMemoryGrowExpr(cast<MemoryGrowExpr>(expr)));
+ break;
+
+ case ExprType::MemoryInit:
+ CHECK_RESULT(delegate_->OnMemoryInitExpr(cast<MemoryInitExpr>(expr)));
+ break;
+
+ case ExprType::MemorySize:
+ CHECK_RESULT(delegate_->OnMemorySizeExpr(cast<MemorySizeExpr>(expr)));
+ break;
+
+ case ExprType::TableCopy:
+ CHECK_RESULT(delegate_->OnTableCopyExpr(cast<TableCopyExpr>(expr)));
+ break;
+
+ case ExprType::ElemDrop:
+ CHECK_RESULT(delegate_->OnElemDropExpr(cast<ElemDropExpr>(expr)));
+ break;
+
+ case ExprType::TableInit:
+ CHECK_RESULT(delegate_->OnTableInitExpr(cast<TableInitExpr>(expr)));
+ break;
+
+ case ExprType::TableGet:
+ CHECK_RESULT(delegate_->OnTableGetExpr(cast<TableGetExpr>(expr)));
+ break;
+
+ case ExprType::TableSet:
+ CHECK_RESULT(delegate_->OnTableSetExpr(cast<TableSetExpr>(expr)));
+ break;
+
+ case ExprType::TableGrow:
+ CHECK_RESULT(delegate_->OnTableGrowExpr(cast<TableGrowExpr>(expr)));
+ break;
+
+ case ExprType::TableSize:
+ CHECK_RESULT(delegate_->OnTableSizeExpr(cast<TableSizeExpr>(expr)));
+ break;
+
+ case ExprType::TableFill:
+ CHECK_RESULT(delegate_->OnTableFillExpr(cast<TableFillExpr>(expr)));
+ break;
+
+ case ExprType::RefFunc:
+ CHECK_RESULT(delegate_->OnRefFuncExpr(cast<RefFuncExpr>(expr)));
+ break;
+
+ case ExprType::RefNull:
+ CHECK_RESULT(delegate_->OnRefNullExpr(cast<RefNullExpr>(expr)));
+ break;
+
+ case ExprType::RefIsNull:
+ CHECK_RESULT(delegate_->OnRefIsNullExpr(cast<RefIsNullExpr>(expr)));
+ break;
+
+ case ExprType::Nop:
+ CHECK_RESULT(delegate_->OnNopExpr(cast<NopExpr>(expr)));
+ break;
+
+ case ExprType::Rethrow:
+ CHECK_RESULT(delegate_->OnRethrowExpr(cast<RethrowExpr>(expr)));
+ break;
+
+ case ExprType::Return:
+ CHECK_RESULT(delegate_->OnReturnExpr(cast<ReturnExpr>(expr)));
+ break;
+
+ case ExprType::ReturnCall:
+ CHECK_RESULT(delegate_->OnReturnCallExpr(cast<ReturnCallExpr>(expr)));
+ break;
+
+ case ExprType::ReturnCallIndirect:
+ CHECK_RESULT(delegate_->OnReturnCallIndirectExpr(
+ cast<ReturnCallIndirectExpr>(expr)));
+ break;
+
+ case ExprType::Select:
+ CHECK_RESULT(delegate_->OnSelectExpr(cast<SelectExpr>(expr)));
+ break;
+
+ case ExprType::Store:
+ CHECK_RESULT(delegate_->OnStoreExpr(cast<StoreExpr>(expr)));
+ break;
+
+ case ExprType::Throw:
+ CHECK_RESULT(delegate_->OnThrowExpr(cast<ThrowExpr>(expr)));
+ break;
+
+ case ExprType::Try: {
+ auto try_expr = cast<TryExpr>(expr);
+ CHECK_RESULT(delegate_->BeginTryExpr(try_expr));
+ PushExprlist(State::Try, expr, try_expr->block.exprs);
+ break;
+ }
+
+ case ExprType::Unary:
+ CHECK_RESULT(delegate_->OnUnaryExpr(cast<UnaryExpr>(expr)));
+ break;
+
+ case ExprType::Ternary:
+ CHECK_RESULT(delegate_->OnTernaryExpr(cast<TernaryExpr>(expr)));
+ break;
+
+ case ExprType::SimdLaneOp: {
+ CHECK_RESULT(delegate_->OnSimdLaneOpExpr(cast<SimdLaneOpExpr>(expr)));
+ break;
+ }
+
+ case ExprType::SimdLoadLane: {
+ CHECK_RESULT(delegate_->OnSimdLoadLaneExpr(cast<SimdLoadLaneExpr>(expr)));
+ break;
+ }
+
+ case ExprType::SimdStoreLane: {
+ CHECK_RESULT(
+ delegate_->OnSimdStoreLaneExpr(cast<SimdStoreLaneExpr>(expr)));
+ break;
+ }
+
+ case ExprType::SimdShuffleOp: {
+ CHECK_RESULT(
+ delegate_->OnSimdShuffleOpExpr(cast<SimdShuffleOpExpr>(expr)));
+ break;
+ }
+
+ case ExprType::Unreachable:
+ CHECK_RESULT(delegate_->OnUnreachableExpr(cast<UnreachableExpr>(expr)));
+ break;
+ }
+
+ return Result::Ok;
+}
+
+void ExprVisitor::PushDefault(Expr* expr) {
+ state_stack_.emplace_back(State::Default);
+ expr_stack_.emplace_back(expr);
+}
+
+void ExprVisitor::PopDefault() {
+ state_stack_.pop_back();
+ expr_stack_.pop_back();
+}
+
+void ExprVisitor::PushExprlist(State state, Expr* expr, ExprList& expr_list) {
+ state_stack_.emplace_back(state);
+ expr_stack_.emplace_back(expr);
+ expr_iter_stack_.emplace_back(expr_list.begin());
+}
+
+void ExprVisitor::PopExprlist() {
+ state_stack_.pop_back();
+ expr_stack_.pop_back();
+ expr_iter_stack_.pop_back();
+}
+
+void ExprVisitor::PushCatch(Expr* expr,
+ Index catch_index,
+ ExprList& expr_list) {
+ state_stack_.emplace_back(State::Catch);
+ expr_stack_.emplace_back(expr);
+ expr_iter_stack_.emplace_back(expr_list.begin());
+ catch_index_stack_.emplace_back(catch_index);
+}
+
+void ExprVisitor::PopCatch() {
+ state_stack_.pop_back();
+ expr_stack_.pop_back();
+ expr_iter_stack_.pop_back();
+ catch_index_stack_.pop_back();
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/feature.cc b/third_party/wasm2c/src/feature.cc
new file mode 100644
index 0000000000..88b474c290
--- /dev/null
+++ b/third_party/wasm2c/src/feature.cc
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/feature.h"
+
+#include "wabt/option-parser.h"
+
+namespace wabt {
+
+void Features::AddOptions(OptionParser* parser) {
+#define WABT_FEATURE(variable, flag, default_, help) \
+ if (default_ == true) { \
+ parser->AddOption("disable-" flag, "Disable " help, \
+ [this]() { disable_##variable(); }); \
+ } else { \
+ parser->AddOption("enable-" flag, "Enable " help, \
+ [this]() { enable_##variable(); }); \
+ }
+
+#include "wabt/feature.def"
+#undef WABT_FEATURE
+ parser->AddOption("enable-all", "Enable all features",
+ [this]() { EnableAll(); });
+}
+
+void Features::UpdateDependencies() {
+ // Exception handling requires reference types.
+ if (exceptions_enabled_) {
+ reference_types_enabled_ = true;
+ }
+
+ // Function references require reference types.
+ if (function_references_enabled_) {
+ reference_types_enabled_ = true;
+ }
+
+ // Reference types requires bulk memory.
+ if (!bulk_memory_enabled_) {
+ reference_types_enabled_ = false;
+ }
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/filenames.cc b/third_party/wasm2c/src/filenames.cc
new file mode 100644
index 0000000000..9f020f46dd
--- /dev/null
+++ b/third_party/wasm2c/src/filenames.cc
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/filenames.h"
+
+namespace wabt {
+
+const char* kWasmExtension = ".wasm";
+
+const char* kWatExtension = ".wat";
+
+std::string_view StripExtension(std::string_view filename) {
+ return filename.substr(0, filename.find_last_of('.'));
+}
+
+std::string_view GetBasename(std::string_view filename) {
+ size_t last_slash = filename.find_last_of('/');
+ size_t last_backslash = filename.find_last_of('\\');
+ if (last_slash == std::string_view::npos &&
+ last_backslash == std::string_view::npos) {
+ return filename;
+ }
+
+ if (last_slash == std::string_view::npos) {
+ if (last_backslash == std::string_view::npos) {
+ return filename;
+ }
+ last_slash = last_backslash;
+ } else if (last_backslash != std::string_view::npos) {
+ last_slash = std::max(last_slash, last_backslash);
+ }
+
+ return filename.substr(last_slash + 1);
+}
+
+std::string_view GetExtension(std::string_view filename) {
+ size_t pos = filename.find_last_of('.');
+ if (pos == std::string_view::npos) {
+ return "";
+ }
+ return filename.substr(pos);
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/generate-names.cc b/third_party/wasm2c/src/generate-names.cc
new file mode 100644
index 0000000000..ec3612d704
--- /dev/null
+++ b/third_party/wasm2c/src/generate-names.cc
@@ -0,0 +1,433 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/generate-names.h"
+
+#include <cassert>
+#include <cstdio>
+#include <string>
+#include <vector>
+
+#include "wabt/cast.h"
+#include "wabt/expr-visitor.h"
+#include "wabt/ir.h"
+
+namespace wabt {
+
+namespace {
+
+class NameGenerator : public ExprVisitor::DelegateNop {
+ public:
+ NameGenerator(NameOpts opts);
+
+ Result VisitModule(Module* module);
+
+ // Implementation of ExprVisitor::DelegateNop.
+ Result BeginBlockExpr(BlockExpr* expr) override;
+ Result BeginTryExpr(TryExpr* expr) override;
+ Result BeginLoopExpr(LoopExpr* expr) override;
+ Result BeginIfExpr(IfExpr* expr) override;
+
+ private:
+ static bool HasName(const std::string& str);
+
+ // Generate a name with the given prefix, followed by the index and
+ // optionally a disambiguating number. If index == kInvalidIndex, the index
+ // is not appended.
+ void GenerateName(const char* prefix,
+ Index index,
+ unsigned disambiguator,
+ std::string* out_str);
+
+ // Like GenerateName, but only generates a name if |out_str| is empty.
+ void MaybeGenerateName(const char* prefix, Index index, std::string* out_str);
+
+ // Generate a name via GenerateName and bind it to the given binding hash. If
+ // the name already exists, the name will be disambiguated until it can be
+ // added.
+ void GenerateAndBindName(BindingHash* bindings,
+ const char* prefix,
+ Index index,
+ std::string* out_str);
+
+ // Like GenerateAndBindName, but only generates a name if |out_str| is empty.
+ void MaybeGenerateAndBindName(BindingHash* bindings,
+ const char* prefix,
+ Index index,
+ std::string* out_str);
+
+ // Like MaybeGenerateAndBindName but uses the name directly, without
+ // appending the index. If the name already exists, a disambiguating suffix
+ // is added.
+ void MaybeUseAndBindName(BindingHash* bindings,
+ const char* name,
+ Index index,
+ std::string* out_str);
+
+ void GenerateAndBindLocalNames(Func* func);
+
+ template <typename T>
+ Result VisitAll(const std::vector<T*>& items,
+ Result (NameGenerator::*func)(Index, T*));
+
+ Result VisitFunc(Index func_index, Func* func);
+ Result VisitGlobal(Index global_index, Global* global);
+ Result VisitType(Index func_type_index, TypeEntry* type);
+ Result VisitTable(Index table_index, Table* table);
+ Result VisitMemory(Index memory_index, Memory* memory);
+ Result VisitTag(Index tag_index, Tag* tag);
+ Result VisitDataSegment(Index data_segment_index, DataSegment* data_segment);
+ Result VisitElemSegment(Index elem_segment_index, ElemSegment* elem_segment);
+ Result VisitImport(Import* import);
+ Result VisitExport(Export* export_);
+
+ Module* module_ = nullptr;
+ ExprVisitor visitor_;
+ Index label_count_ = 0;
+
+ Index num_func_imports_ = 0;
+ Index num_table_imports_ = 0;
+ Index num_memory_imports_ = 0;
+ Index num_global_imports_ = 0;
+ Index num_tag_imports_ = 0;
+
+ NameOpts opts_;
+};
+
+NameGenerator::NameGenerator(NameOpts opts) : visitor_(this), opts_(opts) {}
+
+// static
+bool NameGenerator::HasName(const std::string& str) {
+ return !str.empty();
+}
+
+void NameGenerator::GenerateName(const char* prefix,
+ Index index,
+ unsigned disambiguator,
+ std::string* str) {
+ *str = "$";
+ *str += prefix;
+ if (index != kInvalidIndex) {
+ if (opts_ & NameOpts::AlphaNames) {
+ // For params and locals, do not use a prefix char.
+ if (!strcmp(prefix, "p") || !strcmp(prefix, "l")) {
+ str->pop_back();
+ } else {
+ *str += '_';
+ }
+ *str += IndexToAlphaName(index);
+ } else {
+ *str += std::to_string(index);
+ }
+ }
+ if (disambiguator != 0) {
+ *str += '_' + std::to_string(disambiguator);
+ }
+}
+
+void NameGenerator::MaybeGenerateName(const char* prefix,
+ Index index,
+ std::string* str) {
+ if (!HasName(*str)) {
+ // There's no bindings hash, so the name can't be a duplicate. Therefore it
+ // doesn't need a disambiguating number.
+ GenerateName(prefix, index, 0, str);
+ }
+}
+
+void NameGenerator::GenerateAndBindName(BindingHash* bindings,
+ const char* prefix,
+ Index index,
+ std::string* str) {
+ unsigned disambiguator = 0;
+ while (true) {
+ GenerateName(prefix, index, disambiguator, str);
+ if (bindings->find(*str) == bindings->end()) {
+ bindings->emplace(*str, Binding(index));
+ break;
+ }
+
+ disambiguator++;
+ }
+}
+
+void NameGenerator::MaybeGenerateAndBindName(BindingHash* bindings,
+ const char* prefix,
+ Index index,
+ std::string* str) {
+ if (!HasName(*str)) {
+ GenerateAndBindName(bindings, prefix, index, str);
+ }
+}
+
+void NameGenerator::MaybeUseAndBindName(BindingHash* bindings,
+ const char* name,
+ Index index,
+ std::string* str) {
+ if (!HasName(*str)) {
+ unsigned disambiguator = 0;
+ while (true) {
+ GenerateName(name, kInvalidIndex, disambiguator, str);
+ if (bindings->find(*str) == bindings->end()) {
+ bindings->emplace(*str, Binding(index));
+ break;
+ }
+
+ disambiguator++;
+ }
+ }
+}
+
+void NameGenerator::GenerateAndBindLocalNames(Func* func) {
+ std::vector<std::string> index_to_name;
+ MakeTypeBindingReverseMapping(func->GetNumParamsAndLocals(), func->bindings,
+ &index_to_name);
+ for (size_t i = 0; i < index_to_name.size(); ++i) {
+ const std::string& old_name = index_to_name[i];
+ if (!old_name.empty()) {
+ continue;
+ }
+
+ const char* prefix = i < func->GetNumParams() ? "p" : "l";
+ std::string new_name;
+ GenerateAndBindName(&func->bindings, prefix, i, &new_name);
+ index_to_name[i] = new_name;
+ }
+}
+
+Result NameGenerator::BeginBlockExpr(BlockExpr* expr) {
+ MaybeGenerateName("B", label_count_++, &expr->block.label);
+ return Result::Ok;
+}
+
+Result NameGenerator::BeginTryExpr(TryExpr* expr) {
+ MaybeGenerateName("T", label_count_++, &expr->block.label);
+ return Result::Ok;
+}
+
+Result NameGenerator::BeginLoopExpr(LoopExpr* expr) {
+ MaybeGenerateName("L", label_count_++, &expr->block.label);
+ return Result::Ok;
+}
+
+Result NameGenerator::BeginIfExpr(IfExpr* expr) {
+ MaybeGenerateName("I", label_count_++, &expr->true_.label);
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitFunc(Index func_index, Func* func) {
+ MaybeGenerateAndBindName(&module_->func_bindings, "f", func_index,
+ &func->name);
+ GenerateAndBindLocalNames(func);
+
+ label_count_ = 0;
+ CHECK_RESULT(visitor_.VisitFunc(func));
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitGlobal(Index global_index, Global* global) {
+ MaybeGenerateAndBindName(&module_->global_bindings, "g", global_index,
+ &global->name);
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitType(Index type_index, TypeEntry* type) {
+ MaybeGenerateAndBindName(&module_->type_bindings, "t", type_index,
+ &type->name);
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitTable(Index table_index, Table* table) {
+ MaybeGenerateAndBindName(&module_->table_bindings, "T", table_index,
+ &table->name);
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitMemory(Index memory_index, Memory* memory) {
+ MaybeGenerateAndBindName(&module_->memory_bindings, "M", memory_index,
+ &memory->name);
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitTag(Index tag_index, Tag* tag) {
+ MaybeGenerateAndBindName(&module_->tag_bindings, "e", tag_index, &tag->name);
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitDataSegment(Index data_segment_index,
+ DataSegment* data_segment) {
+ MaybeGenerateAndBindName(&module_->data_segment_bindings, "d",
+ data_segment_index, &data_segment->name);
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitElemSegment(Index elem_segment_index,
+ ElemSegment* elem_segment) {
+ MaybeGenerateAndBindName(&module_->elem_segment_bindings, "e",
+ elem_segment_index, &elem_segment->name);
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitImport(Import* import) {
+ BindingHash* bindings = nullptr;
+ std::string* name = nullptr;
+ Index index = kInvalidIndex;
+
+ switch (import->kind()) {
+ case ExternalKind::Func:
+ if (auto* func_import = cast<FuncImport>(import)) {
+ bindings = &module_->func_bindings;
+ name = &func_import->func.name;
+ index = num_func_imports_++;
+ }
+ break;
+
+ case ExternalKind::Table:
+ if (auto* table_import = cast<TableImport>(import)) {
+ bindings = &module_->table_bindings;
+ name = &table_import->table.name;
+ index = num_table_imports_++;
+ }
+ break;
+
+ case ExternalKind::Memory:
+ if (auto* memory_import = cast<MemoryImport>(import)) {
+ bindings = &module_->memory_bindings;
+ name = &memory_import->memory.name;
+ index = num_memory_imports_++;
+ }
+ break;
+
+ case ExternalKind::Global:
+ if (auto* global_import = cast<GlobalImport>(import)) {
+ bindings = &module_->global_bindings;
+ name = &global_import->global.name;
+ index = num_global_imports_++;
+ }
+ break;
+
+ case ExternalKind::Tag:
+ if (auto* tag_import = cast<TagImport>(import)) {
+ bindings = &module_->tag_bindings;
+ name = &tag_import->tag.name;
+ index = num_tag_imports_++;
+ }
+ break;
+ }
+
+ if (bindings && name) {
+ assert(index != kInvalidIndex);
+ std::string new_name = import->module_name + '.' + import->field_name;
+ MaybeUseAndBindName(bindings, new_name.c_str(), index, name);
+ }
+
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitExport(Export* export_) {
+ BindingHash* bindings = nullptr;
+ std::string* name = nullptr;
+ Index index = kInvalidIndex;
+
+ switch (export_->kind) {
+ case ExternalKind::Func:
+ if (Func* func = module_->GetFunc(export_->var)) {
+ index = module_->GetFuncIndex(export_->var);
+ bindings = &module_->func_bindings;
+ name = &func->name;
+ }
+ break;
+
+ case ExternalKind::Table:
+ if (Table* table = module_->GetTable(export_->var)) {
+ index = module_->GetTableIndex(export_->var);
+ bindings = &module_->table_bindings;
+ name = &table->name;
+ }
+ break;
+
+ case ExternalKind::Memory:
+ if (Memory* memory = module_->GetMemory(export_->var)) {
+ index = module_->GetMemoryIndex(export_->var);
+ bindings = &module_->memory_bindings;
+ name = &memory->name;
+ }
+ break;
+
+ case ExternalKind::Global:
+ if (Global* global = module_->GetGlobal(export_->var)) {
+ index = module_->GetGlobalIndex(export_->var);
+ bindings = &module_->global_bindings;
+ name = &global->name;
+ }
+ break;
+
+ case ExternalKind::Tag:
+ if (Tag* tag = module_->GetTag(export_->var)) {
+ index = module_->GetTagIndex(export_->var);
+ bindings = &module_->tag_bindings;
+ name = &tag->name;
+ }
+ break;
+ }
+
+ if (bindings && name) {
+ MaybeUseAndBindName(bindings, export_->name.c_str(), index, name);
+ }
+
+ return Result::Ok;
+}
+
+template <typename T>
+Result NameGenerator::VisitAll(const std::vector<T*>& items,
+ Result (NameGenerator::*func)(Index, T*)) {
+ for (Index i = 0; i < items.size(); ++i) {
+ CHECK_RESULT((this->*func)(i, items[i]));
+ }
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitModule(Module* module) {
+ module_ = module;
+ // Visit imports and exports first to give better names, derived from the
+ // import/export name.
+ for (auto* import : module->imports) {
+ CHECK_RESULT(VisitImport(import));
+ }
+ for (auto* export_ : module->exports) {
+ CHECK_RESULT(VisitExport(export_));
+ }
+
+ VisitAll(module->globals, &NameGenerator::VisitGlobal);
+ VisitAll(module->types, &NameGenerator::VisitType);
+ VisitAll(module->funcs, &NameGenerator::VisitFunc);
+ VisitAll(module->tables, &NameGenerator::VisitTable);
+ VisitAll(module->memories, &NameGenerator::VisitMemory);
+ VisitAll(module->tags, &NameGenerator::VisitTag);
+ VisitAll(module->data_segments, &NameGenerator::VisitDataSegment);
+ VisitAll(module->elem_segments, &NameGenerator::VisitElemSegment);
+ module_ = nullptr;
+ return Result::Ok;
+}
+
+} // end anonymous namespace
+
+Result GenerateNames(Module* module, NameOpts opts) {
+ NameGenerator generator(opts);
+ return generator.VisitModule(module);
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/ir-util.cc b/third_party/wasm2c/src/ir-util.cc
new file mode 100644
index 0000000000..ee9262cc0c
--- /dev/null
+++ b/third_party/wasm2c/src/ir-util.cc
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/ir-util.h"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cinttypes>
+#include <cstdarg>
+#include <cstdio>
+#include <iterator>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "wabt/cast.h"
+#include "wabt/common.h"
+#include "wabt/expr-visitor.h"
+#include "wabt/ir-util.h"
+#include "wabt/ir.h"
+#include "wabt/literal.h"
+#include "wabt/stream.h"
+
+#define WABT_TRACING 0
+#include "wabt/tracing.h"
+
+using namespace wabt;
+
+const Label* ModuleContext::GetLabel(const Var& var) const {
+ if (var.is_name()) {
+ for (Index i = GetLabelStackSize(); i > 0; --i) {
+ auto label = &label_stack_[i - 1];
+ if (label->name == var.name()) {
+ return label;
+ }
+ }
+ } else if (var.index() < GetLabelStackSize()) {
+ auto label = &label_stack_[GetLabelStackSize() - var.index() - 1];
+ return label;
+ }
+ return nullptr;
+}
+
+Index ModuleContext::GetLabelArity(const Var& var) const {
+ auto label = GetLabel(var);
+ if (!label) {
+ return 0;
+ }
+
+ return label->label_type == LabelType::Loop ? label->param_types.size()
+ : label->result_types.size();
+}
+
+Index ModuleContext::GetFuncParamCount(const Var& var) const {
+ const Func* func = module.GetFunc(var);
+ return func ? func->GetNumParams() : 0;
+}
+
+Index ModuleContext::GetFuncResultCount(const Var& var) const {
+ const Func* func = module.GetFunc(var);
+ return func ? func->GetNumResults() : 0;
+}
+
+void ModuleContext::BeginBlock(LabelType label_type, const Block& block) {
+ label_stack_.emplace_back(label_type, block.label, block.decl.sig.param_types,
+ block.decl.sig.result_types);
+}
+
+void ModuleContext::EndBlock() {
+ label_stack_.pop_back();
+}
+
+void ModuleContext::BeginFunc(const Func& func) {
+ label_stack_.clear();
+ label_stack_.emplace_back(LabelType::Func, std::string(), TypeVector(),
+ func.decl.sig.result_types);
+ current_func_ = &func;
+}
+
+void ModuleContext::EndFunc() {
+ current_func_ = nullptr;
+}
+
+ModuleContext::Arities ModuleContext::GetExprArity(const Expr& expr) const {
+ switch (expr.type()) {
+ case ExprType::AtomicNotify:
+ case ExprType::AtomicRmw:
+ case ExprType::Binary:
+ case ExprType::Compare:
+ case ExprType::TableGrow:
+ return {2, 1};
+
+ case ExprType::AtomicStore:
+ case ExprType::Store:
+ case ExprType::TableSet:
+ return {2, 0};
+
+ case ExprType::Block:
+ return {0, cast<BlockExpr>(&expr)->block.decl.sig.GetNumResults()};
+
+ case ExprType::Br:
+ return {GetLabelArity(cast<BrExpr>(&expr)->var), 1, true};
+
+ case ExprType::BrIf: {
+ Index arity = GetLabelArity(cast<BrIfExpr>(&expr)->var);
+ return {arity + 1, arity};
+ }
+
+ case ExprType::BrTable:
+ return {GetLabelArity(cast<BrTableExpr>(&expr)->default_target) + 1, 1,
+ true};
+
+ case ExprType::Call: {
+ const Var& var = cast<CallExpr>(&expr)->var;
+ return {GetFuncParamCount(var), GetFuncResultCount(var)};
+ }
+
+ case ExprType::ReturnCall: {
+ const Var& var = cast<ReturnCallExpr>(&expr)->var;
+ return {GetFuncParamCount(var), GetFuncResultCount(var), true};
+ }
+
+ case ExprType::CallIndirect: {
+ const auto* ci_expr = cast<CallIndirectExpr>(&expr);
+ return {ci_expr->decl.GetNumParams() + 1, ci_expr->decl.GetNumResults()};
+ }
+
+ case ExprType::CallRef: {
+ const Var& var = cast<CallRefExpr>(&expr)->function_type_index;
+ return {GetFuncParamCount(var) + 1, GetFuncResultCount(var)};
+ }
+
+ case ExprType::ReturnCallIndirect: {
+ const auto* rci_expr = cast<ReturnCallIndirectExpr>(&expr);
+ return {rci_expr->decl.GetNumParams() + 1, rci_expr->decl.GetNumResults(),
+ true};
+ }
+
+ case ExprType::Const:
+ case ExprType::GlobalGet:
+ case ExprType::LocalGet:
+ case ExprType::MemorySize:
+ case ExprType::TableSize:
+ case ExprType::RefNull:
+ case ExprType::RefFunc:
+ return {0, 1};
+
+ case ExprType::Unreachable:
+ return {0, 1, true};
+
+ case ExprType::DataDrop:
+ case ExprType::ElemDrop:
+ case ExprType::AtomicFence:
+ case ExprType::CodeMetadata:
+ return {0, 0};
+
+ case ExprType::MemoryInit:
+ case ExprType::TableInit:
+ case ExprType::MemoryFill:
+ case ExprType::MemoryCopy:
+ case ExprType::TableCopy:
+ case ExprType::TableFill:
+ return {3, 0};
+
+ case ExprType::AtomicLoad:
+ case ExprType::Convert:
+ case ExprType::Load:
+ case ExprType::LocalTee:
+ case ExprType::MemoryGrow:
+ case ExprType::Unary:
+ case ExprType::TableGet:
+ case ExprType::RefIsNull:
+ case ExprType::LoadSplat:
+ case ExprType::LoadZero:
+ return {1, 1};
+
+ case ExprType::Drop:
+ case ExprType::GlobalSet:
+ case ExprType::LocalSet:
+ return {1, 0};
+
+ case ExprType::If:
+ return {1, cast<IfExpr>(&expr)->true_.decl.sig.GetNumResults()};
+
+ case ExprType::Loop:
+ return {0, cast<LoopExpr>(&expr)->block.decl.sig.GetNumResults()};
+
+ case ExprType::Nop:
+ return {0, 0};
+
+ case ExprType::Return:
+ return {static_cast<Index>(current_func_->decl.sig.result_types.size()),
+ 1, true};
+
+ case ExprType::Rethrow:
+ return {0, 0, true};
+
+ case ExprType::AtomicRmwCmpxchg:
+ case ExprType::AtomicWait:
+ case ExprType::Select:
+ return {3, 1};
+
+ case ExprType::Throw: {
+ auto throw_ = cast<ThrowExpr>(&expr);
+ Index operand_count = 0;
+ if (Tag* tag = module.GetTag(throw_->var)) {
+ operand_count = tag->decl.sig.param_types.size();
+ }
+ return {operand_count, 0, true};
+ }
+
+ case ExprType::Try:
+ return {0, cast<TryExpr>(&expr)->block.decl.sig.GetNumResults()};
+
+ case ExprType::Ternary:
+ return {3, 1};
+
+ case ExprType::SimdLaneOp: {
+ const Opcode opcode = cast<SimdLaneOpExpr>(&expr)->opcode;
+ switch (opcode) {
+ case Opcode::I8X16ExtractLaneS:
+ case Opcode::I8X16ExtractLaneU:
+ case Opcode::I16X8ExtractLaneS:
+ case Opcode::I16X8ExtractLaneU:
+ case Opcode::I32X4ExtractLane:
+ case Opcode::I64X2ExtractLane:
+ case Opcode::F32X4ExtractLane:
+ case Opcode::F64X2ExtractLane:
+ return {1, 1};
+
+ case Opcode::I8X16ReplaceLane:
+ case Opcode::I16X8ReplaceLane:
+ case Opcode::I32X4ReplaceLane:
+ case Opcode::I64X2ReplaceLane:
+ case Opcode::F32X4ReplaceLane:
+ case Opcode::F64X2ReplaceLane:
+ return {2, 1};
+
+ default:
+ fprintf(stderr, "Invalid Opcode for expr type: %s\n",
+ GetExprTypeName(expr));
+ assert(0);
+ return {0, 0};
+ }
+ }
+
+ case ExprType::SimdLoadLane:
+ case ExprType::SimdStoreLane: {
+ return {2, 1};
+ }
+
+ case ExprType::SimdShuffleOp:
+ return {2, 1};
+ }
+
+ WABT_UNREACHABLE;
+}
diff --git a/third_party/wasm2c/src/ir.cc b/third_party/wasm2c/src/ir.cc
new file mode 100644
index 0000000000..88db7d5f46
--- /dev/null
+++ b/third_party/wasm2c/src/ir.cc
@@ -0,0 +1,701 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/ir.h"
+
+#include <cassert>
+#include <cstddef>
+#include <numeric>
+
+#include "wabt/cast.h"
+
+namespace {
+
+const char* ExprTypeName[] = {
+ "AtomicFence",
+ "AtomicLoad",
+ "AtomicRmw",
+ "AtomicRmwCmpxchg",
+ "AtomicStore",
+ "AtomicNotify",
+ "AtomicWait",
+ "Binary",
+ "Block",
+ "Br",
+ "BrIf",
+ "BrTable",
+ "Call",
+ "CallIndirect",
+ "CallRef",
+ "CodeMetadata",
+ "Compare",
+ "Const",
+ "Convert",
+ "Drop",
+ "GlobalGet",
+ "GlobalSet",
+ "If",
+ "Load",
+ "LocalGet",
+ "LocalSet",
+ "LocalTee",
+ "Loop",
+ "MemoryCopy",
+ "DataDrop",
+ "MemoryFill",
+ "MemoryGrow",
+ "MemoryInit",
+ "MemorySize",
+ "Nop",
+ "RefIsNull",
+ "RefFunc",
+ "RefNull",
+ "Rethrow",
+ "Return",
+ "ReturnCall",
+ "ReturnCallIndirect",
+ "Select",
+ "SimdLaneOp",
+ "SimdLoadLane",
+ "SimdStoreLane",
+ "SimdShuffleOp",
+ "LoadSplat",
+ "LoadZero",
+ "Store",
+ "TableCopy",
+ "ElemDrop",
+ "TableInit",
+ "TableGet",
+ "TableGrow",
+ "TableSize",
+ "TableSet",
+ "TableFill",
+ "Ternary",
+ "Throw",
+ "Try",
+ "Unary",
+ "Unreachable",
+};
+
+} // end of anonymous namespace
+
+namespace wabt {
+
+const char* GetExprTypeName(ExprType type) {
+ static_assert(WABT_ENUM_COUNT(ExprType) == WABT_ARRAY_SIZE(ExprTypeName),
+ "Malformed ExprTypeName array");
+ return ExprTypeName[size_t(type)];
+}
+
+const char* GetExprTypeName(const Expr& expr) {
+ return GetExprTypeName(expr.type());
+}
+
+bool FuncSignature::operator==(const FuncSignature& rhs) const {
+ return param_types == rhs.param_types && result_types == rhs.result_types;
+}
+
+const Export* Module::GetExport(std::string_view name) const {
+ Index index = export_bindings.FindIndex(name);
+ if (index >= exports.size()) {
+ return nullptr;
+ }
+ return exports[index];
+}
+
+Index Module::GetFuncIndex(const Var& var) const {
+ return func_bindings.FindIndex(var);
+}
+
+Index Module::GetGlobalIndex(const Var& var) const {
+ return global_bindings.FindIndex(var);
+}
+
+Index Module::GetTableIndex(const Var& var) const {
+ return table_bindings.FindIndex(var);
+}
+
+Index Module::GetMemoryIndex(const Var& var) const {
+ return memory_bindings.FindIndex(var);
+}
+
+Index Module::GetFuncTypeIndex(const Var& var) const {
+ return type_bindings.FindIndex(var);
+}
+
+Index Module::GetTagIndex(const Var& var) const {
+ return tag_bindings.FindIndex(var);
+}
+
+Index Module::GetDataSegmentIndex(const Var& var) const {
+ return data_segment_bindings.FindIndex(var);
+}
+
+Index Module::GetElemSegmentIndex(const Var& var) const {
+ return elem_segment_bindings.FindIndex(var);
+}
+
+bool Module::IsImport(ExternalKind kind, const Var& var) const {
+ switch (kind) {
+ case ExternalKind::Func:
+ return GetFuncIndex(var) < num_func_imports;
+
+ case ExternalKind::Global:
+ return GetGlobalIndex(var) < num_global_imports;
+
+ case ExternalKind::Memory:
+ return GetMemoryIndex(var) < num_memory_imports;
+
+ case ExternalKind::Table:
+ return GetTableIndex(var) < num_table_imports;
+
+ case ExternalKind::Tag:
+ return GetTagIndex(var) < num_tag_imports;
+
+ default:
+ return false;
+ }
+}
+
+void LocalTypes::Set(const TypeVector& types) {
+ decls_.clear();
+ if (types.empty()) {
+ return;
+ }
+
+ Type type = types[0];
+ Index count = 1;
+ for (Index i = 1; i < types.size(); ++i) {
+ if (types[i] != type) {
+ decls_.emplace_back(type, count);
+ type = types[i];
+ count = 1;
+ } else {
+ ++count;
+ }
+ }
+ decls_.emplace_back(type, count);
+}
+
+Index LocalTypes::size() const {
+ return std::accumulate(
+ decls_.begin(), decls_.end(), 0,
+ [](Index sum, const Decl& decl) { return sum + decl.second; });
+}
+
+Type LocalTypes::operator[](Index i) const {
+ Index count = 0;
+ for (auto decl : decls_) {
+ if (i < count + decl.second) {
+ return decl.first;
+ }
+ count += decl.second;
+ }
+ assert(i < count);
+ return Type::Any;
+}
+
+Type Func::GetLocalType(Index index) const {
+ Index num_params = decl.GetNumParams();
+ if (index < num_params) {
+ return GetParamType(index);
+ } else {
+ index -= num_params;
+ assert(index < local_types.size());
+ return local_types[index];
+ }
+}
+
+Type Func::GetLocalType(const Var& var) const {
+ return GetLocalType(GetLocalIndex(var));
+}
+
+Index Func::GetLocalIndex(const Var& var) const {
+ if (var.is_index()) {
+ return var.index();
+ }
+ return bindings.FindIndex(var);
+}
+
+const Func* Module::GetFunc(const Var& var) const {
+ return const_cast<Module*>(this)->GetFunc(var);
+}
+
+Func* Module::GetFunc(const Var& var) {
+ Index index = func_bindings.FindIndex(var);
+ if (index >= funcs.size()) {
+ return nullptr;
+ }
+ return funcs[index];
+}
+
+const Global* Module::GetGlobal(const Var& var) const {
+ return const_cast<Module*>(this)->GetGlobal(var);
+}
+
+Global* Module::GetGlobal(const Var& var) {
+ Index index = global_bindings.FindIndex(var);
+ if (index >= globals.size()) {
+ return nullptr;
+ }
+ return globals[index];
+}
+
+const Table* Module::GetTable(const Var& var) const {
+ return const_cast<Module*>(this)->GetTable(var);
+}
+
+Table* Module::GetTable(const Var& var) {
+ Index index = table_bindings.FindIndex(var);
+ if (index >= tables.size()) {
+ return nullptr;
+ }
+ return tables[index];
+}
+
+const Memory* Module::GetMemory(const Var& var) const {
+ return const_cast<Module*>(this)->GetMemory(var);
+}
+
+Memory* Module::GetMemory(const Var& var) {
+ Index index = memory_bindings.FindIndex(var);
+ if (index >= memories.size()) {
+ return nullptr;
+ }
+ return memories[index];
+}
+
+Tag* Module::GetTag(const Var& var) const {
+ Index index = GetTagIndex(var);
+ if (index >= tags.size()) {
+ return nullptr;
+ }
+ return tags[index];
+}
+
+const DataSegment* Module::GetDataSegment(const Var& var) const {
+ return const_cast<Module*>(this)->GetDataSegment(var);
+}
+
+DataSegment* Module::GetDataSegment(const Var& var) {
+ Index index = data_segment_bindings.FindIndex(var);
+ if (index >= data_segments.size()) {
+ return nullptr;
+ }
+ return data_segments[index];
+}
+
+const ElemSegment* Module::GetElemSegment(const Var& var) const {
+ return const_cast<Module*>(this)->GetElemSegment(var);
+}
+
+ElemSegment* Module::GetElemSegment(const Var& var) {
+ Index index = elem_segment_bindings.FindIndex(var);
+ if (index >= elem_segments.size()) {
+ return nullptr;
+ }
+ return elem_segments[index];
+}
+
+const FuncType* Module::GetFuncType(const Var& var) const {
+ return const_cast<Module*>(this)->GetFuncType(var);
+}
+
+FuncType* Module::GetFuncType(const Var& var) {
+ Index index = type_bindings.FindIndex(var);
+ if (index >= types.size()) {
+ return nullptr;
+ }
+ return dyn_cast<FuncType>(types[index]);
+}
+
+Index Module::GetFuncTypeIndex(const FuncSignature& sig) const {
+ for (size_t i = 0; i < types.size(); ++i) {
+ if (auto* func_type = dyn_cast<FuncType>(types[i])) {
+ if (func_type->sig == sig) {
+ return i;
+ }
+ }
+ }
+ return kInvalidIndex;
+}
+
+Index Module::GetFuncTypeIndex(const FuncDeclaration& decl) const {
+ if (decl.has_func_type) {
+ return GetFuncTypeIndex(decl.type_var);
+ } else {
+ return GetFuncTypeIndex(decl.sig);
+ }
+}
+
+void Module::AppendField(std::unique_ptr<DataSegmentModuleField> field) {
+ DataSegment& data_segment = field->data_segment;
+ if (!data_segment.name.empty()) {
+ data_segment_bindings.emplace(data_segment.name,
+ Binding(field->loc, data_segments.size()));
+ }
+ data_segments.push_back(&data_segment);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<ElemSegmentModuleField> field) {
+ ElemSegment& elem_segment = field->elem_segment;
+ if (!elem_segment.name.empty()) {
+ elem_segment_bindings.emplace(elem_segment.name,
+ Binding(field->loc, elem_segments.size()));
+ }
+ elem_segments.push_back(&elem_segment);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<TagModuleField> field) {
+ Tag& tag = field->tag;
+ if (!tag.name.empty()) {
+ tag_bindings.emplace(tag.name, Binding(field->loc, tags.size()));
+ }
+ tags.push_back(&tag);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<ExportModuleField> field) {
+ // Exported names are allowed to be empty.
+ Export& export_ = field->export_;
+ export_bindings.emplace(export_.name, Binding(field->loc, exports.size()));
+ exports.push_back(&export_);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<FuncModuleField> field) {
+ Func& func = field->func;
+ if (!func.name.empty()) {
+ func_bindings.emplace(func.name, Binding(field->loc, funcs.size()));
+ }
+ funcs.push_back(&func);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<TypeModuleField> field) {
+ TypeEntry& type = *field->type;
+ if (!type.name.empty()) {
+ type_bindings.emplace(type.name, Binding(field->loc, types.size()));
+ }
+ types.push_back(&type);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<GlobalModuleField> field) {
+ Global& global = field->global;
+ if (!global.name.empty()) {
+ global_bindings.emplace(global.name, Binding(field->loc, globals.size()));
+ }
+ globals.push_back(&global);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<ImportModuleField> field) {
+ Import* import = field->import.get();
+ const std::string* name = nullptr;
+ BindingHash* bindings = nullptr;
+ Index index = kInvalidIndex;
+
+ switch (import->kind()) {
+ case ExternalKind::Func: {
+ Func& func = cast<FuncImport>(import)->func;
+ name = &func.name;
+ bindings = &func_bindings;
+ index = funcs.size();
+ funcs.push_back(&func);
+ ++num_func_imports;
+ break;
+ }
+
+ case ExternalKind::Table: {
+ Table& table = cast<TableImport>(import)->table;
+ name = &table.name;
+ bindings = &table_bindings;
+ index = tables.size();
+ tables.push_back(&table);
+ ++num_table_imports;
+ break;
+ }
+
+ case ExternalKind::Memory: {
+ Memory& memory = cast<MemoryImport>(import)->memory;
+ name = &memory.name;
+ bindings = &memory_bindings;
+ index = memories.size();
+ memories.push_back(&memory);
+ ++num_memory_imports;
+ break;
+ }
+
+ case ExternalKind::Global: {
+ Global& global = cast<GlobalImport>(import)->global;
+ name = &global.name;
+ bindings = &global_bindings;
+ index = globals.size();
+ globals.push_back(&global);
+ ++num_global_imports;
+ break;
+ }
+
+ case ExternalKind::Tag: {
+ Tag& tag = cast<TagImport>(import)->tag;
+ name = &tag.name;
+ bindings = &tag_bindings;
+ index = tags.size();
+ tags.push_back(&tag);
+ ++num_tag_imports;
+ break;
+ }
+ }
+
+ assert(name && bindings && index != kInvalidIndex);
+ if (!name->empty()) {
+ bindings->emplace(*name, Binding(field->loc, index));
+ }
+ imports.push_back(import);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<MemoryModuleField> field) {
+ Memory& memory = field->memory;
+ if (!memory.name.empty()) {
+ memory_bindings.emplace(memory.name, Binding(field->loc, memories.size()));
+ }
+ memories.push_back(&memory);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<StartModuleField> field) {
+ starts.push_back(&field->start);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<TableModuleField> field) {
+ Table& table = field->table;
+ if (!table.name.empty()) {
+ table_bindings.emplace(table.name, Binding(field->loc, tables.size()));
+ }
+ tables.push_back(&table);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<ModuleField> field) {
+ switch (field->type()) {
+ case ModuleFieldType::Func:
+ AppendField(cast<FuncModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::Global:
+ AppendField(cast<GlobalModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::Import:
+ AppendField(cast<ImportModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::Export:
+ AppendField(cast<ExportModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::Type:
+ AppendField(cast<TypeModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::Table:
+ AppendField(cast<TableModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::ElemSegment:
+ AppendField(cast<ElemSegmentModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::Memory:
+ AppendField(cast<MemoryModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::DataSegment:
+ AppendField(cast<DataSegmentModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::Start:
+ AppendField(cast<StartModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::Tag:
+ AppendField(cast<TagModuleField>(std::move(field)));
+ break;
+ }
+}
+
+void Module::AppendFields(ModuleFieldList* fields) {
+ while (!fields->empty())
+ AppendField(std::unique_ptr<ModuleField>(fields->extract_front()));
+}
+
+const Module* Script::GetFirstModule() const {
+ return const_cast<Script*>(this)->GetFirstModule();
+}
+
+Module* Script::GetFirstModule() {
+ for (const std::unique_ptr<Command>& command : commands) {
+ if (auto* module_command = dyn_cast<ModuleCommand>(command.get())) {
+ return &module_command->module;
+ }
+ }
+ return nullptr;
+}
+
+const Module* Script::GetModule(const Var& var) const {
+ Index index = module_bindings.FindIndex(var);
+ if (index >= commands.size()) {
+ return nullptr;
+ }
+ auto* command = commands[index].get();
+ if (isa<ModuleCommand>(command)) {
+ return &cast<ModuleCommand>(command)->module;
+ } else if (isa<ScriptModuleCommand>(command)) {
+ return &cast<ScriptModuleCommand>(command)->module;
+ }
+ return nullptr;
+}
+
+void MakeTypeBindingReverseMapping(
+ size_t num_types,
+ const BindingHash& bindings,
+ std::vector<std::string>* out_reverse_mapping) {
+ out_reverse_mapping->clear();
+ out_reverse_mapping->resize(num_types);
+ for (const auto& [name, binding] : bindings) {
+ assert(static_cast<size_t>(binding.index) < out_reverse_mapping->size());
+ (*out_reverse_mapping)[binding.index] = name;
+ }
+}
+
+Var::Var() : Var(kInvalidIndex, Location()) {}
+
+Var::Var(Index index, const Location& loc)
+ : loc(loc), type_(VarType::Index), index_(index) {}
+
+Var::Var(std::string_view name, const Location& loc)
+ : loc(loc), type_(VarType::Name), name_(name) {}
+
+Var::Var(Var&& rhs) : Var() {
+ *this = std::move(rhs);
+}
+
+Var::Var(const Var& rhs) : Var() {
+ *this = rhs;
+}
+
+Var& Var::operator=(Var&& rhs) {
+ loc = rhs.loc;
+ if (rhs.is_index()) {
+ set_index(rhs.index_);
+ } else {
+ set_name(rhs.name_);
+ }
+ return *this;
+}
+
+Var& Var::operator=(const Var& rhs) {
+ loc = rhs.loc;
+ if (rhs.is_index()) {
+ set_index(rhs.index_);
+ } else {
+ set_name(rhs.name_);
+ }
+ return *this;
+}
+
+Var::~Var() {
+ Destroy();
+}
+
+void Var::set_index(Index index) {
+ Destroy();
+ type_ = VarType::Index;
+ index_ = index;
+}
+
+void Var::set_name(std::string&& name) {
+ Destroy();
+ type_ = VarType::Name;
+ Construct(name_, std::move(name));
+}
+
+void Var::set_name(std::string_view name) {
+ set_name(std::string(name));
+}
+
+void Var::Destroy() {
+ if (is_name()) {
+ Destruct(name_);
+ }
+}
+
+uint8_t ElemSegment::GetFlags(const Module* module) const {
+ uint8_t flags = 0;
+
+ switch (kind) {
+ case SegmentKind::Active: {
+ Index table_index = module->GetTableIndex(table_var);
+ if (elem_type != Type::FuncRef || table_index != 0) {
+ flags |= SegExplicitIndex;
+ }
+ break;
+ }
+
+ case SegmentKind::Passive:
+ flags |= SegPassive;
+ break;
+
+ case SegmentKind::Declared:
+ flags |= SegDeclared;
+ break;
+ }
+
+ bool all_ref_func =
+ elem_type == Type::FuncRef &&
+ std::all_of(elem_exprs.begin(), elem_exprs.end(),
+ [](const ExprList& elem_expr) {
+ return elem_expr.front().type() == ExprType::RefFunc;
+ });
+
+ if (!all_ref_func) {
+ flags |= SegUseElemExprs;
+ }
+
+ return flags;
+}
+
+uint8_t DataSegment::GetFlags(const Module* module) const {
+ uint8_t flags = 0;
+
+ if (kind == SegmentKind::Passive) {
+ flags |= SegPassive;
+ }
+
+ Index memory_index = module->GetMemoryIndex(memory_var);
+ if (memory_index != 0) {
+ flags |= SegExplicitIndex;
+ }
+
+ return flags;
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/leb128.cc b/third_party/wasm2c/src/leb128.cc
new file mode 100644
index 0000000000..29b20f61bc
--- /dev/null
+++ b/third_party/wasm2c/src/leb128.cc
@@ -0,0 +1,361 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/leb128.h"
+
+#include <type_traits>
+
+#include "wabt/stream.h"
+
+#define MAX_U32_LEB128_BYTES 5
+#define MAX_U64_LEB128_BYTES 10
+
+namespace wabt {
+
+Offset U32Leb128Length(uint32_t value) {
+ uint32_t size = 0;
+ do {
+ value >>= 7;
+ size++;
+ } while (value != 0);
+ return size;
+}
+
+#define LEB128_LOOP_UNTIL(end_cond) \
+ do { \
+ uint8_t byte = value & 0x7f; \
+ value >>= 7; \
+ if (end_cond) { \
+ data[length++] = byte; \
+ break; \
+ } else { \
+ data[length++] = byte | 0x80; \
+ } \
+ } while (1)
+
+Offset WriteFixedU32Leb128At(Stream* stream,
+ Offset offset,
+ uint32_t value,
+ const char* desc) {
+ uint8_t data[MAX_U32_LEB128_BYTES];
+ Offset length =
+ WriteFixedU32Leb128Raw(data, data + MAX_U32_LEB128_BYTES, value);
+ stream->WriteDataAt(offset, data, length, desc);
+ return length;
+}
+
+void WriteU32Leb128(Stream* stream, uint32_t value, const char* desc) {
+ uint8_t data[MAX_U32_LEB128_BYTES];
+ Offset length = 0;
+ LEB128_LOOP_UNTIL(value == 0);
+ stream->WriteData(data, length, desc);
+}
+
+void WriteFixedU32Leb128(Stream* stream, uint32_t value, const char* desc) {
+ uint8_t data[MAX_U32_LEB128_BYTES];
+ Offset length =
+ WriteFixedU32Leb128Raw(data, data + MAX_U32_LEB128_BYTES, value);
+ stream->WriteData(data, length, desc);
+}
+
+// returns the length of the leb128.
+Offset WriteU32Leb128At(Stream* stream,
+ Offset offset,
+ uint32_t value,
+ const char* desc) {
+ uint8_t data[MAX_U32_LEB128_BYTES];
+ Offset length = 0;
+ LEB128_LOOP_UNTIL(value == 0);
+ stream->WriteDataAt(offset, data, length, desc);
+ return length;
+}
+
+Offset WriteU32Leb128Raw(uint8_t* dest, uint8_t* dest_end, uint32_t value) {
+ uint8_t data[MAX_U32_LEB128_BYTES];
+ Offset length = 0;
+ LEB128_LOOP_UNTIL(value == 0);
+ if (static_cast<Offset>(dest_end - dest) < length) {
+ return 0;
+ }
+ memcpy(dest, data, length);
+ return length;
+}
+
+Offset WriteFixedU32Leb128Raw(uint8_t* data, uint8_t* end, uint32_t value) {
+ if (end - data < MAX_U32_LEB128_BYTES) {
+ return 0;
+ }
+ data[0] = (value & 0x7f) | 0x80;
+ data[1] = ((value >> 7) & 0x7f) | 0x80;
+ data[2] = ((value >> 14) & 0x7f) | 0x80;
+ data[3] = ((value >> 21) & 0x7f) | 0x80;
+ data[4] = ((value >> 28) & 0x0f);
+ return MAX_U32_LEB128_BYTES;
+}
+
+static void WriteS32Leb128(Stream* stream, int32_t value, const char* desc) {
+ uint8_t data[MAX_U32_LEB128_BYTES];
+ Offset length = 0;
+ if (value < 0) {
+ LEB128_LOOP_UNTIL(value == -1 && (byte & 0x40));
+ } else {
+ LEB128_LOOP_UNTIL(value == 0 && !(byte & 0x40));
+ }
+
+ stream->WriteData(data, length, desc);
+}
+
+static void WriteS64Leb128(Stream* stream, int64_t value, const char* desc) {
+ uint8_t data[MAX_U64_LEB128_BYTES];
+ Offset length = 0;
+ if (value < 0) {
+ LEB128_LOOP_UNTIL(value == -1 && (byte & 0x40));
+ } else {
+ LEB128_LOOP_UNTIL(value == 0 && !(byte & 0x40));
+ }
+
+ stream->WriteData(data, length, desc);
+}
+
+void WriteS32Leb128(Stream* stream, uint32_t value, const char* desc) {
+ WriteS32Leb128(stream, Bitcast<int32_t>(value), desc);
+}
+
+void WriteU64Leb128(Stream* stream, uint64_t value, const char* desc) {
+ uint8_t data[MAX_U64_LEB128_BYTES];
+ Offset length = 0;
+ LEB128_LOOP_UNTIL(value == 0);
+ stream->WriteData(data, length, desc);
+}
+
+void WriteS64Leb128(Stream* stream, uint64_t value, const char* desc) {
+ WriteS64Leb128(stream, Bitcast<int64_t>(value), desc);
+}
+
+void WriteFixedS32Leb128(Stream* stream, uint32_t value, const char* desc) {
+ uint8_t data[MAX_U32_LEB128_BYTES];
+ data[0] = (value & 0x7f) | 0x80;
+ data[1] = ((value >> 7) & 0x7f) | 0x80;
+ data[2] = ((value >> 14) & 0x7f) | 0x80;
+ data[3] = ((value >> 21) & 0x7f) | 0x80;
+ // The last byte needs to be sign-extended.
+ data[4] = ((value >> 28) & 0x0f);
+ if (static_cast<int32_t>(value) < 0) {
+ data[4] |= 0x70;
+ }
+ stream->WriteData(data, MAX_U32_LEB128_BYTES, desc);
+}
+
+#undef LEB128_LOOP_UNTIL
+
+#define BYTE_AT(type, i, shift) ((static_cast<type>(p[i]) & 0x7f) << (shift))
+
+#define LEB128_1(type) (BYTE_AT(type, 0, 0))
+#define LEB128_2(type) (BYTE_AT(type, 1, 7) | LEB128_1(type))
+#define LEB128_3(type) (BYTE_AT(type, 2, 14) | LEB128_2(type))
+#define LEB128_4(type) (BYTE_AT(type, 3, 21) | LEB128_3(type))
+#define LEB128_5(type) (BYTE_AT(type, 4, 28) | LEB128_4(type))
+#define LEB128_6(type) (BYTE_AT(type, 5, 35) | LEB128_5(type))
+#define LEB128_7(type) (BYTE_AT(type, 6, 42) | LEB128_6(type))
+#define LEB128_8(type) (BYTE_AT(type, 7, 49) | LEB128_7(type))
+#define LEB128_9(type) (BYTE_AT(type, 8, 56) | LEB128_8(type))
+#define LEB128_10(type) (BYTE_AT(type, 9, 63) | LEB128_9(type))
+
+#define SHIFT_AMOUNT(type, sign_bit) (sizeof(type) * 8 - 1 - (sign_bit))
+#define SIGN_EXTEND(type, value, sign_bit) \
+ (static_cast<type>((value) << SHIFT_AMOUNT(type, sign_bit)) >> \
+ SHIFT_AMOUNT(type, sign_bit))
+
+size_t ReadU32Leb128(const uint8_t* p,
+ const uint8_t* end,
+ uint32_t* out_value) {
+ if (p < end && (p[0] & 0x80) == 0) {
+ *out_value = LEB128_1(uint32_t);
+ return 1;
+ } else if (p + 1 < end && (p[1] & 0x80) == 0) {
+ *out_value = LEB128_2(uint32_t);
+ return 2;
+ } else if (p + 2 < end && (p[2] & 0x80) == 0) {
+ *out_value = LEB128_3(uint32_t);
+ return 3;
+ } else if (p + 3 < end && (p[3] & 0x80) == 0) {
+ *out_value = LEB128_4(uint32_t);
+ return 4;
+ } else if (p + 4 < end && (p[4] & 0x80) == 0) {
+ // The top bits set represent values > 32 bits.
+ if (p[4] & 0xf0) {
+ return 0;
+ }
+ *out_value = LEB128_5(uint32_t);
+ return 5;
+ } else {
+ // past the end.
+ *out_value = 0;
+ return 0;
+ }
+}
+
+size_t ReadU64Leb128(const uint8_t* p,
+ const uint8_t* end,
+ uint64_t* out_value) {
+ if (p < end && (p[0] & 0x80) == 0) {
+ *out_value = LEB128_1(uint64_t);
+ return 1;
+ } else if (p + 1 < end && (p[1] & 0x80) == 0) {
+ *out_value = LEB128_2(uint64_t);
+ return 2;
+ } else if (p + 2 < end && (p[2] & 0x80) == 0) {
+ *out_value = LEB128_3(uint64_t);
+ return 3;
+ } else if (p + 3 < end && (p[3] & 0x80) == 0) {
+ *out_value = LEB128_4(uint64_t);
+ return 4;
+ } else if (p + 4 < end && (p[4] & 0x80) == 0) {
+ *out_value = LEB128_5(uint64_t);
+ return 5;
+ } else if (p + 5 < end && (p[5] & 0x80) == 0) {
+ *out_value = LEB128_6(uint64_t);
+ return 6;
+ } else if (p + 6 < end && (p[6] & 0x80) == 0) {
+ *out_value = LEB128_7(uint64_t);
+ return 7;
+ } else if (p + 7 < end && (p[7] & 0x80) == 0) {
+ *out_value = LEB128_8(uint64_t);
+ return 8;
+ } else if (p + 8 < end && (p[8] & 0x80) == 0) {
+ *out_value = LEB128_9(uint64_t);
+ return 9;
+ } else if (p + 9 < end && (p[9] & 0x80) == 0) {
+ // The top bits set represent values > 32 bits.
+ if (p[9] & 0xf0) {
+ return 0;
+ }
+ *out_value = LEB128_10(uint64_t);
+ return 10;
+ } else {
+ // past the end.
+ *out_value = 0;
+ return 0;
+ }
+}
+
+size_t ReadS32Leb128(const uint8_t* p,
+ const uint8_t* end,
+ uint32_t* out_value) {
+ if (p < end && (p[0] & 0x80) == 0) {
+ uint32_t result = LEB128_1(uint32_t);
+ *out_value = SIGN_EXTEND(int32_t, result, 6);
+ return 1;
+ } else if (p + 1 < end && (p[1] & 0x80) == 0) {
+ uint32_t result = LEB128_2(uint32_t);
+ *out_value = SIGN_EXTEND(int32_t, result, 13);
+ return 2;
+ } else if (p + 2 < end && (p[2] & 0x80) == 0) {
+ uint32_t result = LEB128_3(uint32_t);
+ *out_value = SIGN_EXTEND(int32_t, result, 20);
+ return 3;
+ } else if (p + 3 < end && (p[3] & 0x80) == 0) {
+ uint32_t result = LEB128_4(uint32_t);
+ *out_value = SIGN_EXTEND(int32_t, result, 27);
+ return 4;
+ } else if (p + 4 < end && (p[4] & 0x80) == 0) {
+ // The top bits should be a sign-extension of the sign bit.
+ bool sign_bit_set = (p[4] & 0x8);
+ int top_bits = p[4] & 0xf0;
+ if ((sign_bit_set && top_bits != 0x70) ||
+ (!sign_bit_set && top_bits != 0)) {
+ return 0;
+ }
+ uint32_t result = LEB128_5(uint32_t);
+ *out_value = result;
+ return 5;
+ } else {
+ // Past the end.
+ return 0;
+ }
+}
+
+size_t ReadS64Leb128(const uint8_t* p,
+ const uint8_t* end,
+ uint64_t* out_value) {
+ if (p < end && (p[0] & 0x80) == 0) {
+ uint64_t result = LEB128_1(uint64_t);
+ *out_value = SIGN_EXTEND(int64_t, result, 6);
+ return 1;
+ } else if (p + 1 < end && (p[1] & 0x80) == 0) {
+ uint64_t result = LEB128_2(uint64_t);
+ *out_value = SIGN_EXTEND(int64_t, result, 13);
+ return 2;
+ } else if (p + 2 < end && (p[2] & 0x80) == 0) {
+ uint64_t result = LEB128_3(uint64_t);
+ *out_value = SIGN_EXTEND(int64_t, result, 20);
+ return 3;
+ } else if (p + 3 < end && (p[3] & 0x80) == 0) {
+ uint64_t result = LEB128_4(uint64_t);
+ *out_value = SIGN_EXTEND(int64_t, result, 27);
+ return 4;
+ } else if (p + 4 < end && (p[4] & 0x80) == 0) {
+ uint64_t result = LEB128_5(uint64_t);
+ *out_value = SIGN_EXTEND(int64_t, result, 34);
+ return 5;
+ } else if (p + 5 < end && (p[5] & 0x80) == 0) {
+ uint64_t result = LEB128_6(uint64_t);
+ *out_value = SIGN_EXTEND(int64_t, result, 41);
+ return 6;
+ } else if (p + 6 < end && (p[6] & 0x80) == 0) {
+ uint64_t result = LEB128_7(uint64_t);
+ *out_value = SIGN_EXTEND(int64_t, result, 48);
+ return 7;
+ } else if (p + 7 < end && (p[7] & 0x80) == 0) {
+ uint64_t result = LEB128_8(uint64_t);
+ *out_value = SIGN_EXTEND(int64_t, result, 55);
+ return 8;
+ } else if (p + 8 < end && (p[8] & 0x80) == 0) {
+ uint64_t result = LEB128_9(uint64_t);
+ *out_value = SIGN_EXTEND(int64_t, result, 62);
+ return 9;
+ } else if (p + 9 < end && (p[9] & 0x80) == 0) {
+ // The top bits should be a sign-extension of the sign bit.
+ bool sign_bit_set = (p[9] & 0x1);
+ int top_bits = p[9] & 0xfe;
+ if ((sign_bit_set && top_bits != 0x7e) ||
+ (!sign_bit_set && top_bits != 0)) {
+ return 0;
+ }
+ uint64_t result = LEB128_10(uint64_t);
+ *out_value = result;
+ return 10;
+ } else {
+ // Past the end.
+ return 0;
+ }
+}
+
+#undef BYTE_AT
+#undef LEB128_1
+#undef LEB128_2
+#undef LEB128_3
+#undef LEB128_4
+#undef LEB128_5
+#undef LEB128_6
+#undef LEB128_7
+#undef LEB128_8
+#undef LEB128_9
+#undef LEB128_10
+#undef SHIFT_AMOUNT
+#undef SIGN_EXTEND
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/lexer-keywords.txt b/third_party/wasm2c/src/lexer-keywords.txt
new file mode 100644
index 0000000000..9690279297
--- /dev/null
+++ b/third_party/wasm2c/src/lexer-keywords.txt
@@ -0,0 +1,609 @@
+struct TokenInfo {
+ TokenInfo(const char* name) : name(name) {}
+ TokenInfo(const char* name, TokenType token_type)
+ : name(name), token_type(token_type) {}
+ TokenInfo(const char* name, Type value_type)
+ : name(name), token_type(TokenType::ValueType), value_type(value_type) {}
+ TokenInfo(const char* name, Type value_type, TokenType token_type)
+ : name(name), token_type(token_type), value_type(value_type) {}
+ TokenInfo(const char* name, TokenType token_type, Opcode opcode)
+ : name(name), token_type(token_type), opcode(opcode) {}
+
+ const char* name;
+ TokenType token_type;
+ union {
+ Type value_type;
+ Opcode opcode;
+ };
+};
+%%
+array, Type::Array, TokenType::Array
+assert_exception, TokenType::AssertException
+assert_exhaustion, TokenType::AssertExhaustion
+assert_invalid, TokenType::AssertInvalid
+assert_malformed, TokenType::AssertMalformed
+assert_return, TokenType::AssertReturn
+assert_trap, TokenType::AssertTrap
+assert_unlinkable, TokenType::AssertUnlinkable
+atomic.fence, TokenType::AtomicFence, Opcode::AtomicFence
+binary, TokenType::Bin
+block, TokenType::Block, Opcode::Block
+br_if, TokenType::BrIf, Opcode::BrIf
+br_table, TokenType::BrTable, Opcode::BrTable
+br, TokenType::Br, Opcode::Br
+call_indirect, TokenType::CallIndirect, Opcode::CallIndirect
+call_ref, TokenType::CallRef, Opcode::CallRef
+call, TokenType::Call, Opcode::Call
+catch, TokenType::Catch, Opcode::Catch
+catch_all, TokenType::CatchAll, Opcode::CatchAll
+data.drop, TokenType::DataDrop, Opcode::DataDrop
+data, TokenType::Data
+declare, TokenType::Declare
+delegate, TokenType::Delegate
+do, TokenType::Do
+drop, TokenType::Drop, Opcode::Drop
+either, TokenType::Either
+elem.drop, TokenType::ElemDrop, Opcode::ElemDrop
+elem, TokenType::Elem
+else, TokenType::Else, Opcode::Else
+end, TokenType::End, Opcode::End
+tag, TokenType::Tag
+extern, Type::ExternRef, TokenType::Extern
+externref, Type::ExternRef
+export, TokenType::Export
+f32.abs, TokenType::Unary, Opcode::F32Abs
+f32.add, TokenType::Binary, Opcode::F32Add
+f32.ceil, TokenType::Unary, Opcode::F32Ceil
+f32.const, TokenType::Const, Opcode::F32Const
+f32.convert_i32_s, TokenType::Convert, Opcode::F32ConvertI32S
+f32.convert_i32_u, TokenType::Convert, Opcode::F32ConvertI32U
+f32.convert_i64_s, TokenType::Convert, Opcode::F32ConvertI64S
+f32.convert_i64_u, TokenType::Convert, Opcode::F32ConvertI64U
+f32.copysign, TokenType::Binary, Opcode::F32Copysign
+f32.demote_f64, TokenType::Convert, Opcode::F32DemoteF64
+f32.div, TokenType::Binary, Opcode::F32Div
+f32.eq, TokenType::Compare, Opcode::F32Eq
+f32.floor, TokenType::Unary, Opcode::F32Floor
+f32.ge, TokenType::Compare, Opcode::F32Ge
+f32.gt, TokenType::Compare, Opcode::F32Gt
+f32.le, TokenType::Compare, Opcode::F32Le
+f32.load, TokenType::Load, Opcode::F32Load
+f32.lt, TokenType::Compare, Opcode::F32Lt
+f32.max, TokenType::Binary, Opcode::F32Max
+f32.min, TokenType::Binary, Opcode::F32Min
+f32.mul, TokenType::Binary, Opcode::F32Mul
+f32.nearest, TokenType::Unary, Opcode::F32Nearest
+f32.neg, TokenType::Unary, Opcode::F32Neg
+f32.ne, TokenType::Compare, Opcode::F32Ne
+f32.reinterpret_i32, TokenType::Convert, Opcode::F32ReinterpretI32
+f32.sqrt, TokenType::Unary, Opcode::F32Sqrt
+f32.store, TokenType::Store, Opcode::F32Store
+f32.sub, TokenType::Binary, Opcode::F32Sub
+f32.trunc, TokenType::Unary, Opcode::F32Trunc
+f32, Type::F32
+f32x4.abs, TokenType::Unary, Opcode::F32X4Abs
+f32x4.add, TokenType::Binary, Opcode::F32X4Add
+f32x4.ceil, TokenType::Unary, Opcode::F32X4Ceil
+f32x4.convert_i32x4_s, TokenType::Unary, Opcode::F32X4ConvertI32X4S
+f32x4.convert_i32x4_u, TokenType::Unary, Opcode::F32X4ConvertI32X4U
+f32x4.div, TokenType::Binary, Opcode::F32X4Div
+f32x4.eq, TokenType::Compare, Opcode::F32X4Eq
+f32x4.extract_lane, TokenType::SimdLaneOp, Opcode::F32X4ExtractLane
+f32x4.floor, TokenType::Unary, Opcode::F32X4Floor
+f32x4.ge, TokenType::Compare, Opcode::F32X4Ge
+f32x4.gt, TokenType::Compare, Opcode::F32X4Gt
+f32x4.le, TokenType::Compare, Opcode::F32X4Le
+f32x4.lt, TokenType::Compare, Opcode::F32X4Lt
+f32x4.max, TokenType::Binary, Opcode::F32X4Max
+f32x4.min, TokenType::Binary, Opcode::F32X4Min
+f32x4.mul, TokenType::Binary, Opcode::F32X4Mul
+f32x4.nearest, TokenType::Unary, Opcode::F32X4Nearest
+f32x4.neg, TokenType::Unary, Opcode::F32X4Neg
+f32x4.ne, TokenType::Compare, Opcode::F32X4Ne
+f32x4.pmax, TokenType::Binary, Opcode::F32X4PMax
+f32x4.pmin, TokenType::Binary, Opcode::F32X4PMin
+f32x4.relaxed_madd, TokenType::Ternary, Opcode::F32X4RelaxedMadd
+f32x4.relaxed_max, TokenType::Binary, Opcode::F32X4RelaxedMax
+f32x4.relaxed_min, TokenType::Binary, Opcode::F32X4RelaxedMin
+f32x4.relaxed_nmadd, TokenType::Ternary, Opcode::F32X4RelaxedNmadd
+f32x4.replace_lane, TokenType::SimdLaneOp, Opcode::F32X4ReplaceLane
+f32x4.splat, TokenType::Unary, Opcode::F32X4Splat
+f32x4.sqrt, TokenType::Unary, Opcode::F32X4Sqrt
+f32x4.sub, TokenType::Binary, Opcode::F32X4Sub
+f32x4.trunc, TokenType::Unary, Opcode::F32X4Trunc
+f32x4.demote_f64x2_zero, TokenType::Unary, Opcode::F32X4DemoteF64X2Zero
+f32x4, TokenType::F32X4
+f64.abs, TokenType::Unary, Opcode::F64Abs
+f64.add, TokenType::Binary, Opcode::F64Add
+f64.ceil, TokenType::Unary, Opcode::F64Ceil
+f64.const, TokenType::Const, Opcode::F64Const
+f64.convert_i32_s, TokenType::Convert, Opcode::F64ConvertI32S
+f64.convert_i32_u, TokenType::Convert, Opcode::F64ConvertI32U
+f64.convert_i64_s, TokenType::Convert, Opcode::F64ConvertI64S
+f64.convert_i64_u, TokenType::Convert, Opcode::F64ConvertI64U
+f64.copysign, TokenType::Binary, Opcode::F64Copysign
+f64.div, TokenType::Binary, Opcode::F64Div
+f64.eq, TokenType::Compare, Opcode::F64Eq
+f64.floor, TokenType::Unary, Opcode::F64Floor
+f64.ge, TokenType::Compare, Opcode::F64Ge
+f64.gt, TokenType::Compare, Opcode::F64Gt
+f64.le, TokenType::Compare, Opcode::F64Le
+f64.load, TokenType::Load, Opcode::F64Load
+f64.lt, TokenType::Compare, Opcode::F64Lt
+f64.max, TokenType::Binary, Opcode::F64Max
+f64.min, TokenType::Binary, Opcode::F64Min
+f64.mul, TokenType::Binary, Opcode::F64Mul
+f64.nearest, TokenType::Unary, Opcode::F64Nearest
+f64.neg, TokenType::Unary, Opcode::F64Neg
+f64.ne, TokenType::Compare, Opcode::F64Ne
+f64.promote_f32, TokenType::Convert, Opcode::F64PromoteF32
+f64.reinterpret_i64, TokenType::Convert, Opcode::F64ReinterpretI64
+f64.sqrt, TokenType::Unary, Opcode::F64Sqrt
+f64.store, TokenType::Store, Opcode::F64Store
+f64.sub, TokenType::Binary, Opcode::F64Sub
+f64.trunc, TokenType::Unary, Opcode::F64Trunc
+f64, Type::F64
+f64x2.abs, TokenType::Unary, Opcode::F64X2Abs
+f64x2.add, TokenType::Binary, Opcode::F64X2Add
+f64x2.ceil, TokenType::Unary, Opcode::F64X2Ceil
+f64x2.div, TokenType::Binary, Opcode::F64X2Div
+f64x2.eq, TokenType::Compare, Opcode::F64X2Eq
+f64x2.extract_lane, TokenType::SimdLaneOp, Opcode::F64X2ExtractLane
+f64x2.floor, TokenType::Unary, Opcode::F64X2Floor
+f64x2.ge, TokenType::Compare, Opcode::F64X2Ge
+f64x2.gt, TokenType::Compare, Opcode::F64X2Gt
+f64x2.le, TokenType::Compare, Opcode::F64X2Le
+f64x2.lt, TokenType::Compare, Opcode::F64X2Lt
+f64x2.max, TokenType::Binary, Opcode::F64X2Max
+f64x2.min, TokenType::Binary, Opcode::F64X2Min
+f64x2.mul, TokenType::Binary, Opcode::F64X2Mul
+f64x2.nearest, TokenType::Unary, Opcode::F64X2Nearest
+f64x2.neg, TokenType::Unary, Opcode::F64X2Neg
+f64x2.ne, TokenType::Compare, Opcode::F64X2Ne
+f64x2.pmax, TokenType::Binary, Opcode::F64X2PMax
+f64x2.pmin, TokenType::Binary, Opcode::F64X2PMin
+f64x2.relaxed_madd, TokenType::Ternary, Opcode::F64X2RelaxedMadd
+f64x2.relaxed_max, TokenType::Binary, Opcode::F64X2RelaxedMax
+f64x2.relaxed_min, TokenType::Binary, Opcode::F64X2RelaxedMin
+f64x2.relaxed_nmadd, TokenType::Ternary, Opcode::F64X2RelaxedNmadd
+f64x2.replace_lane, TokenType::SimdLaneOp, Opcode::F64X2ReplaceLane
+f64x2.splat, TokenType::Unary, Opcode::F64X2Splat
+f64x2.sqrt, TokenType::Unary, Opcode::F64X2Sqrt
+f64x2.sub, TokenType::Binary, Opcode::F64X2Sub
+f64x2.trunc, TokenType::Unary, Opcode::F64X2Trunc
+f64x2.convert_low_i32x4_s, TokenType::Unary, Opcode::F64X2ConvertLowI32X4S
+f64x2.convert_low_i32x4_u, TokenType::Unary, Opcode::F64X2ConvertLowI32X4U
+f64x2.promote_low_f32x4, TokenType::Unary, Opcode::F64X2PromoteLowF32X4
+f64x2, TokenType::F64X2
+field, TokenType::Field
+funcref, Type::FuncRef
+func, Type::FuncRef, TokenType::Func
+get, TokenType::Get
+global.get, TokenType::GlobalGet, Opcode::GlobalGet
+global.set, TokenType::GlobalSet, Opcode::GlobalSet
+global, TokenType::Global
+i16x8.abs, TokenType::Unary, Opcode::I16X8Abs
+i16x8.add_sat_s, TokenType::Binary, Opcode::I16X8AddSatS
+i16x8.add_sat_u, TokenType::Binary, Opcode::I16X8AddSatU
+i16x8.add, TokenType::Binary, Opcode::I16X8Add
+i16x8.all_true, TokenType::Unary, Opcode::I16X8AllTrue
+i16x8.avgr_u, TokenType::Binary, Opcode::I16X8AvgrU
+i16x8.bitmask, TokenType::Unary, Opcode::I16X8Bitmask
+i16x8.dot_i8x16_i7x16_s, TokenType::Binary, Opcode::I16X8DotI8X16I7X16S
+i16x8.eq, TokenType::Compare, Opcode::I16X8Eq
+i16x8.extract_lane_s, TokenType::SimdLaneOp, Opcode::I16X8ExtractLaneS
+i16x8.extract_lane_u, TokenType::SimdLaneOp, Opcode::I16X8ExtractLaneU
+i16x8.ge_s, TokenType::Compare, Opcode::I16X8GeS
+i16x8.ge_u, TokenType::Compare, Opcode::I16X8GeU
+i16x8.gt_s, TokenType::Compare, Opcode::I16X8GtS
+i16x8.gt_u, TokenType::Compare, Opcode::I16X8GtU
+i16x8.le_s, TokenType::Compare, Opcode::I16X8LeS
+i16x8.le_u, TokenType::Compare, Opcode::I16X8LeU
+v128.load8x8_s, TokenType::Load, Opcode::V128Load8X8S
+v128.load8x8_u, TokenType::Load, Opcode::V128Load8X8U
+i16x8.lt_s, TokenType::Compare, Opcode::I16X8LtS
+i16x8.lt_u, TokenType::Compare, Opcode::I16X8LtU
+i16x8.max_s, TokenType::Binary, Opcode::I16X8MaxS
+i16x8.max_u, TokenType::Binary, Opcode::I16X8MaxU
+i16x8.min_s, TokenType::Binary, Opcode::I16X8MinS
+i16x8.min_u, TokenType::Binary, Opcode::I16X8MinU
+i16x8.mul, TokenType::Binary, Opcode::I16X8Mul
+i16x8.narrow_i32x4_s, TokenType::Binary, Opcode::I16X8NarrowI32X4S
+i16x8.narrow_i32x4_u, TokenType::Binary, Opcode::I16X8NarrowI32X4U
+i16x8.neg, TokenType::Unary, Opcode::I16X8Neg
+i16x8.q15mulr_sat_s, TokenType::Binary, Opcode::I16X8Q15mulrSatS
+i16x8.ne, TokenType::Compare, Opcode::I16X8Ne
+i16x8.relaxed_laneselect, TokenType::Ternary, Opcode::I16X8RelaxedLaneSelect
+i16x8.relaxed_q15mulr_s, TokenType::Binary, Opcode::I16X8RelaxedQ15mulrS
+i16x8.replace_lane, TokenType::SimdLaneOp, Opcode::I16X8ReplaceLane
+i16x8.shl, TokenType::Binary, Opcode::I16X8Shl
+i16x8.shr_s, TokenType::Binary, Opcode::I16X8ShrS
+i16x8.shr_u, TokenType::Binary, Opcode::I16X8ShrU
+i16x8.splat, TokenType::Unary, Opcode::I16X8Splat
+i16x8.sub_sat_s, TokenType::Binary, Opcode::I16X8SubSatS
+i16x8.sub_sat_u, TokenType::Binary, Opcode::I16X8SubSatU
+i16x8.sub, TokenType::Binary, Opcode::I16X8Sub
+i16x8.extadd_pairwise_i8x16_s, TokenType::Unary, Opcode::I16X8ExtaddPairwiseI8X16S
+i16x8.extadd_pairwise_i8x16_u, TokenType::Unary, Opcode::I16X8ExtaddPairwiseI8X16U
+i16x8.extmul_low_i8x16_s, TokenType::Binary, Opcode::I16X8ExtmulLowI8X16S
+i16x8.extmul_high_i8x16_s, TokenType::Binary, Opcode::I16X8ExtmulHighI8X16S
+i16x8.extmul_low_i8x16_u, TokenType::Binary, Opcode::I16X8ExtmulLowI8X16U
+i16x8.extmul_high_i8x16_u, TokenType::Binary, Opcode::I16X8ExtmulHighI8X16U
+i16x8, TokenType::I16X8
+i16x8.extend_high_i8x16_s, TokenType::Unary, Opcode::I16X8ExtendHighI8X16S
+i16x8.extend_high_i8x16_u, TokenType::Unary, Opcode::I16X8ExtendHighI8X16U
+i16x8.extend_low_i8x16_s, TokenType::Unary, Opcode::I16X8ExtendLowI8X16S
+i16x8.extend_low_i8x16_u, TokenType::Unary, Opcode::I16X8ExtendLowI8X16U
+i32.add, TokenType::Binary, Opcode::I32Add
+i32.and, TokenType::Binary, Opcode::I32And
+i32.atomic.load16_u, TokenType::AtomicLoad, Opcode::I32AtomicLoad16U
+i32.atomic.load8_u, TokenType::AtomicLoad, Opcode::I32AtomicLoad8U
+i32.atomic.load, TokenType::AtomicLoad, Opcode::I32AtomicLoad
+i32.atomic.rmw16.add_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw16AddU
+i32.atomic.rmw16.and_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw16AndU
+i32.atomic.rmw16.cmpxchg_u, TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmw16CmpxchgU
+i32.atomic.rmw16.or_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw16OrU
+i32.atomic.rmw16.sub_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw16SubU
+i32.atomic.rmw16.xchg_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw16XchgU
+i32.atomic.rmw16.xor_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw16XorU
+i32.atomic.rmw8.add_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw8AddU
+i32.atomic.rmw8.and_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw8AndU
+i32.atomic.rmw8.cmpxchg_u, TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmw8CmpxchgU
+i32.atomic.rmw8.or_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw8OrU
+i32.atomic.rmw8.sub_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw8SubU
+i32.atomic.rmw8.xchg_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw8XchgU
+i32.atomic.rmw8.xor_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw8XorU
+i32.atomic.rmw.add, TokenType::AtomicRmw, Opcode::I32AtomicRmwAdd
+i32.atomic.rmw.and, TokenType::AtomicRmw, Opcode::I32AtomicRmwAnd
+i32.atomic.rmw.cmpxchg, TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmwCmpxchg
+i32.atomic.rmw.or, TokenType::AtomicRmw, Opcode::I32AtomicRmwOr
+i32.atomic.rmw.sub, TokenType::AtomicRmw, Opcode::I32AtomicRmwSub
+i32.atomic.rmw.xchg, TokenType::AtomicRmw, Opcode::I32AtomicRmwXchg
+i32.atomic.rmw.xor, TokenType::AtomicRmw, Opcode::I32AtomicRmwXor
+i32.atomic.store16, TokenType::AtomicStore, Opcode::I32AtomicStore16
+i32.atomic.store8, TokenType::AtomicStore, Opcode::I32AtomicStore8
+i32.atomic.store, TokenType::AtomicStore, Opcode::I32AtomicStore
+i32.clz, TokenType::Unary, Opcode::I32Clz
+i32.const, TokenType::Const, Opcode::I32Const
+i32.ctz, TokenType::Unary, Opcode::I32Ctz
+i32.div_s, TokenType::Binary, Opcode::I32DivS
+i32.div_u, TokenType::Binary, Opcode::I32DivU
+i32.eq, TokenType::Compare, Opcode::I32Eq
+i32.eqz, TokenType::Convert, Opcode::I32Eqz
+i32.extend16_s, TokenType::Unary, Opcode::I32Extend16S
+i32.extend8_s, TokenType::Unary, Opcode::I32Extend8S
+i32.ge_s, TokenType::Compare, Opcode::I32GeS
+i32.ge_u, TokenType::Compare, Opcode::I32GeU
+i32.gt_s, TokenType::Compare, Opcode::I32GtS
+i32.gt_u, TokenType::Compare, Opcode::I32GtU
+i32.le_s, TokenType::Compare, Opcode::I32LeS
+i32.le_u, TokenType::Compare, Opcode::I32LeU
+i32.load16_s, TokenType::Load, Opcode::I32Load16S
+i32.load16_u, TokenType::Load, Opcode::I32Load16U
+i32.load8_s, TokenType::Load, Opcode::I32Load8S
+i32.load8_u, TokenType::Load, Opcode::I32Load8U
+i32.load, TokenType::Load, Opcode::I32Load
+i32.lt_s, TokenType::Compare, Opcode::I32LtS
+i32.lt_u, TokenType::Compare, Opcode::I32LtU
+i32.mul, TokenType::Binary, Opcode::I32Mul
+i32.ne, TokenType::Compare, Opcode::I32Ne
+i32.or, TokenType::Binary, Opcode::I32Or
+i32.popcnt, TokenType::Unary, Opcode::I32Popcnt
+i32.reinterpret_f32, TokenType::Convert, Opcode::I32ReinterpretF32
+i32.rem_s, TokenType::Binary, Opcode::I32RemS
+i32.rem_u, TokenType::Binary, Opcode::I32RemU
+i32.rotl, TokenType::Binary, Opcode::I32Rotl
+i32.rotr, TokenType::Binary, Opcode::I32Rotr
+i32.shl, TokenType::Binary, Opcode::I32Shl
+i32.shr_s, TokenType::Binary, Opcode::I32ShrS
+i32.shr_u, TokenType::Binary, Opcode::I32ShrU
+i32.store16, TokenType::Store, Opcode::I32Store16
+i32.store8, TokenType::Store, Opcode::I32Store8
+i32.store, TokenType::Store, Opcode::I32Store
+i32.sub, TokenType::Binary, Opcode::I32Sub
+i32.trunc_f32_s, TokenType::Convert, Opcode::I32TruncF32S
+i32.trunc_f32_u, TokenType::Convert, Opcode::I32TruncF32U
+i32.trunc_f64_s, TokenType::Convert, Opcode::I32TruncF64S
+i32.trunc_f64_u, TokenType::Convert, Opcode::I32TruncF64U
+i32.trunc_sat_f32_s, TokenType::Convert, Opcode::I32TruncSatF32S
+i32.trunc_sat_f32_u, TokenType::Convert, Opcode::I32TruncSatF32U
+i32.trunc_sat_f64_s, TokenType::Convert, Opcode::I32TruncSatF64S
+i32.trunc_sat_f64_u, TokenType::Convert, Opcode::I32TruncSatF64U
+i32, Type::I32
+i32.wrap_i64, TokenType::Convert, Opcode::I32WrapI64
+i32x4.abs, TokenType::Unary, Opcode::I32X4Abs
+i32x4.add, TokenType::Binary, Opcode::I32X4Add
+i32x4.all_true, TokenType::Unary, Opcode::I32X4AllTrue
+i32x4.bitmask, TokenType::Unary, Opcode::I32X4Bitmask
+i32x4.dot_i8x16_i7x16_add_s, TokenType::Ternary, Opcode::I32X4DotI8X16I7X16AddS
+i32x4.eq, TokenType::Compare, Opcode::I32X4Eq
+i32x4.extract_lane, TokenType::SimdLaneOp, Opcode::I32X4ExtractLane
+i32x4.ge_s, TokenType::Compare, Opcode::I32X4GeS
+i32x4.ge_u, TokenType::Compare, Opcode::I32X4GeU
+i32x4.gt_s, TokenType::Compare, Opcode::I32X4GtS
+i32x4.gt_u, TokenType::Compare, Opcode::I32X4GtU
+i32x4.le_s, TokenType::Compare, Opcode::I32X4LeS
+i32x4.le_u, TokenType::Compare, Opcode::I32X4LeU
+i32x4.relaxed_trunc_f32x4_s, TokenType::Unary, Opcode::I32X4RelaxedTruncF32X4S
+i32x4.relaxed_trunc_f32x4_u, TokenType::Unary, Opcode::I32X4RelaxedTruncF32X4U
+i32x4.relaxed_trunc_f64x2_s_zero, TokenType::Unary, Opcode::I32X4RelaxedTruncF64X2SZero
+i32x4.relaxed_trunc_f64x2_u_zero, TokenType::Unary, Opcode::I32X4RelaxedTruncF64X2UZero
+v128.load16x4_s, TokenType::Load, Opcode::V128Load16X4S
+v128.load16x4_u, TokenType::Load, Opcode::V128Load16X4U
+i32x4.lt_s, TokenType::Compare, Opcode::I32X4LtS
+i32x4.lt_u, TokenType::Compare, Opcode::I32X4LtU
+i32x4.max_s, TokenType::Binary, Opcode::I32X4MaxS
+i32x4.max_u, TokenType::Binary, Opcode::I32X4MaxU
+i32x4.min_s, TokenType::Binary, Opcode::I32X4MinS
+i32x4.min_u, TokenType::Binary, Opcode::I32X4MinU
+i32x4.dot_i16x8_s, TokenType::Binary, Opcode::I32X4DotI16X8S
+i32x4.mul, TokenType::Binary, Opcode::I32X4Mul
+i32x4.neg, TokenType::Unary, Opcode::I32X4Neg
+i32x4.ne, TokenType::Compare, Opcode::I32X4Ne
+i32x4.relaxed_laneselect, TokenType::Ternary, Opcode::I32X4RelaxedLaneSelect
+i32x4.replace_lane, TokenType::SimdLaneOp, Opcode::I32X4ReplaceLane
+i32x4.shl, TokenType::Binary, Opcode::I32X4Shl
+i32x4.shr_s, TokenType::Binary, Opcode::I32X4ShrS
+i32x4.shr_u, TokenType::Binary, Opcode::I32X4ShrU
+i32x4.splat, TokenType::Unary, Opcode::I32X4Splat
+i32x4.sub, TokenType::Binary, Opcode::I32X4Sub
+i32x4.extadd_pairwise_i16x8_s, TokenType::Unary, Opcode::I32X4ExtaddPairwiseI16X8S
+i32x4.extadd_pairwise_i16x8_u, TokenType::Unary, Opcode::I32X4ExtaddPairwiseI16X8U
+i32x4.extmul_low_i16x8_s, TokenType::Binary, Opcode::I32X4ExtmulLowI16X8S
+i32x4.extmul_high_i16x8_s, TokenType::Binary, Opcode::I32X4ExtmulHighI16X8S
+i32x4.extmul_low_i16x8_u, TokenType::Binary, Opcode::I32X4ExtmulLowI16X8U
+i32x4.extmul_high_i16x8_u, TokenType::Binary, Opcode::I32X4ExtmulHighI16X8U
+i32x4, TokenType::I32X4
+i32x4.trunc_sat_f32x4_s, TokenType::Unary, Opcode::I32X4TruncSatF32X4S
+i32x4.trunc_sat_f32x4_u, TokenType::Unary, Opcode::I32X4TruncSatF32X4U
+i32x4.extend_high_i16x8_s, TokenType::Unary, Opcode::I32X4ExtendHighI16X8S
+i32x4.extend_high_i16x8_u, TokenType::Unary, Opcode::I32X4ExtendHighI16X8U
+i32x4.extend_low_i16x8_s, TokenType::Unary, Opcode::I32X4ExtendLowI16X8S
+i32x4.extend_low_i16x8_u, TokenType::Unary, Opcode::I32X4ExtendLowI16X8U
+i32x4.trunc_sat_f64x2_s_zero, TokenType::Unary, Opcode::I32X4TruncSatF64X2SZero
+i32x4.trunc_sat_f64x2_u_zero, TokenType::Unary, Opcode::I32X4TruncSatF64X2UZero
+i32.xor, TokenType::Binary, Opcode::I32Xor
+i64.add, TokenType::Binary, Opcode::I64Add
+i64.and, TokenType::Binary, Opcode::I64And
+i64.atomic.load16_u, TokenType::AtomicLoad, Opcode::I64AtomicLoad16U
+i64.atomic.load32_u, TokenType::AtomicLoad, Opcode::I64AtomicLoad32U
+i64.atomic.load8_u, TokenType::AtomicLoad, Opcode::I64AtomicLoad8U
+i64.atomic.load, TokenType::AtomicLoad, Opcode::I64AtomicLoad
+i64.atomic.rmw16.add_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw16AddU
+i64.atomic.rmw16.and_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw16AndU
+i64.atomic.rmw16.cmpxchg_u, TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw16CmpxchgU
+i64.atomic.rmw16.or_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw16OrU
+i64.atomic.rmw16.sub_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw16SubU
+i64.atomic.rmw16.xchg_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw16XchgU
+i64.atomic.rmw16.xor_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw16XorU
+i64.atomic.rmw32.add_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw32AddU
+i64.atomic.rmw32.and_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw32AndU
+i64.atomic.rmw32.cmpxchg_u, TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw32CmpxchgU
+i64.atomic.rmw32.or_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw32OrU
+i64.atomic.rmw32.sub_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw32SubU
+i64.atomic.rmw32.xchg_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw32XchgU
+i64.atomic.rmw32.xor_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw32XorU
+i64.atomic.rmw8.add_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw8AddU
+i64.atomic.rmw8.and_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw8AndU
+i64.atomic.rmw8.cmpxchg_u, TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw8CmpxchgU
+i64.atomic.rmw8.or_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw8OrU
+i64.atomic.rmw8.sub_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw8SubU
+i64.atomic.rmw8.xchg_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw8XchgU
+i64.atomic.rmw8.xor_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw8XorU
+i64.atomic.rmw.add, TokenType::AtomicRmw, Opcode::I64AtomicRmwAdd
+i64.atomic.rmw.and, TokenType::AtomicRmw, Opcode::I64AtomicRmwAnd
+i64.atomic.rmw.cmpxchg, TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmwCmpxchg
+i64.atomic.rmw.or, TokenType::AtomicRmw, Opcode::I64AtomicRmwOr
+i64.atomic.rmw.sub, TokenType::AtomicRmw, Opcode::I64AtomicRmwSub
+i64.atomic.rmw.xchg, TokenType::AtomicRmw, Opcode::I64AtomicRmwXchg
+i64.atomic.rmw.xor, TokenType::AtomicRmw, Opcode::I64AtomicRmwXor
+i64.atomic.store16, TokenType::AtomicStore, Opcode::I64AtomicStore16
+i64.atomic.store32, TokenType::AtomicStore, Opcode::I64AtomicStore32
+i64.atomic.store8, TokenType::AtomicStore, Opcode::I64AtomicStore8
+i64.atomic.store, TokenType::AtomicStore, Opcode::I64AtomicStore
+i64.clz, TokenType::Unary, Opcode::I64Clz
+i64.const, TokenType::Const, Opcode::I64Const
+i64.ctz, TokenType::Unary, Opcode::I64Ctz
+i64.div_s, TokenType::Binary, Opcode::I64DivS
+i64.div_u, TokenType::Binary, Opcode::I64DivU
+i64.eq, TokenType::Compare, Opcode::I64Eq
+i64.eqz, TokenType::Convert, Opcode::I64Eqz
+i64.extend16_s, TokenType::Unary, Opcode::I64Extend16S
+i64.extend32_s, TokenType::Unary, Opcode::I64Extend32S
+i64.extend8_s, TokenType::Unary, Opcode::I64Extend8S
+i64.extend_i32_s, TokenType::Convert, Opcode::I64ExtendI32S
+i64.extend_i32_u, TokenType::Convert, Opcode::I64ExtendI32U
+i64.ge_s, TokenType::Compare, Opcode::I64GeS
+i64.ge_u, TokenType::Compare, Opcode::I64GeU
+i64.gt_s, TokenType::Compare, Opcode::I64GtS
+i64.gt_u, TokenType::Compare, Opcode::I64GtU
+i64.le_s, TokenType::Compare, Opcode::I64LeS
+i64.le_u, TokenType::Compare, Opcode::I64LeU
+i64.load16_s, TokenType::Load, Opcode::I64Load16S
+i64.load16_u, TokenType::Load, Opcode::I64Load16U
+i64.load32_s, TokenType::Load, Opcode::I64Load32S
+i64.load32_u, TokenType::Load, Opcode::I64Load32U
+i64.load8_s, TokenType::Load, Opcode::I64Load8S
+i64.load8_u, TokenType::Load, Opcode::I64Load8U
+i64.load, TokenType::Load, Opcode::I64Load
+i64.lt_s, TokenType::Compare, Opcode::I64LtS
+i64.lt_u, TokenType::Compare, Opcode::I64LtU
+i64.mul, TokenType::Binary, Opcode::I64Mul
+i64.ne, TokenType::Compare, Opcode::I64Ne
+i64.or, TokenType::Binary, Opcode::I64Or
+i64.popcnt, TokenType::Unary, Opcode::I64Popcnt
+i64.reinterpret_f64, TokenType::Convert, Opcode::I64ReinterpretF64
+i64.rem_s, TokenType::Binary, Opcode::I64RemS
+i64.rem_u, TokenType::Binary, Opcode::I64RemU
+i64.rotl, TokenType::Binary, Opcode::I64Rotl
+i64.rotr, TokenType::Binary, Opcode::I64Rotr
+i64.shl, TokenType::Binary, Opcode::I64Shl
+i64.shr_s, TokenType::Binary, Opcode::I64ShrS
+i64.shr_u, TokenType::Binary, Opcode::I64ShrU
+i64.store16, TokenType::Store, Opcode::I64Store16
+i64.store32, TokenType::Store, Opcode::I64Store32
+i64.store8, TokenType::Store, Opcode::I64Store8
+i64.store, TokenType::Store, Opcode::I64Store
+i64.sub, TokenType::Binary, Opcode::I64Sub
+i64.trunc_f32_s, TokenType::Convert, Opcode::I64TruncF32S
+i64.trunc_f32_u, TokenType::Convert, Opcode::I64TruncF32U
+i64.trunc_f64_s, TokenType::Convert, Opcode::I64TruncF64S
+i64.trunc_f64_u, TokenType::Convert, Opcode::I64TruncF64U
+i64.trunc_sat_f32_s, TokenType::Convert, Opcode::I64TruncSatF32S
+i64.trunc_sat_f32_u, TokenType::Convert, Opcode::I64TruncSatF32U
+i64.trunc_sat_f64_s, TokenType::Convert, Opcode::I64TruncSatF64S
+i64.trunc_sat_f64_u, TokenType::Convert, Opcode::I64TruncSatF64U
+i64, Type::I64
+i64x2.add, TokenType::Binary, Opcode::I64X2Add
+i64x2.extract_lane, TokenType::SimdLaneOp, Opcode::I64X2ExtractLane
+v128.load32x2_s, TokenType::Load, Opcode::V128Load32X2S
+v128.load32x2_u, TokenType::Load, Opcode::V128Load32X2U
+i64x2.mul, TokenType::Binary, Opcode::I64X2Mul
+i64x2.eq, TokenType::Binary, Opcode::I64X2Eq
+i64x2.ne, TokenType::Binary, Opcode::I64X2Ne
+i64x2.lt_s, TokenType::Binary, Opcode::I64X2LtS
+i64x2.gt_s, TokenType::Binary, Opcode::I64X2GtS
+i64x2.le_s, TokenType::Binary, Opcode::I64X2LeS
+i64x2.ge_s, TokenType::Binary, Opcode::I64X2GeS
+i64x2.abs, TokenType::Unary, Opcode::I64X2Abs
+i64x2.neg, TokenType::Unary, Opcode::I64X2Neg
+i64x2.all_true, TokenType::Unary, Opcode::I64X2AllTrue
+i64x2.bitmask, TokenType::Unary, Opcode::I64X2Bitmask
+i64x2.extend_low_i32x4_s, TokenType::Unary, Opcode::I64X2ExtendLowI32X4S
+i64x2.extend_high_i32x4_s, TokenType::Unary, Opcode::I64X2ExtendHighI32X4S
+i64x2.extend_low_i32x4_u, TokenType::Unary, Opcode::I64X2ExtendLowI32X4U
+i64x2.extend_high_i32x4_u, TokenType::Unary, Opcode::I64X2ExtendHighI32X4U
+i64x2.relaxed_laneselect, TokenType::Ternary, Opcode::I64X2RelaxedLaneSelect
+i64x2.replace_lane, TokenType::SimdLaneOp, Opcode::I64X2ReplaceLane
+i64x2.shl, TokenType::Binary, Opcode::I64X2Shl
+i64x2.shr_s, TokenType::Binary, Opcode::I64X2ShrS
+i64x2.shr_u, TokenType::Binary, Opcode::I64X2ShrU
+i64x2.splat, TokenType::Unary, Opcode::I64X2Splat
+i64x2.sub, TokenType::Binary, Opcode::I64X2Sub
+i64x2.extmul_low_i32x4_s, TokenType::Binary, Opcode::I64X2ExtmulLowI32X4S
+i64x2.extmul_high_i32x4_s, TokenType::Binary, Opcode::I64X2ExtmulHighI32X4S
+i64x2.extmul_low_i32x4_u, TokenType::Binary, Opcode::I64X2ExtmulLowI32X4U
+i64x2.extmul_high_i32x4_u, TokenType::Binary, Opcode::I64X2ExtmulHighI32X4U
+i64x2, TokenType::I64X2
+i64.xor, TokenType::Binary, Opcode::I64Xor
+i8x16.abs, TokenType::Unary, Opcode::I8X16Abs
+i8x16.add_sat_s, TokenType::Binary, Opcode::I8X16AddSatS
+i8x16.add_sat_u, TokenType::Binary, Opcode::I8X16AddSatU
+i8x16.add, TokenType::Binary, Opcode::I8X16Add
+i8x16.all_true, TokenType::Unary, Opcode::I8X16AllTrue
+i8x16.avgr_u, TokenType::Binary, Opcode::I8X16AvgrU
+i8x16.bitmask, TokenType::Unary, Opcode::I8X16Bitmask
+i8x16.eq, TokenType::Compare, Opcode::I8X16Eq
+i8x16.extract_lane_s, TokenType::SimdLaneOp, Opcode::I8X16ExtractLaneS
+i8x16.extract_lane_u, TokenType::SimdLaneOp, Opcode::I8X16ExtractLaneU
+i8x16.ge_s, TokenType::Compare, Opcode::I8X16GeS
+i8x16.ge_u, TokenType::Compare, Opcode::I8X16GeU
+i8x16.gt_s, TokenType::Compare, Opcode::I8X16GtS
+i8x16.gt_u, TokenType::Compare, Opcode::I8X16GtU
+i8x16.le_s, TokenType::Compare, Opcode::I8X16LeS
+i8x16.le_u, TokenType::Compare, Opcode::I8X16LeU
+i8x16.lt_s, TokenType::Compare, Opcode::I8X16LtS
+i8x16.lt_u, TokenType::Compare, Opcode::I8X16LtU
+i8x16.max_s, TokenType::Binary, Opcode::I8X16MaxS
+i8x16.max_u, TokenType::Binary, Opcode::I8X16MaxU
+i8x16.min_s, TokenType::Binary, Opcode::I8X16MinS
+i8x16.min_u, TokenType::Binary, Opcode::I8X16MinU
+i8x16.narrow_i16x8_s, TokenType::Binary, Opcode::I8X16NarrowI16X8S
+i8x16.narrow_i16x8_u, TokenType::Binary, Opcode::I8X16NarrowI16X8U
+i8x16.neg, TokenType::Unary, Opcode::I8X16Neg
+i8x16.popcnt, TokenType::Unary, Opcode::I8X16Popcnt
+i8x16.ne, TokenType::Compare, Opcode::I8X16Ne
+i8x16.relaxed_swizzle, TokenType::Binary, Opcode::I8X16RelaxedSwizzle
+i8x16.relaxed_laneselect, TokenType::Ternary, Opcode::I8X16RelaxedLaneSelect
+i8x16.replace_lane, TokenType::SimdLaneOp, Opcode::I8X16ReplaceLane
+i8x16.shl, TokenType::Binary, Opcode::I8X16Shl
+i8x16.shr_s, TokenType::Binary, Opcode::I8X16ShrS
+i8x16.shr_u, TokenType::Binary, Opcode::I8X16ShrU
+i8x16.splat, TokenType::Unary, Opcode::I8X16Splat
+i8x16.sub_sat_s, TokenType::Binary, Opcode::I8X16SubSatS
+i8x16.sub_sat_u, TokenType::Binary, Opcode::I8X16SubSatU
+i8x16.sub, TokenType::Binary, Opcode::I8X16Sub
+i8x16, TokenType::I8X16
+if, TokenType::If, Opcode::If
+import, TokenType::Import
+input, TokenType::Input
+invoke, TokenType::Invoke
+item, TokenType::Item
+local.get, TokenType::LocalGet, Opcode::LocalGet
+local.set, TokenType::LocalSet, Opcode::LocalSet
+local.tee, TokenType::LocalTee, Opcode::LocalTee
+local, TokenType::Local
+loop, TokenType::Loop, Opcode::Loop
+memory.atomic.notify, TokenType::AtomicNotify, Opcode::MemoryAtomicNotify
+memory.atomic.wait32, TokenType::AtomicWait, Opcode::MemoryAtomicWait32
+memory.atomic.wait64, TokenType::AtomicWait, Opcode::MemoryAtomicWait64
+memory.copy, TokenType::MemoryCopy, Opcode::MemoryCopy
+memory.fill, TokenType::MemoryFill, Opcode::MemoryFill
+memory.grow, TokenType::MemoryGrow, Opcode::MemoryGrow
+memory.init, TokenType::MemoryInit, Opcode::MemoryInit
+memory.size, TokenType::MemorySize, Opcode::MemorySize
+memory, TokenType::Memory
+module, TokenType::Module
+mut, TokenType::Mut
+nan:arithmetic, TokenType::NanArithmetic
+nan:canonical, TokenType::NanCanonical
+nop, TokenType::Nop, Opcode::Nop
+offset, TokenType::Offset
+output, TokenType::Output
+param, TokenType::Param
+ref, TokenType::Ref
+quote, TokenType::Quote
+ref.extern, TokenType::RefExtern
+ref.func, TokenType::RefFunc, Opcode::RefFunc
+ref.is_null, TokenType::RefIsNull, Opcode::RefIsNull
+ref.null, TokenType::RefNull, Opcode::RefNull
+register, TokenType::Register
+result, TokenType::Result
+rethrow, TokenType::Rethrow, Opcode::Rethrow
+return_call_indirect, TokenType::ReturnCallIndirect, Opcode::ReturnCallIndirect
+return_call, TokenType::ReturnCall, Opcode::ReturnCall
+return, TokenType::Return, Opcode::Return
+select, TokenType::Select, Opcode::Select
+shared, TokenType::Shared
+start, TokenType::Start
+struct, Type::Struct, TokenType::Struct
+table.copy, TokenType::TableCopy, Opcode::TableCopy
+table.fill, TokenType::TableFill, Opcode::TableFill
+table.get, TokenType::TableGet, Opcode::TableGet
+table.grow, TokenType::TableGrow, Opcode::TableGrow
+table.init, TokenType::TableInit, Opcode::TableInit
+table.set, TokenType::TableSet, Opcode::TableSet
+table.size, TokenType::TableSize, Opcode::TableSize
+table, TokenType::Table
+then, TokenType::Then
+throw, TokenType::Throw, Opcode::Throw
+try, TokenType::Try, Opcode::Try
+type, TokenType::Type
+unreachable, TokenType::Unreachable, Opcode::Unreachable
+v128.andnot, TokenType::Binary, Opcode::V128Andnot
+v128.and, TokenType::Binary, Opcode::V128And
+v128.bitselect, TokenType::Ternary, Opcode::V128BitSelect
+v128.const, TokenType::Const, Opcode::V128Const
+v128.load, TokenType::Load, Opcode::V128Load
+v128.not, TokenType::Unary, Opcode::V128Not
+v128.or, TokenType::Binary, Opcode::V128Or
+v128.any_true, TokenType::Unary, Opcode::V128AnyTrue
+v128.load32_zero, TokenType::Load, Opcode::V128Load32Zero
+v128.load64_zero, TokenType::Load, Opcode::V128Load64Zero
+v128.store, TokenType::Store, Opcode::V128Store
+v128, Type::V128
+v128.xor, TokenType::Binary, Opcode::V128Xor
+v128.load16_splat, TokenType::Load, Opcode::V128Load16Splat
+v128.load32_splat, TokenType::Load, Opcode::V128Load32Splat
+v128.load64_splat, TokenType::Load, Opcode::V128Load64Splat
+v128.load8_splat, TokenType::Load, Opcode::V128Load8Splat
+v128.load8_lane, TokenType::SimdLoadLane, Opcode::V128Load8Lane
+v128.load16_lane, TokenType::SimdLoadLane, Opcode::V128Load16Lane
+v128.load32_lane, TokenType::SimdLoadLane, Opcode::V128Load32Lane
+v128.load64_lane, TokenType::SimdLoadLane, Opcode::V128Load64Lane
+v128.store8_lane, TokenType::SimdStoreLane, Opcode::V128Store8Lane
+v128.store16_lane, TokenType::SimdStoreLane, Opcode::V128Store16Lane
+v128.store32_lane, TokenType::SimdStoreLane, Opcode::V128Store32Lane
+v128.store64_lane, TokenType::SimdStoreLane, Opcode::V128Store64Lane
+i8x16.shuffle, TokenType::SimdShuffleOp, Opcode::I8X16Shuffle
+i8x16.swizzle, TokenType::Binary, Opcode::I8X16Swizzle
diff --git a/third_party/wasm2c/src/lexer-source-line-finder.cc b/third_party/wasm2c/src/lexer-source-line-finder.cc
new file mode 100644
index 0000000000..4d407e9d8e
--- /dev/null
+++ b/third_party/wasm2c/src/lexer-source-line-finder.cc
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/lexer-source-line-finder.h"
+
+#include <algorithm>
+
+#include "wabt/lexer-source.h"
+
+namespace wabt {
+
+LexerSourceLineFinder::LexerSourceLineFinder(
+ std::unique_ptr<LexerSource> source)
+ : source_(std::move(source)),
+ next_line_start_(0),
+ last_cr_(false),
+ eof_(false) {
+ source_->Seek(0);
+ // Line 0 should not be used; but it makes indexing simpler.
+ line_ranges_.emplace_back(0, 0);
+}
+
+Result LexerSourceLineFinder::GetSourceLine(const Location& loc,
+ Offset max_line_length,
+ SourceLine* out_source_line) {
+ ColumnRange column_range(loc.first_column, loc.last_column);
+ OffsetRange original;
+ CHECK_RESULT(GetLineOffsets(loc.line, &original));
+
+ OffsetRange clamped =
+ ClampSourceLineOffsets(original, column_range, max_line_length);
+ bool has_start_ellipsis = original.start != clamped.start;
+ bool has_end_ellipsis = original.end != clamped.end;
+
+ out_source_line->column_offset = clamped.start - original.start;
+
+ if (has_start_ellipsis) {
+ out_source_line->line += "...";
+ clamped.start += 3;
+ }
+ if (has_end_ellipsis) {
+ clamped.end -= 3;
+ }
+
+ std::vector<char> read_line;
+ CHECK_RESULT(source_->ReadRange(clamped, &read_line));
+ out_source_line->line.append(read_line.begin(), read_line.end());
+
+ if (has_end_ellipsis) {
+ out_source_line->line += "...";
+ }
+
+ return Result::Ok;
+}
+
+bool LexerSourceLineFinder::IsLineCached(int line) const {
+ return static_cast<size_t>(line) < line_ranges_.size();
+}
+
+OffsetRange LexerSourceLineFinder::GetCachedLine(int line) const {
+ assert(IsLineCached(line));
+ return line_ranges_[line];
+}
+
+Result LexerSourceLineFinder::GetLineOffsets(int find_line,
+ OffsetRange* out_range) {
+ if (IsLineCached(find_line)) {
+ *out_range = GetCachedLine(find_line);
+ return Result::Ok;
+ }
+
+ const size_t kBufferSize = 1 << 16;
+ std::vector<char> buffer(kBufferSize);
+
+ assert(!line_ranges_.empty());
+ Offset buffer_file_offset = 0;
+ while (!IsLineCached(find_line) && !eof_) {
+ CHECK_RESULT(source_->Tell(&buffer_file_offset));
+ size_t read_size = source_->Fill(buffer.data(), buffer.size());
+ if (read_size < buffer.size()) {
+ eof_ = true;
+ }
+
+ for (auto iter = buffer.begin(), end = iter + read_size; iter < end;
+ ++iter) {
+ if (*iter == '\n') {
+ // Don't include \n or \r in the line range.
+ Offset line_offset =
+ buffer_file_offset + (iter - buffer.begin()) - last_cr_;
+ line_ranges_.emplace_back(next_line_start_, line_offset);
+ next_line_start_ = line_offset + last_cr_ + 1;
+ }
+ last_cr_ = *iter == '\r';
+ }
+
+ if (eof_) {
+ // Add the final line as an empty range.
+ Offset end = buffer_file_offset + read_size;
+ line_ranges_.emplace_back(next_line_start_, end);
+ }
+ }
+
+ if (IsLineCached(find_line)) {
+ *out_range = GetCachedLine(find_line);
+ return Result::Ok;
+ } else {
+ assert(eof_);
+ return Result::Error;
+ }
+}
+
+// static
+OffsetRange LexerSourceLineFinder::ClampSourceLineOffsets(
+ OffsetRange offset_range,
+ ColumnRange column_range,
+ Offset max_line_length) {
+ Offset line_length = offset_range.size();
+ if (line_length > max_line_length) {
+ size_t column_count = column_range.size();
+ size_t center_on;
+ if (column_count > max_line_length) {
+ // The column range doesn't fit, just center on first_column.
+ center_on = column_range.start - 1;
+ } else {
+ // the entire range fits, display it all in the center.
+ center_on = (column_range.start + column_range.end) / 2 - 1;
+ }
+ if (center_on > max_line_length / 2) {
+ offset_range.start += center_on - max_line_length / 2;
+ }
+ offset_range.start =
+ std::min(offset_range.start, offset_range.end - max_line_length);
+ offset_range.end = offset_range.start + max_line_length;
+ }
+
+ return offset_range;
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/lexer-source.cc b/third_party/wasm2c/src/lexer-source.cc
new file mode 100644
index 0000000000..eff56e2571
--- /dev/null
+++ b/third_party/wasm2c/src/lexer-source.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/lexer-source.h"
+
+#include <algorithm>
+
+namespace wabt {
+
+LexerSource::LexerSource(const void* data, Offset size)
+ : data_(data), size_(size), read_offset_(0) {}
+
+std::unique_ptr<LexerSource> LexerSource::Clone() {
+ LexerSource* result = new LexerSource(data_, size_);
+ result->read_offset_ = read_offset_;
+ return std::unique_ptr<LexerSource>(result);
+}
+
+Result LexerSource::Tell(Offset* out_offset) {
+ *out_offset = read_offset_;
+ return Result::Ok;
+}
+
+size_t LexerSource::Fill(void* dest, Offset size) {
+ Offset read_size = std::min(size, size_ - read_offset_);
+ if (read_size > 0) {
+ const void* src = static_cast<const char*>(data_) + read_offset_;
+ memcpy(dest, src, read_size);
+ read_offset_ += read_size;
+ }
+ return read_size;
+}
+
+Result LexerSource::Seek(Offset offset) {
+ if (offset < size_) {
+ read_offset_ = offset;
+ return Result::Ok;
+ }
+ return Result::Error;
+}
+
+Result LexerSource::ReadRange(OffsetRange range, std::vector<char>* out_data) {
+ OffsetRange clamped = range;
+ clamped.start = std::min(clamped.start, size_);
+ clamped.end = std::min(clamped.end, size_);
+ if (clamped.size()) {
+ out_data->resize(clamped.size());
+ const void* src = static_cast<const char*>(data_) + clamped.start;
+ memcpy(out_data->data(), src, clamped.size());
+ }
+ return Result::Ok;
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/literal.cc b/third_party/wasm2c/src/literal.cc
new file mode 100644
index 0000000000..7076630f66
--- /dev/null
+++ b/third_party/wasm2c/src/literal.cc
@@ -0,0 +1,829 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/literal.h"
+
+#include <cassert>
+#include <cerrno>
+#include <cinttypes>
+#include <cmath>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+#include <type_traits>
+
+namespace wabt {
+
+namespace {
+
+template <typename T>
+struct FloatTraitsBase {};
+
+// The "PlusOne" values are used because normal IEEE floats have an implicit
+// leading one, so they have an additional bit of precision.
+
+template <>
+struct FloatTraitsBase<float> {
+ using Uint = uint32_t;
+ static constexpr int kBits = sizeof(Uint) * 8;
+ static constexpr int kSigBits = 23;
+ static constexpr float kHugeVal = HUGE_VALF;
+ static constexpr int kMaxHexBufferSize = WABT_MAX_FLOAT_HEX;
+
+ static float Strto(const char* s, char** endptr) { return strtof(s, endptr); }
+};
+
+template <>
+struct FloatTraitsBase<double> {
+ using Uint = uint64_t;
+ static constexpr int kBits = sizeof(Uint) * 8;
+ static constexpr int kSigBits = 52;
+ static constexpr float kHugeVal = HUGE_VAL;
+ static constexpr int kMaxHexBufferSize = WABT_MAX_DOUBLE_HEX;
+
+ static double Strto(const char* s, char** endptr) {
+ return strtod(s, endptr);
+ }
+};
+
+template <typename T>
+struct FloatTraits : FloatTraitsBase<T> {
+ using Uint = typename FloatTraitsBase<T>::Uint;
+ using FloatTraitsBase<T>::kBits;
+ using FloatTraitsBase<T>::kSigBits;
+
+ static constexpr int kExpBits = kBits - kSigBits - 1;
+ static constexpr int kSignShift = kBits - 1;
+ static constexpr Uint kSigMask = (Uint(1) << kSigBits) - 1;
+ static constexpr int kSigPlusOneBits = kSigBits + 1;
+ static constexpr Uint kSigPlusOneMask = (Uint(1) << kSigPlusOneBits) - 1;
+ static constexpr int kExpMask = (1 << kExpBits) - 1;
+ static constexpr int kMaxExp = 1 << (kExpBits - 1);
+ static constexpr int kMinExp = -kMaxExp + 1;
+ static constexpr int kExpBias = -kMinExp;
+ static constexpr Uint kQuietNanTag = Uint(1) << (kSigBits - 1);
+};
+
+template <typename T>
+class FloatParser {
+ public:
+ using Traits = FloatTraits<T>;
+ using Uint = typename Traits::Uint;
+ using Float = T;
+
+ static Result Parse(LiteralType,
+ const char* s,
+ const char* end,
+ Uint* out_bits);
+
+ private:
+ static bool StringStartsWith(const char* start,
+ const char* end,
+ const char* prefix);
+ static Uint Make(bool sign, int exp, Uint sig);
+ static Uint ShiftAndRoundToNearest(Uint significand,
+ int shift,
+ bool seen_trailing_non_zero);
+
+ static Result ParseFloat(const char* s, const char* end, Uint* out_bits);
+ static Result ParseNan(const char* s, const char* end, Uint* out_bits);
+ static Result ParseHex(const char* s, const char* end, Uint* out_bits);
+ static void ParseInfinity(const char* s, const char* end, Uint* out_bits);
+};
+
+template <typename T>
+class FloatWriter {
+ public:
+ using Traits = FloatTraits<T>;
+ using Uint = typename Traits::Uint;
+
+ static void WriteHex(char* out, size_t size, Uint bits);
+};
+
+// Return 1 if the non-NULL-terminated string starting with |start| and ending
+// with |end| starts with the NULL-terminated string |prefix|.
+template <typename T>
+// static
+bool FloatParser<T>::StringStartsWith(const char* start,
+ const char* end,
+ const char* prefix) {
+ while (start < end && *prefix) {
+ if (*start != *prefix) {
+ return false;
+ }
+ start++;
+ prefix++;
+ }
+ return *prefix == 0;
+}
+
+// static
+template <typename T>
+Result FloatParser<T>::ParseFloat(const char* s,
+ const char* end,
+ Uint* out_bits) {
+ // Here is the normal behavior for strtof/strtod:
+ //
+ // input | errno | output |
+ // ---------------------------------
+ // overflow | ERANGE | +-HUGE_VAL |
+ // underflow | ERANGE | 0.0 |
+ // otherwise | 0 | value |
+ //
+ // So normally we need to clear errno before calling strto{f,d}, and check
+ // afterward whether it was set to ERANGE.
+ //
+ // glibc seems to have a bug where
+ // strtof("340282356779733661637539395458142568448") will return HUGE_VAL,
+ // but will not set errno to ERANGE. Since this function is only called when
+ // we know that we have parsed a "normal" number (i.e. not "inf"), we know
+ // that if we ever get HUGE_VAL, it must be overflow.
+ //
+ // The WebAssembly spec also ignores underflow, so we don't need to check for
+ // ERANGE at all.
+
+ // WebAssembly floats can contain underscores, but strto* can't parse those,
+ // so remove them first.
+ assert(s <= end);
+ const size_t kBufferSize = end - s + 1; // +1 for \0.
+ char* buffer = static_cast<char*>(alloca(kBufferSize));
+ auto buffer_end =
+ std::copy_if(s, end, buffer, [](char c) -> bool { return c != '_'; });
+ assert(buffer_end < buffer + kBufferSize);
+ *buffer_end = 0;
+
+ char* endptr;
+ Float value = Traits::Strto(buffer, &endptr);
+ if (endptr != buffer_end ||
+ (value == Traits::kHugeVal || value == -Traits::kHugeVal)) {
+ return Result::Error;
+ }
+
+ memcpy(out_bits, &value, sizeof(value));
+ return Result::Ok;
+}
+
+// static
+template <typename T>
+typename FloatParser<T>::Uint FloatParser<T>::Make(bool sign,
+ int exp,
+ Uint sig) {
+ assert(exp >= Traits::kMinExp && exp <= Traits::kMaxExp);
+ assert(sig <= Traits::kSigMask);
+ return (Uint(sign) << Traits::kSignShift) |
+ (Uint(exp + Traits::kExpBias) << Traits::kSigBits) | sig;
+}
+
+// static
+template <typename T>
+typename FloatParser<T>::Uint FloatParser<T>::ShiftAndRoundToNearest(
+ Uint significand,
+ int shift,
+ bool seen_trailing_non_zero) {
+ assert(shift > 0);
+ // Round ties to even.
+ if ((significand & (Uint(1) << shift)) || seen_trailing_non_zero) {
+ significand += Uint(1) << (shift - 1);
+ }
+ significand >>= shift;
+ return significand;
+}
+
+// static
+template <typename T>
+Result FloatParser<T>::ParseNan(const char* s,
+ const char* end,
+ Uint* out_bits) {
+ bool is_neg = false;
+ if (*s == '-') {
+ is_neg = true;
+ s++;
+ } else if (*s == '+') {
+ s++;
+ }
+ assert(StringStartsWith(s, end, "nan"));
+ s += 3;
+
+ Uint tag;
+ if (s != end) {
+ tag = 0;
+ assert(StringStartsWith(s, end, ":0x"));
+ s += 3;
+
+ for (; s < end; ++s) {
+ if (*s == '_') {
+ continue;
+ }
+ uint32_t digit;
+ CHECK_RESULT(ParseHexdigit(*s, &digit));
+ tag = tag * 16 + digit;
+ // Check for overflow.
+ if (tag > Traits::kSigMask) {
+ return Result::Error;
+ }
+ }
+
+ // NaN cannot have a zero tag, that is reserved for infinity.
+ if (tag == 0) {
+ return Result::Error;
+ }
+ } else {
+ tag = Traits::kQuietNanTag;
+ }
+
+ *out_bits = Make(is_neg, Traits::kMaxExp, tag);
+ return Result::Ok;
+}
+
+// static
+template <typename T>
+Result FloatParser<T>::ParseHex(const char* s,
+ const char* end,
+ Uint* out_bits) {
+ bool is_neg = false;
+ if (*s == '-') {
+ is_neg = true;
+ s++;
+ } else if (*s == '+') {
+ s++;
+ }
+ assert(StringStartsWith(s, end, "0x"));
+ s += 2;
+
+ // Loop over the significand; everything up to the 'p'.
+ // This code is a bit nasty because we want to support extra zeroes anywhere
+ // without having to use many significand bits.
+ // e.g.
+ // 0x00000001.0p0 => significand = 1, significand_exponent = 0
+ // 0x10000000.0p0 => significand = 1, significand_exponent = 28
+ // 0x0.000001p0 => significand = 1, significand_exponent = -24
+ bool seen_dot = false;
+ bool seen_trailing_non_zero = false;
+ Uint significand = 0;
+ int significand_exponent = 0; // Exponent adjustment due to dot placement.
+ for (; s < end; ++s) {
+ uint32_t digit;
+ if (*s == '_') {
+ continue;
+ } else if (*s == '.') {
+ seen_dot = true;
+ } else if (Succeeded(ParseHexdigit(*s, &digit))) {
+ if (Traits::kBits - Clz(significand) <= Traits::kSigPlusOneBits) {
+ significand = (significand << 4) + digit;
+ if (seen_dot) {
+ significand_exponent -= 4;
+ }
+ } else {
+ if (!seen_trailing_non_zero && digit != 0) {
+ seen_trailing_non_zero = true;
+ }
+ if (!seen_dot) {
+ significand_exponent += 4;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (significand == 0) {
+ // 0 or -0.
+ *out_bits = Make(is_neg, Traits::kMinExp, 0);
+ return Result::Ok;
+ }
+
+ int exponent = 0;
+ bool exponent_is_neg = false;
+ if (s < end) {
+ assert(*s == 'p' || *s == 'P');
+ s++;
+ // Exponent is always positive, but significand_exponent is signed.
+ // significand_exponent_add is negated if exponent will be negative, so it
+ // can be easily summed to see if the exponent is too large (see below).
+ int significand_exponent_add = 0;
+ if (*s == '-') {
+ exponent_is_neg = true;
+ significand_exponent_add = -significand_exponent;
+ s++;
+ } else if (*s == '+') {
+ s++;
+ significand_exponent_add = significand_exponent;
+ }
+
+ for (; s < end; ++s) {
+ if (*s == '_') {
+ continue;
+ }
+
+ uint32_t digit = (*s - '0');
+ assert(digit <= 9);
+ exponent = exponent * 10 + digit;
+ if (exponent + significand_exponent_add >= Traits::kMaxExp) {
+ break;
+ }
+ }
+ }
+
+ if (exponent_is_neg) {
+ exponent = -exponent;
+ }
+
+ int significand_bits = Traits::kBits - Clz(significand);
+ // -1 for the implicit 1 bit of the significand.
+ exponent += significand_exponent + significand_bits - 1;
+
+ if (exponent <= Traits::kMinExp) {
+ // Maybe subnormal.
+ auto update_seen_trailing_non_zero = [&](int shift) {
+ assert(shift > 0);
+ auto mask = (Uint(1) << (shift - 1)) - 1;
+ seen_trailing_non_zero |= (significand & mask) != 0;
+ };
+
+ // Normalize significand.
+ if (significand_bits > Traits::kSigBits) {
+ int shift = significand_bits - Traits::kSigBits;
+ update_seen_trailing_non_zero(shift);
+ significand >>= shift;
+ } else if (significand_bits < Traits::kSigBits) {
+ significand <<= (Traits::kSigBits - significand_bits);
+ }
+
+ int shift = Traits::kMinExp - exponent;
+ if (shift <= Traits::kSigBits) {
+ if (shift) {
+ update_seen_trailing_non_zero(shift);
+ significand =
+ ShiftAndRoundToNearest(significand, shift, seen_trailing_non_zero) &
+ Traits::kSigMask;
+ }
+ exponent = Traits::kMinExp;
+
+ if (significand != 0) {
+ *out_bits = Make(is_neg, exponent, significand);
+ return Result::Ok;
+ }
+ }
+
+ // Not subnormal, too small; return 0 or -0.
+ *out_bits = Make(is_neg, Traits::kMinExp, 0);
+ } else {
+ // Maybe Normal value.
+ if (significand_bits > Traits::kSigPlusOneBits) {
+ significand = ShiftAndRoundToNearest(
+ significand, significand_bits - Traits::kSigPlusOneBits,
+ seen_trailing_non_zero);
+ if (significand > Traits::kSigPlusOneMask) {
+ exponent++;
+ }
+ } else if (significand_bits < Traits::kSigPlusOneBits) {
+ significand <<= (Traits::kSigPlusOneBits - significand_bits);
+ }
+
+ if (exponent >= Traits::kMaxExp) {
+ // Would be inf or -inf, but the spec doesn't allow rounding hex-floats to
+ // infinity.
+ return Result::Error;
+ }
+
+ *out_bits = Make(is_neg, exponent, significand & Traits::kSigMask);
+ }
+
+ return Result::Ok;
+}
+
+// static
+template <typename T>
+void FloatParser<T>::ParseInfinity(const char* s,
+ const char* end,
+ Uint* out_bits) {
+ bool is_neg = false;
+ if (*s == '-') {
+ is_neg = true;
+ s++;
+ } else if (*s == '+') {
+ s++;
+ }
+ assert(StringStartsWith(s, end, "inf"));
+ *out_bits = Make(is_neg, Traits::kMaxExp, 0);
+}
+
+// static
+template <typename T>
+Result FloatParser<T>::Parse(LiteralType literal_type,
+ const char* s,
+ const char* end,
+ Uint* out_bits) {
+#if COMPILER_IS_MSVC
+ if (literal_type == LiteralType::Int && StringStartsWith(s, end, "0x")) {
+ // Some MSVC crt implementation of strtof doesn't support hex strings
+ literal_type = LiteralType::Hexfloat;
+ }
+#endif
+ switch (literal_type) {
+ case LiteralType::Int:
+ case LiteralType::Float:
+ return ParseFloat(s, end, out_bits);
+
+ case LiteralType::Hexfloat:
+ return ParseHex(s, end, out_bits);
+
+ case LiteralType::Infinity:
+ ParseInfinity(s, end, out_bits);
+ return Result::Ok;
+
+ case LiteralType::Nan:
+ return ParseNan(s, end, out_bits);
+ }
+
+ WABT_UNREACHABLE;
+}
+
+// static
+template <typename T>
+void FloatWriter<T>::WriteHex(char* out, size_t size, Uint bits) {
+ static constexpr int kNumNybbles = Traits::kBits / 4;
+ static constexpr int kTopNybbleShift = Traits::kBits - 4;
+ static constexpr Uint kTopNybble = Uint(0xf) << kTopNybbleShift;
+ static const char s_hex_digits[] = "0123456789abcdef";
+
+ char buffer[Traits::kMaxHexBufferSize];
+ char* p = buffer;
+ bool is_neg = (bits >> Traits::kSignShift);
+ int exp = ((bits >> Traits::kSigBits) & Traits::kExpMask) - Traits::kExpBias;
+ Uint sig = bits & Traits::kSigMask;
+
+ if (is_neg) {
+ *p++ = '-';
+ }
+ if (exp == Traits::kMaxExp) {
+ // Infinity or nan.
+ if (sig == 0) {
+ strcpy(p, "inf");
+ p += 3;
+ } else {
+ strcpy(p, "nan");
+ p += 3;
+ if (sig != Traits::kQuietNanTag) {
+ strcpy(p, ":0x");
+ p += 3;
+ // Skip leading zeroes.
+ int num_nybbles = kNumNybbles;
+ while ((sig & kTopNybble) == 0) {
+ sig <<= 4;
+ num_nybbles--;
+ }
+ while (num_nybbles) {
+ Uint nybble = (sig >> kTopNybbleShift) & 0xf;
+ *p++ = s_hex_digits[nybble];
+ sig <<= 4;
+ --num_nybbles;
+ }
+ }
+ }
+ } else {
+ bool is_zero = sig == 0 && exp == Traits::kMinExp;
+ strcpy(p, "0x");
+ p += 2;
+ *p++ = is_zero ? '0' : '1';
+
+ // Shift sig up so the top 4-bits are at the top of the Uint.
+ sig <<= Traits::kBits - Traits::kSigBits;
+
+ if (sig) {
+ if (exp == Traits::kMinExp) {
+ // Subnormal; shift the significand up, and shift out the implicit 1.
+ Uint leading_zeroes = Clz(sig);
+ if (leading_zeroes < Traits::kSignShift) {
+ sig <<= leading_zeroes + 1;
+ } else {
+ sig = 0;
+ }
+ exp -= leading_zeroes;
+ }
+
+ *p++ = '.';
+ while (sig) {
+ int nybble = (sig >> kTopNybbleShift) & 0xf;
+ *p++ = s_hex_digits[nybble];
+ sig <<= 4;
+ }
+ }
+ *p++ = 'p';
+ if (is_zero) {
+ strcpy(p, "+0");
+ p += 2;
+ } else {
+ if (exp < 0) {
+ *p++ = '-';
+ exp = -exp;
+ } else {
+ *p++ = '+';
+ }
+ if (exp >= 1000) {
+ *p++ = '1';
+ }
+ if (exp >= 100) {
+ *p++ = '0' + (exp / 100) % 10;
+ }
+ if (exp >= 10) {
+ *p++ = '0' + (exp / 10) % 10;
+ }
+ *p++ = '0' + exp % 10;
+ }
+ }
+
+ size_t len = p - buffer;
+ if (len >= size) {
+ len = size - 1;
+ }
+ memcpy(out, buffer, len);
+ out[len] = '\0';
+}
+
+} // end anonymous namespace
+
+Result ParseHexdigit(char c, uint32_t* out) {
+ if (static_cast<unsigned int>(c - '0') <= 9) {
+ *out = c - '0';
+ return Result::Ok;
+ } else if (static_cast<unsigned int>(c - 'a') < 6) {
+ *out = 10 + (c - 'a');
+ return Result::Ok;
+ } else if (static_cast<unsigned int>(c - 'A') < 6) {
+ *out = 10 + (c - 'A');
+ return Result::Ok;
+ }
+ return Result::Error;
+}
+
+Result ParseUint64(const char* s, const char* end, uint64_t* out) {
+ if (s == end) {
+ return Result::Error;
+ }
+ uint64_t value = 0;
+ if (*s == '0' && s + 1 < end && s[1] == 'x') {
+ s += 2;
+ if (s == end) {
+ return Result::Error;
+ }
+ constexpr uint64_t kMaxDiv16 = UINT64_MAX / 16;
+ constexpr uint64_t kMaxMod16 = UINT64_MAX % 16;
+ for (; s < end; ++s) {
+ uint32_t digit;
+ if (*s == '_') {
+ continue;
+ }
+ CHECK_RESULT(ParseHexdigit(*s, &digit));
+ // Check for overflow.
+ if (value > kMaxDiv16 || (value == kMaxDiv16 && digit > kMaxMod16)) {
+ return Result::Error;
+ }
+ value = value * 16 + digit;
+ }
+ } else {
+ constexpr uint64_t kMaxDiv10 = UINT64_MAX / 10;
+ constexpr uint64_t kMaxMod10 = UINT64_MAX % 10;
+ for (; s < end; ++s) {
+ if (*s == '_') {
+ continue;
+ }
+ uint32_t digit = (*s - '0');
+ if (digit > 9) {
+ return Result::Error;
+ }
+ // Check for overflow.
+ if (value > kMaxDiv10 || (value == kMaxDiv10 && digit > kMaxMod10)) {
+ return Result::Error;
+ }
+ value = value * 10 + digit;
+ }
+ }
+ if (s != end) {
+ return Result::Error;
+ }
+ *out = value;
+ return Result::Ok;
+}
+
+Result ParseInt64(const char* s,
+ const char* end,
+ uint64_t* out,
+ ParseIntType parse_type) {
+ bool has_sign = false;
+ if (*s == '-' || *s == '+') {
+ if (parse_type == ParseIntType::UnsignedOnly) {
+ return Result::Error;
+ }
+ if (*s == '-') {
+ has_sign = true;
+ }
+ s++;
+ }
+ uint64_t value = 0;
+ Result result = ParseUint64(s, end, &value);
+ if (has_sign) {
+ // abs(INT64_MIN) == INT64_MAX + 1.
+ if (value > static_cast<uint64_t>(INT64_MAX) + 1) {
+ return Result::Error;
+ }
+ value = UINT64_MAX - value + 1;
+ }
+ *out = value;
+ return result;
+}
+
+namespace {
+uint32_t AddWithCarry(uint32_t x, uint32_t y, uint32_t* carry) {
+ // Increments *carry if the addition overflows, otherwise leaves carry alone.
+ if ((0xffffffff - x) < y) {
+ ++*carry;
+ }
+ return x + y;
+}
+
+void Mul10(v128* v) {
+ // Multiply-by-10 decomposes into (x << 3) + (x << 1). We implement those
+ // operations with carrying from smaller quads of the v128 to the larger
+ // quads.
+
+ constexpr uint32_t kTopThreeBits = 0xe0000000;
+ constexpr uint32_t kTopBit = 0x80000000;
+
+ uint32_t carry_into_v1 =
+ ((v->u32(0) & kTopThreeBits) >> 29) + ((v->u32(0) & kTopBit) >> 31);
+ v->set_u32(0, AddWithCarry(v->u32(0) << 3, v->u32(0) << 1, &carry_into_v1));
+ uint32_t carry_into_v2 =
+ ((v->u32(1) & kTopThreeBits) >> 29) + ((v->u32(1) & kTopBit) >> 31);
+ v->set_u32(1, AddWithCarry(v->u32(1) << 3, v->u32(1) << 1, &carry_into_v2));
+ v->set_u32(1, AddWithCarry(v->u32(1), carry_into_v1, &carry_into_v2));
+ uint32_t carry_into_v3 =
+ ((v->u32(2) & kTopThreeBits) >> 29) + ((v->u32(2) & kTopBit) >> 31);
+ v->set_u32(2, AddWithCarry(v->u32(2) << 3, v->u32(2) << 1, &carry_into_v3));
+ v->set_u32(2, AddWithCarry(v->u32(2), carry_into_v2, &carry_into_v3));
+ v->set_u32(3, v->u32(3) * 10 + carry_into_v3);
+}
+} // namespace
+
+Result ParseUint128(const char* s, const char* end, v128* out) {
+ if (s == end) {
+ return Result::Error;
+ }
+
+ out->set_zero();
+
+ while (true) {
+ uint32_t digit = (*s - '0');
+ if (digit > 9) {
+ return Result::Error;
+ }
+
+ uint32_t carry_into_v1 = 0;
+ uint32_t carry_into_v2 = 0;
+ uint32_t carry_into_v3 = 0;
+ uint32_t overflow = 0;
+ out->set_u32(0, AddWithCarry(out->u32(0), digit, &carry_into_v1));
+ out->set_u32(1, AddWithCarry(out->u32(1), carry_into_v1, &carry_into_v2));
+ out->set_u32(2, AddWithCarry(out->u32(2), carry_into_v2, &carry_into_v3));
+ out->set_u32(3, AddWithCarry(out->u32(3), carry_into_v3, &overflow));
+ if (overflow) {
+ return Result::Error;
+ }
+
+ ++s;
+
+ if (s == end) {
+ break;
+ }
+
+ Mul10(out);
+ }
+ return Result::Ok;
+}
+
+template <typename U>
+Result ParseInt(const char* s,
+ const char* end,
+ U* out,
+ ParseIntType parse_type) {
+ using S = typename std::make_signed<U>::type;
+ uint64_t value;
+ bool has_sign = false;
+ if (*s == '-' || *s == '+') {
+ if (parse_type == ParseIntType::UnsignedOnly) {
+ return Result::Error;
+ }
+ if (*s == '-') {
+ has_sign = true;
+ }
+ s++;
+ }
+ CHECK_RESULT(ParseUint64(s, end, &value));
+
+ if (has_sign) {
+ // abs(INTN_MIN) == INTN_MAX + 1.
+ if (value > static_cast<uint64_t>(std::numeric_limits<S>::max()) + 1) {
+ return Result::Error;
+ }
+ value = std::numeric_limits<U>::max() - value + 1;
+ } else {
+ if (value > static_cast<uint64_t>(std::numeric_limits<U>::max())) {
+ return Result::Error;
+ }
+ }
+ *out = static_cast<U>(value);
+ return Result::Ok;
+}
+
+Result ParseInt8(const char* s,
+ const char* end,
+ uint8_t* out,
+ ParseIntType parse_type) {
+ return ParseInt(s, end, out, parse_type);
+}
+
+Result ParseInt16(const char* s,
+ const char* end,
+ uint16_t* out,
+ ParseIntType parse_type) {
+ return ParseInt(s, end, out, parse_type);
+}
+
+Result ParseInt32(const char* s,
+ const char* end,
+ uint32_t* out,
+ ParseIntType parse_type) {
+ return ParseInt(s, end, out, parse_type);
+}
+
+Result ParseFloat(LiteralType literal_type,
+ const char* s,
+ const char* end,
+ uint32_t* out_bits) {
+ return FloatParser<float>::Parse(literal_type, s, end, out_bits);
+}
+
+Result ParseDouble(LiteralType literal_type,
+ const char* s,
+ const char* end,
+ uint64_t* out_bits) {
+ return FloatParser<double>::Parse(literal_type, s, end, out_bits);
+}
+
+void WriteFloatHex(char* buffer, size_t size, uint32_t bits) {
+ return FloatWriter<float>::WriteHex(buffer, size, bits);
+}
+
+void WriteDoubleHex(char* buffer, size_t size, uint64_t bits) {
+ return FloatWriter<double>::WriteHex(buffer, size, bits);
+}
+
+void WriteUint128(char* buffer, size_t size, v128 bits) {
+ uint64_t digits;
+ uint64_t remainder;
+ char reversed_buffer[40];
+ size_t len = 0;
+ do {
+ remainder = bits.u32(3);
+
+ for (int i = 3; i != 0; --i) {
+ digits = remainder / 10;
+ remainder = ((remainder - digits * 10) << 32) + bits.u32(i - 1);
+ bits.set_u32(i, digits);
+ }
+
+ digits = remainder / 10;
+ remainder = remainder - digits * 10;
+ bits.set_u32(0, digits);
+
+ char remainder_buffer[21];
+ snprintf(remainder_buffer, 21, "%" PRIu64, remainder);
+ int remainder_buffer_len = strlen(remainder_buffer);
+ assert(len + remainder_buffer_len < sizeof(reversed_buffer));
+ memcpy(&reversed_buffer[len], remainder_buffer, remainder_buffer_len);
+ len += remainder_buffer_len;
+ } while (!bits.is_zero());
+ size_t truncated_tail = 0;
+ if (len >= size) {
+ truncated_tail = len - size + 1;
+ len = size - 1;
+ }
+ std::reverse_copy(reversed_buffer + truncated_tail,
+ reversed_buffer + len + truncated_tail, buffer);
+ buffer[len] = '\0';
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/opcode-code-table.c b/third_party/wasm2c/src/opcode-code-table.c
new file mode 100644
index 0000000000..e3fdc994e1
--- /dev/null
+++ b/third_party/wasm2c/src/opcode-code-table.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2018 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/opcode-code-table.h"
+
+#include "wabt/config.h"
+
+#include <stdint.h>
+
+typedef enum WabtOpcodeEnum {
+#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \
+ text, decomp) \
+ Name,
+#include "wabt/opcode.def"
+#undef WABT_OPCODE
+ Invalid,
+} WabtOpcodeEnum;
+
+WABT_STATIC_ASSERT(Invalid <= WABT_OPCODE_CODE_TABLE_SIZE);
+
+/* The array index calculated below must match the one in Opcode::FromCode. */
+uint32_t WabtOpcodeCodeTable[WABT_OPCODE_CODE_TABLE_SIZE] = {
+#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \
+ text, decomp) \
+ [(prefix << MAX_OPCODE_BITS) + code] = Name,
+#include "wabt/opcode.def"
+#undef WABT_OPCODE
+};
diff --git a/third_party/wasm2c/src/opcode.cc b/third_party/wasm2c/src/opcode.cc
new file mode 100644
index 0000000000..3a62fa7182
--- /dev/null
+++ b/third_party/wasm2c/src/opcode.cc
@@ -0,0 +1,429 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/opcode.h"
+
+#include "wabt/feature.h"
+
+namespace wabt {
+
+// static
+Opcode::Info Opcode::infos_[] = {
+#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \
+ text, decomp) \
+ {text, decomp, Type::rtype, {Type::type1, Type::type2, Type::type3}, \
+ mem_size, prefix, code, PrefixCode(prefix, code)},
+#include "wabt/opcode.def"
+#undef WABT_OPCODE
+
+ {"<invalid>", "", Type::Void, {Type::Void, Type::Void, Type::Void}, 0, 0, 0, 0},
+};
+
+#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \
+ text, decomp) \
+ /* static */ Opcode Opcode::Name##_Opcode(Opcode::Name);
+#include "wabt/opcode.def"
+#undef WABT_OPCODE
+
+Opcode::Info Opcode::GetInfo() const {
+ if (enum_ < Invalid) {
+ return infos_[enum_];
+ }
+
+ Info invalid_info = infos_[Opcode::Invalid];
+ DecodeInvalidOpcode(enum_, &invalid_info.prefix, &invalid_info.code);
+ invalid_info.prefix_code = PrefixCode(invalid_info.prefix, invalid_info.code);
+ return invalid_info;
+}
+
+bool Opcode::IsNaturallyAligned(Address alignment) const {
+ Address opcode_align = GetMemorySize();
+ return alignment == WABT_USE_NATURAL_ALIGNMENT || alignment == opcode_align;
+}
+
+Address Opcode::GetAlignment(Address alignment) const {
+ if (alignment == WABT_USE_NATURAL_ALIGNMENT) {
+ return GetMemorySize();
+ }
+ return alignment;
+}
+
+bool Opcode::IsEnabled(const Features& features) const {
+ switch (enum_) {
+ case Opcode::Try:
+ case Opcode::Catch:
+ case Opcode::Delegate:
+ case Opcode::Throw:
+ case Opcode::Rethrow:
+ return features.exceptions_enabled();
+
+ case Opcode::ReturnCallIndirect:
+ case Opcode::ReturnCall:
+ return features.tail_call_enabled();
+
+ case Opcode::I32TruncSatF32S:
+ case Opcode::I32TruncSatF32U:
+ case Opcode::I32TruncSatF64S:
+ case Opcode::I32TruncSatF64U:
+ case Opcode::I64TruncSatF32S:
+ case Opcode::I64TruncSatF32U:
+ case Opcode::I64TruncSatF64S:
+ case Opcode::I64TruncSatF64U:
+ return features.sat_float_to_int_enabled();
+
+ case Opcode::I32Extend8S:
+ case Opcode::I32Extend16S:
+ case Opcode::I64Extend8S:
+ case Opcode::I64Extend16S:
+ case Opcode::I64Extend32S:
+ return features.sign_extension_enabled();
+
+ case Opcode::MemoryAtomicNotify:
+ case Opcode::MemoryAtomicWait32:
+ case Opcode::MemoryAtomicWait64:
+ case Opcode::AtomicFence:
+ case Opcode::I32AtomicLoad:
+ case Opcode::I64AtomicLoad:
+ case Opcode::I32AtomicLoad8U:
+ case Opcode::I32AtomicLoad16U:
+ case Opcode::I64AtomicLoad8U:
+ case Opcode::I64AtomicLoad16U:
+ case Opcode::I64AtomicLoad32U:
+ case Opcode::I32AtomicStore:
+ case Opcode::I64AtomicStore:
+ case Opcode::I32AtomicStore8:
+ case Opcode::I32AtomicStore16:
+ case Opcode::I64AtomicStore8:
+ case Opcode::I64AtomicStore16:
+ case Opcode::I64AtomicStore32:
+ case Opcode::I32AtomicRmwAdd:
+ case Opcode::I64AtomicRmwAdd:
+ case Opcode::I32AtomicRmw8AddU:
+ case Opcode::I32AtomicRmw16AddU:
+ case Opcode::I64AtomicRmw8AddU:
+ case Opcode::I64AtomicRmw16AddU:
+ case Opcode::I64AtomicRmw32AddU:
+ case Opcode::I32AtomicRmwSub:
+ case Opcode::I64AtomicRmwSub:
+ case Opcode::I32AtomicRmw8SubU:
+ case Opcode::I32AtomicRmw16SubU:
+ case Opcode::I64AtomicRmw8SubU:
+ case Opcode::I64AtomicRmw16SubU:
+ case Opcode::I64AtomicRmw32SubU:
+ case Opcode::I32AtomicRmwAnd:
+ case Opcode::I64AtomicRmwAnd:
+ case Opcode::I32AtomicRmw8AndU:
+ case Opcode::I32AtomicRmw16AndU:
+ case Opcode::I64AtomicRmw8AndU:
+ case Opcode::I64AtomicRmw16AndU:
+ case Opcode::I64AtomicRmw32AndU:
+ case Opcode::I32AtomicRmwOr:
+ case Opcode::I64AtomicRmwOr:
+ case Opcode::I32AtomicRmw8OrU:
+ case Opcode::I32AtomicRmw16OrU:
+ case Opcode::I64AtomicRmw8OrU:
+ case Opcode::I64AtomicRmw16OrU:
+ case Opcode::I64AtomicRmw32OrU:
+ case Opcode::I32AtomicRmwXor:
+ case Opcode::I64AtomicRmwXor:
+ case Opcode::I32AtomicRmw8XorU:
+ case Opcode::I32AtomicRmw16XorU:
+ case Opcode::I64AtomicRmw8XorU:
+ case Opcode::I64AtomicRmw16XorU:
+ case Opcode::I64AtomicRmw32XorU:
+ case Opcode::I32AtomicRmwXchg:
+ case Opcode::I64AtomicRmwXchg:
+ case Opcode::I32AtomicRmw8XchgU:
+ case Opcode::I32AtomicRmw16XchgU:
+ case Opcode::I64AtomicRmw8XchgU:
+ case Opcode::I64AtomicRmw16XchgU:
+ case Opcode::I64AtomicRmw32XchgU:
+ case Opcode::I32AtomicRmwCmpxchg:
+ case Opcode::I64AtomicRmwCmpxchg:
+ case Opcode::I32AtomicRmw8CmpxchgU:
+ case Opcode::I32AtomicRmw16CmpxchgU:
+ case Opcode::I64AtomicRmw8CmpxchgU:
+ case Opcode::I64AtomicRmw16CmpxchgU:
+ case Opcode::I64AtomicRmw32CmpxchgU:
+ return features.threads_enabled();
+
+ case Opcode::V128Const:
+ case Opcode::V128Load:
+ case Opcode::V128Store:
+ case Opcode::I8X16Splat:
+ case Opcode::I16X8Splat:
+ case Opcode::I32X4Splat:
+ case Opcode::I64X2Splat:
+ case Opcode::F32X4Splat:
+ case Opcode::F64X2Splat:
+ case Opcode::I8X16ExtractLaneS:
+ case Opcode::I8X16ExtractLaneU:
+ case Opcode::I16X8ExtractLaneS:
+ case Opcode::I16X8ExtractLaneU:
+ case Opcode::I32X4ExtractLane:
+ case Opcode::I64X2ExtractLane:
+ case Opcode::F32X4ExtractLane:
+ case Opcode::F64X2ExtractLane:
+ case Opcode::I8X16ReplaceLane:
+ case Opcode::I16X8ReplaceLane:
+ case Opcode::I32X4ReplaceLane:
+ case Opcode::I64X2ReplaceLane:
+ case Opcode::F32X4ReplaceLane:
+ case Opcode::F64X2ReplaceLane:
+ case Opcode::I8X16Add:
+ case Opcode::I16X8Add:
+ case Opcode::I32X4Add:
+ case Opcode::I64X2Add:
+ case Opcode::I8X16Sub:
+ case Opcode::I16X8Sub:
+ case Opcode::I32X4Sub:
+ case Opcode::I64X2Sub:
+ case Opcode::I16X8Mul:
+ case Opcode::I32X4Mul:
+ case Opcode::I8X16Neg:
+ case Opcode::I16X8Neg:
+ case Opcode::I32X4Neg:
+ case Opcode::I64X2Neg:
+ case Opcode::I8X16AddSatS:
+ case Opcode::I8X16AddSatU:
+ case Opcode::I16X8AddSatS:
+ case Opcode::I16X8AddSatU:
+ case Opcode::I8X16SubSatS:
+ case Opcode::I8X16SubSatU:
+ case Opcode::I16X8SubSatS:
+ case Opcode::I16X8SubSatU:
+ case Opcode::I8X16Shl:
+ case Opcode::I16X8Shl:
+ case Opcode::I32X4Shl:
+ case Opcode::I64X2Shl:
+ case Opcode::I8X16ShrS:
+ case Opcode::I8X16ShrU:
+ case Opcode::I16X8ShrS:
+ case Opcode::I16X8ShrU:
+ case Opcode::I32X4ShrS:
+ case Opcode::I32X4ShrU:
+ case Opcode::I64X2ShrS:
+ case Opcode::I64X2ShrU:
+ case Opcode::V128And:
+ case Opcode::V128Or:
+ case Opcode::V128Xor:
+ case Opcode::V128Not:
+ case Opcode::V128BitSelect:
+ case Opcode::V128AnyTrue:
+ case Opcode::I8X16Bitmask:
+ case Opcode::I16X8Bitmask:
+ case Opcode::I32X4Bitmask:
+ case Opcode::I64X2Bitmask:
+ case Opcode::I8X16AllTrue:
+ case Opcode::I16X8AllTrue:
+ case Opcode::I32X4AllTrue:
+ case Opcode::I64X2AllTrue:
+ case Opcode::I8X16Eq:
+ case Opcode::I16X8Eq:
+ case Opcode::I32X4Eq:
+ case Opcode::F32X4Eq:
+ case Opcode::F64X2Eq:
+ case Opcode::I8X16Ne:
+ case Opcode::I16X8Ne:
+ case Opcode::I32X4Ne:
+ case Opcode::F32X4Ne:
+ case Opcode::F64X2Ne:
+ case Opcode::I8X16LtS:
+ case Opcode::I8X16LtU:
+ case Opcode::I16X8LtS:
+ case Opcode::I16X8LtU:
+ case Opcode::I32X4LtS:
+ case Opcode::I32X4LtU:
+ case Opcode::F32X4Lt:
+ case Opcode::F64X2Lt:
+ case Opcode::I8X16LeS:
+ case Opcode::I8X16LeU:
+ case Opcode::I16X8LeS:
+ case Opcode::I16X8LeU:
+ case Opcode::I32X4LeS:
+ case Opcode::I32X4LeU:
+ case Opcode::F32X4Le:
+ case Opcode::F64X2Le:
+ case Opcode::I8X16GtS:
+ case Opcode::I8X16GtU:
+ case Opcode::I16X8GtS:
+ case Opcode::I16X8GtU:
+ case Opcode::I32X4GtS:
+ case Opcode::I32X4GtU:
+ case Opcode::F32X4Gt:
+ case Opcode::F64X2Gt:
+ case Opcode::I8X16GeS:
+ case Opcode::I8X16GeU:
+ case Opcode::I16X8GeS:
+ case Opcode::I16X8GeU:
+ case Opcode::I32X4GeS:
+ case Opcode::I32X4GeU:
+ case Opcode::F32X4Ge:
+ case Opcode::F64X2Ge:
+ case Opcode::F32X4Neg:
+ case Opcode::F64X2Neg:
+ case Opcode::F32X4Abs:
+ case Opcode::F64X2Abs:
+ case Opcode::F32X4Min:
+ case Opcode::F32X4PMin:
+ case Opcode::F64X2Min:
+ case Opcode::F64X2PMin:
+ case Opcode::F32X4Max:
+ case Opcode::F32X4PMax:
+ case Opcode::F64X2Max:
+ case Opcode::F64X2PMax:
+ case Opcode::F32X4Add:
+ case Opcode::F64X2Add:
+ case Opcode::F32X4Sub:
+ case Opcode::F64X2Sub:
+ case Opcode::F32X4Div:
+ case Opcode::F64X2Div:
+ case Opcode::F32X4Mul:
+ case Opcode::F64X2Mul:
+ case Opcode::F32X4Sqrt:
+ case Opcode::F64X2Sqrt:
+ case Opcode::F32X4ConvertI32X4S:
+ case Opcode::F32X4ConvertI32X4U:
+ case Opcode::I32X4TruncSatF32X4S:
+ case Opcode::I32X4TruncSatF32X4U:
+ case Opcode::I8X16Swizzle:
+ case Opcode::I8X16Shuffle:
+ case Opcode::V128Load8Splat:
+ case Opcode::V128Load16Splat:
+ case Opcode::V128Load32Splat:
+ case Opcode::V128Load64Splat:
+ case Opcode::V128Load8Lane:
+ case Opcode::V128Load16Lane:
+ case Opcode::V128Load32Lane:
+ case Opcode::V128Load64Lane:
+ case Opcode::V128Store8Lane:
+ case Opcode::V128Store16Lane:
+ case Opcode::V128Store32Lane:
+ case Opcode::V128Store64Lane:
+ case Opcode::I8X16Abs:
+ case Opcode::I16X8Abs:
+ case Opcode::I32X4Abs:
+ return features.simd_enabled();
+
+ case Opcode::I8X16RelaxedSwizzle:
+ case Opcode::I32X4RelaxedTruncF32X4S:
+ case Opcode::I32X4RelaxedTruncF32X4U:
+ case Opcode::I32X4RelaxedTruncF64X2SZero:
+ case Opcode::I32X4RelaxedTruncF64X2UZero:
+ case Opcode::F32X4RelaxedMadd:
+ case Opcode::F32X4RelaxedNmadd:
+ case Opcode::F64X2RelaxedMadd:
+ case Opcode::F64X2RelaxedNmadd:
+ case Opcode::I8X16RelaxedLaneSelect:
+ case Opcode::I16X8RelaxedLaneSelect:
+ case Opcode::I32X4RelaxedLaneSelect:
+ case Opcode::I64X2RelaxedLaneSelect:
+ case Opcode::F32X4RelaxedMin:
+ case Opcode::F32X4RelaxedMax:
+ case Opcode::F64X2RelaxedMin:
+ case Opcode::F64X2RelaxedMax:
+ case Opcode::I16X8RelaxedQ15mulrS:
+ case Opcode::I16X8DotI8X16I7X16S:
+ case Opcode::I32X4DotI8X16I7X16AddS:
+ return features.relaxed_simd_enabled();
+
+ case Opcode::MemoryInit:
+ case Opcode::DataDrop:
+ case Opcode::MemoryCopy:
+ case Opcode::MemoryFill:
+ case Opcode::TableInit:
+ case Opcode::ElemDrop:
+ case Opcode::TableCopy:
+ return features.bulk_memory_enabled();
+
+ case Opcode::TableGet:
+ case Opcode::TableSet:
+ case Opcode::TableGrow:
+ case Opcode::TableSize:
+ case Opcode::RefNull:
+ case Opcode::RefIsNull:
+ return features.reference_types_enabled();
+
+ case Opcode::CallRef:
+ return features.function_references_enabled();
+
+ // Interpreter opcodes are never "enabled".
+ case Opcode::InterpAlloca:
+ case Opcode::InterpBrUnless:
+ case Opcode::InterpCallImport:
+ case Opcode::InterpData:
+ case Opcode::InterpDropKeep:
+ return false;
+
+ default:
+ return true;
+ }
+}
+
+uint32_t Opcode::GetSimdLaneCount() const {
+ switch (enum_) {
+ case Opcode::I8X16ExtractLaneS:
+ case Opcode::I8X16ExtractLaneU:
+ case Opcode::I8X16ReplaceLane:
+ case Opcode::V128Load8Lane:
+ case Opcode::V128Store8Lane:
+ return 16;
+ break;
+ case Opcode::I16X8ExtractLaneS:
+ case Opcode::I16X8ExtractLaneU:
+ case Opcode::I16X8ReplaceLane:
+ case Opcode::V128Load16Lane:
+ case Opcode::V128Store16Lane:
+ return 8;
+ break;
+ case Opcode::F32X4ExtractLane:
+ case Opcode::F32X4ReplaceLane:
+ case Opcode::I32X4ExtractLane:
+ case Opcode::I32X4ReplaceLane:
+ case Opcode::V128Load32Lane:
+ case Opcode::V128Store32Lane:
+ return 4;
+ break;
+ case Opcode::F64X2ExtractLane:
+ case Opcode::F64X2ReplaceLane:
+ case Opcode::I64X2ExtractLane:
+ case Opcode::I64X2ReplaceLane:
+ case Opcode::V128Load64Lane:
+ case Opcode::V128Store64Lane:
+ return 2;
+ break;
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+// Get the byte sequence for this opcode, including prefix.
+std::vector<uint8_t> Opcode::GetBytes() const {
+ std::vector<uint8_t> result;
+ if (HasPrefix()) {
+ result.push_back(GetPrefix());
+ uint8_t buffer[5];
+ Offset length =
+ WriteU32Leb128Raw(buffer, buffer + sizeof(buffer), GetCode());
+ assert(length != 0);
+ result.insert(result.end(), buffer, buffer + length);
+ } else {
+ result.push_back(GetCode());
+ }
+ return result;
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/option-parser.cc b/third_party/wasm2c/src/option-parser.cc
new file mode 100644
index 0000000000..88fe75780b
--- /dev/null
+++ b/third_party/wasm2c/src/option-parser.cc
@@ -0,0 +1,351 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/option-parser.h"
+
+#include <cstdarg>
+#include <cstdio>
+#include <cstring>
+
+#include "wabt/config.h"
+
+namespace wabt {
+
+OptionParser::Option::Option(char short_name,
+ const std::string& long_name,
+ const std::string& metavar,
+ HasArgument has_argument,
+ const std::string& help,
+ const Callback& callback)
+ : short_name(short_name),
+ long_name(long_name),
+ metavar(metavar),
+ has_argument(has_argument == HasArgument::Yes),
+ help(help),
+ callback(callback) {}
+
+OptionParser::Argument::Argument(const std::string& name,
+ ArgumentCount count,
+ const Callback& callback)
+ : name(name), count(count), callback(callback) {}
+
+OptionParser::OptionParser(const char* program_name, const char* description)
+ : program_name_(program_name),
+ description_(description),
+ on_error_([this](const std::string& message) { DefaultError(message); }) {
+ // Add common options
+ AddOption("help", "Print this help message", [this]() {
+ PrintHelp();
+ exit(0);
+ });
+ AddOption("version", "Print version information", []() {
+ printf("%s\n", WABT_VERSION_STRING);
+ exit(0);
+ });
+}
+
+void OptionParser::AddOption(const Option& option) {
+ options_.emplace_back(option);
+}
+
+void OptionParser::AddArgument(const std::string& name,
+ ArgumentCount count,
+ const Callback& callback) {
+ arguments_.emplace_back(name, count, callback);
+}
+
+void OptionParser::AddOption(char short_name,
+ const char* long_name,
+ const char* help,
+ const NullCallback& callback) {
+ Option option(short_name, long_name, std::string(), HasArgument::No, help,
+ [callback](const char*) { callback(); });
+ AddOption(option);
+}
+
+void OptionParser::AddOption(const char* long_name,
+ const char* help,
+ const NullCallback& callback) {
+ Option option('\0', long_name, std::string(), HasArgument::No, help,
+ [callback](const char*) { callback(); });
+ AddOption(option);
+}
+
+void OptionParser::AddOption(char short_name,
+ const char* long_name,
+ const char* metavar,
+ const char* help,
+ const Callback& callback) {
+ Option option(short_name, long_name, metavar, HasArgument::Yes, help,
+ callback);
+ AddOption(option);
+}
+
+void OptionParser::SetErrorCallback(const Callback& callback) {
+ on_error_ = callback;
+}
+
+// static
+int OptionParser::Match(const char* s,
+ const std::string& full,
+ bool has_argument) {
+ int i;
+ for (i = 0;; i++) {
+ if (full[i] == '\0') {
+ // Perfect match. Return +1, so it will be preferred over a longer option
+ // with the same prefix.
+ if (s[i] == '\0') {
+ return i + 1;
+ }
+
+ // We want to fail if s is longer than full, e.g. --foobar vs. --foo.
+ // However, if s ends with an '=', it's OK.
+ if (!(has_argument && s[i] == '=')) {
+ return -1;
+ }
+ break;
+ }
+ if (s[i] == '\0') {
+ break;
+ }
+ if (s[i] != full[i]) {
+ return -1;
+ }
+ }
+ return i;
+}
+
+void OptionParser::Errorf(const char* format, ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ std::string msg(program_name_);
+ msg += ": ";
+ msg += buffer;
+ msg += "\nTry '--help' for more information.";
+ on_error_(msg.c_str());
+}
+
+void OptionParser::DefaultError(const std::string& message) {
+ WABT_FATAL("%s\n", message.c_str());
+}
+
+void OptionParser::HandleArgument(size_t* arg_index, const char* arg_value) {
+ if (*arg_index >= arguments_.size()) {
+ Errorf("unexpected argument '%s'", arg_value);
+ return;
+ }
+ Argument& argument = arguments_[*arg_index];
+ argument.callback(arg_value);
+ argument.handled_count++;
+
+ if (argument.count == ArgumentCount::One) {
+ (*arg_index)++;
+ }
+}
+
+void OptionParser::Parse(int argc, char* argv[]) {
+ size_t arg_index = 0;
+ bool processing_options = true;
+
+ for (int i = 1; i < argc; ++i) {
+ const char* arg = argv[i];
+ if (!processing_options || arg[0] != '-') {
+ // Non-option argument.
+ HandleArgument(&arg_index, arg);
+ continue;
+ }
+
+ if (arg[1] == '-') {
+ if (arg[2] == '\0') {
+ // -- on its own means stop processing args, everything should
+ // be treated as positional.
+ processing_options = false;
+ continue;
+ }
+ // Long option.
+ int best_index = -1;
+ int best_length = 0;
+ int best_count = 0;
+ for (size_t j = 0; j < options_.size(); ++j) {
+ const Option& option = options_[j];
+ if (!option.long_name.empty()) {
+ int match_length =
+ Match(&arg[2], option.long_name, option.has_argument);
+ if (match_length > best_length) {
+ best_index = j;
+ best_length = match_length;
+ best_count = 1;
+ } else if (match_length == best_length && best_length > 0) {
+ best_count++;
+ }
+ }
+ }
+
+ if (best_count > 1) {
+ Errorf("ambiguous option '%s'", arg);
+ continue;
+ } else if (best_count == 0) {
+ Errorf("unknown option '%s'", arg);
+ continue;
+ }
+
+ const Option& best_option = options_[best_index];
+ const char* option_argument = nullptr;
+ if (best_option.has_argument) {
+ if (arg[best_length + 1] != 0 && // This byte is 0 on a full match.
+ arg[best_length + 2] == '=') { // +2 to skip "--".
+ option_argument = &arg[best_length + 3];
+ } else {
+ if (i + 1 == argc || argv[i + 1][0] == '-') {
+ Errorf("option '--%s' requires argument",
+ best_option.long_name.c_str());
+ continue;
+ }
+ ++i;
+ option_argument = argv[i];
+ }
+ }
+ best_option.callback(option_argument);
+ } else {
+ // Short option.
+ if (arg[1] == '\0') {
+ // Just "-".
+ HandleArgument(&arg_index, arg);
+ continue;
+ }
+
+ // Allow short names to be combined, e.g. "-d -v" => "-dv".
+ for (int k = 1; arg[k]; ++k) {
+ bool matched = false;
+ for (const Option& option : options_) {
+ if (option.short_name && arg[k] == option.short_name) {
+ const char* option_argument = nullptr;
+ if (option.has_argument) {
+ // A short option with a required argument cannot be followed
+ // by other short options_.
+ if (arg[k + 1] != '\0') {
+ Errorf("option '-%c' requires argument", option.short_name);
+ break;
+ }
+
+ if (i + 1 == argc || argv[i + 1][0] == '-') {
+ Errorf("option '-%c' requires argument", option.short_name);
+ break;
+ }
+ ++i;
+ option_argument = argv[i];
+ }
+ option.callback(option_argument);
+ matched = true;
+ break;
+ }
+ }
+
+ if (!matched) {
+ Errorf("unknown option '-%c'", arg[k]);
+ continue;
+ }
+ }
+ }
+ }
+
+ // For now, all arguments must be provided. Check that the last Argument was
+ // handled at least once.
+ if (!arguments_.empty() && arguments_.back().handled_count == 0) {
+ for (size_t i = arg_index; i < arguments_.size(); ++i) {
+ if (arguments_[i].count != ArgumentCount::ZeroOrMore) {
+ Errorf("expected %s argument.", arguments_[i].name.c_str());
+ }
+ }
+ }
+}
+
+void OptionParser::PrintHelp() {
+ printf("usage: %s [options]", program_name_.c_str());
+
+ for (size_t i = 0; i < arguments_.size(); ++i) {
+ Argument& argument = arguments_[i];
+ switch (argument.count) {
+ case ArgumentCount::One:
+ printf(" %s", argument.name.c_str());
+ break;
+
+ case ArgumentCount::OneOrMore:
+ printf(" %s+", argument.name.c_str());
+ break;
+
+ case ArgumentCount::ZeroOrMore:
+ printf(" [%s]...", argument.name.c_str());
+ break;
+ }
+ }
+
+ printf("\n\n");
+ printf("%s\n", description_.c_str());
+ printf("options:\n");
+
+ const size_t kExtraSpace = 8;
+ size_t longest_name_length = 0;
+ for (const Option& option : options_) {
+ size_t length;
+ if (!option.long_name.empty()) {
+ length = option.long_name.size();
+ if (!option.metavar.empty()) {
+ // +1 for '='.
+ length += option.metavar.size() + 1;
+ }
+ } else {
+ continue;
+ }
+
+ if (length > longest_name_length) {
+ longest_name_length = length;
+ }
+ }
+
+ for (const Option& option : options_) {
+ if (!option.short_name && option.long_name.empty()) {
+ continue;
+ }
+
+ std::string line;
+ if (option.short_name) {
+ line += std::string(" -") + option.short_name + ", ";
+ } else {
+ line += " ";
+ }
+
+ std::string flag;
+ if (!option.long_name.empty()) {
+ flag = "--";
+ if (!option.metavar.empty()) {
+ flag += option.long_name + '=' + option.metavar;
+ } else {
+ flag += option.long_name;
+ }
+ }
+
+ // +2 for "--" of the long flag name.
+ size_t remaining = longest_name_length + kExtraSpace + 2 - flag.size();
+ line += flag + std::string(remaining, ' ');
+
+ if (!option.help.empty()) {
+ line += option.help;
+ }
+ printf("%s\n", line.c_str());
+ }
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/prebuilt/.clang-format b/third_party/wasm2c/src/prebuilt/.clang-format
new file mode 100644
index 0000000000..9d159247d5
--- /dev/null
+++ b/third_party/wasm2c/src/prebuilt/.clang-format
@@ -0,0 +1,2 @@
+DisableFormat: true
+SortIncludes: false
diff --git a/third_party/wasm2c/src/prebuilt/lexer-keywords.cc b/third_party/wasm2c/src/prebuilt/lexer-keywords.cc
new file mode 100644
index 0000000000..4a7da0e06d
--- /dev/null
+++ b/third_party/wasm2c/src/prebuilt/lexer-keywords.cc
@@ -0,0 +1,1796 @@
+/* C++ code produced by gperf version 3.1 */
+/* Command-line: gperf -m 50 -L C++ -N InWordSet -E -t -c --output-file=src/prebuilt/lexer-keywords.cc src/lexer-keywords.txt */
+/* Computed positions: -k'1,3,5-8,10,12,15,17-19,23,27,$' */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646. */
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>."
+#endif
+
+#line 1 "src/lexer-keywords.txt"
+struct TokenInfo {
+ TokenInfo(const char* name) : name(name) {}
+ TokenInfo(const char* name, TokenType token_type)
+ : name(name), token_type(token_type) {}
+ TokenInfo(const char* name, Type value_type)
+ : name(name), token_type(TokenType::ValueType), value_type(value_type) {}
+ TokenInfo(const char* name, Type value_type, TokenType token_type)
+ : name(name), token_type(token_type), value_type(value_type) {}
+ TokenInfo(const char* name, TokenType token_type, Opcode opcode)
+ : name(name), token_type(token_type), opcode(opcode) {}
+
+ const char* name;
+ TokenType token_type;
+ union {
+ Type value_type;
+ Opcode opcode;
+ };
+};
+/* maximum key range = 2423, duplicates = 0 */
+
+class Perfect_Hash
+{
+private:
+ static inline unsigned int hash (const char *str, size_t len);
+public:
+ static struct TokenInfo *InWordSet (const char *str, size_t len);
+};
+
+inline unsigned int
+Perfect_Hash::hash (const char *str, size_t len)
+{
+ static unsigned short asso_values[] =
+ {
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 7, 2453, 2453, 532,
+ 325, 8, 13, 7, 327, 314, 111, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 220, 10, 35, 678, 60,
+ 49, 11, 538, 7, 399, 128, 15, 34, 9, 56,
+ 13, 64, 766, 737, 14, 9, 19, 7, 411, 435,
+ 122, 393, 105, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453,
+ 2453, 2453, 2453, 2453, 2453, 2453, 2453
+ };
+ unsigned int hval = len;
+
+ switch (hval)
+ {
+ default:
+ hval += asso_values[static_cast<unsigned char>(str[26])];
+ [[fallthrough]];
+ case 26:
+ case 25:
+ case 24:
+ case 23:
+ hval += asso_values[static_cast<unsigned char>(str[22])];
+ [[fallthrough]];
+ case 22:
+ case 21:
+ case 20:
+ case 19:
+ hval += asso_values[static_cast<unsigned char>(str[18])];
+ [[fallthrough]];
+ case 18:
+ hval += asso_values[static_cast<unsigned char>(str[17])];
+ [[fallthrough]];
+ case 17:
+ hval += asso_values[static_cast<unsigned char>(str[16])];
+ [[fallthrough]];
+ case 16:
+ case 15:
+ hval += asso_values[static_cast<unsigned char>(str[14])];
+ [[fallthrough]];
+ case 14:
+ case 13:
+ case 12:
+ hval += asso_values[static_cast<unsigned char>(str[11])];
+ [[fallthrough]];
+ case 11:
+ case 10:
+ hval += asso_values[static_cast<unsigned char>(str[9])];
+ [[fallthrough]];
+ case 9:
+ case 8:
+ hval += asso_values[static_cast<unsigned char>(str[7])];
+ [[fallthrough]];
+ case 7:
+ hval += asso_values[static_cast<unsigned char>(str[6])];
+ [[fallthrough]];
+ case 6:
+ hval += asso_values[static_cast<unsigned char>(str[5])];
+ [[fallthrough]];
+ case 5:
+ hval += asso_values[static_cast<unsigned char>(str[4])];
+ [[fallthrough]];
+ case 4:
+ case 3:
+ hval += asso_values[static_cast<unsigned char>(str[2]+1)];
+ [[fallthrough]];
+ case 2:
+ case 1:
+ hval += asso_values[static_cast<unsigned char>(str[0]+1)];
+ break;
+ }
+ return hval + asso_values[static_cast<unsigned char>(str[len - 1])];
+}
+
+struct TokenInfo *
+Perfect_Hash::InWordSet (const char *str, size_t len)
+{
+ enum
+ {
+ TOTAL_KEYWORDS = 590,
+ MIN_WORD_LENGTH = 2,
+ MAX_WORD_LENGTH = 32,
+ MIN_HASH_VALUE = 30,
+ MAX_HASH_VALUE = 2452
+ };
+
+ static struct TokenInfo wordlist[] =
+ {
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""},
+#line 145 "src/lexer-keywords.txt"
+ {"f64", Type::F64},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 456 "src/lexer-keywords.txt"
+ {"i64", Type::I64},
+ {""}, {""}, {""},
+#line 547 "src/lexer-keywords.txt"
+ {"mut", TokenType::Mut},
+ {""}, {""}, {""},
+#line 115 "src/lexer-keywords.txt"
+ {"f32x4", TokenType::F32X4},
+ {""}, {""},
+#line 128 "src/lexer-keywords.txt"
+ {"f64.ge", TokenType::Compare, Opcode::F64Ge},
+#line 67 "src/lexer-keywords.txt"
+ {"f32.ge", TokenType::Compare, Opcode::F32Ge},
+#line 130 "src/lexer-keywords.txt"
+ {"f64.le", TokenType::Compare, Opcode::F64Le},
+#line 69 "src/lexer-keywords.txt"
+ {"f32.le", TokenType::Compare, Opcode::F32Le},
+ {""},
+#line 356 "src/lexer-keywords.txt"
+ {"i32x4", TokenType::I32X4},
+#line 138 "src/lexer-keywords.txt"
+ {"f64.ne", TokenType::Compare, Opcode::F64Ne},
+#line 77 "src/lexer-keywords.txt"
+ {"f32.ne", TokenType::Compare, Opcode::F32Ne},
+#line 40 "src/lexer-keywords.txt"
+ {"data", TokenType::Data},
+ {""},
+#line 137 "src/lexer-keywords.txt"
+ {"f64.neg", TokenType::Unary, Opcode::F64Neg},
+#line 76 "src/lexer-keywords.txt"
+ {"f32.neg", TokenType::Unary, Opcode::F32Neg},
+#line 546 "src/lexer-keywords.txt"
+ {"module", TokenType::Module},
+#line 565 "src/lexer-keywords.txt"
+ {"return", TokenType::Return, Opcode::Return},
+#line 432 "src/lexer-keywords.txt"
+ {"i64.ne", TokenType::Compare, Opcode::I64Ne},
+#line 289 "src/lexer-keywords.txt"
+ {"i32.ne", TokenType::Compare, Opcode::I32Ne},
+#line 129 "src/lexer-keywords.txt"
+ {"f64.gt", TokenType::Compare, Opcode::F64Gt},
+#line 68 "src/lexer-keywords.txt"
+ {"f32.gt", TokenType::Compare, Opcode::F32Gt},
+#line 132 "src/lexer-keywords.txt"
+ {"f64.lt", TokenType::Compare, Opcode::F64Lt},
+#line 71 "src/lexer-keywords.txt"
+ {"f32.lt", TokenType::Compare, Opcode::F32Lt},
+ {""}, {""},
+#line 559 "src/lexer-keywords.txt"
+ {"ref.null", TokenType::RefNull, Opcode::RefNull},
+#line 93 "src/lexer-keywords.txt"
+ {"f32x4.ge", TokenType::Compare, Opcode::F32X4Ge},
+ {""},
+#line 95 "src/lexer-keywords.txt"
+ {"f32x4.le", TokenType::Compare, Opcode::F32X4Le},
+#line 101 "src/lexer-keywords.txt"
+ {"f32x4.neg", TokenType::Unary, Opcode::F32X4Neg},
+#line 33 "src/lexer-keywords.txt"
+ {"br", TokenType::Br, Opcode::Br},
+#line 43 "src/lexer-keywords.txt"
+ {"do", TokenType::Do},
+#line 102 "src/lexer-keywords.txt"
+ {"f32x4.ne", TokenType::Compare, Opcode::F32X4Ne},
+ {""}, {""},
+#line 561 "src/lexer-keywords.txt"
+ {"result", TokenType::Result},
+ {""},
+#line 341 "src/lexer-keywords.txt"
+ {"i32x4.neg", TokenType::Unary, Opcode::I32X4Neg},
+ {""},
+#line 322 "src/lexer-keywords.txt"
+ {"i32x4.ge_u", TokenType::Compare, Opcode::I32X4GeU},
+#line 342 "src/lexer-keywords.txt"
+ {"i32x4.ne", TokenType::Compare, Opcode::I32X4Ne},
+#line 326 "src/lexer-keywords.txt"
+ {"i32x4.le_u", TokenType::Compare, Opcode::I32X4LeU},
+#line 94 "src/lexer-keywords.txt"
+ {"f32x4.gt", TokenType::Compare, Opcode::F32X4Gt},
+#line 321 "src/lexer-keywords.txt"
+ {"i32x4.ge_s", TokenType::Compare, Opcode::I32X4GeS},
+#line 96 "src/lexer-keywords.txt"
+ {"f32x4.lt", TokenType::Compare, Opcode::F32X4Lt},
+#line 325 "src/lexer-keywords.txt"
+ {"i32x4.le_s", TokenType::Compare, Opcode::I32X4LeS},
+ {""},
+#line 324 "src/lexer-keywords.txt"
+ {"i32x4.gt_u", TokenType::Compare, Opcode::I32X4GtU},
+#line 577 "src/lexer-keywords.txt"
+ {"table", TokenType::Table},
+#line 334 "src/lexer-keywords.txt"
+ {"i32x4.lt_u", TokenType::Compare, Opcode::I32X4LtU},
+ {""},
+#line 323 "src/lexer-keywords.txt"
+ {"i32x4.gt_s", TokenType::Compare, Opcode::I32X4GtS},
+ {""},
+#line 333 "src/lexer-keywords.txt"
+ {"i32x4.lt_s", TokenType::Compare, Opcode::I32X4LtS},
+ {""}, {""},
+#line 135 "src/lexer-keywords.txt"
+ {"f64.mul", TokenType::Binary, Opcode::F64Mul},
+#line 74 "src/lexer-keywords.txt"
+ {"f32.mul", TokenType::Binary, Opcode::F32Mul},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 431 "src/lexer-keywords.txt"
+ {"i64.mul", TokenType::Binary, Opcode::I64Mul},
+#line 288 "src/lexer-keywords.txt"
+ {"i32.mul", TokenType::Binary, Opcode::I32Mul},
+ {""}, {""},
+#line 100 "src/lexer-keywords.txt"
+ {"f32x4.nearest", TokenType::Unary, Opcode::F32X4Nearest},
+ {""},
+#line 99 "src/lexer-keywords.txt"
+ {"f32x4.mul", TokenType::Binary, Opcode::F32X4Mul},
+ {""},
+#line 36 "src/lexer-keywords.txt"
+ {"call", TokenType::Call, Opcode::Call},
+ {""},
+#line 433 "src/lexer-keywords.txt"
+ {"i64.or", TokenType::Binary, Opcode::I64Or},
+#line 290 "src/lexer-keywords.txt"
+ {"i32.or", TokenType::Binary, Opcode::I32Or},
+ {""}, {""},
+#line 340 "src/lexer-keywords.txt"
+ {"i32x4.mul", TokenType::Binary, Opcode::I32X4Mul},
+ {""},
+#line 136 "src/lexer-keywords.txt"
+ {"f64.nearest", TokenType::Unary, Opcode::F64Nearest},
+#line 75 "src/lexer-keywords.txt"
+ {"f32.nearest", TokenType::Unary, Opcode::F32Nearest},
+#line 535 "src/lexer-keywords.txt"
+ {"local", TokenType::Local},
+ {""}, {""},
+#line 572 "src/lexer-keywords.txt"
+ {"table.get", TokenType::TableGet, Opcode::TableGet},
+#line 569 "src/lexer-keywords.txt"
+ {"struct", Type::Struct, TokenType::Struct},
+#line 575 "src/lexer-keywords.txt"
+ {"table.set", TokenType::TableSet, Opcode::TableSet},
+#line 86 "src/lexer-keywords.txt"
+ {"f32x4.ceil", TokenType::Unary, Opcode::F32X4Ceil},
+#line 180 "src/lexer-keywords.txt"
+ {"func", Type::FuncRef, TokenType::Func},
+#line 144 "src/lexer-keywords.txt"
+ {"f64.trunc", TokenType::Unary, Opcode::F64Trunc},
+#line 82 "src/lexer-keywords.txt"
+ {"f32.trunc", TokenType::Unary, Opcode::F32Trunc},
+#line 41 "src/lexer-keywords.txt"
+ {"declare", TokenType::Declare},
+ {""},
+#line 142 "src/lexer-keywords.txt"
+ {"f64.store", TokenType::Store, Opcode::F64Store},
+#line 80 "src/lexer-keywords.txt"
+ {"f32.store", TokenType::Store, Opcode::F32Store},
+ {""}, {""}, {""},
+#line 438 "src/lexer-keywords.txt"
+ {"i64.rotl", TokenType::Binary, Opcode::I64Rotl},
+#line 295 "src/lexer-keywords.txt"
+ {"i32.rotl", TokenType::Binary, Opcode::I32Rotl},
+ {""},
+#line 446 "src/lexer-keywords.txt"
+ {"i64.store", TokenType::Store, Opcode::I64Store},
+#line 302 "src/lexer-keywords.txt"
+ {"i32.store", TokenType::Store, Opcode::I32Store},
+ {""}, {""},
+#line 113 "src/lexer-keywords.txt"
+ {"f32x4.trunc", TokenType::Unary, Opcode::F32X4Trunc},
+ {""}, {""},
+#line 439 "src/lexer-keywords.txt"
+ {"i64.rotr", TokenType::Binary, Opcode::I64Rotr},
+#line 296 "src/lexer-keywords.txt"
+ {"i32.rotr", TokenType::Binary, Opcode::I32Rotr},
+ {""},
+#line 42 "src/lexer-keywords.txt"
+ {"delegate", TokenType::Delegate},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 532 "src/lexer-keywords.txt"
+ {"local.get", TokenType::LocalGet, Opcode::LocalGet},
+ {""},
+#line 533 "src/lexer-keywords.txt"
+ {"local.set", TokenType::LocalSet, Opcode::LocalSet},
+ {""},
+#line 534 "src/lexer-keywords.txt"
+ {"local.tee", TokenType::LocalTee, Opcode::LocalTee},
+ {""}, {""}, {""},
+#line 367 "src/lexer-keywords.txt"
+ {"i64.and", TokenType::Binary, Opcode::I64And},
+#line 238 "src/lexer-keywords.txt"
+ {"i32.and", TokenType::Binary, Opcode::I32And},
+#line 85 "src/lexer-keywords.txt"
+ {"f32x4.add", TokenType::Binary, Opcode::F32X4Add},
+ {""},
+#line 566 "src/lexer-keywords.txt"
+ {"select", TokenType::Select, Opcode::Select},
+ {""}, {""}, {""}, {""}, {""},
+#line 315 "src/lexer-keywords.txt"
+ {"i32x4.add", TokenType::Binary, Opcode::I32X4Add},
+ {""}, {""},
+#line 119 "src/lexer-keywords.txt"
+ {"f64.const", TokenType::Const, Opcode::F64Const},
+#line 57 "src/lexer-keywords.txt"
+ {"f32.const", TokenType::Const, Opcode::F32Const},
+ {""},
+#line 109 "src/lexer-keywords.txt"
+ {"f32x4.replace_lane", TokenType::SimdLaneOp, Opcode::F32X4ReplaceLane},
+ {""}, {""}, {""}, {""},
+#line 405 "src/lexer-keywords.txt"
+ {"i64.const", TokenType::Const, Opcode::I64Const},
+#line 267 "src/lexer-keywords.txt"
+ {"i32.const", TokenType::Const, Opcode::I32Const},
+ {""},
+#line 344 "src/lexer-keywords.txt"
+ {"i32x4.replace_lane", TokenType::SimdLaneOp, Opcode::I32X4ReplaceLane},
+ {""}, {""}, {""},
+#line 117 "src/lexer-keywords.txt"
+ {"f64.add", TokenType::Binary, Opcode::F64Add},
+#line 55 "src/lexer-keywords.txt"
+ {"f32.add", TokenType::Binary, Opcode::F32Add},
+#line 107 "src/lexer-keywords.txt"
+ {"f32x4.relaxed_min", TokenType::Binary, Opcode::F32X4RelaxedMin},
+ {""}, {""}, {""}, {""}, {""},
+#line 366 "src/lexer-keywords.txt"
+ {"i64.add", TokenType::Binary, Opcode::I64Add},
+#line 237 "src/lexer-keywords.txt"
+ {"i32.add", TokenType::Binary, Opcode::I32Add},
+ {""}, {""},
+#line 556 "src/lexer-keywords.txt"
+ {"ref.extern", TokenType::RefExtern},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""},
+#line 131 "src/lexer-keywords.txt"
+ {"f64.load", TokenType::Load, Opcode::F64Load},
+#line 70 "src/lexer-keywords.txt"
+ {"f32.load", TokenType::Load, Opcode::F32Load},
+ {""},
+#line 134 "src/lexer-keywords.txt"
+ {"f64.min", TokenType::Binary, Opcode::F64Min},
+#line 73 "src/lexer-keywords.txt"
+ {"f32.min", TokenType::Binary, Opcode::F32Min},
+ {""}, {""}, {""},
+#line 428 "src/lexer-keywords.txt"
+ {"i64.load", TokenType::Load, Opcode::I64Load},
+#line 285 "src/lexer-keywords.txt"
+ {"i32.load", TokenType::Load, Opcode::I32Load},
+ {""},
+#line 118 "src/lexer-keywords.txt"
+ {"f64.ceil", TokenType::Unary, Opcode::F64Ceil},
+#line 56 "src/lexer-keywords.txt"
+ {"f32.ceil", TokenType::Unary, Opcode::F32Ceil},
+#line 98 "src/lexer-keywords.txt"
+ {"f32x4.min", TokenType::Binary, Opcode::F32X4Min},
+ {""},
+#line 488 "src/lexer-keywords.txt"
+ {"i64.xor", TokenType::Binary, Opcode::I64Xor},
+#line 365 "src/lexer-keywords.txt"
+ {"i32.xor", TokenType::Binary, Opcode::I32Xor},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 576 "src/lexer-keywords.txt"
+ {"table.size", TokenType::TableSize, Opcode::TableSize},
+ {""}, {""},
+#line 97 "src/lexer-keywords.txt"
+ {"f32x4.max", TokenType::Binary, Opcode::F32X4Max},
+ {""},
+#line 343 "src/lexer-keywords.txt"
+ {"i32x4.relaxed_laneselect", TokenType::Ternary, Opcode::I32X4RelaxedLaneSelect},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""},
+#line 574 "src/lexer-keywords.txt"
+ {"table.init", TokenType::TableInit, Opcode::TableInit},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 417 "src/lexer-keywords.txt"
+ {"i64.ge_u", TokenType::Compare, Opcode::I64GeU},
+#line 276 "src/lexer-keywords.txt"
+ {"i32.ge_u", TokenType::Compare, Opcode::I32GeU},
+#line 421 "src/lexer-keywords.txt"
+ {"i64.le_u", TokenType::Compare, Opcode::I64LeU},
+#line 280 "src/lexer-keywords.txt"
+ {"i32.le_u", TokenType::Compare, Opcode::I32LeU},
+#line 416 "src/lexer-keywords.txt"
+ {"i64.ge_s", TokenType::Compare, Opcode::I64GeS},
+#line 275 "src/lexer-keywords.txt"
+ {"i32.ge_s", TokenType::Compare, Opcode::I32GeS},
+#line 420 "src/lexer-keywords.txt"
+ {"i64.le_s", TokenType::Compare, Opcode::I64LeS},
+#line 279 "src/lexer-keywords.txt"
+ {"i32.le_s", TokenType::Compare, Opcode::I32LeS},
+#line 419 "src/lexer-keywords.txt"
+ {"i64.gt_u", TokenType::Compare, Opcode::I64GtU},
+#line 278 "src/lexer-keywords.txt"
+ {"i32.gt_u", TokenType::Compare, Opcode::I32GtU},
+#line 430 "src/lexer-keywords.txt"
+ {"i64.lt_u", TokenType::Compare, Opcode::I64LtU},
+#line 287 "src/lexer-keywords.txt"
+ {"i32.lt_u", TokenType::Compare, Opcode::I32LtU},
+#line 418 "src/lexer-keywords.txt"
+ {"i64.gt_s", TokenType::Compare, Opcode::I64GtS},
+#line 277 "src/lexer-keywords.txt"
+ {"i32.gt_s", TokenType::Compare, Opcode::I32GtS},
+#line 429 "src/lexer-keywords.txt"
+ {"i64.lt_s", TokenType::Compare, Opcode::I64LtS},
+#line 286 "src/lexer-keywords.txt"
+ {"i32.lt_s", TokenType::Compare, Opcode::I32LtS},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 91 "src/lexer-keywords.txt"
+ {"f32x4.extract_lane", TokenType::SimdLaneOp, Opcode::F32X4ExtractLane},
+ {""},
+#line 403 "src/lexer-keywords.txt"
+ {"i64.atomic.store", TokenType::AtomicStore, Opcode::I64AtomicStore},
+#line 265 "src/lexer-keywords.txt"
+ {"i32.atomic.store", TokenType::AtomicStore, Opcode::I32AtomicStore},
+#line 404 "src/lexer-keywords.txt"
+ {"i64.clz", TokenType::Unary, Opcode::I64Clz},
+#line 266 "src/lexer-keywords.txt"
+ {"i32.clz", TokenType::Unary, Opcode::I32Clz},
+ {""}, {""},
+#line 320 "src/lexer-keywords.txt"
+ {"i32x4.extract_lane", TokenType::SimdLaneOp, Opcode::I32X4ExtractLane},
+ {""}, {""}, {""}, {""}, {""},
+#line 406 "src/lexer-keywords.txt"
+ {"i64.ctz", TokenType::Unary, Opcode::I64Ctz},
+#line 268 "src/lexer-keywords.txt"
+ {"i32.ctz", TokenType::Unary, Opcode::I32Ctz},
+#line 108 "src/lexer-keywords.txt"
+ {"f32x4.relaxed_nmadd", TokenType::Ternary, Opcode::F32X4RelaxedNmadd},
+ {""},
+#line 396 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.or", TokenType::AtomicRmw, Opcode::I64AtomicRmwOr},
+#line 259 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.or", TokenType::AtomicRmw, Opcode::I32AtomicRmwOr},
+ {""}, {""}, {""},
+#line 105 "src/lexer-keywords.txt"
+ {"f32x4.relaxed_madd", TokenType::Ternary, Opcode::F32X4RelaxedMadd},
+ {""},
+#line 330 "src/lexer-keywords.txt"
+ {"i32x4.relaxed_trunc_f64x2_u_zero", TokenType::Unary, Opcode::I32X4RelaxedTruncF64X2UZero},
+#line 383 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.sub_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32SubU},
+#line 329 "src/lexer-keywords.txt"
+ {"i32x4.relaxed_trunc_f64x2_s_zero", TokenType::Unary, Opcode::I32X4RelaxedTruncF64X2SZero},
+#line 449 "src/lexer-keywords.txt"
+ {"i64.trunc_f32_u", TokenType::Convert, Opcode::I64TruncF32U},
+#line 305 "src/lexer-keywords.txt"
+ {"i32.trunc_f32_u", TokenType::Convert, Opcode::I32TruncF32U},
+ {""}, {""},
+#line 448 "src/lexer-keywords.txt"
+ {"i64.trunc_f32_s", TokenType::Convert, Opcode::I64TruncF32S},
+#line 304 "src/lexer-keywords.txt"
+ {"i32.trunc_f32_s", TokenType::Convert, Opcode::I32TruncF32S},
+ {""},
+#line 437 "src/lexer-keywords.txt"
+ {"i64.rem_u", TokenType::Binary, Opcode::I64RemU},
+#line 294 "src/lexer-keywords.txt"
+ {"i32.rem_u", TokenType::Binary, Opcode::I32RemU},
+#line 436 "src/lexer-keywords.txt"
+ {"i64.rem_s", TokenType::Binary, Opcode::I64RemS},
+#line 293 "src/lexer-keywords.txt"
+ {"i32.rem_s", TokenType::Binary, Opcode::I32RemS},
+#line 83 "src/lexer-keywords.txt"
+ {"f32", Type::F32},
+ {""}, {""},
+#line 316 "src/lexer-keywords.txt"
+ {"i32x4.all_true", TokenType::Unary, Opcode::I32X4AllTrue},
+ {""}, {""}, {""}, {""},
+#line 312 "src/lexer-keywords.txt"
+ {"i32", Type::I32},
+#line 564 "src/lexer-keywords.txt"
+ {"return_call", TokenType::ReturnCall, Opcode::ReturnCall},
+ {""}, {""}, {""},
+#line 133 "src/lexer-keywords.txt"
+ {"f64.max", TokenType::Binary, Opcode::F64Max},
+#line 72 "src/lexer-keywords.txt"
+ {"f32.max", TokenType::Binary, Opcode::F32Max},
+ {""}, {""},
+#line 445 "src/lexer-keywords.txt"
+ {"i64.store8", TokenType::Store, Opcode::I64Store8},
+#line 301 "src/lexer-keywords.txt"
+ {"i32.store8", TokenType::Store, Opcode::I32Store8},
+#line 380 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.and_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32AndU},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 336 "src/lexer-keywords.txt"
+ {"i32x4.max_u", TokenType::Binary, Opcode::I32X4MaxU},
+ {""},
+#line 335 "src/lexer-keywords.txt"
+ {"i32x4.max_s", TokenType::Binary, Opcode::I32X4MaxS},
+ {""}, {""}, {""},
+#line 371 "src/lexer-keywords.txt"
+ {"i64.atomic.load", TokenType::AtomicLoad, Opcode::I64AtomicLoad},
+#line 241 "src/lexer-keywords.txt"
+ {"i32.atomic.load", TokenType::AtomicLoad, Opcode::I32AtomicLoad},
+ {""}, {""}, {""},
+#line 153 "src/lexer-keywords.txt"
+ {"f64x2.ge", TokenType::Compare, Opcode::F64X2Ge},
+ {""},
+#line 155 "src/lexer-keywords.txt"
+ {"f64x2.le", TokenType::Compare, Opcode::F64X2Le},
+#line 161 "src/lexer-keywords.txt"
+ {"f64x2.neg", TokenType::Unary, Opcode::F64X2Neg},
+#line 399 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.xor", TokenType::AtomicRmw, Opcode::I64AtomicRmwXor},
+#line 262 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.xor", TokenType::AtomicRmw, Opcode::I32AtomicRmwXor},
+#line 162 "src/lexer-keywords.txt"
+ {"f64x2.ne", TokenType::Compare, Opcode::F64X2Ne},
+ {""},
+#line 382 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.or_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32OrU},
+ {""}, {""},
+#line 469 "src/lexer-keywords.txt"
+ {"i64x2.neg", TokenType::Unary, Opcode::I64X2Neg},
+ {""}, {""},
+#line 463 "src/lexer-keywords.txt"
+ {"i64x2.ne", TokenType::Binary, Opcode::I64X2Ne},
+#line 379 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.add_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32AddU},
+#line 154 "src/lexer-keywords.txt"
+ {"f64x2.gt", TokenType::Compare, Opcode::F64X2Gt},
+#line 467 "src/lexer-keywords.txt"
+ {"i64x2.ge_s", TokenType::Binary, Opcode::I64X2GeS},
+#line 156 "src/lexer-keywords.txt"
+ {"f64x2.lt", TokenType::Compare, Opcode::F64X2Lt},
+#line 466 "src/lexer-keywords.txt"
+ {"i64x2.le_s", TokenType::Binary, Opcode::I64X2LeS},
+ {""}, {""}, {""},
+#line 394 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.and", TokenType::AtomicRmw, Opcode::I64AtomicRmwAnd},
+#line 257 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.and", TokenType::AtomicRmw, Opcode::I32AtomicRmwAnd},
+#line 465 "src/lexer-keywords.txt"
+ {"i64x2.gt_s", TokenType::Binary, Opcode::I64X2GtS},
+ {""},
+#line 464 "src/lexer-keywords.txt"
+ {"i64x2.lt_s", TokenType::Binary, Opcode::I64X2LtS},
+ {""}, {""}, {""}, {""},
+#line 558 "src/lexer-keywords.txt"
+ {"ref.is_null", TokenType::RefIsNull, Opcode::RefIsNull},
+#line 50 "src/lexer-keywords.txt"
+ {"tag", TokenType::Tag},
+#line 427 "src/lexer-keywords.txt"
+ {"i64.load8_u", TokenType::Load, Opcode::I64Load8U},
+#line 284 "src/lexer-keywords.txt"
+ {"i32.load8_u", TokenType::Load, Opcode::I32Load8U},
+#line 426 "src/lexer-keywords.txt"
+ {"i64.load8_s", TokenType::Load, Opcode::I64Load8S},
+#line 283 "src/lexer-keywords.txt"
+ {"i32.load8_s", TokenType::Load, Opcode::I32Load8S},
+ {""}, {""},
+#line 106 "src/lexer-keywords.txt"
+ {"f32x4.relaxed_max", TokenType::Binary, Opcode::F32X4RelaxedMax},
+ {""},
+#line 160 "src/lexer-keywords.txt"
+ {"f64x2.nearest", TokenType::Unary, Opcode::F64X2Nearest},
+ {""},
+#line 159 "src/lexer-keywords.txt"
+ {"f64x2.mul", TokenType::Binary, Opcode::F64X2Mul},
+#line 181 "src/lexer-keywords.txt"
+ {"get", TokenType::Get},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 461 "src/lexer-keywords.txt"
+ {"i64x2.mul", TokenType::Binary, Opcode::I64X2Mul},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 393 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.add", TokenType::AtomicRmw, Opcode::I64AtomicRmwAdd},
+#line 256 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.add", TokenType::AtomicRmw, Opcode::I32AtomicRmwAdd},
+ {""},
+#line 148 "src/lexer-keywords.txt"
+ {"f64x2.ceil", TokenType::Unary, Opcode::F64X2Ceil},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 440 "src/lexer-keywords.txt"
+ {"i64.shl", TokenType::Binary, Opcode::I64Shl},
+#line 297 "src/lexer-keywords.txt"
+ {"i32.shl", TokenType::Binary, Opcode::I32Shl},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 173 "src/lexer-keywords.txt"
+ {"f64x2.trunc", TokenType::Unary, Opcode::F64X2Trunc},
+ {""},
+#line 338 "src/lexer-keywords.txt"
+ {"i32x4.min_u", TokenType::Binary, Opcode::I32X4MinU},
+ {""},
+#line 337 "src/lexer-keywords.txt"
+ {"i32x4.min_s", TokenType::Binary, Opcode::I32X4MinS},
+ {""},
+#line 345 "src/lexer-keywords.txt"
+ {"i32x4.shl", TokenType::Binary, Opcode::I32X4Shl},
+ {""}, {""},
+#line 444 "src/lexer-keywords.txt"
+ {"i64.store32", TokenType::Store, Opcode::I64Store32},
+#line 548 "src/lexer-keywords.txt"
+ {"nan:arithmetic", TokenType::NanArithmetic},
+ {""}, {""},
+#line 413 "src/lexer-keywords.txt"
+ {"i64.extend8_s", TokenType::Unary, Opcode::I64Extend8S},
+#line 274 "src/lexer-keywords.txt"
+ {"i32.extend8_s", TokenType::Unary, Opcode::I32Extend8S},
+ {""}, {""}, {""}, {""}, {""},
+#line 560 "src/lexer-keywords.txt"
+ {"register", TokenType::Register},
+ {""},
+#line 549 "src/lexer-keywords.txt"
+ {"nan:canonical", TokenType::NanCanonical},
+ {""},
+#line 213 "src/lexer-keywords.txt"
+ {"i16x8.neg", TokenType::Unary, Opcode::I16X8Neg},
+#line 147 "src/lexer-keywords.txt"
+ {"f64x2.add", TokenType::Binary, Opcode::F64X2Add},
+#line 197 "src/lexer-keywords.txt"
+ {"i16x8.ge_u", TokenType::Compare, Opcode::I16X8GeU},
+#line 215 "src/lexer-keywords.txt"
+ {"i16x8.ne", TokenType::Compare, Opcode::I16X8Ne},
+#line 201 "src/lexer-keywords.txt"
+ {"i16x8.le_u", TokenType::Compare, Opcode::I16X8LeU},
+ {""},
+#line 196 "src/lexer-keywords.txt"
+ {"i16x8.ge_s", TokenType::Compare, Opcode::I16X8GeS},
+ {""},
+#line 200 "src/lexer-keywords.txt"
+ {"i16x8.le_s", TokenType::Compare, Opcode::I16X8LeS},
+#line 457 "src/lexer-keywords.txt"
+ {"i64x2.add", TokenType::Binary, Opcode::I64X2Add},
+#line 199 "src/lexer-keywords.txt"
+ {"i16x8.gt_u", TokenType::Compare, Opcode::I16X8GtU},
+ {""},
+#line 205 "src/lexer-keywords.txt"
+ {"i16x8.lt_u", TokenType::Compare, Opcode::I16X8LtU},
+#line 385 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.xor_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32XorU},
+#line 198 "src/lexer-keywords.txt"
+ {"i16x8.gt_s", TokenType::Compare, Opcode::I16X8GtS},
+#line 169 "src/lexer-keywords.txt"
+ {"f64x2.replace_lane", TokenType::SimdLaneOp, Opcode::F64X2ReplaceLane},
+#line 204 "src/lexer-keywords.txt"
+ {"i16x8.lt_s", TokenType::Compare, Opcode::I16X8LtS},
+#line 384 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.xchg_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32XchgU},
+ {""},
+#line 387 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.and_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8AndU},
+#line 250 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.and_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8AndU},
+#line 580 "src/lexer-keywords.txt"
+ {"try", TokenType::Try, Opcode::Try},
+ {""},
+#line 477 "src/lexer-keywords.txt"
+ {"i64x2.replace_lane", TokenType::SimdLaneOp, Opcode::I64X2ReplaceLane},
+ {""},
+#line 530 "src/lexer-keywords.txt"
+ {"invoke", TokenType::Invoke},
+ {""}, {""}, {""},
+#line 167 "src/lexer-keywords.txt"
+ {"f64x2.relaxed_min", TokenType::Binary, Opcode::F64X2RelaxedMin},
+ {""},
+#line 402 "src/lexer-keywords.txt"
+ {"i64.atomic.store8", TokenType::AtomicStore, Opcode::I64AtomicStore8},
+#line 264 "src/lexer-keywords.txt"
+ {"i32.atomic.store8", TokenType::AtomicStore, Opcode::I32AtomicStore8},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 210 "src/lexer-keywords.txt"
+ {"i16x8.mul", TokenType::Binary, Opcode::I16X8Mul},
+ {""},
+#line 425 "src/lexer-keywords.txt"
+ {"i64.load32_u", TokenType::Load, Opcode::I64Load32U},
+ {""},
+#line 423 "src/lexer-keywords.txt"
+ {"i64.load16_u", TokenType::Load, Opcode::I64Load16U},
+#line 282 "src/lexer-keywords.txt"
+ {"i32.load16_u", TokenType::Load, Opcode::I32Load16U},
+#line 424 "src/lexer-keywords.txt"
+ {"i64.load32_s", TokenType::Load, Opcode::I64Load32S},
+ {""},
+#line 422 "src/lexer-keywords.txt"
+ {"i64.load16_s", TokenType::Load, Opcode::I64Load16S},
+#line 281 "src/lexer-keywords.txt"
+ {"i32.load16_s", TokenType::Load, Opcode::I32Load16S},
+ {""}, {""}, {""}, {""},
+#line 386 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.add_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8AddU},
+#line 249 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.add_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8AddU},
+ {""}, {""}, {""}, {""}, {""},
+#line 589 "src/lexer-keywords.txt"
+ {"v128.or", TokenType::Binary, Opcode::V128Or},
+ {""}, {""},
+#line 158 "src/lexer-keywords.txt"
+ {"f64x2.min", TokenType::Binary, Opcode::F64X2Min},
+ {""}, {""},
+#line 527 "src/lexer-keywords.txt"
+ {"if", TokenType::If, Opcode::If},
+#line 232 "src/lexer-keywords.txt"
+ {"i16x8", TokenType::I16X8},
+#line 554 "src/lexer-keywords.txt"
+ {"ref", TokenType::Ref},
+#line 594 "src/lexer-keywords.txt"
+ {"v128", Type::V128},
+ {""}, {""}, {""},
+#line 578 "src/lexer-keywords.txt"
+ {"then", TokenType::Then},
+ {""}, {""}, {""},
+#line 370 "src/lexer-keywords.txt"
+ {"i64.atomic.load8_u", TokenType::AtomicLoad, Opcode::I64AtomicLoad8U},
+#line 240 "src/lexer-keywords.txt"
+ {"i32.atomic.load8_u", TokenType::AtomicLoad, Opcode::I32AtomicLoad8U},
+#line 157 "src/lexer-keywords.txt"
+ {"f64x2.max", TokenType::Binary, Opcode::F64X2Max},
+ {""},
+#line 476 "src/lexer-keywords.txt"
+ {"i64x2.relaxed_laneselect", TokenType::Ternary, Opcode::I64X2RelaxedLaneSelect},
+ {""},
+#line 48 "src/lexer-keywords.txt"
+ {"else", TokenType::Else, Opcode::Else},
+#line 588 "src/lexer-keywords.txt"
+ {"v128.not", TokenType::Unary, Opcode::V128Not},
+#line 593 "src/lexer-keywords.txt"
+ {"v128.store", TokenType::Store, Opcode::V128Store},
+ {""},
+#line 544 "src/lexer-keywords.txt"
+ {"memory.size", TokenType::MemorySize, Opcode::MemorySize},
+ {""}, {""},
+#line 328 "src/lexer-keywords.txt"
+ {"i32x4.relaxed_trunc_f32x4_u", TokenType::Unary, Opcode::I32X4RelaxedTruncF32X4U},
+ {""}, {""},
+#line 412 "src/lexer-keywords.txt"
+ {"i64.extend32_s", TokenType::Unary, Opcode::I64Extend32S},
+#line 327 "src/lexer-keywords.txt"
+ {"i32x4.relaxed_trunc_f32x4_s", TokenType::Unary, Opcode::I32X4RelaxedTruncF32X4S},
+#line 411 "src/lexer-keywords.txt"
+ {"i64.extend16_s", TokenType::Unary, Opcode::I64Extend16S},
+#line 273 "src/lexer-keywords.txt"
+ {"i32.extend16_s", TokenType::Unary, Opcode::I32Extend16S},
+ {""}, {""}, {""},
+#line 188 "src/lexer-keywords.txt"
+ {"i16x8.add", TokenType::Binary, Opcode::I16X8Add},
+#line 45 "src/lexer-keywords.txt"
+ {"either", TokenType::Either},
+#line 51 "src/lexer-keywords.txt"
+ {"extern", Type::ExternRef, TokenType::Extern},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 49 "src/lexer-keywords.txt"
+ {"end", TokenType::End, Opcode::End},
+ {""},
+#line 218 "src/lexer-keywords.txt"
+ {"i16x8.replace_lane", TokenType::SimdLaneOp, Opcode::I16X8ReplaceLane},
+#line 584 "src/lexer-keywords.txt"
+ {"v128.and", TokenType::Binary, Opcode::V128And},
+#line 415 "src/lexer-keywords.txt"
+ {"i64.extend_i32_u", TokenType::Convert, Opcode::I64ExtendI32U},
+ {""},
+#line 414 "src/lexer-keywords.txt"
+ {"i64.extend_i32_s", TokenType::Convert, Opcode::I64ExtendI32S},
+ {""},
+#line 392 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.xor_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8XorU},
+#line 255 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.xor_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8XorU},
+ {""}, {""},
+#line 531 "src/lexer-keywords.txt"
+ {"item", TokenType::Item},
+ {""},
+#line 151 "src/lexer-keywords.txt"
+ {"f64x2.extract_lane", TokenType::SimdLaneOp, Opcode::F64X2ExtractLane},
+#line 587 "src/lexer-keywords.txt"
+ {"v128.load", TokenType::Load, Opcode::V128Load},
+ {""}, {""}, {""}, {""},
+#line 34 "src/lexer-keywords.txt"
+ {"call_indirect", TokenType::CallIndirect, Opcode::CallIndirect},
+ {""},
+#line 458 "src/lexer-keywords.txt"
+ {"i64x2.extract_lane", TokenType::SimdLaneOp, Opcode::I64X2ExtractLane},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 168 "src/lexer-keywords.txt"
+ {"f64x2.relaxed_nmadd", TokenType::Ternary, Opcode::F64X2RelaxedNmadd},
+#line 89 "src/lexer-keywords.txt"
+ {"f32x4.div", TokenType::Binary, Opcode::F32X4Div},
+ {""}, {""},
+#line 586 "src/lexer-keywords.txt"
+ {"v128.const", TokenType::Const, Opcode::V128Const},
+ {""}, {""},
+#line 165 "src/lexer-keywords.txt"
+ {"f64x2.relaxed_madd", TokenType::Ternary, Opcode::F64X2RelaxedMadd},
+ {""},
+#line 607 "src/lexer-keywords.txt"
+ {"v128.store64_lane", TokenType::SimdStoreLane, Opcode::V128Store64Lane},
+#line 583 "src/lexer-keywords.txt"
+ {"v128.andnot", TokenType::Binary, Opcode::V128Andnot},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 178 "src/lexer-keywords.txt"
+ {"field", TokenType::Field},
+ {""}, {""},
+#line 451 "src/lexer-keywords.txt"
+ {"i64.trunc_f64_u", TokenType::Convert, Opcode::I64TruncF64U},
+#line 307 "src/lexer-keywords.txt"
+ {"i32.trunc_f64_u", TokenType::Convert, Opcode::I32TruncF64U},
+ {""}, {""},
+#line 450 "src/lexer-keywords.txt"
+ {"i64.trunc_f64_s", TokenType::Convert, Opcode::I64TruncF64S},
+#line 306 "src/lexer-keywords.txt"
+ {"i32.trunc_f64_s", TokenType::Convert, Opcode::I32TruncF64S},
+#line 470 "src/lexer-keywords.txt"
+ {"i64x2.all_true", TokenType::Unary, Opcode::I64X2AllTrue},
+ {""}, {""}, {""}, {""}, {""},
+#line 216 "src/lexer-keywords.txt"
+ {"i16x8.relaxed_laneselect", TokenType::Ternary, Opcode::I16X8RelaxedLaneSelect},
+ {""}, {""}, {""}, {""}, {""},
+#line 177 "src/lexer-keywords.txt"
+ {"f64x2", TokenType::F64X2},
+ {""},
+#line 92 "src/lexer-keywords.txt"
+ {"f32x4.floor", TokenType::Unary, Opcode::F32X4Floor},
+#line 595 "src/lexer-keywords.txt"
+ {"v128.xor", TokenType::Binary, Opcode::V128Xor},
+ {""}, {""}, {""}, {""},
+#line 487 "src/lexer-keywords.txt"
+ {"i64x2", TokenType::I64X2},
+ {""}, {""},
+#line 442 "src/lexer-keywords.txt"
+ {"i64.shr_u", TokenType::Binary, Opcode::I64ShrU},
+#line 299 "src/lexer-keywords.txt"
+ {"i32.shr_u", TokenType::Binary, Opcode::I32ShrU},
+#line 441 "src/lexer-keywords.txt"
+ {"i64.shr_s", TokenType::Binary, Opcode::I64ShrS},
+#line 298 "src/lexer-keywords.txt"
+ {"i32.shr_s", TokenType::Binary, Opcode::I32ShrS},
+ {""}, {""}, {""}, {""}, {""},
+#line 347 "src/lexer-keywords.txt"
+ {"i32x4.shr_u", TokenType::Binary, Opcode::I32X4ShrU},
+ {""},
+#line 346 "src/lexer-keywords.txt"
+ {"i32x4.shr_s", TokenType::Binary, Opcode::I32X4ShrS},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""},
+#line 557 "src/lexer-keywords.txt"
+ {"ref.func", TokenType::RefFunc, Opcode::RefFunc},
+ {""}, {""}, {""},
+#line 389 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.or_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8OrU},
+#line 252 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.or_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8OrU},
+ {""}, {""}, {""}, {""},
+#line 127 "src/lexer-keywords.txt"
+ {"f64.floor", TokenType::Unary, Opcode::F64Floor},
+#line 66 "src/lexer-keywords.txt"
+ {"f32.floor", TokenType::Unary, Opcode::F32Floor},
+ {""},
+#line 600 "src/lexer-keywords.txt"
+ {"v128.load8_lane", TokenType::SimdLoadLane, Opcode::V128Load8Lane},
+ {""},
+#line 460 "src/lexer-keywords.txt"
+ {"v128.load32x2_u", TokenType::Load, Opcode::V128Load32X2U},
+ {""}, {""}, {""},
+#line 459 "src/lexer-keywords.txt"
+ {"v128.load32x2_s", TokenType::Load, Opcode::V128Load32X2S},
+ {""}, {""}, {""},
+#line 563 "src/lexer-keywords.txt"
+ {"return_call_indirect", TokenType::ReturnCallIndirect, Opcode::ReturnCallIndirect},
+#line 543 "src/lexer-keywords.txt"
+ {"memory.init", TokenType::MemoryInit, Opcode::MemoryInit},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 166 "src/lexer-keywords.txt"
+ {"f64x2.relaxed_max", TokenType::Binary, Opcode::F64X2RelaxedMax},
+ {""}, {""},
+#line 38 "src/lexer-keywords.txt"
+ {"catch_all", TokenType::CatchAll, Opcode::CatchAll},
+#line 112 "src/lexer-keywords.txt"
+ {"f32x4.sub", TokenType::Binary, Opcode::F32X4Sub},
+ {""},
+#line 568 "src/lexer-keywords.txt"
+ {"start", TokenType::Start},
+ {""}, {""}, {""}, {""}, {""},
+#line 349 "src/lexer-keywords.txt"
+ {"i32x4.sub", TokenType::Binary, Opcode::I32X4Sub},
+#line 224 "src/lexer-keywords.txt"
+ {"i16x8.sub_sat_u", TokenType::Binary, Opcode::I16X8SubSatU},
+#line 599 "src/lexer-keywords.txt"
+ {"v128.load8_splat", TokenType::Load, Opcode::V128Load8Splat},
+ {""},
+#line 189 "src/lexer-keywords.txt"
+ {"i16x8.all_true", TokenType::Unary, Opcode::I16X8AllTrue},
+#line 223 "src/lexer-keywords.txt"
+ {"i16x8.sub_sat_s", TokenType::Binary, Opcode::I16X8SubSatS},
+#line 116 "src/lexer-keywords.txt"
+ {"f64.abs", TokenType::Unary, Opcode::F64Abs},
+#line 54 "src/lexer-keywords.txt"
+ {"f32.abs", TokenType::Unary, Opcode::F32Abs},
+ {""}, {""}, {""}, {""}, {""},
+#line 581 "src/lexer-keywords.txt"
+ {"type", TokenType::Type},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 84 "src/lexer-keywords.txt"
+ {"f32x4.abs", TokenType::Unary, Opcode::F32X4Abs},
+ {""}, {""},
+#line 398 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.xchg", TokenType::AtomicRmw, Opcode::I64AtomicRmwXchg},
+#line 261 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.xchg", TokenType::AtomicRmw, Opcode::I32AtomicRmwXchg},
+#line 63 "src/lexer-keywords.txt"
+ {"f32.demote_f64", TokenType::Convert, Opcode::F32DemoteF64},
+ {""}, {""},
+#line 314 "src/lexer-keywords.txt"
+ {"i32x4.abs", TokenType::Unary, Opcode::I32X4Abs},
+ {""},
+#line 207 "src/lexer-keywords.txt"
+ {"i16x8.max_u", TokenType::Binary, Opcode::I16X8MaxU},
+ {""},
+#line 206 "src/lexer-keywords.txt"
+ {"i16x8.max_s", TokenType::Binary, Opcode::I16X8MaxS},
+#line 571 "src/lexer-keywords.txt"
+ {"table.fill", TokenType::TableFill, Opcode::TableFill},
+#line 478 "src/lexer-keywords.txt"
+ {"i64x2.shl", TokenType::Binary, Opcode::I64X2Shl},
+ {""},
+#line 513 "src/lexer-keywords.txt"
+ {"i8x16.neg", TokenType::Unary, Opcode::I8X16Neg},
+ {""},
+#line 500 "src/lexer-keywords.txt"
+ {"i8x16.ge_u", TokenType::Compare, Opcode::I8X16GeU},
+#line 515 "src/lexer-keywords.txt"
+ {"i8x16.ne", TokenType::Compare, Opcode::I8X16Ne},
+#line 504 "src/lexer-keywords.txt"
+ {"i8x16.le_u", TokenType::Compare, Opcode::I8X16LeU},
+ {""},
+#line 499 "src/lexer-keywords.txt"
+ {"i8x16.ge_s", TokenType::Compare, Opcode::I8X16GeS},
+ {""},
+#line 503 "src/lexer-keywords.txt"
+ {"i8x16.le_s", TokenType::Compare, Opcode::I8X16LeS},
+ {""},
+#line 502 "src/lexer-keywords.txt"
+ {"i8x16.gt_u", TokenType::Compare, Opcode::I8X16GtU},
+ {""},
+#line 506 "src/lexer-keywords.txt"
+ {"i8x16.lt_u", TokenType::Compare, Opcode::I8X16LtU},
+#line 529 "src/lexer-keywords.txt"
+ {"input", TokenType::Input},
+#line 501 "src/lexer-keywords.txt"
+ {"i8x16.gt_s", TokenType::Compare, Opcode::I8X16GtS},
+ {""},
+#line 505 "src/lexer-keywords.txt"
+ {"i8x16.lt_s", TokenType::Compare, Opcode::I8X16LtS},
+ {""}, {""},
+#line 203 "src/lexer-keywords.txt"
+ {"v128.load8x8_u", TokenType::Load, Opcode::V128Load8X8U},
+ {""},
+#line 202 "src/lexer-keywords.txt"
+ {"v128.load8x8_s", TokenType::Load, Opcode::V128Load8X8S},
+ {""}, {""}, {""},
+#line 555 "src/lexer-keywords.txt"
+ {"quote", TokenType::Quote},
+ {""}, {""},
+#line 528 "src/lexer-keywords.txt"
+ {"import", TokenType::Import},
+ {""},
+#line 567 "src/lexer-keywords.txt"
+ {"shared", TokenType::Shared},
+#line 604 "src/lexer-keywords.txt"
+ {"v128.store8_lane", TokenType::SimdStoreLane, Opcode::V128Store8Lane},
+ {""},
+#line 187 "src/lexer-keywords.txt"
+ {"i16x8.add_sat_u", TokenType::Binary, Opcode::I16X8AddSatU},
+ {""}, {""}, {""},
+#line 186 "src/lexer-keywords.txt"
+ {"i16x8.add_sat_s", TokenType::Binary, Opcode::I16X8AddSatS},
+#line 141 "src/lexer-keywords.txt"
+ {"f64.sqrt", TokenType::Unary, Opcode::F64Sqrt},
+#line 79 "src/lexer-keywords.txt"
+ {"f32.sqrt", TokenType::Unary, Opcode::F32Sqrt},
+#line 32 "src/lexer-keywords.txt"
+ {"br_table", TokenType::BrTable, Opcode::BrTable},
+ {""},
+#line 552 "src/lexer-keywords.txt"
+ {"output", TokenType::Output},
+ {""},
+#line 602 "src/lexer-keywords.txt"
+ {"v128.load32_lane", TokenType::SimdLoadLane, Opcode::V128Load32Lane},
+ {""},
+#line 551 "src/lexer-keywords.txt"
+ {"offset", TokenType::Offset},
+#line 111 "src/lexer-keywords.txt"
+ {"f32x4.sqrt", TokenType::Unary, Opcode::F32X4Sqrt},
+ {""}, {""},
+#line 545 "src/lexer-keywords.txt"
+ {"memory", TokenType::Memory},
+#line 381 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw32CmpxchgU},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 123 "src/lexer-keywords.txt"
+ {"f64.convert_i64_u", TokenType::Convert, Opcode::F64ConvertI64U},
+#line 61 "src/lexer-keywords.txt"
+ {"f32.convert_i64_u", TokenType::Convert, Opcode::F32ConvertI64U},
+ {""}, {""},
+#line 122 "src/lexer-keywords.txt"
+ {"f64.convert_i64_s", TokenType::Convert, Opcode::F64ConvertI64S},
+#line 60 "src/lexer-keywords.txt"
+ {"f32.convert_i64_s", TokenType::Convert, Opcode::F32ConvertI64S},
+#line 408 "src/lexer-keywords.txt"
+ {"i64.div_u", TokenType::Binary, Opcode::I64DivU},
+#line 270 "src/lexer-keywords.txt"
+ {"i32.div_u", TokenType::Binary, Opcode::I32DivU},
+#line 407 "src/lexer-keywords.txt"
+ {"i64.div_s", TokenType::Binary, Opcode::I64DivS},
+#line 269 "src/lexer-keywords.txt"
+ {"i32.div_s", TokenType::Binary, Opcode::I32DivS},
+#line 597 "src/lexer-keywords.txt"
+ {"v128.load32_splat", TokenType::Load, Opcode::V128Load32Splat},
+ {""}, {""}, {""},
+#line 376 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.sub_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16SubU},
+#line 246 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.sub_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16SubU},
+ {""}, {""}, {""},
+#line 37 "src/lexer-keywords.txt"
+ {"catch", TokenType::Catch, Opcode::Catch},
+ {""}, {""}, {""},
+#line 553 "src/lexer-keywords.txt"
+ {"param", TokenType::Param},
+ {""}, {""}, {""}, {""}, {""},
+#line 209 "src/lexer-keywords.txt"
+ {"i16x8.min_u", TokenType::Binary, Opcode::I16X8MinU},
+ {""},
+#line 208 "src/lexer-keywords.txt"
+ {"i16x8.min_s", TokenType::Binary, Opcode::I16X8MinS},
+ {""},
+#line 219 "src/lexer-keywords.txt"
+ {"i16x8.shl", TokenType::Binary, Opcode::I16X8Shl},
+ {""},
+#line 110 "src/lexer-keywords.txt"
+ {"f32x4.splat", TokenType::Unary, Opcode::F32X4Splat},
+ {""}, {""}, {""}, {""},
+#line 591 "src/lexer-keywords.txt"
+ {"v128.load32_zero", TokenType::Load, Opcode::V128Load32Zero},
+ {""}, {""},
+#line 348 "src/lexer-keywords.txt"
+ {"i32x4.splat", TokenType::Unary, Opcode::I32X4Splat},
+#line 492 "src/lexer-keywords.txt"
+ {"i8x16.add", TokenType::Binary, Opcode::I8X16Add},
+ {""},
+#line 373 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.and_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16AndU},
+#line 243 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.and_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16AndU},
+ {""}, {""}, {""},
+#line 579 "src/lexer-keywords.txt"
+ {"throw", TokenType::Throw, Opcode::Throw},
+#line 369 "src/lexer-keywords.txt"
+ {"i64.atomic.load32_u", TokenType::AtomicLoad, Opcode::I64AtomicLoad32U},
+#line 104 "src/lexer-keywords.txt"
+ {"f32x4.pmin", TokenType::Binary, Opcode::F32X4PMin},
+#line 368 "src/lexer-keywords.txt"
+ {"i64.atomic.load16_u", TokenType::AtomicLoad, Opcode::I64AtomicLoad16U},
+#line 239 "src/lexer-keywords.txt"
+ {"i32.atomic.load16_u", TokenType::AtomicLoad, Opcode::I32AtomicLoad16U},
+ {""}, {""},
+#line 518 "src/lexer-keywords.txt"
+ {"i8x16.replace_lane", TokenType::SimdLaneOp, Opcode::I8X16ReplaceLane},
+#line 30 "src/lexer-keywords.txt"
+ {"block", TokenType::Block, Opcode::Block},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 375 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.or_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16OrU},
+#line 245 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.or_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16OrU},
+ {""}, {""}, {""}, {""}, {""},
+#line 372 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.add_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16AddU},
+#line 242 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.add_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16AddU},
+ {""}, {""}, {""},
+#line 39 "src/lexer-keywords.txt"
+ {"data.drop", TokenType::DataDrop, Opcode::DataDrop},
+ {""}, {""},
+#line 29 "src/lexer-keywords.txt"
+ {"binary", TokenType::Bin},
+ {""}, {""},
+#line 190 "src/lexer-keywords.txt"
+ {"i16x8.avgr_u", TokenType::Binary, Opcode::I16X8AvgrU},
+#line 195 "src/lexer-keywords.txt"
+ {"i16x8.extract_lane_u", TokenType::SimdLaneOp, Opcode::I16X8ExtractLaneU},
+ {""},
+#line 194 "src/lexer-keywords.txt"
+ {"i16x8.extract_lane_s", TokenType::SimdLaneOp, Opcode::I16X8ExtractLaneS},
+ {""}, {""}, {""}, {""},
+#line 590 "src/lexer-keywords.txt"
+ {"v128.any_true", TokenType::Unary, Opcode::V128AnyTrue},
+ {""},
+#line 149 "src/lexer-keywords.txt"
+ {"f64x2.div", TokenType::Binary, Opcode::F64X2Div},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 606 "src/lexer-keywords.txt"
+ {"v128.store32_lane", TokenType::SimdStoreLane, Opcode::V128Store32Lane},
+ {""},
+#line 605 "src/lexer-keywords.txt"
+ {"v128.store16_lane", TokenType::SimdStoreLane, Opcode::V128Store16Lane},
+#line 401 "src/lexer-keywords.txt"
+ {"i64.atomic.store32", TokenType::AtomicStore, Opcode::I64AtomicStore32},
+ {""},
+#line 542 "src/lexer-keywords.txt"
+ {"memory.grow", TokenType::MemoryGrow, Opcode::MemoryGrow},
+#line 517 "src/lexer-keywords.txt"
+ {"i8x16.relaxed_laneselect", TokenType::Ternary, Opcode::I8X16RelaxedLaneSelect},
+ {""}, {""},
+#line 317 "src/lexer-keywords.txt"
+ {"i32x4.bitmask", TokenType::Unary, Opcode::I32X4Bitmask},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 562 "src/lexer-keywords.txt"
+ {"rethrow", TokenType::Rethrow, Opcode::Rethrow},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 582 "src/lexer-keywords.txt"
+ {"unreachable", TokenType::Unreachable, Opcode::Unreachable},
+ {""},
+#line 152 "src/lexer-keywords.txt"
+ {"f64x2.floor", TokenType::Unary, Opcode::F64X2Floor},
+ {""}, {""}, {""},
+#line 573 "src/lexer-keywords.txt"
+ {"table.grow", TokenType::TableGrow, Opcode::TableGrow},
+#line 410 "src/lexer-keywords.txt"
+ {"i64.eqz", TokenType::Convert, Opcode::I64Eqz},
+#line 272 "src/lexer-keywords.txt"
+ {"i32.eqz", TokenType::Convert, Opcode::I32Eqz},
+ {""}, {""},
+#line 391 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.xchg_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8XchgU},
+#line 254 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.xchg_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8XchgU},
+ {""}, {""}, {""}, {""}, {""},
+#line 443 "src/lexer-keywords.txt"
+ {"i64.store16", TokenType::Store, Opcode::I64Store16},
+#line 300 "src/lexer-keywords.txt"
+ {"i32.store16", TokenType::Store, Opcode::I32Store16},
+#line 480 "src/lexer-keywords.txt"
+ {"i64x2.shr_u", TokenType::Binary, Opcode::I64X2ShrU},
+ {""},
+#line 479 "src/lexer-keywords.txt"
+ {"i64x2.shr_s", TokenType::Binary, Opcode::I64X2ShrS},
+ {""}, {""},
+#line 570 "src/lexer-keywords.txt"
+ {"table.copy", TokenType::TableCopy, Opcode::TableCopy},
+ {""},
+#line 541 "src/lexer-keywords.txt"
+ {"memory.fill", TokenType::MemoryFill, Opcode::MemoryFill},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""},
+#line 125 "src/lexer-keywords.txt"
+ {"f64.div", TokenType::Binary, Opcode::F64Div},
+#line 64 "src/lexer-keywords.txt"
+ {"f32.div", TokenType::Binary, Opcode::F32Div},
+ {""},
+#line 25 "src/lexer-keywords.txt"
+ {"assert_return", TokenType::AssertReturn},
+#line 378 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.xor_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16XorU},
+#line 248 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.xor_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16XorU},
+ {""}, {""},
+#line 377 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.xchg_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16XchgU},
+#line 247 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.xchg_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16XchgU},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""},
+#line 524 "src/lexer-keywords.txt"
+ {"i8x16.sub_sat_u", TokenType::Binary, Opcode::I8X16SubSatU},
+ {""}, {""},
+#line 493 "src/lexer-keywords.txt"
+ {"i8x16.all_true", TokenType::Unary, Opcode::I8X16AllTrue},
+#line 523 "src/lexer-keywords.txt"
+ {"i8x16.sub_sat_s", TokenType::Binary, Opcode::I8X16SubSatS},
+ {""}, {""},
+#line 172 "src/lexer-keywords.txt"
+ {"f64x2.sub", TokenType::Binary, Opcode::F64X2Sub},
+ {""}, {""}, {""}, {""},
+#line 339 "src/lexer-keywords.txt"
+ {"i32x4.dot_i16x8_s", TokenType::Binary, Opcode::I32X4DotI16X8S},
+ {""}, {""},
+#line 482 "src/lexer-keywords.txt"
+ {"i64x2.sub", TokenType::Binary, Opcode::I64X2Sub},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 526 "src/lexer-keywords.txt"
+ {"i8x16", TokenType::I8X16},
+ {""}, {""}, {""},
+#line 508 "src/lexer-keywords.txt"
+ {"i8x16.max_u", TokenType::Binary, Opcode::I8X16MaxU},
+ {""},
+#line 507 "src/lexer-keywords.txt"
+ {"i8x16.max_s", TokenType::Binary, Opcode::I8X16MaxS},
+ {""}, {""}, {""},
+#line 146 "src/lexer-keywords.txt"
+ {"f64x2.abs", TokenType::Unary, Opcode::F64X2Abs},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 468 "src/lexer-keywords.txt"
+ {"i64x2.abs", TokenType::Unary, Opcode::I64X2Abs},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 221 "src/lexer-keywords.txt"
+ {"i16x8.shr_u", TokenType::Binary, Opcode::I16X8ShrU},
+ {""},
+#line 220 "src/lexer-keywords.txt"
+ {"i16x8.shr_s", TokenType::Binary, Opcode::I16X8ShrS},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 390 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.sub_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8SubU},
+#line 253 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.sub_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8SubU},
+ {""}, {""}, {""}, {""}, {""},
+#line 491 "src/lexer-keywords.txt"
+ {"i8x16.add_sat_u", TokenType::Binary, Opcode::I8X16AddSatU},
+#line 103 "src/lexer-keywords.txt"
+ {"f32x4.pmax", TokenType::Binary, Opcode::F32X4PMax},
+ {""}, {""},
+#line 490 "src/lexer-keywords.txt"
+ {"i8x16.add_sat_s", TokenType::Binary, Opcode::I8X16AddSatS},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""},
+#line 455 "src/lexer-keywords.txt"
+ {"i64.trunc_sat_f64_u", TokenType::Convert, Opcode::I64TruncSatF64U},
+#line 311 "src/lexer-keywords.txt"
+ {"i32.trunc_sat_f64_u", TokenType::Convert, Opcode::I32TruncSatF64U},
+#line 47 "src/lexer-keywords.txt"
+ {"elem", TokenType::Elem},
+ {""},
+#line 454 "src/lexer-keywords.txt"
+ {"i64.trunc_sat_f64_s", TokenType::Convert, Opcode::I64TruncSatF64S},
+#line 310 "src/lexer-keywords.txt"
+ {"i32.trunc_sat_f64_s", TokenType::Convert, Opcode::I32TruncSatF64S},
+#line 171 "src/lexer-keywords.txt"
+ {"f64x2.sqrt", TokenType::Unary, Opcode::F64X2Sqrt},
+ {""}, {""}, {""},
+#line 52 "src/lexer-keywords.txt"
+ {"externref", Type::ExternRef},
+#line 603 "src/lexer-keywords.txt"
+ {"v128.load64_lane", TokenType::SimdLoadLane, Opcode::V128Load64Lane},
+ {""},
+#line 351 "src/lexer-keywords.txt"
+ {"i32x4.extadd_pairwise_i16x8_u", TokenType::Unary, Opcode::I32X4ExtaddPairwiseI16X8U},
+ {""},
+#line 350 "src/lexer-keywords.txt"
+ {"i32x4.extadd_pairwise_i16x8_s", TokenType::Unary, Opcode::I32X4ExtaddPairwiseI16X8S},
+#line 225 "src/lexer-keywords.txt"
+ {"i16x8.sub", TokenType::Binary, Opcode::I16X8Sub},
+#line 31 "src/lexer-keywords.txt"
+ {"br_if", TokenType::BrIf, Opcode::BrIf},
+#line 121 "src/lexer-keywords.txt"
+ {"f64.convert_i32_u", TokenType::Convert, Opcode::F64ConvertI32U},
+#line 59 "src/lexer-keywords.txt"
+ {"f32.convert_i32_u", TokenType::Convert, Opcode::F32ConvertI32U},
+ {""},
+#line 24 "src/lexer-keywords.txt"
+ {"assert_malformed", TokenType::AssertMalformed},
+#line 120 "src/lexer-keywords.txt"
+ {"f64.convert_i32_s", TokenType::Convert, Opcode::F64ConvertI32S},
+#line 58 "src/lexer-keywords.txt"
+ {"f32.convert_i32_s", TokenType::Convert, Opcode::F32ConvertI32S},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 510 "src/lexer-keywords.txt"
+ {"i8x16.min_u", TokenType::Binary, Opcode::I8X16MinU},
+ {""},
+#line 509 "src/lexer-keywords.txt"
+ {"i8x16.min_s", TokenType::Binary, Opcode::I8X16MinS},
+ {""},
+#line 519 "src/lexer-keywords.txt"
+ {"i8x16.shl", TokenType::Binary, Opcode::I8X16Shl},
+#line 598 "src/lexer-keywords.txt"
+ {"v128.load64_splat", TokenType::Load, Opcode::V128Load64Splat},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 185 "src/lexer-keywords.txt"
+ {"i16x8.abs", TokenType::Unary, Opcode::I16X8Abs},
+#line 179 "src/lexer-keywords.txt"
+ {"funcref", Type::FuncRef},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 170 "src/lexer-keywords.txt"
+ {"f64x2.splat", TokenType::Unary, Opcode::F64X2Splat},
+ {""}, {""}, {""}, {""}, {""},
+#line 516 "src/lexer-keywords.txt"
+ {"i8x16.relaxed_swizzle", TokenType::Binary, Opcode::I8X16RelaxedSwizzle},
+ {""},
+#line 481 "src/lexer-keywords.txt"
+ {"i64x2.splat", TokenType::Unary, Opcode::I64X2Splat},
+ {""}, {""}, {""}, {""},
+#line 592 "src/lexer-keywords.txt"
+ {"v128.load64_zero", TokenType::Load, Opcode::V128Load64Zero},
+#line 88 "src/lexer-keywords.txt"
+ {"f32x4.convert_i32x4_u", TokenType::Unary, Opcode::F32X4ConvertI32X4U},
+ {""},
+#line 87 "src/lexer-keywords.txt"
+ {"f32x4.convert_i32x4_s", TokenType::Unary, Opcode::F32X4ConvertI32X4S},
+ {""},
+#line 164 "src/lexer-keywords.txt"
+ {"f64x2.pmin", TokenType::Binary, Opcode::F64X2PMin},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""},
+#line 474 "src/lexer-keywords.txt"
+ {"i64x2.extend_low_i32x4_u", TokenType::Unary, Opcode::I64X2ExtendLowI32X4U},
+ {""},
+#line 472 "src/lexer-keywords.txt"
+ {"i64x2.extend_low_i32x4_s", TokenType::Unary, Opcode::I64X2ExtendLowI32X4S},
+ {""}, {""},
+#line 485 "src/lexer-keywords.txt"
+ {"i64x2.extmul_low_i32x4_u", TokenType::Binary, Opcode::I64X2ExtmulLowI32X4U},
+#line 184 "src/lexer-keywords.txt"
+ {"global", TokenType::Global},
+#line 483 "src/lexer-keywords.txt"
+ {"i64x2.extmul_low_i32x4_s", TokenType::Binary, Opcode::I64X2ExtmulLowI32X4S},
+ {""}, {""},
+#line 494 "src/lexer-keywords.txt"
+ {"i8x16.avgr_u", TokenType::Binary, Opcode::I8X16AvgrU},
+#line 498 "src/lexer-keywords.txt"
+ {"i8x16.extract_lane_u", TokenType::SimdLaneOp, Opcode::I8X16ExtractLaneU},
+ {""},
+#line 497 "src/lexer-keywords.txt"
+ {"i8x16.extract_lane_s", TokenType::SimdLaneOp, Opcode::I8X16ExtractLaneS},
+ {""}, {""}, {""}, {""},
+#line 358 "src/lexer-keywords.txt"
+ {"i32x4.trunc_sat_f32x4_u", TokenType::Unary, Opcode::I32X4TruncSatF32X4U},
+ {""}, {""}, {""},
+#line 357 "src/lexer-keywords.txt"
+ {"i32x4.trunc_sat_f32x4_s", TokenType::Unary, Opcode::I32X4TruncSatF32X4S},
+#line 332 "src/lexer-keywords.txt"
+ {"v128.load16x4_u", TokenType::Load, Opcode::V128Load16X4U},
+ {""}, {""}, {""},
+#line 331 "src/lexer-keywords.txt"
+ {"v128.load16x4_s", TokenType::Load, Opcode::V128Load16X4S},
+ {""}, {""}, {""}, {""},
+#line 395 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.cmpxchg", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmwCmpxchg},
+#line 258 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.cmpxchg", TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmwCmpxchg},
+ {""}, {""},
+#line 360 "src/lexer-keywords.txt"
+ {"i32x4.extend_high_i16x8_u", TokenType::Unary, Opcode::I32X4ExtendHighI16X8U},
+ {""},
+#line 359 "src/lexer-keywords.txt"
+ {"i32x4.extend_high_i16x8_s", TokenType::Unary, Opcode::I32X4ExtendHighI16X8S},
+ {""}, {""},
+#line 355 "src/lexer-keywords.txt"
+ {"i32x4.extmul_high_i16x8_u", TokenType::Binary, Opcode::I32X4ExtmulHighI16X8U},
+ {""},
+#line 353 "src/lexer-keywords.txt"
+ {"i32x4.extmul_high_i16x8_s", TokenType::Binary, Opcode::I32X4ExtmulHighI16X8S},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 182 "src/lexer-keywords.txt"
+ {"global.get", TokenType::GlobalGet, Opcode::GlobalGet},
+#line 471 "src/lexer-keywords.txt"
+ {"i64x2.bitmask", TokenType::Unary, Opcode::I64X2Bitmask},
+#line 183 "src/lexer-keywords.txt"
+ {"global.set", TokenType::GlobalSet, Opcode::GlobalSet},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""},
+#line 222 "src/lexer-keywords.txt"
+ {"i16x8.splat", TokenType::Unary, Opcode::I16X8Splat},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""},
+#line 388 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw8CmpxchgU},
+#line 251 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmw8CmpxchgU},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""},
+#line 114 "src/lexer-keywords.txt"
+ {"f32x4.demote_f64x2_zero", TokenType::Unary, Opcode::F32X4DemoteF64X2Zero},
+ {""}, {""}, {""},
+#line 364 "src/lexer-keywords.txt"
+ {"i32x4.trunc_sat_f64x2_u_zero", TokenType::Unary, Opcode::I32X4TruncSatF64X2UZero},
+ {""},
+#line 363 "src/lexer-keywords.txt"
+ {"i32x4.trunc_sat_f64x2_s_zero", TokenType::Unary, Opcode::I32X4TruncSatF64X2SZero},
+ {""}, {""},
+#line 609 "src/lexer-keywords.txt"
+ {"i8x16.swizzle", TokenType::Binary, Opcode::I8X16Swizzle},
+ {""}, {""}, {""}, {""}, {""},
+#line 585 "src/lexer-keywords.txt"
+ {"v128.bitselect", TokenType::Ternary, Opcode::V128BitSelect},
+ {""}, {""},
+#line 53 "src/lexer-keywords.txt"
+ {"export", TokenType::Export},
+#line 212 "src/lexer-keywords.txt"
+ {"i16x8.narrow_i32x4_u", TokenType::Binary, Opcode::I16X8NarrowI32X4U},
+ {""},
+#line 211 "src/lexer-keywords.txt"
+ {"i16x8.narrow_i32x4_s", TokenType::Binary, Opcode::I16X8NarrowI32X4S},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""},
+#line 601 "src/lexer-keywords.txt"
+ {"v128.load16_lane", TokenType::SimdLoadLane, Opcode::V128Load16Lane},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 374 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw16CmpxchgU},
+#line 244 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmw16CmpxchgU},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 191 "src/lexer-keywords.txt"
+ {"i16x8.bitmask", TokenType::Unary, Opcode::I16X8Bitmask},
+#line 217 "src/lexer-keywords.txt"
+ {"i16x8.relaxed_q15mulr_s", TokenType::Binary, Opcode::I16X8RelaxedQ15mulrS},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 596 "src/lexer-keywords.txt"
+ {"v128.load16_splat", TokenType::Load, Opcode::V128Load16Splat},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""},
+#line 521 "src/lexer-keywords.txt"
+ {"i8x16.shr_u", TokenType::Binary, Opcode::I8X16ShrU},
+ {""},
+#line 520 "src/lexer-keywords.txt"
+ {"i8x16.shr_s", TokenType::Binary, Opcode::I8X16ShrS},
+ {""}, {""},
+#line 143 "src/lexer-keywords.txt"
+ {"f64.sub", TokenType::Binary, Opcode::F64Sub},
+#line 81 "src/lexer-keywords.txt"
+ {"f32.sub", TokenType::Binary, Opcode::F32Sub},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 447 "src/lexer-keywords.txt"
+ {"i64.sub", TokenType::Binary, Opcode::I64Sub},
+#line 303 "src/lexer-keywords.txt"
+ {"i32.sub", TokenType::Binary, Opcode::I32Sub},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""},
+#line 236 "src/lexer-keywords.txt"
+ {"i16x8.extend_low_i8x16_u", TokenType::Unary, Opcode::I16X8ExtendLowI8X16U},
+ {""},
+#line 235 "src/lexer-keywords.txt"
+ {"i16x8.extend_low_i8x16_s", TokenType::Unary, Opcode::I16X8ExtendLowI8X16S},
+ {""}, {""},
+#line 230 "src/lexer-keywords.txt"
+ {"i16x8.extmul_low_i8x16_u", TokenType::Binary, Opcode::I16X8ExtmulLowI8X16U},
+ {""},
+#line 228 "src/lexer-keywords.txt"
+ {"i16x8.extmul_low_i8x16_s", TokenType::Binary, Opcode::I16X8ExtmulLowI8X16S},
+#line 163 "src/lexer-keywords.txt"
+ {"f64x2.pmax", TokenType::Binary, Opcode::F64X2PMax},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 362 "src/lexer-keywords.txt"
+ {"i32x4.extend_low_i16x8_u", TokenType::Unary, Opcode::I32X4ExtendLowI16X8U},
+ {""},
+#line 361 "src/lexer-keywords.txt"
+ {"i32x4.extend_low_i16x8_s", TokenType::Unary, Opcode::I32X4ExtendLowI16X8S},
+#line 35 "src/lexer-keywords.txt"
+ {"call_ref", TokenType::CallRef, Opcode::CallRef},
+ {""},
+#line 354 "src/lexer-keywords.txt"
+ {"i32x4.extmul_low_i16x8_u", TokenType::Binary, Opcode::I32X4ExtmulLowI16X8U},
+ {""},
+#line 352 "src/lexer-keywords.txt"
+ {"i32x4.extmul_low_i16x8_s", TokenType::Binary, Opcode::I32X4ExtmulLowI16X8S},
+#line 313 "src/lexer-keywords.txt"
+ {"i32.wrap_i64", TokenType::Convert, Opcode::I32WrapI64},
+ {""}, {""}, {""}, {""}, {""},
+#line 525 "src/lexer-keywords.txt"
+ {"i8x16.sub", TokenType::Binary, Opcode::I8X16Sub},
+#line 453 "src/lexer-keywords.txt"
+ {"i64.trunc_sat_f32_u", TokenType::Convert, Opcode::I64TruncSatF32U},
+#line 309 "src/lexer-keywords.txt"
+ {"i32.trunc_sat_f32_u", TokenType::Convert, Opcode::I32TruncSatF32U},
+ {""}, {""},
+#line 452 "src/lexer-keywords.txt"
+ {"i64.trunc_sat_f32_s", TokenType::Convert, Opcode::I64TruncSatF32S},
+#line 308 "src/lexer-keywords.txt"
+ {"i32.trunc_sat_f32_s", TokenType::Convert, Opcode::I32TruncSatF32S},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 318 "src/lexer-keywords.txt"
+ {"i32x4.dot_i8x16_i7x16_add_s", TokenType::Ternary, Opcode::I32X4DotI8X16I7X16AddS},
+ {""}, {""}, {""}, {""},
+#line 124 "src/lexer-keywords.txt"
+ {"f64.copysign", TokenType::Binary, Opcode::F64Copysign},
+#line 62 "src/lexer-keywords.txt"
+ {"f32.copysign", TokenType::Binary, Opcode::F32Copysign},
+ {""}, {""},
+#line 475 "src/lexer-keywords.txt"
+ {"i64x2.extend_high_i32x4_u", TokenType::Unary, Opcode::I64X2ExtendHighI32X4U},
+ {""},
+#line 473 "src/lexer-keywords.txt"
+ {"i64x2.extend_high_i32x4_s", TokenType::Unary, Opcode::I64X2ExtendHighI32X4S},
+ {""}, {""},
+#line 486 "src/lexer-keywords.txt"
+ {"i64x2.extmul_high_i32x4_u", TokenType::Binary, Opcode::I64X2ExtmulHighI32X4U},
+#line 489 "src/lexer-keywords.txt"
+ {"i8x16.abs", TokenType::Unary, Opcode::I8X16Abs},
+#line 484 "src/lexer-keywords.txt"
+ {"i64x2.extmul_high_i32x4_s", TokenType::Binary, Opcode::I64X2ExtmulHighI32X4S},
+ {""},
+#line 140 "src/lexer-keywords.txt"
+ {"f64.reinterpret_i64", TokenType::Convert, Opcode::F64ReinterpretI64},
+ {""},
+#line 20 "src/lexer-keywords.txt"
+ {"array", Type::Array, TokenType::Array},
+ {""}, {""}, {""}, {""},
+#line 400 "src/lexer-keywords.txt"
+ {"i64.atomic.store16", TokenType::AtomicStore, Opcode::I64AtomicStore16},
+#line 263 "src/lexer-keywords.txt"
+ {"i32.atomic.store16", TokenType::AtomicStore, Opcode::I32AtomicStore16},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""},
+#line 126 "src/lexer-keywords.txt"
+ {"f64.eq", TokenType::Compare, Opcode::F64Eq},
+#line 65 "src/lexer-keywords.txt"
+ {"f32.eq", TokenType::Compare, Opcode::F32Eq},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 409 "src/lexer-keywords.txt"
+ {"i64.eq", TokenType::Compare, Opcode::I64Eq},
+#line 271 "src/lexer-keywords.txt"
+ {"i32.eq", TokenType::Compare, Opcode::I32Eq},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""},
+#line 90 "src/lexer-keywords.txt"
+ {"f32x4.eq", TokenType::Compare, Opcode::F32X4Eq},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 319 "src/lexer-keywords.txt"
+ {"i32x4.eq", TokenType::Compare, Opcode::I32X4Eq},
+ {""},
+#line 22 "src/lexer-keywords.txt"
+ {"assert_exhaustion", TokenType::AssertExhaustion},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 44 "src/lexer-keywords.txt"
+ {"drop", TokenType::Drop, Opcode::Drop},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""},
+#line 23 "src/lexer-keywords.txt"
+ {"assert_invalid", TokenType::AssertInvalid},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 550 "src/lexer-keywords.txt"
+ {"nop", TokenType::Nop, Opcode::Nop},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""},
+#line 522 "src/lexer-keywords.txt"
+ {"i8x16.splat", TokenType::Unary, Opcode::I8X16Splat},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 536 "src/lexer-keywords.txt"
+ {"loop", TokenType::Loop, Opcode::Loop},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 538 "src/lexer-keywords.txt"
+ {"memory.atomic.wait32", TokenType::AtomicWait, Opcode::MemoryAtomicWait32},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 539 "src/lexer-keywords.txt"
+ {"memory.atomic.wait64", TokenType::AtomicWait, Opcode::MemoryAtomicWait64},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""},
+#line 397 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.sub", TokenType::AtomicRmw, Opcode::I64AtomicRmwSub},
+#line 260 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.sub", TokenType::AtomicRmw, Opcode::I32AtomicRmwSub},
+#line 495 "src/lexer-keywords.txt"
+ {"i8x16.bitmask", TokenType::Unary, Opcode::I8X16Bitmask},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 540 "src/lexer-keywords.txt"
+ {"memory.copy", TokenType::MemoryCopy, Opcode::MemoryCopy},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""},
+#line 514 "src/lexer-keywords.txt"
+ {"i8x16.popcnt", TokenType::Unary, Opcode::I8X16Popcnt},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""},
+#line 27 "src/lexer-keywords.txt"
+ {"assert_unlinkable", TokenType::AssertUnlinkable},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 608 "src/lexer-keywords.txt"
+ {"i8x16.shuffle", TokenType::SimdShuffleOp, Opcode::I8X16Shuffle},
+ {""}, {""}, {""}, {""},
+#line 434 "src/lexer-keywords.txt"
+ {"i64.popcnt", TokenType::Unary, Opcode::I64Popcnt},
+#line 291 "src/lexer-keywords.txt"
+ {"i32.popcnt", TokenType::Unary, Opcode::I32Popcnt},
+ {""}, {""}, {""}, {""},
+#line 175 "src/lexer-keywords.txt"
+ {"f64x2.convert_low_i32x4_u", TokenType::Unary, Opcode::F64X2ConvertLowI32X4U},
+ {""},
+#line 174 "src/lexer-keywords.txt"
+ {"f64x2.convert_low_i32x4_s", TokenType::Unary, Opcode::F64X2ConvertLowI32X4S},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""},
+#line 227 "src/lexer-keywords.txt"
+ {"i16x8.extadd_pairwise_i8x16_u", TokenType::Unary, Opcode::I16X8ExtaddPairwiseI8X16U},
+ {""},
+#line 226 "src/lexer-keywords.txt"
+ {"i16x8.extadd_pairwise_i8x16_s", TokenType::Unary, Opcode::I16X8ExtaddPairwiseI8X16S},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 537 "src/lexer-keywords.txt"
+ {"memory.atomic.notify", TokenType::AtomicNotify, Opcode::MemoryAtomicNotify},
+ {""}, {""},
+#line 26 "src/lexer-keywords.txt"
+ {"assert_trap", TokenType::AssertTrap},
+#line 78 "src/lexer-keywords.txt"
+ {"f32.reinterpret_i32", TokenType::Convert, Opcode::F32ReinterpretI32},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 139 "src/lexer-keywords.txt"
+ {"f64.promote_f32", TokenType::Convert, Opcode::F64PromoteF32},
+ {""}, {""}, {""}, {""},
+#line 192 "src/lexer-keywords.txt"
+ {"i16x8.dot_i8x16_i7x16_s", TokenType::Binary, Opcode::I16X8DotI8X16I7X16S},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 150 "src/lexer-keywords.txt"
+ {"f64x2.eq", TokenType::Compare, Opcode::F64X2Eq},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 462 "src/lexer-keywords.txt"
+ {"i64x2.eq", TokenType::Binary, Opcode::I64X2Eq},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 234 "src/lexer-keywords.txt"
+ {"i16x8.extend_high_i8x16_u", TokenType::Unary, Opcode::I16X8ExtendHighI8X16U},
+ {""},
+#line 233 "src/lexer-keywords.txt"
+ {"i16x8.extend_high_i8x16_s", TokenType::Unary, Opcode::I16X8ExtendHighI8X16S},
+ {""}, {""},
+#line 231 "src/lexer-keywords.txt"
+ {"i16x8.extmul_high_i8x16_u", TokenType::Binary, Opcode::I16X8ExtmulHighI8X16U},
+#line 21 "src/lexer-keywords.txt"
+ {"assert_exception", TokenType::AssertException},
+#line 229 "src/lexer-keywords.txt"
+ {"i16x8.extmul_high_i8x16_s", TokenType::Binary, Opcode::I16X8ExtmulHighI8X16S},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""},
+#line 435 "src/lexer-keywords.txt"
+ {"i64.reinterpret_f64", TokenType::Convert, Opcode::I64ReinterpretF64},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 193 "src/lexer-keywords.txt"
+ {"i16x8.eq", TokenType::Compare, Opcode::I16X8Eq},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 46 "src/lexer-keywords.txt"
+ {"elem.drop", TokenType::ElemDrop, Opcode::ElemDrop},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 214 "src/lexer-keywords.txt"
+ {"i16x8.q15mulr_sat_s", TokenType::Binary, Opcode::I16X8Q15mulrSatS},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 292 "src/lexer-keywords.txt"
+ {"i32.reinterpret_f32", TokenType::Convert, Opcode::I32ReinterpretF32},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""},
+#line 28 "src/lexer-keywords.txt"
+ {"atomic.fence", TokenType::AtomicFence, Opcode::AtomicFence},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""},
+#line 496 "src/lexer-keywords.txt"
+ {"i8x16.eq", TokenType::Compare, Opcode::I8X16Eq},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 512 "src/lexer-keywords.txt"
+ {"i8x16.narrow_i16x8_u", TokenType::Binary, Opcode::I8X16NarrowI16X8U},
+ {""},
+#line 511 "src/lexer-keywords.txt"
+ {"i8x16.narrow_i16x8_s", TokenType::Binary, Opcode::I8X16NarrowI16X8S},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 176 "src/lexer-keywords.txt"
+ {"f64x2.promote_low_f32x4", TokenType::Unary, Opcode::F64X2PromoteLowF32X4}
+ };
+
+ if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+ {
+ unsigned int key = hash (str, len);
+
+ if (key <= MAX_HASH_VALUE)
+ {
+ const char *s = wordlist[key].name;
+
+ if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0')
+ return &wordlist[key];
+ }
+ }
+ return 0;
+}
diff --git a/third_party/wasm2c/src/prebuilt/wasm2c_header_bottom.cc b/third_party/wasm2c/src/prebuilt/wasm2c_header_bottom.cc
new file mode 100644
index 0000000000..7db8468ff7
--- /dev/null
+++ b/third_party/wasm2c/src/prebuilt/wasm2c_header_bottom.cc
@@ -0,0 +1,7 @@
+const char* s_header_bottom = R"w2c_template(#ifdef __cplusplus
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(#endif
+)w2c_template"
+;
diff --git a/third_party/wasm2c/src/prebuilt/wasm2c_header_top.cc b/third_party/wasm2c/src/prebuilt/wasm2c_header_top.cc
new file mode 100644
index 0000000000..a72864c8db
--- /dev/null
+++ b/third_party/wasm2c/src/prebuilt/wasm2c_header_top.cc
@@ -0,0 +1,37 @@
+const char* s_header_top = R"w2c_template(#include <stdint.h>
+)w2c_template"
+R"w2c_template(
+#ifndef WASM_RT_CORE_TYPES_DEFINED
+)w2c_template"
+R"w2c_template(#define WASM_RT_CORE_TYPES_DEFINED
+)w2c_template"
+R"w2c_template(typedef uint8_t u8;
+)w2c_template"
+R"w2c_template(typedef int8_t s8;
+)w2c_template"
+R"w2c_template(typedef uint16_t u16;
+)w2c_template"
+R"w2c_template(typedef int16_t s16;
+)w2c_template"
+R"w2c_template(typedef uint32_t u32;
+)w2c_template"
+R"w2c_template(typedef int32_t s32;
+)w2c_template"
+R"w2c_template(typedef uint64_t u64;
+)w2c_template"
+R"w2c_template(typedef int64_t s64;
+)w2c_template"
+R"w2c_template(typedef float f32;
+)w2c_template"
+R"w2c_template(typedef double f64;
+)w2c_template"
+R"w2c_template(#endif
+)w2c_template"
+R"w2c_template(
+#ifdef __cplusplus
+)w2c_template"
+R"w2c_template(extern "C" {
+)w2c_template"
+R"w2c_template(#endif
+)w2c_template"
+;
diff --git a/third_party/wasm2c/src/prebuilt/wasm2c_simd_source_declarations.cc b/third_party/wasm2c/src/prebuilt/wasm2c_simd_source_declarations.cc
new file mode 100644
index 0000000000..cc8bd8c625
--- /dev/null
+++ b/third_party/wasm2c/src/prebuilt/wasm2c_simd_source_declarations.cc
@@ -0,0 +1,224 @@
+const char* s_simd_source_declarations = R"w2c_template(#ifdef __x86_64__
+)w2c_template"
+R"w2c_template(#define SIMD_FORCE_READ(var) wasm_asm("" ::"x"(var));
+)w2c_template"
+R"w2c_template(#else
+)w2c_template"
+R"w2c_template(#define SIMD_FORCE_READ(var)
+)w2c_template"
+R"w2c_template(#endif
+)w2c_template"
+R"w2c_template(// TODO: equivalent constraint for ARM and other architectures
+)w2c_template"
+R"w2c_template(
+#define DEFINE_SIMD_LOAD_FUNC(name, func, t) \
+)w2c_template"
+R"w2c_template( static inline v128 name(wasm_rt_memory_t* mem, u64 addr) { \
+)w2c_template"
+R"w2c_template( MEMCHECK(mem, addr, t); \
+)w2c_template"
+R"w2c_template( v128 result = func((v128*)&mem->data[addr]); \
+)w2c_template"
+R"w2c_template( SIMD_FORCE_READ(result); \
+)w2c_template"
+R"w2c_template( return result; \
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(
+#define DEFINE_SIMD_LOAD_LANE(name, func, t, lane) \
+)w2c_template"
+R"w2c_template( static inline v128 name(wasm_rt_memory_t* mem, u64 addr, v128 vec) { \
+)w2c_template"
+R"w2c_template( MEMCHECK(mem, addr, t); \
+)w2c_template"
+R"w2c_template( v128 result = func((v128*)&mem->data[addr], vec, lane); \
+)w2c_template"
+R"w2c_template( SIMD_FORCE_READ(result); \
+)w2c_template"
+R"w2c_template( return result; \
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(
+#define DEFINE_SIMD_STORE(name, t) \
+)w2c_template"
+R"w2c_template( static inline void name(wasm_rt_memory_t* mem, u64 addr, v128 value) { \
+)w2c_template"
+R"w2c_template( MEMCHECK(mem, addr, t); \
+)w2c_template"
+R"w2c_template( simde_wasm_v128_store((v128*)&mem->data[addr], value); \
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(
+#define DEFINE_SIMD_STORE_LANE(name, func, t, lane) \
+)w2c_template"
+R"w2c_template( static inline void name(wasm_rt_memory_t* mem, u64 addr, v128 value) { \
+)w2c_template"
+R"w2c_template( MEMCHECK(mem, addr, t); \
+)w2c_template"
+R"w2c_template( func((v128*)&mem->data[addr], value, lane); \
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(
+// clang-format off
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_FUNC(v128_load, simde_wasm_v128_load, v128)
+)w2c_template"
+R"w2c_template(
+DEFINE_SIMD_LOAD_FUNC(v128_load8_splat, simde_wasm_v128_load8_splat, u8)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_FUNC(v128_load16_splat, simde_wasm_v128_load16_splat, u16)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_FUNC(v128_load32_splat, simde_wasm_v128_load32_splat, u32)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_FUNC(v128_load64_splat, simde_wasm_v128_load64_splat, u64)
+)w2c_template"
+R"w2c_template(
+DEFINE_SIMD_LOAD_FUNC(i16x8_load8x8, simde_wasm_i16x8_load8x8, u64)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_FUNC(u16x8_load8x8, simde_wasm_u16x8_load8x8, u64)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_FUNC(i32x4_load16x4, simde_wasm_i32x4_load16x4, u64)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_FUNC(u32x4_load16x4, simde_wasm_u32x4_load16x4, u64)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_FUNC(i64x2_load32x2, simde_wasm_i64x2_load32x2, u64)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_FUNC(u64x2_load32x2, simde_wasm_u64x2_load32x2, u64)
+)w2c_template"
+R"w2c_template(
+DEFINE_SIMD_LOAD_FUNC(v128_load32_zero, simde_wasm_v128_load32_zero, u32)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_FUNC(v128_load64_zero, simde_wasm_v128_load64_zero, u64)
+)w2c_template"
+R"w2c_template(
+DEFINE_SIMD_LOAD_LANE(v128_load8_lane0, simde_wasm_v128_load8_lane, u8, 0)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane1, simde_wasm_v128_load8_lane, u8, 1)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane2, simde_wasm_v128_load8_lane, u8, 2)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane3, simde_wasm_v128_load8_lane, u8, 3)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane4, simde_wasm_v128_load8_lane, u8, 4)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane5, simde_wasm_v128_load8_lane, u8, 5)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane6, simde_wasm_v128_load8_lane, u8, 6)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane7, simde_wasm_v128_load8_lane, u8, 7)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane8, simde_wasm_v128_load8_lane, u8, 8)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane9, simde_wasm_v128_load8_lane, u8, 9)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane10, simde_wasm_v128_load8_lane, u8, 10)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane11, simde_wasm_v128_load8_lane, u8, 11)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane12, simde_wasm_v128_load8_lane, u8, 12)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane13, simde_wasm_v128_load8_lane, u8, 13)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane14, simde_wasm_v128_load8_lane, u8, 14)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane15, simde_wasm_v128_load8_lane, u8, 15)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane0, simde_wasm_v128_load16_lane, u16, 0)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane1, simde_wasm_v128_load16_lane, u16, 1)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane2, simde_wasm_v128_load16_lane, u16, 2)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane3, simde_wasm_v128_load16_lane, u16, 3)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane4, simde_wasm_v128_load16_lane, u16, 4)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane5, simde_wasm_v128_load16_lane, u16, 5)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane6, simde_wasm_v128_load16_lane, u16, 6)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane7, simde_wasm_v128_load16_lane, u16, 7)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load32_lane0, simde_wasm_v128_load32_lane, u32, 0)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load32_lane1, simde_wasm_v128_load32_lane, u32, 1)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load32_lane2, simde_wasm_v128_load32_lane, u32, 2)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load32_lane3, simde_wasm_v128_load32_lane, u32, 3)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load64_lane0, simde_wasm_v128_load64_lane, u64, 0)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load64_lane1, simde_wasm_v128_load64_lane, u64, 1)
+)w2c_template"
+R"w2c_template(
+DEFINE_SIMD_STORE(v128_store, v128)
+)w2c_template"
+R"w2c_template(
+DEFINE_SIMD_STORE_LANE(v128_store8_lane0, simde_wasm_v128_store8_lane, u8, 0)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane1, simde_wasm_v128_store8_lane, u8, 1)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane2, simde_wasm_v128_store8_lane, u8, 2)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane3, simde_wasm_v128_store8_lane, u8, 3)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane4, simde_wasm_v128_store8_lane, u8, 4)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane5, simde_wasm_v128_store8_lane, u8, 5)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane6, simde_wasm_v128_store8_lane, u8, 6)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane7, simde_wasm_v128_store8_lane, u8, 7)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane8, simde_wasm_v128_store8_lane, u8, 8)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane9, simde_wasm_v128_store8_lane, u8, 9)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane10, simde_wasm_v128_store8_lane, u8, 10)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane11, simde_wasm_v128_store8_lane, u8, 11)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane12, simde_wasm_v128_store8_lane, u8, 12)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane13, simde_wasm_v128_store8_lane, u8, 13)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane14, simde_wasm_v128_store8_lane, u8, 14)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane15, simde_wasm_v128_store8_lane, u8, 15)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane0, simde_wasm_v128_store16_lane, u16, 0)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane1, simde_wasm_v128_store16_lane, u16, 1)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane2, simde_wasm_v128_store16_lane, u16, 2)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane3, simde_wasm_v128_store16_lane, u16, 3)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane4, simde_wasm_v128_store16_lane, u16, 4)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane5, simde_wasm_v128_store16_lane, u16, 5)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane6, simde_wasm_v128_store16_lane, u16, 6)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane7, simde_wasm_v128_store16_lane, u16, 7)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store32_lane0, simde_wasm_v128_store32_lane, u32, 0)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store32_lane1, simde_wasm_v128_store32_lane, u32, 1)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store32_lane2, simde_wasm_v128_store32_lane, u32, 2)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store32_lane3, simde_wasm_v128_store32_lane, u32, 3)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store64_lane0, simde_wasm_v128_store64_lane, u64, 0)
+)w2c_template"
+R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store64_lane1, simde_wasm_v128_store64_lane, u64, 1)
+)w2c_template"
+R"w2c_template(// clang-format on
+)w2c_template"
+;
diff --git a/third_party/wasm2c/src/prebuilt/wasm2c_source_declarations.cc b/third_party/wasm2c/src/prebuilt/wasm2c_source_declarations.cc
new file mode 100644
index 0000000000..ca541778e4
--- /dev/null
+++ b/third_party/wasm2c/src/prebuilt/wasm2c_source_declarations.cc
@@ -0,0 +1,1110 @@
+const char* s_source_declarations = R"w2c_template(
+#define TRAP(x) (wasm_rt_trap(WASM_RT_TRAP_##x), 0)
+)w2c_template"
+R"w2c_template(
+#if WASM_RT_USE_STACK_DEPTH_COUNT
+)w2c_template"
+R"w2c_template(#define FUNC_PROLOGUE \
+)w2c_template"
+R"w2c_template( if (++wasm_rt_call_stack_depth > WASM_RT_MAX_CALL_STACK_DEPTH) \
+)w2c_template"
+R"w2c_template( TRAP(EXHAUSTION);
+)w2c_template"
+R"w2c_template(
+#define FUNC_EPILOGUE --wasm_rt_call_stack_depth
+)w2c_template"
+R"w2c_template(#else
+)w2c_template"
+R"w2c_template(#define FUNC_PROLOGUE
+)w2c_template"
+R"w2c_template(
+#define FUNC_EPILOGUE
+)w2c_template"
+R"w2c_template(#endif
+)w2c_template"
+R"w2c_template(
+#define UNREACHABLE TRAP(UNREACHABLE)
+)w2c_template"
+R"w2c_template(
+static inline bool func_types_eq(const wasm_rt_func_type_t a,
+)w2c_template"
+R"w2c_template( const wasm_rt_func_type_t b) {
+)w2c_template"
+R"w2c_template( return (a == b) || LIKELY(a && b && !memcmp(a, b, 32));
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+#define CALL_INDIRECT(table, t, ft, x, ...) \
+)w2c_template"
+R"w2c_template( (LIKELY((x) < table.size && table.data[x].func && \
+)w2c_template"
+R"w2c_template( func_types_eq(ft, table.data[x].func_type)) || \
+)w2c_template"
+R"w2c_template( TRAP(CALL_INDIRECT), \
+)w2c_template"
+R"w2c_template( ((t)table.data[x].func)(__VA_ARGS__))
+)w2c_template"
+R"w2c_template(
+#ifdef SUPPORT_MEMORY64
+)w2c_template"
+R"w2c_template(#define RANGE_CHECK(mem, offset, len) \
+)w2c_template"
+R"w2c_template( do { \
+)w2c_template"
+R"w2c_template( uint64_t res; \
+)w2c_template"
+R"w2c_template( if (__builtin_add_overflow(offset, len, &res)) \
+)w2c_template"
+R"w2c_template( TRAP(OOB); \
+)w2c_template"
+R"w2c_template( if (UNLIKELY(res > mem->size)) \
+)w2c_template"
+R"w2c_template( TRAP(OOB); \
+)w2c_template"
+R"w2c_template( } while (0);
+)w2c_template"
+R"w2c_template(#else
+)w2c_template"
+R"w2c_template(#define RANGE_CHECK(mem, offset, len) \
+)w2c_template"
+R"w2c_template( if (UNLIKELY(offset + (uint64_t)len > mem->size)) \
+)w2c_template"
+R"w2c_template( TRAP(OOB);
+)w2c_template"
+R"w2c_template(#endif
+)w2c_template"
+R"w2c_template(
+#if WASM_RT_MEMCHECK_GUARD_PAGES
+)w2c_template"
+R"w2c_template(#define MEMCHECK(mem, a, t)
+)w2c_template"
+R"w2c_template(#else
+)w2c_template"
+R"w2c_template(#define MEMCHECK(mem, a, t) RANGE_CHECK(mem, a, sizeof(t))
+)w2c_template"
+R"w2c_template(#endif
+)w2c_template"
+R"w2c_template(
+#ifdef __GNUC__
+)w2c_template"
+R"w2c_template(#define wasm_asm __asm__
+)w2c_template"
+R"w2c_template(#else
+)w2c_template"
+R"w2c_template(#define wasm_asm(X)
+)w2c_template"
+R"w2c_template(#endif
+)w2c_template"
+R"w2c_template(
+#if WABT_BIG_ENDIAN
+)w2c_template"
+R"w2c_template(static inline void load_data(void* dest, const void* src, size_t n) {
+)w2c_template"
+R"w2c_template( if (!n) {
+)w2c_template"
+R"w2c_template( return;
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( size_t i = 0;
+)w2c_template"
+R"w2c_template( u8* dest_chars = dest;
+)w2c_template"
+R"w2c_template( memcpy(dest, src, n);
+)w2c_template"
+R"w2c_template( for (i = 0; i < (n >> 1); i++) {
+)w2c_template"
+R"w2c_template( u8 cursor = dest_chars[i];
+)w2c_template"
+R"w2c_template( dest_chars[i] = dest_chars[n - i - 1];
+)w2c_template"
+R"w2c_template( dest_chars[n - i - 1] = cursor;
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(#define LOAD_DATA(m, o, i, s) \
+)w2c_template"
+R"w2c_template( do { \
+)w2c_template"
+R"w2c_template( RANGE_CHECK((&m), m.size - o - s, s); \
+)w2c_template"
+R"w2c_template( load_data(&(m.data[m.size - o - s]), i, s); \
+)w2c_template"
+R"w2c_template( } while (0)
+)w2c_template"
+R"w2c_template(#define DEFINE_LOAD(name, t1, t2, t3) \
+)w2c_template"
+R"w2c_template( static inline t3 name(wasm_rt_memory_t* mem, u64 addr) { \
+)w2c_template"
+R"w2c_template( MEMCHECK(mem, addr, t1); \
+)w2c_template"
+R"w2c_template( t1 result; \
+)w2c_template"
+R"w2c_template( wasm_rt_memcpy(&result, &mem->data[mem->size - addr - sizeof(t1)], \
+)w2c_template"
+R"w2c_template( sizeof(t1)); \
+)w2c_template"
+R"w2c_template( wasm_asm("" ::"r"(result)); \
+)w2c_template"
+R"w2c_template( return (t3)(t2)result; \
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(
+#define DEFINE_STORE(name, t1, t2) \
+)w2c_template"
+R"w2c_template( static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value) { \
+)w2c_template"
+R"w2c_template( MEMCHECK(mem, addr, t1); \
+)w2c_template"
+R"w2c_template( t1 wrapped = (t1)value; \
+)w2c_template"
+R"w2c_template( wasm_rt_memcpy(&mem->data[mem->size - addr - sizeof(t1)], &wrapped, \
+)w2c_template"
+R"w2c_template( sizeof(t1)); \
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(#else
+)w2c_template"
+R"w2c_template(static inline void load_data(void* dest, const void* src, size_t n) {
+)w2c_template"
+R"w2c_template( if (!n) {
+)w2c_template"
+R"w2c_template( return;
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( memcpy(dest, src, n);
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(#define LOAD_DATA(m, o, i, s) \
+)w2c_template"
+R"w2c_template( do { \
+)w2c_template"
+R"w2c_template( RANGE_CHECK((&m), o, s); \
+)w2c_template"
+R"w2c_template( load_data(&(m.data[o]), i, s); \
+)w2c_template"
+R"w2c_template( } while (0)
+)w2c_template"
+R"w2c_template(#define DEFINE_LOAD(name, t1, t2, t3) \
+)w2c_template"
+R"w2c_template( static inline t3 name(wasm_rt_memory_t* mem, u64 addr) { \
+)w2c_template"
+R"w2c_template( MEMCHECK(mem, addr, t1); \
+)w2c_template"
+R"w2c_template( t1 result; \
+)w2c_template"
+R"w2c_template( wasm_rt_memcpy(&result, &mem->data[addr], sizeof(t1)); \
+)w2c_template"
+R"w2c_template( wasm_asm("" ::"r"(result)); \
+)w2c_template"
+R"w2c_template( return (t3)(t2)result; \
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(
+#define DEFINE_STORE(name, t1, t2) \
+)w2c_template"
+R"w2c_template( static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value) { \
+)w2c_template"
+R"w2c_template( MEMCHECK(mem, addr, t1); \
+)w2c_template"
+R"w2c_template( t1 wrapped = (t1)value; \
+)w2c_template"
+R"w2c_template( wasm_rt_memcpy(&mem->data[addr], &wrapped, sizeof(t1)); \
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(#endif
+)w2c_template"
+R"w2c_template(
+DEFINE_LOAD(i32_load, u32, u32, u32)
+)w2c_template"
+R"w2c_template(DEFINE_LOAD(i64_load, u64, u64, u64)
+)w2c_template"
+R"w2c_template(DEFINE_LOAD(f32_load, f32, f32, f32)
+)w2c_template"
+R"w2c_template(DEFINE_LOAD(f64_load, f64, f64, f64)
+)w2c_template"
+R"w2c_template(DEFINE_LOAD(i32_load8_s, s8, s32, u32)
+)w2c_template"
+R"w2c_template(DEFINE_LOAD(i64_load8_s, s8, s64, u64)
+)w2c_template"
+R"w2c_template(DEFINE_LOAD(i32_load8_u, u8, u32, u32)
+)w2c_template"
+R"w2c_template(DEFINE_LOAD(i64_load8_u, u8, u64, u64)
+)w2c_template"
+R"w2c_template(DEFINE_LOAD(i32_load16_s, s16, s32, u32)
+)w2c_template"
+R"w2c_template(DEFINE_LOAD(i64_load16_s, s16, s64, u64)
+)w2c_template"
+R"w2c_template(DEFINE_LOAD(i32_load16_u, u16, u32, u32)
+)w2c_template"
+R"w2c_template(DEFINE_LOAD(i64_load16_u, u16, u64, u64)
+)w2c_template"
+R"w2c_template(DEFINE_LOAD(i64_load32_s, s32, s64, u64)
+)w2c_template"
+R"w2c_template(DEFINE_LOAD(i64_load32_u, u32, u64, u64)
+)w2c_template"
+R"w2c_template(DEFINE_STORE(i32_store, u32, u32)
+)w2c_template"
+R"w2c_template(DEFINE_STORE(i64_store, u64, u64)
+)w2c_template"
+R"w2c_template(DEFINE_STORE(f32_store, f32, f32)
+)w2c_template"
+R"w2c_template(DEFINE_STORE(f64_store, f64, f64)
+)w2c_template"
+R"w2c_template(DEFINE_STORE(i32_store8, u8, u32)
+)w2c_template"
+R"w2c_template(DEFINE_STORE(i32_store16, u16, u32)
+)w2c_template"
+R"w2c_template(DEFINE_STORE(i64_store8, u8, u64)
+)w2c_template"
+R"w2c_template(DEFINE_STORE(i64_store16, u16, u64)
+)w2c_template"
+R"w2c_template(DEFINE_STORE(i64_store32, u32, u64)
+)w2c_template"
+R"w2c_template(
+#if defined(_MSC_VER)
+)w2c_template"
+R"w2c_template(
+// Adapted from
+)w2c_template"
+R"w2c_template(// https://github.com/nemequ/portable-snippets/blob/master/builtin/builtin.h
+)w2c_template"
+R"w2c_template(
+static inline int I64_CLZ(unsigned long long v) {
+)w2c_template"
+R"w2c_template( unsigned long r = 0;
+)w2c_template"
+R"w2c_template(#if defined(_M_AMD64) || defined(_M_ARM)
+)w2c_template"
+R"w2c_template( if (_BitScanReverse64(&r, v)) {
+)w2c_template"
+R"w2c_template( return 63 - r;
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(#else
+)w2c_template"
+R"w2c_template( if (_BitScanReverse(&r, (unsigned long)(v >> 32))) {
+)w2c_template"
+R"w2c_template( return 31 - r;
+)w2c_template"
+R"w2c_template( } else if (_BitScanReverse(&r, (unsigned long)v)) {
+)w2c_template"
+R"w2c_template( return 63 - r;
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(#endif
+)w2c_template"
+R"w2c_template( return 64;
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static inline int I32_CLZ(unsigned long v) {
+)w2c_template"
+R"w2c_template( unsigned long r = 0;
+)w2c_template"
+R"w2c_template( if (_BitScanReverse(&r, v)) {
+)w2c_template"
+R"w2c_template( return 31 - r;
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( return 32;
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static inline int I64_CTZ(unsigned long long v) {
+)w2c_template"
+R"w2c_template( if (!v) {
+)w2c_template"
+R"w2c_template( return 64;
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( unsigned long r = 0;
+)w2c_template"
+R"w2c_template(#if defined(_M_AMD64) || defined(_M_ARM)
+)w2c_template"
+R"w2c_template( _BitScanForward64(&r, v);
+)w2c_template"
+R"w2c_template( return (int)r;
+)w2c_template"
+R"w2c_template(#else
+)w2c_template"
+R"w2c_template( if (_BitScanForward(&r, (unsigned int)(v))) {
+)w2c_template"
+R"w2c_template( return (int)(r);
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(
+ _BitScanForward(&r, (unsigned int)(v >> 32));
+)w2c_template"
+R"w2c_template( return (int)(r + 32);
+)w2c_template"
+R"w2c_template(#endif
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static inline int I32_CTZ(unsigned long v) {
+)w2c_template"
+R"w2c_template( if (!v) {
+)w2c_template"
+R"w2c_template( return 32;
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( unsigned long r = 0;
+)w2c_template"
+R"w2c_template( _BitScanForward(&r, v);
+)w2c_template"
+R"w2c_template( return (int)r;
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+#define POPCOUNT_DEFINE_PORTABLE(f_n, T) \
+)w2c_template"
+R"w2c_template( static inline u32 f_n(T x) { \
+)w2c_template"
+R"w2c_template( x = x - ((x >> 1) & (T) ~(T)0 / 3); \
+)w2c_template"
+R"w2c_template( x = (x & (T) ~(T)0 / 15 * 3) + ((x >> 2) & (T) ~(T)0 / 15 * 3); \
+)w2c_template"
+R"w2c_template( x = (x + (x >> 4)) & (T) ~(T)0 / 255 * 15; \
+)w2c_template"
+R"w2c_template( return (T)(x * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8; \
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(
+POPCOUNT_DEFINE_PORTABLE(I32_POPCNT, u32)
+)w2c_template"
+R"w2c_template(POPCOUNT_DEFINE_PORTABLE(I64_POPCNT, u64)
+)w2c_template"
+R"w2c_template(
+#undef POPCOUNT_DEFINE_PORTABLE
+)w2c_template"
+R"w2c_template(
+#else
+)w2c_template"
+R"w2c_template(
+#define I32_CLZ(x) ((x) ? __builtin_clz(x) : 32)
+)w2c_template"
+R"w2c_template(#define I64_CLZ(x) ((x) ? __builtin_clzll(x) : 64)
+)w2c_template"
+R"w2c_template(#define I32_CTZ(x) ((x) ? __builtin_ctz(x) : 32)
+)w2c_template"
+R"w2c_template(#define I64_CTZ(x) ((x) ? __builtin_ctzll(x) : 64)
+)w2c_template"
+R"w2c_template(#define I32_POPCNT(x) (__builtin_popcount(x))
+)w2c_template"
+R"w2c_template(#define I64_POPCNT(x) (__builtin_popcountll(x))
+)w2c_template"
+R"w2c_template(
+#endif
+)w2c_template"
+R"w2c_template(
+#define DIV_S(ut, min, x, y) \
+)w2c_template"
+R"w2c_template( ((UNLIKELY((y) == 0)) \
+)w2c_template"
+R"w2c_template( ? TRAP(DIV_BY_ZERO) \
+)w2c_template"
+R"w2c_template( : (UNLIKELY((x) == min && (y) == -1)) ? TRAP(INT_OVERFLOW) \
+)w2c_template"
+R"w2c_template( : (ut)((x) / (y)))
+)w2c_template"
+R"w2c_template(
+#define REM_S(ut, min, x, y) \
+)w2c_template"
+R"w2c_template( ((UNLIKELY((y) == 0)) \
+)w2c_template"
+R"w2c_template( ? TRAP(DIV_BY_ZERO) \
+)w2c_template"
+R"w2c_template( : (UNLIKELY((x) == min && (y) == -1)) ? 0 : (ut)((x) % (y)))
+)w2c_template"
+R"w2c_template(
+#define I32_DIV_S(x, y) DIV_S(u32, INT32_MIN, (s32)x, (s32)y)
+)w2c_template"
+R"w2c_template(#define I64_DIV_S(x, y) DIV_S(u64, INT64_MIN, (s64)x, (s64)y)
+)w2c_template"
+R"w2c_template(#define I32_REM_S(x, y) REM_S(u32, INT32_MIN, (s32)x, (s32)y)
+)w2c_template"
+R"w2c_template(#define I64_REM_S(x, y) REM_S(u64, INT64_MIN, (s64)x, (s64)y)
+)w2c_template"
+R"w2c_template(
+#define DIVREM_U(op, x, y) \
+)w2c_template"
+R"w2c_template( ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) : ((x)op(y)))
+)w2c_template"
+R"w2c_template(
+#define DIV_U(x, y) DIVREM_U(/, x, y)
+)w2c_template"
+R"w2c_template(#define REM_U(x, y) DIVREM_U(%, x, y)
+)w2c_template"
+R"w2c_template(
+#define ROTL(x, y, mask) \
+)w2c_template"
+R"w2c_template( (((x) << ((y) & (mask))) | ((x) >> (((mask) - (y) + 1) & (mask))))
+)w2c_template"
+R"w2c_template(#define ROTR(x, y, mask) \
+)w2c_template"
+R"w2c_template( (((x) >> ((y) & (mask))) | ((x) << (((mask) - (y) + 1) & (mask))))
+)w2c_template"
+R"w2c_template(
+#define I32_ROTL(x, y) ROTL(x, y, 31)
+)w2c_template"
+R"w2c_template(#define I64_ROTL(x, y) ROTL(x, y, 63)
+)w2c_template"
+R"w2c_template(#define I32_ROTR(x, y) ROTR(x, y, 31)
+)w2c_template"
+R"w2c_template(#define I64_ROTR(x, y) ROTR(x, y, 63)
+)w2c_template"
+R"w2c_template(
+#define FMIN(x, y) \
+)w2c_template"
+R"w2c_template( ((UNLIKELY((x) != (x))) \
+)w2c_template"
+R"w2c_template( ? NAN \
+)w2c_template"
+R"w2c_template( : (UNLIKELY((y) != (y))) \
+)w2c_template"
+R"w2c_template( ? NAN \
+)w2c_template"
+R"w2c_template( : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? x : y) \
+)w2c_template"
+R"w2c_template( : (x < y) ? x : y)
+)w2c_template"
+R"w2c_template(
+#define FMAX(x, y) \
+)w2c_template"
+R"w2c_template( ((UNLIKELY((x) != (x))) \
+)w2c_template"
+R"w2c_template( ? NAN \
+)w2c_template"
+R"w2c_template( : (UNLIKELY((y) != (y))) \
+)w2c_template"
+R"w2c_template( ? NAN \
+)w2c_template"
+R"w2c_template( : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? y : x) \
+)w2c_template"
+R"w2c_template( : (x > y) ? x : y)
+)w2c_template"
+R"w2c_template(
+#define TRUNC_S(ut, st, ft, min, minop, max, x) \
+)w2c_template"
+R"w2c_template( ((UNLIKELY((x) != (x))) \
+)w2c_template"
+R"w2c_template( ? TRAP(INVALID_CONVERSION) \
+)w2c_template"
+R"w2c_template( : (UNLIKELY(!((x)minop(min) && (x) < (max)))) ? TRAP(INT_OVERFLOW) \
+)w2c_template"
+R"w2c_template( : (ut)(st)(x))
+)w2c_template"
+R"w2c_template(
+#define I32_TRUNC_S_F32(x) \
+)w2c_template"
+R"w2c_template( TRUNC_S(u32, s32, f32, (f32)INT32_MIN, >=, 2147483648.f, x)
+)w2c_template"
+R"w2c_template(#define I64_TRUNC_S_F32(x) \
+)w2c_template"
+R"w2c_template( TRUNC_S(u64, s64, f32, (f32)INT64_MIN, >=, (f32)INT64_MAX, x)
+)w2c_template"
+R"w2c_template(#define I32_TRUNC_S_F64(x) \
+)w2c_template"
+R"w2c_template( TRUNC_S(u32, s32, f64, -2147483649., >, 2147483648., x)
+)w2c_template"
+R"w2c_template(#define I64_TRUNC_S_F64(x) \
+)w2c_template"
+R"w2c_template( TRUNC_S(u64, s64, f64, (f64)INT64_MIN, >=, (f64)INT64_MAX, x)
+)w2c_template"
+R"w2c_template(
+#define TRUNC_U(ut, ft, max, x) \
+)w2c_template"
+R"w2c_template( ((UNLIKELY((x) != (x))) \
+)w2c_template"
+R"w2c_template( ? TRAP(INVALID_CONVERSION) \
+)w2c_template"
+R"w2c_template( : (UNLIKELY(!((x) > (ft)-1 && (x) < (max)))) ? TRAP(INT_OVERFLOW) \
+)w2c_template"
+R"w2c_template( : (ut)(x))
+)w2c_template"
+R"w2c_template(
+#define I32_TRUNC_U_F32(x) TRUNC_U(u32, f32, 4294967296.f, x)
+)w2c_template"
+R"w2c_template(#define I64_TRUNC_U_F32(x) TRUNC_U(u64, f32, (f32)UINT64_MAX, x)
+)w2c_template"
+R"w2c_template(#define I32_TRUNC_U_F64(x) TRUNC_U(u32, f64, 4294967296., x)
+)w2c_template"
+R"w2c_template(#define I64_TRUNC_U_F64(x) TRUNC_U(u64, f64, (f64)UINT64_MAX, x)
+)w2c_template"
+R"w2c_template(
+#define TRUNC_SAT_S(ut, st, ft, min, smin, minop, max, smax, x) \
+)w2c_template"
+R"w2c_template( ((UNLIKELY((x) != (x))) \
+)w2c_template"
+R"w2c_template( ? 0 \
+)w2c_template"
+R"w2c_template( : (UNLIKELY(!((x)minop(min)))) \
+)w2c_template"
+R"w2c_template( ? smin \
+)w2c_template"
+R"w2c_template( : (UNLIKELY(!((x) < (max)))) ? smax : (ut)(st)(x))
+)w2c_template"
+R"w2c_template(
+#define I32_TRUNC_SAT_S_F32(x) \
+)w2c_template"
+R"w2c_template( TRUNC_SAT_S(u32, s32, f32, (f32)INT32_MIN, INT32_MIN, >=, 2147483648.f, \
+)w2c_template"
+R"w2c_template( INT32_MAX, x)
+)w2c_template"
+R"w2c_template(#define I64_TRUNC_SAT_S_F32(x) \
+)w2c_template"
+R"w2c_template( TRUNC_SAT_S(u64, s64, f32, (f32)INT64_MIN, INT64_MIN, >=, (f32)INT64_MAX, \
+)w2c_template"
+R"w2c_template( INT64_MAX, x)
+)w2c_template"
+R"w2c_template(#define I32_TRUNC_SAT_S_F64(x) \
+)w2c_template"
+R"w2c_template( TRUNC_SAT_S(u32, s32, f64, -2147483649., INT32_MIN, >, 2147483648., \
+)w2c_template"
+R"w2c_template( INT32_MAX, x)
+)w2c_template"
+R"w2c_template(#define I64_TRUNC_SAT_S_F64(x) \
+)w2c_template"
+R"w2c_template( TRUNC_SAT_S(u64, s64, f64, (f64)INT64_MIN, INT64_MIN, >=, (f64)INT64_MAX, \
+)w2c_template"
+R"w2c_template( INT64_MAX, x)
+)w2c_template"
+R"w2c_template(
+#define TRUNC_SAT_U(ut, ft, max, smax, x) \
+)w2c_template"
+R"w2c_template( ((UNLIKELY((x) != (x))) ? 0 \
+)w2c_template"
+R"w2c_template( : (UNLIKELY(!((x) > (ft)-1))) \
+)w2c_template"
+R"w2c_template( ? 0 \
+)w2c_template"
+R"w2c_template( : (UNLIKELY(!((x) < (max)))) ? smax : (ut)(x))
+)w2c_template"
+R"w2c_template(
+#define I32_TRUNC_SAT_U_F32(x) \
+)w2c_template"
+R"w2c_template( TRUNC_SAT_U(u32, f32, 4294967296.f, UINT32_MAX, x)
+)w2c_template"
+R"w2c_template(#define I64_TRUNC_SAT_U_F32(x) \
+)w2c_template"
+R"w2c_template( TRUNC_SAT_U(u64, f32, (f32)UINT64_MAX, UINT64_MAX, x)
+)w2c_template"
+R"w2c_template(#define I32_TRUNC_SAT_U_F64(x) TRUNC_SAT_U(u32, f64, 4294967296., UINT32_MAX, x)
+)w2c_template"
+R"w2c_template(#define I64_TRUNC_SAT_U_F64(x) \
+)w2c_template"
+R"w2c_template( TRUNC_SAT_U(u64, f64, (f64)UINT64_MAX, UINT64_MAX, x)
+)w2c_template"
+R"w2c_template(
+#define DEFINE_REINTERPRET(name, t1, t2) \
+)w2c_template"
+R"w2c_template( static inline t2 name(t1 x) { \
+)w2c_template"
+R"w2c_template( t2 result; \
+)w2c_template"
+R"w2c_template( memcpy(&result, &x, sizeof(result)); \
+)w2c_template"
+R"w2c_template( return result; \
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(
+DEFINE_REINTERPRET(f32_reinterpret_i32, u32, f32)
+)w2c_template"
+R"w2c_template(DEFINE_REINTERPRET(i32_reinterpret_f32, f32, u32)
+)w2c_template"
+R"w2c_template(DEFINE_REINTERPRET(f64_reinterpret_i64, u64, f64)
+)w2c_template"
+R"w2c_template(DEFINE_REINTERPRET(i64_reinterpret_f64, f64, u64)
+)w2c_template"
+R"w2c_template(
+static float quiet_nanf(float x) {
+)w2c_template"
+R"w2c_template( uint32_t tmp;
+)w2c_template"
+R"w2c_template( memcpy(&tmp, &x, 4);
+)w2c_template"
+R"w2c_template( tmp |= 0x7fc00000lu;
+)w2c_template"
+R"w2c_template( memcpy(&x, &tmp, 4);
+)w2c_template"
+R"w2c_template( return x;
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static double quiet_nan(double x) {
+)w2c_template"
+R"w2c_template( uint64_t tmp;
+)w2c_template"
+R"w2c_template( memcpy(&tmp, &x, 8);
+)w2c_template"
+R"w2c_template( tmp |= 0x7ff8000000000000llu;
+)w2c_template"
+R"w2c_template( memcpy(&x, &tmp, 8);
+)w2c_template"
+R"w2c_template( return x;
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static double wasm_quiet(double x) {
+)w2c_template"
+R"w2c_template( if (UNLIKELY(isnan(x))) {
+)w2c_template"
+R"w2c_template( return quiet_nan(x);
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( return x;
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static float wasm_quietf(float x) {
+)w2c_template"
+R"w2c_template( if (UNLIKELY(isnan(x))) {
+)w2c_template"
+R"w2c_template( return quiet_nanf(x);
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( return x;
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static double wasm_floor(double x) {
+)w2c_template"
+R"w2c_template( if (UNLIKELY(isnan(x))) {
+)w2c_template"
+R"w2c_template( return quiet_nan(x);
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( return floor(x);
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static float wasm_floorf(float x) {
+)w2c_template"
+R"w2c_template( if (UNLIKELY(isnan(x))) {
+)w2c_template"
+R"w2c_template( return quiet_nanf(x);
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( return floorf(x);
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static double wasm_ceil(double x) {
+)w2c_template"
+R"w2c_template( if (UNLIKELY(isnan(x))) {
+)w2c_template"
+R"w2c_template( return quiet_nan(x);
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( return ceil(x);
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static float wasm_ceilf(float x) {
+)w2c_template"
+R"w2c_template( if (UNLIKELY(isnan(x))) {
+)w2c_template"
+R"w2c_template( return quiet_nanf(x);
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( return ceilf(x);
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static double wasm_trunc(double x) {
+)w2c_template"
+R"w2c_template( if (UNLIKELY(isnan(x))) {
+)w2c_template"
+R"w2c_template( return quiet_nan(x);
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( return trunc(x);
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static float wasm_truncf(float x) {
+)w2c_template"
+R"w2c_template( if (UNLIKELY(isnan(x))) {
+)w2c_template"
+R"w2c_template( return quiet_nanf(x);
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( return truncf(x);
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static float wasm_nearbyintf(float x) {
+)w2c_template"
+R"w2c_template( if (UNLIKELY(isnan(x))) {
+)w2c_template"
+R"w2c_template( return quiet_nanf(x);
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( return nearbyintf(x);
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static double wasm_nearbyint(double x) {
+)w2c_template"
+R"w2c_template( if (UNLIKELY(isnan(x))) {
+)w2c_template"
+R"w2c_template( return quiet_nan(x);
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( return nearbyint(x);
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static float wasm_fabsf(float x) {
+)w2c_template"
+R"w2c_template( if (UNLIKELY(isnan(x))) {
+)w2c_template"
+R"w2c_template( uint32_t tmp;
+)w2c_template"
+R"w2c_template( memcpy(&tmp, &x, 4);
+)w2c_template"
+R"w2c_template( tmp = tmp & ~(1UL << 31);
+)w2c_template"
+R"w2c_template( memcpy(&x, &tmp, 4);
+)w2c_template"
+R"w2c_template( return x;
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( return fabsf(x);
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static double wasm_fabs(double x) {
+)w2c_template"
+R"w2c_template( if (UNLIKELY(isnan(x))) {
+)w2c_template"
+R"w2c_template( uint64_t tmp;
+)w2c_template"
+R"w2c_template( memcpy(&tmp, &x, 8);
+)w2c_template"
+R"w2c_template( tmp = tmp & ~(1ULL << 63);
+)w2c_template"
+R"w2c_template( memcpy(&x, &tmp, 8);
+)w2c_template"
+R"w2c_template( return x;
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( return fabs(x);
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static double wasm_sqrt(double x) {
+)w2c_template"
+R"w2c_template( if (UNLIKELY(isnan(x))) {
+)w2c_template"
+R"w2c_template( return quiet_nan(x);
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( return sqrt(x);
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static float wasm_sqrtf(float x) {
+)w2c_template"
+R"w2c_template( if (UNLIKELY(isnan(x))) {
+)w2c_template"
+R"w2c_template( return quiet_nanf(x);
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template( return sqrtf(x);
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static inline void memory_fill(wasm_rt_memory_t* mem, u32 d, u32 val, u32 n) {
+)w2c_template"
+R"w2c_template( RANGE_CHECK(mem, d, n);
+)w2c_template"
+R"w2c_template( memset(mem->data + d, val, n);
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static inline void memory_copy(wasm_rt_memory_t* dest,
+)w2c_template"
+R"w2c_template( const wasm_rt_memory_t* src,
+)w2c_template"
+R"w2c_template( u32 dest_addr,
+)w2c_template"
+R"w2c_template( u32 src_addr,
+)w2c_template"
+R"w2c_template( u32 n) {
+)w2c_template"
+R"w2c_template( RANGE_CHECK(dest, dest_addr, n);
+)w2c_template"
+R"w2c_template( RANGE_CHECK(src, src_addr, n);
+)w2c_template"
+R"w2c_template( memmove(dest->data + dest_addr, src->data + src_addr, n);
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+static inline void memory_init(wasm_rt_memory_t* dest,
+)w2c_template"
+R"w2c_template( const u8* src,
+)w2c_template"
+R"w2c_template( u32 src_size,
+)w2c_template"
+R"w2c_template( u32 dest_addr,
+)w2c_template"
+R"w2c_template( u32 src_addr,
+)w2c_template"
+R"w2c_template( u32 n) {
+)w2c_template"
+R"w2c_template( if (UNLIKELY(src_addr + (uint64_t)n > src_size))
+)w2c_template"
+R"w2c_template( TRAP(OOB);
+)w2c_template"
+R"w2c_template( LOAD_DATA((*dest), dest_addr, src + src_addr, n);
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+typedef struct {
+)w2c_template"
+R"w2c_template( wasm_rt_func_type_t type;
+)w2c_template"
+R"w2c_template( wasm_rt_function_ptr_t func;
+)w2c_template"
+R"w2c_template( size_t module_offset;
+)w2c_template"
+R"w2c_template(} wasm_elem_segment_expr_t;
+)w2c_template"
+R"w2c_template(
+static inline void funcref_table_init(wasm_rt_funcref_table_t* dest,
+)w2c_template"
+R"w2c_template( const wasm_elem_segment_expr_t* src,
+)w2c_template"
+R"w2c_template( u32 src_size,
+)w2c_template"
+R"w2c_template( u32 dest_addr,
+)w2c_template"
+R"w2c_template( u32 src_addr,
+)w2c_template"
+R"w2c_template( u32 n,
+)w2c_template"
+R"w2c_template( void* module_instance) {
+)w2c_template"
+R"w2c_template( if (UNLIKELY(src_addr + (uint64_t)n > src_size))
+)w2c_template"
+R"w2c_template( TRAP(OOB);
+)w2c_template"
+R"w2c_template( if (UNLIKELY(dest_addr + (uint64_t)n > dest->size))
+)w2c_template"
+R"w2c_template( TRAP(OOB);
+)w2c_template"
+R"w2c_template( for (u32 i = 0; i < n; i++) {
+)w2c_template"
+R"w2c_template( const wasm_elem_segment_expr_t* src_expr = &src[src_addr + i];
+)w2c_template"
+R"w2c_template( dest->data[dest_addr + i] =
+)w2c_template"
+R"w2c_template( (wasm_rt_funcref_t){src_expr->type, src_expr->func,
+)w2c_template"
+R"w2c_template( (char*)module_instance + src_expr->module_offset};
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+// Currently Wasm only supports initializing externref tables with ref.null.
+)w2c_template"
+R"w2c_template(static inline void externref_table_init(wasm_rt_externref_table_t* dest,
+)w2c_template"
+R"w2c_template( u32 src_size,
+)w2c_template"
+R"w2c_template( u32 dest_addr,
+)w2c_template"
+R"w2c_template( u32 src_addr,
+)w2c_template"
+R"w2c_template( u32 n) {
+)w2c_template"
+R"w2c_template( if (UNLIKELY(src_addr + (uint64_t)n > src_size))
+)w2c_template"
+R"w2c_template( TRAP(OOB);
+)w2c_template"
+R"w2c_template( if (UNLIKELY(dest_addr + (uint64_t)n > dest->size))
+)w2c_template"
+R"w2c_template( TRAP(OOB);
+)w2c_template"
+R"w2c_template( for (u32 i = 0; i < n; i++) {
+)w2c_template"
+R"w2c_template( dest->data[dest_addr + i] = wasm_rt_externref_null_value;
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(}
+)w2c_template"
+R"w2c_template(
+#define DEFINE_TABLE_COPY(type) \
+)w2c_template"
+R"w2c_template( static inline void type##_table_copy(wasm_rt_##type##_table_t* dest, \
+)w2c_template"
+R"w2c_template( const wasm_rt_##type##_table_t* src, \
+)w2c_template"
+R"w2c_template( u32 dest_addr, u32 src_addr, u32 n) { \
+)w2c_template"
+R"w2c_template( if (UNLIKELY(dest_addr + (uint64_t)n > dest->size)) \
+)w2c_template"
+R"w2c_template( TRAP(OOB); \
+)w2c_template"
+R"w2c_template( if (UNLIKELY(src_addr + (uint64_t)n > src->size)) \
+)w2c_template"
+R"w2c_template( TRAP(OOB); \
+)w2c_template"
+R"w2c_template( \
+)w2c_template"
+R"w2c_template( memmove(dest->data + dest_addr, src->data + src_addr, \
+)w2c_template"
+R"w2c_template( n * sizeof(wasm_rt_##type##_t)); \
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(
+DEFINE_TABLE_COPY(funcref)
+)w2c_template"
+R"w2c_template(DEFINE_TABLE_COPY(externref)
+)w2c_template"
+R"w2c_template(
+#define DEFINE_TABLE_GET(type) \
+)w2c_template"
+R"w2c_template( static inline wasm_rt_##type##_t type##_table_get( \
+)w2c_template"
+R"w2c_template( const wasm_rt_##type##_table_t* table, u32 i) { \
+)w2c_template"
+R"w2c_template( if (UNLIKELY(i >= table->size)) \
+)w2c_template"
+R"w2c_template( TRAP(OOB); \
+)w2c_template"
+R"w2c_template( return table->data[i]; \
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(
+DEFINE_TABLE_GET(funcref)
+)w2c_template"
+R"w2c_template(DEFINE_TABLE_GET(externref)
+)w2c_template"
+R"w2c_template(
+#define DEFINE_TABLE_SET(type) \
+)w2c_template"
+R"w2c_template( static inline void type##_table_set(const wasm_rt_##type##_table_t* table, \
+)w2c_template"
+R"w2c_template( u32 i, const wasm_rt_##type##_t val) { \
+)w2c_template"
+R"w2c_template( if (UNLIKELY(i >= table->size)) \
+)w2c_template"
+R"w2c_template( TRAP(OOB); \
+)w2c_template"
+R"w2c_template( table->data[i] = val; \
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(
+DEFINE_TABLE_SET(funcref)
+)w2c_template"
+R"w2c_template(DEFINE_TABLE_SET(externref)
+)w2c_template"
+R"w2c_template(
+#define DEFINE_TABLE_FILL(type) \
+)w2c_template"
+R"w2c_template( static inline void type##_table_fill(const wasm_rt_##type##_table_t* table, \
+)w2c_template"
+R"w2c_template( u32 d, const wasm_rt_##type##_t val, \
+)w2c_template"
+R"w2c_template( u32 n) { \
+)w2c_template"
+R"w2c_template( if (UNLIKELY((uint64_t)d + n > table->size)) \
+)w2c_template"
+R"w2c_template( TRAP(OOB); \
+)w2c_template"
+R"w2c_template( for (uint32_t i = d; i < d + n; i++) { \
+)w2c_template"
+R"w2c_template( table->data[i] = val; \
+)w2c_template"
+R"w2c_template( } \
+)w2c_template"
+R"w2c_template( }
+)w2c_template"
+R"w2c_template(
+DEFINE_TABLE_FILL(funcref)
+)w2c_template"
+R"w2c_template(DEFINE_TABLE_FILL(externref)
+)w2c_template"
+R"w2c_template(
+#if defined(__GNUC__) || defined(__clang__)
+)w2c_template"
+R"w2c_template(#define FUNC_TYPE_DECL_EXTERN_T(x) extern const char* const x
+)w2c_template"
+R"w2c_template(#define FUNC_TYPE_EXTERN_T(x) const char* const x
+)w2c_template"
+R"w2c_template(#define FUNC_TYPE_T(x) static const char* const x
+)w2c_template"
+R"w2c_template(#else
+)w2c_template"
+R"w2c_template(#define FUNC_TYPE_DECL_EXTERN_T(x) extern const char x[]
+)w2c_template"
+R"w2c_template(#define FUNC_TYPE_EXTERN_T(x) const char x[]
+)w2c_template"
+R"w2c_template(#define FUNC_TYPE_T(x) static const char x[]
+)w2c_template"
+R"w2c_template(#endif
+)w2c_template"
+;
diff --git a/third_party/wasm2c/src/prebuilt/wasm2c_source_includes.cc b/third_party/wasm2c/src/prebuilt/wasm2c_source_includes.cc
new file mode 100644
index 0000000000..a17c5511b7
--- /dev/null
+++ b/third_party/wasm2c/src/prebuilt/wasm2c_source_includes.cc
@@ -0,0 +1,33 @@
+const char* s_source_includes = R"w2c_template(#include <assert.h>
+)w2c_template"
+R"w2c_template(#include <math.h>
+)w2c_template"
+R"w2c_template(#include <stdarg.h>
+)w2c_template"
+R"w2c_template(#include <stddef.h>
+)w2c_template"
+R"w2c_template(#include <string.h>
+)w2c_template"
+R"w2c_template(#if defined(__MINGW32__)
+)w2c_template"
+R"w2c_template(#include <malloc.h>
+)w2c_template"
+R"w2c_template(#elif defined(_MSC_VER)
+)w2c_template"
+R"w2c_template(#include <intrin.h>
+)w2c_template"
+R"w2c_template(#include <malloc.h>
+)w2c_template"
+R"w2c_template(#define alloca _alloca
+)w2c_template"
+R"w2c_template(#elif defined(__FreeBSD__) || defined(__OpenBSD__)
+)w2c_template"
+R"w2c_template(#include <stdlib.h>
+)w2c_template"
+R"w2c_template(#else
+)w2c_template"
+R"w2c_template(#include <alloca.h>
+)w2c_template"
+R"w2c_template(#endif
+)w2c_template"
+;
diff --git a/third_party/wasm2c/src/resolve-names.cc b/third_party/wasm2c/src/resolve-names.cc
new file mode 100644
index 0000000000..9f0b3b0269
--- /dev/null
+++ b/third_party/wasm2c/src/resolve-names.cc
@@ -0,0 +1,649 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/resolve-names.h"
+
+#include <cassert>
+#include <cstdio>
+
+#include "wabt/cast.h"
+#include "wabt/expr-visitor.h"
+#include "wabt/ir.h"
+#include "wabt/wast-lexer.h"
+
+namespace wabt {
+
+namespace {
+
+class NameResolver : public ExprVisitor::DelegateNop {
+ public:
+ NameResolver(Script* script, Errors* errors);
+
+ Result VisitModule(Module* module);
+ Result VisitScript(Script* script);
+
+ // Implementation of ExprVisitor::DelegateNop.
+ Result BeginBlockExpr(BlockExpr*) override;
+ Result EndBlockExpr(BlockExpr*) override;
+ Result OnBrExpr(BrExpr*) override;
+ Result OnBrIfExpr(BrIfExpr*) override;
+ Result OnBrTableExpr(BrTableExpr*) override;
+ Result OnCallExpr(CallExpr*) override;
+ Result OnCallIndirectExpr(CallIndirectExpr*) override;
+ Result OnCatchExpr(TryExpr*, Catch*) override;
+ Result OnDelegateExpr(TryExpr*) override;
+ Result OnReturnCallExpr(ReturnCallExpr*) override;
+ Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr*) override;
+ Result OnGlobalGetExpr(GlobalGetExpr*) override;
+ Result OnGlobalSetExpr(GlobalSetExpr*) override;
+ Result BeginIfExpr(IfExpr*) override;
+ Result EndIfExpr(IfExpr*) override;
+ Result OnLoadExpr(LoadExpr*) override;
+ Result OnLocalGetExpr(LocalGetExpr*) override;
+ Result OnLocalSetExpr(LocalSetExpr*) override;
+ Result OnLocalTeeExpr(LocalTeeExpr*) override;
+ Result BeginLoopExpr(LoopExpr*) override;
+ Result EndLoopExpr(LoopExpr*) override;
+ Result OnMemoryCopyExpr(MemoryCopyExpr*) override;
+ Result OnDataDropExpr(DataDropExpr*) override;
+ Result OnMemoryFillExpr(MemoryFillExpr*) override;
+ Result OnMemoryGrowExpr(MemoryGrowExpr*) override;
+ Result OnMemoryInitExpr(MemoryInitExpr*) override;
+ Result OnMemorySizeExpr(MemorySizeExpr*) override;
+ Result OnElemDropExpr(ElemDropExpr*) override;
+ Result OnTableCopyExpr(TableCopyExpr*) override;
+ Result OnTableInitExpr(TableInitExpr*) override;
+ Result OnTableGetExpr(TableGetExpr*) override;
+ Result OnTableSetExpr(TableSetExpr*) override;
+ Result OnTableGrowExpr(TableGrowExpr*) override;
+ Result OnTableSizeExpr(TableSizeExpr*) override;
+ Result OnTableFillExpr(TableFillExpr*) override;
+ Result OnRefFuncExpr(RefFuncExpr*) override;
+ Result OnStoreExpr(StoreExpr*) override;
+ Result BeginTryExpr(TryExpr*) override;
+ Result EndTryExpr(TryExpr*) override;
+ Result OnThrowExpr(ThrowExpr*) override;
+ Result OnRethrowExpr(RethrowExpr*) override;
+ Result OnSimdLoadLaneExpr(SimdLoadLaneExpr*) override;
+ Result OnSimdStoreLaneExpr(SimdStoreLaneExpr*) override;
+
+ private:
+ void PrintError(const Location* loc, const char* fmt, ...);
+ void PushLabel(const std::string& label);
+ void PopLabel();
+ void CheckDuplicateBindings(const BindingHash* bindings, const char* desc);
+ void PrintDuplicateBindingsError(const BindingHash::value_type&,
+ const BindingHash::value_type&,
+ const char* desc);
+ void ResolveLabelVar(Var* var);
+ void ResolveVar(const BindingHash* bindings, Var* var, const char* desc);
+ void ResolveFuncVar(Var* var);
+ void ResolveGlobalVar(Var* var);
+ void ResolveFuncTypeVar(Var* var);
+ void ResolveTableVar(Var* var);
+ void ResolveMemoryVar(Var* var);
+ void ResolveTagVar(Var* var);
+ void ResolveDataSegmentVar(Var* var);
+ void ResolveElemSegmentVar(Var* var);
+ void ResolveLocalVar(Var* var);
+ void ResolveBlockDeclarationVar(BlockDeclaration* decl);
+ void VisitFunc(Func* func);
+ void VisitExport(Export* export_);
+ void VisitGlobal(Global* global);
+ void VisitTag(Tag* tag);
+ void VisitElemSegment(ElemSegment* segment);
+ void VisitDataSegment(DataSegment* segment);
+ void VisitScriptModule(ScriptModule* script_module);
+ void VisitCommand(Command* command);
+
+ Errors* errors_ = nullptr;
+ Script* script_ = nullptr;
+ Module* current_module_ = nullptr;
+ Func* current_func_ = nullptr;
+ ExprVisitor visitor_;
+ std::vector<std::string> labels_;
+ Result result_ = Result::Ok;
+};
+
+NameResolver::NameResolver(Script* script, Errors* errors)
+ : errors_(errors), script_(script), visitor_(this) {}
+
+} // end anonymous namespace
+
+void WABT_PRINTF_FORMAT(3, 4) NameResolver::PrintError(const Location* loc,
+ const char* format,
+ ...) {
+ result_ = Result::Error;
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ errors_->emplace_back(ErrorLevel::Error, *loc, buffer);
+}
+
+void NameResolver::PushLabel(const std::string& label) {
+ labels_.push_back(label);
+}
+
+void NameResolver::PopLabel() {
+ labels_.pop_back();
+}
+
+void NameResolver::CheckDuplicateBindings(const BindingHash* bindings,
+ const char* desc) {
+ bindings->FindDuplicates([this, desc](const BindingHash::value_type& a,
+ const BindingHash::value_type& b) {
+ PrintDuplicateBindingsError(a, b, desc);
+ });
+}
+
+void NameResolver::PrintDuplicateBindingsError(const BindingHash::value_type& a,
+ const BindingHash::value_type& b,
+ const char* desc) {
+ // Choose the location that is later in the file.
+ const Location& a_loc = a.second.loc;
+ const Location& b_loc = b.second.loc;
+ const Location& loc = a_loc.line > b_loc.line ? a_loc : b_loc;
+ PrintError(&loc, "redefinition of %s \"%s\"", desc, a.first.c_str());
+}
+
+void NameResolver::ResolveLabelVar(Var* var) {
+ if (var->is_name()) {
+ for (int i = labels_.size() - 1; i >= 0; --i) {
+ const std::string& label = labels_[i];
+ if (label == var->name()) {
+ var->set_index(labels_.size() - i - 1);
+ return;
+ }
+ }
+ PrintError(&var->loc, "undefined label variable \"%s\"",
+ var->name().c_str());
+ }
+}
+
+void NameResolver::ResolveVar(const BindingHash* bindings,
+ Var* var,
+ const char* desc) {
+ if (var->is_name()) {
+ Index index = bindings->FindIndex(*var);
+ if (index == kInvalidIndex) {
+ PrintError(&var->loc, "undefined %s variable \"%s\"", desc,
+ var->name().c_str());
+ return;
+ }
+
+ var->set_index(index);
+ }
+}
+
+void NameResolver::ResolveFuncVar(Var* var) {
+ ResolveVar(&current_module_->func_bindings, var, "function");
+}
+
+void NameResolver::ResolveGlobalVar(Var* var) {
+ ResolveVar(&current_module_->global_bindings, var, "global");
+}
+
+void NameResolver::ResolveFuncTypeVar(Var* var) {
+ ResolveVar(&current_module_->type_bindings, var, "type");
+}
+
+void NameResolver::ResolveTableVar(Var* var) {
+ ResolveVar(&current_module_->table_bindings, var, "table");
+}
+
+void NameResolver::ResolveMemoryVar(Var* var) {
+ ResolveVar(&current_module_->memory_bindings, var, "memory");
+}
+
+void NameResolver::ResolveTagVar(Var* var) {
+ ResolveVar(&current_module_->tag_bindings, var, "tag");
+}
+
+void NameResolver::ResolveDataSegmentVar(Var* var) {
+ ResolveVar(&current_module_->data_segment_bindings, var, "data segment");
+}
+
+void NameResolver::ResolveElemSegmentVar(Var* var) {
+ ResolveVar(&current_module_->elem_segment_bindings, var, "elem segment");
+}
+
+void NameResolver::ResolveLocalVar(Var* var) {
+ if (var->is_name()) {
+ if (!current_func_) {
+ return;
+ }
+
+ Index index = current_func_->GetLocalIndex(*var);
+ if (index == kInvalidIndex) {
+ PrintError(&var->loc, "undefined local variable \"%s\"",
+ var->name().c_str());
+ return;
+ }
+
+ var->set_index(index);
+ }
+}
+
+void NameResolver::ResolveBlockDeclarationVar(BlockDeclaration* decl) {
+ if (decl->has_func_type) {
+ ResolveFuncTypeVar(&decl->type_var);
+ }
+}
+
+Result NameResolver::BeginBlockExpr(BlockExpr* expr) {
+ PushLabel(expr->block.label);
+ ResolveBlockDeclarationVar(&expr->block.decl);
+ return Result::Ok;
+}
+
+Result NameResolver::EndBlockExpr(BlockExpr* expr) {
+ PopLabel();
+ return Result::Ok;
+}
+
+Result NameResolver::BeginLoopExpr(LoopExpr* expr) {
+ PushLabel(expr->block.label);
+ ResolveBlockDeclarationVar(&expr->block.decl);
+ return Result::Ok;
+}
+
+Result NameResolver::EndLoopExpr(LoopExpr* expr) {
+ PopLabel();
+ return Result::Ok;
+}
+
+Result NameResolver::OnBrExpr(BrExpr* expr) {
+ ResolveLabelVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnBrIfExpr(BrIfExpr* expr) {
+ ResolveLabelVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnBrTableExpr(BrTableExpr* expr) {
+ for (Var& target : expr->targets)
+ ResolveLabelVar(&target);
+ ResolveLabelVar(&expr->default_target);
+ return Result::Ok;
+}
+
+Result NameResolver::OnCallExpr(CallExpr* expr) {
+ ResolveFuncVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnCallIndirectExpr(CallIndirectExpr* expr) {
+ if (expr->decl.has_func_type) {
+ ResolveFuncTypeVar(&expr->decl.type_var);
+ }
+ ResolveTableVar(&expr->table);
+ return Result::Ok;
+}
+
+Result NameResolver::OnReturnCallExpr(ReturnCallExpr* expr) {
+ ResolveFuncVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnReturnCallIndirectExpr(ReturnCallIndirectExpr* expr) {
+ if (expr->decl.has_func_type) {
+ ResolveFuncTypeVar(&expr->decl.type_var);
+ }
+ ResolveTableVar(&expr->table);
+ return Result::Ok;
+}
+
+Result NameResolver::OnGlobalGetExpr(GlobalGetExpr* expr) {
+ ResolveGlobalVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnGlobalSetExpr(GlobalSetExpr* expr) {
+ ResolveGlobalVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::BeginIfExpr(IfExpr* expr) {
+ PushLabel(expr->true_.label);
+ ResolveBlockDeclarationVar(&expr->true_.decl);
+ return Result::Ok;
+}
+
+Result NameResolver::EndIfExpr(IfExpr* expr) {
+ PopLabel();
+ return Result::Ok;
+}
+
+Result NameResolver::OnLoadExpr(LoadExpr* expr) {
+ ResolveMemoryVar(&expr->memidx);
+ return Result::Ok;
+}
+
+Result NameResolver::OnLocalGetExpr(LocalGetExpr* expr) {
+ ResolveLocalVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnLocalSetExpr(LocalSetExpr* expr) {
+ ResolveLocalVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnLocalTeeExpr(LocalTeeExpr* expr) {
+ ResolveLocalVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnMemoryCopyExpr(MemoryCopyExpr* expr) {
+ ResolveMemoryVar(&expr->srcmemidx);
+ ResolveMemoryVar(&expr->destmemidx);
+ return Result::Ok;
+}
+
+Result NameResolver::OnDataDropExpr(DataDropExpr* expr) {
+ ResolveDataSegmentVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnMemoryFillExpr(MemoryFillExpr* expr) {
+ ResolveMemoryVar(&expr->memidx);
+ return Result::Ok;
+}
+
+Result NameResolver::OnMemoryGrowExpr(MemoryGrowExpr* expr) {
+ ResolveMemoryVar(&expr->memidx);
+ return Result::Ok;
+}
+
+Result NameResolver::OnMemoryInitExpr(MemoryInitExpr* expr) {
+ ResolveDataSegmentVar(&expr->var);
+ ResolveMemoryVar(&expr->memidx);
+ return Result::Ok;
+}
+
+Result NameResolver::OnMemorySizeExpr(MemorySizeExpr* expr) {
+ ResolveMemoryVar(&expr->memidx);
+ return Result::Ok;
+}
+
+Result NameResolver::OnElemDropExpr(ElemDropExpr* expr) {
+ ResolveElemSegmentVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableCopyExpr(TableCopyExpr* expr) {
+ ResolveTableVar(&expr->dst_table);
+ ResolveTableVar(&expr->src_table);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableInitExpr(TableInitExpr* expr) {
+ ResolveElemSegmentVar(&expr->segment_index);
+ ResolveTableVar(&expr->table_index);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableGetExpr(TableGetExpr* expr) {
+ ResolveTableVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableSetExpr(TableSetExpr* expr) {
+ ResolveTableVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableGrowExpr(TableGrowExpr* expr) {
+ ResolveTableVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableSizeExpr(TableSizeExpr* expr) {
+ ResolveTableVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableFillExpr(TableFillExpr* expr) {
+ ResolveTableVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnRefFuncExpr(RefFuncExpr* expr) {
+ ResolveFuncVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnStoreExpr(StoreExpr* expr) {
+ ResolveMemoryVar(&expr->memidx);
+ return Result::Ok;
+}
+
+Result NameResolver::BeginTryExpr(TryExpr* expr) {
+ PushLabel(expr->block.label);
+ ResolveBlockDeclarationVar(&expr->block.decl);
+ return Result::Ok;
+}
+
+Result NameResolver::EndTryExpr(TryExpr*) {
+ PopLabel();
+ return Result::Ok;
+}
+
+Result NameResolver::OnCatchExpr(TryExpr*, Catch* catch_) {
+ if (!catch_->IsCatchAll()) {
+ ResolveTagVar(&catch_->var);
+ }
+ return Result::Ok;
+}
+
+Result NameResolver::OnDelegateExpr(TryExpr* expr) {
+ // Pop the label here as a try-delegate has no `end` instruction.
+ PopLabel();
+
+ // We resolve *after* popping the label in order to ensure that the
+ // delegate label starts counting after the current try-delegate.
+ ResolveLabelVar(&expr->delegate_target);
+
+ return Result::Ok;
+}
+
+Result NameResolver::OnThrowExpr(ThrowExpr* expr) {
+ ResolveTagVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnRethrowExpr(RethrowExpr* expr) {
+ // Note: the variable refers to corresponding (enclosing) catch, using the try
+ // block label for context.
+ ResolveLabelVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnSimdLoadLaneExpr(SimdLoadLaneExpr* expr) {
+ ResolveMemoryVar(&expr->memidx);
+ return Result::Ok;
+}
+
+Result NameResolver::OnSimdStoreLaneExpr(SimdStoreLaneExpr* expr) {
+ ResolveMemoryVar(&expr->memidx);
+ return Result::Ok;
+}
+
+void NameResolver::VisitFunc(Func* func) {
+ current_func_ = func;
+ if (func->decl.has_func_type) {
+ ResolveFuncTypeVar(&func->decl.type_var);
+ }
+
+ func->bindings.FindDuplicates(
+ [=](const BindingHash::value_type& a, const BindingHash::value_type& b) {
+ const char* desc =
+ (a.second.index < func->GetNumParams()) ? "parameter" : "local";
+ PrintDuplicateBindingsError(a, b, desc);
+ });
+
+ visitor_.VisitFunc(func);
+ current_func_ = nullptr;
+}
+
+void NameResolver::VisitExport(Export* export_) {
+ switch (export_->kind) {
+ case ExternalKind::Func:
+ ResolveFuncVar(&export_->var);
+ break;
+
+ case ExternalKind::Table:
+ ResolveTableVar(&export_->var);
+ break;
+
+ case ExternalKind::Memory:
+ ResolveMemoryVar(&export_->var);
+ break;
+
+ case ExternalKind::Global:
+ ResolveGlobalVar(&export_->var);
+ break;
+
+ case ExternalKind::Tag:
+ ResolveTagVar(&export_->var);
+ break;
+ }
+}
+
+void NameResolver::VisitGlobal(Global* global) {
+ visitor_.VisitExprList(global->init_expr);
+}
+
+void NameResolver::VisitTag(Tag* tag) {
+ if (tag->decl.has_func_type) {
+ ResolveFuncTypeVar(&tag->decl.type_var);
+ }
+}
+
+void NameResolver::VisitElemSegment(ElemSegment* segment) {
+ ResolveTableVar(&segment->table_var);
+ visitor_.VisitExprList(segment->offset);
+ for (ExprList& elem_expr : segment->elem_exprs) {
+ if (elem_expr.size() == 1 &&
+ elem_expr.front().type() == ExprType::RefFunc) {
+ ResolveFuncVar(&cast<RefFuncExpr>(&elem_expr.front())->var);
+ }
+ }
+}
+
+void NameResolver::VisitDataSegment(DataSegment* segment) {
+ ResolveMemoryVar(&segment->memory_var);
+ visitor_.VisitExprList(segment->offset);
+}
+
+Result NameResolver::VisitModule(Module* module) {
+ current_module_ = module;
+ CheckDuplicateBindings(&module->elem_segment_bindings, "elem");
+ CheckDuplicateBindings(&module->func_bindings, "function");
+ CheckDuplicateBindings(&module->global_bindings, "global");
+ CheckDuplicateBindings(&module->type_bindings, "type");
+ CheckDuplicateBindings(&module->table_bindings, "table");
+ CheckDuplicateBindings(&module->memory_bindings, "memory");
+ CheckDuplicateBindings(&module->tag_bindings, "tag");
+
+ for (Func* func : module->funcs)
+ VisitFunc(func);
+ for (Export* export_ : module->exports)
+ VisitExport(export_);
+ for (Global* global : module->globals)
+ VisitGlobal(global);
+ for (Tag* tag : module->tags)
+ VisitTag(tag);
+ for (ElemSegment* elem_segment : module->elem_segments)
+ VisitElemSegment(elem_segment);
+ for (DataSegment* data_segment : module->data_segments)
+ VisitDataSegment(data_segment);
+ for (Var* start : module->starts)
+ ResolveFuncVar(start);
+ current_module_ = nullptr;
+ return result_;
+}
+
+void NameResolver::VisitScriptModule(ScriptModule* script_module) {
+ if (auto* tsm = dyn_cast<TextScriptModule>(script_module)) {
+ VisitModule(&tsm->module);
+ }
+}
+
+void NameResolver::VisitCommand(Command* command) {
+ switch (command->type) {
+ case CommandType::Module:
+ VisitModule(&cast<ModuleCommand>(command)->module);
+ break;
+
+ case CommandType::ScriptModule:
+ VisitModule(&cast<ScriptModuleCommand>(command)->module);
+ break;
+
+ case CommandType::Action:
+ case CommandType::AssertReturn:
+ case CommandType::AssertTrap:
+ case CommandType::AssertExhaustion:
+ case CommandType::AssertException:
+ case CommandType::Register:
+ /* Don't resolve a module_var, since it doesn't really behave like other
+ * vars. You can't reference a module by index. */
+ break;
+
+ case CommandType::AssertMalformed:
+ /* Malformed modules should not be text; the whole point of this
+ * assertion is to test for malformed binary modules. */
+ break;
+
+ case CommandType::AssertInvalid: {
+ auto* assert_invalid_command = cast<AssertInvalidCommand>(command);
+ /* The module may be invalid because the names cannot be resolved; we
+ * don't want to print errors or fail if that's the case, but we still
+ * should try to resolve names when possible. */
+ Errors errors;
+ NameResolver new_resolver(script_, &errors);
+ new_resolver.VisitScriptModule(assert_invalid_command->module.get());
+ break;
+ }
+
+ case CommandType::AssertUnlinkable:
+ VisitScriptModule(cast<AssertUnlinkableCommand>(command)->module.get());
+ break;
+
+ case CommandType::AssertUninstantiable:
+ VisitScriptModule(
+ cast<AssertUninstantiableCommand>(command)->module.get());
+ break;
+ }
+}
+
+Result NameResolver::VisitScript(Script* script) {
+ for (const std::unique_ptr<Command>& command : script->commands)
+ VisitCommand(command.get());
+ return result_;
+}
+
+Result ResolveNamesModule(Module* module, Errors* errors) {
+ NameResolver resolver(nullptr, errors);
+ return resolver.VisitModule(module);
+}
+
+Result ResolveNamesScript(Script* script, Errors* errors) {
+ NameResolver resolver(script, errors);
+ return resolver.VisitScript(script);
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/sha256.cc b/third_party/wasm2c/src/sha256.cc
new file mode 100644
index 0000000000..efcb27a1e6
--- /dev/null
+++ b/third_party/wasm2c/src/sha256.cc
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/sha256.h"
+
+#if HAVE_OPENSSL_SHA_H
+#include <openssl/sha.h>
+#else
+#include "picosha2.h"
+#endif
+
+namespace wabt {
+
+/**
+ * SHA-256 the "input" sv into the output "digest".
+ *
+ * Uses OpenSSL's libcrypto or vendored PicoSHA2.
+ */
+void sha256(std::string_view input, std::string& digest) {
+ digest.clear();
+
+#if HAVE_OPENSSL_SHA_H
+ digest.resize(SHA256_DIGEST_LENGTH);
+ if (!SHA256(reinterpret_cast<const uint8_t*>(input.data()), input.size(),
+ reinterpret_cast<uint8_t*>(digest.data()))) {
+ // should not be possible to fail here, but check (and abort) just in case
+ abort();
+ }
+#else
+ digest.resize(picosha2::k_digest_size);
+ picosha2::hash256(input, digest);
+#endif
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/shared-validator.cc b/third_party/wasm2c/src/shared-validator.cc
new file mode 100644
index 0000000000..ec596cad98
--- /dev/null
+++ b/third_party/wasm2c/src/shared-validator.cc
@@ -0,0 +1,1203 @@
+/*
+ * Copyright 2020 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/shared-validator.h"
+
+#include <algorithm>
+#include <cinttypes>
+#include <limits>
+
+namespace wabt {
+
+TypeVector SharedValidator::ToTypeVector(Index count, const Type* types) {
+ return TypeVector(&types[0], &types[count]);
+}
+
+SharedValidator::SharedValidator(Errors* errors, const ValidateOptions& options)
+ : options_(options), errors_(errors), typechecker_(options.features) {
+ typechecker_.set_error_callback(
+ [this](const char* msg) { OnTypecheckerError(msg); });
+}
+
+Result WABT_PRINTF_FORMAT(3, 4) SharedValidator::PrintError(const Location& loc,
+ const char* format,
+ ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ errors_->emplace_back(ErrorLevel::Error, loc, buffer);
+ return Result::Error;
+}
+
+void SharedValidator::OnTypecheckerError(const char* msg) {
+ PrintError(expr_loc_, "%s", msg);
+}
+
+Result SharedValidator::OnFuncType(const Location& loc,
+ Index param_count,
+ const Type* param_types,
+ Index result_count,
+ const Type* result_types,
+ Index type_index) {
+ Result result = Result::Ok;
+ if (!options_.features.multi_value_enabled() && result_count > 1) {
+ result |= PrintError(loc,
+ "multiple result values are not supported without "
+ "multi-value enabled.");
+ }
+ func_types_.emplace(
+ num_types_++,
+ FuncType{ToTypeVector(param_count, param_types),
+ ToTypeVector(result_count, result_types), type_index});
+ return result;
+}
+
+Result SharedValidator::OnStructType(const Location&,
+ Index field_count,
+ TypeMut* fields) {
+ struct_types_.emplace(num_types_++, StructType{TypeMutVector(
+ &fields[0], &fields[field_count])});
+ return Result::Ok;
+}
+
+Result SharedValidator::OnArrayType(const Location&, TypeMut field) {
+ array_types_.emplace(num_types_++, ArrayType{field});
+ return Result::Ok;
+}
+
+Result SharedValidator::OnFunction(const Location& loc, Var sig_var) {
+ Result result = Result::Ok;
+ FuncType type;
+ result |= CheckFuncTypeIndex(sig_var, &type);
+ funcs_.push_back(type);
+ return result;
+}
+
+Result SharedValidator::CheckLimits(const Location& loc,
+ const Limits& limits,
+ uint64_t absolute_max,
+ const char* desc) {
+ Result result = Result::Ok;
+ if (limits.initial > absolute_max) {
+ result |=
+ PrintError(loc, "initial %s (%" PRIu64 ") must be <= (%" PRIu64 ")",
+ desc, limits.initial, absolute_max);
+ }
+
+ if (limits.has_max) {
+ if (limits.max > absolute_max) {
+ result |= PrintError(loc, "max %s (%" PRIu64 ") must be <= (%" PRIu64 ")",
+ desc, limits.max, absolute_max);
+ }
+
+ if (limits.max < limits.initial) {
+ result |= PrintError(
+ loc, "max %s (%" PRIu64 ") must be >= initial %s (%" PRIu64 ")", desc,
+ limits.max, desc, limits.initial);
+ }
+ }
+ return result;
+}
+
+Result SharedValidator::OnTable(const Location& loc,
+ Type elem_type,
+ const Limits& limits) {
+ Result result = Result::Ok;
+ if (tables_.size() > 0 && !options_.features.reference_types_enabled()) {
+ result |= PrintError(loc, "only one table allowed");
+ }
+ result |= CheckLimits(loc, limits, UINT32_MAX, "elems");
+
+ if (limits.is_shared) {
+ result |= PrintError(loc, "tables may not be shared");
+ }
+ if (elem_type != Type::FuncRef &&
+ !options_.features.reference_types_enabled()) {
+ result |= PrintError(loc, "tables must have funcref type");
+ }
+ if (!elem_type.IsRef()) {
+ result |= PrintError(loc, "tables must have reference types");
+ }
+
+ tables_.push_back(TableType{elem_type, limits});
+ return result;
+}
+
+Result SharedValidator::OnMemory(const Location& loc, const Limits& limits) {
+ Result result = Result::Ok;
+ if (memories_.size() > 0 && !options_.features.multi_memory_enabled()) {
+ result |= PrintError(loc, "only one memory block allowed");
+ }
+ result |= CheckLimits(
+ loc, limits, limits.is_64 ? WABT_MAX_PAGES64 : WABT_MAX_PAGES32, "pages");
+
+ if (limits.is_shared) {
+ if (!options_.features.threads_enabled()) {
+ result |= PrintError(loc, "memories may not be shared");
+ } else if (!limits.has_max) {
+ result |= PrintError(loc, "shared memories must have max sizes");
+ }
+ }
+
+ memories_.push_back(MemoryType{limits});
+ return result;
+}
+
+Result SharedValidator::OnGlobalImport(const Location& loc,
+ Type type,
+ bool mutable_) {
+ Result result = Result::Ok;
+ if (mutable_ && !options_.features.mutable_globals_enabled()) {
+ result |= PrintError(loc, "mutable globals cannot be imported");
+ }
+ globals_.push_back(GlobalType{type, mutable_});
+ ++num_imported_globals_;
+ return result;
+}
+
+Result SharedValidator::OnGlobal(const Location& loc,
+ Type type,
+ bool mutable_) {
+ globals_.push_back(GlobalType{type, mutable_});
+ return Result::Ok;
+}
+
+Result SharedValidator::CheckType(const Location& loc,
+ Type actual,
+ Type expected,
+ const char* desc) {
+ if (Failed(TypeChecker::CheckType(actual, expected))) {
+ PrintError(loc, "type mismatch at %s. got %s, expected %s", desc,
+ actual.GetName().c_str(), expected.GetName().c_str());
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+Result SharedValidator::OnTag(const Location& loc, Var sig_var) {
+ Result result = Result::Ok;
+ FuncType type;
+ result |= CheckFuncTypeIndex(sig_var, &type);
+ if (!type.results.empty()) {
+ result |= PrintError(loc, "Tag signature must have 0 results.");
+ }
+ tags_.push_back(TagType{type.params});
+ return result;
+}
+
+Result SharedValidator::OnExport(const Location& loc,
+ ExternalKind kind,
+ Var item_var,
+ std::string_view name) {
+ Result result = Result::Ok;
+ auto name_str = std::string(name);
+ if (export_names_.find(name_str) != export_names_.end()) {
+ result |= PrintError(loc, "duplicate export \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ export_names_.insert(name_str);
+
+ switch (kind) {
+ case ExternalKind::Func:
+ result |= CheckFuncIndex(item_var);
+ declared_funcs_.insert(item_var.index());
+ break;
+
+ case ExternalKind::Table:
+ result |= CheckTableIndex(item_var);
+ break;
+
+ case ExternalKind::Memory:
+ result |= CheckMemoryIndex(item_var);
+ break;
+
+ case ExternalKind::Global:
+ result |= CheckGlobalIndex(item_var);
+ break;
+
+ case ExternalKind::Tag:
+ result |= CheckTagIndex(item_var);
+ break;
+ }
+ return result;
+}
+
+Result SharedValidator::OnStart(const Location& loc, Var func_var) {
+ Result result = Result::Ok;
+ if (starts_++ > 0) {
+ result |= PrintError(loc, "only one start function allowed");
+ }
+ FuncType func_type;
+ result |= CheckFuncIndex(func_var, &func_type);
+ if (func_type.params.size() != 0) {
+ result |= PrintError(loc, "start function must be nullary");
+ }
+ if (func_type.results.size() != 0) {
+ result |= PrintError(loc, "start function must not return anything");
+ }
+ return result;
+}
+
+Result SharedValidator::OnElemSegment(const Location& loc,
+ Var table_var,
+ SegmentKind kind) {
+ Result result = Result::Ok;
+ TableType table_type;
+ if (kind == SegmentKind::Active) {
+ result |= CheckTableIndex(table_var, &table_type);
+ }
+ // Type gets set later in OnElemSegmentElemType.
+ elems_.push_back(
+ ElemType{Type::Void, kind == SegmentKind::Active, table_type.element});
+ return result;
+}
+
+Result SharedValidator::OnElemSegmentElemType(const Location& loc,
+ Type elem_type) {
+ Result result = Result::Ok;
+ auto& elem = elems_.back();
+ if (elem.is_active) {
+ // Check that the type of the elem segment matches the table in which
+ // it is active.
+ result |= CheckType(loc, elem.table_type, elem_type, "elem segment");
+ }
+ elem.element = elem_type;
+ return result;
+}
+
+Result SharedValidator::OnElemSegmentElemExpr_RefNull(const Location& loc,
+ Type type) {
+ return CheckType(loc, type, elems_.back().element, "elem expression");
+}
+
+Result SharedValidator::OnElemSegmentElemExpr_RefFunc(const Location& loc,
+ Var func_var) {
+ Result result = Result::Ok;
+ result |=
+ CheckType(loc, Type::FuncRef, elems_.back().element, "elem expression");
+ result |= CheckFuncIndex(func_var);
+ declared_funcs_.insert(func_var.index());
+ return result;
+}
+
+Result SharedValidator::OnElemSegmentElemExpr_Other(const Location& loc) {
+ return PrintError(loc,
+ "invalid elem expression expression; must be either "
+ "ref.null or ref.func.");
+}
+
+void SharedValidator::OnDataCount(Index count) {
+ data_segments_ = count;
+}
+
+Result SharedValidator::OnDataSegment(const Location& loc,
+ Var memory_var,
+ SegmentKind kind) {
+ Result result = Result::Ok;
+ if (kind == SegmentKind::Active) {
+ result |= CheckMemoryIndex(memory_var);
+ }
+ return result;
+}
+
+Result SharedValidator::CheckDeclaredFunc(Var func_var) {
+ if (declared_funcs_.count(func_var.index()) == 0) {
+ return PrintError(func_var.loc,
+ "function %" PRIindex
+ " is not declared in any elem sections",
+ func_var.index());
+ }
+ return Result::Ok;
+}
+
+Result SharedValidator::EndModule() {
+ // Verify that any ref.func used in init expressions for globals are
+ // mentioned in an elems section. This can't be done while process the
+ // globals because the global section comes before the elem section.
+ Result result = Result::Ok;
+ for (Var func_var : check_declared_funcs_) {
+ result |= CheckDeclaredFunc(func_var);
+ }
+ return result;
+}
+
+Result SharedValidator::CheckIndex(Var var, Index max_index, const char* desc) {
+ if (var.index() >= max_index) {
+ return PrintError(
+ var.loc, "%s variable out of range: %" PRIindex " (max %" PRIindex ")",
+ desc, var.index(), max_index);
+ }
+ return Result::Ok;
+}
+
+template <typename T>
+Result SharedValidator::CheckIndexWithValue(Var var,
+ const std::vector<T>& values,
+ T* out,
+ const char* desc) {
+ Result result = CheckIndex(var, values.size(), desc);
+ if (out) {
+ *out = Succeeded(result) ? values[var.index()] : T{};
+ }
+ return result;
+}
+
+Result SharedValidator::CheckLocalIndex(Var local_var, Type* out_type) {
+ auto iter = std::upper_bound(
+ locals_.begin(), locals_.end(), local_var.index(),
+ [](Index index, const LocalDecl& decl) { return index < decl.end; });
+ if (iter == locals_.end()) {
+ // TODO: better error
+ return PrintError(local_var.loc, "local variable out of range (max %u)",
+ GetLocalCount());
+ }
+ *out_type = iter->type;
+ return Result::Ok;
+}
+
+Result SharedValidator::CheckFuncTypeIndex(Var sig_var, FuncType* out) {
+ Result result = CheckIndex(sig_var, num_types_, "function type");
+ if (Failed(result)) {
+ *out = FuncType{};
+ return Result::Error;
+ }
+
+ auto iter = func_types_.find(sig_var.index());
+ if (iter == func_types_.end()) {
+ return PrintError(sig_var.loc, "type %d is not a function",
+ sig_var.index());
+ }
+
+ if (out) {
+ *out = iter->second;
+ }
+ return Result::Ok;
+}
+
+Result SharedValidator::CheckFuncIndex(Var func_var, FuncType* out) {
+ return CheckIndexWithValue(func_var, funcs_, out, "function");
+}
+
+Result SharedValidator::CheckMemoryIndex(Var memory_var, MemoryType* out) {
+ return CheckIndexWithValue(memory_var, memories_, out, "memory");
+}
+
+Result SharedValidator::CheckTableIndex(Var table_var, TableType* out) {
+ return CheckIndexWithValue(table_var, tables_, out, "table");
+}
+
+Result SharedValidator::CheckGlobalIndex(Var global_var, GlobalType* out) {
+ return CheckIndexWithValue(global_var, globals_, out, "global");
+}
+
+Result SharedValidator::CheckTagIndex(Var tag_var, TagType* out) {
+ return CheckIndexWithValue(tag_var, tags_, out, "tag");
+}
+
+Result SharedValidator::CheckElemSegmentIndex(Var elem_segment_var,
+ ElemType* out) {
+ return CheckIndexWithValue(elem_segment_var, elems_, out, "elem_segment");
+}
+
+Result SharedValidator::CheckDataSegmentIndex(Var data_segment_var) {
+ return CheckIndex(data_segment_var, data_segments_, "data_segment");
+}
+
+Result SharedValidator::CheckBlockSignature(const Location& loc,
+ Opcode opcode,
+ Type sig_type,
+ TypeVector* out_param_types,
+ TypeVector* out_result_types) {
+ Result result = Result::Ok;
+
+ if (sig_type.IsIndex()) {
+ Index sig_index = sig_type.GetIndex();
+ FuncType func_type;
+ result |= CheckFuncTypeIndex(Var(sig_index, loc), &func_type);
+
+ if (!func_type.params.empty() && !options_.features.multi_value_enabled()) {
+ result |= PrintError(loc, "%s params not currently supported.",
+ opcode.GetName());
+ }
+ // Multiple results without --enable-multi-value is checked above in
+ // OnType.
+
+ *out_param_types = func_type.params;
+ *out_result_types = func_type.results;
+ } else {
+ out_param_types->clear();
+ *out_result_types = sig_type.GetInlineVector();
+ }
+
+ return result;
+}
+
+Index SharedValidator::GetFunctionTypeIndex(Index func_index) const {
+ assert(func_index < funcs_.size());
+ return funcs_[func_index].type_index;
+}
+
+Result SharedValidator::BeginInitExpr(const Location& loc, Type type) {
+ expr_loc_ = loc;
+ in_init_expr_ = true;
+ return typechecker_.BeginInitExpr(type);
+}
+
+Result SharedValidator::EndInitExpr() {
+ in_init_expr_ = false;
+ return typechecker_.EndInitExpr();
+}
+
+Result SharedValidator::BeginFunctionBody(const Location& loc,
+ Index func_index) {
+ expr_loc_ = loc;
+ locals_.clear();
+ if (func_index < funcs_.size()) {
+ for (Type type : funcs_[func_index].params) {
+ // TODO: Coalesce parameters of the same type?
+ locals_.push_back(LocalDecl{type, GetLocalCount() + 1});
+ }
+ return typechecker_.BeginFunction(funcs_[func_index].results);
+ } else {
+ // Signature isn't available, use empty.
+ return typechecker_.BeginFunction(TypeVector());
+ }
+}
+
+Result SharedValidator::EndFunctionBody(const Location& loc) {
+ expr_loc_ = loc;
+ return typechecker_.EndFunction();
+}
+
+Result SharedValidator::OnLocalDecl(const Location& loc,
+ Index count,
+ Type type) {
+ const auto max_locals = std::numeric_limits<Index>::max();
+ if (count > max_locals - GetLocalCount()) {
+ PrintError(loc, "local count must be < 0x10000000");
+ return Result::Error;
+ }
+ locals_.push_back(LocalDecl{type, GetLocalCount() + count});
+ return Result::Ok;
+}
+
+Index SharedValidator::GetLocalCount() const {
+ return locals_.empty() ? 0 : locals_.back().end;
+}
+
+static bool is_power_of_two(uint32_t x) {
+ return x && ((x & (x - 1)) == 0);
+}
+
+Result SharedValidator::CheckAlign(const Location& loc,
+ Address alignment,
+ Address natural_alignment) {
+ if (!is_power_of_two(alignment)) {
+ PrintError(loc, "alignment (%" PRIaddress ") must be a power of 2",
+ alignment);
+ return Result::Error;
+ }
+ if (alignment > natural_alignment) {
+ PrintError(
+ loc,
+ "alignment must not be larger than natural alignment (%" PRIaddress ")",
+ natural_alignment);
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+Result SharedValidator::CheckAtomicAlign(const Location& loc,
+ Address alignment,
+ Address natural_alignment) {
+ if (!is_power_of_two(alignment)) {
+ PrintError(loc, "alignment (%" PRIaddress ") must be a power of 2",
+ alignment);
+ return Result::Error;
+ }
+ if (alignment != natural_alignment) {
+ PrintError(loc,
+ "alignment must be equal to natural alignment (%" PRIaddress ")",
+ natural_alignment);
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+bool SharedValidator::ValidInitOpcode(Opcode opcode) const {
+ if (opcode == Opcode::GlobalGet || opcode == Opcode::I32Const ||
+ opcode == Opcode::I64Const || opcode == Opcode::F32Const ||
+ opcode == Opcode::F64Const || opcode == Opcode::RefFunc ||
+ opcode == Opcode::RefNull) {
+ return true;
+ }
+ if (options_.features.extended_const_enabled()) {
+ if (opcode == Opcode::I32Mul || opcode == Opcode::I64Mul ||
+ opcode == Opcode::I32Sub || opcode == Opcode::I64Sub ||
+ opcode == Opcode::I32Add || opcode == Opcode::I64Add) {
+ return true;
+ }
+ }
+ return false;
+}
+
+Result SharedValidator::CheckInstr(Opcode opcode, const Location& loc) {
+ expr_loc_ = loc;
+ if (in_init_expr_ && !ValidInitOpcode(opcode)) {
+ PrintError(loc,
+ "invalid initializer: instruction not valid in initializer "
+ "expression: %s",
+ opcode.GetName());
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+Result SharedValidator::OnAtomicFence(const Location& loc,
+ uint32_t consistency_model) {
+ Result result = CheckInstr(Opcode::AtomicFence, loc);
+ if (consistency_model != 0) {
+ result |= PrintError(
+ loc, "unexpected atomic.fence consistency model (expected 0): %u",
+ consistency_model);
+ }
+ result |= typechecker_.OnAtomicFence(consistency_model);
+ return result;
+}
+
+Result SharedValidator::OnAtomicLoad(const Location& loc,
+ Opcode opcode,
+ Var memidx,
+ Address alignment) {
+ Result result = CheckInstr(opcode, loc);
+ MemoryType mt;
+ result |= CheckMemoryIndex(memidx, &mt);
+ result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnAtomicLoad(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnAtomicNotify(const Location& loc,
+ Opcode opcode,
+ Var memidx,
+ Address alignment) {
+ Result result = CheckInstr(opcode, loc);
+ MemoryType mt;
+ result |= CheckMemoryIndex(memidx, &mt);
+ result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnAtomicNotify(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnAtomicRmwCmpxchg(const Location& loc,
+ Opcode opcode,
+ Var memidx,
+ Address alignment) {
+ Result result = CheckInstr(opcode, loc);
+ MemoryType mt;
+ result |= CheckMemoryIndex(memidx, &mt);
+ result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnAtomicRmwCmpxchg(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnAtomicRmw(const Location& loc,
+ Opcode opcode,
+ Var memidx,
+ Address alignment) {
+ Result result = CheckInstr(opcode, loc);
+ MemoryType mt;
+ result |= CheckMemoryIndex(memidx, &mt);
+ result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnAtomicRmw(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnAtomicStore(const Location& loc,
+ Opcode opcode,
+ Var memidx,
+ Address alignment) {
+ Result result = CheckInstr(opcode, loc);
+ MemoryType mt;
+ result |= CheckMemoryIndex(memidx, &mt);
+ result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnAtomicStore(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnAtomicWait(const Location& loc,
+ Opcode opcode,
+ Var memidx,
+ Address alignment) {
+ Result result = CheckInstr(opcode, loc);
+ MemoryType mt;
+ result |= CheckMemoryIndex(memidx, &mt);
+ result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnAtomicWait(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnBinary(const Location& loc, Opcode opcode) {
+ Result result = CheckInstr(opcode, loc);
+ result |= typechecker_.OnBinary(opcode);
+ return result;
+}
+
+Result SharedValidator::OnBlock(const Location& loc, Type sig_type) {
+ Result result = CheckInstr(Opcode::Block, loc);
+ TypeVector param_types, result_types;
+ result |= CheckBlockSignature(loc, Opcode::Block, sig_type, &param_types,
+ &result_types);
+ result |= typechecker_.OnBlock(param_types, result_types);
+ return result;
+}
+
+Result SharedValidator::OnBr(const Location& loc, Var depth) {
+ Result result = CheckInstr(Opcode::Br, loc);
+ result |= typechecker_.OnBr(depth.index());
+ return result;
+}
+
+Result SharedValidator::OnBrIf(const Location& loc, Var depth) {
+ Result result = CheckInstr(Opcode::BrIf, loc);
+ result |= typechecker_.OnBrIf(depth.index());
+ return result;
+}
+
+Result SharedValidator::BeginBrTable(const Location& loc) {
+ Result result = CheckInstr(Opcode::BrTable, loc);
+ result |= typechecker_.BeginBrTable();
+ return result;
+}
+
+Result SharedValidator::OnBrTableTarget(const Location& loc, Var depth) {
+ Result result = Result::Ok;
+ expr_loc_ = loc;
+ result |= typechecker_.OnBrTableTarget(depth.index());
+ return result;
+}
+
+Result SharedValidator::EndBrTable(const Location& loc) {
+ Result result = CheckInstr(Opcode::BrTable, loc);
+ result |= typechecker_.EndBrTable();
+ return result;
+}
+
+Result SharedValidator::OnCall(const Location& loc, Var func_var) {
+ Result result = CheckInstr(Opcode::Call, loc);
+ FuncType func_type;
+ result |= CheckFuncIndex(func_var, &func_type);
+ result |= typechecker_.OnCall(func_type.params, func_type.results);
+ return result;
+}
+
+Result SharedValidator::OnCallIndirect(const Location& loc,
+ Var sig_var,
+ Var table_var) {
+ Result result = CheckInstr(Opcode::CallIndirect, loc);
+ FuncType func_type;
+ result |= CheckFuncTypeIndex(sig_var, &func_type);
+ result |= CheckTableIndex(table_var);
+ result |= typechecker_.OnCallIndirect(func_type.params, func_type.results);
+ return result;
+}
+
+Result SharedValidator::OnCallRef(const Location& loc,
+ Index* function_type_index) {
+ Result result = CheckInstr(Opcode::CallRef, loc);
+ Index func_index;
+ result |= typechecker_.OnIndexedFuncRef(&func_index);
+ if (Failed(result)) {
+ return result;
+ }
+ FuncType func_type;
+ result |= CheckFuncTypeIndex(Var(func_index, loc), &func_type);
+ result |= typechecker_.OnCall(func_type.params, func_type.results);
+ if (Succeeded(result)) {
+ *function_type_index = func_index;
+ }
+ return result;
+}
+
+Result SharedValidator::OnCatch(const Location& loc,
+ Var tag_var,
+ bool is_catch_all) {
+ Result result = CheckInstr(Opcode::Catch, loc);
+ if (is_catch_all) {
+ TypeVector empty;
+ result |= typechecker_.OnCatch(empty);
+ } else {
+ TagType tag_type;
+ result |= CheckTagIndex(tag_var, &tag_type);
+ result |= typechecker_.OnCatch(tag_type.params);
+ }
+ return result;
+}
+
+Result SharedValidator::OnCompare(const Location& loc, Opcode opcode) {
+ Result result = CheckInstr(opcode, loc);
+ result |= typechecker_.OnCompare(opcode);
+ return result;
+}
+
+Result SharedValidator::OnConst(const Location& loc, Type type) {
+ Result result = Result::Ok;
+ expr_loc_ = loc;
+ result |= typechecker_.OnConst(type);
+ return result;
+}
+
+Result SharedValidator::OnConvert(const Location& loc, Opcode opcode) {
+ Result result = CheckInstr(opcode, loc);
+ result |= typechecker_.OnConvert(opcode);
+ return result;
+}
+
+Result SharedValidator::OnDataDrop(const Location& loc, Var segment_var) {
+ Result result = CheckInstr(Opcode::DataDrop, loc);
+ result |= CheckDataSegmentIndex(segment_var);
+ result |= typechecker_.OnDataDrop(segment_var.index());
+ return result;
+}
+
+Result SharedValidator::OnDelegate(const Location& loc, Var depth) {
+ Result result = CheckInstr(Opcode::Delegate, loc);
+ result |= typechecker_.OnDelegate(depth.index());
+ return result;
+}
+
+Result SharedValidator::OnDrop(const Location& loc) {
+ Result result = CheckInstr(Opcode::Drop, loc);
+ result |= typechecker_.OnDrop();
+ return result;
+}
+
+Result SharedValidator::OnElemDrop(const Location& loc, Var segment_var) {
+ Result result = CheckInstr(Opcode::ElemDrop, loc);
+ result |= CheckElemSegmentIndex(segment_var);
+ result |= typechecker_.OnElemDrop(segment_var.index());
+ return result;
+}
+
+Result SharedValidator::OnElse(const Location& loc) {
+ // Don't call CheckInstr or update expr_loc_ here because if we fail we want
+ // the last expression in the If block to be reported as the error location,
+ // not the else itself.
+ Result result = Result::Ok;
+ result |= typechecker_.OnElse();
+ return result;
+}
+
+Result SharedValidator::OnEnd(const Location& loc) {
+ Result result = CheckInstr(Opcode::End, loc);
+ result |= typechecker_.OnEnd();
+ return result;
+}
+
+Result SharedValidator::OnGlobalGet(const Location& loc, Var global_var) {
+ Result result = CheckInstr(Opcode::GlobalGet, loc);
+ GlobalType global_type;
+ result |= CheckGlobalIndex(global_var, &global_type);
+ result |= typechecker_.OnGlobalGet(global_type.type);
+ if (Succeeded(result) && in_init_expr_) {
+ if (global_var.index() >= num_imported_globals_) {
+ result |= PrintError(
+ global_var.loc,
+ "initializer expression can only reference an imported global");
+ }
+ if (global_type.mutable_) {
+ result |= PrintError(
+ loc, "initializer expression cannot reference a mutable global");
+ }
+ }
+
+ return result;
+}
+
+Result SharedValidator::OnGlobalSet(const Location& loc, Var global_var) {
+ Result result = CheckInstr(Opcode::GlobalSet, loc);
+ GlobalType global_type;
+ result |= CheckGlobalIndex(global_var, &global_type);
+ if (!global_type.mutable_) {
+ result |= PrintError(
+ loc, "can't global.set on immutable global at index %" PRIindex ".",
+ global_var.index());
+ }
+ result |= typechecker_.OnGlobalSet(global_type.type);
+ return result;
+}
+
+Result SharedValidator::OnIf(const Location& loc, Type sig_type) {
+ Result result = CheckInstr(Opcode::If, loc);
+ TypeVector param_types, result_types;
+ result |= CheckBlockSignature(loc, Opcode::If, sig_type, &param_types,
+ &result_types);
+ result |= typechecker_.OnIf(param_types, result_types);
+ return result;
+}
+
+Result SharedValidator::OnLoad(const Location& loc,
+ Opcode opcode,
+ Var memidx,
+ Address alignment) {
+ Result result = CheckInstr(opcode, loc);
+ MemoryType mt;
+ result |= CheckMemoryIndex(memidx, &mt);
+ result |= CheckAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnLoad(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnLoadSplat(const Location& loc,
+ Opcode opcode,
+ Var memidx,
+ Address alignment) {
+ Result result = CheckInstr(opcode, loc);
+ MemoryType mt;
+ result |= CheckMemoryIndex(memidx, &mt);
+ result |= CheckAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnLoad(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnLoadZero(const Location& loc,
+ Opcode opcode,
+ Var memidx,
+ Address alignment) {
+ Result result = CheckInstr(opcode, loc);
+ MemoryType mt;
+ result |= CheckMemoryIndex(memidx, &mt);
+ result |= CheckAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnLoad(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnLocalGet(const Location& loc, Var local_var) {
+ CHECK_RESULT(CheckInstr(Opcode::LocalGet, loc));
+ Result result = Result::Ok;
+ Type type = Type::Any;
+ result |= CheckLocalIndex(local_var, &type);
+ result |= typechecker_.OnLocalGet(type);
+ return result;
+}
+
+Result SharedValidator::OnLocalSet(const Location& loc, Var local_var) {
+ CHECK_RESULT(CheckInstr(Opcode::LocalSet, loc));
+ Result result = Result::Ok;
+ Type type = Type::Any;
+ result |= CheckLocalIndex(local_var, &type);
+ result |= typechecker_.OnLocalSet(type);
+ return result;
+}
+
+Result SharedValidator::OnLocalTee(const Location& loc, Var local_var) {
+ CHECK_RESULT(CheckInstr(Opcode::LocalTee, loc));
+ Result result = Result::Ok;
+ Type type = Type::Any;
+ result |= CheckLocalIndex(local_var, &type);
+ result |= typechecker_.OnLocalTee(type);
+ return result;
+}
+
+Result SharedValidator::OnLoop(const Location& loc, Type sig_type) {
+ Result result = CheckInstr(Opcode::Loop, loc);
+ TypeVector param_types, result_types;
+ result |= CheckBlockSignature(loc, Opcode::Loop, sig_type, &param_types,
+ &result_types);
+ result |= typechecker_.OnLoop(param_types, result_types);
+ return result;
+}
+
+Result SharedValidator::OnMemoryCopy(const Location& loc,
+ Var srcmemidx,
+ Var destmemidx) {
+ Result result = CheckInstr(Opcode::MemoryCopy, loc);
+ MemoryType srcmt;
+ MemoryType dstmt;
+ result |= CheckMemoryIndex(srcmemidx, &srcmt);
+ result |= CheckMemoryIndex(destmemidx, &dstmt);
+ result |= typechecker_.OnMemoryCopy(srcmt.limits, dstmt.limits);
+ return result;
+}
+
+Result SharedValidator::OnMemoryFill(const Location& loc, Var memidx) {
+ Result result = CheckInstr(Opcode::MemoryFill, loc);
+ MemoryType mt;
+ result |= CheckMemoryIndex(memidx, &mt);
+ result |= typechecker_.OnMemoryFill(mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnMemoryGrow(const Location& loc, Var memidx) {
+ Result result = CheckInstr(Opcode::MemoryGrow, loc);
+ MemoryType mt;
+ result |= CheckMemoryIndex(memidx, &mt);
+ result |= typechecker_.OnMemoryGrow(mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnMemoryInit(const Location& loc,
+ Var segment_var,
+ Var memidx) {
+ Result result = CheckInstr(Opcode::MemoryInit, loc);
+ MemoryType mt;
+ result |= CheckMemoryIndex(memidx, &mt);
+ result |= CheckDataSegmentIndex(segment_var);
+ result |= typechecker_.OnMemoryInit(segment_var.index(), mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnMemorySize(const Location& loc, Var memidx) {
+ Result result = CheckInstr(Opcode::MemorySize, loc);
+ MemoryType mt;
+ result |= CheckMemoryIndex(memidx, &mt);
+ result |= typechecker_.OnMemorySize(mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnNop(const Location& loc) {
+ Result result = CheckInstr(Opcode::Nop, loc);
+ return result;
+}
+
+Result SharedValidator::OnRefFunc(const Location& loc, Var func_var) {
+ Result result = CheckInstr(Opcode::RefFunc, loc);
+ result |= CheckFuncIndex(func_var);
+ if (Succeeded(result)) {
+ // References in initializer expressions are considered declarations, as
+ // opposed to references in function bodies that are considered usages.
+ if (in_init_expr_) {
+ declared_funcs_.insert(func_var.index());
+ } else {
+ check_declared_funcs_.push_back(func_var);
+ }
+ Index func_type = GetFunctionTypeIndex(func_var.index());
+ result |= typechecker_.OnRefFuncExpr(func_type);
+ }
+ return result;
+}
+
+Result SharedValidator::OnRefIsNull(const Location& loc) {
+ Result result = CheckInstr(Opcode::RefIsNull, loc);
+ result |= typechecker_.OnRefIsNullExpr();
+ return result;
+}
+
+Result SharedValidator::OnRefNull(const Location& loc, Type type) {
+ Result result = CheckInstr(Opcode::RefNull, loc);
+ result |= typechecker_.OnRefNullExpr(type);
+ return result;
+}
+
+Result SharedValidator::OnRethrow(const Location& loc, Var depth) {
+ Result result = CheckInstr(Opcode::Rethrow, loc);
+ result |= typechecker_.OnRethrow(depth.index());
+ return result;
+}
+
+Result SharedValidator::OnReturnCall(const Location& loc, Var func_var) {
+ Result result = CheckInstr(Opcode::ReturnCall, loc);
+ FuncType func_type;
+ result |= CheckFuncIndex(func_var, &func_type);
+ result |= typechecker_.OnReturnCall(func_type.params, func_type.results);
+ return result;
+}
+
+Result SharedValidator::OnReturnCallIndirect(const Location& loc,
+ Var sig_var,
+ Var table_var) {
+ Result result = CheckInstr(Opcode::CallIndirect, loc);
+ result |= CheckTableIndex(table_var);
+ FuncType func_type;
+ result |= CheckFuncTypeIndex(sig_var, &func_type);
+ result |=
+ typechecker_.OnReturnCallIndirect(func_type.params, func_type.results);
+ return result;
+}
+
+Result SharedValidator::OnReturn(const Location& loc) {
+ Result result = CheckInstr(Opcode::Return, loc);
+ result |= typechecker_.OnReturn();
+ return result;
+}
+
+Result SharedValidator::OnSelect(const Location& loc,
+ Index result_count,
+ Type* result_types) {
+ Result result = CheckInstr(Opcode::Select, loc);
+ if (result_count > 1) {
+ result |=
+ PrintError(loc, "invalid arity in select instruction: %" PRIindex ".",
+ result_count);
+ } else {
+ result |= typechecker_.OnSelect(ToTypeVector(result_count, result_types));
+ }
+ return result;
+}
+
+Result SharedValidator::OnSimdLaneOp(const Location& loc,
+ Opcode opcode,
+ uint64_t value) {
+ Result result = CheckInstr(opcode, loc);
+ result |= typechecker_.OnSimdLaneOp(opcode, value);
+ return result;
+}
+
+Result SharedValidator::OnSimdLoadLane(const Location& loc,
+ Opcode opcode,
+ Var memidx,
+ Address alignment,
+ uint64_t value) {
+ Result result = CheckInstr(opcode, loc);
+ MemoryType mt;
+ result |= CheckMemoryIndex(memidx, &mt);
+ result |= CheckAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnSimdLoadLane(opcode, mt.limits, value);
+ return result;
+}
+
+Result SharedValidator::OnSimdStoreLane(const Location& loc,
+ Opcode opcode,
+ Var memidx,
+ Address alignment,
+ uint64_t value) {
+ Result result = CheckInstr(opcode, loc);
+ MemoryType mt;
+ result |= CheckMemoryIndex(memidx, &mt);
+ result |= CheckAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnSimdStoreLane(opcode, mt.limits, value);
+ return result;
+}
+
+Result SharedValidator::OnSimdShuffleOp(const Location& loc,
+ Opcode opcode,
+ v128 value) {
+ Result result = CheckInstr(opcode, loc);
+ result |= typechecker_.OnSimdShuffleOp(opcode, value);
+ return result;
+}
+
+Result SharedValidator::OnStore(const Location& loc,
+ Opcode opcode,
+ Var memidx,
+ Address alignment) {
+ Result result = CheckInstr(opcode, loc);
+ MemoryType mt;
+ result |= CheckMemoryIndex(memidx, &mt);
+ result |= CheckAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnStore(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnTableCopy(const Location& loc,
+ Var dst_var,
+ Var src_var) {
+ Result result = CheckInstr(Opcode::TableCopy, loc);
+ TableType dst_table;
+ TableType src_table;
+ result |= CheckTableIndex(dst_var, &dst_table);
+ result |= CheckTableIndex(src_var, &src_table);
+ result |= typechecker_.OnTableCopy();
+ result |= CheckType(loc, src_table.element, dst_table.element, "table.copy");
+ return result;
+}
+
+Result SharedValidator::OnTableFill(const Location& loc, Var table_var) {
+ Result result = CheckInstr(Opcode::TableFill, loc);
+ TableType table_type;
+ result |= CheckTableIndex(table_var, &table_type);
+ result |= typechecker_.OnTableFill(table_type.element);
+ return result;
+}
+
+Result SharedValidator::OnTableGet(const Location& loc, Var table_var) {
+ Result result = CheckInstr(Opcode::TableGet, loc);
+ TableType table_type;
+ result |= CheckTableIndex(table_var, &table_type);
+ result |= typechecker_.OnTableGet(table_type.element);
+ return result;
+}
+
+Result SharedValidator::OnTableGrow(const Location& loc, Var table_var) {
+ Result result = CheckInstr(Opcode::TableGrow, loc);
+ TableType table_type;
+ result |= CheckTableIndex(table_var, &table_type);
+ result |= typechecker_.OnTableGrow(table_type.element);
+ return result;
+}
+
+Result SharedValidator::OnTableInit(const Location& loc,
+ Var segment_var,
+ Var table_var) {
+ Result result = CheckInstr(Opcode::TableInit, loc);
+ TableType table_type;
+ ElemType elem_type;
+ result |= CheckTableIndex(table_var, &table_type);
+ result |= CheckElemSegmentIndex(segment_var, &elem_type);
+ result |= typechecker_.OnTableInit(table_var.index(), segment_var.index());
+ result |= CheckType(loc, elem_type.element, table_type.element, "table.init");
+ return result;
+}
+
+Result SharedValidator::OnTableSet(const Location& loc, Var table_var) {
+ Result result = CheckInstr(Opcode::TableSet, loc);
+ TableType table_type;
+ result |= CheckTableIndex(table_var, &table_type);
+ result |= typechecker_.OnTableSet(table_type.element);
+ return result;
+}
+
+Result SharedValidator::OnTableSize(const Location& loc, Var table_var) {
+ Result result = CheckInstr(Opcode::TableSize, loc);
+ result |= CheckTableIndex(table_var);
+ result |= typechecker_.OnTableSize();
+ return result;
+}
+
+Result SharedValidator::OnTernary(const Location& loc, Opcode opcode) {
+ Result result = CheckInstr(opcode, loc);
+ result |= typechecker_.OnTernary(opcode);
+ return result;
+}
+
+Result SharedValidator::OnThrow(const Location& loc, Var tag_var) {
+ Result result = CheckInstr(Opcode::Throw, loc);
+ TagType tag_type;
+ result |= CheckTagIndex(tag_var, &tag_type);
+ result |= typechecker_.OnThrow(tag_type.params);
+ return result;
+}
+
+Result SharedValidator::OnTry(const Location& loc, Type sig_type) {
+ Result result = CheckInstr(Opcode::Try, loc);
+ TypeVector param_types, result_types;
+ result |= CheckBlockSignature(loc, Opcode::Try, sig_type, &param_types,
+ &result_types);
+ result |= typechecker_.OnTry(param_types, result_types);
+ return result;
+}
+
+Result SharedValidator::OnUnary(const Location& loc, Opcode opcode) {
+ Result result = CheckInstr(opcode, loc);
+ result |= typechecker_.OnUnary(opcode);
+ return result;
+}
+
+Result SharedValidator::OnUnreachable(const Location& loc) {
+ Result result = CheckInstr(Opcode::Unreachable, loc);
+ result |= typechecker_.OnUnreachable();
+ return result;
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/stream.cc b/third_party/wasm2c/src/stream.cc
new file mode 100644
index 0000000000..5f27e01b2e
--- /dev/null
+++ b/third_party/wasm2c/src/stream.cc
@@ -0,0 +1,330 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/stream.h"
+
+#include <cassert>
+#include <cctype>
+#include <cerrno>
+
+#define DUMP_OCTETS_PER_LINE 16
+#define DUMP_OCTETS_PER_GROUP 2
+
+#define ERROR0(msg) fprintf(stderr, "%s:%d: " msg, __FILE__, __LINE__)
+#define ERROR(fmt, ...) \
+ fprintf(stderr, "%s:%d: " fmt, __FILE__, __LINE__, __VA_ARGS__)
+
+namespace wabt {
+
+Stream::Stream(Stream* log_stream)
+ : offset_(0), result_(Result::Ok), log_stream_(log_stream) {}
+
+void Stream::AddOffset(ssize_t delta) {
+ offset_ += delta;
+}
+
+void Stream::WriteDataAt(size_t at,
+ const void* src,
+ size_t size,
+ const char* desc,
+ PrintChars print_chars) {
+ if (Failed(result_)) {
+ return;
+ }
+ if (log_stream_) {
+ log_stream_->WriteMemoryDump(src, size, at, print_chars, nullptr, desc);
+ }
+ result_ = WriteDataImpl(at, src, size);
+}
+
+void Stream::WriteData(const void* src,
+ size_t size,
+ const char* desc,
+ PrintChars print_chars) {
+ WriteDataAt(offset_, src, size, desc, print_chars);
+ offset_ += size;
+}
+
+void Stream::MoveData(size_t dst_offset, size_t src_offset, size_t size) {
+ if (Failed(result_)) {
+ return;
+ }
+ if (log_stream_) {
+ log_stream_->Writef(
+ "; move data: [%" PRIzx ", %" PRIzx ") -> [%" PRIzx ", %" PRIzx ")\n",
+ src_offset, src_offset + size, dst_offset, dst_offset + size);
+ }
+ result_ = MoveDataImpl(dst_offset, src_offset, size);
+}
+
+void Stream::Truncate(size_t size) {
+ if (Failed(result_)) {
+ return;
+ }
+ if (log_stream_) {
+ log_stream_->Writef("; truncate to %" PRIzd " (0x%" PRIzx ")\n", size,
+ size);
+ }
+ result_ = TruncateImpl(size);
+ if (Succeeded(result_) && offset_ > size) {
+ offset_ = size;
+ }
+}
+
+void Stream::Writef(const char* format, ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ WriteData(buffer, length);
+}
+
+void Stream::WriteMemoryDump(const void* start,
+ size_t size,
+ size_t offset,
+ PrintChars print_chars,
+ const char* prefix,
+ const char* desc) {
+ const uint8_t* p = static_cast<const uint8_t*>(start);
+ const uint8_t* end = p + size;
+ while (p < end) {
+ const uint8_t* line = p;
+ const uint8_t* line_end = p + DUMP_OCTETS_PER_LINE;
+ if (prefix) {
+ Writef("%s", prefix);
+ }
+ Writef("%07" PRIzx ": ", reinterpret_cast<intptr_t>(p) -
+ reinterpret_cast<intptr_t>(start) + offset);
+ while (p < line_end) {
+ for (int i = 0; i < DUMP_OCTETS_PER_GROUP; ++i, ++p) {
+ if (p < end) {
+ Writef("%02x", *p);
+ } else {
+ WriteChar(' ');
+ WriteChar(' ');
+ }
+ }
+ WriteChar(' ');
+ }
+
+ if (print_chars == PrintChars::Yes) {
+ WriteChar(' ');
+ p = line;
+ for (int i = 0; i < DUMP_OCTETS_PER_LINE && p < end; ++i, ++p)
+ WriteChar(isprint(*p) ? *p : '.');
+ }
+
+ /* if there are multiple lines, only print the desc on the last one */
+ if (p >= end && desc) {
+ Writef(" ; %s", desc);
+ }
+ WriteChar('\n');
+ }
+}
+
+Result OutputBuffer::WriteToFile(std::string_view filename) const {
+ std::string filename_str(filename);
+ FILE* file = fopen(filename_str.c_str(), "wb");
+ if (!file) {
+ ERROR("unable to open %s for writing\n", filename_str.c_str());
+ return Result::Error;
+ }
+
+ if (data.empty()) {
+ fclose(file);
+ return Result::Ok;
+ }
+
+ ssize_t bytes = fwrite(data.data(), 1, data.size(), file);
+ if (bytes < 0 || static_cast<size_t>(bytes) != data.size()) {
+ ERROR("failed to write %" PRIzd " bytes to %s\n", data.size(),
+ filename_str.c_str());
+ fclose(file);
+ return Result::Error;
+ }
+
+ fclose(file);
+ return Result::Ok;
+}
+
+Result OutputBuffer::WriteToStdout() const {
+ if (data.empty()) {
+ return Result::Ok;
+ }
+ ssize_t bytes = fwrite(data.data(), 1, data.size(), stdout);
+ if (bytes < 0 || static_cast<size_t>(bytes) != data.size()) {
+ ERROR("failed to write %" PRIzd " bytes to stdout\n", data.size());
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+MemoryStream::MemoryStream(Stream* log_stream)
+ : Stream(log_stream), buf_(new OutputBuffer()) {}
+
+MemoryStream::MemoryStream(std::unique_ptr<OutputBuffer>&& buf,
+ Stream* log_stream)
+ : Stream(log_stream), buf_(std::move(buf)) {}
+
+std::unique_ptr<OutputBuffer> MemoryStream::ReleaseOutputBuffer() {
+ return std::move(buf_);
+}
+
+void MemoryStream::Clear() {
+ if (buf_)
+ buf_->clear();
+ else
+ buf_.reset(new OutputBuffer());
+}
+
+Result MemoryStream::WriteDataImpl(size_t dst_offset,
+ const void* src,
+ size_t size) {
+ if (size == 0) {
+ return Result::Ok;
+ }
+ size_t end = dst_offset + size;
+ if (end > buf_->data.size()) {
+ buf_->data.resize(end);
+ }
+ uint8_t* dst = &buf_->data[dst_offset];
+ memcpy(dst, src, size);
+ return Result::Ok;
+}
+
+Result MemoryStream::MoveDataImpl(size_t dst_offset,
+ size_t src_offset,
+ size_t size) {
+ if (size == 0) {
+ return Result::Ok;
+ }
+ size_t src_end = src_offset + size;
+ size_t dst_end = dst_offset + size;
+ size_t end = src_end > dst_end ? src_end : dst_end;
+ if (end > buf_->data.size()) {
+ buf_->data.resize(end);
+ }
+
+ uint8_t* dst = &buf_->data[dst_offset];
+ uint8_t* src = &buf_->data[src_offset];
+ memmove(dst, src, size);
+ return Result::Ok;
+}
+
+Result MemoryStream::TruncateImpl(size_t size) {
+ if (size > buf_->data.size()) {
+ return Result::Error;
+ }
+ buf_->data.resize(size);
+ return Result::Ok;
+}
+
+FileStream::FileStream(std::string_view filename, Stream* log_stream)
+ : Stream(log_stream), file_(nullptr), offset_(0), should_close_(false) {
+ std::string filename_str(filename);
+ file_ = fopen(filename_str.c_str(), "wb");
+
+ // TODO(binji): this is pretty cheesy, should come up with a better API.
+ if (file_) {
+ should_close_ = true;
+ } else {
+ ERROR("fopen name=\"%s\" failed, errno=%d\n", filename_str.c_str(), errno);
+ }
+}
+
+FileStream::FileStream(FILE* file, Stream* log_stream)
+ : Stream(log_stream), file_(file), offset_(0), should_close_(false) {}
+
+FileStream::FileStream(FileStream&& other) {
+ *this = std::move(other);
+}
+
+FileStream& FileStream::operator=(FileStream&& other) {
+ file_ = other.file_;
+ offset_ = other.offset_;
+ should_close_ = other.should_close_;
+ other.file_ = nullptr;
+ other.offset_ = 0;
+ other.should_close_ = false;
+ return *this;
+}
+
+FileStream::~FileStream() {
+ // We don't want to close existing files (stdout/sterr, for example).
+ if (should_close_) {
+ fclose(file_);
+ }
+}
+
+void FileStream::Flush() {
+ if (file_) {
+ fflush(file_);
+ }
+}
+
+Result FileStream::WriteDataImpl(size_t at, const void* data, size_t size) {
+ if (!file_) {
+ return Result::Error;
+ }
+ if (size == 0) {
+ return Result::Ok;
+ }
+ if (at != offset_) {
+ if (fseek(file_, at, SEEK_SET) != 0) {
+ ERROR("fseek offset=%" PRIzd " failed, errno=%d\n", size, errno);
+ return Result::Error;
+ }
+ offset_ = at;
+ }
+ if (fwrite(data, size, 1, file_) != 1) {
+ ERROR("fwrite size=%" PRIzd " failed, errno=%d\n", size, errno);
+ return Result::Error;
+ }
+ offset_ += size;
+ return Result::Ok;
+}
+
+Result FileStream::MoveDataImpl(size_t dst_offset,
+ size_t src_offset,
+ size_t size) {
+ if (!file_) {
+ return Result::Error;
+ }
+ if (size == 0) {
+ return Result::Ok;
+ }
+ // TODO(binji): implement if needed.
+ ERROR0("FileStream::MoveDataImpl not implemented!\n");
+ return Result::Error;
+}
+
+Result FileStream::TruncateImpl(size_t size) {
+ if (!file_) {
+ return Result::Error;
+ }
+ // TODO(binji): implement if needed.
+ ERROR0("FileStream::TruncateImpl not implemented!\n");
+ return Result::Error;
+}
+
+// static
+std::unique_ptr<FileStream> FileStream::CreateStdout() {
+ return std::unique_ptr<FileStream>(new FileStream(stdout));
+}
+
+// static
+std::unique_ptr<FileStream> FileStream::CreateStderr() {
+ return std::unique_ptr<FileStream>(new FileStream(stderr));
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/test-binary-reader.cc b/third_party/wasm2c/src/test-binary-reader.cc
new file mode 100644
index 0000000000..b3e3540d4f
--- /dev/null
+++ b/third_party/wasm2c/src/test-binary-reader.cc
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2018 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gtest/gtest.h"
+
+#include "wabt/binary-reader-nop.h"
+#include "wabt/binary-reader.h"
+#include "wabt/leb128.h"
+#include "wabt/opcode.h"
+
+using namespace wabt;
+
+namespace {
+
+struct BinaryReaderError : BinaryReaderNop {
+ bool OnError(const Error& error) override {
+ first_error = error;
+ return true; // Error handled.
+ }
+
+ Error first_error;
+};
+
+} // End of anonymous namespace
+
+TEST(BinaryReader, DisabledOpcodes) {
+ // Use the default features.
+ ReadBinaryOptions options;
+
+ // Loop through all opcodes.
+ for (uint32_t i = 0; i < static_cast<uint32_t>(Opcode::Invalid); ++i) {
+ Opcode opcode(static_cast<Opcode::Enum>(i));
+ if (opcode.IsEnabled(options.features)) {
+ continue;
+ }
+
+ // Use a shorter name to make the clang-formatted table below look nicer.
+ std::vector<uint8_t> b = opcode.GetBytes();
+ assert(b.size() <= 3);
+ b.resize(3);
+
+ uint8_t data[] = {
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, // magic + version
+ 0x01, 0x04, 0x01, 0x60, 0x00, 0x00, // type section: 1 type, (func)
+ 0x03, 0x02, 0x01, 0x00, // func section: 1 func, type 0
+ 0x0a, 0x07, 0x01, 0x05, 0x00, // code section: 1 func, 0 locals
+ b[0], b[1], b[2], // The instruction, padded with zeroes
+ 0x0b, // end
+ };
+ const size_t size = sizeof(data);
+
+ BinaryReaderError reader;
+ Result result = ReadBinary(data, size, &reader, options);
+ EXPECT_EQ(Result::Error, result);
+
+ // This relies on the binary reader checking whether the opcode is allowed
+ // before reading any further data needed by the instruction.
+ const std::string& message = reader.first_error.message;
+ EXPECT_EQ(0u, message.find("unexpected opcode"))
+ << "Got error message: " << message;
+ }
+}
diff --git a/third_party/wasm2c/src/test-circular-array.cc b/third_party/wasm2c/src/test-circular-array.cc
new file mode 100644
index 0000000000..a2a8919ef4
--- /dev/null
+++ b/third_party/wasm2c/src/test-circular-array.cc
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gtest/gtest.h"
+
+#include <memory>
+
+#include "wabt/circular-array.h"
+
+using namespace wabt;
+
+namespace {
+
+struct TestObject {
+ static int construct_count;
+ static int destruct_count;
+
+ TestObject(int data = 0, int data2 = 0) : data(data), data2(data2) {
+ construct_count++;
+ }
+
+ TestObject(const TestObject& other) {
+ *this = other;
+ construct_count++;
+ }
+
+ TestObject& operator=(const TestObject& other) {
+ data = other.data;
+ data2 = other.data2;
+ return *this;
+ }
+
+ TestObject(TestObject&& other) { *this = std::move(other); }
+
+ TestObject& operator=(TestObject&& other) {
+ data = other.data;
+ data2 = other.data2;
+ other.moved = true;
+ return *this;
+ }
+
+ ~TestObject() {
+ if (!moved) {
+ destruct_count++;
+ }
+ }
+
+ int data = 0;
+ int data2 = 0;
+ bool moved = false;
+};
+
+// static
+int TestObject::construct_count = 0;
+// static
+int TestObject::destruct_count = 0;
+
+class CircularArrayTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Reset to 0 even if the previous test leaked objects to keep the tests
+ // independent.
+ TestObject::construct_count = 0;
+ TestObject::destruct_count = 0;
+ }
+
+ virtual void TearDown() {
+ ASSERT_EQ(0, TestObject::construct_count - TestObject::destruct_count)
+ << "construct count: " << TestObject::construct_count
+ << ", destruct_count: " << TestObject::destruct_count;
+ }
+
+ template <size_t N>
+ void AssertCircularArrayEq(const CircularArray<TestObject, N>& ca,
+ const std::vector<int>& expected) {
+ if (expected.empty()) {
+ ASSERT_EQ(0U, ca.size());
+ ASSERT_TRUE(ca.empty());
+ } else {
+ ASSERT_EQ(expected.size(), ca.size());
+ ASSERT_FALSE(ca.empty());
+
+ ASSERT_EQ(expected.front(), ca.front().data);
+ ASSERT_EQ(expected.back(), ca.back().data);
+
+ for (size_t i = 0; i < ca.size(); ++i) {
+ ASSERT_EQ(expected[i], ca.at(i).data);
+ ASSERT_EQ(expected[i], ca[i].data);
+ }
+ }
+ }
+};
+
+} // end anonymous namespace
+
+// Basic API tests
+
+TEST_F(CircularArrayTest, default_constructor) {
+ CircularArray<TestObject, 2> ca;
+}
+
+TEST_F(CircularArrayTest, at) {
+ CircularArray<TestObject, 2> ca;
+ ca.push_back(TestObject(1));
+ ca.push_back(TestObject(2));
+
+ ASSERT_EQ(1, ca.at(0).data);
+ ASSERT_EQ(2, ca.at(1).data);
+}
+
+TEST_F(CircularArrayTest, const_at) {
+ CircularArray<TestObject, 2> ca;
+ const auto& cca = ca;
+
+ ca.push_back(TestObject(1));
+ ca.push_back(TestObject(2));
+
+ ASSERT_EQ(1, cca.at(0).data);
+ ASSERT_EQ(2, cca.at(1).data);
+}
+
+TEST_F(CircularArrayTest, operator_brackets) {
+ CircularArray<TestObject, 2> ca;
+ ca.push_back(TestObject(1));
+ ca.push_back(TestObject(2));
+
+ ASSERT_EQ(1, ca[0].data);
+ ASSERT_EQ(2, ca[1].data);
+}
+
+TEST_F(CircularArrayTest, const_operator_brackets) {
+ CircularArray<TestObject, 2> ca;
+ const auto& cca = ca;
+
+ ca.push_back(TestObject(1));
+ ca.push_back(TestObject(2));
+
+ ASSERT_EQ(1, cca[0].data);
+ ASSERT_EQ(2, cca[1].data);
+}
+
+TEST_F(CircularArrayTest, back) {
+ CircularArray<TestObject, 2> ca;
+
+ ca.push_back(TestObject(1));
+ ASSERT_EQ(1, ca.back().data);
+
+ ca.push_back(TestObject(2));
+ ASSERT_EQ(2, ca.back().data);
+}
+
+TEST_F(CircularArrayTest, const_back) {
+ CircularArray<TestObject, 2> ca;
+ const auto& cca = ca;
+
+ ca.push_back(TestObject(1));
+ ASSERT_EQ(1, cca.back().data);
+
+ ca.push_back(TestObject(2));
+ ASSERT_EQ(2, cca.back().data);
+}
+
+TEST_F(CircularArrayTest, empty) {
+ CircularArray<TestObject, 2> ca;
+
+ ASSERT_TRUE(ca.empty());
+
+ ca.push_back(TestObject(1));
+ ASSERT_FALSE(ca.empty());
+
+ ca.push_back(TestObject(2));
+ ASSERT_FALSE(ca.empty());
+
+ ca.pop_back();
+ ASSERT_FALSE(ca.empty());
+
+ ca.pop_back();
+ ASSERT_TRUE(ca.empty());
+}
+
+TEST_F(CircularArrayTest, front) {
+ CircularArray<TestObject, 2> ca;
+
+ ca.push_back(TestObject(1));
+ ASSERT_EQ(1, ca.front().data);
+
+ ca.push_back(TestObject(2));
+ ASSERT_EQ(1, ca.front().data);
+}
+
+TEST_F(CircularArrayTest, const_front) {
+ CircularArray<TestObject, 2> ca;
+ const auto& cca = ca;
+
+ ca.push_back(TestObject(1));
+ ASSERT_EQ(1, cca.front().data);
+
+ ca.push_back(TestObject(2));
+ ASSERT_EQ(1, cca.front().data);
+}
+
+TEST_F(CircularArrayTest, size) {
+ CircularArray<TestObject, 2> ca;
+
+ ASSERT_EQ(0U, ca.size());
+
+ ca.push_back(TestObject(1));
+ ASSERT_EQ(1U, ca.size());
+
+ ca.push_back(TestObject(2));
+ ASSERT_EQ(2U, ca.size());
+
+ ca.pop_back();
+ ASSERT_EQ(1U, ca.size());
+
+ ca.pop_back();
+ ASSERT_EQ(0U, ca.size());
+}
+
+TEST_F(CircularArrayTest, clear) {
+ CircularArray<TestObject, 2> ca;
+
+ ca.push_back(TestObject(1));
+ ca.push_back(TestObject(2));
+ ASSERT_EQ(2U, ca.size());
+
+ ca.clear();
+ ASSERT_EQ(0U, ca.size());
+}
+
+// More involved tests
+
+TEST_F(CircularArrayTest, circular) {
+ CircularArray<TestObject, 4> ca;
+
+ ca.push_back(TestObject(1));
+ AssertCircularArrayEq(ca, {1});
+
+ ca.push_back(TestObject(2));
+ AssertCircularArrayEq(ca, {1, 2});
+
+ ca.push_back(TestObject(3));
+ AssertCircularArrayEq(ca, {1, 2, 3});
+
+ ca.pop_front();
+ AssertCircularArrayEq(ca, {2, 3});
+
+ ca.push_back(TestObject(4));
+ AssertCircularArrayEq(ca, {2, 3, 4});
+
+ ca.pop_front();
+ AssertCircularArrayEq(ca, {3, 4});
+
+ ca.pop_front();
+ AssertCircularArrayEq(ca, {4});
+
+ ca.push_back(TestObject(5));
+ AssertCircularArrayEq(ca, {4, 5});
+
+ ca.push_back(TestObject(6));
+ AssertCircularArrayEq(ca, {4, 5, 6});
+
+ ca.pop_back();
+ AssertCircularArrayEq(ca, {4, 5});
+
+ ca.pop_back();
+ AssertCircularArrayEq(ca, {4});
+
+ ca.pop_front();
+ AssertCircularArrayEq(ca, {});
+}
diff --git a/third_party/wasm2c/src/test-filenames.cc b/third_party/wasm2c/src/test-filenames.cc
new file mode 100644
index 0000000000..b734f58bda
--- /dev/null
+++ b/third_party/wasm2c/src/test-filenames.cc
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gtest/gtest.h"
+
+#include "wabt/filenames.h"
+
+using namespace wabt;
+
+namespace {
+
+void assert_string_view_eq(const char* s, std::string_view sv) {
+ size_t len = std::strlen(s);
+ ASSERT_EQ(len, sv.size());
+ for (size_t i = 0; i < len; ++i) {
+ ASSERT_EQ(s[i], sv[i]);
+ }
+}
+
+} // end anonymous namespace
+
+TEST(filenames, GetBasename) {
+ assert_string_view_eq("foo.txt", GetBasename("/bar/dir/foo.txt"));
+ assert_string_view_eq("foo.txt", GetBasename("bar/dir/foo.txt"));
+ assert_string_view_eq("foo.txt", GetBasename("/foo.txt"));
+ assert_string_view_eq("foo.txt", GetBasename("\\bar\\dir\\foo.txt"));
+ assert_string_view_eq("foo.txt", GetBasename("bar\\dir\\foo.txt"));
+ assert_string_view_eq("foo.txt", GetBasename("\\foo.txt"));
+ assert_string_view_eq("foo.txt", GetBasename("foo.txt"));
+ assert_string_view_eq("foo", GetBasename("foo"));
+ assert_string_view_eq("", GetBasename(""));
+}
+
+TEST(filenames, GetExtension) {
+ assert_string_view_eq(".txt", GetExtension("/bar/dir/foo.txt"));
+ assert_string_view_eq(".txt", GetExtension("bar/dir/foo.txt"));
+ assert_string_view_eq(".txt", GetExtension("foo.txt"));
+ assert_string_view_eq("", GetExtension("foo"));
+ assert_string_view_eq("", GetExtension(""));
+}
+
+TEST(filenames, StripExtension) {
+ assert_string_view_eq("/bar/dir/foo", StripExtension("/bar/dir/foo.txt"));
+ assert_string_view_eq("bar/dir/foo", StripExtension("bar/dir/foo.txt"));
+ assert_string_view_eq("foo", StripExtension("foo.txt"));
+ assert_string_view_eq("foo", StripExtension("foo"));
+ assert_string_view_eq("", StripExtension(""));
+}
diff --git a/third_party/wasm2c/src/test-hexfloat.cc b/third_party/wasm2c/src/test-hexfloat.cc
new file mode 100644
index 0000000000..d732c99202
--- /dev/null
+++ b/third_party/wasm2c/src/test-hexfloat.cc
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdio>
+#include <thread>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+#include "wabt/literal.h"
+
+#define FOREACH_UINT32_MULTIPLIER 1
+
+#define FOREACH_UINT32(bits) \
+ uint32_t last_bits = 0; \
+ uint32_t bits = shard; \
+ int last_top_byte = -1; \
+ for (; bits >= last_bits; \
+ last_bits = bits, bits += num_threads_ * FOREACH_UINT32_MULTIPLIER)
+
+#define LOG_COMPLETION(bits) \
+ if (shard == 0) { \
+ int top_byte = bits >> 24; \
+ if (top_byte != last_top_byte) { \
+ printf("value: 0x%08x (%d%%)\r", bits, \
+ static_cast<int>(static_cast<double>(bits) * 100 / UINT32_MAX)); \
+ fflush(stdout); \
+ last_top_byte = top_byte; \
+ } \
+ }
+
+#define LOG_DONE() \
+ if (shard == 0) { \
+ printf("done.\n"); \
+ fflush(stdout); \
+ }
+
+using namespace wabt;
+
+template <typename T, typename F>
+T bit_cast(F value) {
+ T result;
+ memcpy(&result, &value, sizeof(result));
+ return result;
+}
+
+static bool is_infinity_or_nan(uint32_t float_bits) {
+ return ((float_bits >> 23) & 0xff) == 0xff;
+}
+
+static bool is_infinity_or_nan(uint64_t double_bits) {
+ return ((double_bits >> 52) & 0x7ff) == 0x7ff;
+}
+
+class ThreadedTest : public ::testing::Test {
+ protected:
+ static constexpr int kDefaultNumThreads = 2;
+
+ virtual void SetUp() {
+ num_threads_ = std::thread::hardware_concurrency();
+ if (num_threads_ == 0)
+ num_threads_ = kDefaultNumThreads;
+ }
+
+ virtual void RunShard(int shard) = 0;
+
+ void RunThreads() {
+ std::vector<std::thread> threads;
+
+ for (int i = 0; i < num_threads_; ++i) {
+ threads.emplace_back(&ThreadedTest::RunShard, this, i);
+ }
+
+ for (std::thread& thread : threads) {
+ thread.join();
+ }
+ }
+
+ int num_threads_;
+};
+
+/* floats */
+class AllFloatsParseTest : public ThreadedTest {
+ protected:
+ virtual void RunShard(int shard) {
+ char buffer[100];
+ FOREACH_UINT32(bits) {
+ LOG_COMPLETION(bits);
+ if (is_infinity_or_nan(bits))
+ continue;
+
+ float value = bit_cast<float>(bits);
+ int len = snprintf(buffer, sizeof(buffer), "%a", value);
+
+ uint32_t me;
+ ASSERT_EQ(Result::Ok,
+ ParseFloat(LiteralType::Hexfloat, buffer, buffer + len, &me));
+ ASSERT_EQ(me, bits);
+ }
+ LOG_DONE();
+ }
+};
+
+TEST_F(AllFloatsParseTest, Run) {
+ RunThreads();
+}
+
+class AllFloatsWriteTest : public ThreadedTest {
+ protected:
+ virtual void RunShard(int shard) {
+ char buffer[100];
+ FOREACH_UINT32(bits) {
+ LOG_COMPLETION(bits);
+ if (is_infinity_or_nan(bits))
+ continue;
+
+ WriteFloatHex(buffer, sizeof(buffer), bits);
+
+ char* endptr;
+ float them_float = strtof(buffer, &endptr);
+ uint32_t them_bits = bit_cast<uint32_t>(them_float);
+ ASSERT_EQ(bits, them_bits);
+ }
+ LOG_DONE();
+ }
+};
+
+TEST_F(AllFloatsWriteTest, Run) {
+ RunThreads();
+}
+
+class AllFloatsRoundtripTest : public ThreadedTest {
+ protected:
+ static LiteralType ClassifyFloat(uint32_t float_bits) {
+ if (is_infinity_or_nan(float_bits)) {
+ if (float_bits & 0x7fffff) {
+ return LiteralType::Nan;
+ } else {
+ return LiteralType::Infinity;
+ }
+ } else {
+ return LiteralType::Hexfloat;
+ }
+ }
+
+ virtual void RunShard(int shard) {
+ char buffer[100];
+ FOREACH_UINT32(bits) {
+ LOG_COMPLETION(bits);
+ WriteFloatHex(buffer, sizeof(buffer), bits);
+ int len = strlen(buffer);
+
+ uint32_t new_bits;
+ ASSERT_EQ(Result::Ok, ParseFloat(ClassifyFloat(bits), buffer,
+ buffer + len, &new_bits));
+ ASSERT_EQ(new_bits, bits);
+ }
+ LOG_DONE();
+ }
+};
+
+TEST_F(AllFloatsRoundtripTest, Run) {
+ RunThreads();
+}
+
+/* doubles */
+class ManyDoublesParseTest : public ThreadedTest {
+ protected:
+ virtual void RunShard(int shard) {
+ char buffer[100];
+ FOREACH_UINT32(halfbits) {
+ LOG_COMPLETION(halfbits);
+ uint64_t bits = (static_cast<uint64_t>(halfbits) << 32) | halfbits;
+ if (is_infinity_or_nan(bits))
+ continue;
+
+ double value = bit_cast<double>(bits);
+ int len = snprintf(buffer, sizeof(buffer), "%a", value);
+
+ uint64_t me;
+ ASSERT_EQ(Result::Ok,
+ ParseDouble(LiteralType::Hexfloat, buffer, buffer + len, &me));
+ ASSERT_EQ(me, bits);
+ }
+ LOG_DONE();
+ }
+};
+
+TEST_F(ManyDoublesParseTest, Run) {
+ RunThreads();
+}
+
+class ManyDoublesWriteTest : public ThreadedTest {
+ protected:
+ virtual void RunShard(int shard) {
+ char buffer[100];
+ FOREACH_UINT32(halfbits) {
+ LOG_COMPLETION(halfbits);
+ uint64_t bits = (static_cast<uint64_t>(halfbits) << 32) | halfbits;
+ if (is_infinity_or_nan(bits))
+ continue;
+
+ WriteDoubleHex(buffer, sizeof(buffer), bits);
+
+ char* endptr;
+ double them_double = strtod(buffer, &endptr);
+ uint64_t them_bits = bit_cast<uint64_t>(them_double);
+ ASSERT_EQ(bits, them_bits);
+ }
+ LOG_DONE();
+ }
+};
+
+TEST_F(ManyDoublesWriteTest, Run) {
+ RunThreads();
+}
+
+class ManyDoublesRoundtripTest : public ThreadedTest {
+ protected:
+ static LiteralType ClassifyDouble(uint64_t double_bits) {
+ if (is_infinity_or_nan(double_bits)) {
+ if (double_bits & 0xfffffffffffffULL) {
+ return LiteralType::Nan;
+ } else {
+ return LiteralType::Infinity;
+ }
+ } else {
+ return LiteralType::Hexfloat;
+ }
+ }
+
+ virtual void RunShard(int shard) {
+ char buffer[100];
+ FOREACH_UINT32(halfbits) {
+ LOG_COMPLETION(halfbits);
+ uint64_t bits = (static_cast<uint64_t>(halfbits) << 32) | halfbits;
+ WriteDoubleHex(buffer, sizeof(buffer), bits);
+ int len = strlen(buffer);
+
+ uint64_t new_bits;
+ ASSERT_EQ(Result::Ok, ParseDouble(ClassifyDouble(bits), buffer,
+ buffer + len, &new_bits));
+ ASSERT_EQ(new_bits, bits);
+ }
+ LOG_DONE();
+ }
+};
+
+TEST_F(ManyDoublesRoundtripTest, Run) {
+ RunThreads();
+}
diff --git a/third_party/wasm2c/src/test-interp.cc b/third_party/wasm2c/src/test-interp.cc
new file mode 100644
index 0000000000..11e4770fca
--- /dev/null
+++ b/third_party/wasm2c/src/test-interp.cc
@@ -0,0 +1,723 @@
+/*
+ * Copyright 2020 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gtest/gtest.h"
+
+#include "wabt/binary-reader.h"
+#include "wabt/error-formatter.h"
+
+#include "wabt/interp/binary-reader-interp.h"
+#include "wabt/interp/interp.h"
+
+using namespace wabt;
+using namespace wabt::interp;
+
+class InterpTest : public ::testing::Test {
+ public:
+ void ReadModule(const std::vector<u8>& data) {
+ Errors errors;
+ ReadBinaryOptions options;
+ Result result = ReadBinaryInterp("<internal>", data.data(), data.size(),
+ options, &errors, &module_desc_);
+ ASSERT_EQ(Result::Ok, result)
+ << FormatErrorsToString(errors, Location::Type::Binary);
+ }
+
+ void Instantiate(const RefVec& imports = RefVec{}) {
+ mod_ = Module::New(store_, module_desc_);
+ RefPtr<Trap> trap;
+ inst_ = Instance::Instantiate(store_, mod_.ref(), imports, &trap);
+ ASSERT_TRUE(inst_) << trap->message();
+ }
+
+ DefinedFunc::Ptr GetFuncExport(interp::Index index) {
+ EXPECT_LT(index, inst_->exports().size());
+ return store_.UnsafeGet<DefinedFunc>(inst_->exports()[index]);
+ }
+
+ void ExpectBufferStrEq(OutputBuffer& buf, const char* str) {
+ std::string buf_str(buf.data.begin(), buf.data.end());
+ EXPECT_STREQ(buf_str.c_str(), str);
+ }
+
+ Store store_;
+ ModuleDesc module_desc_;
+ Module::Ptr mod_;
+ Instance::Ptr inst_;
+};
+
+TEST_F(InterpTest, Empty) {
+ ASSERT_TRUE(mod_.empty());
+ ReadModule({0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00});
+ Instantiate();
+ ASSERT_FALSE(mod_.empty());
+}
+
+TEST_F(InterpTest, MVP) {
+ // (module
+ // (type (;0;) (func (param i32) (result i32)))
+ // (type (;1;) (func (param f32) (result f32)))
+ // (type (;2;) (func))
+ // (import "foo" "bar" (func (;0;) (type 0)))
+ // (func (;1;) (type 1) (param f32) (result f32)
+ // (f32.const 0x1.5p+5 (;=42;)))
+ // (func (;2;) (type 2))
+ // (table (;0;) 1 2 funcref)
+ // (memory (;0;) 1)
+ // (global (;0;) i32 (i32.const 1))
+ // (export "quux" (func 1))
+ // (start 2)
+ // (elem (;0;) (i32.const 0) 0 1)
+ // (data (;0;) (i32.const 2) "hello"))
+ ReadModule({
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x03, 0x60,
+ 0x01, 0x7f, 0x01, 0x7f, 0x60, 0x01, 0x7d, 0x01, 0x7d, 0x60, 0x00, 0x00,
+ 0x02, 0x0b, 0x01, 0x03, 0x66, 0x6f, 0x6f, 0x03, 0x62, 0x61, 0x72, 0x00,
+ 0x00, 0x03, 0x03, 0x02, 0x01, 0x02, 0x04, 0x05, 0x01, 0x70, 0x01, 0x01,
+ 0x02, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x06, 0x01, 0x7f, 0x00, 0x41,
+ 0x01, 0x0b, 0x07, 0x08, 0x01, 0x04, 0x71, 0x75, 0x75, 0x78, 0x00, 0x01,
+ 0x08, 0x01, 0x02, 0x09, 0x08, 0x01, 0x00, 0x41, 0x00, 0x0b, 0x02, 0x00,
+ 0x01, 0x0a, 0x0c, 0x02, 0x07, 0x00, 0x43, 0x00, 0x00, 0x28, 0x42, 0x0b,
+ 0x02, 0x00, 0x0b, 0x0b, 0x0b, 0x01, 0x00, 0x41, 0x02, 0x0b, 0x05, 0x68,
+ 0x65, 0x6c, 0x6c, 0x6f,
+ });
+
+ EXPECT_EQ(3u, module_desc_.func_types.size());
+ EXPECT_EQ(1u, module_desc_.imports.size());
+ EXPECT_EQ(2u, module_desc_.funcs.size());
+ EXPECT_EQ(1u, module_desc_.tables.size());
+ EXPECT_EQ(1u, module_desc_.memories.size());
+ EXPECT_EQ(1u, module_desc_.globals.size());
+ EXPECT_EQ(0u, module_desc_.tags.size());
+ EXPECT_EQ(1u, module_desc_.exports.size());
+ EXPECT_EQ(1u, module_desc_.starts.size());
+ EXPECT_EQ(1u, module_desc_.elems.size());
+ EXPECT_EQ(1u, module_desc_.datas.size());
+}
+
+namespace {
+
+// (func (export "fac") (param $n i32) (result i32)
+// (local $result i32)
+// (local.set $result (i32.const 1))
+// (loop (result i32)
+// (local.set $result
+// (i32.mul
+// (br_if 1 (local.get $result) (i32.eqz (local.get $n)))
+// (local.get $n)))
+// (local.set $n (i32.sub (local.get $n) (i32.const 1)))
+// (br 0)))
+const std::vector<u8> s_fac_module = {
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01,
+ 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x07,
+ 0x01, 0x03, 0x66, 0x61, 0x63, 0x00, 0x00, 0x0a, 0x22, 0x01, 0x20,
+ 0x01, 0x01, 0x7f, 0x41, 0x01, 0x21, 0x01, 0x03, 0x7f, 0x20, 0x01,
+ 0x20, 0x00, 0x45, 0x0d, 0x01, 0x20, 0x00, 0x6c, 0x21, 0x01, 0x20,
+ 0x00, 0x41, 0x01, 0x6b, 0x21, 0x00, 0x0c, 0x00, 0x0b, 0x0b,
+};
+
+} // namespace
+
+TEST_F(InterpTest, Disassemble) {
+ ReadModule(s_fac_module);
+
+ MemoryStream stream;
+ module_desc_.istream.Disassemble(&stream);
+ auto buf = stream.ReleaseOutputBuffer();
+
+ ExpectBufferStrEq(*buf,
+R"( 0| alloca 1
+ 8| i32.const 1
+ 16| local.set $2, %[-1]
+ 24| local.get $1
+ 32| local.get $3
+ 40| i32.eqz %[-1]
+ 44| br_unless @60, %[-1]
+ 52| br @116
+ 60| local.get $3
+ 68| i32.mul %[-2], %[-1]
+ 72| local.set $2, %[-1]
+ 80| local.get $2
+ 88| i32.const 1
+ 96| i32.sub %[-2], %[-1]
+ 100| local.set $3, %[-1]
+ 108| br @24
+ 116| drop_keep $2 $1
+ 128| return
+)");
+}
+
+TEST_F(InterpTest, Fac) {
+ ReadModule(s_fac_module);
+ Instantiate();
+ auto func = GetFuncExport(0);
+
+ Values results;
+ Trap::Ptr trap;
+ Result result = func->Call(store_, {Value::Make(5)}, results, &trap);
+
+ ASSERT_EQ(Result::Ok, result);
+ EXPECT_EQ(1u, results.size());
+ EXPECT_EQ(120u, results[0].Get<u32>());
+}
+
+TEST_F(InterpTest, Fac_Trace) {
+ ReadModule(s_fac_module);
+ Instantiate();
+ auto func = GetFuncExport(0);
+
+ Values results;
+ Trap::Ptr trap;
+ MemoryStream stream;
+ Result result = func->Call(store_, {Value::Make(2)}, results, &trap, &stream);
+ ASSERT_EQ(Result::Ok, result);
+
+ auto buf = stream.ReleaseOutputBuffer();
+ ExpectBufferStrEq(*buf,
+R"(#0. 0: V:1 | alloca 1
+#0. 8: V:2 | i32.const 1
+#0. 16: V:3 | local.set $2, 1
+#0. 24: V:2 | local.get $1
+#0. 32: V:3 | local.get $3
+#0. 40: V:4 | i32.eqz 2
+#0. 44: V:4 | br_unless @60, 0
+#0. 60: V:3 | local.get $3
+#0. 68: V:4 | i32.mul 1, 2
+#0. 72: V:3 | local.set $2, 2
+#0. 80: V:2 | local.get $2
+#0. 88: V:3 | i32.const 1
+#0. 96: V:4 | i32.sub 2, 1
+#0. 100: V:3 | local.set $3, 1
+#0. 108: V:2 | br @24
+#0. 24: V:2 | local.get $1
+#0. 32: V:3 | local.get $3
+#0. 40: V:4 | i32.eqz 1
+#0. 44: V:4 | br_unless @60, 0
+#0. 60: V:3 | local.get $3
+#0. 68: V:4 | i32.mul 2, 1
+#0. 72: V:3 | local.set $2, 2
+#0. 80: V:2 | local.get $2
+#0. 88: V:3 | i32.const 1
+#0. 96: V:4 | i32.sub 1, 1
+#0. 100: V:3 | local.set $3, 0
+#0. 108: V:2 | br @24
+#0. 24: V:2 | local.get $1
+#0. 32: V:3 | local.get $3
+#0. 40: V:4 | i32.eqz 0
+#0. 44: V:4 | br_unless @60, 1
+#0. 52: V:3 | br @116
+#0. 116: V:3 | drop_keep $2 $1
+#0. 128: V:1 | return
+)");
+}
+
+TEST_F(InterpTest, Local_Trace) {
+ // (func (export "a")
+ // (local i32 i64 f32 f64)
+ // (local.set 0 (i32.const 0))
+ // (local.set 1 (i64.const 1))
+ // (local.set 2 (f32.const 2))
+ // (local.set 3 (f64.const 3)))
+ ReadModule({
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01,
+ 0x60, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x07, 0x05, 0x01, 0x01,
+ 0x61, 0x00, 0x00, 0x0a, 0x26, 0x01, 0x24, 0x04, 0x01, 0x7f, 0x01,
+ 0x7e, 0x01, 0x7d, 0x01, 0x7c, 0x41, 0x00, 0x21, 0x00, 0x42, 0x01,
+ 0x21, 0x01, 0x43, 0x00, 0x00, 0x00, 0x40, 0x21, 0x02, 0x44, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x21, 0x03, 0x0b,
+ });
+
+ Instantiate();
+ auto func = GetFuncExport(0);
+
+ Values results;
+ Trap::Ptr trap;
+ MemoryStream stream;
+ Result result = func->Call(store_, {}, results, &trap, &stream);
+ ASSERT_EQ(Result::Ok, result);
+
+ auto buf = stream.ReleaseOutputBuffer();
+ ExpectBufferStrEq(*buf,
+R"(#0. 0: V:0 | alloca 4
+#0. 8: V:4 | i32.const 0
+#0. 16: V:5 | local.set $5, 0
+#0. 24: V:4 | i64.const 1
+#0. 36: V:5 | local.set $4, 1
+#0. 44: V:4 | f32.const 2
+#0. 52: V:5 | local.set $3, 2
+#0. 60: V:4 | f64.const 3
+#0. 72: V:5 | local.set $2, 3
+#0. 80: V:4 | drop_keep $4 $0
+#0. 92: V:0 | return
+)");
+}
+
+TEST_F(InterpTest, HostFunc) {
+ // (import "" "f" (func $f (param i32) (result i32)))
+ // (func (export "g") (result i32)
+ // (call $f (i32.const 1)))
+ ReadModule({
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0a,
+ 0x02, 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x60, 0x00, 0x01, 0x7f,
+ 0x02, 0x06, 0x01, 0x00, 0x01, 0x66, 0x00, 0x00, 0x03, 0x02,
+ 0x01, 0x01, 0x07, 0x05, 0x01, 0x01, 0x67, 0x00, 0x01, 0x0a,
+ 0x08, 0x01, 0x06, 0x00, 0x41, 0x01, 0x10, 0x00, 0x0b,
+ });
+
+ auto host_func =
+ HostFunc::New(store_, FuncType{{ValueType::I32}, {ValueType::I32}},
+ [](Thread& thread, const Values& params, Values& results,
+ Trap::Ptr* out_trap) -> Result {
+ results[0] = Value::Make(params[0].Get<u32>() + 1);
+ return Result::Ok;
+ });
+
+ Instantiate({host_func->self()});
+
+ Values results;
+ Trap::Ptr trap;
+ Result result = GetFuncExport(0)->Call(store_, {}, results, &trap);
+
+ ASSERT_EQ(Result::Ok, result);
+ EXPECT_EQ(1u, results.size());
+ EXPECT_EQ(2u, results[0].Get<u32>());
+}
+
+TEST_F(InterpTest, HostFunc_PingPong) {
+ // (import "" "f" (func $f (param i32) (result i32)))
+ // (func (export "g") (param i32) (result i32)
+ // (call $f (i32.add (local.get 0) (i32.const 1))))
+ ReadModule({
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60,
+ 0x01, 0x7f, 0x01, 0x7f, 0x02, 0x06, 0x01, 0x00, 0x01, 0x66, 0x00, 0x00,
+ 0x03, 0x02, 0x01, 0x00, 0x07, 0x05, 0x01, 0x01, 0x67, 0x00, 0x01, 0x0a,
+ 0x0b, 0x01, 0x09, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x10, 0x00, 0x0b,
+ });
+
+ auto host_func =
+ HostFunc::New(store_, FuncType{{ValueType::I32}, {ValueType::I32}},
+ [&](Thread& thread, const Values& params, Values& results,
+ Trap::Ptr* out_trap) -> Result {
+ auto val = params[0].Get<u32>();
+ if (val < 10) {
+ return GetFuncExport(0)->Call(
+ store_, {Value::Make(val * 2)}, results, out_trap);
+ }
+ results[0] = Value::Make(val);
+ return Result::Ok;
+ });
+
+ Instantiate({host_func->self()});
+
+ // Should produce the following calls:
+ // g(1) -> f(2) -> g(4) -> f(5) -> g(10) -> f(11) -> return 11
+
+ Values results;
+ Trap::Ptr trap;
+ Result result =
+ GetFuncExport(0)->Call(store_, {Value::Make(1)}, results, &trap);
+
+ ASSERT_EQ(Result::Ok, result);
+ EXPECT_EQ(1u, results.size());
+ EXPECT_EQ(11u, results[0].Get<u32>());
+}
+
+TEST_F(InterpTest, HostFunc_PingPong_SameThread) {
+ // (import "" "f" (func $f (param i32) (result i32)))
+ // (func (export "g") (param i32) (result i32)
+ // (call $f (i32.add (local.get 0) (i32.const 1))))
+ ReadModule({
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60,
+ 0x01, 0x7f, 0x01, 0x7f, 0x02, 0x06, 0x01, 0x00, 0x01, 0x66, 0x00, 0x00,
+ 0x03, 0x02, 0x01, 0x00, 0x07, 0x05, 0x01, 0x01, 0x67, 0x00, 0x01, 0x0a,
+ 0x0b, 0x01, 0x09, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x10, 0x00, 0x0b,
+ });
+
+ Thread thread(store_);
+
+ auto host_func =
+ HostFunc::New(store_, FuncType{{ValueType::I32}, {ValueType::I32}},
+ [&](Thread& t, const Values& params, Values& results,
+ Trap::Ptr* out_trap) -> Result {
+ auto val = params[0].Get<u32>();
+ if (val < 10) {
+ return GetFuncExport(0)->Call(t, {Value::Make(val * 2)},
+ results, out_trap);
+ }
+ results[0] = Value::Make(val);
+ return Result::Ok;
+ });
+
+ Instantiate({host_func->self()});
+
+ // Should produce the following calls:
+ // g(1) -> f(2) -> g(4) -> f(5) -> g(10) -> f(11) -> return 11
+
+ Values results;
+ Trap::Ptr trap;
+ Result result =
+ GetFuncExport(0)->Call(thread, {Value::Make(1)}, results, &trap);
+
+ ASSERT_EQ(Result::Ok, result);
+ EXPECT_EQ(1u, results.size());
+ EXPECT_EQ(11u, results[0].Get<u32>());
+}
+
+TEST_F(InterpTest, HostTrap) {
+ // (import "host" "a" (func $0))
+ // (func $1 call $0)
+ // (start $1)
+ ReadModule({
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01,
+ 0x60, 0x00, 0x00, 0x02, 0x0a, 0x01, 0x04, 0x68, 0x6f, 0x73, 0x74,
+ 0x01, 0x61, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x08, 0x01, 0x01,
+ 0x0a, 0x06, 0x01, 0x04, 0x00, 0x10, 0x00, 0x0b,
+ });
+
+ auto host_func =
+ HostFunc::New(store_, FuncType{{}, {}},
+ [&](Thread& thread, const Values& params, Values& results,
+ Trap::Ptr* out_trap) -> Result {
+ *out_trap = Trap::New(store_, "boom");
+ return Result::Error;
+ });
+
+ mod_ = Module::New(store_, module_desc_);
+ RefPtr<Trap> trap;
+ Instance::Instantiate(store_, mod_.ref(), {host_func->self()}, &trap);
+
+ ASSERT_TRUE(trap);
+ ASSERT_EQ("boom", trap->message());
+}
+
+TEST_F(InterpTest, Rot13) {
+ // (import "host" "mem" (memory $mem 1))
+ // (import "host" "fill_buf" (func $fill_buf (param i32 i32) (result i32)))
+ // (import "host" "buf_done" (func $buf_done (param i32 i32)))
+ //
+ // (func $rot13c (param $c i32) (result i32)
+ // (local $uc i32)
+ //
+ // ;; No change if < 'A'.
+ // (if (i32.lt_u (local.get $c) (i32.const 65))
+ // (return (local.get $c)))
+ //
+ // ;; Clear 5th bit of c, to force uppercase. 0xdf = 0b11011111
+ // (local.set $uc (i32.and (local.get $c) (i32.const 0xdf)))
+ //
+ // ;; In range ['A', 'M'] return |c| + 13.
+ // (if (i32.le_u (local.get $uc) (i32.const 77))
+ // (return (i32.add (local.get $c) (i32.const 13))))
+ //
+ // ;; In range ['N', 'Z'] return |c| - 13.
+ // (if (i32.le_u (local.get $uc) (i32.const 90))
+ // (return (i32.sub (local.get $c) (i32.const 13))))
+ //
+ // ;; No change for everything else.
+ // (return (local.get $c))
+ // )
+ //
+ // (func (export "rot13")
+ // (local $size i32)
+ // (local $i i32)
+ //
+ // ;; Ask host to fill memory [0, 1024) with data.
+ // (call $fill_buf (i32.const 0) (i32.const 1024))
+ //
+ // ;; The host returns the size filled.
+ // (local.set $size)
+ //
+ // ;; Loop over all bytes and rot13 them.
+ // (block $exit
+ // (loop $top
+ // ;; if (i >= size) break
+ // (if (i32.ge_u (local.get $i) (local.get $size)) (br $exit))
+ //
+ // ;; mem[i] = rot13c(mem[i])
+ // (i32.store8
+ // (local.get $i)
+ // (call $rot13c
+ // (i32.load8_u (local.get $i))))
+ //
+ // ;; i++
+ // (local.set $i (i32.add (local.get $i) (i32.const 1)))
+ // (br $top)
+ // )
+ // )
+ //
+ // (call $buf_done (i32.const 0) (local.get $size))
+ // )
+ ReadModule({
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x14, 0x04, 0x60,
+ 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x02, 0x7f, 0x7f, 0x00, 0x60, 0x01,
+ 0x7f, 0x01, 0x7f, 0x60, 0x00, 0x00, 0x02, 0x2d, 0x03, 0x04, 0x68, 0x6f,
+ 0x73, 0x74, 0x03, 0x6d, 0x65, 0x6d, 0x02, 0x00, 0x01, 0x04, 0x68, 0x6f,
+ 0x73, 0x74, 0x08, 0x66, 0x69, 0x6c, 0x6c, 0x5f, 0x62, 0x75, 0x66, 0x00,
+ 0x00, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x08, 0x62, 0x75, 0x66, 0x5f, 0x64,
+ 0x6f, 0x6e, 0x65, 0x00, 0x01, 0x03, 0x03, 0x02, 0x02, 0x03, 0x07, 0x09,
+ 0x01, 0x05, 0x72, 0x6f, 0x74, 0x31, 0x33, 0x00, 0x03, 0x0a, 0x74, 0x02,
+ 0x39, 0x01, 0x01, 0x7f, 0x20, 0x00, 0x41, 0xc1, 0x00, 0x49, 0x04, 0x40,
+ 0x20, 0x00, 0x0f, 0x0b, 0x20, 0x00, 0x41, 0xdf, 0x01, 0x71, 0x21, 0x01,
+ 0x20, 0x01, 0x41, 0xcd, 0x00, 0x4d, 0x04, 0x40, 0x20, 0x00, 0x41, 0x0d,
+ 0x6a, 0x0f, 0x0b, 0x20, 0x01, 0x41, 0xda, 0x00, 0x4d, 0x04, 0x40, 0x20,
+ 0x00, 0x41, 0x0d, 0x6b, 0x0f, 0x0b, 0x20, 0x00, 0x0f, 0x0b, 0x38, 0x01,
+ 0x02, 0x7f, 0x41, 0x00, 0x41, 0x80, 0x08, 0x10, 0x00, 0x21, 0x00, 0x02,
+ 0x40, 0x03, 0x40, 0x20, 0x01, 0x20, 0x00, 0x4f, 0x04, 0x40, 0x0c, 0x02,
+ 0x0b, 0x20, 0x01, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x10, 0x02, 0x3a, 0x00,
+ 0x00, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, 0x00, 0x0b, 0x0b,
+ 0x41, 0x00, 0x20, 0x00, 0x10, 0x01, 0x0b,
+ });
+
+ auto host_func =
+ HostFunc::New(store_, FuncType{{ValueType::I32}, {ValueType::I32}},
+ [](Thread& thread, const Values& params, Values& results,
+ Trap::Ptr* out_trap) -> Result {
+ results[0] = Value::Make(params[0].Get<u32>() + 1);
+ return Result::Ok;
+ });
+
+ std::string string_data = "Hello, WebAssembly!";
+
+ auto memory = Memory::New(store_, MemoryType{Limits{1}});
+
+ auto fill_buf = [&](Thread& thread, const Values& params, Values& results,
+ Trap::Ptr* out_trap) -> Result {
+ // (param $ptr i32) (param $max_size i32) (result $size i32)
+ EXPECT_EQ(2u, params.size());
+ EXPECT_EQ(1u, results.size());
+
+ u32 ptr = params[0].Get<u32>();
+ u32 max_size = params[1].Get<u32>();
+ u32 size = std::min(max_size, u32(string_data.size()));
+
+ EXPECT_LT(ptr + size, memory->ByteSize());
+
+ std::copy(string_data.begin(), string_data.begin() + size,
+ memory->UnsafeData() + ptr);
+
+ results[0].Set(size);
+ return Result::Ok;
+ };
+ auto fill_buf_func = HostFunc::New(
+ store_, FuncType{{ValueType::I32, ValueType::I32}, {ValueType::I32}},
+ fill_buf);
+
+ auto buf_done = [&](Thread& thread, const Values& params, Values& results,
+ Trap::Ptr* out_trap) -> Result {
+ // (param $ptr i32) (param $size i32)
+ EXPECT_EQ(2u, params.size());
+ EXPECT_EQ(0u, results.size());
+
+ u32 ptr = params[0].Get<u32>();
+ u32 size = params[1].Get<u32>();
+
+ EXPECT_LT(ptr + size, memory->ByteSize());
+
+ string_data.resize(size);
+ std::copy(memory->UnsafeData() + ptr, memory->UnsafeData() + ptr + size,
+ string_data.begin());
+
+ return Result::Ok;
+ };
+ auto buf_done_func = HostFunc::New(
+ store_, FuncType{{ValueType::I32, ValueType::I32}, {}}, buf_done);
+
+ Instantiate({memory->self(), fill_buf_func->self(), buf_done_func->self()});
+
+ auto rot13 = GetFuncExport(0);
+
+ Values results;
+ Trap::Ptr trap;
+ ASSERT_EQ(Result::Ok, rot13->Call(store_, {}, results, &trap));
+
+ ASSERT_EQ("Uryyb, JroNffrzoyl!", string_data);
+
+ ASSERT_EQ(Result::Ok, rot13->Call(store_, {}, results, &trap));
+
+ ASSERT_EQ("Hello, WebAssembly!", string_data);
+}
+
+class InterpGCTest : public InterpTest {
+ public:
+ void SetUp() override { before_new = store_.object_count(); }
+
+ void TearDown() override {
+ // Reset instance and module, in case they were allocated.
+ inst_.reset();
+ mod_.reset();
+
+ store_.Collect();
+ EXPECT_EQ(before_new, store_.object_count());
+ }
+
+ size_t before_new;
+};
+
+TEST_F(InterpGCTest, Collect_Basic) {
+ auto foreign = Foreign::New(store_, nullptr);
+ auto after_new = store_.object_count();
+ EXPECT_EQ(before_new + 1, after_new);
+
+ // Remove root, but object is not destroyed until collect.
+ foreign.reset();
+ EXPECT_EQ(after_new, store_.object_count());
+}
+
+TEST_F(InterpGCTest, Collect_GlobalCycle) {
+ auto gt = GlobalType{ValueType::ExternRef, Mutability::Var};
+ auto g1 = Global::New(store_, gt, Value::Make(Ref::Null));
+ auto g2 = Global::New(store_, gt, Value::Make(g1->self()));
+ g1->Set(store_, g2->self());
+
+ auto after_new = store_.object_count();
+ EXPECT_EQ(before_new + 2, after_new);
+
+ // Remove g1 root, but it's kept alive by g2.
+ g1.reset();
+ store_.Collect();
+ EXPECT_EQ(after_new, store_.object_count());
+
+ // Remove g2 root, now both should be removed.
+ g2.reset();
+}
+
+TEST_F(InterpGCTest, Collect_TableCycle) {
+ auto tt = TableType{ValueType::ExternRef, Limits{2}};
+ auto t1 = Table::New(store_, tt);
+ auto t2 = Table::New(store_, tt);
+ auto t3 = Table::New(store_, tt);
+
+ t1->Set(store_, 0, t1->self()); // t1 references itself.
+ t2->Set(store_, 0, t3->self());
+ t3->Set(store_, 0, t2->self()); // t2 and t3 reference each other.
+ t3->Set(store_, 1, t1->self()); // t3 also references t1.
+
+ auto after_new = store_.object_count();
+ EXPECT_EQ(before_new + 3, after_new);
+
+ // Remove t1 and t2 roots, but their kept alive by t3.
+ t1.reset();
+ t2.reset();
+ store_.Collect();
+ EXPECT_EQ(after_new, store_.object_count());
+
+ // Remove t3 root, now all should be removed.
+ t3.reset();
+}
+
+TEST_F(InterpGCTest, Collect_Func) {
+ ReadModule(s_fac_module);
+ Instantiate();
+ auto func = GetFuncExport(0);
+
+ auto after_new = store_.object_count();
+ EXPECT_EQ(before_new + 3, after_new); // module, instance, func.
+
+ // Reset module and instance roots, but they'll be kept alive by the func.
+ mod_.reset();
+ inst_.reset();
+ store_.Collect();
+ EXPECT_EQ(after_new, store_.object_count());
+}
+
+TEST_F(InterpGCTest, Collect_InstanceImport) {
+ // (import "" "f" (func))
+ // (import "" "t" (table 0 funcref))
+ // (import "" "m" (memory 0))
+ // (import "" "g" (global i32))
+ ReadModule({
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01,
+ 0x60, 0x00, 0x00, 0x02, 0x19, 0x04, 0x00, 0x01, 0x66, 0x00, 0x00,
+ 0x00, 0x01, 0x74, 0x01, 0x70, 0x00, 0x00, 0x00, 0x01, 0x6d, 0x02,
+ 0x00, 0x00, 0x00, 0x01, 0x67, 0x03, 0x7f, 0x00,
+ });
+ auto f = HostFunc::New(store_, FuncType{{}, {}},
+ [](Thread& thread, const Values&, Values&,
+ Trap::Ptr*) -> Result { return Result::Ok; });
+ auto t = Table::New(store_, TableType{ValueType::FuncRef, Limits{0}});
+ auto m = Memory::New(store_, MemoryType{Limits{0}});
+ auto g = Global::New(store_, GlobalType{ValueType::I32, Mutability::Const},
+ Value::Make(5));
+
+ Instantiate({f->self(), t->self(), m->self(), g->self()});
+ auto after_new = store_.object_count();
+ EXPECT_EQ(before_new + 6, after_new); // module, instance, f, t, m, g
+
+ // Instance keeps all imports alive.
+ f.reset();
+ t.reset();
+ m.reset();
+ g.reset();
+ store_.Collect();
+ EXPECT_EQ(after_new, store_.object_count());
+}
+
+TEST_F(InterpGCTest, Collect_InstanceExport) {
+ // (func (export "f"))
+ // (global (export "g") i32 (i32.const 0))
+ // (table (export "t") 0 funcref)
+ // (memory (export "m") 0)
+ ReadModule({
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01,
+ 0x60, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x04, 0x04, 0x01, 0x70,
+ 0x00, 0x00, 0x05, 0x03, 0x01, 0x00, 0x00, 0x06, 0x06, 0x01, 0x7f,
+ 0x00, 0x41, 0x00, 0x0b, 0x07, 0x11, 0x04, 0x01, 0x66, 0x00, 0x00,
+ 0x01, 0x67, 0x03, 0x00, 0x01, 0x74, 0x01, 0x00, 0x01, 0x6d, 0x02,
+ 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
+ });
+ Instantiate();
+ auto after_new = store_.object_count();
+ EXPECT_EQ(before_new + 7,
+ after_new); // module, instance, f, t, m, g, g-init-func
+
+ // Instance keeps all exports alive, except the init func which can be
+ // collected once its has been run
+ store_.Collect();
+ EXPECT_EQ(after_new - 1, store_.object_count());
+}
+
+TEST_F(InterpGCTest, Collect_DeepRecursion) {
+ const size_t table_count = 65;
+
+ TableType tt = TableType{ValueType::ExternRef, Limits{1}};
+
+ // Create a chain of tables, where each contains
+ // a single reference to the next table.
+
+ Table::Ptr prev_table = Table::New(store_, tt);
+
+ for (size_t i = 1; i < table_count; i++) {
+ Table::Ptr new_table = Table::New(store_, tt);
+
+ new_table->Set(store_, 0, prev_table->self());
+
+ prev_table.reset();
+ prev_table = std::move(new_table);
+ }
+
+ store_.Collect();
+ EXPECT_EQ(table_count + 1, store_.object_count());
+
+ // Remove the last root, now all should be removed.
+ prev_table.reset();
+
+ store_.Collect();
+ EXPECT_EQ(1u, store_.object_count());
+}
+
+// TODO: Test for Thread keeping references alive as locals/params/stack values.
+// This requires better tracking of references than currently exists in the
+// interpreter. (see TODOs in Select/LocalGet/GlobalGet)
diff --git a/third_party/wasm2c/src/test-intrusive-list.cc b/third_party/wasm2c/src/test-intrusive-list.cc
new file mode 100644
index 0000000000..25059caba8
--- /dev/null
+++ b/third_party/wasm2c/src/test-intrusive-list.cc
@@ -0,0 +1,583 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gtest/gtest.h"
+
+#include <memory>
+
+#include "wabt/intrusive-list.h"
+
+using namespace wabt;
+
+namespace {
+
+struct TestObject : intrusive_list_base<TestObject> {
+ static int creation_count;
+
+ TestObject(int data = 0, int data2 = 0) : data(data), data2(data2) {
+ ++creation_count;
+ }
+
+ // Allow move.
+ TestObject(TestObject&& other) {
+ // Don't increment creation_count; we're moving from other.
+ *this = std::move(other);
+ }
+
+ TestObject& operator=(TestObject&& other) {
+ data = other.data;
+ data2 = other.data2;
+ other.moved = true;
+ return *this;
+ }
+
+ // Disallow copy.
+ TestObject(const TestObject&) = delete;
+ TestObject& operator=(const TestObject&) = delete;
+
+ ~TestObject() {
+ if (!moved) {
+ creation_count--;
+ }
+ }
+
+ int data;
+ int data2;
+ bool moved = false;
+};
+
+// static
+int TestObject::creation_count = 0;
+
+using TestObjectList = intrusive_list<TestObject>;
+
+class IntrusiveListTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Reset to 0 even if the previous test leaked objects to keep the tests
+ // independent.
+ TestObject::creation_count = 0;
+ }
+
+ virtual void TearDown() { ASSERT_EQ(0, TestObject::creation_count); }
+
+ TestObjectList NewList(const std::vector<int>& data_values) {
+ TestObjectList result;
+ for (auto data_value : data_values)
+ result.emplace_back(data_value);
+ return result;
+ }
+
+ void AssertListEq(const TestObjectList& list,
+ const std::vector<int>& expected) {
+ size_t count = 0;
+ for (const TestObject& node : list) {
+ ASSERT_EQ(expected[count++], node.data);
+ }
+ ASSERT_EQ(count, expected.size());
+ }
+
+ void AssertListEq(const TestObjectList& list,
+ const std::vector<int>& expected,
+ const std::vector<int>& expected2) {
+ assert(expected.size() == expected2.size());
+ size_t count = 0;
+ for (const TestObject& node : list) {
+ ASSERT_EQ(expected[count], node.data);
+ ASSERT_EQ(expected2[count], node.data2);
+ count++;
+ }
+ ASSERT_EQ(count, expected.size());
+ }
+};
+
+} // end anonymous namespace
+
+TEST_F(IntrusiveListTest, default_constructor) {
+ TestObjectList list;
+}
+
+TEST_F(IntrusiveListTest, node_constructor) {
+ TestObjectList list(std::make_unique<TestObject>(1));
+ AssertListEq(list, {1});
+}
+
+TEST_F(IntrusiveListTest, node_move_constructor) {
+ TestObjectList list(TestObject(1));
+ AssertListEq(list, {1});
+}
+
+TEST_F(IntrusiveListTest, move_constructor) {
+ TestObjectList list1 = NewList({1, 2, 3});
+ TestObjectList list2(std::move(list1));
+ AssertListEq(list1, {});
+ AssertListEq(list2, {1, 2, 3});
+}
+
+TEST_F(IntrusiveListTest, move_assignment_operator) {
+ TestObjectList list1 = NewList({1, 2, 3});
+ TestObjectList list2;
+
+ list2 = std::move(list1);
+ AssertListEq(list1, {});
+ AssertListEq(list2, {1, 2, 3});
+}
+
+namespace {
+
+class IntrusiveListIteratorTest : public IntrusiveListTest {
+ protected:
+ virtual void SetUp() {
+ IntrusiveListTest::SetUp();
+
+ list_.emplace_back(1);
+ list_.emplace_back(2);
+ list_.emplace_back(3);
+ }
+
+ virtual void TearDown() {
+ list_.clear();
+
+ IntrusiveListTest::TearDown();
+ }
+
+ template <typename Iter>
+ void TestForward(Iter first, Iter last, const std::vector<int>& expected) {
+ size_t count = 0;
+ while (first != last) {
+ ASSERT_EQ(expected[count], first->data);
+ ++first;
+ ++count;
+ }
+ ASSERT_EQ(count, expected.size());
+ }
+
+ template <typename Iter>
+ void TestBackward(Iter first, Iter last, const std::vector<int>& expected) {
+ size_t count = 0;
+ while (first != last) {
+ --last;
+ ASSERT_EQ(expected[count], last->data);
+ ++count;
+ }
+ ASSERT_EQ(count, expected.size());
+ }
+
+ TestObjectList list_;
+ const TestObjectList& clist_ = list_;
+};
+
+} // end of anonymous namespace
+
+TEST_F(IntrusiveListIteratorTest, begin_end_forward) {
+ TestForward(list_.begin(), list_.end(), {1, 2, 3});
+ TestForward(clist_.begin(), clist_.end(), {1, 2, 3});
+}
+
+TEST_F(IntrusiveListIteratorTest, rbegin_rend_forward) {
+ TestForward(list_.rbegin(), list_.rend(), {3, 2, 1});
+ TestForward(clist_.rbegin(), clist_.rend(), {3, 2, 1});
+}
+
+TEST_F(IntrusiveListIteratorTest, cbegin_cend_forward) {
+ TestForward(list_.cbegin(), list_.cend(), {1, 2, 3});
+ TestForward(clist_.cbegin(), clist_.cend(), {1, 2, 3});
+}
+
+TEST_F(IntrusiveListIteratorTest, crbegin_crend_forward) {
+ TestForward(list_.crbegin(), list_.crend(), {3, 2, 1});
+ TestForward(clist_.crbegin(), clist_.crend(), {3, 2, 1});
+}
+
+TEST_F(IntrusiveListIteratorTest, begin_end_backward) {
+ TestBackward(list_.begin(), list_.end(), {3, 2, 1});
+ TestBackward(clist_.begin(), clist_.end(), {3, 2, 1});
+}
+
+TEST_F(IntrusiveListIteratorTest, rbegin_rend_backward) {
+ TestBackward(list_.rbegin(), list_.rend(), {1, 2, 3});
+ TestBackward(clist_.rbegin(), clist_.rend(), {1, 2, 3});
+}
+
+TEST_F(IntrusiveListIteratorTest, cbegin_cend_backward) {
+ TestBackward(list_.cbegin(), list_.cend(), {3, 2, 1});
+ TestBackward(clist_.cbegin(), clist_.cend(), {3, 2, 1});
+}
+
+TEST_F(IntrusiveListIteratorTest, crbegin_crend_backward) {
+ TestBackward(list_.crbegin(), list_.crend(), {1, 2, 3});
+ TestBackward(clist_.crbegin(), clist_.crend(), {1, 2, 3});
+}
+
+TEST_F(IntrusiveListTest, size_empty) {
+ TestObjectList list;
+ ASSERT_EQ(0U, list.size());
+ ASSERT_TRUE(list.empty());
+
+ list.emplace_back(1);
+ ASSERT_EQ(1U, list.size());
+ ASSERT_FALSE(list.empty());
+}
+
+TEST_F(IntrusiveListTest, front_back) {
+ TestObjectList list;
+
+ list.emplace_back(1);
+ ASSERT_EQ(1, list.front().data);
+ ASSERT_EQ(1, list.back().data);
+
+ list.emplace_back(2);
+ ASSERT_EQ(1, list.front().data);
+ ASSERT_EQ(2, list.back().data);
+
+ const TestObjectList& clist = list;
+ ASSERT_EQ(1, clist.front().data);
+ ASSERT_EQ(2, clist.back().data);
+}
+
+TEST_F(IntrusiveListTest, emplace_front) {
+ TestObjectList list;
+
+ // Pass an additional arg to show that forwarding works properly.
+ list.emplace_front(1, 100);
+ list.emplace_front(2, 200);
+ list.emplace_front(3, 300);
+ list.emplace_front(4, 400);
+
+ AssertListEq(list, {4, 3, 2, 1}, {400, 300, 200, 100});
+}
+
+TEST_F(IntrusiveListTest, emplace_back) {
+ TestObjectList list;
+
+ // Pass an additional arg to show that forwarding works properly.
+ list.emplace_back(1, 100);
+ list.emplace_back(2, 200);
+ list.emplace_back(3, 300);
+ list.emplace_back(4, 400);
+
+ AssertListEq(list, {1, 2, 3, 4}, {100, 200, 300, 400});
+}
+
+TEST_F(IntrusiveListTest, push_front_pointer) {
+ TestObjectList list;
+
+ list.push_front(std::make_unique<TestObject>(1));
+ list.push_front(std::make_unique<TestObject>(2));
+ list.push_front(std::make_unique<TestObject>(3));
+
+ AssertListEq(list, {3, 2, 1});
+}
+
+TEST_F(IntrusiveListTest, push_back_pointer) {
+ TestObjectList list;
+
+ list.push_back(std::make_unique<TestObject>(1));
+ list.push_back(std::make_unique<TestObject>(2));
+ list.push_back(std::make_unique<TestObject>(3));
+
+ AssertListEq(list, {1, 2, 3});
+}
+
+TEST_F(IntrusiveListTest, push_front_move) {
+ TestObjectList list;
+
+ list.push_front(TestObject(1));
+ list.push_front(TestObject(2));
+ list.push_front(TestObject(3));
+
+ AssertListEq(list, {3, 2, 1});
+}
+
+TEST_F(IntrusiveListTest, push_back_move) {
+ TestObjectList list;
+
+ list.push_back(TestObject(1));
+ list.push_back(TestObject(2));
+ list.push_back(TestObject(3));
+
+ AssertListEq(list, {1, 2, 3});
+}
+
+TEST_F(IntrusiveListTest, pop_front) {
+ TestObjectList list = NewList({1, 2, 3, 4});
+
+ list.pop_front();
+ AssertListEq(list, {2, 3, 4});
+ list.pop_front();
+ AssertListEq(list, {3, 4});
+ list.pop_front();
+ AssertListEq(list, {4});
+ list.pop_front();
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, pop_back) {
+ TestObjectList list = NewList({1, 2, 3, 4});
+
+ list.pop_back();
+ AssertListEq(list, {1, 2, 3});
+ list.pop_back();
+ AssertListEq(list, {1, 2});
+ list.pop_back();
+ AssertListEq(list, {1});
+ list.pop_back();
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, extract_front) {
+ TestObjectList list = NewList({1, 2, 3});
+
+ std::unique_ptr<TestObject> t1(list.extract_front());
+ ASSERT_EQ(1, t1->data);
+ AssertListEq(list, {2, 3});
+
+ std::unique_ptr<TestObject> t2(list.extract_front());
+ ASSERT_EQ(2, t2->data);
+ AssertListEq(list, {3});
+
+ std::unique_ptr<TestObject> t3(list.extract_front());
+ ASSERT_EQ(3, t3->data);
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, extract_back) {
+ TestObjectList list = NewList({1, 2, 3});
+
+ std::unique_ptr<TestObject> t1(list.extract_back());
+ ASSERT_EQ(3, t1->data);
+ AssertListEq(list, {1, 2});
+
+ std::unique_ptr<TestObject> t2(list.extract_back());
+ ASSERT_EQ(2, t2->data);
+ AssertListEq(list, {1});
+
+ std::unique_ptr<TestObject> t3(list.extract_back());
+ ASSERT_EQ(1, t3->data);
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, emplace) {
+ TestObjectList list;
+
+ // Pass an additional arg to show that forwarding works properly.
+ list.emplace(list.begin(), 2, 200);
+ list.emplace(list.end(), 4, 400);
+ list.emplace(std::next(list.begin()), 3, 300);
+ list.emplace(list.begin(), 1, 100);
+
+ AssertListEq(list, {1, 2, 3, 4}, {100, 200, 300, 400});
+}
+
+TEST_F(IntrusiveListTest, insert_pointer) {
+ TestObjectList list;
+
+ list.insert(list.begin(), std::make_unique<TestObject>(2));
+ list.insert(list.end(), std::make_unique<TestObject>(4));
+ list.insert(std::next(list.begin()), std::make_unique<TestObject>(3));
+ list.insert(list.begin(), std::make_unique<TestObject>(1));
+
+ AssertListEq(list, {1, 2, 3, 4});
+}
+
+TEST_F(IntrusiveListTest, insert_move) {
+ TestObjectList list;
+
+ list.insert(list.begin(), TestObject(2));
+ list.insert(list.end(), TestObject(4));
+ list.insert(std::next(list.begin()), TestObject(3));
+ list.insert(list.begin(), TestObject(1));
+
+ AssertListEq(list, {1, 2, 3, 4});
+}
+
+TEST_F(IntrusiveListTest, extract) {
+ TestObjectList list = NewList({1, 2, 3, 4});
+
+ TestObjectList::iterator t1_iter = std::next(list.begin(), 0);
+ TestObjectList::iterator t2_iter = std::next(list.begin(), 1);
+ TestObjectList::iterator t3_iter = std::next(list.begin(), 2);
+ TestObjectList::iterator t4_iter = std::next(list.begin(), 3);
+
+ std::unique_ptr<TestObject> t2(list.extract(t2_iter));
+ ASSERT_EQ(2, t2->data);
+ AssertListEq(list, {1, 3, 4});
+
+ std::unique_ptr<TestObject> t4(list.extract(t4_iter));
+ ASSERT_EQ(4, t4->data);
+ AssertListEq(list, {1, 3});
+
+ std::unique_ptr<TestObject> t1(list.extract(t1_iter));
+ ASSERT_EQ(1, t1->data);
+ AssertListEq(list, {3});
+
+ std::unique_ptr<TestObject> t3(list.extract(t3_iter));
+ ASSERT_EQ(3, t3->data);
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, erase) {
+ TestObjectList list = NewList({1, 2, 3, 4});
+
+ TestObjectList::iterator t1_iter = std::next(list.begin(), 0);
+ TestObjectList::iterator t2_iter = std::next(list.begin(), 1);
+ TestObjectList::iterator t3_iter = std::next(list.begin(), 2);
+ TestObjectList::iterator t4_iter = std::next(list.begin(), 3);
+
+ // erase returns an iterator to the following node.
+ ASSERT_EQ(3, list.erase(t2_iter)->data);
+ AssertListEq(list, {1, 3, 4});
+
+ ASSERT_EQ(list.end(), list.erase(t4_iter));
+ AssertListEq(list, {1, 3});
+
+ ASSERT_EQ(3, list.erase(t1_iter)->data);
+ AssertListEq(list, {3});
+
+ ASSERT_EQ(list.end(), list.erase(t3_iter));
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, erase_range) {
+ TestObjectList list = NewList({1, 2, 3, 4, 5, 6});
+
+ // OK to erase an empty range.
+ list.erase(list.begin(), list.begin());
+ list.erase(list.end(), list.end());
+
+ // Erase the first element (1).
+ list.erase(list.begin(), std::next(list.begin()));
+ AssertListEq(list, {2, 3, 4, 5, 6});
+
+ // Erase the last element (6).
+ list.erase(std::prev(list.end()), list.end());
+ AssertListEq(list, {2, 3, 4, 5});
+
+ // Erase [3, 4] => [2, 5]
+ list.erase(std::next(list.begin()), std::prev(list.end()));
+ AssertListEq(list, {2, 5});
+
+ // Erase the rest.
+ list.erase(list.begin(), list.end());
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, swap) {
+ TestObjectList list1 = NewList({1, 2, 3, 4});
+
+ TestObjectList list2 = NewList({100, 200, 300});
+
+ AssertListEq(list1, {1, 2, 3, 4});
+ AssertListEq(list2, {100, 200, 300});
+
+ list1.swap(list2);
+
+ AssertListEq(list1, {100, 200, 300});
+ AssertListEq(list2, {1, 2, 3, 4});
+}
+
+TEST_F(IntrusiveListTest, clear) {
+ TestObjectList list = NewList({1, 2, 3, 4});
+
+ ASSERT_FALSE(list.empty());
+
+ list.clear();
+
+ ASSERT_EQ(0U, list.size());
+ ASSERT_TRUE(list.empty());
+}
+
+TEST_F(IntrusiveListTest, splice_list) {
+ TestObjectList list1 = NewList({1, 2, 3});
+
+ // Splice at beginning.
+ TestObjectList list2 = NewList({100, 200});
+ list1.splice(list1.begin(), list2);
+ AssertListEq(list1, {100, 200, 1, 2, 3});
+ AssertListEq(list2, {});
+
+ // Splice at end.
+ TestObjectList list3 = NewList({500, 600, 700});
+ list1.splice(list1.end(), list3);
+ AssertListEq(list1, {100, 200, 1, 2, 3, 500, 600, 700});
+ AssertListEq(list3, {});
+
+ // Splice in the middle.
+ TestObjectList list4 = NewList({400});
+ list1.splice(std::next(list1.begin(), 4), list4);
+ AssertListEq(list1, {100, 200, 1, 2, 400, 3, 500, 600, 700});
+ AssertListEq(list4, {});
+}
+
+TEST_F(IntrusiveListTest, splice_list_move) {
+ TestObjectList list1 = NewList({1, 2, 3});
+
+ // Splice at beginning.
+ list1.splice(list1.begin(), NewList({100, 200}));
+ AssertListEq(list1, {100, 200, 1, 2, 3});
+
+ // Splice at end.
+ list1.splice(list1.end(), NewList({500, 600, 700}));
+ AssertListEq(list1, {100, 200, 1, 2, 3, 500, 600, 700});
+
+ // Splice in the middle.
+ list1.splice(std::next(list1.begin(), 4), NewList({400}));
+ AssertListEq(list1, {100, 200, 1, 2, 400, 3, 500, 600, 700});
+}
+
+TEST_F(IntrusiveListTest, splice_node) {
+ TestObjectList list1 = NewList({1, 2, 3});
+
+ // Splice at beginning.
+ TestObjectList list2 = NewList({100, 200});
+ list1.splice(list1.begin(), list2, list2.begin());
+ AssertListEq(list1, {100, 1, 2, 3});
+ AssertListEq(list2, {200});
+
+ // Splice at end.
+ TestObjectList list3 = NewList({500, 600, 700});
+ list1.splice(list1.end(), list3, std::next(list3.begin(), 2));
+ AssertListEq(list1, {100, 1, 2, 3, 700});
+ AssertListEq(list3, {500, 600});
+
+ // Splice in the middle.
+ TestObjectList list4 = NewList({400});
+ list1.splice(std::next(list1.begin(), 3), list4, list4.begin());
+ AssertListEq(list1, {100, 1, 2, 400, 3, 700});
+ AssertListEq(list4, {});
+}
+
+TEST_F(IntrusiveListTest, splice_range) {
+ TestObjectList list1 = NewList({1, 2, 3});
+
+ // Splice at beginning.
+ TestObjectList list2 = NewList({100, 200, 300});
+ list1.splice(list1.begin(), list2, list2.begin(), std::prev(list2.end()));
+ AssertListEq(list1, {100, 200, 1, 2, 3});
+ AssertListEq(list2, {300});
+
+ // Splice at end.
+ TestObjectList list3 = NewList({500, 600, 700});
+ list1.splice(list1.end(), list3, std::next(list3.begin()), list3.end());
+ AssertListEq(list1, {100, 200, 1, 2, 3, 600, 700});
+ AssertListEq(list3, {500});
+
+ // Splice in the middle.
+ TestObjectList list4 = NewList({400});
+ list1.splice(std::next(list1.begin(), 4), list4, list4.begin(), list4.end());
+ AssertListEq(list1, {100, 200, 1, 2, 400, 3, 600, 700});
+ AssertListEq(list4, {});
+}
diff --git a/third_party/wasm2c/src/test-literal.cc b/third_party/wasm2c/src/test-literal.cc
new file mode 100644
index 0000000000..d9990d16f7
--- /dev/null
+++ b/third_party/wasm2c/src/test-literal.cc
@@ -0,0 +1,838 @@
+/*
+ * Copyright 2018 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cassert>
+#include <cstdio>
+#include <thread>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+#include "wabt/literal.h"
+
+using namespace wabt;
+
+namespace {
+
+enum ParseIntTypeCombo {
+ UnsignedOnly,
+ SignedAndUnsigned,
+ Both,
+};
+
+template <typename T>
+void AssertIntEquals(
+ T expected,
+ const char* s,
+ Result (*parse_int)(const char*, const char*, T*, ParseIntType),
+ ParseIntTypeCombo parse_type = Both) {
+ const char* const end = s + strlen(s);
+ T actual;
+ if (parse_type == UnsignedOnly || parse_type == Both) {
+ ASSERT_EQ(Result::Ok,
+ parse_int(s, end, &actual, ParseIntType::UnsignedOnly))
+ << s;
+ ASSERT_EQ(expected, actual);
+ } else {
+ ASSERT_EQ(Result::Error,
+ parse_int(s, end, &actual, ParseIntType::UnsignedOnly))
+ << s;
+ }
+
+ if (parse_type == SignedAndUnsigned || parse_type == Both) {
+ ASSERT_EQ(Result::Ok,
+ parse_int(s, end, &actual, ParseIntType::SignedAndUnsigned))
+ << s;
+ ASSERT_EQ(expected, actual);
+ } else {
+ ASSERT_EQ(Result::Error,
+ parse_int(s, end, &actual, ParseIntType::SignedAndUnsigned))
+ << s;
+ }
+}
+
+void AssertInt8Equals(uint8_t expected,
+ const char* s,
+ ParseIntTypeCombo parse_type = Both) {
+ AssertIntEquals(expected, s, ParseInt8, parse_type);
+}
+
+void AssertInt16Equals(uint16_t expected,
+ const char* s,
+ ParseIntTypeCombo parse_type = Both) {
+ AssertIntEquals(expected, s, ParseInt16, parse_type);
+}
+
+void AssertInt32Equals(uint32_t expected,
+ const char* s,
+ ParseIntTypeCombo parse_type = Both) {
+ AssertIntEquals(expected, s, ParseInt32, parse_type);
+}
+
+void AssertInt64Equals(uint64_t expected,
+ const char* s,
+ ParseIntTypeCombo parse_type = Both) {
+ AssertIntEquals(expected, s, ParseInt64, parse_type);
+}
+
+void AssertUint128Equals(v128 expected, const char* s) {
+ const char* const end = s + strlen(s);
+ v128 actual;
+ ASSERT_EQ(Result::Ok, ParseUint128(s, end, &actual)) << s;
+ ASSERT_EQ(expected, actual);
+}
+
+void AssertInt8Fails(const char* s) {
+ const char* const end = s + strlen(s);
+ uint8_t actual;
+ ASSERT_EQ(Result::Error,
+ ParseInt8(s, end, &actual, ParseIntType::SignedAndUnsigned))
+ << s;
+ ASSERT_EQ(Result::Error,
+ ParseInt8(s, end, &actual, ParseIntType::UnsignedOnly))
+ << s;
+}
+
+void AssertInt16Fails(const char* s) {
+ const char* const end = s + strlen(s);
+ uint16_t actual;
+ ASSERT_EQ(Result::Error,
+ ParseInt16(s, end, &actual, ParseIntType::SignedAndUnsigned))
+ << s;
+ ASSERT_EQ(Result::Error,
+ ParseInt16(s, end, &actual, ParseIntType::UnsignedOnly))
+ << s;
+}
+
+void AssertInt32Fails(const char* s) {
+ const char* const end = s + strlen(s);
+ uint32_t actual;
+ ASSERT_EQ(Result::Error,
+ ParseInt32(s, end, &actual, ParseIntType::SignedAndUnsigned))
+ << s;
+ ASSERT_EQ(Result::Error,
+ ParseInt32(s, end, &actual, ParseIntType::UnsignedOnly))
+ << s;
+}
+
+void AssertInt64Fails(const char* s) {
+ const char* const end = s + strlen(s);
+ uint64_t actual;
+ ASSERT_EQ(Result::Error,
+ ParseInt64(s, end, &actual, ParseIntType::SignedAndUnsigned))
+ << s;
+ ASSERT_EQ(Result::Error,
+ ParseInt64(s, end, &actual, ParseIntType::UnsignedOnly))
+ << s;
+}
+
+void AssertUint64Equals(uint64_t expected, const char* s) {
+ uint64_t actual;
+ ASSERT_EQ(Result::Ok, ParseUint64(s, s + strlen(s), &actual)) << s;
+ ASSERT_EQ(expected, actual);
+}
+
+void AssertUint64Fails(const char* s) {
+ uint64_t actual_bits;
+ ASSERT_EQ(Result::Error, ParseUint64(s, s + strlen(s), &actual_bits)) << s;
+}
+
+void AssertUint128Fails(const char* s) {
+ v128 actual;
+ ASSERT_EQ(Result::Error, ParseUint128(s, s + strlen(s), &actual)) << s;
+}
+
+void AssertHexFloatEquals(uint32_t expected_bits, const char* s) {
+ uint32_t actual_bits;
+ ASSERT_EQ(Result::Ok,
+ ParseFloat(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits))
+ << s;
+ ASSERT_EQ(expected_bits, actual_bits) << s;
+}
+
+void AssertHexFloatFails(const char* s) {
+ uint32_t actual_bits;
+ ASSERT_EQ(Result::Error,
+ ParseFloat(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits))
+ << s;
+}
+
+void AssertHexDoubleEquals(uint64_t expected_bits, const char* s) {
+ uint64_t actual_bits;
+ ASSERT_EQ(Result::Ok,
+ ParseDouble(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits))
+ << s;
+ ASSERT_EQ(expected_bits, actual_bits);
+}
+
+void AssertHexDoubleFails(const char* s) {
+ uint64_t actual_bits;
+ ASSERT_EQ(Result::Error,
+ ParseDouble(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits))
+ << s;
+}
+
+} // end anonymous namespace
+
+TEST(ParseInt8, Both) {
+ AssertInt8Equals(0, "0");
+ AssertInt8Equals(100, "100");
+ AssertInt8Equals(123, "123");
+ AssertInt8Equals(127, "127");
+ AssertInt8Equals(255, "255");
+ AssertInt8Equals(0xca, "0xca");
+ AssertInt8Equals(0x7f, "0x7f");
+ AssertInt8Equals(0x80, "0x80");
+ AssertInt8Equals(0xff, "0xff");
+}
+
+TEST(ParseInt8, SignedAndUnsigned) {
+ AssertInt8Equals(128, "-128", SignedAndUnsigned);
+ AssertInt8Equals(-0x80, "-0x80", SignedAndUnsigned);
+ AssertInt8Equals(255, "-1", SignedAndUnsigned);
+ AssertInt8Equals(-1, "-0x1", SignedAndUnsigned);
+ AssertInt8Equals(1, "+1", SignedAndUnsigned);
+ AssertInt8Equals(-0x7b, "-0x7B", SignedAndUnsigned);
+ AssertInt8Equals(0xab, "+0xab", SignedAndUnsigned);
+}
+
+TEST(ParseInt8, Invalid) {
+ AssertInt8Fails("");
+ AssertInt8Fails("-100hello");
+ AssertInt8Fails("0XAB");
+ AssertInt8Fails("0xga");
+ AssertInt8Fails("two");
+}
+
+TEST(ParseInt8, Underscores) {
+ AssertInt8Equals(123, "12_3", Both);
+ AssertInt8Equals(123, "+12_3", SignedAndUnsigned);
+ AssertInt8Equals(-123, "-1_23", SignedAndUnsigned);
+ AssertInt8Equals(19, "1______9", Both);
+ AssertInt8Equals(0xab, "0xa_b", Both);
+ AssertInt8Equals(0xab, "+0xa_b", SignedAndUnsigned);
+ AssertInt8Equals(-0x7b, "-0x7_b", SignedAndUnsigned);
+}
+
+TEST(ParseInt8, Overflow) {
+ AssertInt8Fails("256");
+ AssertInt8Fails("-129");
+ AssertInt8Fails("0x100");
+ AssertInt8Fails("-0x81");
+ AssertInt8Fails("1231231231231231231231");
+}
+
+TEST(ParseInt16, Both) {
+ AssertInt16Equals(0, "0");
+ AssertInt16Equals(1000, "1000");
+ AssertInt16Equals(12345, "12345");
+ AssertInt16Equals(32767, "32767");
+ AssertInt16Equals(65535, "65535");
+ AssertInt16Equals(0xcafe, "0xcafe");
+ AssertInt16Equals(0x7fff, "0x7fff");
+ AssertInt16Equals(0x8000, "0x8000");
+ AssertInt16Equals(0xffff, "0xffff");
+}
+
+TEST(ParseInt16, SignedAndUnsigned) {
+ AssertInt16Equals(32768, "-32768", SignedAndUnsigned);
+ AssertInt16Equals(-0x8000, "-0x8000", SignedAndUnsigned);
+ AssertInt16Equals(65535, "-1", SignedAndUnsigned);
+ AssertInt16Equals(-1, "-0x1", SignedAndUnsigned);
+ AssertInt16Equals(1, "+1", SignedAndUnsigned);
+ AssertInt16Equals(-0x7bcd, "-0x7BCD", SignedAndUnsigned);
+ AssertInt16Equals(0xabcd, "+0xabcd", SignedAndUnsigned);
+}
+
+TEST(ParseInt16, Invalid) {
+ AssertInt16Fails("");
+ AssertInt16Fails("-100hello");
+ AssertInt16Fails("0XABCD");
+ AssertInt16Fails("0xgabb");
+ AssertInt16Fails("two");
+}
+
+TEST(ParseInt16, Underscores) {
+ AssertInt16Equals(12345, "12_345", Both);
+ AssertInt16Equals(12345, "+12_345", SignedAndUnsigned);
+ AssertInt16Equals(-12345, "-123_45", SignedAndUnsigned);
+ AssertInt16Equals(19, "1______9", Both);
+ AssertInt16Equals(0xabcd, "0xa_b_c_d", Both);
+ AssertInt16Equals(0xabcd, "+0xa_b_c_d", SignedAndUnsigned);
+ AssertInt16Equals(-0x7bcd, "-0x7_b_c_d", SignedAndUnsigned);
+}
+
+TEST(ParseInt16, Overflow) {
+ AssertInt16Fails("65536");
+ AssertInt16Fails("-32769");
+ AssertInt16Fails("0x10000");
+ AssertInt16Fails("-0x8001");
+ AssertInt16Fails("1231231231231231231231");
+}
+
+TEST(ParseInt32, Both) {
+ AssertInt32Equals(0, "0");
+ AssertInt32Equals(1000, "1000");
+ AssertInt32Equals(123456789, "123456789");
+ AssertInt32Equals(2147483647, "2147483647");
+ AssertInt32Equals(4294967295u, "4294967295");
+ AssertInt32Equals(0xcafef00du, "0xcafef00d");
+ AssertInt32Equals(0x7fffffff, "0x7fffffff");
+ AssertInt32Equals(0x80000000u, "0x80000000");
+ AssertInt32Equals(0xffffffffu, "0xffffffff");
+}
+
+TEST(ParseInt32, SignedAndUnsigned) {
+ AssertInt32Equals(2147483648, "-2147483648", SignedAndUnsigned);
+ AssertInt32Equals(-0x80000000ll, "-0x80000000", SignedAndUnsigned);
+ AssertInt32Equals(4294967295u, "-1", SignedAndUnsigned);
+ AssertInt32Equals(-1, "-0x1", SignedAndUnsigned);
+ AssertInt32Equals(1, "+1", SignedAndUnsigned);
+ AssertInt32Equals(-0xabcd, "-0xABCD", SignedAndUnsigned);
+ AssertInt32Equals(0xabcd, "+0xabcd", SignedAndUnsigned);
+}
+
+TEST(ParseInt32, Invalid) {
+ AssertInt32Fails("");
+ AssertInt32Fails("-100hello");
+ AssertInt32Fails("0XABCDEF");
+ AssertInt32Fails("0xgabba");
+ AssertInt32Fails("two");
+}
+
+TEST(ParseInt32, Underscores) {
+ AssertInt32Equals(123456789, "12_345_6789", Both);
+ AssertInt32Equals(123456789, "+12_345_6789", SignedAndUnsigned);
+ AssertInt32Equals(-123456789, "-12345_6789", SignedAndUnsigned);
+ AssertInt32Equals(19, "1______9", Both);
+ AssertInt32Equals(0xabcd, "0xa_b_c_d", Both);
+ AssertInt32Equals(0xabcd, "+0xa_b_c_d", SignedAndUnsigned);
+ AssertInt32Equals(-0xabcd, "-0xa_b_c_d", SignedAndUnsigned);
+}
+
+TEST(ParseInt32, Overflow) {
+ AssertInt32Fails("4294967296");
+ AssertInt32Fails("-2147483649");
+ AssertInt32Fails("0x100000000");
+ AssertInt32Fails("-0x80000001");
+ AssertInt32Fails("1231231231231231231231");
+}
+
+TEST(ParseInt64, Both) {
+ AssertInt64Equals(0, "0");
+ AssertInt64Equals(1000, "1000");
+ AssertInt64Equals(123456789, "123456789");
+ AssertInt64Equals(9223372036854775807ull, "9223372036854775807");
+ AssertInt64Equals(18446744073709551615ull, "18446744073709551615");
+ AssertInt64Equals(0x7fffffffffffffffull, "0x7fffffffffffffff");
+ AssertInt64Equals(0x8000000000000000ull, "0x8000000000000000");
+ AssertInt64Equals(0xffffffffffffffffull, "0xffffffffffffffff");
+}
+
+TEST(ParseInt64, SignedAndUnsigned) {
+ AssertInt64Equals(9223372036854775808ull, "-9223372036854775808",
+ SignedAndUnsigned);
+ AssertInt64Equals(18446744073709551615ull, "-1", SignedAndUnsigned);
+ AssertInt64Equals(-1, "-0x1", SignedAndUnsigned);
+ AssertInt64Equals(1, "+1", SignedAndUnsigned);
+ AssertInt64Equals(-0x0bcdefabcdefabcdll, "-0x0BCDEFABCDEFABCD",
+ SignedAndUnsigned);
+ AssertInt64Equals(0xabcdefabcdefabcdll, "+0xabcdefabcdefabcd",
+ SignedAndUnsigned);
+}
+
+TEST(ParseInt64, Invalid) {
+ AssertInt64Fails("");
+ AssertInt64Fails("-100hello");
+ AssertInt64Fails("0XABCDEF");
+ AssertInt64Fails("0xgabba");
+ AssertInt64Fails("two");
+}
+
+TEST(ParseInt64, Underscores) {
+ AssertInt64Equals(123456789, "12_345_6789", Both);
+ AssertInt64Equals(123456789, "+12_345_6789", SignedAndUnsigned);
+ AssertInt64Equals(-123456789, "-12345_6789", SignedAndUnsigned);
+ AssertInt64Equals(19, "1______9", Both);
+ AssertInt64Equals(0xabcd, "0xa_b_c_d", Both);
+ AssertInt64Equals(0xabcd, "+0xa_b_c_d", SignedAndUnsigned);
+ AssertInt64Equals(-0xabcd, "-0xa_b_c_d", SignedAndUnsigned);
+}
+
+TEST(ParseInt64, Overflow) {
+ AssertInt64Fails("18446744073709551616");
+ AssertInt64Fails("-9223372036854775809");
+ AssertInt32Fails("0x10000000000000000");
+ AssertInt32Fails("-0x80000000000000001");
+ AssertInt64Fails("1231231231231231231231");
+}
+
+TEST(ParseUint64, Basic) {
+ AssertUint64Equals(0, "0");
+ AssertUint64Equals(1000, "1000");
+ AssertUint64Equals(123456789, "123456789");
+ AssertUint64Equals(1844674407370955161ull, "1844674407370955161");
+ AssertUint64Equals(18446744073709551615ull, "18446744073709551615");
+
+ AssertUint64Equals(0, "0x0");
+ AssertUint64Equals(0x1000, "0x1000");
+ AssertUint64Equals(0x123456789, "0x123456789");
+ AssertUint64Equals(0xabcdef, "0xabcdef");
+ AssertUint64Equals(0xffffffffffffffull, "0xffffffffffffff");
+ AssertUint64Equals(0xfffffffffffffffull, "0xfffffffffffffff");
+
+ AssertUint64Equals(0xabcdefabcdefabcdull, "0xabcdefabcdefabcd");
+}
+
+TEST(ParseUint64, NoOctal) {
+ AssertUint64Equals(100, "0100");
+ AssertUint64Equals(888, "0000888");
+}
+
+TEST(ParseUint64, Invalid) {
+ AssertUint64Fails("");
+ AssertUint64Fails("-100");
+ AssertUint64Fails("0XABCDEF");
+ AssertUint64Fails("0xgabba");
+ AssertUint64Fails("two");
+}
+
+TEST(ParseUint64, Underscores) {
+ AssertUint64Equals(123456789, "12_345_6789");
+ AssertUint64Equals(19, "1______9");
+ AssertUint64Equals(0xabcd, "0xa_b_c_d");
+}
+
+TEST(ParseUint64, Overflow) {
+ AssertUint64Fails("0x10000000000000000");
+ AssertUint64Fails("18446744073709551616");
+ AssertUint64Fails("62857453058642199420");
+ AssertUint64Fails("82000999361882825820");
+ AssertUint64Fails("126539114687237086210");
+ AssertUint64Fails("10000000000000000000000000000000000000000");
+}
+
+TEST(ParseUint128, Basic) {
+ AssertUint128Equals({0, 0, 0, 0}, "0");
+ AssertUint128Equals({1, 0, 0, 0}, "1");
+ AssertUint128Equals({0x100f0e0d, 0x0c0b0a09, 0x08070605, 0x04030201},
+ "5332529520247008778714484145835150861");
+ AssertUint128Equals({0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff},
+ "340282366920938463463374607431768211455");
+ AssertUint128Equals({0, 0, 1, 0}, "18446744073709551616");
+}
+
+TEST(ParseUint128, Invalid) {
+ AssertUint128Fails("");
+ AssertUint128Fails("-1");
+ AssertUint128Fails("340282366920938463463374607431768211456");
+ AssertUint128Fails("123a456");
+}
+
+TEST(ParseFloat, NonCanonical) {
+ AssertHexFloatEquals(0x3f800000, "0x00000000000000000000001.0p0");
+ AssertHexFloatEquals(0x3f800000, "0x1.00000000000000000000000p0");
+ AssertHexFloatEquals(0x3f800000, "0x0.0000000000000000000001p88");
+}
+
+TEST(ParseFloat, Basic) {
+ AssertHexFloatEquals(0, "0x0p0");
+ AssertHexFloatEquals(0x3f800000, "0x1");
+}
+
+TEST(ParseFloat, Rounding) {
+ // |------- 23 bits -----| V-- extra bit
+ //
+ // 11111111111111111111101 0 ==> no rounding
+ AssertHexFloatEquals(0x7f7ffffd, "0x1.fffffap127");
+ // 11111111111111111111101 1 ==> round up
+ AssertHexFloatEquals(0x7f7ffffe, "0x1.fffffbp127");
+ // 11111111111111111111110 0 ==> no rounding
+ AssertHexFloatEquals(0x7f7ffffe, "0x1.fffffcp127");
+ // 11111111111111111111110 1 ==> round down
+ AssertHexFloatEquals(0x7f7ffffe, "0x1.fffffdp127");
+ // 11111111111111111111111 0 ==> no rounding
+ AssertHexFloatEquals(0x7f7fffff, "0x1.fffffep127");
+}
+
+// Duplicate the spec tests here for easier debugging.
+TEST(ParseFloat, RoundingSpec) {
+ static const struct {
+ const char* input;
+ uint32_t output;
+ } kTests[] = {
+ {"+0x1.00000100000000000p-50", 0x26800000},
+ {"-0x1.00000100000000000p-50", 0xa6800000},
+ {"+0x1.00000100000000001p-50", 0x26800001},
+ {"-0x1.00000100000000001p-50", 0xa6800001},
+ {"+0x1.000001fffffffffffp-50", 0x26800001},
+ {"-0x1.000001fffffffffffp-50", 0xa6800001},
+ {"+0x1.00000200000000000p-50", 0x26800001},
+ {"-0x1.00000200000000000p-50", 0xa6800001},
+ {"+0x1.00000200000000001p-50", 0x26800001},
+ {"-0x1.00000200000000001p-50", 0xa6800001},
+ {"+0x1.000002fffffffffffp-50", 0x26800001},
+ {"-0x1.000002fffffffffffp-50", 0xa6800001},
+ {"+0x1.00000300000000000p-50", 0x26800002},
+ {"-0x1.00000300000000000p-50", 0xa6800002},
+ {"+0x1.00000300000000001p-50", 0x26800002},
+ {"-0x1.00000300000000001p-50", 0xa6800002},
+ {"+0x1.000003fffffffffffp-50", 0x26800002},
+ {"-0x1.000003fffffffffffp-50", 0xa6800002},
+ {"+0x1.00000400000000000p-50", 0x26800002},
+ {"-0x1.00000400000000000p-50", 0xa6800002},
+ {"+0x1.00000400000000001p-50", 0x26800002},
+ {"-0x1.00000400000000001p-50", 0xa6800002},
+ {"+0x1.000004fffffffffffp-50", 0x26800002},
+ {"-0x1.000004fffffffffffp-50", 0xa6800002},
+ {"+0x1.00000500000000000p-50", 0x26800002},
+ {"-0x1.00000500000000000p-50", 0xa6800002},
+ {"+0x1.00000500000000001p-50", 0x26800003},
+ {"-0x1.00000500000000001p-50", 0xa6800003},
+ {"+0x4000.004000000p-64", 0x26800000},
+ {"-0x4000.004000000p-64", 0xa6800000},
+ {"+0x4000.004000001p-64", 0x26800001},
+ {"-0x4000.004000001p-64", 0xa6800001},
+ {"+0x4000.007ffffffp-64", 0x26800001},
+ {"-0x4000.007ffffffp-64", 0xa6800001},
+ {"+0x4000.008000000p-64", 0x26800001},
+ {"-0x4000.008000000p-64", 0xa6800001},
+ {"+0x4000.008000001p-64", 0x26800001},
+ {"-0x4000.008000001p-64", 0xa6800001},
+ {"+0x4000.00bffffffp-64", 0x26800001},
+ {"-0x4000.00bffffffp-64", 0xa6800001},
+ {"+0x4000.00c000000p-64", 0x26800002},
+ {"-0x4000.00c000000p-64", 0xa6800002},
+ {"+0x4000.00c000001p-64", 0x26800002},
+ {"-0x4000.00c000001p-64", 0xa6800002},
+ {"+0x4000.00fffffffp-64", 0x26800002},
+ {"-0x4000.00fffffffp-64", 0xa6800002},
+ {"+0x4000.010000001p-64", 0x26800002},
+ {"-0x4000.010000001p-64", 0xa6800002},
+ {"+0x4000.013ffffffp-64", 0x26800002},
+ {"-0x4000.013ffffffp-64", 0xa6800002},
+ {"+0x4000.014000001p-64", 0x26800003},
+ {"-0x4000.014000001p-64", 0xa6800003},
+ {"+0x1.00000100000000000p+50", 0x58800000},
+ {"-0x1.00000100000000000p+50", 0xd8800000},
+ {"+0x1.00000100000000001p+50", 0x58800001},
+ {"-0x1.00000100000000001p+50", 0xd8800001},
+ {"+0x1.000001fffffffffffp+50", 0x58800001},
+ {"-0x1.000001fffffffffffp+50", 0xd8800001},
+ {"+0x1.00000200000000000p+50", 0x58800001},
+ {"-0x1.00000200000000000p+50", 0xd8800001},
+ {"+0x1.00000200000000001p+50", 0x58800001},
+ {"-0x1.00000200000000001p+50", 0xd8800001},
+ {"+0x1.000002fffffffffffp+50", 0x58800001},
+ {"-0x1.000002fffffffffffp+50", 0xd8800001},
+ {"+0x1.00000300000000000p+50", 0x58800002},
+ {"-0x1.00000300000000000p+50", 0xd8800002},
+ {"+0x1.00000300000000001p+50", 0x58800002},
+ {"-0x1.00000300000000001p+50", 0xd8800002},
+ {"+0x1.000003fffffffffffp+50", 0x58800002},
+ {"-0x1.000003fffffffffffp+50", 0xd8800002},
+ {"+0x1.00000400000000000p+50", 0x58800002},
+ {"-0x1.00000400000000000p+50", 0xd8800002},
+ {"+0x1.00000400000000001p+50", 0x58800002},
+ {"-0x1.00000400000000001p+50", 0xd8800002},
+ {"+0x1.000004fffffffffffp+50", 0x58800002},
+ {"-0x1.000004fffffffffffp+50", 0xd8800002},
+ {"+0x1.00000500000000000p+50", 0x58800002},
+ {"-0x1.00000500000000000p+50", 0xd8800002},
+ {"+0x1.00000500000000001p+50", 0x58800003},
+ {"-0x1.00000500000000001p+50", 0xd8800003},
+ {"+0x4000004000000", 0x58800000},
+ {"-0x4000004000000", 0xd8800000},
+ {"+0x4000004000001", 0x58800001},
+ {"-0x4000004000001", 0xd8800001},
+ {"+0x4000007ffffff", 0x58800001},
+ {"-0x4000007ffffff", 0xd8800001},
+ {"+0x4000008000000", 0x58800001},
+ {"-0x4000008000000", 0xd8800001},
+ {"+0x4000008000001", 0x58800001},
+ {"-0x4000008000001", 0xd8800001},
+ {"+0x400000bffffff", 0x58800001},
+ {"-0x400000bffffff", 0xd8800001},
+ {"+0x400000c000000", 0x58800002},
+ {"-0x400000c000000", 0xd8800002},
+ {"+0x0.00000100000000000p-126", 0x0},
+ {"-0x0.00000100000000000p-126", 0x80000000},
+ {"+0x0.00000100000000001p-126", 0x1},
+ {"-0x0.00000100000000001p-126", 0x80000001},
+ {"+0x0.00000101000000000p-126", 0x1},
+ {"+0x0.000001fffffffffffp-126", 0x1},
+ {"-0x0.000001fffffffffffp-126", 0x80000001},
+ {"+0x0.00000200000000000p-126", 0x1},
+ {"-0x0.00000200000000000p-126", 0x80000001},
+ {"+0x0.00000200000000001p-126", 0x1},
+ {"-0x0.00000200000000001p-126", 0x80000001},
+ {"+0x0.000002fffffffffffp-126", 0x1},
+ {"-0x0.000002fffffffffffp-126", 0x80000001},
+ {"+0x0.00000300000000000p-126", 0x2},
+ {"-0x0.00000300000000000p-126", 0x80000002},
+ {"+0x0.00000300000000001p-126", 0x2},
+ {"-0x0.00000300000000001p-126", 0x80000002},
+ {"+0x0.000003fffffffffffp-126", 0x2},
+ {"-0x0.000003fffffffffffp-126", 0x80000002},
+ {"+0x0.00000400000000000p-126", 0x2},
+ {"-0x0.00000400000000000p-126", 0x80000002},
+ {"+0x0.00000400000000001p-126", 0x2},
+ {"-0x0.00000400000000001p-126", 0x80000002},
+ {"+0x0.000004fffffffffffp-126", 0x2},
+ {"-0x0.000004fffffffffffp-126", 0x80000002},
+ {"+0x0.00000500000000000p-126", 0x2},
+ {"-0x0.00000500000000000p-126", 0x80000002},
+ {"+0x0.00000500000000001p-126", 0x3},
+ {"-0x0.00000500000000001p-126", 0x80000003},
+ {"+0x1.fffffe8p127", 0x7f7fffff},
+ {"-0x1.fffffe8p127", 0xff7fffff},
+ {"+0x1.fffffefffffff8p127", 0x7f7fffff},
+ {"-0x1.fffffefffffff8p127", 0xff7fffff},
+ {"+0x1.fffffefffffffffffp127", 0x7f7fffff},
+ {"-0x1.fffffefffffffffffp127", 0xff7fffff},
+ };
+
+ for (auto test : kTests) {
+ AssertHexFloatEquals(test.output, test.input);
+ }
+}
+
+TEST(ParseFloat, OutOfRange) {
+ AssertHexFloatFails("0x1p128");
+ AssertHexFloatFails("-0x1p128");
+ AssertHexFloatFails("0x1.ffffffp127");
+ AssertHexFloatFails("-0x1.ffffffp127");
+}
+
+TEST(ParseDouble, NonCanonical) {
+ AssertHexDoubleEquals(0x3ff0000000000000, "0x00000000000000000000001.0p0");
+ AssertHexDoubleEquals(0x3ff0000000000000, "0x1.00000000000000000000000p0");
+ AssertHexDoubleEquals(0x3ff0000000000000, "0x0.0000000000000000000001p88");
+}
+
+TEST(ParseDouble, Rounding) {
+ // |-------------------- 52 bits ---------------------| V-- extra bit
+ //
+ // 1111111111111111111111111111111111111111111111111101 0 ==> no rounding
+ AssertHexDoubleEquals(0x7feffffffffffffd, "0x1.ffffffffffffd0p1023");
+ // 1111111111111111111111111111111111111111111111111101 1 ==> round up
+ AssertHexDoubleEquals(0x7feffffffffffffe, "0x1.ffffffffffffd8p1023");
+ // 1111111111111111111111111111111111111111111111111110 0 ==> no rounding
+ AssertHexDoubleEquals(0x7feffffffffffffe, "0x1.ffffffffffffe0p1023");
+ // 1111111111111111111111111111111111111111111111111110 1 ==> round down
+ AssertHexDoubleEquals(0x7feffffffffffffe, "0x1.ffffffffffffe8p1023");
+ // 1111111111111111111111111111111111111111111111111111 0 ==> no rounding
+ AssertHexDoubleEquals(0x7fefffffffffffff, "0x1.fffffffffffff0p1023");
+}
+
+TEST(ParseDouble, OutOfRange) {
+ AssertHexDoubleFails("0x1p1024");
+ AssertHexDoubleFails("-0x1p1024");
+ AssertHexDoubleFails("0x1.fffffffffffff8p1023");
+ AssertHexDoubleFails("-0x1.fffffffffffff8p1023");
+}
+
+// Duplicate the spec tests here for easier debugging.
+TEST(ParseDouble, RoundingSpec) {
+ static const struct {
+ const char* input;
+ uint64_t output;
+ } kTests[] = {
+ {"+0x1.000000000000080000000000p-600", 1905022642377719808ull},
+ {"-0x1.000000000000080000000000p-600", 11128394679232495616ull},
+ {"+0x1.000000000000080000000001p-600", 1905022642377719809ull},
+ {"-0x1.000000000000080000000001p-600", 11128394679232495617ull},
+ {"+0x1.0000000000000fffffffffffp-600", 1905022642377719809ull},
+ {"-0x1.0000000000000fffffffffffp-600", 11128394679232495617ull},
+ {"+0x1.000000000000100000000000p-600", 1905022642377719809ull},
+ {"-0x1.000000000000100000000000p-600", 11128394679232495617ull},
+ {"+0x1.000000000000100000000001p-600", 1905022642377719809ull},
+ {"-0x1.000000000000100000000001p-600", 11128394679232495617ull},
+ {"+0x1.00000000000017ffffffffffp-600", 1905022642377719809ull},
+ {"-0x1.00000000000017ffffffffffp-600", 11128394679232495617ull},
+ {"+0x1.000000000000180000000000p-600", 1905022642377719810ull},
+ {"-0x1.000000000000180000000000p-600", 11128394679232495618ull},
+ {"+0x1.000000000000180000000001p-600", 1905022642377719810ull},
+ {"-0x1.000000000000180000000001p-600", 11128394679232495618ull},
+ {"+0x1.0000000000001fffffffffffp-600", 1905022642377719810ull},
+ {"-0x1.0000000000001fffffffffffp-600", 11128394679232495618ull},
+ {"+0x1.000000000000200000000000p-600", 1905022642377719810ull},
+ {"-0x1.000000000000200000000000p-600", 11128394679232495618ull},
+ {"+0x1.000000000000200000000001p-600", 1905022642377719810ull},
+ {"-0x1.000000000000200000000001p-600", 11128394679232495618ull},
+ {"+0x1.00000000000027ffffffffffp-600", 1905022642377719810ull},
+ {"-0x1.00000000000027ffffffffffp-600", 11128394679232495618ull},
+ {"+0x1.000000000000280000000001p-600", 1905022642377719811ull},
+ {"-0x1.000000000000280000000001p-600", 11128394679232495619ull},
+ {"+0x8000000.000000400000000000p-627", 1905022642377719808ull},
+ {"-0x8000000.000000400000000000p-627", 11128394679232495616ull},
+ {"+0x8000000.000000400000000001p-627", 1905022642377719809ull},
+ {"-0x8000000.000000400000000001p-627", 11128394679232495617ull},
+ {"+0x8000000.0000007fffffffffffp-627", 1905022642377719809ull},
+ {"-0x8000000.0000007fffffffffffp-627", 11128394679232495617ull},
+ {"+0x8000000.000000800000000000p-627", 1905022642377719809ull},
+ {"-0x8000000.000000800000000000p-627", 11128394679232495617ull},
+ {"+0x8000000.000000800000000001p-627", 1905022642377719809ull},
+ {"-0x8000000.000000800000000001p-627", 11128394679232495617ull},
+ {"+0x8000000.000000bfffffffffffp-627", 1905022642377719809ull},
+ {"-0x8000000.000000bfffffffffffp-627", 11128394679232495617ull},
+ {"+0x8000000.000000c00000000000p-627", 1905022642377719810ull},
+ {"-0x8000000.000000c00000000000p-627", 11128394679232495618ull},
+ {"+0x8000000.000000c00000000001p-627", 1905022642377719810ull},
+ {"-0x8000000.000000c00000000001p-627", 11128394679232495618ull},
+ {"+0x8000000.000000ffffffffffffp-627", 1905022642377719810ull},
+ {"-0x8000000.000000ffffffffffffp-627", 11128394679232495618ull},
+ {"+0x8000000.000001000000000000p-627", 1905022642377719810ull},
+ {"-0x8000000.000001000000000000p-627", 11128394679232495618ull},
+ {"+0x8000000.000001000000000001p-627", 1905022642377719810ull},
+ {"-0x8000000.000001000000000001p-627", 11128394679232495618ull},
+ {"+0x8000000.0000013fffffffffffp-627", 1905022642377719810ull},
+ {"-0x8000000.0000013fffffffffffp-627", 11128394679232495618ull},
+ {"+0x8000000.000001400000000001p-627", 1905022642377719811ull},
+ {"-0x8000000.000001400000000001p-627", 11128394679232495619ull},
+ {"+0x1.000000000000080000000000p+600", 7309342195222315008ull},
+ {"-0x1.000000000000080000000000p+600", 16532714232077090816ull},
+ {"+0x1.000000000000080000000001p+600", 7309342195222315009ull},
+ {"-0x1.000000000000080000000001p+600", 16532714232077090817ull},
+ {"+0x1.0000000000000fffffffffffp+600", 7309342195222315009ull},
+ {"-0x1.0000000000000fffffffffffp+600", 16532714232077090817ull},
+ {"+0x1.000000000000100000000000p+600", 7309342195222315009ull},
+ {"-0x1.000000000000100000000000p+600", 16532714232077090817ull},
+ {"+0x1.000000000000100000000001p+600", 7309342195222315009ull},
+ {"-0x1.000000000000100000000001p+600", 16532714232077090817ull},
+ {"+0x1.00000000000017ffffffffffp+600", 7309342195222315009ull},
+ {"-0x1.00000000000017ffffffffffp+600", 16532714232077090817ull},
+ {"+0x1.000000000000180000000000p+600", 7309342195222315010ull},
+ {"-0x1.000000000000180000000000p+600", 16532714232077090818ull},
+ {"+0x1.000000000000180000000001p+600", 7309342195222315010ull},
+ {"-0x1.000000000000180000000001p+600", 16532714232077090818ull},
+ {"+0x1.0000000000001fffffffffffp+600", 7309342195222315010ull},
+ {"-0x1.0000000000001fffffffffffp+600", 16532714232077090818ull},
+ {"+0x1.000000000000200000000000p+600", 7309342195222315010ull},
+ {"-0x1.000000000000200000000000p+600", 16532714232077090818ull},
+ {"+0x1.000000000000200000000001p+600", 7309342195222315010ull},
+ {"-0x1.000000000000200000000001p+600", 16532714232077090818ull},
+ {"+0x1.00000000000027ffffffffffp+600", 7309342195222315010ull},
+ {"-0x1.00000000000027ffffffffffp+600", 16532714232077090818ull},
+ {"+0x1.000000000000280000000000p+600", 7309342195222315010ull},
+ {"-0x1.000000000000280000000000p+600", 16532714232077090818ull},
+ {"+0x1.000000000000280000000001p+600", 7309342195222315011ull},
+ {"-0x1.000000000000280000000001p+600", 16532714232077090819ull},
+ {"+0x2000000000000100000000000", 5044031582654955520ull},
+ {"-0x2000000000000100000000000", 14267403619509731328ull},
+ {"+0x2000000000000100000000001", 5044031582654955521ull},
+ {"-0x2000000000000100000000001", 14267403619509731329ull},
+ {"+0x20000000000001fffffffffff", 5044031582654955521ull},
+ {"-0x20000000000001fffffffffff", 14267403619509731329ull},
+ {"+0x2000000000000200000000000", 5044031582654955521ull},
+ {"-0x2000000000000200000000000", 14267403619509731329ull},
+ {"+0x2000000000000200000000001", 5044031582654955521ull},
+ {"-0x2000000000000200000000001", 14267403619509731329ull},
+ {"+0x20000000000002fffffffffff", 5044031582654955521ull},
+ {"-0x20000000000002fffffffffff", 14267403619509731329ull},
+ {"+0x2000000000000300000000000", 5044031582654955522ull},
+ {"-0x2000000000000300000000000", 14267403619509731330ull},
+ {"+0x2000000000000300000000001", 5044031582654955522ull},
+ {"-0x2000000000000300000000001", 14267403619509731330ull},
+ {"+0x20000000000003fffffffffff", 5044031582654955522ull},
+ {"-0x20000000000003fffffffffff", 14267403619509731330ull},
+ {"+0x2000000000000400000000000", 5044031582654955522ull},
+ {"-0x2000000000000400000000000", 14267403619509731330ull},
+ {"+0x2000000000000400000000001", 5044031582654955522ull},
+ {"-0x2000000000000400000000001", 14267403619509731330ull},
+ {"+0x20000000000004fffffffffff", 5044031582654955522ull},
+ {"-0x20000000000004fffffffffff", 14267403619509731330ull},
+ {"+0x2000000000000500000000000", 5044031582654955522ull},
+ {"-0x2000000000000500000000000", 14267403619509731330ull},
+ {"+0x2000000000000500000000001", 5044031582654955523ull},
+ {"-0x2000000000000500000000001", 14267403619509731331ull},
+ {"+0x0.000000000000080000000000p-1022", 0ull},
+ {"-0x0.000000000000080000000000p-1022", 9223372036854775808ull},
+ {"+0x0.000000000000080000000001p-1022", 1ull},
+ {"-0x0.000000000000080000000001p-1022", 9223372036854775809ull},
+ {"+0x0.0000000000000fffffffffffp-1022", 1ull},
+ {"-0x0.0000000000000fffffffffffp-1022", 9223372036854775809ull},
+ {"+0x0.000000000000100000000000p-1022", 1ull},
+ {"-0x0.000000000000100000000000p-1022", 9223372036854775809ull},
+ {"+0x0.000000000000100000000001p-1022", 1ull},
+ {"-0x0.000000000000100000000001p-1022", 9223372036854775809ull},
+ {"+0x0.00000000000017ffffffffffp-1022", 1ull},
+ {"-0x0.00000000000017ffffffffffp-1022", 9223372036854775809ull},
+ {"+0x0.000000000000180000000000p-1022", 2ull},
+ {"-0x0.000000000000180000000000p-1022", 9223372036854775810ull},
+ {"+0x0.000000000000180000000001p-1022", 2ull},
+ {"-0x0.000000000000180000000001p-1022", 9223372036854775810ull},
+ {"+0x0.0000000000001fffffffffffp-1022", 2ull},
+ {"-0x0.0000000000001fffffffffffp-1022", 9223372036854775810ull},
+ {"+0x0.000000000000200000000000p-1022", 2ull},
+ {"-0x0.000000000000200000000000p-1022", 9223372036854775810ull},
+ {"+0x0.000000000000200000000001p-1022", 2ull},
+ {"-0x0.000000000000200000000001p-1022", 9223372036854775810ull},
+ {"+0x0.00000000000027ffffffffffp-1022", 2ull},
+ {"-0x0.00000000000027ffffffffffp-1022", 9223372036854775810ull},
+ {"+0x0.000000000000280000000000p-1022", 2ull},
+ {"-0x0.000000000000280000000000p-1022", 9223372036854775810ull},
+ {"+0x1.000000000000280000000001p-1022", 4503599627370499ull},
+ {"-0x1.000000000000280000000001p-1022", 9227875636482146307ull},
+ {"+0x1.fffffffffffff4p1023", 9218868437227405311ull},
+ {"-0x1.fffffffffffff4p1023", 18442240474082181119ull},
+ {"+0x1.fffffffffffff7ffffffp1023", 9218868437227405311ull},
+ {"-0x1.fffffffffffff7ffffffp1023", 18442240474082181119ull},
+ };
+
+ for (auto test : kTests) {
+ AssertHexDoubleEquals(test.output, test.input);
+ }
+}
+
+void AssertWriteUint128Equals(const v128& value, const std::string& expected) {
+ assert(expected.length() < 128);
+ char buffer[128];
+ WriteUint128(buffer, 128, value);
+ std::string actual(buffer, buffer + expected.length());
+ ASSERT_EQ(expected, actual);
+ ASSERT_EQ(buffer[expected.length()], '\0');
+}
+
+TEST(WriteUint128, Basic) {
+ AssertWriteUint128Equals({0, 0, 0, 0}, "0");
+ AssertWriteUint128Equals({1, 0, 0, 0}, "1");
+ AssertWriteUint128Equals({0x100f0e0d, 0x0c0b0a09, 0x08070605, 0x04030201},
+ "5332529520247008778714484145835150861");
+ AssertWriteUint128Equals({0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff},
+ "272314856204801878456120017448021860915");
+ AssertWriteUint128Equals({0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff},
+ "340282366920938463463374607431768211455");
+ AssertWriteUint128Equals({0, 0, 1, 0}, "18446744073709551616");
+}
+
+TEST(WriteUint128, BufferTooSmall) {
+ {
+ char buffer[20];
+ WriteUint128(buffer, 20, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff});
+ ASSERT_EQ(buffer[19], '\0');
+ std::string actual(buffer, buffer + 19);
+ ASSERT_EQ("3402823669209384634", actual);
+ }
+
+ {
+ char buffer[3];
+ WriteUint128(buffer, 3, {123, 0, 0, 0});
+ ASSERT_EQ(buffer[0], '1');
+ ASSERT_EQ(buffer[1], '2');
+ ASSERT_EQ(buffer[2], '\0');
+ }
+}
diff --git a/third_party/wasm2c/src/test-option-parser.cc b/third_party/wasm2c/src/test-option-parser.cc
new file mode 100644
index 0000000000..757e6578ff
--- /dev/null
+++ b/third_party/wasm2c/src/test-option-parser.cc
@@ -0,0 +1,180 @@
+// Copyright 2019 WebAssembly Community Group participants
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gtest/gtest.h"
+
+#include <string>
+
+#include "wabt/option-parser.h"
+
+using namespace wabt;
+
+#define ERROR_ENDING "\nTry '--help' for more information."
+
+TEST(OptionParser, LongFlag) {
+ bool flag = false;
+ OptionParser parser("prog", "desc");
+ parser.AddOption("flag", "help", [&]() { flag = true; });
+ const char* args[] = {"prog name", "--flag"};
+ parser.Parse(2, const_cast<char**>(args));
+ EXPECT_EQ(true, flag);
+}
+
+TEST(OptionParser, ShortAndLongFlag) {
+ int count = 0;
+ OptionParser parser("prog", "desc");
+ parser.AddOption('f', "flag", "help", [&]() { ++count; });
+ const char* args[] = {"prog name", "-f", "--flag", "-f", "--flag"};
+ parser.Parse(5, const_cast<char**>(args));
+ EXPECT_EQ(4, count);
+}
+
+TEST(OptionParser, ShortFlagCombined) {
+ int count = 0;
+ OptionParser parser("prog", "desc");
+ parser.AddOption('a', "a", "help", [&]() { count += 1; });
+ parser.AddOption('b', "b", "help", [&]() { count += 2; });
+ const char* args[] = {"prog name", "-aa", "-abb"};
+ parser.Parse(3, const_cast<char**>(args));
+ EXPECT_EQ(7, count);
+}
+
+TEST(OptionParser, UnknownShortOption) {
+ std::string error;
+ OptionParser parser("prog", "desc");
+ parser.SetErrorCallback([&](const char* msg) { error = msg; });
+ const char* args[] = {"prog name", "-f"};
+ parser.Parse(2, const_cast<char**>(args));
+ EXPECT_EQ(error, "prog: unknown option '-f'" ERROR_ENDING);
+}
+
+TEST(OptionParser, UnknownLongOption) {
+ std::string error;
+ OptionParser parser("prog", "desc");
+ parser.SetErrorCallback([&](const char* msg) { error = msg; });
+ const char* args[] = {"prog name", "--foo"};
+ parser.Parse(2, const_cast<char**>(args));
+ EXPECT_EQ(error, "prog: unknown option '--foo'" ERROR_ENDING);
+}
+
+TEST(OptionParser, ShortAndLongParam) {
+ std::string param;
+ OptionParser parser("prog", "desc");
+ parser.AddOption('p', "param", "metavar", "help",
+ [&](const char* arg) { param += arg; });
+ const char* args[] = {"prog name", "-p", "h", "--param", "el", "--param=lo"};
+ parser.Parse(6, const_cast<char**>(args));
+ EXPECT_EQ("hello", param);
+}
+
+TEST(OptionParser, MissingParam) {
+ std::string error;
+ std::string param;
+ OptionParser parser("prog", "desc");
+ parser.SetErrorCallback([&](const char* msg) { error = msg; });
+ parser.AddOption('p', "param", "metavar", "help",
+ [&](const char* arg) { param = arg; });
+ const char* args[] = {"prog name", "--param"};
+ parser.Parse(2, const_cast<char**>(args));
+ EXPECT_EQ("", param);
+ EXPECT_EQ(error, "prog: option '--param' requires argument" ERROR_ENDING);
+}
+
+TEST(OptionParser, MissingArgument) {
+ std::string error;
+ OptionParser parser("prog", "desc");
+ parser.AddArgument("arg", OptionParser::ArgumentCount::One,
+ [&](const char* arg) {});
+ parser.SetErrorCallback([&](const char* msg) { error = msg; });
+ const char* args[] = {"prog name"};
+ parser.Parse(1, const_cast<char**>(args));
+ EXPECT_EQ(error, "prog: expected arg argument." ERROR_ENDING);
+}
+
+TEST(OptionParser, FlagCombinedAfterShortParam) {
+ std::string error;
+ std::string param;
+ bool has_x = false;
+
+ OptionParser parser("prog", "desc");
+ parser.SetErrorCallback([&](const char* msg) { error = msg; });
+ parser.AddOption('p', "p", "metavar", "help",
+ [&](const char* arg) { param = arg; });
+ parser.AddOption('x', "x", "help", [&]() { has_x = true; });
+ const char* args[] = {"prog name", "-px", "stuff"};
+ parser.Parse(3, const_cast<char**>(args));
+ EXPECT_EQ("", param);
+ EXPECT_TRUE(has_x);
+ EXPECT_EQ(error, "prog: unexpected argument 'stuff'" ERROR_ENDING);
+}
+
+TEST(OptionParser, OneArgument) {
+ std::string argument;
+ OptionParser parser("prog", "desc");
+ parser.AddArgument("arg", OptionParser::ArgumentCount::One,
+ [&](const char* arg) { argument = arg; });
+ const char* args[] = {"prog name", "hello"};
+ parser.Parse(2, const_cast<char**>(args));
+ EXPECT_EQ("hello", argument);
+}
+
+TEST(OptionParser, TooManyArguments) {
+ std::string error;
+ OptionParser parser("prog", "desc");
+ parser.SetErrorCallback([&](const char* msg) { error = msg; });
+ parser.AddArgument("arg", OptionParser::ArgumentCount::One,
+ [&](const char* arg) {});
+ const char* args[] = {"prog name", "hello", "goodbye"};
+ parser.Parse(3, const_cast<char**>(args));
+ EXPECT_EQ(error, "prog: unexpected argument 'goodbye'" ERROR_ENDING);
+}
+
+TEST(OptionParser, OneOrMoreArguments) {
+ std::string argument;
+ OptionParser parser("prog", "desc");
+ parser.AddArgument("arg", OptionParser::ArgumentCount::OneOrMore,
+ [&](const char* arg) { argument += arg; });
+ const char* args[] = {"prog name", "hello", "goodbye"};
+ parser.Parse(3, const_cast<char**>(args));
+ EXPECT_EQ("hellogoodbye", argument);
+}
+
+TEST(OptionParser, ZeroOrMoreArguments) {
+ std::string argument;
+ OptionParser parser("prog", "desc");
+ parser.AddArgument("arg", OptionParser::ArgumentCount::ZeroOrMore,
+ [&](const char* arg) { argument += arg; });
+
+ const char* args_none[] = {"prog name"};
+ parser.Parse(1, const_cast<char**>(args_none));
+ EXPECT_EQ("", argument);
+
+ const char* args_many[] = {"prog name", "hello", "goodbye"};
+ parser.Parse(3, const_cast<char**>(args_many));
+ EXPECT_EQ("hellogoodbye", argument);
+}
+
+TEST(OptionParser, StopProccessing) {
+ std::string argument;
+ bool has_x = false;
+ OptionParser parser("prog", "desc");
+ parser.AddArgument("arg", OptionParser::ArgumentCount::ZeroOrMore,
+ [&](const char* arg) { argument += arg; });
+ parser.AddOption('x', "x", "help", [&]() { has_x = true; });
+
+ const char* args_many[] = {"prog name", "-x", "--", "foo", "-x", "-y", "bar"};
+ parser.Parse(7, const_cast<char**>(args_many));
+ EXPECT_TRUE(has_x);
+ EXPECT_EQ("foo-x-ybar", argument);
+}
diff --git a/third_party/wasm2c/src/test-utf8.cc b/third_party/wasm2c/src/test-utf8.cc
new file mode 100644
index 0000000000..d19077e951
--- /dev/null
+++ b/third_party/wasm2c/src/test-utf8.cc
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gtest/gtest.h"
+
+#include "wabt/utf8.h"
+
+using namespace wabt;
+
+namespace {
+
+void assert_is_valid_utf8(bool expected,
+ int length,
+ int cu0 = 0,
+ int cu1 = 0,
+ int cu2 = 0,
+ int cu3 = 0) {
+ assert(length <= 4);
+ char buf[4] = {static_cast<char>(cu0), static_cast<char>(cu1),
+ static_cast<char>(cu2), static_cast<char>(cu3)};
+ if (expected) {
+ // Make sure it fails if there are continuation bytes past the end of the
+ // string.
+ for (int bad_length = 1; bad_length < length; ++bad_length) {
+ ASSERT_FALSE(IsValidUtf8(buf, bad_length))
+ << cu0 << ", " << cu1 << ", " << cu2 << ", " << cu3;
+ }
+ }
+
+ ASSERT_TRUE(expected == IsValidUtf8(buf, length))
+ << cu0 << ", " << cu1 << ", " << cu2 << ", " << cu3;
+}
+
+bool is_in_range(int x, int low, int high) {
+ return x >= low && x < high;
+}
+
+} // end anonymous namespace
+
+#define FOR_RANGE(var, low, high) for (int var = low; var < high; var++)
+#define FOR_EACH_BYTE(var) FOR_RANGE(var, 0, 0x100)
+
+TEST(utf8, valid_empty) {
+ assert_is_valid_utf8(true, 0);
+}
+
+TEST(utf8, valid_1_byte) {
+ FOR_RANGE(cu0, 0, 0x80) { assert_is_valid_utf8(true, 1, cu0); }
+}
+
+TEST(utf8, invalid_continuation_bytes) {
+ FOR_RANGE(cu0, 0x80, 0xc0) { assert_is_valid_utf8(false, 1, cu0); }
+}
+
+TEST(utf8, invalid_2_byte) {
+ FOR_RANGE(cu0, 0xc0, 0xc2) { assert_is_valid_utf8(false, 1, cu0); }
+}
+
+TEST(utf8, valid_2_bytes) {
+ FOR_RANGE(cu0, 0xc2, 0xe0) {
+ FOR_EACH_BYTE(cu1) {
+ bool is_valid = is_in_range(cu1, 0x80, 0xc0);
+ assert_is_valid_utf8(is_valid, 2, cu0, cu1);
+ }
+ }
+}
+
+TEST(utf8, valid_3_bytes_e0) {
+ int cu0 = 0xe0;
+ FOR_EACH_BYTE(cu1) {
+ FOR_EACH_BYTE(cu2) {
+ bool is_valid =
+ is_in_range(cu1, 0xa0, 0xc0) && is_in_range(cu2, 0x80, 0xc0);
+ assert_is_valid_utf8(is_valid, 3, cu0, cu1, cu2);
+ }
+ }
+}
+
+TEST(utf8, valid_3_bytes) {
+ FOR_RANGE(cu0, 0xe1, 0xf0) {
+ // Handle 0xed in valid_3_bytes_ed.
+ if (cu0 == 0xed) {
+ continue;
+ }
+
+ FOR_EACH_BYTE(cu1) {
+ FOR_EACH_BYTE(cu2) {
+ bool is_valid =
+ is_in_range(cu1, 0x80, 0xc0) && is_in_range(cu2, 0x80, 0xc0);
+ assert_is_valid_utf8(is_valid, 3, cu0, cu1, cu2);
+ }
+ }
+ }
+}
+
+TEST(utf8, valid_3_bytes_ed) {
+ int cu0 = 0xed;
+ FOR_EACH_BYTE(cu1) {
+ FOR_EACH_BYTE(cu2) {
+ bool is_valid =
+ is_in_range(cu1, 0x80, 0xa0) && is_in_range(cu2, 0x80, 0xc0);
+ assert_is_valid_utf8(is_valid, 3, cu0, cu1, cu2);
+ }
+ }
+}
+
+TEST(utf8, valid_4_bytes_f0) {
+ int cu0 = 0xf0;
+ FOR_EACH_BYTE(cu1) {
+ FOR_EACH_BYTE(cu2) {
+ FOR_EACH_BYTE(cu3) {
+ bool is_valid = is_in_range(cu1, 0x90, 0xc0) &&
+ is_in_range(cu2, 0x80, 0xc0) &&
+ is_in_range(cu3, 0x80, 0xc0);
+ assert_is_valid_utf8(is_valid, 4, cu0, cu1, cu2, cu3);
+ }
+ }
+ }
+}
+
+TEST(utf8, valid_4_bytes) {
+ FOR_RANGE(cu0, 0xf1, 0xf4) {
+ FOR_EACH_BYTE(cu1) {
+ FOR_EACH_BYTE(cu2) {
+ FOR_EACH_BYTE(cu3) {
+ bool is_valid = is_in_range(cu1, 0x80, 0xc0) &&
+ is_in_range(cu2, 0x80, 0xc0) &&
+ is_in_range(cu3, 0x80, 0xc0);
+ assert_is_valid_utf8(is_valid, 4, cu0, cu1, cu2, cu3);
+ }
+ }
+ }
+ }
+}
+
+TEST(utf8, valid_4_bytes_f4) {
+ int cu0 = 0xf4;
+ FOR_EACH_BYTE(cu1) {
+ FOR_EACH_BYTE(cu2) {
+ FOR_EACH_BYTE(cu3) {
+ bool is_valid = is_in_range(cu1, 0x80, 0x90) &&
+ is_in_range(cu2, 0x80, 0xc0) &&
+ is_in_range(cu3, 0x80, 0xc0);
+ assert_is_valid_utf8(is_valid, 4, cu0, cu1, cu2, cu3);
+ }
+ }
+ }
+}
+
+TEST(utf8, invalid_4_bytes) {
+ FOR_RANGE(cu0, 0xf5, 0x100) {
+ assert_is_valid_utf8(false, 4, cu0, 0x80, 0x80, 0x80);
+ }
+}
diff --git a/third_party/wasm2c/src/test-wast-parser.cc b/third_party/wasm2c/src/test-wast-parser.cc
new file mode 100644
index 0000000000..60fc601dbd
--- /dev/null
+++ b/third_party/wasm2c/src/test-wast-parser.cc
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gtest/gtest.h"
+
+#include <memory>
+
+#include "wabt/wast-lexer.h"
+#include "wabt/wast-parser.h"
+
+using namespace wabt;
+
+namespace {
+
+std::string repeat(std::string s, size_t count) {
+ std::string result;
+ for (size_t i = 0; i < count; ++i) {
+ result += s;
+ }
+ return result;
+}
+
+Errors ParseInvalidModule(std::string text) {
+ Errors errors;
+ auto lexer =
+ WastLexer::CreateBufferLexer("test", text.c_str(), text.size(), &errors);
+ std::unique_ptr<Module> module;
+ Features features;
+ WastParseOptions options(features);
+ Result result = ParseWatModule(lexer.get(), &module, &errors, &options);
+ EXPECT_EQ(Result::Error, result);
+
+ return errors;
+}
+
+} // end of anonymous namespace
+
+TEST(WastParser, LongToken) {
+ std::string text;
+ text = "(module (memory ";
+ text += repeat("a", 0x5000);
+ text += "))";
+
+ Errors errors = ParseInvalidModule(text);
+ ASSERT_EQ(1u, errors.size());
+
+ ASSERT_EQ(ErrorLevel::Error, errors[0].error_level);
+ ASSERT_EQ(1, errors[0].loc.line);
+ ASSERT_EQ(17, errors[0].loc.first_column);
+ ASSERT_STREQ(
+ R"(unexpected token "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...", expected a natural number (e.g. 123).)",
+ errors[0].message.c_str());
+}
+
+TEST(WastParser, LongTokenSpace) {
+ std::string text;
+ text = "notparen";
+ text += repeat(" ", 0x10000);
+ text += "notmodule";
+
+ Errors errors = ParseInvalidModule(text);
+ ASSERT_EQ(2u, errors.size());
+
+ ASSERT_EQ(ErrorLevel::Error, errors[0].error_level);
+ ASSERT_EQ(1, errors[0].loc.line);
+ ASSERT_EQ(1, errors[0].loc.first_column);
+ ASSERT_STREQ(
+ R"(unexpected token "notparen", expected a module field or a module.)",
+ errors[0].message.c_str());
+
+ ASSERT_EQ(1, errors[1].loc.line);
+ ASSERT_EQ(65545, errors[1].loc.first_column);
+ ASSERT_STREQ(R"(unexpected token notmodule, expected EOF.)",
+ errors[1].message.c_str());
+}
diff --git a/third_party/wasm2c/src/token.cc b/third_party/wasm2c/src/token.cc
new file mode 100644
index 0000000000..576fadd185
--- /dev/null
+++ b/third_party/wasm2c/src/token.cc
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/token.h"
+
+namespace wabt {
+
+const char* GetTokenTypeName(TokenType token_type) {
+ static const char* s_names[] = {
+#define WABT_TOKEN(name, string) string,
+#define WABT_TOKEN_FIRST(name, string)
+#define WABT_TOKEN_LAST(name, string)
+#include "wabt/token.def"
+#undef WABT_TOKEN
+#undef WABT_TOKEN_FIRST
+#undef WABT_TOKEN_LAST
+ };
+
+ static_assert(
+ WABT_ARRAY_SIZE(s_names) == WABT_ENUM_COUNT(TokenType),
+ "Expected TokenType names list length to match number of TokenTypes.");
+
+ int x = static_cast<int>(token_type);
+ if (x < WABT_ENUM_COUNT(TokenType)) {
+ return s_names[x];
+ }
+
+ return "Invalid";
+}
+
+Token::Token(Location loc, TokenType token_type)
+ : loc(loc), token_type_(token_type) {
+ assert(IsTokenTypeBare(token_type_));
+}
+
+Token::Token(Location loc, TokenType token_type, Type type)
+ : loc(loc), token_type_(token_type) {
+ assert(HasType());
+ Construct(type_, type);
+}
+
+Token::Token(Location loc, TokenType token_type, std::string_view text)
+ : loc(loc), token_type_(token_type) {
+ assert(HasText());
+ Construct(text_, text);
+}
+
+Token::Token(Location loc, TokenType token_type, Opcode opcode)
+ : loc(loc), token_type_(token_type) {
+ assert(HasOpcode());
+ Construct(opcode_, opcode);
+}
+
+Token::Token(Location loc, TokenType token_type, const Literal& literal)
+ : loc(loc), token_type_(token_type) {
+ assert(HasLiteral());
+ Construct(literal_, literal);
+}
+
+std::string Token::to_string() const {
+ if (IsTokenTypeBare(token_type_)) {
+ return GetTokenTypeName(token_type_);
+ } else if (HasLiteral()) {
+ return std::string(literal_.text);
+ } else if (HasOpcode()) {
+ return opcode_.GetName();
+ } else if (HasText()) {
+ return std::string(text_);
+ } else if (IsTokenTypeRefKind(token_type_)) {
+ return type_.GetRefKindName();
+ } else {
+ assert(HasType());
+ return type_.GetName();
+ }
+}
+
+std::string Token::to_string_clamp(size_t max_length) const {
+ std::string s = to_string();
+ if (s.length() > max_length) {
+ return s.substr(0, max_length - 3) + "...";
+ } else {
+ return s;
+ }
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/tools/wasm2c.cc b/third_party/wasm2c/src/tools/wasm2c.cc
new file mode 100644
index 0000000000..b1c1db8c9a
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm2c.cc
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cassert>
+#include <cinttypes>
+#include <cstdio>
+#include <cstdlib>
+
+#include "wabt/apply-names.h"
+#include "wabt/binary-reader-ir.h"
+#include "wabt/binary-reader.h"
+#include "wabt/error-formatter.h"
+#include "wabt/feature.h"
+#include "wabt/filenames.h"
+#include "wabt/generate-names.h"
+#include "wabt/ir.h"
+#include "wabt/option-parser.h"
+#include "wabt/result.h"
+#include "wabt/stream.h"
+#include "wabt/validator.h"
+#include "wabt/wast-lexer.h"
+
+#include "wabt/c-writer.h"
+
+using namespace wabt;
+
+static int s_verbose;
+static std::string s_infile;
+static std::string s_outfile;
+static unsigned int s_num_outputs = 1;
+static Features s_features;
+static WriteCOptions s_write_c_options;
+static bool s_read_debug_names = true;
+static std::unique_ptr<FileStream> s_log_stream;
+
+static const char s_description[] =
+ R"( Read a file in the WebAssembly binary format, and convert it to
+ a C source file and header.
+
+examples:
+ # parse binary file test.wasm and write test.c and test.h
+ $ wasm2c test.wasm -o test.c
+
+ # parse test.wasm, write test.c and test.h, but ignore the debug names, if any
+ $ wasm2c test.wasm --no-debug-names -o test.c
+)";
+
+static const std::string supported_features[] = {
+ "multi-memory", "multi-value", "sign-extension", "saturating-float-to-int",
+ "exceptions", "memory64", "extended-const", "simd"};
+
+static bool IsFeatureSupported(const std::string& feature) {
+ return std::find(std::begin(supported_features), std::end(supported_features),
+ feature) != std::end(supported_features);
+};
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("wasm2c", s_description);
+
+ parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
+ s_verbose++;
+ s_log_stream = FileStream::CreateStderr();
+ });
+ parser.AddOption(
+ 'o', "output", "FILENAME",
+ "Output file for the generated C source file, by default use stdout",
+ [](const char* argument) {
+ s_outfile = argument;
+ ConvertBackslashToSlash(&s_outfile);
+ });
+ parser.AddOption(
+ '\0', "num-outputs", "NUM", "Number of output files to write",
+ [](const char* argument) { s_num_outputs = atoi(argument); });
+ parser.AddOption(
+ 'n', "module-name", "MODNAME",
+ "Unique name for the module being generated. This name is prefixed to\n"
+ "each of the generaed C symbols. By default, the module name from the\n"
+ "names section is used. If that is not present the name of the input\n"
+ "file is used as the default.\n",
+ [](const char* argument) { s_write_c_options.module_name = argument; });
+ s_features.AddOptions(&parser);
+ parser.AddOption("no-debug-names", "Ignore debug names in the binary file",
+ []() { s_read_debug_names = false; });
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) {
+ s_infile = argument;
+ ConvertBackslashToSlash(&s_infile);
+ });
+ parser.Parse(argc, argv);
+
+ bool any_non_supported_feature = false;
+#define WABT_FEATURE(variable, flag, default_, help) \
+ any_non_supported_feature |= \
+ (s_features.variable##_enabled() != default_) && \
+ s_features.variable##_enabled() && !IsFeatureSupported(flag);
+#include "wabt/feature.def"
+#undef WABT_FEATURE
+
+ if (any_non_supported_feature) {
+ fprintf(stderr,
+ "wasm2c currently only supports a limited set of features.\n");
+ exit(1);
+ }
+}
+
+// TODO(binji): copied from binary-writer-spec.cc, probably should share.
+static std::string_view strip_extension(std::string_view s) {
+ std::string_view ext = s.substr(s.find_last_of('.'));
+ std::string_view result = s;
+
+ if (ext == ".c")
+ result.remove_suffix(ext.length());
+ return result;
+}
+
+Result Wasm2cMain(Errors& errors) {
+ if (s_num_outputs < 1) {
+ fprintf(stderr, "Number of output files must be positive.\n");
+ return Result::Error;
+ }
+
+ std::vector<uint8_t> file_data;
+ CHECK_RESULT(ReadFile(s_infile.c_str(), &file_data));
+
+ Module module;
+ const bool kStopOnFirstError = true;
+ const bool kFailOnCustomSectionError = true;
+ ReadBinaryOptions options(s_features, s_log_stream.get(), s_read_debug_names,
+ kStopOnFirstError, kFailOnCustomSectionError);
+ CHECK_RESULT(ReadBinaryIr(s_infile.c_str(), file_data.data(),
+ file_data.size(), options, &errors, &module));
+ CHECK_RESULT(ValidateModule(&module, &errors, s_features));
+ CHECK_RESULT(GenerateNames(&module));
+ /* TODO(binji): This shouldn't fail; if a name can't be applied
+ * (because the index is invalid, say) it should just be skipped. */
+ ApplyNames(&module);
+
+ if (!s_outfile.empty()) {
+ std::string header_name_full =
+ std::string(strip_extension(s_outfile)) + ".h";
+ std::vector<FileStream> c_streams;
+ if (s_num_outputs == 1) {
+ c_streams.emplace_back(s_outfile.c_str());
+ } else {
+ std::string output_prefix{strip_extension(s_outfile)};
+ for (unsigned int i = 0; i < s_num_outputs; i++) {
+ c_streams.emplace_back(output_prefix + "_" + std::to_string(i) + ".c");
+ }
+ }
+ std::vector<Stream*> c_stream_ptrs;
+ for (auto& s : c_streams) {
+ c_stream_ptrs.emplace_back(&s);
+ }
+ FileStream h_stream(header_name_full);
+ std::string_view header_name = GetBasename(header_name_full);
+ if (s_write_c_options.module_name.empty()) {
+ s_write_c_options.module_name = module.name;
+ if (s_write_c_options.module_name.empty()) {
+ // In the absence of module name in the names section use the filename.
+ s_write_c_options.module_name = StripExtension(GetBasename(s_infile));
+ }
+ }
+ if (s_num_outputs == 1) {
+ CHECK_RESULT(WriteC(std::move(c_stream_ptrs), &h_stream, c_stream_ptrs[0],
+ std::string(header_name).c_str(), "", &module,
+ s_write_c_options));
+ } else {
+ std::string header_impl_name_full =
+ std::string(strip_extension(s_outfile)) + "-impl.h";
+ FileStream h_impl_stream(header_impl_name_full);
+ std::string_view header_impl_name = GetBasename(header_impl_name_full);
+ CHECK_RESULT(WriteC(std::move(c_stream_ptrs), &h_stream, &h_impl_stream,
+ std::string(header_name).c_str(),
+ std::string(header_impl_name).c_str(), &module,
+ s_write_c_options));
+ }
+ } else {
+ FileStream stream(stdout);
+ CHECK_RESULT(WriteC({&stream}, &stream, &stream, "wasm.h", "", &module,
+ s_write_c_options));
+ }
+
+ return Result::Ok;
+}
+
+int ProgramMain(int argc, char** argv) {
+ Result result;
+
+ InitStdio();
+ ParseOptions(argc, argv);
+
+ Errors errors;
+ result = Wasm2cMain(errors);
+ FormatErrorsToFile(errors, Location::Type::Binary);
+
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tracing.cc b/third_party/wasm2c/src/tracing.cc
new file mode 100644
index 0000000000..21f512f587
--- /dev/null
+++ b/third_party/wasm2c/src/tracing.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define WABT_TRACING 1
+#include "wabt/tracing.h"
+
+namespace {
+
+size_t indent = 0;
+const char* indent_text = " ";
+
+void Fill() {
+ for (size_t i = 0; i < indent; ++i)
+ fputs(indent_text, stderr);
+}
+
+void Indent() {
+ Fill();
+ ++indent;
+}
+
+void Dedent() {
+ if (indent) {
+ --indent;
+ }
+ Fill();
+}
+
+} // end of anonymous namespace
+
+namespace wabt {
+
+// static
+
+TraceScope::TraceScope(const char* method) : method_(method) {
+ PrintEnter(method);
+ PrintNewline();
+}
+
+TraceScope::~TraceScope() {
+ Dedent();
+ fputs("<- ", stderr);
+ fputs(method_, stderr);
+ fputc('\n', stderr);
+}
+
+void TraceScope::PrintEnter(const char* method) {
+ Indent();
+ fputs("-> ", stderr);
+ fputs(method, stderr);
+ fputs("(", stderr);
+}
+
+void TraceScope::PrintNewline() {
+ fputs(")\n", stderr);
+}
+
+} // end of namespace wabt
diff --git a/third_party/wasm2c/src/type-checker.cc b/third_party/wasm2c/src/type-checker.cc
new file mode 100644
index 0000000000..79b4f60496
--- /dev/null
+++ b/third_party/wasm2c/src/type-checker.cc
@@ -0,0 +1,989 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/type-checker.h"
+
+#include <cinttypes>
+
+namespace wabt {
+
+namespace {
+
+std::string TypesToString(const TypeVector& types,
+ const char* prefix = nullptr) {
+ std::string result = "[";
+ if (prefix) {
+ result += prefix;
+ }
+
+ for (size_t i = 0; i < types.size(); ++i) {
+ result += types[i].GetName();
+ if (i < types.size() - 1) {
+ result += ", ";
+ }
+ }
+ result += "]";
+ return result;
+}
+
+} // end anonymous namespace
+
+TypeChecker::Label::Label(LabelType label_type,
+ const TypeVector& param_types,
+ const TypeVector& result_types,
+ size_t limit)
+ : label_type(label_type),
+ param_types(param_types),
+ result_types(result_types),
+ type_stack_limit(limit),
+ unreachable(false) {}
+
+void TypeChecker::PrintError(const char* fmt, ...) {
+ if (error_callback_) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, fmt);
+ error_callback_(buffer);
+ }
+}
+
+Result TypeChecker::GetLabel(Index depth, Label** out_label) {
+ if (depth >= label_stack_.size()) {
+ assert(label_stack_.size() > 0);
+ PrintError("invalid depth: %" PRIindex " (max %" PRIzd ")", depth,
+ label_stack_.size() - 1);
+ *out_label = nullptr;
+ return Result::Error;
+ }
+ *out_label = &label_stack_[label_stack_.size() - depth - 1];
+ return Result::Ok;
+}
+
+Result TypeChecker::GetRethrowLabel(Index depth, Label** out_label) {
+ if (Failed(GetLabel(depth, out_label))) {
+ return Result::Error;
+ }
+
+ if ((*out_label)->label_type == LabelType::Catch) {
+ return Result::Ok;
+ }
+
+ std::string candidates;
+ for (Index idx = 0; idx < label_stack_.size(); idx++) {
+ LabelType type = label_stack_[label_stack_.size() - idx - 1].label_type;
+ if (type == LabelType::Catch) {
+ if (!candidates.empty()) {
+ candidates.append(", ");
+ }
+ candidates.append(std::to_string(idx));
+ }
+ }
+
+ if (candidates.empty()) {
+ PrintError("rethrow not in try catch block");
+ } else {
+ PrintError("invalid rethrow depth: %" PRIindex " (catches: %s)", depth,
+ candidates.c_str());
+ }
+ *out_label = nullptr;
+ return Result::Error;
+}
+
+Result TypeChecker::GetCatchCount(Index depth, Index* out_count) {
+ Label* unused;
+ if (Failed(GetLabel(depth, &unused))) {
+ return Result::Error;
+ }
+
+ Index catch_count = 0;
+ for (Index idx = 0; idx <= depth; idx++) {
+ LabelType type = label_stack_[label_stack_.size() - idx - 1].label_type;
+ if (type == LabelType::Catch) {
+ catch_count++;
+ }
+ }
+ *out_count = catch_count;
+
+ return Result::Ok;
+}
+
+Result TypeChecker::TopLabel(Label** out_label) {
+ return GetLabel(0, out_label);
+}
+
+bool TypeChecker::IsUnreachable() {
+ Label* label;
+ if (Failed(TopLabel(&label))) {
+ return true;
+ }
+ return label->unreachable;
+}
+
+void TypeChecker::ResetTypeStackToLabel(Label* label) {
+ type_stack_.resize(label->type_stack_limit);
+}
+
+Result TypeChecker::SetUnreachable() {
+ Label* label;
+ CHECK_RESULT(TopLabel(&label));
+ label->unreachable = true;
+ ResetTypeStackToLabel(label);
+ return Result::Ok;
+}
+
+void TypeChecker::PushLabel(LabelType label_type,
+ const TypeVector& param_types,
+ const TypeVector& result_types) {
+ label_stack_.emplace_back(label_type, param_types, result_types,
+ type_stack_.size());
+}
+
+Result TypeChecker::PopLabel() {
+ label_stack_.pop_back();
+ return Result::Ok;
+}
+
+Result TypeChecker::CheckLabelType(Label* label, LabelType label_type) {
+ return label->label_type == label_type ? Result::Ok : Result::Error;
+}
+
+Result TypeChecker::Check2LabelTypes(Label* label,
+ LabelType label_type1,
+ LabelType label_type2) {
+ return label->label_type == label_type1 || label->label_type == label_type2
+ ? Result::Ok
+ : Result::Error;
+}
+
+Result TypeChecker::GetThisFunctionLabel(Label** label) {
+ return GetLabel(label_stack_.size() - 1, label);
+}
+
+Result TypeChecker::PeekType(Index depth, Type* out_type) {
+ Label* label;
+ CHECK_RESULT(TopLabel(&label));
+
+ if (label->type_stack_limit + depth >= type_stack_.size()) {
+ *out_type = Type::Any;
+ return label->unreachable ? Result::Ok : Result::Error;
+ }
+ *out_type = type_stack_[type_stack_.size() - depth - 1];
+ return Result::Ok;
+}
+
+Result TypeChecker::PeekAndCheckType(Index depth, Type expected) {
+ Type actual = Type::Any;
+ Result result = PeekType(depth, &actual);
+ return result | CheckType(actual, expected);
+}
+
+Result TypeChecker::DropTypes(size_t drop_count) {
+ Label* label;
+ CHECK_RESULT(TopLabel(&label));
+ if (label->type_stack_limit + drop_count > type_stack_.size()) {
+ ResetTypeStackToLabel(label);
+ return label->unreachable ? Result::Ok : Result::Error;
+ }
+ type_stack_.erase(type_stack_.end() - drop_count, type_stack_.end());
+ return Result::Ok;
+}
+
+void TypeChecker::PushType(Type type) {
+ if (type != Type::Void) {
+ type_stack_.push_back(type);
+ }
+}
+
+void TypeChecker::PushTypes(const TypeVector& types) {
+ for (Type type : types) {
+ PushType(type);
+ }
+}
+
+Result TypeChecker::CheckTypeStackEnd(const char* desc) {
+ Label* label;
+ CHECK_RESULT(TopLabel(&label));
+ Result result = (type_stack_.size() == label->type_stack_limit)
+ ? Result::Ok
+ : Result::Error;
+ PrintStackIfFailedV(result, desc, {}, /*is_end=*/true);
+ return result;
+}
+
+Result TypeChecker::CheckType(Type actual, Type expected) {
+ if (expected == Type::Any || actual == Type::Any) {
+ return Result::Ok;
+ }
+
+ if (expected == Type::Reference && actual == Type::Reference) {
+ return expected.GetReferenceIndex() == actual.GetReferenceIndex()
+ ? Result::Ok
+ : Result::Error;
+ }
+ if (actual != expected) {
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+Result TypeChecker::CheckTypes(const TypeVector& actual,
+ const TypeVector& expected) {
+ if (actual.size() != expected.size()) {
+ return Result::Error;
+ } else {
+ Result result = Result::Ok;
+ for (size_t i = 0; i < actual.size(); i++)
+ result |= CheckType(actual[i], expected[i]);
+ return result;
+ }
+}
+
+Result TypeChecker::CheckSignature(const TypeVector& sig, const char* desc) {
+ Result result = Result::Ok;
+ for (size_t i = 0; i < sig.size(); ++i) {
+ result |= PeekAndCheckType(sig.size() - i - 1, sig[i]);
+ }
+ PrintStackIfFailed(result, desc, sig);
+ return result;
+}
+
+Result TypeChecker::CheckReturnSignature(const TypeVector& actual,
+ const TypeVector& expected,
+ const char* desc) {
+ Result result = CheckTypes(actual, expected);
+ if (Failed(result)) {
+ PrintError("return signatures have inconsistent types: expected %s, got %s",
+ TypesToString(expected).c_str(), TypesToString(actual).c_str());
+ }
+ return result;
+}
+
+Result TypeChecker::PopAndCheckSignature(const TypeVector& sig,
+ const char* desc) {
+ Result result = CheckSignature(sig, desc);
+ result |= DropTypes(sig.size());
+ return result;
+}
+
+Result TypeChecker::PopAndCheckCall(const TypeVector& param_types,
+ const TypeVector& result_types,
+ const char* desc) {
+ Result result = CheckSignature(param_types, desc);
+ result |= DropTypes(param_types.size());
+ PushTypes(result_types);
+ return result;
+}
+
+Result TypeChecker::PopAndCheck1Type(Type expected, const char* desc) {
+ Result result = Result::Ok;
+ result |= PeekAndCheckType(0, expected);
+ PrintStackIfFailed(result, desc, expected);
+ result |= DropTypes(1);
+ return result;
+}
+
+Result TypeChecker::PopAndCheck2Types(Type expected1,
+ Type expected2,
+ const char* desc) {
+ Result result = Result::Ok;
+ result |= PeekAndCheckType(0, expected2);
+ result |= PeekAndCheckType(1, expected1);
+ PrintStackIfFailed(result, desc, expected1, expected2);
+ result |= DropTypes(2);
+ return result;
+}
+
+Result TypeChecker::PopAndCheck3Types(Type expected1,
+ Type expected2,
+ Type expected3,
+ const char* desc) {
+ Result result = Result::Ok;
+ result |= PeekAndCheckType(0, expected3);
+ result |= PeekAndCheckType(1, expected2);
+ result |= PeekAndCheckType(2, expected1);
+ PrintStackIfFailed(result, desc, expected1, expected2, expected3);
+ result |= DropTypes(3);
+ return result;
+}
+
+// Some paramater types depend on the memory being used.
+// For example load/store operands, or memory.fill operands.
+static Type GetMemoryParam(Type param, const Limits* limits) {
+ return limits ? limits->IndexType() : param;
+}
+
+Result TypeChecker::CheckOpcode1(Opcode opcode, const Limits* limits) {
+ Result result = PopAndCheck1Type(
+ GetMemoryParam(opcode.GetParamType1(), limits), opcode.GetName());
+ PushType(opcode.GetResultType());
+ return result;
+}
+
+Result TypeChecker::CheckOpcode2(Opcode opcode, const Limits* limits) {
+ Result result =
+ PopAndCheck2Types(GetMemoryParam(opcode.GetParamType1(), limits),
+ opcode.GetParamType2(), opcode.GetName());
+ PushType(opcode.GetResultType());
+ return result;
+}
+
+Result TypeChecker::CheckOpcode3(Opcode opcode,
+ const Limits* limits1,
+ const Limits* limits2,
+ const Limits* limits3) {
+ Result result = PopAndCheck3Types(
+ GetMemoryParam(opcode.GetParamType1(), limits1),
+ GetMemoryParam(opcode.GetParamType2(), limits2),
+ GetMemoryParam(opcode.GetParamType3(), limits3), opcode.GetName());
+ PushType(opcode.GetResultType());
+ return result;
+}
+
+void TypeChecker::PrintStackIfFailedV(Result result,
+ const char* desc,
+ const TypeVector& expected,
+ bool is_end) {
+ if (Succeeded(result)) {
+ return;
+ }
+
+ size_t limit = 0;
+ Label* label;
+ if (Succeeded(TopLabel(&label))) {
+ limit = label->type_stack_limit;
+ }
+
+ TypeVector actual;
+ size_t max_depth = type_stack_.size() - limit;
+
+ // In general we want to print as many values of the actual stack as were
+ // expected. However, if the stack was expected to be empty, we should
+ // print some amount of the actual stack.
+ size_t actual_size;
+ if (expected.size() == 0) {
+ // Don't print too many elements if the stack is really deep.
+ const size_t kMaxActualStackToPrint = 4;
+ actual_size = std::min(kMaxActualStackToPrint, max_depth);
+ } else {
+ actual_size = std::min(expected.size(), max_depth);
+ }
+
+ bool incomplete_actual_stack = actual_size != max_depth;
+
+ for (size_t i = 0; i < actual_size; ++i) {
+ Type type;
+ Result result = PeekType(actual_size - i - 1, &type);
+ WABT_USE(result);
+ assert(Succeeded(result));
+ actual.push_back(type);
+ }
+
+ std::string message = "type mismatch in ";
+ if (is_end) {
+ message = "type mismatch at end of ";
+ }
+ message += desc;
+ message += ", expected ";
+ message += TypesToString(expected);
+ message += " but got ";
+ message += TypesToString(actual, incomplete_actual_stack ? "... " : nullptr);
+
+ PrintError("%s", message.c_str());
+}
+
+Result TypeChecker::BeginFunction(const TypeVector& sig) {
+ type_stack_.clear();
+ label_stack_.clear();
+ PushLabel(LabelType::Func, TypeVector(), sig);
+ return Result::Ok;
+}
+
+Result TypeChecker::OnAtomicLoad(Opcode opcode, const Limits& limits) {
+ return CheckOpcode1(opcode, &limits);
+}
+
+Result TypeChecker::OnAtomicStore(Opcode opcode, const Limits& limits) {
+ return CheckOpcode2(opcode, &limits);
+}
+
+Result TypeChecker::OnAtomicRmw(Opcode opcode, const Limits& limits) {
+ return CheckOpcode2(opcode, &limits);
+}
+
+Result TypeChecker::OnAtomicRmwCmpxchg(Opcode opcode, const Limits& limits) {
+ return CheckOpcode3(opcode, &limits);
+}
+
+Result TypeChecker::OnAtomicWait(Opcode opcode, const Limits& limits) {
+ return CheckOpcode3(opcode, &limits);
+}
+
+Result TypeChecker::OnAtomicFence(uint32_t consistency_model) {
+ return Result::Ok;
+}
+
+Result TypeChecker::OnAtomicNotify(Opcode opcode, const Limits& limits) {
+ return CheckOpcode2(opcode, &limits);
+}
+
+Result TypeChecker::OnBinary(Opcode opcode) {
+ return CheckOpcode2(opcode);
+}
+
+Result TypeChecker::OnBlock(const TypeVector& param_types,
+ const TypeVector& result_types) {
+ Result result = PopAndCheckSignature(param_types, "block");
+ PushLabel(LabelType::Block, param_types, result_types);
+ PushTypes(param_types);
+ return result;
+}
+
+Result TypeChecker::OnBr(Index depth) {
+ Result result = Result::Ok;
+ Label* label;
+ CHECK_RESULT(GetLabel(depth, &label));
+ result |= CheckSignature(label->br_types(), "br");
+ CHECK_RESULT(SetUnreachable());
+ return result;
+}
+
+Result TypeChecker::OnBrIf(Index depth) {
+ Result result = PopAndCheck1Type(Type::I32, "br_if");
+ Label* label;
+ CHECK_RESULT(GetLabel(depth, &label));
+ result |= PopAndCheckSignature(label->br_types(), "br_if");
+ PushTypes(label->br_types());
+ return result;
+}
+
+Result TypeChecker::BeginBrTable() {
+ br_table_sig_ = nullptr;
+ return PopAndCheck1Type(Type::I32, "br_table");
+}
+
+Result TypeChecker::OnBrTableTarget(Index depth) {
+ Result result = Result::Ok;
+ Label* label;
+ CHECK_RESULT(GetLabel(depth, &label));
+ TypeVector& label_sig = label->br_types();
+ result |= CheckSignature(label_sig, "br_table");
+
+ // Make sure this label's signature is consistent with the previous labels'
+ // signatures.
+ if (br_table_sig_ == nullptr) {
+ br_table_sig_ = &label_sig;
+ } else {
+ if (br_table_sig_->size() != label_sig.size()) {
+ result |= Result::Error;
+ PrintError("br_table labels have inconsistent types: expected %s, got %s",
+ TypesToString(*br_table_sig_).c_str(),
+ TypesToString(label_sig).c_str());
+ }
+ }
+
+ return result;
+}
+
+Result TypeChecker::EndBrTable() {
+ return SetUnreachable();
+}
+
+Result TypeChecker::OnCall(const TypeVector& param_types,
+ const TypeVector& result_types) {
+ return PopAndCheckCall(param_types, result_types, "call");
+}
+
+Result TypeChecker::OnCallIndirect(const TypeVector& param_types,
+ const TypeVector& result_types) {
+ Result result = PopAndCheck1Type(Type::I32, "call_indirect");
+ result |= PopAndCheckCall(param_types, result_types, "call_indirect");
+ return result;
+}
+
+Result TypeChecker::OnIndexedFuncRef(Index* out_index) {
+ Type type;
+ CHECK_RESULT(PeekType(0, &type));
+ Result result = Result::Ok;
+ if (!(type == Type::Any || type.IsReferenceWithIndex())) {
+ TypeVector actual;
+ actual.push_back(type);
+ std::string message =
+ "type mismatch in call_ref, expected reference but got " +
+ TypesToString(actual);
+ PrintError("%s", message.c_str());
+ result = Result::Error;
+ }
+ if (Succeeded(result)) {
+ *out_index = type.GetReferenceIndex();
+ }
+ result |= DropTypes(1);
+ return result;
+}
+
+Result TypeChecker::OnReturnCall(const TypeVector& param_types,
+ const TypeVector& result_types) {
+ Result result = PopAndCheckSignature(param_types, "return_call");
+ Label* func_label;
+ CHECK_RESULT(GetThisFunctionLabel(&func_label));
+ result |= CheckReturnSignature(result_types, func_label->result_types,
+ "return_call");
+
+ CHECK_RESULT(SetUnreachable());
+ return result;
+}
+
+Result TypeChecker::OnReturnCallIndirect(const TypeVector& param_types,
+ const TypeVector& result_types) {
+ Result result = PopAndCheck1Type(Type::I32, "return_call_indirect");
+
+ result |= PopAndCheckSignature(param_types, "return_call_indirect");
+ Label* func_label;
+ CHECK_RESULT(GetThisFunctionLabel(&func_label));
+ result |= CheckReturnSignature(result_types, func_label->result_types,
+ "return_call_indirect");
+
+ CHECK_RESULT(SetUnreachable());
+ return result;
+}
+
+Result TypeChecker::OnCompare(Opcode opcode) {
+ return CheckOpcode2(opcode);
+}
+
+Result TypeChecker::OnCatch(const TypeVector& sig) {
+ Result result = Result::Ok;
+ Label* label;
+ CHECK_RESULT(TopLabel(&label));
+ result |= Check2LabelTypes(label, LabelType::Try, LabelType::Catch);
+ result |= PopAndCheckSignature(label->result_types, "try block");
+ result |= CheckTypeStackEnd("try block");
+ ResetTypeStackToLabel(label);
+ label->label_type = LabelType::Catch;
+ label->unreachable = false;
+ PushTypes(sig);
+ return result;
+}
+
+Result TypeChecker::OnConst(Type type) {
+ PushType(type);
+ return Result::Ok;
+}
+
+Result TypeChecker::OnConvert(Opcode opcode) {
+ return CheckOpcode1(opcode);
+}
+
+Result TypeChecker::OnDelegate(Index depth) {
+ Result result = Result::Ok;
+ Label* label;
+ // Delegate starts counting after the current try, as the delegate
+ // instruction is not actually in the try block.
+ CHECK_RESULT(GetLabel(depth + 1, &label));
+
+ Label* try_label;
+ CHECK_RESULT(TopLabel(&try_label));
+ result |= CheckLabelType(try_label, LabelType::Try);
+ result |= PopAndCheckSignature(try_label->result_types, "try block");
+ result |= CheckTypeStackEnd("try block");
+ ResetTypeStackToLabel(try_label);
+
+ // Since an end instruction does not follow a delegate, we push
+ // the block results here and pop the label.
+ PushTypes(try_label->result_types);
+ PopLabel();
+ return result;
+}
+
+Result TypeChecker::OnDrop() {
+ Result result = Result::Ok;
+ result |= DropTypes(1);
+ PrintStackIfFailed(result, "drop", Type::Any);
+ return result;
+}
+
+Result TypeChecker::OnElse() {
+ Result result = Result::Ok;
+ Label* label;
+ CHECK_RESULT(TopLabel(&label));
+ result |= CheckLabelType(label, LabelType::If);
+ result |= PopAndCheckSignature(label->result_types, "`if true` branch");
+ result |= CheckTypeStackEnd("`if true` branch");
+ ResetTypeStackToLabel(label);
+ PushTypes(label->param_types);
+ label->label_type = LabelType::Else;
+ label->unreachable = false;
+ return result;
+}
+
+Result TypeChecker::OnEnd(Label* label,
+ const char* sig_desc,
+ const char* end_desc) {
+ Result result = Result::Ok;
+ result |= PopAndCheckSignature(label->result_types, sig_desc);
+ result |= CheckTypeStackEnd(end_desc);
+ ResetTypeStackToLabel(label);
+ PushTypes(label->result_types);
+ PopLabel();
+ return result;
+}
+
+Result TypeChecker::OnEnd() {
+ Result result = Result::Ok;
+ static const char* s_label_type_name[] = {
+ "function", "initializer expression", "block", "loop",
+ "if", "`if false` branch", "try", "try catch"};
+ WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(s_label_type_name) == kLabelTypeCount);
+ Label* label;
+ CHECK_RESULT(TopLabel(&label));
+ assert(static_cast<int>(label->label_type) < kLabelTypeCount);
+ if (label->label_type == LabelType::If) {
+ // An if without an else will just pass the params through, so the result
+ // types must be the same as the param types. It has the same behavior as
+ // an empty else block.
+ CHECK_RESULT(OnElse());
+ }
+
+ const char* desc = s_label_type_name[static_cast<int>(label->label_type)];
+ result |= OnEnd(label, desc, desc);
+ return result;
+}
+
+Result TypeChecker::OnIf(const TypeVector& param_types,
+ const TypeVector& result_types) {
+ Result result = PopAndCheck1Type(Type::I32, "if");
+ result |= PopAndCheckSignature(param_types, "if");
+ PushLabel(LabelType::If, param_types, result_types);
+ PushTypes(param_types);
+ return result;
+}
+
+Result TypeChecker::OnGlobalGet(Type type) {
+ PushType(type);
+ return Result::Ok;
+}
+
+Result TypeChecker::OnGlobalSet(Type type) {
+ return PopAndCheck1Type(type, "global.set");
+}
+
+Result TypeChecker::OnLoad(Opcode opcode, const Limits& limits) {
+ return CheckOpcode1(opcode, &limits);
+}
+
+Result TypeChecker::OnLocalGet(Type type) {
+ PushType(type);
+ return Result::Ok;
+}
+
+Result TypeChecker::OnLocalSet(Type type) {
+ return PopAndCheck1Type(type, "local.set");
+}
+
+Result TypeChecker::OnLocalTee(Type type) {
+ Result result = Result::Ok;
+ result |= PopAndCheck1Type(type, "local.tee");
+ PushType(type);
+ return result;
+}
+
+Result TypeChecker::OnLoop(const TypeVector& param_types,
+ const TypeVector& result_types) {
+ Result result = PopAndCheckSignature(param_types, "loop");
+ PushLabel(LabelType::Loop, param_types, result_types);
+ PushTypes(param_types);
+ return result;
+}
+
+Result TypeChecker::OnMemoryCopy(const Limits& src_limits,
+ const Limits& dst_limits) {
+ Limits size_limits = src_limits;
+ // The memory64 proposal specifies that the type of the size argument should
+ // be the mimimum of the two memory types.
+ if (src_limits.is_64 && !dst_limits.is_64) {
+ size_limits = dst_limits;
+ }
+ return CheckOpcode3(Opcode::MemoryCopy, &src_limits, &dst_limits,
+ &size_limits);
+}
+
+Result TypeChecker::OnDataDrop(uint32_t segment) {
+ return Result::Ok;
+}
+
+Result TypeChecker::OnMemoryFill(const Limits& limits) {
+ return CheckOpcode3(Opcode::MemoryFill, &limits, nullptr, &limits);
+}
+
+Result TypeChecker::OnMemoryGrow(const Limits& limits) {
+ Result result = PopAndCheck1Type(limits.IndexType(), "memory.grow");
+ PushType(limits.IndexType());
+ return result;
+}
+
+Result TypeChecker::OnMemoryInit(uint32_t segment, const Limits& limits) {
+ return CheckOpcode3(Opcode::MemoryInit, &limits);
+}
+
+Result TypeChecker::OnMemorySize(const Limits& limits) {
+ PushType(limits.IndexType());
+ return Result::Ok;
+}
+
+Result TypeChecker::OnTableCopy() {
+ return CheckOpcode3(Opcode::TableCopy);
+}
+
+Result TypeChecker::OnElemDrop(uint32_t segment) {
+ return Result::Ok;
+}
+
+Result TypeChecker::OnTableInit(uint32_t table, uint32_t segment) {
+ return CheckOpcode3(Opcode::TableInit);
+}
+
+Result TypeChecker::OnTableGet(Type elem_type) {
+ Result result = PopAndCheck1Type(Type::I32, "table.get");
+ PushType(elem_type);
+ return result;
+}
+
+Result TypeChecker::OnTableSet(Type elem_type) {
+ return PopAndCheck2Types(Type::I32, elem_type, "table.set");
+}
+
+Result TypeChecker::OnTableGrow(Type elem_type) {
+ Result result = PopAndCheck2Types(elem_type, Type::I32, "table.grow");
+ PushType(Type::I32);
+ return result;
+}
+
+Result TypeChecker::OnTableSize() {
+ PushType(Type::I32);
+ return Result::Ok;
+}
+
+Result TypeChecker::OnTableFill(Type elem_type) {
+ return PopAndCheck3Types(Type::I32, elem_type, Type::I32, "table.fill");
+}
+
+Result TypeChecker::OnRefFuncExpr(Index func_type) {
+ if (features_.function_references_enabled()) {
+ PushType(Type(Type::Reference, func_type));
+ } else {
+ PushType(Type::FuncRef);
+ }
+ return Result::Ok;
+}
+
+Result TypeChecker::OnRefNullExpr(Type type) {
+ PushType(type);
+ return Result::Ok;
+}
+
+Result TypeChecker::OnRefIsNullExpr() {
+ Type type;
+ Result result = PeekType(0, &type);
+ if (!(type == Type::Any || type.IsRef())) {
+ TypeVector actual;
+ if (Succeeded(result)) {
+ actual.push_back(type);
+ }
+ std::string message =
+ "type mismatch in ref.is_null, expected reference but got " +
+ TypesToString(actual);
+ PrintError("%s", message.c_str());
+ result = Result::Error;
+ }
+ result |= DropTypes(1);
+ PushType(Type::I32);
+ return result;
+}
+
+Result TypeChecker::OnRethrow(Index depth) {
+ Result result = Result::Ok;
+ Label* label;
+ CHECK_RESULT(GetRethrowLabel(depth, &label));
+ CHECK_RESULT(SetUnreachable());
+ return result;
+}
+
+Result TypeChecker::OnThrow(const TypeVector& sig) {
+ Result result = Result::Ok;
+ result |= PopAndCheckSignature(sig, "throw");
+ CHECK_RESULT(SetUnreachable());
+ return result;
+}
+
+Result TypeChecker::OnReturn() {
+ Result result = Result::Ok;
+ Label* func_label;
+ CHECK_RESULT(GetThisFunctionLabel(&func_label));
+ result |= PopAndCheckSignature(func_label->result_types, "return");
+ CHECK_RESULT(SetUnreachable());
+ return result;
+}
+
+Result TypeChecker::OnSelect(const TypeVector& expected) {
+ Result result = Result::Ok;
+ Type type1 = Type::Any;
+ Type type2 = Type::Any;
+ Type result_type = Type::Any;
+ result |= PeekAndCheckType(0, Type::I32);
+ result |= PeekType(1, &type1);
+ result |= PeekType(2, &type2);
+ if (expected.empty()) {
+ if (type1.IsRef() || type2.IsRef()) {
+ result = Result::Error;
+ } else {
+ result |= CheckType(type1, type2);
+ result_type = type1;
+ }
+ } else {
+ assert(expected.size() == 1);
+ result |= CheckType(type1, expected[0]);
+ result |= CheckType(type2, expected[0]);
+ }
+ PrintStackIfFailed(result, "select", result_type, result_type, Type::I32);
+ result |= DropTypes(3);
+ PushType(result_type);
+ return result;
+}
+
+Result TypeChecker::OnStore(Opcode opcode, const Limits& limits) {
+ return CheckOpcode2(opcode, &limits);
+}
+
+Result TypeChecker::OnTry(const TypeVector& param_types,
+ const TypeVector& result_types) {
+ Result result = PopAndCheckSignature(param_types, "try");
+ PushLabel(LabelType::Try, param_types, result_types);
+ PushTypes(param_types);
+ return result;
+}
+
+Result TypeChecker::OnUnary(Opcode opcode) {
+ return CheckOpcode1(opcode);
+}
+
+Result TypeChecker::OnTernary(Opcode opcode) {
+ return CheckOpcode3(opcode);
+}
+
+Result TypeChecker::OnSimdLaneOp(Opcode opcode, uint64_t lane_idx) {
+ Result result = Result::Ok;
+ uint32_t lane_count = opcode.GetSimdLaneCount();
+ if (lane_idx >= lane_count) {
+ PrintError("lane index must be less than %d (got %" PRIu64 ")", lane_count,
+ lane_idx);
+ result = Result::Error;
+ }
+
+ switch (opcode) {
+ case Opcode::I8X16ExtractLaneS:
+ case Opcode::I8X16ExtractLaneU:
+ case Opcode::I16X8ExtractLaneS:
+ case Opcode::I16X8ExtractLaneU:
+ case Opcode::I32X4ExtractLane:
+ case Opcode::F32X4ExtractLane:
+ case Opcode::I64X2ExtractLane:
+ case Opcode::F64X2ExtractLane:
+ result |= CheckOpcode1(opcode);
+ break;
+ case Opcode::I8X16ReplaceLane:
+ case Opcode::I16X8ReplaceLane:
+ case Opcode::I32X4ReplaceLane:
+ case Opcode::F32X4ReplaceLane:
+ case Opcode::I64X2ReplaceLane:
+ case Opcode::F64X2ReplaceLane:
+ result |= CheckOpcode2(opcode);
+ break;
+ default:
+ WABT_UNREACHABLE;
+ }
+ return result;
+}
+
+Result TypeChecker::OnSimdLoadLane(Opcode opcode,
+ const Limits& limits,
+ uint64_t lane_idx) {
+ Result result = Result::Ok;
+ uint32_t lane_count = opcode.GetSimdLaneCount();
+ if (lane_idx >= lane_count) {
+ PrintError("lane index must be less than %d (got %" PRIu64 ")", lane_count,
+ lane_idx);
+ result = Result::Error;
+ }
+ result |= CheckOpcode2(opcode, &limits);
+ return result;
+}
+
+Result TypeChecker::OnSimdStoreLane(Opcode opcode,
+ const Limits& limits,
+ uint64_t lane_idx) {
+ Result result = Result::Ok;
+ uint32_t lane_count = opcode.GetSimdLaneCount();
+ if (lane_idx >= lane_count) {
+ PrintError("lane index must be less than %d (got %" PRIu64 ")", lane_count,
+ lane_idx);
+ result = Result::Error;
+ }
+ result |= CheckOpcode2(opcode, &limits);
+ return result;
+}
+
+Result TypeChecker::OnSimdShuffleOp(Opcode opcode, v128 lane_idx) {
+ Result result = Result::Ok;
+ uint8_t simd_data[16];
+ memcpy(simd_data, &lane_idx, 16);
+ for (int i = 0; i < 16; i++) {
+ if (simd_data[i] >= 32) {
+ PrintError("lane index must be less than 32 (got %d)", simd_data[i]);
+ result = Result::Error;
+ }
+ }
+
+ result |= CheckOpcode2(opcode);
+ return result;
+}
+
+Result TypeChecker::OnUnreachable() {
+ return SetUnreachable();
+}
+
+Result TypeChecker::EndFunction() {
+ Result result = Result::Ok;
+ Label* label;
+ CHECK_RESULT(TopLabel(&label));
+ result |= CheckLabelType(label, LabelType::Func);
+ result |= OnEnd(label, "implicit return", "function");
+ return result;
+}
+
+Result TypeChecker::BeginInitExpr(Type type) {
+ type_stack_.clear();
+ label_stack_.clear();
+ PushLabel(LabelType::InitExpr, TypeVector(), {type});
+ return Result::Ok;
+}
+
+Result TypeChecker::EndInitExpr() {
+ Result result = Result::Ok;
+ Label* label;
+ CHECK_RESULT(TopLabel(&label));
+ result |= CheckLabelType(label, LabelType::InitExpr);
+ result |= OnEnd(label, "initializer expression", "initializer expression");
+ return result;
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/utf8.cc b/third_party/wasm2c/src/utf8.cc
new file mode 100644
index 0000000000..dd95c8c63d
--- /dev/null
+++ b/third_party/wasm2c/src/utf8.cc
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/utf8.h"
+
+#include <cstdint>
+
+namespace wabt {
+
+namespace {
+
+// clang-format off
+const int s_utf8_length[256] = {
+ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0
+ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0
+ 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xf0
+};
+// clang-format on
+
+// Returns true if this is a valid continuation byte.
+bool IsCont(uint8_t c) {
+ return (c & 0xc0) == 0x80;
+}
+
+} // end anonymous namespace
+
+bool IsValidUtf8(const char* s, size_t s_length) {
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(s);
+ const uint8_t* end = p + s_length;
+ while (p < end) {
+ uint8_t cu0 = *p;
+ int length = s_utf8_length[cu0];
+ if (p + length > end) {
+ return false;
+ }
+
+ switch (length) {
+ case 0:
+ return false;
+
+ case 1:
+ p++;
+ break;
+
+ case 2:
+ p++;
+ if (!IsCont(*p++)) {
+ return false;
+ }
+ break;
+
+ case 3: {
+ p++;
+ uint8_t cu1 = *p++;
+ uint8_t cu2 = *p++;
+ if (!(IsCont(cu1) && IsCont(cu2)) ||
+ (cu0 == 0xe0 && cu1 < 0xa0) || // Overlong encoding.
+ (cu0 == 0xed && cu1 >= 0xa0)) // UTF-16 surrogate halves.
+ return false;
+ break;
+ }
+
+ case 4: {
+ p++;
+ uint8_t cu1 = *p++;
+ uint8_t cu2 = *p++;
+ uint8_t cu3 = *p++;
+ if (!(IsCont(cu1) && IsCont(cu2) && IsCont(cu3)) ||
+ (cu0 == 0xf0 && cu1 < 0x90) || // Overlong encoding.
+ (cu0 == 0xf4 && cu1 >= 0x90)) // Code point >= 0x11000.
+ return false;
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/validator.cc b/third_party/wasm2c/src/validator.cc
new file mode 100644
index 0000000000..1fa568abad
--- /dev/null
+++ b/third_party/wasm2c/src/validator.cc
@@ -0,0 +1,1086 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/validator.h"
+
+#include <cassert>
+#include <cinttypes>
+#include <cstdarg>
+#include <cstdio>
+
+#include "wabt/config.h"
+
+#include "wabt/binary-reader.h"
+#include "wabt/cast.h"
+#include "wabt/expr-visitor.h"
+#include "wabt/ir.h"
+#include "wabt/shared-validator.h"
+
+namespace wabt {
+
+namespace {
+
+class ScriptValidator {
+ public:
+ WABT_DISALLOW_COPY_AND_ASSIGN(ScriptValidator);
+ ScriptValidator(Errors*, const Script*, const ValidateOptions& options);
+
+ Result CheckScript();
+
+ private:
+ struct ActionResult {
+ enum class Kind {
+ Error,
+ Types,
+ Type,
+ } kind;
+
+ union {
+ const TypeVector* types;
+ Type type;
+ };
+ };
+
+ void WABT_PRINTF_FORMAT(3, 4)
+ PrintError(const Location* loc, const char* fmt, ...);
+ void CheckTypeIndex(const Location* loc,
+ Type actual,
+ Type expected,
+ const char* desc,
+ Index index,
+ const char* index_kind);
+ void CheckResultTypes(const Location* loc,
+ const TypeVector& actual,
+ const TypeVector& expected,
+ const char* desc);
+ void CheckExpectation(const Location* loc,
+ const TypeVector& result_types,
+ const ConstVector& expected,
+ const char* desc);
+ void CheckExpectationTypes(const Location* loc,
+ const TypeVector& result_types,
+ const Expectation* expect,
+ const char* desc);
+
+ const TypeVector* CheckInvoke(const InvokeAction* action);
+ Result CheckGet(const GetAction* action, Type* out_type);
+ ActionResult CheckAction(const Action* action);
+ void CheckCommand(const Command* command);
+
+ const ValidateOptions& options_;
+ Errors* errors_ = nullptr;
+ const Script* script_ = nullptr;
+
+ Result result_ = Result::Ok;
+};
+
+class Validator : public ExprVisitor::Delegate {
+ public:
+ Validator(Errors*, const Module* module, const ValidateOptions& options);
+
+ Result CheckModule();
+
+ Result OnBinaryExpr(BinaryExpr*) override;
+ Result BeginBlockExpr(BlockExpr*) override;
+ Result EndBlockExpr(BlockExpr*) override;
+ Result OnBrExpr(BrExpr*) override;
+ Result OnBrIfExpr(BrIfExpr*) override;
+ Result OnBrTableExpr(BrTableExpr*) override;
+ 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;
+ Result OnDropExpr(DropExpr*) override;
+ Result OnGlobalGetExpr(GlobalGetExpr*) override;
+ Result OnGlobalSetExpr(GlobalSetExpr*) override;
+ Result BeginIfExpr(IfExpr*) override;
+ Result AfterIfTrueExpr(IfExpr*) override;
+ Result EndIfExpr(IfExpr*) override;
+ Result OnLoadExpr(LoadExpr*) override;
+ Result OnLocalGetExpr(LocalGetExpr*) override;
+ Result OnLocalSetExpr(LocalSetExpr*) override;
+ Result OnLocalTeeExpr(LocalTeeExpr*) override;
+ Result BeginLoopExpr(LoopExpr*) override;
+ Result EndLoopExpr(LoopExpr*) override;
+ Result OnMemoryCopyExpr(MemoryCopyExpr*) override;
+ Result OnDataDropExpr(DataDropExpr*) override;
+ Result OnMemoryFillExpr(MemoryFillExpr*) override;
+ Result OnMemoryGrowExpr(MemoryGrowExpr*) override;
+ Result OnMemoryInitExpr(MemoryInitExpr*) override;
+ Result OnMemorySizeExpr(MemorySizeExpr*) override;
+ Result OnTableCopyExpr(TableCopyExpr*) override;
+ Result OnElemDropExpr(ElemDropExpr*) override;
+ Result OnTableInitExpr(TableInitExpr*) override;
+ Result OnTableGetExpr(TableGetExpr*) override;
+ Result OnTableSetExpr(TableSetExpr*) override;
+ Result OnTableGrowExpr(TableGrowExpr*) override;
+ Result OnTableSizeExpr(TableSizeExpr*) override;
+ Result OnTableFillExpr(TableFillExpr*) override;
+ Result OnRefFuncExpr(RefFuncExpr*) override;
+ Result OnRefNullExpr(RefNullExpr*) override;
+ Result OnRefIsNullExpr(RefIsNullExpr*) override;
+ Result OnNopExpr(NopExpr*) override;
+ Result OnReturnExpr(ReturnExpr*) override;
+ Result OnReturnCallExpr(ReturnCallExpr*) override;
+ Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr*) override;
+ Result OnSelectExpr(SelectExpr*) override;
+ Result OnStoreExpr(StoreExpr*) override;
+ Result OnUnaryExpr(UnaryExpr*) override;
+ Result OnUnreachableExpr(UnreachableExpr*) override;
+ Result BeginTryExpr(TryExpr*) override;
+ Result OnCatchExpr(TryExpr*, Catch*) override;
+ Result OnDelegateExpr(TryExpr*) override;
+ Result EndTryExpr(TryExpr*) override;
+ Result OnThrowExpr(ThrowExpr*) override;
+ Result OnRethrowExpr(RethrowExpr*) override;
+ Result OnAtomicWaitExpr(AtomicWaitExpr*) override;
+ Result OnAtomicFenceExpr(AtomicFenceExpr*) override;
+ Result OnAtomicNotifyExpr(AtomicNotifyExpr*) override;
+ Result OnAtomicLoadExpr(AtomicLoadExpr*) override;
+ Result OnAtomicStoreExpr(AtomicStoreExpr*) override;
+ Result OnAtomicRmwExpr(AtomicRmwExpr*) override;
+ Result OnAtomicRmwCmpxchgExpr(AtomicRmwCmpxchgExpr*) override;
+ Result OnTernaryExpr(TernaryExpr*) override;
+ Result OnSimdLaneOpExpr(SimdLaneOpExpr*) override;
+ Result OnSimdLoadLaneExpr(SimdLoadLaneExpr*) override;
+ Result OnSimdStoreLaneExpr(SimdStoreLaneExpr*) override;
+ Result OnSimdShuffleOpExpr(SimdShuffleOpExpr*) override;
+ Result OnLoadSplatExpr(LoadSplatExpr*) override;
+ Result OnLoadZeroExpr(LoadZeroExpr*) override;
+
+ private:
+ Type GetDeclarationType(const FuncDeclaration&);
+ Var GetFuncTypeIndex(const Location&, const FuncDeclaration&);
+
+ const ValidateOptions& options_;
+ Errors* errors_ = nullptr;
+ SharedValidator validator_;
+ const Module* current_module_ = nullptr;
+ Result result_ = Result::Ok;
+};
+
+ScriptValidator::ScriptValidator(Errors* errors,
+ const Script* script,
+ const ValidateOptions& options)
+ : options_(options), errors_(errors), script_(script) {}
+
+void ScriptValidator::PrintError(const Location* loc, const char* format, ...) {
+ result_ = Result::Error;
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ errors_->emplace_back(ErrorLevel::Error, *loc, buffer);
+}
+
+void ScriptValidator::CheckTypeIndex(const Location* loc,
+ Type actual,
+ Type expected,
+ const char* desc,
+ Index index,
+ const char* index_kind) {
+ if (Failed(TypeChecker::CheckType(actual, expected))) {
+ PrintError(loc,
+ "type mismatch for %s %" PRIindex " of %s. got %s, expected %s",
+ index_kind, index, desc, actual.GetName().c_str(),
+ expected.GetName().c_str());
+ }
+}
+
+void ScriptValidator::CheckResultTypes(const Location* loc,
+ const TypeVector& actual,
+ const TypeVector& expected,
+ const char* desc) {
+ if (actual.size() == expected.size()) {
+ for (size_t i = 0; i < actual.size(); ++i) {
+ CheckTypeIndex(loc, actual[i], expected[i], desc, i, "result");
+ }
+ } else {
+ PrintError(loc, "expected %" PRIzd " results, got %" PRIzd, expected.size(),
+ actual.size());
+ }
+}
+
+void ScriptValidator::CheckExpectation(const Location* loc,
+ const TypeVector& result_types,
+ const ConstVector& expected,
+ const char* desc) {
+ // Here we take the concrete expected output types verify those actains
+ // the types that are the result of the action.
+ TypeVector actual_types;
+ for (auto ex : expected) {
+ actual_types.push_back(ex.type());
+ }
+ CheckResultTypes(loc, actual_types, result_types, desc);
+}
+
+void ScriptValidator::CheckExpectationTypes(const Location* loc,
+ const TypeVector& result_types,
+ const Expectation* expect,
+ const char* desc) {
+ switch (expect->type()) {
+ case ExpectationType::Values: {
+ CheckExpectation(loc, result_types, expect->expected, desc);
+ break;
+ }
+
+ case ExpectationType::Either: {
+ auto* either = cast<EitherExpectation>(expect);
+ for (auto alt : either->expected) {
+ CheckExpectation(loc, result_types, {alt}, desc);
+ }
+ break;
+ }
+ }
+}
+
+Type Validator::GetDeclarationType(const FuncDeclaration& decl) {
+ if (decl.has_func_type) {
+ return Type(decl.type_var.index());
+ }
+ if (decl.sig.param_types.empty()) {
+ if (decl.sig.result_types.empty()) {
+ return Type::Void;
+ }
+ if (decl.sig.result_types.size() == 1) {
+ return decl.sig.result_types[0];
+ }
+ }
+ return Type(current_module_->GetFuncTypeIndex(decl));
+}
+
+Var Validator::GetFuncTypeIndex(const Location& default_loc,
+ const FuncDeclaration& decl) {
+ if (decl.has_func_type) {
+ return decl.type_var;
+ }
+ return Var(current_module_->GetFuncTypeIndex(decl), default_loc);
+}
+
+Result Validator::OnBinaryExpr(BinaryExpr* expr) {
+ result_ |= validator_.OnBinary(expr->loc, expr->opcode);
+ return Result::Ok;
+}
+
+Result Validator::BeginBlockExpr(BlockExpr* expr) {
+ result_ |=
+ validator_.OnBlock(expr->loc, GetDeclarationType(expr->block.decl));
+ return Result::Ok;
+}
+
+Result Validator::EndBlockExpr(BlockExpr* expr) {
+ result_ |= validator_.OnEnd(expr->block.end_loc);
+ return Result::Ok;
+}
+
+Result Validator::OnBrExpr(BrExpr* expr) {
+ result_ |= validator_.OnBr(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnBrIfExpr(BrIfExpr* expr) {
+ result_ |= validator_.OnBrIf(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnBrTableExpr(BrTableExpr* expr) {
+ result_ |= validator_.BeginBrTable(expr->loc);
+ for (const Var& var : expr->targets) {
+ result_ |= validator_.OnBrTableTarget(expr->loc, var);
+ }
+ result_ |= validator_.OnBrTableTarget(expr->loc, expr->default_target);
+ result_ |= validator_.EndBrTable(expr->loc);
+ return Result::Ok;
+}
+
+Result Validator::OnCallExpr(CallExpr* expr) {
+ result_ |= validator_.OnCall(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnCallIndirectExpr(CallIndirectExpr* expr) {
+ result_ |= validator_.OnCallIndirect(
+ expr->loc, GetFuncTypeIndex(expr->loc, expr->decl), expr->table);
+ return Result::Ok;
+}
+
+Result Validator::OnCallRefExpr(CallRefExpr* expr) {
+ Index function_type_index;
+ result_ |= validator_.OnCallRef(expr->loc, &function_type_index);
+ if (Succeeded(result_)) {
+ expr->function_type_index = Var{function_type_index, expr->loc};
+ return Result::Ok;
+ }
+
+ 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;
+}
+
+Result Validator::OnConstExpr(ConstExpr* expr) {
+ result_ |= validator_.OnConst(expr->loc, expr->const_.type());
+ return Result::Ok;
+}
+
+Result Validator::OnConvertExpr(ConvertExpr* expr) {
+ result_ |= validator_.OnConvert(expr->loc, expr->opcode);
+ return Result::Ok;
+}
+
+Result Validator::OnDropExpr(DropExpr* expr) {
+ result_ |= validator_.OnDrop(expr->loc);
+ return Result::Ok;
+}
+
+Result Validator::OnGlobalGetExpr(GlobalGetExpr* expr) {
+ result_ |= validator_.OnGlobalGet(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnGlobalSetExpr(GlobalSetExpr* expr) {
+ result_ |= validator_.OnGlobalSet(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::BeginIfExpr(IfExpr* expr) {
+ result_ |= validator_.OnIf(expr->loc, GetDeclarationType(expr->true_.decl));
+ return Result::Ok;
+}
+
+Result Validator::AfterIfTrueExpr(IfExpr* expr) {
+ if (!expr->false_.empty()) {
+ result_ |= validator_.OnElse(expr->true_.end_loc);
+ }
+ return Result::Ok;
+}
+
+Result Validator::EndIfExpr(IfExpr* expr) {
+ result_ |= validator_.OnEnd(expr->false_.empty() ? expr->true_.end_loc
+ : expr->false_end_loc);
+ return Result::Ok;
+}
+
+Result Validator::OnLoadExpr(LoadExpr* expr) {
+ result_ |= validator_.OnLoad(expr->loc, expr->opcode, expr->memidx,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Result Validator::OnLocalGetExpr(LocalGetExpr* expr) {
+ result_ |= validator_.OnLocalGet(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnLocalSetExpr(LocalSetExpr* expr) {
+ result_ |= validator_.OnLocalSet(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnLocalTeeExpr(LocalTeeExpr* expr) {
+ result_ |= validator_.OnLocalTee(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::BeginLoopExpr(LoopExpr* expr) {
+ result_ |= validator_.OnLoop(expr->loc, GetDeclarationType(expr->block.decl));
+ return Result::Ok;
+}
+
+Result Validator::EndLoopExpr(LoopExpr* expr) {
+ result_ |= validator_.OnEnd(expr->block.end_loc);
+ return Result::Ok;
+}
+
+Result Validator::OnMemoryCopyExpr(MemoryCopyExpr* expr) {
+ result_ |=
+ validator_.OnMemoryCopy(expr->loc, expr->srcmemidx, expr->destmemidx);
+ return Result::Ok;
+}
+
+Result Validator::OnDataDropExpr(DataDropExpr* expr) {
+ result_ |= validator_.OnDataDrop(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnMemoryFillExpr(MemoryFillExpr* expr) {
+ result_ |= validator_.OnMemoryFill(expr->loc, expr->memidx);
+ return Result::Ok;
+}
+
+Result Validator::OnMemoryGrowExpr(MemoryGrowExpr* expr) {
+ result_ |= validator_.OnMemoryGrow(expr->loc, expr->memidx);
+ return Result::Ok;
+}
+
+Result Validator::OnMemoryInitExpr(MemoryInitExpr* expr) {
+ result_ |= validator_.OnMemoryInit(expr->loc, expr->var, expr->memidx);
+ return Result::Ok;
+}
+
+Result Validator::OnMemorySizeExpr(MemorySizeExpr* expr) {
+ result_ |= validator_.OnMemorySize(expr->loc, expr->memidx);
+ return Result::Ok;
+}
+
+Result Validator::OnTableCopyExpr(TableCopyExpr* expr) {
+ result_ |=
+ validator_.OnTableCopy(expr->loc, expr->dst_table, expr->src_table);
+ return Result::Ok;
+}
+
+Result Validator::OnElemDropExpr(ElemDropExpr* expr) {
+ result_ |= validator_.OnElemDrop(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnTableInitExpr(TableInitExpr* expr) {
+ result_ |=
+ validator_.OnTableInit(expr->loc, expr->segment_index, expr->table_index);
+ return Result::Ok;
+}
+
+Result Validator::OnTableGetExpr(TableGetExpr* expr) {
+ result_ |= validator_.OnTableGet(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnTableSetExpr(TableSetExpr* expr) {
+ result_ |= validator_.OnTableSet(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnTableGrowExpr(TableGrowExpr* expr) {
+ result_ |= validator_.OnTableGrow(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnTableSizeExpr(TableSizeExpr* expr) {
+ result_ |= validator_.OnTableSize(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnTableFillExpr(TableFillExpr* expr) {
+ result_ |= validator_.OnTableFill(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnRefFuncExpr(RefFuncExpr* expr) {
+ result_ |= validator_.OnRefFunc(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnRefNullExpr(RefNullExpr* expr) {
+ result_ |= validator_.OnRefNull(expr->loc, expr->type);
+ return Result::Ok;
+}
+
+Result Validator::OnRefIsNullExpr(RefIsNullExpr* expr) {
+ result_ |= validator_.OnRefIsNull(expr->loc);
+ return Result::Ok;
+}
+
+Result Validator::OnNopExpr(NopExpr* expr) {
+ result_ |= validator_.OnNop(expr->loc);
+ return Result::Ok;
+}
+
+Result Validator::OnReturnExpr(ReturnExpr* expr) {
+ result_ |= validator_.OnReturn(expr->loc);
+ return Result::Ok;
+}
+
+Result Validator::OnReturnCallExpr(ReturnCallExpr* expr) {
+ result_ |= validator_.OnReturnCall(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnReturnCallIndirectExpr(ReturnCallIndirectExpr* expr) {
+ result_ |= validator_.OnReturnCallIndirect(
+ expr->loc, GetFuncTypeIndex(expr->loc, expr->decl), expr->table);
+ return Result::Ok;
+}
+
+Result Validator::OnSelectExpr(SelectExpr* expr) {
+ result_ |= validator_.OnSelect(expr->loc, expr->result_type.size(),
+ expr->result_type.data());
+ // TODO: Existing behavior fails when select fails.
+#if 0
+ return Result::Ok;
+#else
+ return result_;
+#endif
+}
+
+Result Validator::OnStoreExpr(StoreExpr* expr) {
+ result_ |= validator_.OnStore(expr->loc, expr->opcode, expr->memidx,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Result Validator::OnUnaryExpr(UnaryExpr* expr) {
+ result_ |= validator_.OnUnary(expr->loc, expr->opcode);
+ return Result::Ok;
+}
+
+Result Validator::OnUnreachableExpr(UnreachableExpr* expr) {
+ result_ |= validator_.OnUnreachable(expr->loc);
+ return Result::Ok;
+}
+
+Result Validator::BeginTryExpr(TryExpr* expr) {
+ result_ |= validator_.OnTry(expr->loc, GetDeclarationType(expr->block.decl));
+ return Result::Ok;
+}
+
+Result Validator::OnCatchExpr(TryExpr*, Catch* catch_) {
+ result_ |= validator_.OnCatch(catch_->loc, catch_->var, catch_->IsCatchAll());
+ return Result::Ok;
+}
+
+Result Validator::OnDelegateExpr(TryExpr* expr) {
+ result_ |= validator_.OnDelegate(expr->loc, expr->delegate_target);
+ return Result::Ok;
+}
+
+Result Validator::EndTryExpr(TryExpr* expr) {
+ result_ |= validator_.OnEnd(expr->block.end_loc);
+ return Result::Ok;
+}
+
+Result Validator::OnThrowExpr(ThrowExpr* expr) {
+ result_ |= validator_.OnThrow(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnRethrowExpr(RethrowExpr* expr) {
+ result_ |= validator_.OnRethrow(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnAtomicWaitExpr(AtomicWaitExpr* expr) {
+ result_ |= validator_.OnAtomicWait(expr->loc, expr->opcode, expr->memidx,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Result Validator::OnAtomicFenceExpr(AtomicFenceExpr* expr) {
+ result_ |= validator_.OnAtomicFence(expr->loc, expr->consistency_model);
+ return Result::Ok;
+}
+
+Result Validator::OnAtomicNotifyExpr(AtomicNotifyExpr* expr) {
+ result_ |= validator_.OnAtomicNotify(expr->loc, expr->opcode, expr->memidx,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Result Validator::OnAtomicLoadExpr(AtomicLoadExpr* expr) {
+ result_ |= validator_.OnAtomicLoad(expr->loc, expr->opcode, expr->memidx,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Result Validator::OnAtomicStoreExpr(AtomicStoreExpr* expr) {
+ result_ |= validator_.OnAtomicStore(expr->loc, expr->opcode, expr->memidx,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Result Validator::OnAtomicRmwExpr(AtomicRmwExpr* expr) {
+ result_ |= validator_.OnAtomicRmw(expr->loc, expr->opcode, expr->memidx,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Result Validator::OnAtomicRmwCmpxchgExpr(AtomicRmwCmpxchgExpr* expr) {
+ result_ |=
+ validator_.OnAtomicRmwCmpxchg(expr->loc, expr->opcode, expr->memidx,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Result Validator::OnTernaryExpr(TernaryExpr* expr) {
+ result_ |= validator_.OnTernary(expr->loc, expr->opcode);
+ return Result::Ok;
+}
+
+Result Validator::OnSimdLaneOpExpr(SimdLaneOpExpr* expr) {
+ result_ |= validator_.OnSimdLaneOp(expr->loc, expr->opcode, expr->val);
+ return Result::Ok;
+}
+
+Result Validator::OnSimdLoadLaneExpr(SimdLoadLaneExpr* expr) {
+ result_ |= validator_.OnSimdLoadLane(expr->loc, expr->opcode, expr->memidx,
+ expr->opcode.GetAlignment(expr->align),
+ expr->val);
+ return Result::Ok;
+}
+
+Result Validator::OnSimdStoreLaneExpr(SimdStoreLaneExpr* expr) {
+ result_ |= validator_.OnSimdStoreLane(expr->loc, expr->opcode, expr->memidx,
+ expr->opcode.GetAlignment(expr->align),
+ expr->val);
+ return Result::Ok;
+}
+
+Result Validator::OnSimdShuffleOpExpr(SimdShuffleOpExpr* expr) {
+ result_ |= validator_.OnSimdShuffleOp(expr->loc, expr->opcode, expr->val);
+ return Result::Ok;
+}
+
+Result Validator::OnLoadSplatExpr(LoadSplatExpr* expr) {
+ result_ |= validator_.OnLoadSplat(expr->loc, expr->opcode, expr->memidx,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Result Validator::OnLoadZeroExpr(LoadZeroExpr* expr) {
+ result_ |= validator_.OnLoadZero(expr->loc, expr->opcode, expr->memidx,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Validator::Validator(Errors* errors,
+ const Module* module,
+ const ValidateOptions& options)
+ : options_(options),
+ errors_(errors),
+ validator_(errors_, options_),
+ current_module_(module) {}
+
+Result Validator::CheckModule() {
+ const Module* module = current_module_;
+
+ // Type section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<TypeModuleField>(&field)) {
+ switch (f->type->kind()) {
+ case TypeEntryKind::Func: {
+ FuncType* func_type = cast<FuncType>(f->type.get());
+ result_ |= validator_.OnFuncType(
+ field.loc, func_type->sig.param_types.size(),
+ func_type->sig.param_types.data(),
+ func_type->sig.result_types.size(),
+ func_type->sig.result_types.data(),
+ module->GetFuncTypeIndex(func_type->sig));
+ break;
+ }
+
+ case TypeEntryKind::Struct: {
+ StructType* struct_type = cast<StructType>(f->type.get());
+ TypeMutVector type_muts;
+ for (auto&& field : struct_type->fields) {
+ type_muts.push_back(TypeMut{field.type, field.mutable_});
+ }
+ result_ |= validator_.OnStructType(field.loc, type_muts.size(),
+ type_muts.data());
+ break;
+ }
+
+ case TypeEntryKind::Array: {
+ ArrayType* array_type = cast<ArrayType>(f->type.get());
+ result_ |= validator_.OnArrayType(
+ field.loc,
+ TypeMut{array_type->field.type, array_type->field.mutable_});
+ break;
+ }
+ }
+ }
+ }
+
+ // Import section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<ImportModuleField>(&field)) {
+ switch (f->import->kind()) {
+ case ExternalKind::Func: {
+ auto&& func = cast<FuncImport>(f->import.get())->func;
+ result_ |= validator_.OnFunction(
+ field.loc, GetFuncTypeIndex(field.loc, func.decl));
+ break;
+ }
+
+ case ExternalKind::Table: {
+ auto&& table = cast<TableImport>(f->import.get())->table;
+ result_ |=
+ validator_.OnTable(field.loc, table.elem_type, table.elem_limits);
+ break;
+ }
+
+ case ExternalKind::Memory: {
+ auto&& memory = cast<MemoryImport>(f->import.get())->memory;
+ result_ |= validator_.OnMemory(field.loc, memory.page_limits);
+ break;
+ }
+
+ case ExternalKind::Global: {
+ auto&& global = cast<GlobalImport>(f->import.get())->global;
+ result_ |= validator_.OnGlobalImport(field.loc, global.type,
+ global.mutable_);
+ break;
+ }
+
+ case ExternalKind::Tag: {
+ auto&& tag = cast<TagImport>(f->import.get())->tag;
+ result_ |= validator_.OnTag(field.loc,
+ GetFuncTypeIndex(field.loc, tag.decl));
+ break;
+ }
+ }
+ }
+ }
+
+ // Func section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<FuncModuleField>(&field)) {
+ result_ |= validator_.OnFunction(
+ field.loc, GetFuncTypeIndex(field.loc, f->func.decl));
+ }
+ }
+
+ // Table section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<TableModuleField>(&field)) {
+ result_ |= validator_.OnTable(field.loc, f->table.elem_type,
+ f->table.elem_limits);
+ }
+ }
+
+ // Memory section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<MemoryModuleField>(&field)) {
+ result_ |= validator_.OnMemory(field.loc, f->memory.page_limits);
+ }
+ }
+
+ // Global section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<GlobalModuleField>(&field)) {
+ result_ |=
+ validator_.OnGlobal(field.loc, f->global.type, f->global.mutable_);
+
+ // Init expr.
+ result_ |= validator_.BeginInitExpr(field.loc, f->global.type);
+ ExprVisitor visitor(this);
+ result_ |=
+ visitor.VisitExprList(const_cast<ExprList&>(f->global.init_expr));
+ result_ |= validator_.EndInitExpr();
+ }
+ }
+
+ // Tag section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<TagModuleField>(&field)) {
+ result_ |=
+ validator_.OnTag(field.loc, GetFuncTypeIndex(field.loc, f->tag.decl));
+ }
+ }
+
+ // Export section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<ExportModuleField>(&field)) {
+ result_ |= validator_.OnExport(field.loc, f->export_.kind, f->export_.var,
+ f->export_.name);
+ }
+ }
+
+ // Start section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<StartModuleField>(&field)) {
+ result_ |= validator_.OnStart(field.loc, f->start);
+ }
+ }
+
+ // Elem segment section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<ElemSegmentModuleField>(&field)) {
+ result_ |= validator_.OnElemSegment(field.loc, f->elem_segment.table_var,
+ f->elem_segment.kind);
+
+ result_ |= validator_.OnElemSegmentElemType(field.loc,
+ f->elem_segment.elem_type);
+
+ // Init expr.
+ if (f->elem_segment.kind == SegmentKind::Active) {
+ result_ |= validator_.BeginInitExpr(field.loc, Type::I32);
+ ExprVisitor visitor(this);
+ result_ |= visitor.VisitExprList(
+ const_cast<ExprList&>(f->elem_segment.offset));
+ result_ |= validator_.EndInitExpr();
+ }
+
+ // Element expr.
+ for (auto&& elem_expr : f->elem_segment.elem_exprs) {
+ if (elem_expr.size() == 1) {
+ const Expr* expr = &elem_expr.front();
+ switch (expr->type()) {
+ case ExprType::RefNull:
+ result_ |= validator_.OnElemSegmentElemExpr_RefNull(
+ expr->loc, cast<RefNullExpr>(expr)->type);
+ break;
+ case ExprType::RefFunc:
+ result_ |= validator_.OnElemSegmentElemExpr_RefFunc(
+ expr->loc, cast<RefFuncExpr>(expr)->var);
+ break;
+ default:
+ result_ |= validator_.OnElemSegmentElemExpr_Other(expr->loc);
+ break;
+ }
+ } else if (elem_expr.size() > 1) {
+ result_ |= validator_.OnElemSegmentElemExpr_Other(field.loc);
+ }
+ }
+ }
+ }
+
+ // DataCount section.
+ validator_.OnDataCount(module->data_segments.size());
+
+ // Code section.
+ Index func_index = module->num_func_imports;
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<FuncModuleField>(&field)) {
+ const Location& body_start = f->func.loc;
+ const Location& body_end =
+ f->func.exprs.empty() ? body_start : f->func.exprs.back().loc;
+ result_ |= validator_.BeginFunctionBody(body_start, func_index++);
+
+ for (auto&& decl : f->func.local_types.decls()) {
+ result_ |= validator_.OnLocalDecl(body_start, decl.second, decl.first);
+ }
+
+ ExprVisitor visitor(this);
+ result_ |= visitor.VisitExprList(const_cast<ExprList&>(f->func.exprs));
+ result_ |= validator_.EndFunctionBody(body_end);
+ }
+ }
+
+ // Data segment section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<DataSegmentModuleField>(&field)) {
+ result_ |= validator_.OnDataSegment(field.loc, f->data_segment.memory_var,
+ f->data_segment.kind);
+
+ // Init expr.
+ if (f->data_segment.kind == SegmentKind::Active) {
+ Type offset_type = Type::I32;
+ Index memory_index = module->GetMemoryIndex(f->data_segment.memory_var);
+ if (memory_index < module->memories.size() &&
+ module->memories[memory_index]->page_limits.is_64) {
+ offset_type = Type::I64;
+ }
+ result_ |= validator_.BeginInitExpr(field.loc, offset_type);
+ ExprVisitor visitor(this);
+ result_ |= visitor.VisitExprList(
+ const_cast<ExprList&>(f->data_segment.offset));
+ result_ |= validator_.EndInitExpr();
+ }
+ }
+ }
+
+ result_ |= validator_.EndModule();
+
+ return result_;
+}
+
+// Returns the result type of the invoked function, checked by the caller;
+// returning nullptr means that another error occured first, so the result type
+// should be ignored.
+const TypeVector* ScriptValidator::CheckInvoke(const InvokeAction* action) {
+ const Module* module = script_->GetModule(action->module_var);
+ if (!module) {
+ PrintError(&action->loc, "unknown module");
+ return nullptr;
+ }
+
+ const Export* export_ = module->GetExport(action->name);
+ if (!export_) {
+ PrintError(&action->loc, "unknown function export \"%s\"",
+ action->name.c_str());
+ return nullptr;
+ }
+
+ const Func* func = module->GetFunc(export_->var);
+ if (!func) {
+ // This error will have already been reported, just skip it.
+ return nullptr;
+ }
+
+ size_t actual_args = action->args.size();
+ size_t expected_args = func->GetNumParams();
+ if (expected_args != actual_args) {
+ PrintError(&action->loc,
+ "too %s parameters to function. got %" PRIzd
+ ", expected %" PRIzd,
+ actual_args > expected_args ? "many" : "few", actual_args,
+ expected_args);
+ return nullptr;
+ }
+ for (size_t i = 0; i < actual_args; ++i) {
+ const Const* const_ = &action->args[i];
+ CheckTypeIndex(&const_->loc, const_->type(), func->GetParamType(i),
+ "invoke", i, "argument");
+ }
+
+ return &func->decl.sig.result_types;
+}
+
+Result ScriptValidator::CheckGet(const GetAction* action, Type* out_type) {
+ const Module* module = script_->GetModule(action->module_var);
+ if (!module) {
+ PrintError(&action->loc, "unknown module");
+ return Result::Error;
+ }
+
+ const Export* export_ = module->GetExport(action->name);
+ if (!export_) {
+ PrintError(&action->loc, "unknown global export \"%s\"",
+ action->name.c_str());
+ return Result::Error;
+ }
+
+ const Global* global = module->GetGlobal(export_->var);
+ if (!global) {
+ // This error will have already been reported, just skip it.
+ return Result::Error;
+ }
+
+ *out_type = global->type;
+ return Result::Ok;
+}
+
+ScriptValidator::ActionResult ScriptValidator::CheckAction(
+ const Action* action) {
+ ActionResult result;
+ ZeroMemory(result);
+
+ switch (action->type()) {
+ case ActionType::Invoke:
+ result.types = CheckInvoke(cast<InvokeAction>(action));
+ result.kind =
+ result.types ? ActionResult::Kind::Types : ActionResult::Kind::Error;
+ break;
+
+ case ActionType::Get:
+ if (Succeeded(CheckGet(cast<GetAction>(action), &result.type))) {
+ result.kind = ActionResult::Kind::Type;
+ } else {
+ result.kind = ActionResult::Kind::Error;
+ }
+ break;
+ }
+
+ return result;
+}
+
+void ScriptValidator::CheckCommand(const Command* command) {
+ switch (command->type) {
+ case CommandType::Module: {
+ Validator module_validator(errors_, &cast<ModuleCommand>(command)->module,
+ options_);
+ module_validator.CheckModule();
+ break;
+ }
+
+ case CommandType::ScriptModule: {
+ Validator module_validator(
+ errors_, &cast<ScriptModuleCommand>(command)->module, options_);
+ module_validator.CheckModule();
+ break;
+ }
+
+ case CommandType::Action:
+ // Ignore result type.
+ CheckAction(cast<ActionCommand>(command)->action.get());
+ break;
+
+ case CommandType::Register:
+ case CommandType::AssertMalformed:
+ case CommandType::AssertInvalid:
+ case CommandType::AssertUnlinkable:
+ case CommandType::AssertUninstantiable:
+ // Ignore.
+ break;
+
+ case CommandType::AssertReturn: {
+ auto* assert_return_command = cast<AssertReturnCommand>(command);
+ const Action* action = assert_return_command->action.get();
+ ActionResult result = CheckAction(action);
+ const Expectation* expected = assert_return_command->expected.get();
+ switch (result.kind) {
+ case ActionResult::Kind::Types:
+ CheckExpectationTypes(&action->loc, *result.types, expected,
+ "action");
+ break;
+
+ case ActionResult::Kind::Type:
+ CheckExpectationTypes(&action->loc, {result.type}, expected,
+ "action");
+ break;
+
+ case ActionResult::Kind::Error:
+ // Error occurred, don't do any further checks.
+ break;
+ }
+ break;
+ }
+
+ case CommandType::AssertTrap:
+ // ignore result type.
+ CheckAction(cast<AssertTrapCommand>(command)->action.get());
+ break;
+ case CommandType::AssertExhaustion:
+ // ignore result type.
+ CheckAction(cast<AssertExhaustionCommand>(command)->action.get());
+ break;
+ case CommandType::AssertException:
+ // ignore result type.
+ CheckAction(cast<AssertExceptionCommand>(command)->action.get());
+ break;
+ }
+}
+
+Result ScriptValidator::CheckScript() {
+ for (const std::unique_ptr<Command>& command : script_->commands)
+ CheckCommand(command.get());
+ return result_;
+}
+
+} // end anonymous namespace
+
+Result ValidateScript(const Script* script,
+ Errors* errors,
+ const ValidateOptions& options) {
+ ScriptValidator validator(errors, script, options);
+
+ return validator.CheckScript();
+}
+
+Result ValidateModule(const Module* module,
+ Errors* errors,
+ const ValidateOptions& options) {
+ Validator validator(errors, module, options);
+
+ return validator.CheckModule();
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/wabt.post.js b/third_party/wasm2c/src/wabt.post.js
new file mode 100644
index 0000000000..7ed0cb21de
--- /dev/null
+++ b/third_party/wasm2c/src/wabt.post.js
@@ -0,0 +1,387 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var WABT_OK = 0;
+var WABT_ERROR = 1;
+
+/// features and default enabled state
+const FEATURES = Object.freeze({
+ 'exceptions': false,
+ 'mutable_globals': true,
+ 'sat_float_to_int': true,
+ 'sign_extension': true,
+ 'simd': true,
+ 'threads': false,
+ 'function_references': false,
+ 'multi_value': true,
+ 'tail_call': false,
+ 'bulk_memory': true,
+ 'reference_types': true,
+ 'annotations': false,
+ 'code_metadata': false,
+ 'gc': false,
+ 'memory64': false,
+ 'multi_memory': false,
+ 'extended_const': false,
+ 'relaxed_simd': false,
+});
+
+/// If value is not undefined, return it. Otherwise return default_.
+function maybeDefault(value, default_) {
+ if (value === undefined) {
+ return default_;
+ }
+ return value;
+}
+
+/// Coerce value to boolean if not undefined. Otherwise return default_.
+function booleanOrDefault(value, default_) {
+ return !!maybeDefault(value, default_);
+}
+
+/// Allocate memory in the Module.
+function malloc(size) {
+ var addr = Module._malloc(size);
+ if (addr == 0) {
+ throw new Error('out of memory');
+ }
+ return addr;
+}
+
+/// Convert an ArrayBuffer/TypedArray/string into a buffer that can be
+/// used by the Module.
+function allocateBuffer(buf) {
+ var addr;
+ var size;
+ if (buf instanceof ArrayBuffer) {
+ size = buf.byteLength;
+ addr = malloc(size);
+ (new Uint8Array(HEAP8.buffer, addr, size)).set(new Uint8Array(buf))
+ } else if (ArrayBuffer.isView(buf)) {
+ size = buf.buffer.byteLength;
+ addr = malloc(size);
+ (new Uint8Array(HEAP8.buffer, addr, size)).set(buf);
+ } else if (typeof buf == 'string') {
+ size = buf.length;
+ addr = malloc(size);
+ writeAsciiToMemory(buf, addr, true); // don't null-terminate
+ } else {
+ throw new Error('unknown buffer type: ' + buf);
+ }
+ return {addr: addr, size: size};
+}
+
+function allocateCString(s) {
+ var size = s.length;
+ var addr = malloc(size);
+ writeAsciiToMemory(s, addr);
+ return {addr: addr, size: size};
+}
+
+
+/// Features
+function Features(obj) {
+ this.addr = Module._wabt_new_features();
+ for ([f, v] of Object.entries(FEATURES)) {
+ this[f] = booleanOrDefault(obj[f], v);
+ }
+}
+Features.prototype = Object.create(Object.prototype);
+
+Features.prototype.destroy = function() {
+ Module._wabt_destroy_features(this.addr);
+};
+
+Object.keys(FEATURES).forEach(function(feature) {
+ Object.defineProperty(Features.prototype, feature, {
+ enumerable: true,
+ get: function() {
+ return Module['_wabt_' + feature + '_enabled'](this.addr);
+ },
+ set: function(newValue) {
+ Module['_wabt_set_' + feature + '_enabled'](this.addr, newValue | 0);
+ }
+ });
+});
+
+
+/// Lexer
+function Lexer(filename, buffer, errors) {
+ this.filenameObj = allocateCString(filename);
+ this.bufferObj = allocateBuffer(buffer);
+ this.addr = Module._wabt_new_wast_buffer_lexer(
+ this.filenameObj.addr, this.bufferObj.addr, this.bufferObj.size,
+ errors.addr);
+}
+Lexer.prototype = Object.create(Object.prototype);
+
+Lexer.prototype.destroy = function() {
+ Module._wabt_destroy_wast_lexer(this.addr);
+ Module._free(this.bufferObj.addr);
+ Module._free(this.filenameObj.addr);
+};
+
+
+/// OutputBuffer
+function OutputBuffer(addr) {
+ this.addr = addr;
+}
+OutputBuffer.prototype = Object.create(Object.prototype);
+
+OutputBuffer.prototype.toTypedArray = function() {
+ if (!this.addr) {
+ return null;
+ }
+
+ var addr = Module._wabt_output_buffer_get_data(this.addr);
+ var size = Module._wabt_output_buffer_get_size(this.addr);
+ var buffer = new Uint8Array(size);
+ buffer.set(new Uint8Array(HEAPU8.buffer, addr, size));
+ return buffer;
+};
+
+OutputBuffer.prototype.toString = function() {
+ if (!this.addr) {
+ return '';
+ }
+
+ var addr = Module._wabt_output_buffer_get_data(this.addr);
+ var size = Module._wabt_output_buffer_get_size(this.addr);
+ return UTF8ToString(addr, size);
+};
+
+OutputBuffer.prototype.destroy = function() {
+ Module._wabt_destroy_output_buffer(this.addr);
+};
+
+
+/// Errors
+function Errors(kind) {
+ this.kind = kind;
+ this.addr = Module._wabt_new_errors();
+}
+Errors.prototype = Object.create(Object.prototype);
+
+Errors.prototype.format = function() {
+ var buffer;
+ switch (this.kind) {
+ case 'text':
+ buffer = new OutputBuffer(
+ Module._wabt_format_text_errors(this.addr, this.lexer.addr));
+ break;
+ case 'binary':
+ buffer = new OutputBuffer(Module._wabt_format_binary_errors(this.addr));
+ break;
+ default:
+ throw new Error('Invalid Errors kind: ' + this.kind);
+ }
+ var message = buffer.toString();
+ buffer.destroy();
+ return message;
+};
+
+Errors.prototype.destroy = function() {
+ Module._wabt_destroy_errors(this.addr);
+ if (this.lexer) {
+ this.lexer.destroy();
+ }
+};
+
+
+/// parseWat
+function parseWat(filename, buffer, options) {
+ var errors = new Errors('text');
+ var lexer = new Lexer(filename, buffer, errors);
+ errors.lexer = lexer;
+ var features = new Features(options || {});
+
+ try {
+ var parseResult_addr =
+ Module._wabt_parse_wat(lexer.addr, features.addr, errors.addr);
+
+ var result = Module._wabt_parse_wat_result_get_result(parseResult_addr);
+ if (result !== WABT_OK) {
+ throw new Error('parseWat failed:\n' + errors.format());
+ }
+
+ var module_addr =
+ Module._wabt_parse_wat_result_release_module(parseResult_addr);
+ var result = new WasmModule(module_addr, errors);
+ // Clear errors so it isn't destroyed below.
+ errors = null;
+ return result;
+ } finally {
+ Module._wabt_destroy_parse_wat_result(parseResult_addr);
+ features.destroy();
+ if (errors) {
+ errors.destroy();
+ }
+ }
+}
+
+
+// readWasm
+function readWasm(buffer, options) {
+ var bufferObj = allocateBuffer(buffer);
+ var errors = new Errors('binary');
+ var readDebugNames = booleanOrDefault(options.readDebugNames, false);
+ var features = new Features(options);
+
+ try {
+ var readBinaryResult_addr = Module._wabt_read_binary(
+ bufferObj.addr, bufferObj.size, readDebugNames, features.addr,
+ errors.addr);
+
+ var result =
+ Module._wabt_read_binary_result_get_result(readBinaryResult_addr);
+ if (result !== WABT_OK) {
+ throw new Error('readWasm failed:\n' + errors.format());
+ }
+
+ var module_addr =
+ Module._wabt_read_binary_result_release_module(readBinaryResult_addr);
+ var result = new WasmModule(module_addr, errors);
+ // Clear errors so it isn't destroyed below.
+ errors = null;
+ return result;
+ } finally {
+ Module._wabt_destroy_read_binary_result(readBinaryResult_addr);
+ features.destroy();
+ if (errors) {
+ errors.destroy();
+ }
+ Module._free(bufferObj.addr);
+ }
+}
+
+
+// WasmModule (can't call it Module because emscripten has claimed it.)
+function WasmModule(module_addr, errors) {
+ this.module_addr = module_addr;
+ this.errors = errors;
+}
+WasmModule.prototype = Object.create(Object.prototype);
+
+WasmModule.prototype.validate = function(options) {
+ var features = new Features(options || {});
+ try {
+ var result = Module._wabt_validate_module(
+ this.module_addr, features.addr, this.errors.addr);
+ if (result !== WABT_OK) {
+ throw new Error('validate failed:\n' + this.errors.format());
+ }
+ } finally {
+ features.destroy();
+ }
+};
+
+WasmModule.prototype.resolveNames = function() {
+ // No-op, this is now part of text parsing.
+};
+
+WasmModule.prototype.generateNames = function() {
+ var result = Module._wabt_generate_names_module(this.module_addr);
+ if (result !== WABT_OK) {
+ throw new Error('generateNames failed.');
+ }
+};
+
+WasmModule.prototype.applyNames = function() {
+ var result = Module._wabt_apply_names_module(this.module_addr);
+ if (result !== WABT_OK) {
+ throw new Error('applyNames failed.');
+ }
+};
+
+WasmModule.prototype.toText = function(options) {
+ var foldExprs = booleanOrDefault(options.foldExprs, false);
+ var inlineExport = booleanOrDefault(options.inlineExport, false);
+
+ var writeModuleResult_addr =
+ Module._wabt_write_text_module(this.module_addr, foldExprs, inlineExport);
+
+ var result =
+ Module._wabt_write_module_result_get_result(writeModuleResult_addr);
+
+ try {
+ if (result !== WABT_OK) {
+ throw new Error('toText failed.');
+ }
+
+ var outputBuffer =
+ new OutputBuffer(Module._wabt_write_module_result_release_output_buffer(
+ writeModuleResult_addr));
+
+ return outputBuffer.toString();
+
+ } finally {
+ if (outputBuffer) {
+ outputBuffer.destroy();
+ }
+ Module._wabt_destroy_write_module_result(writeModuleResult_addr);
+ }
+};
+
+WasmModule.prototype.toBinary = function(options) {
+ var log = booleanOrDefault(options.log, false);
+ var canonicalize_lebs = booleanOrDefault(options.canonicalize_lebs, true);
+ var relocatable = booleanOrDefault(options.relocatable, false);
+ var write_debug_names = booleanOrDefault(options.write_debug_names, false);
+
+ var writeModuleResult_addr = Module._wabt_write_binary_module(
+ this.module_addr, log, canonicalize_lebs, relocatable, write_debug_names);
+
+ var result =
+ Module._wabt_write_module_result_get_result(writeModuleResult_addr);
+
+ try {
+ if (result !== WABT_OK) {
+ throw new Error('toBinary failed.');
+ }
+
+ var binaryOutputBuffer =
+ new OutputBuffer(Module._wabt_write_module_result_release_output_buffer(
+ writeModuleResult_addr));
+ var logOutputBuffer = new OutputBuffer(
+ Module._wabt_write_module_result_release_log_output_buffer(
+ writeModuleResult_addr));
+
+ return {
+ buffer: binaryOutputBuffer.toTypedArray(),
+ log: logOutputBuffer.toString()
+ };
+
+ } finally {
+ if (binaryOutputBuffer) {
+ binaryOutputBuffer.destroy();
+ }
+ if (logOutputBuffer) {
+ logOutputBuffer.destroy();
+ }
+ Module._wabt_destroy_write_module_result(writeModuleResult_addr);
+ }
+};
+
+WasmModule.prototype.destroy = function() {
+ Module._wabt_destroy_module(this.module_addr);
+ if (this.errors) {
+ this.errors.destroy();
+ }
+};
+
+Module['parseWat'] = parseWat;
+Module['readWasm'] = readWasm;
+Module['FEATURES'] = FEATURES;
diff --git a/third_party/wasm2c/src/wast-lexer.cc b/third_party/wasm2c/src/wast-lexer.cc
new file mode 100644
index 0000000000..1f89c3ff47
--- /dev/null
+++ b/third_party/wasm2c/src/wast-lexer.cc
@@ -0,0 +1,626 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/wast-lexer.h"
+
+#include <cassert>
+#include <cstdio>
+
+#include "wabt/config.h"
+
+#include "wabt/lexer-source.h"
+
+#define ERROR(...) Error(GetLocation(), __VA_ARGS__)
+
+namespace wabt {
+
+namespace {
+
+#include "prebuilt/lexer-keywords.cc"
+
+} // namespace
+
+WastLexer::WastLexer(std::unique_ptr<LexerSource> source,
+ std::string_view filename,
+ Errors* errors)
+ : source_(std::move(source)),
+ filename_(filename),
+ line_(1),
+ buffer_(static_cast<const char*>(source_->data())),
+ buffer_end_(buffer_ + source_->size()),
+ line_start_(buffer_),
+ token_start_(buffer_),
+ cursor_(buffer_),
+ errors_(errors) {}
+
+// static
+std::unique_ptr<WastLexer> WastLexer::CreateBufferLexer(
+ std::string_view filename,
+ const void* data,
+ size_t size,
+ Errors* errors) {
+ return std::make_unique<WastLexer>(std::make_unique<LexerSource>(data, size),
+ filename, errors);
+}
+
+Token WastLexer::GetToken() {
+ while (true) {
+ token_start_ = cursor_;
+ switch (PeekChar()) {
+ case kEof:
+ return BareToken(TokenType::Eof);
+
+ case '(':
+ if (MatchString("(;")) {
+ if (ReadBlockComment()) {
+ continue;
+ }
+ return BareToken(TokenType::Eof);
+ } else if (MatchString("(@")) {
+ GetIdChars();
+ // offset=2 to skip the "(@" prefix
+ return TextToken(TokenType::LparAnn, 2);
+ } else {
+ ReadChar();
+ return BareToken(TokenType::Lpar);
+ }
+ break;
+
+ case ')':
+ ReadChar();
+ return BareToken(TokenType::Rpar);
+
+ case ';':
+ if (MatchString(";;")) {
+ if (ReadLineComment()) {
+ continue;
+ }
+ return BareToken(TokenType::Eof);
+ } else {
+ ReadChar();
+ ERROR("unexpected char");
+ continue;
+ }
+ break;
+
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ ReadWhitespace();
+ continue;
+
+ case '"':
+ return GetStringToken();
+
+ case '+':
+ case '-':
+ ReadChar();
+ switch (PeekChar()) {
+ case 'i':
+ return GetInfToken();
+
+ case 'n':
+ return GetNanToken();
+
+ case '0':
+ return MatchString("0x") ? GetHexNumberToken(TokenType::Int)
+ : GetNumberToken(TokenType::Int);
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return GetNumberToken(TokenType::Int);
+
+ default:
+ return GetReservedToken();
+ }
+ break;
+
+ case '0':
+ return MatchString("0x") ? GetHexNumberToken(TokenType::Nat)
+ : GetNumberToken(TokenType::Nat);
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return GetNumberToken(TokenType::Nat);
+
+ case '$':
+ return GetIdChars(); // Initial $ is idchar, so this produces id token
+
+ case 'a':
+ return GetNameEqNumToken("align=", TokenType::AlignEqNat);
+
+ case 'i':
+ return GetInfToken();
+
+ case 'n':
+ return GetNanToken();
+
+ case 'o':
+ return GetNameEqNumToken("offset=", TokenType::OffsetEqNat);
+
+ default:
+ if (IsKeyword(PeekChar())) {
+ return GetKeywordToken();
+ } else if (IsIdChar(PeekChar())) {
+ return GetReservedToken();
+ } else {
+ ReadChar();
+ ERROR("unexpected char");
+ continue;
+ }
+ }
+ }
+}
+
+Location WastLexer::GetLocation() {
+ auto column = [=](const char* p) {
+ return std::max(1, static_cast<int>(p - line_start_ + 1));
+ };
+ return Location(filename_, line_, column(token_start_), column(cursor_));
+}
+
+std::string_view WastLexer::GetText(size_t offset) {
+ // Bounds checks are necessary because token_start may have been moved
+ // (e.g. if GetStringToken found a newline and reset token_start to
+ // point at it).
+
+ if (token_start_ + offset >= buffer_end_)
+ return {};
+
+ if (cursor_ <= token_start_ + offset)
+ return {};
+
+ return std::string_view(token_start_ + offset,
+ (cursor_ - token_start_) - offset);
+}
+
+Token WastLexer::BareToken(TokenType token_type) {
+ return Token(GetLocation(), token_type);
+}
+
+Token WastLexer::LiteralToken(TokenType token_type, LiteralType literal_type) {
+ return Token(GetLocation(), token_type, Literal(literal_type, GetText()));
+}
+
+Token WastLexer::TextToken(TokenType token_type, size_t offset) {
+ return Token(GetLocation(), token_type, GetText(offset));
+}
+
+int WastLexer::PeekChar() {
+ return cursor_ < buffer_end_ ? static_cast<uint8_t>(*cursor_) : kEof;
+}
+
+int WastLexer::ReadChar() {
+ return cursor_ < buffer_end_ ? static_cast<uint8_t>(*cursor_++) : kEof;
+}
+
+bool WastLexer::MatchChar(char c) {
+ if (PeekChar() == c) {
+ ReadChar();
+ return true;
+ }
+ return false;
+}
+
+bool WastLexer::MatchString(std::string_view s) {
+ const char* saved_cursor = cursor_;
+ for (char c : s) {
+ if (ReadChar() != c) {
+ cursor_ = saved_cursor;
+ return false;
+ }
+ }
+ return true;
+}
+
+void WastLexer::Newline() {
+ line_++;
+ line_start_ = cursor_;
+}
+
+bool WastLexer::ReadBlockComment() {
+ int nesting = 1;
+ while (true) {
+ switch (ReadChar()) {
+ case kEof:
+ ERROR("EOF in block comment");
+ return false;
+
+ case ';':
+ if (MatchChar(')') && --nesting == 0) {
+ return true;
+ }
+ break;
+
+ case '(':
+ if (MatchChar(';')) {
+ nesting++;
+ }
+ break;
+
+ case '\n':
+ Newline();
+ break;
+ }
+ }
+}
+
+bool WastLexer::ReadLineComment() {
+ while (true) {
+ switch (ReadChar()) {
+ case kEof:
+ return false;
+
+ case '\n':
+ Newline();
+ return true;
+ }
+ }
+}
+
+void WastLexer::ReadWhitespace() {
+ while (true) {
+ switch (PeekChar()) {
+ case ' ':
+ case '\t':
+ case '\r':
+ ReadChar();
+ break;
+
+ case '\n':
+ ReadChar();
+ Newline();
+ break;
+
+ default:
+ return;
+ }
+ }
+}
+
+Token WastLexer::GetStringToken() {
+ const char* saved_token_start = token_start_;
+ bool has_error = false;
+ bool in_string = true;
+ ReadChar();
+ while (in_string) {
+ switch (ReadChar()) {
+ case kEof:
+ return BareToken(TokenType::Eof);
+
+ case '\n':
+ token_start_ = cursor_ - 1;
+ ERROR("newline in string");
+ has_error = true;
+ Newline();
+ continue;
+
+ case '"':
+ if (PeekChar() == '"') {
+ ERROR("invalid string token");
+ has_error = true;
+ }
+ in_string = false;
+ break;
+
+ case '\\': {
+ switch (ReadChar()) {
+ case 't':
+ case 'n':
+ case 'r':
+ case '"':
+ case '\'':
+ case '\\':
+ // Valid escape.
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F': // Hex byte escape.
+ if (IsHexDigit(PeekChar())) {
+ ReadChar();
+ } else {
+ token_start_ = cursor_ - 2;
+ goto error;
+ }
+ break;
+
+ case 'u': {
+ token_start_ = cursor_ - 2;
+ if (ReadChar() != '{') {
+ goto error;
+ }
+
+ // Value must be a valid unicode scalar value.
+ uint32_t digit;
+ uint32_t scalar_value = 0;
+
+ while (IsHexDigit(PeekChar())) {
+ ParseHexdigit(*cursor_++, &digit);
+
+ scalar_value = (scalar_value << 4) | digit;
+ // Maximum value of a unicode code point.
+ if (scalar_value >= 0x110000) {
+ goto error;
+ }
+ }
+
+ if (PeekChar() != '}') {
+ goto error;
+ }
+
+ // Scalars between 0xd800 and 0xdfff are not allowed.
+ if ((scalar_value >= 0xd800 && scalar_value < 0xe000) ||
+ token_start_ == cursor_ - 3) {
+ ReadChar();
+ goto error;
+ }
+ break;
+ }
+
+ default:
+ token_start_ = cursor_ - 2;
+ goto error;
+
+ error:
+ ERROR("bad escape \"%.*s\"",
+ static_cast<int>(cursor_ - token_start_), token_start_);
+ has_error = true;
+ break;
+ }
+ break;
+ }
+ }
+ }
+ token_start_ = saved_token_start;
+ if (has_error) {
+ return Token(GetLocation(), TokenType::Invalid);
+ }
+
+ return TextToken(TokenType::Text);
+}
+
+// static
+bool WastLexer::IsCharClass(int c, CharClass bit) {
+ // Generated by the following python script:
+ //
+ // def Range(c, lo, hi): return lo <= c <= hi
+ // def IsDigit(c): return Range(c, '0', '9')
+ // def IsHexDigit(c): return IsDigit(c) or Range(c.lower(), 'a', 'f')
+ // def IsKeyword(c): return Range(c, 'a', 'z')
+ // def IsIdChar(c): return Range(c, '!', '~') and c not in '"(),;[]{}'
+ //
+ // print ([0] + [
+ // (8 if IsDigit(c) else 0) |
+ // (4 if IsHexDigit(c) else 0) |
+ // (2 if IsKeyword(c) else 0) |
+ // (1 if IsIdChar(c) else 0)
+ // for c in map(chr, range(0, 127))
+ // ])
+ static const char kCharClasses[257] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1,
+ 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 1, 0, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0,
+ 1, 1, 1, 7, 7, 7, 7, 7, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 1, 0, 1,
+ };
+
+ assert(c >= -1 && c < 256);
+ return (kCharClasses[c + 1] & static_cast<int>(bit)) != 0;
+}
+
+bool WastLexer::ReadNum() {
+ if (IsDigit(PeekChar())) {
+ ReadChar();
+ return MatchChar('_') || IsDigit(PeekChar()) ? ReadNum() : true;
+ }
+ return false;
+}
+
+bool WastLexer::ReadHexNum() {
+ if (IsHexDigit(PeekChar())) {
+ ReadChar();
+ return MatchChar('_') || IsHexDigit(PeekChar()) ? ReadHexNum() : true;
+ }
+ return false;
+}
+
+WastLexer::ReservedChars WastLexer::ReadReservedChars() {
+ ReservedChars ret{ReservedChars::None};
+ while (true) {
+ auto peek = PeekChar();
+ if (IsIdChar(peek)) {
+ ReadChar();
+ if (ret == ReservedChars::None) {
+ ret = ReservedChars::Id;
+ }
+ } else if (peek == '"') {
+ GetStringToken();
+ ret = ReservedChars::Some;
+ } else {
+ break;
+ }
+ }
+ return ret;
+}
+
+void WastLexer::ReadSign() {
+ if (PeekChar() == '+' || PeekChar() == '-') {
+ ReadChar();
+ }
+}
+
+Token WastLexer::GetNumberToken(TokenType token_type) {
+ if (ReadNum()) {
+ if (MatchChar('.')) {
+ token_type = TokenType::Float;
+ if (IsDigit(PeekChar()) && !ReadNum()) {
+ return GetReservedToken();
+ }
+ }
+ if (MatchChar('e') || MatchChar('E')) {
+ token_type = TokenType::Float;
+ ReadSign();
+ if (!ReadNum()) {
+ return GetReservedToken();
+ }
+ }
+ if (NoTrailingReservedChars()) {
+ if (token_type == TokenType::Float) {
+ return LiteralToken(token_type, LiteralType::Float);
+ } else {
+ return LiteralToken(token_type, LiteralType::Int);
+ }
+ }
+ }
+ return GetReservedToken();
+}
+
+Token WastLexer::GetHexNumberToken(TokenType token_type) {
+ if (ReadHexNum()) {
+ if (MatchChar('.')) {
+ token_type = TokenType::Float;
+ if (IsHexDigit(PeekChar()) && !ReadHexNum()) {
+ return GetReservedToken();
+ }
+ }
+ if (MatchChar('p') || MatchChar('P')) {
+ token_type = TokenType::Float;
+ ReadSign();
+ if (!ReadNum()) {
+ return GetReservedToken();
+ }
+ }
+ if (NoTrailingReservedChars()) {
+ if (token_type == TokenType::Float) {
+ return LiteralToken(token_type, LiteralType::Hexfloat);
+ } else {
+ return LiteralToken(token_type, LiteralType::Int);
+ }
+ }
+ }
+ return GetReservedToken();
+}
+
+Token WastLexer::GetInfToken() {
+ if (MatchString("inf")) {
+ if (NoTrailingReservedChars()) {
+ return LiteralToken(TokenType::Float, LiteralType::Infinity);
+ }
+ return GetReservedToken();
+ }
+ return GetKeywordToken();
+}
+
+Token WastLexer::GetNanToken() {
+ if (MatchString("nan")) {
+ if (MatchChar(':')) {
+ if (MatchString("0x") && ReadHexNum() && NoTrailingReservedChars()) {
+ return LiteralToken(TokenType::Float, LiteralType::Nan);
+ }
+ } else if (NoTrailingReservedChars()) {
+ return LiteralToken(TokenType::Float, LiteralType::Nan);
+ }
+ }
+ return GetKeywordToken();
+}
+
+Token WastLexer::GetNameEqNumToken(std::string_view name,
+ TokenType token_type) {
+ if (MatchString(name)) {
+ if (MatchString("0x")) {
+ if (ReadHexNum() && NoTrailingReservedChars()) {
+ return TextToken(token_type, name.size());
+ }
+ } else if (ReadNum() && NoTrailingReservedChars()) {
+ return TextToken(token_type, name.size());
+ }
+ }
+ return GetKeywordToken();
+}
+
+Token WastLexer::GetIdChars() {
+ if (ReadReservedChars() == ReservedChars::Id) {
+ return TextToken(TokenType::Var);
+ }
+
+ return TextToken(TokenType::Reserved);
+}
+
+Token WastLexer::GetKeywordToken() {
+ ReadReservedChars();
+ TokenInfo* info =
+ Perfect_Hash::InWordSet(token_start_, cursor_ - token_start_);
+ if (!info) {
+ return TextToken(TokenType::Reserved);
+ }
+ if (IsTokenTypeBare(info->token_type)) {
+ return BareToken(info->token_type);
+ } else if (IsTokenTypeType(info->token_type) ||
+ IsTokenTypeRefKind(info->token_type)) {
+ return Token(GetLocation(), info->token_type, info->value_type);
+ } else {
+ assert(IsTokenTypeOpcode(info->token_type));
+ return Token(GetLocation(), info->token_type, info->opcode);
+ }
+}
+
+Token WastLexer::GetReservedToken() {
+ ReadReservedChars();
+ return TextToken(TokenType::Reserved);
+}
+
+void WastLexer::Error(Location loc, const char* format, ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ errors_->emplace_back(ErrorLevel::Error, loc, buffer);
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/wast-parser.cc b/third_party/wasm2c/src/wast-parser.cc
new file mode 100644
index 0000000000..a596f018dc
--- /dev/null
+++ b/third_party/wasm2c/src/wast-parser.cc
@@ -0,0 +1,3616 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/wast-parser.h"
+
+#include "wabt/binary-reader-ir.h"
+#include "wabt/binary-reader.h"
+#include "wabt/cast.h"
+#include "wabt/expr-visitor.h"
+#include "wabt/resolve-names.h"
+#include "wabt/stream.h"
+#include "wabt/utf8.h"
+#include "wabt/validator.h"
+
+#define WABT_TRACING 0
+#include "wabt/tracing.h"
+
+#define EXPECT(token_type) CHECK_RESULT(Expect(TokenType::token_type))
+
+namespace wabt {
+
+namespace {
+
+static const size_t kMaxErrorTokenLength = 80;
+
+bool IsPowerOfTwo(uint32_t x) {
+ return x && ((x & (x - 1)) == 0);
+}
+
+template <typename OutputIter>
+void RemoveEscapes(std::string_view text, OutputIter dest) {
+ // Remove surrounding quotes; if any. This may be empty if the string was
+ // invalid (e.g. if it contained a bad escape sequence).
+ if (text.size() <= 2) {
+ return;
+ }
+
+ text = text.substr(1, text.size() - 2);
+
+ const char* src = text.data();
+ const char* end = text.data() + text.size();
+
+ while (src < end) {
+ if (*src == '\\') {
+ src++;
+ switch (*src) {
+ case 'n':
+ *dest++ = '\n';
+ break;
+ case 'r':
+ *dest++ = '\r';
+ break;
+ case 't':
+ *dest++ = '\t';
+ break;
+ case '\\':
+ *dest++ = '\\';
+ break;
+ case '\'':
+ *dest++ = '\'';
+ break;
+ case '\"':
+ *dest++ = '\"';
+ break;
+ case 'u': {
+ // The string should be validated already,
+ // so this must be a valid unicode escape sequence.
+ uint32_t digit;
+ uint32_t scalar_value = 0;
+
+ // Skip u and { characters.
+ src += 2;
+
+ do {
+ if (Succeeded(ParseHexdigit(src[0], &digit))) {
+ scalar_value = (scalar_value << 4) | digit;
+ } else {
+ assert(0);
+ }
+ src++;
+ } while (src[0] != '}');
+
+ // Maximum value of a unicode scalar value
+ assert(scalar_value < 0x110000);
+
+ // Encode the unicode scalar value as UTF8 sequence
+ if (scalar_value < 0x80) {
+ *dest++ = static_cast<uint8_t>(scalar_value);
+ } else {
+ if (scalar_value < 0x800) {
+ *dest++ = static_cast<uint8_t>(0xc0 | (scalar_value >> 6));
+ } else {
+ if (scalar_value < 0x10000) {
+ *dest++ = static_cast<uint8_t>(0xe0 | (scalar_value >> 12));
+ } else {
+ *dest++ = static_cast<uint8_t>(0xf0 | (scalar_value >> 18));
+ *dest++ =
+ static_cast<uint8_t>(0x80 | ((scalar_value >> 12) & 0x3f));
+ }
+
+ *dest++ =
+ static_cast<uint8_t>(0x80 | ((scalar_value >> 6) & 0x3f));
+ }
+
+ *dest++ = static_cast<uint8_t>(0x80 | (scalar_value & 0x3f));
+ }
+ break;
+ }
+ default: {
+ // The string should be validated already, so we know this is a hex
+ // sequence.
+ uint32_t hi;
+ uint32_t lo;
+ if (Succeeded(ParseHexdigit(src[0], &hi)) &&
+ Succeeded(ParseHexdigit(src[1], &lo))) {
+ *dest++ = (hi << 4) | lo;
+ } else {
+ assert(0);
+ }
+ src++;
+ break;
+ }
+ }
+ src++;
+ } else {
+ *dest++ = *src++;
+ }
+ }
+}
+
+using TextVector = std::vector<std::string_view>;
+
+template <typename OutputIter>
+void RemoveEscapes(const TextVector& texts, OutputIter out) {
+ for (std::string_view text : texts)
+ RemoveEscapes(text, out);
+}
+
+bool IsPlainInstr(TokenType token_type) {
+ switch (token_type) {
+ case TokenType::Unreachable:
+ case TokenType::Nop:
+ case TokenType::Drop:
+ case TokenType::Select:
+ case TokenType::Br:
+ case TokenType::BrIf:
+ case TokenType::BrTable:
+ case TokenType::Return:
+ case TokenType::ReturnCall:
+ case TokenType::ReturnCallIndirect:
+ case TokenType::Call:
+ case TokenType::CallIndirect:
+ case TokenType::CallRef:
+ case TokenType::LocalGet:
+ case TokenType::LocalSet:
+ case TokenType::LocalTee:
+ case TokenType::GlobalGet:
+ case TokenType::GlobalSet:
+ case TokenType::Load:
+ case TokenType::Store:
+ case TokenType::Const:
+ case TokenType::Unary:
+ case TokenType::Binary:
+ case TokenType::Compare:
+ case TokenType::Convert:
+ case TokenType::MemoryCopy:
+ case TokenType::DataDrop:
+ case TokenType::MemoryFill:
+ case TokenType::MemoryGrow:
+ case TokenType::MemoryInit:
+ case TokenType::MemorySize:
+ case TokenType::TableCopy:
+ case TokenType::ElemDrop:
+ case TokenType::TableInit:
+ case TokenType::TableGet:
+ case TokenType::TableSet:
+ case TokenType::TableGrow:
+ case TokenType::TableSize:
+ case TokenType::TableFill:
+ case TokenType::Throw:
+ case TokenType::Rethrow:
+ case TokenType::RefFunc:
+ case TokenType::RefNull:
+ case TokenType::RefIsNull:
+ case TokenType::AtomicLoad:
+ case TokenType::AtomicStore:
+ case TokenType::AtomicRmw:
+ case TokenType::AtomicRmwCmpxchg:
+ case TokenType::AtomicNotify:
+ case TokenType::AtomicFence:
+ case TokenType::AtomicWait:
+ case TokenType::Ternary:
+ case TokenType::SimdLaneOp:
+ case TokenType::SimdLoadLane:
+ case TokenType::SimdStoreLane:
+ case TokenType::SimdShuffleOp:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsBlockInstr(TokenType token_type) {
+ switch (token_type) {
+ case TokenType::Block:
+ case TokenType::Loop:
+ case TokenType::If:
+ case TokenType::Try:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsPlainOrBlockInstr(TokenType token_type) {
+ return IsPlainInstr(token_type) || IsBlockInstr(token_type);
+}
+
+bool IsExpr(TokenTypePair pair) {
+ return pair[0] == TokenType::Lpar && IsPlainOrBlockInstr(pair[1]);
+}
+
+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;
+}
+
+bool IsModuleField(TokenTypePair pair) {
+ if (pair[0] != TokenType::Lpar) {
+ return false;
+ }
+
+ switch (pair[1]) {
+ case TokenType::Data:
+ case TokenType::Elem:
+ case TokenType::Tag:
+ case TokenType::Export:
+ case TokenType::Func:
+ case TokenType::Type:
+ case TokenType::Global:
+ case TokenType::Import:
+ case TokenType::Memory:
+ case TokenType::Start:
+ case TokenType::Table:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsCommand(TokenTypePair pair) {
+ if (pair[0] != TokenType::Lpar) {
+ return false;
+ }
+
+ switch (pair[1]) {
+ case TokenType::AssertException:
+ case TokenType::AssertExhaustion:
+ case TokenType::AssertInvalid:
+ case TokenType::AssertMalformed:
+ case TokenType::AssertReturn:
+ case TokenType::AssertTrap:
+ case TokenType::AssertUnlinkable:
+ case TokenType::Get:
+ case TokenType::Invoke:
+ case TokenType::Input:
+ case TokenType::Module:
+ case TokenType::Output:
+ case TokenType::Register:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsEmptySignature(const FuncSignature& sig) {
+ return sig.result_types.empty() && sig.param_types.empty();
+}
+
+bool ResolveFuncTypeWithEmptySignature(const Module& module,
+ FuncDeclaration* decl) {
+ // Resolve func type variables where the signature was not specified
+ // explicitly, e.g.: (func (type 1) ...)
+ if (decl->has_func_type && IsEmptySignature(decl->sig)) {
+ const FuncType* func_type = module.GetFuncType(decl->type_var);
+ if (func_type) {
+ decl->sig = func_type->sig;
+ return true;
+ }
+ }
+ return false;
+}
+
+void ResolveTypeName(
+ const Module& module,
+ Type& type,
+ Index index,
+ const std::unordered_map<uint32_t, std::string>& bindings) {
+ if (type != Type::Reference || type.GetReferenceIndex() != kInvalidIndex) {
+ return;
+ }
+
+ const auto name_iterator = bindings.find(index);
+ assert(name_iterator != bindings.cend());
+ const auto type_index = module.type_bindings.FindIndex(name_iterator->second);
+ assert(type_index != kInvalidIndex);
+ type = Type(Type::Reference, type_index);
+}
+
+void ResolveTypeNames(const Module& module, FuncDeclaration* decl) {
+ assert(decl);
+ auto& signature = decl->sig;
+
+ for (uint32_t param_index = 0; param_index < signature.GetNumParams();
+ ++param_index) {
+ ResolveTypeName(module, signature.param_types[param_index], param_index,
+ signature.param_type_names);
+ }
+
+ for (uint32_t result_index = 0; result_index < signature.GetNumResults();
+ ++result_index) {
+ ResolveTypeName(module, signature.result_types[result_index], result_index,
+ signature.result_type_names);
+ }
+}
+
+void ResolveImplicitlyDefinedFunctionType(const Location& loc,
+ Module* module,
+ const FuncDeclaration& decl) {
+ // Resolve implicitly defined function types, e.g.: (func (param i32) ...)
+ if (!decl.has_func_type) {
+ Index func_type_index = module->GetFuncTypeIndex(decl.sig);
+ if (func_type_index == kInvalidIndex) {
+ auto func_type_field = std::make_unique<TypeModuleField>(loc);
+ auto func_type = std::make_unique<FuncType>();
+ func_type->sig = decl.sig;
+ func_type_field->type = std::move(func_type);
+ module->AppendField(std::move(func_type_field));
+ }
+ }
+}
+
+Result CheckTypeIndex(const Location& loc,
+ Type actual,
+ Type expected,
+ const char* desc,
+ Index index,
+ const char* index_kind,
+ Errors* errors) {
+ // Types must match exactly; no subtyping should be allowed.
+ if (actual != expected) {
+ errors->emplace_back(
+ ErrorLevel::Error, loc,
+ StringPrintf("type mismatch for %s %" PRIindex
+ " of %s. got %s, expected %s",
+ index_kind, index, desc, actual.GetName().c_str(),
+ expected.GetName().c_str()));
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+Result CheckTypes(const Location& loc,
+ const TypeVector& actual,
+ const TypeVector& expected,
+ const char* desc,
+ const char* index_kind,
+ Errors* errors) {
+ Result result = Result::Ok;
+ if (actual.size() == expected.size()) {
+ for (size_t i = 0; i < actual.size(); ++i) {
+ result |= CheckTypeIndex(loc, actual[i], expected[i], desc, i, index_kind,
+ errors);
+ }
+ } else {
+ errors->emplace_back(
+ ErrorLevel::Error, loc,
+ StringPrintf("expected %" PRIzd " %ss, got %" PRIzd, expected.size(),
+ index_kind, actual.size()));
+ result = Result::Error;
+ }
+ return result;
+}
+
+Result CheckFuncTypeVarMatchesExplicit(const Location& loc,
+ const Module& module,
+ const FuncDeclaration& decl,
+ Errors* errors) {
+ Result result = Result::Ok;
+ if (decl.has_func_type) {
+ const FuncType* func_type = module.GetFuncType(decl.type_var);
+ if (func_type) {
+ result |=
+ CheckTypes(loc, decl.sig.result_types, func_type->sig.result_types,
+ "function", "result", errors);
+ result |=
+ CheckTypes(loc, decl.sig.param_types, func_type->sig.param_types,
+ "function", "argument", errors);
+ } else if (!(decl.sig.param_types.empty() &&
+ decl.sig.result_types.empty())) {
+ // We want to check whether the function type at the explicit index
+ // matches the given param and result types. If they were omitted then
+ // they'll be resolved automatically (see
+ // ResolveFuncTypeWithEmptySignature), but if they are provided then we
+ // have to check. If we get here then the type var is invalid, so we
+ // can't check whether they match.
+ if (decl.type_var.is_index()) {
+ errors->emplace_back(ErrorLevel::Error, loc,
+ StringPrintf("invalid func type index %" PRIindex,
+ decl.type_var.index()));
+ } else {
+ errors->emplace_back(ErrorLevel::Error, loc,
+ StringPrintf("expected func type identifier %s",
+ decl.type_var.name().c_str()));
+ }
+ result = Result::Error;
+ }
+ }
+ return result;
+}
+
+bool IsInlinableFuncSignature(const FuncSignature& sig) {
+ return sig.GetNumParams() == 0 && sig.GetNumResults() <= 1;
+}
+
+class ResolveFuncTypesExprVisitorDelegate : public ExprVisitor::DelegateNop {
+ public:
+ explicit ResolveFuncTypesExprVisitorDelegate(Module* module, Errors* errors)
+ : module_(module), errors_(errors) {}
+
+ void ResolveBlockDeclaration(const Location& loc, BlockDeclaration* decl) {
+ ResolveTypeNames(*module_, decl);
+ ResolveFuncTypeWithEmptySignature(*module_, decl);
+ if (!IsInlinableFuncSignature(decl->sig)) {
+ ResolveImplicitlyDefinedFunctionType(loc, module_, *decl);
+ }
+ }
+
+ Result BeginBlockExpr(BlockExpr* expr) override {
+ ResolveBlockDeclaration(expr->loc, &expr->block.decl);
+ return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_,
+ expr->block.decl, errors_);
+ }
+
+ Result BeginIfExpr(IfExpr* expr) override {
+ ResolveBlockDeclaration(expr->loc, &expr->true_.decl);
+ return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_,
+ expr->true_.decl, errors_);
+ }
+
+ Result BeginLoopExpr(LoopExpr* expr) override {
+ ResolveBlockDeclaration(expr->loc, &expr->block.decl);
+ return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_,
+ expr->block.decl, errors_);
+ }
+
+ Result BeginTryExpr(TryExpr* expr) override {
+ ResolveBlockDeclaration(expr->loc, &expr->block.decl);
+ return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_,
+ expr->block.decl, errors_);
+ }
+
+ Result OnCallIndirectExpr(CallIndirectExpr* expr) override {
+ ResolveFuncTypeWithEmptySignature(*module_, &expr->decl);
+ ResolveImplicitlyDefinedFunctionType(expr->loc, module_, expr->decl);
+ return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_, expr->decl,
+ errors_);
+ }
+
+ Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr* expr) override {
+ ResolveFuncTypeWithEmptySignature(*module_, &expr->decl);
+ ResolveImplicitlyDefinedFunctionType(expr->loc, module_, expr->decl);
+ return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_, expr->decl,
+ errors_);
+ }
+
+ private:
+ Module* module_;
+ Errors* errors_;
+};
+
+Result ResolveFuncTypes(Module* module, Errors* errors) {
+ Result result = Result::Ok;
+ for (ModuleField& field : module->fields) {
+ Func* func = nullptr;
+ FuncDeclaration* decl = nullptr;
+ if (auto* func_field = dyn_cast<FuncModuleField>(&field)) {
+ func = &func_field->func;
+ decl = &func->decl;
+ } else if (auto* tag_field = dyn_cast<TagModuleField>(&field)) {
+ decl = &tag_field->tag.decl;
+ } else if (auto* import_field = dyn_cast<ImportModuleField>(&field)) {
+ if (auto* func_import =
+ dyn_cast<FuncImport>(import_field->import.get())) {
+ // Only check the declaration, not the function itself, since it is an
+ // import.
+ decl = &func_import->func.decl;
+ } else if (auto* tag_import =
+ dyn_cast<TagImport>(import_field->import.get())) {
+ decl = &tag_import->tag.decl;
+ } else {
+ continue;
+ }
+ } else {
+ continue;
+ }
+
+ bool has_func_type_and_empty_signature = false;
+
+ if (decl) {
+ ResolveTypeNames(*module, decl);
+ has_func_type_and_empty_signature =
+ ResolveFuncTypeWithEmptySignature(*module, decl);
+ ResolveImplicitlyDefinedFunctionType(field.loc, module, *decl);
+ result |=
+ CheckFuncTypeVarMatchesExplicit(field.loc, *module, *decl, errors);
+ }
+
+ if (func) {
+ if (has_func_type_and_empty_signature) {
+ // The call to ResolveFuncTypeWithEmptySignature may have updated the
+ // function signature so there are parameters. Since parameters and
+ // local variables share the same index space, we need to increment the
+ // local indexes bound to a given name by the number of parameters in
+ // the function.
+ for (auto& [name, binding] : func->bindings) {
+ binding.index += func->GetNumParams();
+ }
+ }
+
+ ResolveFuncTypesExprVisitorDelegate delegate(module, errors);
+ ExprVisitor visitor(&delegate);
+ result |= visitor.VisitFunc(func);
+ }
+ }
+ return result;
+}
+
+void AppendInlineExportFields(Module* module,
+ ModuleFieldList* fields,
+ Index index) {
+ Location last_field_loc = module->fields.back().loc;
+
+ for (ModuleField& field : *fields) {
+ auto* export_field = cast<ExportModuleField>(&field);
+ export_field->export_.var = Var(index, last_field_loc);
+ }
+
+ module->AppendFields(fields);
+}
+
+} // End of anonymous namespace
+
+WastParser::WastParser(WastLexer* lexer,
+ Errors* errors,
+ WastParseOptions* options)
+ : lexer_(lexer), errors_(errors), options_(options) {}
+
+void WastParser::Error(Location loc, const char* format, ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ errors_->emplace_back(ErrorLevel::Error, loc, buffer);
+}
+
+Token WastParser::GetToken() {
+ if (tokens_.empty()) {
+ tokens_.push_back(lexer_->GetToken());
+ }
+ return tokens_.front();
+}
+
+Location WastParser::GetLocation() {
+ return GetToken().loc;
+}
+
+TokenType WastParser::Peek(size_t n) {
+ while (tokens_.size() <= n) {
+ Token cur = lexer_->GetToken();
+ if (cur.token_type() != TokenType::LparAnn) {
+ tokens_.push_back(cur);
+ } else {
+ // 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();
+ switch (cur.token_type()) {
+ case TokenType::Lpar:
+ case TokenType::LparAnn:
+ indent++;
+ break;
+
+ case TokenType::Rpar:
+ indent--;
+ break;
+
+ case TokenType::Eof:
+ indent = 0;
+ Error(cur.loc, "unterminated annotation");
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+ return tokens_.at(n).token_type();
+}
+
+TokenTypePair WastParser::PeekPair() {
+ return TokenTypePair{{Peek(), Peek(1)}};
+}
+
+bool WastParser::PeekMatch(TokenType type, size_t n) {
+ return Peek(n) == type;
+}
+
+bool WastParser::PeekMatchLpar(TokenType type) {
+ return Peek() == TokenType::Lpar && Peek(1) == type;
+}
+
+bool WastParser::PeekMatchExpr() {
+ return IsExpr(PeekPair());
+}
+
+bool WastParser::PeekMatchRefType() {
+ return options_->features.function_references_enabled() &&
+ PeekMatchLpar(TokenType::Ref);
+}
+
+bool WastParser::Match(TokenType type) {
+ if (PeekMatch(type)) {
+ Consume();
+ return true;
+ }
+ return false;
+}
+
+bool WastParser::MatchLpar(TokenType type) {
+ if (PeekMatchLpar(type)) {
+ Consume();
+ Consume();
+ return true;
+ }
+ return false;
+}
+
+Result WastParser::Expect(TokenType type) {
+ if (!Match(type)) {
+ Token token = Consume();
+ Error(token.loc, "unexpected token %s, expected %s.",
+ token.to_string_clamp(kMaxErrorTokenLength).c_str(),
+ GetTokenTypeName(type));
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+Token WastParser::Consume() {
+ assert(!tokens_.empty());
+ Token token = tokens_.front();
+ tokens_.pop_front();
+ return token;
+}
+
+Result WastParser::Synchronize(SynchronizeFunc func) {
+ static const int kMaxConsumed = 10;
+ for (int i = 0; i < kMaxConsumed; ++i) {
+ if (func(PeekPair())) {
+ return Result::Ok;
+ }
+
+ Token token = Consume();
+ if (token.token_type() == TokenType::Reserved) {
+ Error(token.loc, "unexpected token %s.",
+ token.to_string_clamp(kMaxErrorTokenLength).c_str());
+ }
+ }
+
+ return Result::Error;
+}
+
+void WastParser::ErrorUnlessOpcodeEnabled(const Token& token) {
+ Opcode opcode = token.opcode();
+ if (!opcode.IsEnabled(options_->features)) {
+ Error(token.loc, "opcode not allowed: %s", opcode.GetName());
+ }
+}
+
+Result WastParser::ErrorExpected(const std::vector<std::string>& expected,
+ const char* example) {
+ Token token = Consume();
+ std::string expected_str;
+ if (!expected.empty()) {
+ expected_str = ", expected ";
+ for (size_t i = 0; i < expected.size(); ++i) {
+ if (i != 0) {
+ if (i == expected.size() - 1) {
+ expected_str += " or ";
+ } else {
+ expected_str += ", ";
+ }
+ }
+
+ expected_str += expected[i];
+ }
+
+ if (example) {
+ expected_str += " (e.g. ";
+ expected_str += example;
+ expected_str += ")";
+ }
+ }
+
+ Error(token.loc, "unexpected token \"%s\"%s.",
+ token.to_string_clamp(kMaxErrorTokenLength).c_str(),
+ expected_str.c_str());
+ return Result::Error;
+}
+
+Result WastParser::ErrorIfLpar(const std::vector<std::string>& expected,
+ const char* example) {
+ if (Match(TokenType::Lpar)) {
+ GetToken();
+ return ErrorExpected(expected, example);
+ }
+ return Result::Ok;
+}
+
+bool WastParser::ParseBindVarOpt(std::string* name) {
+ WABT_TRACE(ParseBindVarOpt);
+ if (!PeekMatch(TokenType::Var)) {
+ return false;
+ }
+ Token token = Consume();
+ *name = std::string(token.text());
+ return true;
+}
+
+Result WastParser::ParseVar(Var* out_var) {
+ WABT_TRACE(ParseVar);
+ if (PeekMatch(TokenType::Nat)) {
+ Token token = Consume();
+ std::string_view sv = token.literal().text;
+ uint64_t index = kInvalidIndex;
+ if (Failed(ParseUint64(sv, &index))) {
+ // Print an error, but don't fail parsing.
+ Error(token.loc, "invalid int \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(sv));
+ }
+
+ *out_var = Var(index, token.loc);
+ return Result::Ok;
+ } else if (PeekMatch(TokenType::Var)) {
+ Token token = Consume();
+ *out_var = Var(token.text(), token.loc);
+ return Result::Ok;
+ } else {
+ return ErrorExpected({"a numeric index", "a name"}, "12 or $foo");
+ }
+}
+
+bool WastParser::ParseVarOpt(Var* out_var, Var default_var) {
+ WABT_TRACE(ParseVarOpt);
+ if (PeekMatch(TokenType::Nat) || PeekMatch(TokenType::Var)) {
+ Result result = ParseVar(out_var);
+ // Should always succeed, the only way it could fail is if the token
+ // doesn't match.
+ assert(Succeeded(result));
+ WABT_USE(result);
+ return true;
+ } else {
+ *out_var = default_var;
+ return false;
+ }
+}
+
+Result WastParser::ParseOffsetExpr(ExprList* out_expr_list) {
+ WABT_TRACE(ParseOffsetExpr);
+ if (!ParseOffsetExprOpt(out_expr_list)) {
+ return ErrorExpected({"an offset expr"}, "(i32.const 123)");
+ }
+ return Result::Ok;
+}
+
+bool WastParser::ParseOffsetExprOpt(ExprList* out_expr_list) {
+ WABT_TRACE(ParseOffsetExprOpt);
+ if (MatchLpar(TokenType::Offset)) {
+ CHECK_RESULT(ParseTerminatingInstrList(out_expr_list));
+ EXPECT(Rpar);
+ return true;
+ } else if (PeekMatchExpr()) {
+ CHECK_RESULT(ParseExpr(out_expr_list));
+ return true;
+ } else {
+ return false;
+ }
+}
+
+Result WastParser::ParseTextList(std::vector<uint8_t>* out_data) {
+ WABT_TRACE(ParseTextList);
+ if (!ParseTextListOpt(out_data)) {
+ // TODO(binji): Add error message here.
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+bool WastParser::ParseTextListOpt(std::vector<uint8_t>* out_data) {
+ WABT_TRACE(ParseTextListOpt);
+ TextVector texts;
+ while (PeekMatch(TokenType::Text))
+ texts.push_back(Consume().text());
+
+ RemoveEscapes(texts, std::back_inserter(*out_data));
+ return !texts.empty();
+}
+
+Result WastParser::ParseVarList(VarVector* out_var_list) {
+ WABT_TRACE(ParseVarList);
+ Var var;
+ while (ParseVarOpt(&var)) {
+ out_var_list->emplace_back(var);
+ }
+ if (out_var_list->empty()) {
+ return ErrorExpected({"a var"}, "12 or $foo");
+ } else {
+ return Result::Ok;
+ }
+}
+
+bool WastParser::ParseElemExprOpt(ExprList* out_elem_expr) {
+ WABT_TRACE(ParseElemExprOpt);
+ bool item = MatchLpar(TokenType::Item);
+ ExprList exprs;
+ if (item) {
+ if (ParseTerminatingInstrList(&exprs) != Result::Ok) {
+ return false;
+ }
+ EXPECT(Rpar);
+ } else {
+ if (ParseExpr(&exprs) != Result::Ok) {
+ return false;
+ }
+ }
+ if (!exprs.size()) {
+ return false;
+ }
+ *out_elem_expr = std::move(exprs);
+ return true;
+}
+
+bool WastParser::ParseElemExprListOpt(ExprListVector* out_list) {
+ ExprList elem_expr;
+ while (ParseElemExprOpt(&elem_expr)) {
+ out_list->push_back(std::move(elem_expr));
+ }
+ return !out_list->empty();
+}
+
+bool WastParser::ParseElemExprVarListOpt(ExprListVector* out_list) {
+ WABT_TRACE(ParseElemExprVarListOpt);
+ Var var;
+ ExprList init_expr;
+ while (ParseVarOpt(&var)) {
+ init_expr.push_back(std::make_unique<RefFuncExpr>(var));
+ out_list->push_back(std::move(init_expr));
+ }
+ return !out_list->empty();
+}
+
+Result WastParser::ParseValueType(Var* out_type) {
+ WABT_TRACE(ParseValueType);
+
+ const bool is_ref_type = PeekMatchRefType();
+ const bool is_value_type = PeekMatch(TokenType::ValueType);
+
+ if (!is_value_type && !is_ref_type) {
+ return ErrorExpected(
+ {"i32", "i64", "f32", "f64", "v128", "externref", "funcref"});
+ }
+
+ if (is_ref_type) {
+ EXPECT(Lpar);
+ EXPECT(Ref);
+ CHECK_RESULT(ParseVar(out_type));
+ EXPECT(Rpar);
+ return Result::Ok;
+ }
+
+ Token token = Consume();
+ Type type = token.type();
+ bool is_enabled;
+ switch (type) {
+ case Type::V128:
+ is_enabled = options_->features.simd_enabled();
+ break;
+ case Type::FuncRef:
+ case Type::ExternRef:
+ is_enabled = options_->features.reference_types_enabled();
+ break;
+ default:
+ is_enabled = true;
+ break;
+ }
+
+ if (!is_enabled) {
+ Error(token.loc, "value type not allowed: %s", type.GetName().c_str());
+ return Result::Error;
+ }
+
+ *out_type = Var(type, GetLocation());
+ return Result::Ok;
+}
+
+Result WastParser::ParseValueTypeList(
+ TypeVector* out_type_list,
+ std::unordered_map<uint32_t, std::string>* type_names) {
+ WABT_TRACE(ParseValueTypeList);
+ while (true) {
+ if (!PeekMatchRefType() && !PeekMatch(TokenType::ValueType)) {
+ break;
+ }
+
+ Var type;
+ CHECK_RESULT(ParseValueType(&type));
+
+ if (type.is_index()) {
+ out_type_list->push_back(Type(type.index()));
+ } else {
+ assert(type.is_name());
+ assert(options_->features.function_references_enabled());
+ type_names->emplace(out_type_list->size(), type.name());
+ out_type_list->push_back(Type(Type::Reference, kInvalidIndex));
+ }
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseRefKind(Type* out_type) {
+ WABT_TRACE(ParseRefKind);
+ if (!IsTokenTypeRefKind(Peek())) {
+ return ErrorExpected({"func", "extern", "exn"});
+ }
+
+ Token token = Consume();
+ Type type = token.type();
+
+ if ((type == Type::ExternRef &&
+ !options_->features.reference_types_enabled()) ||
+ ((type == Type::Struct || type == Type::Array) &&
+ !options_->features.gc_enabled())) {
+ Error(token.loc, "value type not allowed: %s", type.GetName().c_str());
+ return Result::Error;
+ }
+
+ *out_type = type;
+ return Result::Ok;
+}
+
+Result WastParser::ParseRefType(Type* out_type) {
+ WABT_TRACE(ParseRefType);
+ if (!PeekMatch(TokenType::ValueType)) {
+ return ErrorExpected({"funcref", "externref"});
+ }
+
+ Token token = Consume();
+ Type type = token.type();
+ if (type == Type::ExternRef &&
+ !options_->features.reference_types_enabled()) {
+ Error(token.loc, "value type not allowed: %s", type.GetName().c_str());
+ return Result::Error;
+ }
+
+ *out_type = type;
+ return Result::Ok;
+}
+
+bool WastParser::ParseRefTypeOpt(Type* out_type) {
+ WABT_TRACE(ParseRefTypeOpt);
+ if (!PeekMatch(TokenType::ValueType)) {
+ return false;
+ }
+
+ Token token = Consume();
+ Type type = token.type();
+ if (type == Type::ExternRef &&
+ !options_->features.reference_types_enabled()) {
+ return false;
+ }
+
+ *out_type = type;
+ return true;
+}
+
+Result WastParser::ParseQuotedText(std::string* text, bool check_utf8) {
+ WABT_TRACE(ParseQuotedText);
+ if (!PeekMatch(TokenType::Text)) {
+ return ErrorExpected({"a quoted string"}, "\"foo\"");
+ }
+
+ Token token = Consume();
+ RemoveEscapes(token.text(), std::back_inserter(*text));
+ if (check_utf8 && !IsValidUtf8(text->data(), text->length())) {
+ Error(token.loc, "quoted string has an invalid utf-8 encoding");
+ }
+ return Result::Ok;
+}
+
+bool WastParser::ParseOffsetOpt(Address* out_offset) {
+ WABT_TRACE(ParseOffsetOpt);
+ if (PeekMatch(TokenType::OffsetEqNat)) {
+ Token token = Consume();
+ uint64_t offset64;
+ std::string_view sv = token.text();
+ if (Failed(ParseInt64(sv, &offset64, ParseIntType::SignedAndUnsigned))) {
+ Error(token.loc, "invalid offset \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(sv));
+ }
+ // FIXME: make this depend on the current memory.
+ if (offset64 > UINT32_MAX) {
+ Error(token.loc, "offset must be less than or equal to 0xffffffff");
+ }
+ *out_offset = offset64;
+ return true;
+ } else {
+ *out_offset = 0;
+ return false;
+ }
+}
+
+bool WastParser::ParseAlignOpt(Address* out_align) {
+ WABT_TRACE(ParseAlignOpt);
+ if (PeekMatch(TokenType::AlignEqNat)) {
+ Token token = Consume();
+ std::string_view sv = token.text();
+ if (Failed(ParseInt64(sv, out_align, ParseIntType::UnsignedOnly))) {
+ Error(token.loc, "invalid alignment \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(sv));
+ }
+
+ if (!IsPowerOfTwo(*out_align)) {
+ Error(token.loc, "alignment must be power-of-two");
+ }
+
+ return true;
+ } else {
+ *out_align = WABT_USE_NATURAL_ALIGNMENT;
+ return false;
+ }
+}
+
+Result WastParser::ParseMemidx(Location loc, Var* out_memidx) {
+ WABT_TRACE(ParseMemidx);
+ if (PeekMatchLpar(TokenType::Memory)) {
+ if (!options_->features.multi_memory_enabled()) {
+ Error(loc, "Specifying memory variable is not allowed");
+ return Result::Error;
+ }
+ EXPECT(Lpar);
+ EXPECT(Memory);
+ CHECK_RESULT(ParseVar(out_memidx));
+ EXPECT(Rpar);
+ } else {
+ if (ParseVarOpt(out_memidx, Var(0, loc)) &&
+ !options_->features.multi_memory_enabled()) {
+ Error(loc, "Specifying memory variable is not allowed");
+ return Result::Error;
+ }
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseLimitsIndex(Limits* out_limits) {
+ WABT_TRACE(ParseLimitsIndex);
+
+ if (PeekMatch(TokenType::ValueType)) {
+ if (GetToken().type() == Type::I64) {
+ Consume();
+ out_limits->is_64 = true;
+ } else if (GetToken().type() == Type::I32) {
+ Consume();
+ out_limits->is_64 = false;
+ }
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseLimits(Limits* out_limits) {
+ WABT_TRACE(ParseLimits);
+
+ CHECK_RESULT(ParseNat(&out_limits->initial, out_limits->is_64));
+ if (PeekMatch(TokenType::Nat)) {
+ CHECK_RESULT(ParseNat(&out_limits->max, out_limits->is_64));
+ out_limits->has_max = true;
+ } else {
+ out_limits->has_max = false;
+ }
+
+ if (Match(TokenType::Shared)) {
+ out_limits->is_shared = true;
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseNat(uint64_t* out_nat, bool is_64) {
+ WABT_TRACE(ParseNat);
+ if (!PeekMatch(TokenType::Nat)) {
+ return ErrorExpected({"a natural number"}, "123");
+ }
+
+ Token token = Consume();
+ std::string_view sv = token.literal().text;
+ if (Failed(ParseUint64(sv, out_nat)) || (!is_64 && *out_nat > 0xffffffffu)) {
+ Error(token.loc, "invalid int \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(sv));
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseModule(std::unique_ptr<Module>* out_module) {
+ WABT_TRACE(ParseModule);
+ auto module = std::make_unique<Module>();
+
+ if (PeekMatchLpar(TokenType::Module)) {
+ // Starts with "(module". Allow text and binary modules, but no quoted
+ // modules.
+ CommandPtr command;
+ CHECK_RESULT(ParseModuleCommand(nullptr, &command));
+ if (isa<ModuleCommand>(command.get())) {
+ auto module_command = cast<ModuleCommand>(std::move(command));
+ *module = std::move(module_command->module);
+ } else {
+ assert(isa<ScriptModuleCommand>(command.get()));
+ auto module_command = cast<ScriptModuleCommand>(std::move(command));
+ *module = std::move(module_command->module);
+ }
+ } else if (IsModuleField(PeekPair())) {
+ // Parse an inline module (i.e. one with no surrounding (module)).
+ CHECK_RESULT(ParseModuleFieldList(module.get()));
+ } else {
+ ConsumeIfLpar();
+ ErrorExpected({"a module field", "a module"});
+ }
+
+ EXPECT(Eof);
+ if (errors_->size() == 0) {
+ *out_module = std::move(module);
+ return Result::Ok;
+ } else {
+ return Result::Error;
+ }
+}
+
+Result WastParser::ParseScript(std::unique_ptr<Script>* out_script) {
+ WABT_TRACE(ParseScript);
+ auto script = std::make_unique<Script>();
+
+ // Don't consume the Lpar yet, even though it is required. This way the
+ // sub-parser functions (e.g. ParseFuncModuleField) can consume it and keep
+ // the parsing structure more regular.
+ if (IsModuleField(PeekPair())) {
+ // Parse an inline module (i.e. one with no surrounding (module)).
+ auto command = std::make_unique<ModuleCommand>();
+ command->module.loc = GetLocation();
+ CHECK_RESULT(ParseModuleFieldList(&command->module));
+ script->commands.emplace_back(std::move(command));
+ } else if (IsCommand(PeekPair())) {
+ CHECK_RESULT(ParseCommandList(script.get(), &script->commands));
+ } else {
+ ConsumeIfLpar();
+ ErrorExpected({"a module field", "a command"});
+ }
+
+ EXPECT(Eof);
+ if (errors_->size() == 0) {
+ *out_script = std::move(script);
+ return Result::Ok;
+ } else {
+ return Result::Error;
+ }
+}
+
+Result WastParser::ParseModuleFieldList(Module* module) {
+ WABT_TRACE(ParseModuleFieldList);
+ while (IsModuleField(PeekPair())) {
+ if (Failed(ParseModuleField(module))) {
+ CHECK_RESULT(Synchronize(IsModuleField));
+ }
+ }
+ CHECK_RESULT(ResolveFuncTypes(module, errors_));
+ CHECK_RESULT(ResolveNamesModule(module, errors_));
+ return Result::Ok;
+}
+
+Result WastParser::ParseModuleField(Module* module) {
+ WABT_TRACE(ParseModuleField);
+ switch (Peek(1)) {
+ case TokenType::Data: return ParseDataModuleField(module);
+ case TokenType::Elem: return ParseElemModuleField(module);
+ case TokenType::Tag: return ParseTagModuleField(module);
+ case TokenType::Export: return ParseExportModuleField(module);
+ case TokenType::Func: return ParseFuncModuleField(module);
+ case TokenType::Type: return ParseTypeModuleField(module);
+ case TokenType::Global: return ParseGlobalModuleField(module);
+ case TokenType::Import: return ParseImportModuleField(module);
+ case TokenType::Memory: return ParseMemoryModuleField(module);
+ case TokenType::Start: return ParseStartModuleField(module);
+ case TokenType::Table: return ParseTableModuleField(module);
+ default:
+ assert(
+ !"ParseModuleField should only be called if IsModuleField() is true");
+ return Result::Error;
+ }
+}
+
+Result WastParser::ParseDataModuleField(Module* module) {
+ WABT_TRACE(ParseDataModuleField);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Data);
+ std::string name;
+ ParseBindVarOpt(&name);
+ auto field = std::make_unique<DataSegmentModuleField>(loc, name);
+
+ if (PeekMatchLpar(TokenType::Memory)) {
+ EXPECT(Lpar);
+ EXPECT(Memory);
+ CHECK_RESULT(ParseVar(&field->data_segment.memory_var));
+ EXPECT(Rpar);
+ CHECK_RESULT(ParseOffsetExpr(&field->data_segment.offset));
+ } else if (ParseVarOpt(&field->data_segment.memory_var, Var(0, loc))) {
+ CHECK_RESULT(ParseOffsetExpr(&field->data_segment.offset));
+ } else if (!ParseOffsetExprOpt(&field->data_segment.offset)) {
+ if (!options_->features.bulk_memory_enabled()) {
+ Error(loc, "passive data segments are not allowed");
+ return Result::Error;
+ }
+
+ field->data_segment.kind = SegmentKind::Passive;
+ }
+
+ ParseTextListOpt(&field->data_segment.data);
+ EXPECT(Rpar);
+ module->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result WastParser::ParseElemModuleField(Module* module) {
+ WABT_TRACE(ParseElemModuleField);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Elem);
+
+ // With MVP text format the name here was intended to refer to the table
+ // that the elem segment was part of, but we never did anything with this name
+ // since there was only one table anyway.
+ // With bulk-memory enabled this introduces a new name for the particular
+ // elem segment.
+ std::string initial_name;
+ bool has_name = ParseBindVarOpt(&initial_name);
+
+ std::string segment_name = initial_name;
+ if (!options_->features.bulk_memory_enabled()) {
+ segment_name = "";
+ }
+ auto field = std::make_unique<ElemSegmentModuleField>(loc, segment_name);
+ if (options_->features.reference_types_enabled() &&
+ Match(TokenType::Declare)) {
+ field->elem_segment.kind = SegmentKind::Declared;
+ }
+
+ // Optional table specifier
+ if (options_->features.bulk_memory_enabled()) {
+ if (PeekMatchLpar(TokenType::Table)) {
+ EXPECT(Lpar);
+ EXPECT(Table);
+ CHECK_RESULT(ParseVar(&field->elem_segment.table_var));
+ EXPECT(Rpar);
+ } else {
+ ParseVarOpt(&field->elem_segment.table_var, Var(0, loc));
+ }
+ } else {
+ if (has_name) {
+ field->elem_segment.table_var = Var(initial_name, loc);
+ } else {
+ ParseVarOpt(&field->elem_segment.table_var, Var(0, loc));
+ }
+ }
+
+ // Parse offset expression, if not declared/passive segment.
+ if (options_->features.bulk_memory_enabled()) {
+ if (field->elem_segment.kind != SegmentKind::Declared &&
+ !ParseOffsetExprOpt(&field->elem_segment.offset)) {
+ field->elem_segment.kind = SegmentKind::Passive;
+ }
+ } else {
+ CHECK_RESULT(ParseOffsetExpr(&field->elem_segment.offset));
+ }
+
+ if (ParseRefTypeOpt(&field->elem_segment.elem_type)) {
+ ParseElemExprListOpt(&field->elem_segment.elem_exprs);
+ } else {
+ field->elem_segment.elem_type = Type::FuncRef;
+ if (PeekMatch(TokenType::Func)) {
+ EXPECT(Func);
+ }
+ ParseElemExprVarListOpt(&field->elem_segment.elem_exprs);
+ }
+ EXPECT(Rpar);
+ module->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result WastParser::ParseTagModuleField(Module* module) {
+ WABT_TRACE(ParseTagModuleField);
+ if (!options_->features.exceptions_enabled()) {
+ Error(Consume().loc, "tag not allowed");
+ return Result::Error;
+ }
+ EXPECT(Lpar);
+ EXPECT(Tag);
+ Location loc = GetLocation();
+
+ std::string name;
+ ParseBindVarOpt(&name);
+
+ ModuleFieldList export_fields;
+ CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Tag));
+
+ if (PeekMatchLpar(TokenType::Import)) {
+ CheckImportOrdering(module);
+ auto import = std::make_unique<TagImport>(name);
+ Tag& tag = import->tag;
+ CHECK_RESULT(ParseInlineImport(import.get()));
+ CHECK_RESULT(ParseTypeUseOpt(&tag.decl));
+ CHECK_RESULT(ParseUnboundFuncSignature(&tag.decl.sig));
+ CHECK_RESULT(ErrorIfLpar({"type", "param", "result"}));
+ auto field =
+ std::make_unique<ImportModuleField>(std::move(import), GetLocation());
+ module->AppendField(std::move(field));
+ } else {
+ auto field = std::make_unique<TagModuleField>(loc, name);
+ CHECK_RESULT(ParseTypeUseOpt(&field->tag.decl));
+ CHECK_RESULT(ParseUnboundFuncSignature(&field->tag.decl.sig));
+ module->AppendField(std::move(field));
+ }
+
+ AppendInlineExportFields(module, &export_fields, module->tags.size() - 1);
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseExportModuleField(Module* module) {
+ WABT_TRACE(ParseExportModuleField);
+ EXPECT(Lpar);
+ auto field = std::make_unique<ExportModuleField>(GetLocation());
+ EXPECT(Export);
+ CHECK_RESULT(ParseQuotedText(&field->export_.name));
+ CHECK_RESULT(ParseExportDesc(&field->export_));
+ EXPECT(Rpar);
+ module->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result WastParser::ParseFuncModuleField(Module* module) {
+ WABT_TRACE(ParseFuncModuleField);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Func);
+ std::string name;
+ ParseBindVarOpt(&name);
+
+ ModuleFieldList export_fields;
+ CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Func));
+
+ if (PeekMatchLpar(TokenType::Import)) {
+ CheckImportOrdering(module);
+ auto import = std::make_unique<FuncImport>(name);
+ Func& func = import->func;
+ CHECK_RESULT(ParseInlineImport(import.get()));
+ CHECK_RESULT(ParseTypeUseOpt(&func.decl));
+ CHECK_RESULT(ParseFuncSignature(&func.decl.sig, &func.bindings));
+ CHECK_RESULT(ErrorIfLpar({"type", "param", "result"}));
+ auto field =
+ std::make_unique<ImportModuleField>(std::move(import), GetLocation());
+ module->AppendField(std::move(field));
+ } else {
+ auto field = std::make_unique<FuncModuleField>(loc, name);
+ Func& func = field->func;
+ func.loc = GetLocation();
+ CHECK_RESULT(ParseTypeUseOpt(&func.decl));
+ CHECK_RESULT(ParseFuncSignature(&func.decl.sig, &func.bindings));
+ TypeVector local_types;
+ CHECK_RESULT(ParseBoundValueTypeList(
+ TokenType::Local, &local_types, &func.bindings,
+ &func.decl.sig.param_type_names, func.GetNumParams()));
+ func.local_types.Set(local_types);
+ CHECK_RESULT(ParseTerminatingInstrList(&func.exprs));
+ module->AppendField(std::move(field));
+ }
+
+ AppendInlineExportFields(module, &export_fields, module->funcs.size() - 1);
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseTypeModuleField(Module* module) {
+ WABT_TRACE(ParseTypeModuleField);
+ EXPECT(Lpar);
+ auto field = std::make_unique<TypeModuleField>(GetLocation());
+ EXPECT(Type);
+
+ std::string name;
+ ParseBindVarOpt(&name);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+
+ if (Match(TokenType::Func)) {
+ auto func_type = std::make_unique<FuncType>(name);
+ BindingHash bindings;
+ CHECK_RESULT(ParseFuncSignature(&func_type->sig, &bindings));
+ CHECK_RESULT(ErrorIfLpar({"param", "result"}));
+ field->type = std::move(func_type);
+ } else if (Match(TokenType::Struct)) {
+ if (!options_->features.gc_enabled()) {
+ Error(loc, "struct not allowed");
+ return Result::Error;
+ }
+ auto struct_type = std::make_unique<StructType>(name);
+ CHECK_RESULT(ParseFieldList(&struct_type->fields));
+ field->type = std::move(struct_type);
+ } else if (Match(TokenType::Array)) {
+ if (!options_->features.gc_enabled()) {
+ Error(loc, "array type not allowed");
+ }
+ auto array_type = std::make_unique<ArrayType>(name);
+ CHECK_RESULT(ParseField(&array_type->field));
+ field->type = std::move(array_type);
+ } else {
+ return ErrorExpected({"func", "struct", "array"});
+ }
+
+ EXPECT(Rpar);
+ EXPECT(Rpar);
+ module->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result WastParser::ParseField(Field* field) {
+ WABT_TRACE(ParseField);
+ auto parse_mut_valuetype = [&]() -> Result {
+ // TODO: Share with ParseGlobalType?
+ if (MatchLpar(TokenType::Mut)) {
+ field->mutable_ = true;
+ Var type;
+ CHECK_RESULT(ParseValueType(&type));
+ field->type = Type(type.index());
+ EXPECT(Rpar);
+ } else {
+ field->mutable_ = false;
+ Var type;
+ CHECK_RESULT(ParseValueType(&type));
+ field->type = Type(type.index());
+ }
+ return Result::Ok;
+ };
+
+ if (MatchLpar(TokenType::Field)) {
+ ParseBindVarOpt(&field->name);
+ CHECK_RESULT(parse_mut_valuetype());
+ EXPECT(Rpar);
+ } else {
+ CHECK_RESULT(parse_mut_valuetype());
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseFieldList(std::vector<Field>* fields) {
+ WABT_TRACE(ParseFieldList);
+ while (PeekMatch(TokenType::ValueType) || PeekMatch(TokenType::Lpar)) {
+ Field field;
+ CHECK_RESULT(ParseField(&field));
+ fields->push_back(field);
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseGlobalModuleField(Module* module) {
+ WABT_TRACE(ParseGlobalModuleField);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Global);
+ std::string name;
+ ParseBindVarOpt(&name);
+
+ ModuleFieldList export_fields;
+ CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Global));
+
+ if (PeekMatchLpar(TokenType::Import)) {
+ CheckImportOrdering(module);
+ auto import = std::make_unique<GlobalImport>(name);
+ CHECK_RESULT(ParseInlineImport(import.get()));
+ CHECK_RESULT(ParseGlobalType(&import->global));
+ auto field =
+ std::make_unique<ImportModuleField>(std::move(import), GetLocation());
+ module->AppendField(std::move(field));
+ } else {
+ auto field = std::make_unique<GlobalModuleField>(loc, name);
+ CHECK_RESULT(ParseGlobalType(&field->global));
+ CHECK_RESULT(ParseTerminatingInstrList(&field->global.init_expr));
+ module->AppendField(std::move(field));
+ }
+
+ AppendInlineExportFields(module, &export_fields, module->globals.size() - 1);
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseImportModuleField(Module* module) {
+ WABT_TRACE(ParseImportModuleField);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ CheckImportOrdering(module);
+ EXPECT(Import);
+ std::string module_name;
+ std::string field_name;
+ CHECK_RESULT(ParseQuotedText(&module_name));
+ CHECK_RESULT(ParseQuotedText(&field_name));
+ EXPECT(Lpar);
+
+ std::unique_ptr<ImportModuleField> field;
+ std::string name;
+
+ switch (Peek()) {
+ case TokenType::Func: {
+ Consume();
+ ParseBindVarOpt(&name);
+ auto import = std::make_unique<FuncImport>(name);
+ CHECK_RESULT(ParseTypeUseOpt(&import->func.decl));
+ CHECK_RESULT(
+ ParseFuncSignature(&import->func.decl.sig, &import->func.bindings));
+ CHECK_RESULT(ErrorIfLpar({"param", "result"}));
+ EXPECT(Rpar);
+ field = std::make_unique<ImportModuleField>(std::move(import), loc);
+ break;
+ }
+
+ case TokenType::Table: {
+ Consume();
+ ParseBindVarOpt(&name);
+ auto import = std::make_unique<TableImport>(name);
+ CHECK_RESULT(ParseLimits(&import->table.elem_limits));
+ CHECK_RESULT(ParseRefType(&import->table.elem_type));
+ EXPECT(Rpar);
+ field = std::make_unique<ImportModuleField>(std::move(import), loc);
+ break;
+ }
+
+ case TokenType::Memory: {
+ Consume();
+ ParseBindVarOpt(&name);
+ auto import = std::make_unique<MemoryImport>(name);
+ CHECK_RESULT(ParseLimitsIndex(&import->memory.page_limits));
+ CHECK_RESULT(ParseLimits(&import->memory.page_limits));
+ EXPECT(Rpar);
+ field = std::make_unique<ImportModuleField>(std::move(import), loc);
+ break;
+ }
+
+ case TokenType::Global: {
+ Consume();
+ ParseBindVarOpt(&name);
+ auto import = std::make_unique<GlobalImport>(name);
+ CHECK_RESULT(ParseGlobalType(&import->global));
+ EXPECT(Rpar);
+ field = std::make_unique<ImportModuleField>(std::move(import), loc);
+ break;
+ }
+
+ case TokenType::Tag: {
+ Consume();
+ ParseBindVarOpt(&name);
+ auto import = std::make_unique<TagImport>(name);
+ CHECK_RESULT(ParseTypeUseOpt(&import->tag.decl));
+ CHECK_RESULT(ParseUnboundFuncSignature(&import->tag.decl.sig));
+ EXPECT(Rpar);
+ field = std::make_unique<ImportModuleField>(std::move(import), loc);
+ break;
+ }
+
+ default:
+ return ErrorExpected({"an external kind"});
+ }
+
+ field->import->module_name = module_name;
+ field->import->field_name = field_name;
+
+ module->AppendField(std::move(field));
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseMemoryModuleField(Module* module) {
+ WABT_TRACE(ParseMemoryModuleField);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Memory);
+ std::string name;
+ ParseBindVarOpt(&name);
+
+ ModuleFieldList export_fields;
+ CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Memory));
+
+ if (PeekMatchLpar(TokenType::Import)) {
+ CheckImportOrdering(module);
+ auto import = std::make_unique<MemoryImport>(name);
+ CHECK_RESULT(ParseInlineImport(import.get()));
+ CHECK_RESULT(ParseLimitsIndex(&import->memory.page_limits));
+ CHECK_RESULT(ParseLimits(&import->memory.page_limits));
+ auto field =
+ std::make_unique<ImportModuleField>(std::move(import), GetLocation());
+ module->AppendField(std::move(field));
+ } else {
+ auto field = std::make_unique<MemoryModuleField>(loc, name);
+ CHECK_RESULT(ParseLimitsIndex(&field->memory.page_limits));
+ if (MatchLpar(TokenType::Data)) {
+ auto data_segment_field = std::make_unique<DataSegmentModuleField>(loc);
+ DataSegment& data_segment = data_segment_field->data_segment;
+ data_segment.memory_var = Var(module->memories.size(), GetLocation());
+ data_segment.offset.push_back(std::make_unique<ConstExpr>(
+ field->memory.page_limits.is_64 ? Const::I64(0) : Const::I32(0)));
+ data_segment.offset.back().loc = loc;
+ ParseTextListOpt(&data_segment.data);
+ EXPECT(Rpar);
+
+ uint32_t byte_size = WABT_ALIGN_UP_TO_PAGE(data_segment.data.size());
+ uint32_t page_size = WABT_BYTES_TO_PAGES(byte_size);
+ field->memory.page_limits.initial = page_size;
+ field->memory.page_limits.max = page_size;
+ field->memory.page_limits.has_max = true;
+
+ module->AppendField(std::move(field));
+ module->AppendField(std::move(data_segment_field));
+ } else {
+ CHECK_RESULT(ParseLimits(&field->memory.page_limits));
+ module->AppendField(std::move(field));
+ }
+ }
+
+ AppendInlineExportFields(module, &export_fields, module->memories.size() - 1);
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseStartModuleField(Module* module) {
+ WABT_TRACE(ParseStartModuleField);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ if (module->starts.size() > 0) {
+ Error(loc, "multiple start sections");
+ return Result::Error;
+ }
+ EXPECT(Start);
+ Var var;
+ CHECK_RESULT(ParseVar(&var));
+ EXPECT(Rpar);
+ module->AppendField(std::make_unique<StartModuleField>(var, loc));
+ return Result::Ok;
+}
+
+Result WastParser::ParseTableModuleField(Module* module) {
+ WABT_TRACE(ParseTableModuleField);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Table);
+ std::string name;
+ ParseBindVarOpt(&name);
+
+ ModuleFieldList export_fields;
+ CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Table));
+
+ if (PeekMatchLpar(TokenType::Import)) {
+ CheckImportOrdering(module);
+ auto import = std::make_unique<TableImport>(name);
+ CHECK_RESULT(ParseInlineImport(import.get()));
+ CHECK_RESULT(ParseLimits(&import->table.elem_limits));
+ CHECK_RESULT(ParseRefType(&import->table.elem_type));
+ auto field =
+ std::make_unique<ImportModuleField>(std::move(import), GetLocation());
+ module->AppendField(std::move(field));
+ } else if (PeekMatch(TokenType::ValueType)) {
+ Type elem_type;
+ CHECK_RESULT(ParseRefType(&elem_type));
+
+ EXPECT(Lpar);
+ EXPECT(Elem);
+
+ auto elem_segment_field = std::make_unique<ElemSegmentModuleField>(loc);
+ ElemSegment& elem_segment = elem_segment_field->elem_segment;
+ elem_segment.table_var = Var(module->tables.size(), GetLocation());
+ elem_segment.offset.push_back(std::make_unique<ConstExpr>(Const::I32(0)));
+ elem_segment.offset.back().loc = loc;
+ elem_segment.elem_type = elem_type;
+ // Syntax is either an optional list of var (legacy), or a non-empty list
+ // of elem expr.
+ ExprList elem_expr;
+ if (ParseElemExprOpt(&elem_expr)) {
+ elem_segment.elem_exprs.push_back(std::move(elem_expr));
+ // Parse the rest.
+ ParseElemExprListOpt(&elem_segment.elem_exprs);
+ } else {
+ ParseElemExprVarListOpt(&elem_segment.elem_exprs);
+ }
+ EXPECT(Rpar);
+
+ auto table_field = std::make_unique<TableModuleField>(loc, name);
+ table_field->table.elem_limits.initial = elem_segment.elem_exprs.size();
+ table_field->table.elem_limits.max = elem_segment.elem_exprs.size();
+ table_field->table.elem_limits.has_max = true;
+ table_field->table.elem_type = elem_type;
+ module->AppendField(std::move(table_field));
+ module->AppendField(std::move(elem_segment_field));
+ } else {
+ auto field = std::make_unique<TableModuleField>(loc, name);
+ CHECK_RESULT(ParseLimits(&field->table.elem_limits));
+ CHECK_RESULT(ParseRefType(&field->table.elem_type));
+ module->AppendField(std::move(field));
+ }
+
+ AppendInlineExportFields(module, &export_fields, module->tables.size() - 1);
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseExportDesc(Export* export_) {
+ WABT_TRACE(ParseExportDesc);
+ EXPECT(Lpar);
+ switch (Peek()) {
+ case TokenType::Func: export_->kind = ExternalKind::Func; break;
+ case TokenType::Table: export_->kind = ExternalKind::Table; break;
+ case TokenType::Memory: export_->kind = ExternalKind::Memory; break;
+ case TokenType::Global: export_->kind = ExternalKind::Global; break;
+ case TokenType::Tag: export_->kind = ExternalKind::Tag; break;
+ default:
+ return ErrorExpected({"an external kind"});
+ }
+ Consume();
+ CHECK_RESULT(ParseVar(&export_->var));
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseInlineExports(ModuleFieldList* fields,
+ ExternalKind kind) {
+ WABT_TRACE(ParseInlineExports);
+ while (PeekMatchLpar(TokenType::Export)) {
+ EXPECT(Lpar);
+ auto field = std::make_unique<ExportModuleField>(GetLocation());
+ field->export_.kind = kind;
+ EXPECT(Export);
+ CHECK_RESULT(ParseQuotedText(&field->export_.name));
+ EXPECT(Rpar);
+ fields->push_back(std::move(field));
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseInlineImport(Import* import) {
+ WABT_TRACE(ParseInlineImport);
+ EXPECT(Lpar);
+ EXPECT(Import);
+ CHECK_RESULT(ParseQuotedText(&import->module_name));
+ CHECK_RESULT(ParseQuotedText(&import->field_name));
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseTypeUseOpt(FuncDeclaration* decl) {
+ WABT_TRACE(ParseTypeUseOpt);
+ if (MatchLpar(TokenType::Type)) {
+ decl->has_func_type = true;
+ CHECK_RESULT(ParseVar(&decl->type_var));
+ EXPECT(Rpar);
+ } else {
+ decl->has_func_type = false;
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseFuncSignature(FuncSignature* sig,
+ BindingHash* param_bindings) {
+ WABT_TRACE(ParseFuncSignature);
+ CHECK_RESULT(ParseBoundValueTypeList(TokenType::Param, &sig->param_types,
+ param_bindings, &sig->param_type_names));
+ CHECK_RESULT(ParseResultList(&sig->result_types, &sig->result_type_names));
+ return Result::Ok;
+}
+
+Result WastParser::ParseUnboundFuncSignature(FuncSignature* sig) {
+ WABT_TRACE(ParseUnboundFuncSignature);
+ CHECK_RESULT(ParseUnboundValueTypeList(TokenType::Param, &sig->param_types,
+ &sig->param_type_names));
+ CHECK_RESULT(ParseResultList(&sig->result_types, &sig->result_type_names));
+ return Result::Ok;
+}
+
+Result WastParser::ParseBoundValueTypeList(
+ TokenType token,
+ TypeVector* types,
+ BindingHash* bindings,
+ std::unordered_map<uint32_t, std::string>* type_names,
+ Index binding_index_offset) {
+ WABT_TRACE(ParseBoundValueTypeList);
+ while (MatchLpar(token)) {
+ if (PeekMatch(TokenType::Var)) {
+ std::string name;
+ Var type;
+ Location loc = GetLocation();
+ ParseBindVarOpt(&name);
+ CHECK_RESULT(ParseValueType(&type));
+ bindings->emplace(name,
+ Binding(loc, binding_index_offset + types->size()));
+ if (type.is_index()) {
+ types->push_back(Type(type.index()));
+ } else {
+ assert(type.is_name());
+ assert(options_->features.function_references_enabled());
+ type_names->emplace(binding_index_offset + types->size(), type.name());
+ types->push_back(Type(Type::Reference, kInvalidIndex));
+ }
+ } else {
+ CHECK_RESULT(ParseValueTypeList(types, type_names));
+ }
+ EXPECT(Rpar);
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseUnboundValueTypeList(
+ TokenType token,
+ TypeVector* types,
+ std::unordered_map<uint32_t, std::string>* type_names) {
+ WABT_TRACE(ParseUnboundValueTypeList);
+ while (MatchLpar(token)) {
+ CHECK_RESULT(ParseValueTypeList(types, type_names));
+ EXPECT(Rpar);
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseResultList(
+ TypeVector* result_types,
+ std::unordered_map<uint32_t, std::string>* type_names) {
+ WABT_TRACE(ParseResultList);
+ return ParseUnboundValueTypeList(TokenType::Result, result_types, type_names);
+}
+
+Result WastParser::ParseInstrList(ExprList* exprs) {
+ WABT_TRACE(ParseInstrList);
+ ExprList 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 {
+ break;
+ }
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseTerminatingInstrList(ExprList* exprs) {
+ WABT_TRACE(ParseTerminatingInstrList);
+ Result result = ParseInstrList(exprs);
+ // An InstrList often has no further Lpar following it, because it would have
+ // gobbled it up. So if there is a following Lpar it is an error. If we
+ // handle it here we can produce a nicer error message.
+ CHECK_RESULT(ErrorIfLpar({"an instr"}));
+ return result;
+}
+
+Result WastParser::ParseInstr(ExprList* exprs) {
+ WABT_TRACE(ParseInstr);
+ if (IsPlainInstr(Peek())) {
+ std::unique_ptr<Expr> expr;
+ CHECK_RESULT(ParsePlainInstr(&expr));
+ exprs->push_back(std::move(expr));
+ return Result::Ok;
+ } else if (IsBlockInstr(Peek())) {
+ std::unique_ptr<Expr> expr;
+ CHECK_RESULT(ParseBlockInstr(&expr));
+ exprs->push_back(std::move(expr));
+ return Result::Ok;
+ } else if (PeekMatchExpr()) {
+ return ParseExpr(exprs);
+ } else {
+ assert(!"ParseInstr should only be called when IsInstr() is true");
+ return Result::Error;
+ }
+}
+
+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, false));
+ std::vector<uint8_t> data(data_text.begin(), data_text.end());
+ exprs->push_back(std::make_unique<CodeMetadataExpr>(name, std::move(data)));
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParsePlainInstrVar(Location loc,
+ std::unique_ptr<Expr>* out_expr) {
+ Var var;
+ CHECK_RESULT(ParseVar(&var));
+ out_expr->reset(new T(var, loc));
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParseMemoryInstrVar(Location loc,
+ std::unique_ptr<Expr>* out_expr) {
+ Var memidx;
+ Var var;
+ if (PeekMatchLpar(TokenType::Memory)) {
+ if (!options_->features.multi_memory_enabled()) {
+ Error(loc, "Specifying memory variable is not allowed");
+ return Result::Error;
+ }
+ CHECK_RESULT(ParseMemidx(loc, &memidx));
+ CHECK_RESULT(ParseVar(&var));
+ out_expr->reset(new T(var, memidx, loc));
+ } else {
+ CHECK_RESULT(ParseVar(&memidx));
+ if (ParseVarOpt(&var, Var(0, loc))) {
+ if (!options_->features.multi_memory_enabled()) {
+ Error(loc, "Specifiying memory variable is not allowed");
+ return Result::Error;
+ }
+ out_expr->reset(new T(var, memidx, loc));
+ } else {
+ out_expr->reset(new T(memidx, var, loc));
+ }
+ }
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParseLoadStoreInstr(Location loc,
+ Token token,
+ std::unique_ptr<Expr>* out_expr) {
+ Opcode opcode = token.opcode();
+ Var memidx;
+ Address offset;
+ Address align;
+ CHECK_RESULT(ParseMemidx(loc, &memidx));
+ ParseOffsetOpt(&offset);
+ ParseAlignOpt(&align);
+ out_expr->reset(new T(opcode, memidx, align, offset, loc));
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParseSIMDLoadStoreInstr(Location loc,
+ Token token,
+ std::unique_ptr<Expr>* out_expr) {
+ ErrorUnlessOpcodeEnabled(token);
+
+ Var memidx(0, loc);
+
+ if (options_->features.multi_memory_enabled()) {
+ // We have to be a little careful when reading the memeory index.
+ // If there is just a single integer folloing the instruction that
+ // represents the lane index, so we check for either a pair of intergers
+ // or an integers followed by offset= or align=.
+ bool try_read_mem_index = true;
+ if (PeekMatch(TokenType::Nat)) {
+ // The next token could be a memory index or a lane index
+ if (!PeekMatch(TokenType::OffsetEqNat, 1) &&
+ !PeekMatch(TokenType::AlignEqNat, 1) &&
+ !PeekMatch(TokenType::Nat, 1)) {
+ try_read_mem_index = false;
+ }
+ }
+ if (try_read_mem_index) {
+ CHECK_RESULT(ParseMemidx(loc, &memidx));
+ }
+ }
+ Address offset;
+ Address align;
+ ParseOffsetOpt(&offset);
+ ParseAlignOpt(&align);
+
+ uint64_t lane_idx = 0;
+ Result result = ParseSimdLane(loc, &lane_idx);
+
+ if (Failed(result)) {
+ return Result::Error;
+ }
+
+ out_expr->reset(new T(token.opcode(), memidx, align, offset, lane_idx, loc));
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParseMemoryExpr(Location loc,
+ std::unique_ptr<Expr>* out_expr) {
+ Var memidx;
+ CHECK_RESULT(ParseMemidx(loc, &memidx));
+ out_expr->reset(new T(memidx, loc));
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParseMemoryBinaryExpr(Location loc,
+ std::unique_ptr<Expr>* out_expr) {
+ Var srcmemidx;
+ Var destmemidx;
+ CHECK_RESULT(ParseMemidx(loc, &srcmemidx));
+ CHECK_RESULT(ParseMemidx(loc, &destmemidx));
+ out_expr->reset(new T(srcmemidx, destmemidx, loc));
+ return Result::Ok;
+}
+
+Result WastParser::ParseSimdLane(Location loc, uint64_t* lane_idx) {
+ if (!PeekMatch(TokenType::Nat) && !PeekMatch(TokenType::Int)) {
+ return ErrorExpected({"a natural number in range [0, 32)"});
+ }
+
+ Literal literal = Consume().literal();
+
+ Result result =
+ ParseInt64(literal.text, lane_idx, ParseIntType::UnsignedOnly);
+
+ if (Failed(result)) {
+ Error(loc, "invalid literal \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(literal.text));
+ return Result::Error;
+ }
+
+ // The valid range is only [0, 32), but it's only malformed if it can't
+ // fit in a byte.
+ if (*lane_idx > 255) {
+ Error(loc, "lane index \"" PRIstringview "\" out-of-range [0, 32)",
+ WABT_PRINTF_STRING_VIEW_ARG(literal.text));
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParsePlainInstr(std::unique_ptr<Expr>* out_expr) {
+ WABT_TRACE(ParsePlainInstr);
+ Location loc = GetLocation();
+ switch (Peek()) {
+ case TokenType::Unreachable:
+ Consume();
+ out_expr->reset(new UnreachableExpr(loc));
+ break;
+
+ case TokenType::Nop:
+ Consume();
+ out_expr->reset(new NopExpr(loc));
+ break;
+
+ case TokenType::Drop:
+ Consume();
+ out_expr->reset(new DropExpr(loc));
+ break;
+
+ case TokenType::Select: {
+ Consume();
+ TypeVector result;
+ if (options_->features.reference_types_enabled() &&
+ PeekMatchLpar(TokenType::Result)) {
+ CHECK_RESULT(ParseResultList(&result, nullptr));
+ }
+ out_expr->reset(new SelectExpr(result, loc));
+ break;
+ }
+
+ case TokenType::Br:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<BrExpr>(loc, out_expr));
+ break;
+
+ case TokenType::BrIf:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<BrIfExpr>(loc, out_expr));
+ break;
+
+ case TokenType::BrTable: {
+ Consume();
+ auto expr = std::make_unique<BrTableExpr>(loc);
+ CHECK_RESULT(ParseVarList(&expr->targets));
+ expr->default_target = expr->targets.back();
+ expr->targets.pop_back();
+ *out_expr = std::move(expr);
+ break;
+ }
+
+ case TokenType::Return:
+ Consume();
+ out_expr->reset(new ReturnExpr(loc));
+ break;
+
+ case TokenType::Call:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<CallExpr>(loc, out_expr));
+ break;
+
+ case TokenType::CallIndirect: {
+ Consume();
+ auto expr = std::make_unique<CallIndirectExpr>(loc);
+ ParseVarOpt(&expr->table, Var(0, loc));
+ CHECK_RESULT(ParseTypeUseOpt(&expr->decl));
+ CHECK_RESULT(ParseUnboundFuncSignature(&expr->decl.sig));
+ *out_expr = std::move(expr);
+ break;
+ }
+
+ case TokenType::CallRef: {
+ ErrorUnlessOpcodeEnabled(Consume());
+ out_expr->reset(new CallRefExpr(loc));
+ break;
+ }
+
+ case TokenType::ReturnCall:
+ ErrorUnlessOpcodeEnabled(Consume());
+ CHECK_RESULT(ParsePlainInstrVar<ReturnCallExpr>(loc, out_expr));
+ break;
+
+ case TokenType::ReturnCallIndirect: {
+ ErrorUnlessOpcodeEnabled(Consume());
+ auto expr = std::make_unique<ReturnCallIndirectExpr>(loc);
+ ParseVarOpt(&expr->table, Var(0, loc));
+ CHECK_RESULT(ParseTypeUseOpt(&expr->decl));
+ CHECK_RESULT(ParseUnboundFuncSignature(&expr->decl.sig));
+ *out_expr = std::move(expr);
+ break;
+ }
+
+ case TokenType::LocalGet:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<LocalGetExpr>(loc, out_expr));
+ break;
+
+ case TokenType::LocalSet:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<LocalSetExpr>(loc, out_expr));
+ break;
+
+ case TokenType::LocalTee:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<LocalTeeExpr>(loc, out_expr));
+ break;
+
+ case TokenType::GlobalGet:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<GlobalGetExpr>(loc, out_expr));
+ break;
+
+ case TokenType::GlobalSet:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<GlobalSetExpr>(loc, out_expr));
+ break;
+
+ case TokenType::Load:
+ CHECK_RESULT(ParseLoadStoreInstr<LoadExpr>(loc, Consume(), out_expr));
+ break;
+
+ case TokenType::Store:
+ CHECK_RESULT(ParseLoadStoreInstr<StoreExpr>(loc, Consume(), out_expr));
+ break;
+
+ case TokenType::Const: {
+ Const const_;
+ CHECK_RESULT(ParseConst(&const_, ConstType::Normal));
+ out_expr->reset(new ConstExpr(const_, loc));
+ break;
+ }
+
+ case TokenType::Unary: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ out_expr->reset(new UnaryExpr(token.opcode(), loc));
+ break;
+ }
+
+ case TokenType::Binary: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ out_expr->reset(new BinaryExpr(token.opcode(), loc));
+ break;
+ }
+
+ case TokenType::Compare:
+ out_expr->reset(new CompareExpr(Consume().opcode(), loc));
+ break;
+
+ case TokenType::Convert: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ out_expr->reset(new ConvertExpr(token.opcode(), loc));
+ break;
+ }
+
+ case TokenType::MemoryCopy:
+ ErrorUnlessOpcodeEnabled(Consume());
+ CHECK_RESULT(ParseMemoryBinaryExpr<MemoryCopyExpr>(loc, out_expr));
+ break;
+
+ case TokenType::MemoryFill:
+ ErrorUnlessOpcodeEnabled(Consume());
+ CHECK_RESULT(ParseMemoryExpr<MemoryFillExpr>(loc, out_expr));
+ break;
+
+ case TokenType::DataDrop:
+ ErrorUnlessOpcodeEnabled(Consume());
+ CHECK_RESULT(ParsePlainInstrVar<DataDropExpr>(loc, out_expr));
+ break;
+
+ case TokenType::MemoryInit:
+ ErrorUnlessOpcodeEnabled(Consume());
+ CHECK_RESULT(ParseMemoryInstrVar<MemoryInitExpr>(loc, out_expr));
+ break;
+
+ case TokenType::MemorySize:
+ Consume();
+ CHECK_RESULT(ParseMemoryExpr<MemorySizeExpr>(loc, out_expr));
+ break;
+
+ case TokenType::MemoryGrow:
+ Consume();
+ CHECK_RESULT(ParseMemoryExpr<MemoryGrowExpr>(loc, out_expr));
+ break;
+
+ case TokenType::TableCopy: {
+ ErrorUnlessOpcodeEnabled(Consume());
+ Var dst(0, loc);
+ Var src(0, loc);
+ if (options_->features.reference_types_enabled()) {
+ ParseVarOpt(&dst, dst);
+ ParseVarOpt(&src, src);
+ }
+ out_expr->reset(new TableCopyExpr(dst, src, loc));
+ break;
+ }
+
+ case TokenType::ElemDrop:
+ ErrorUnlessOpcodeEnabled(Consume());
+ CHECK_RESULT(ParsePlainInstrVar<ElemDropExpr>(loc, out_expr));
+ break;
+
+ case TokenType::TableInit: {
+ ErrorUnlessOpcodeEnabled(Consume());
+ Var segment_index(0, loc);
+ CHECK_RESULT(ParseVar(&segment_index));
+ Var table_index(0, loc);
+ if (ParseVarOpt(&table_index, table_index)) {
+ // Here are the two forms:
+ //
+ // table.init $elemidx ...
+ // table.init $tableidx $elemidx ...
+ //
+ // So if both indexes are provided, we need to swap them.
+ std::swap(segment_index, table_index);
+ }
+ out_expr->reset(new TableInitExpr(segment_index, table_index, loc));
+ break;
+ }
+
+ case TokenType::TableGet: {
+ ErrorUnlessOpcodeEnabled(Consume());
+ Var table_index(0, loc);
+ ParseVarOpt(&table_index, table_index);
+ out_expr->reset(new TableGetExpr(table_index, loc));
+ break;
+ }
+
+ case TokenType::TableSet: {
+ ErrorUnlessOpcodeEnabled(Consume());
+ Var table_index(0, loc);
+ ParseVarOpt(&table_index, table_index);
+ out_expr->reset(new TableSetExpr(table_index, loc));
+ break;
+ }
+
+ case TokenType::TableGrow: {
+ ErrorUnlessOpcodeEnabled(Consume());
+ Var table_index(0, loc);
+ ParseVarOpt(&table_index, table_index);
+ out_expr->reset(new TableGrowExpr(table_index, loc));
+ break;
+ }
+
+ case TokenType::TableSize: {
+ ErrorUnlessOpcodeEnabled(Consume());
+ Var table_index(0, loc);
+ ParseVarOpt(&table_index, table_index);
+ out_expr->reset(new TableSizeExpr(table_index, loc));
+ break;
+ }
+
+ case TokenType::TableFill: {
+ ErrorUnlessOpcodeEnabled(Consume());
+ Var table_index(0, loc);
+ ParseVarOpt(&table_index, table_index);
+ out_expr->reset(new TableFillExpr(table_index, loc));
+ break;
+ }
+
+ case TokenType::RefFunc:
+ ErrorUnlessOpcodeEnabled(Consume());
+ CHECK_RESULT(ParsePlainInstrVar<RefFuncExpr>(loc, out_expr));
+ break;
+
+ case TokenType::RefNull: {
+ ErrorUnlessOpcodeEnabled(Consume());
+ Type type;
+ CHECK_RESULT(ParseRefKind(&type));
+ out_expr->reset(new RefNullExpr(type, loc));
+ break;
+ }
+
+ case TokenType::RefIsNull:
+ ErrorUnlessOpcodeEnabled(Consume());
+ out_expr->reset(new RefIsNullExpr(loc));
+ break;
+
+ case TokenType::Throw:
+ ErrorUnlessOpcodeEnabled(Consume());
+ CHECK_RESULT(ParsePlainInstrVar<ThrowExpr>(loc, out_expr));
+ break;
+
+ case TokenType::Rethrow:
+ ErrorUnlessOpcodeEnabled(Consume());
+ CHECK_RESULT(ParsePlainInstrVar<RethrowExpr>(loc, out_expr));
+ break;
+
+ case TokenType::AtomicNotify: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ CHECK_RESULT(ParseLoadStoreInstr<AtomicNotifyExpr>(loc, token, out_expr));
+ break;
+ }
+
+ case TokenType::AtomicFence: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ uint32_t consistency_model = 0x0;
+ out_expr->reset(new AtomicFenceExpr(consistency_model, loc));
+ break;
+ }
+
+ case TokenType::AtomicWait: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ CHECK_RESULT(ParseLoadStoreInstr<AtomicWaitExpr>(loc, token, out_expr));
+ break;
+ }
+
+ case TokenType::AtomicLoad: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ CHECK_RESULT(ParseLoadStoreInstr<AtomicLoadExpr>(loc, token, out_expr));
+ break;
+ }
+
+ case TokenType::AtomicStore: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ CHECK_RESULT(ParseLoadStoreInstr<AtomicStoreExpr>(loc, token, out_expr));
+ break;
+ }
+
+ case TokenType::AtomicRmw: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ CHECK_RESULT(ParseLoadStoreInstr<AtomicRmwExpr>(loc, token, out_expr));
+ break;
+ }
+
+ case TokenType::AtomicRmwCmpxchg: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ CHECK_RESULT(
+ ParseLoadStoreInstr<AtomicRmwCmpxchgExpr>(loc, token, out_expr));
+ break;
+ }
+
+ case TokenType::Ternary: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ out_expr->reset(new TernaryExpr(token.opcode(), loc));
+ break;
+ }
+
+ case TokenType::SimdLaneOp: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+
+ uint64_t lane_idx = 0;
+ Result result = ParseSimdLane(loc, &lane_idx);
+
+ if (Failed(result)) {
+ return Result::Error;
+ }
+
+ out_expr->reset(new SimdLaneOpExpr(token.opcode(), lane_idx, loc));
+ break;
+ }
+
+ case TokenType::SimdLoadLane: {
+ CHECK_RESULT(
+ ParseSIMDLoadStoreInstr<SimdLoadLaneExpr>(loc, Consume(), out_expr));
+ break;
+ }
+
+ case TokenType::SimdStoreLane: {
+ CHECK_RESULT(
+ ParseSIMDLoadStoreInstr<SimdStoreLaneExpr>(loc, Consume(), out_expr));
+ break;
+ }
+
+ case TokenType::SimdShuffleOp: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ v128 values;
+ for (int lane = 0; lane < 16; ++lane) {
+ Location loc = GetLocation();
+ uint64_t lane_idx;
+ Result result = ParseSimdLane(loc, &lane_idx);
+ if (Failed(result)) {
+ return Result::Error;
+ }
+
+ values.set_u8(lane, static_cast<uint8_t>(lane_idx));
+ }
+
+ out_expr->reset(new SimdShuffleOpExpr(token.opcode(), values, loc));
+ break;
+ }
+
+ default:
+ assert(
+ !"ParsePlainInstr should only be called when IsPlainInstr() is true");
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseSimdV128Const(Const* const_,
+ TokenType token_type,
+ ConstType const_type) {
+ WABT_TRACE(ParseSimdV128Const);
+
+ uint8_t lane_count = 0;
+ bool integer = true;
+ switch (token_type) {
+ case TokenType::I8X16: { lane_count = 16; break; }
+ case TokenType::I16X8: { lane_count = 8; break; }
+ case TokenType::I32X4: { lane_count = 4; break; }
+ case TokenType::I64X2: { lane_count = 2; break; }
+ case TokenType::F32X4: { lane_count = 4; integer = false; break; }
+ case TokenType::F64X2: { lane_count = 2; integer = false; break; }
+ default: {
+ Error(const_->loc,
+ "Unexpected type at start of simd constant. "
+ "Expected one of: i8x16, i16x8, i32x4, i64x2, f32x4, f64x2. "
+ "Found \"%s\".",
+ GetTokenTypeName(token_type));
+ return Result::Error;
+ }
+ }
+ Consume();
+
+ const_->loc = GetLocation();
+
+ for (int lane = 0; lane < lane_count; ++lane) {
+ Location loc = GetLocation();
+
+ // Check that the lane literal type matches the element type of the v128:
+ Token token = GetToken();
+ switch (token.token_type()) {
+ case TokenType::Nat:
+ case TokenType::Int:
+ // OK.
+ break;
+
+ case TokenType::Float:
+ case TokenType::NanArithmetic:
+ case TokenType::NanCanonical:
+ if (integer) {
+ goto error;
+ }
+ break;
+
+ error:
+ default:
+ if (integer) {
+ return ErrorExpected({"a Nat or Integer literal"}, "123");
+ } else {
+ return ErrorExpected({"a Float literal"}, "42.0");
+ }
+ }
+
+ Result result;
+
+ // For each type, parse the next literal, bound check it, and write it to
+ // the array of bytes:
+ if (integer) {
+ std::string_view sv = Consume().literal().text;
+
+ switch (lane_count) {
+ case 16: {
+ uint8_t value = 0;
+ result = ParseInt8(sv, &value, ParseIntType::SignedAndUnsigned);
+ const_->set_v128_u8(lane, value);
+ break;
+ }
+ case 8: {
+ uint16_t value = 0;
+ result = ParseInt16(sv, &value, ParseIntType::SignedAndUnsigned);
+ const_->set_v128_u16(lane, value);
+ break;
+ }
+ case 4: {
+ uint32_t value = 0;
+ result = ParseInt32(sv, &value, ParseIntType::SignedAndUnsigned);
+ const_->set_v128_u32(lane, value);
+ break;
+ }
+ case 2: {
+ uint64_t value = 0;
+ result = ParseInt64(sv, &value, ParseIntType::SignedAndUnsigned);
+ const_->set_v128_u64(lane, value);
+ break;
+ }
+ }
+ } else {
+ Const lane_const_;
+ switch (lane_count) {
+ case 4:
+ result = ParseF32(&lane_const_, const_type);
+ const_->set_v128_f32(lane, lane_const_.f32_bits());
+ break;
+
+ case 2:
+ result = ParseF64(&lane_const_, const_type);
+ const_->set_v128_f64(lane, lane_const_.f64_bits());
+ break;
+ }
+
+ const_->set_expected_nan(lane, lane_const_.expected_nan());
+ }
+
+ if (Failed(result)) {
+ Error(loc, "invalid literal \"%s\"", token.to_string().c_str());
+ return Result::Error;
+ }
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseExpectedNan(ExpectedNan* expected) {
+ WABT_TRACE(ParseExpectedNan);
+ TokenType token_type = Peek();
+ switch (token_type) {
+ case TokenType::NanArithmetic:
+ *expected = ExpectedNan::Arithmetic;
+ break;
+ case TokenType::NanCanonical:
+ *expected = ExpectedNan::Canonical;
+ break;
+ default:
+ return Result::Error;
+ }
+ Consume();
+ return Result::Ok;
+}
+
+Result WastParser::ParseF32(Const* const_, ConstType const_type) {
+ ExpectedNan expected;
+ if (const_type == ConstType::Expectation &&
+ Succeeded(ParseExpectedNan(&expected))) {
+ const_->set_f32(expected);
+ return Result::Ok;
+ }
+
+ auto token = Consume();
+ if (!token.HasLiteral()) {
+ return Result::Error;
+ }
+
+ auto literal = token.literal();
+ uint32_t f32_bits;
+ Result result = ParseFloat(literal.type, literal.text, &f32_bits);
+ const_->set_f32(f32_bits);
+ return result;
+}
+
+Result WastParser::ParseF64(Const* const_, ConstType const_type) {
+ ExpectedNan expected;
+ if (const_type == ConstType::Expectation &&
+ Succeeded(ParseExpectedNan(&expected))) {
+ const_->set_f64(expected);
+ return Result::Ok;
+ }
+
+ auto token = Consume();
+ if (!token.HasLiteral()) {
+ return Result::Error;
+ }
+
+ auto literal = token.literal();
+ uint64_t f64_bits;
+ Result result = ParseDouble(literal.type, literal.text, &f64_bits);
+ const_->set_f64(f64_bits);
+ return result;
+}
+
+Result WastParser::ParseConst(Const* const_, ConstType const_type) {
+ WABT_TRACE(ParseConst);
+ Token opcode_token = Consume();
+ Opcode opcode = opcode_token.opcode();
+ const_->loc = GetLocation();
+ Token token = GetToken();
+
+ // V128 is fully handled by ParseSimdV128Const:
+ if (opcode != Opcode::V128Const) {
+ switch (token.token_type()) {
+ case TokenType::Nat:
+ case TokenType::Int:
+ case TokenType::Float:
+ // OK.
+ break;
+ case TokenType::NanArithmetic:
+ case TokenType::NanCanonical:
+ break;
+ default:
+ return ErrorExpected({"a numeric literal"}, "123, -45, 6.7e8");
+ }
+ }
+
+ Result result;
+ switch (opcode) {
+ case Opcode::I32Const: {
+ auto token = Consume();
+ if (!token.HasLiteral()) {
+ return Result::Error;
+ }
+ auto sv = token.literal().text;
+ uint32_t u32;
+ result = ParseInt32(sv, &u32, ParseIntType::SignedAndUnsigned);
+ const_->set_u32(u32);
+ break;
+ }
+
+ case Opcode::I64Const: {
+ auto token = Consume();
+ if (!token.HasLiteral()) {
+ return Result::Error;
+ }
+ auto sv = token.literal().text;
+ uint64_t u64;
+ result = ParseInt64(sv, &u64, ParseIntType::SignedAndUnsigned);
+ const_->set_u64(u64);
+ break;
+ }
+
+ case Opcode::F32Const:
+ result = ParseF32(const_, const_type);
+ break;
+
+ case Opcode::F64Const:
+ result = ParseF64(const_, const_type);
+ break;
+
+ case Opcode::V128Const:
+ ErrorUnlessOpcodeEnabled(opcode_token);
+ // Parse V128 Simd Const (16 bytes).
+ result = ParseSimdV128Const(const_, token.token_type(), const_type);
+ // ParseSimdV128Const report error already, just return here if parser get
+ // errors.
+ if (Failed(result)) {
+ return Result::Error;
+ }
+ break;
+
+ default:
+ assert(!"ParseConst called with invalid opcode");
+ return Result::Error;
+ }
+
+ if (Failed(result)) {
+ Error(const_->loc, "invalid literal \"%s\"", token.to_string().c_str());
+ // Return if parser get errors.
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseExternref(Const* const_) {
+ WABT_TRACE(ParseExternref);
+ Token token = Consume();
+ if (!options_->features.reference_types_enabled()) {
+ Error(token.loc, "externref not allowed");
+ return Result::Error;
+ }
+
+ Literal literal;
+ std::string_view sv;
+ const_->loc = GetLocation();
+ TokenType token_type = Peek();
+
+ switch (token_type) {
+ case TokenType::Nat:
+ case TokenType::Int: {
+ literal = Consume().literal();
+ sv = literal.text;
+ break;
+ }
+ default:
+ return ErrorExpected({"a numeric literal"}, "123");
+ }
+
+ uint64_t ref_bits;
+ Result result = ParseInt64(sv, &ref_bits, ParseIntType::UnsignedOnly);
+
+ const_->set_externref(static_cast<uintptr_t>(ref_bits));
+
+ if (Failed(result)) {
+ Error(const_->loc, "invalid literal \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(literal.text));
+ // Return if parser get errors.
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseConstList(ConstVector* consts, ConstType type) {
+ WABT_TRACE(ParseConstList);
+ while (PeekMatchLpar(TokenType::Const) || PeekMatchLpar(TokenType::RefNull) ||
+ PeekMatchLpar(TokenType::RefExtern) ||
+ PeekMatchLpar(TokenType::RefFunc)) {
+ Consume();
+ Const const_;
+ switch (Peek()) {
+ case TokenType::Const:
+ CHECK_RESULT(ParseConst(&const_, type));
+ break;
+ case TokenType::RefNull: {
+ auto token = Consume();
+ Type type;
+ CHECK_RESULT(ParseRefKind(&type));
+ ErrorUnlessOpcodeEnabled(token);
+ const_.loc = GetLocation();
+ const_.set_null(type);
+ break;
+ }
+ case TokenType::RefFunc: {
+ auto token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ const_.loc = GetLocation();
+ const_.set_funcref();
+ break;
+ }
+ case TokenType::RefExtern:
+ CHECK_RESULT(ParseExternref(&const_));
+ break;
+ default:
+ assert(!"unreachable");
+ return Result::Error;
+ }
+ EXPECT(Rpar);
+ consts->push_back(const_);
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseBlockInstr(std::unique_ptr<Expr>* out_expr) {
+ WABT_TRACE(ParseBlockInstr);
+ Location loc = GetLocation();
+
+ switch (Peek()) {
+ case TokenType::Block: {
+ Consume();
+ auto expr = std::make_unique<BlockExpr>(loc);
+ CHECK_RESULT(ParseLabelOpt(&expr->block.label));
+ CHECK_RESULT(ParseBlock(&expr->block));
+ EXPECT(End);
+ CHECK_RESULT(ParseEndLabelOpt(expr->block.label));
+ *out_expr = std::move(expr);
+ break;
+ }
+
+ case TokenType::Loop: {
+ Consume();
+ auto expr = std::make_unique<LoopExpr>(loc);
+ CHECK_RESULT(ParseLabelOpt(&expr->block.label));
+ CHECK_RESULT(ParseBlock(&expr->block));
+ EXPECT(End);
+ CHECK_RESULT(ParseEndLabelOpt(expr->block.label));
+ *out_expr = std::move(expr);
+ break;
+ }
+
+ case TokenType::If: {
+ Consume();
+ auto expr = std::make_unique<IfExpr>(loc);
+ CHECK_RESULT(ParseLabelOpt(&expr->true_.label));
+ CHECK_RESULT(ParseBlock(&expr->true_));
+ if (Match(TokenType::Else)) {
+ CHECK_RESULT(ParseEndLabelOpt(expr->true_.label));
+ CHECK_RESULT(ParseTerminatingInstrList(&expr->false_));
+ expr->false_end_loc = GetLocation();
+ }
+ EXPECT(End);
+ CHECK_RESULT(ParseEndLabelOpt(expr->true_.label));
+ *out_expr = std::move(expr);
+ break;
+ }
+
+ case TokenType::Try: {
+ ErrorUnlessOpcodeEnabled(Consume());
+ auto expr = std::make_unique<TryExpr>(loc);
+ CatchVector catches;
+ CHECK_RESULT(ParseLabelOpt(&expr->block.label));
+ CHECK_RESULT(ParseBlock(&expr->block));
+ if (IsCatch(Peek())) {
+ CHECK_RESULT(ParseCatchInstrList(&expr->catches));
+ expr->kind = TryKind::Catch;
+ } else if (PeekMatch(TokenType::Delegate)) {
+ Consume();
+ Var var;
+ CHECK_RESULT(ParseVar(&var));
+ expr->delegate_target = var;
+ expr->kind = TryKind::Delegate;
+ }
+ CHECK_RESULT(ErrorIfLpar({"a valid try clause"}));
+ expr->block.end_loc = GetLocation();
+ if (expr->kind != TryKind::Delegate) {
+ EXPECT(End);
+ }
+ CHECK_RESULT(ParseEndLabelOpt(expr->block.label));
+ *out_expr = std::move(expr);
+ break;
+ }
+
+ default:
+ assert(
+ !"ParseBlockInstr should only be called when IsBlockInstr() is true");
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseLabelOpt(std::string* out_label) {
+ WABT_TRACE(ParseLabelOpt);
+ if (PeekMatch(TokenType::Var)) {
+ *out_label = std::string(Consume().text());
+ } else {
+ out_label->clear();
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseEndLabelOpt(const std::string& begin_label) {
+ WABT_TRACE(ParseEndLabelOpt);
+ Location loc = GetLocation();
+ std::string end_label;
+ CHECK_RESULT(ParseLabelOpt(&end_label));
+ if (!end_label.empty()) {
+ if (begin_label.empty()) {
+ Error(loc, "unexpected label \"%s\"", end_label.c_str());
+ } else if (begin_label != end_label) {
+ Error(loc, "mismatching label \"%s\" != \"%s\"", begin_label.c_str(),
+ end_label.c_str());
+ }
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseBlockDeclaration(BlockDeclaration* decl) {
+ WABT_TRACE(ParseBlockDeclaration);
+ FuncDeclaration func_decl;
+ CHECK_RESULT(ParseTypeUseOpt(&func_decl));
+ CHECK_RESULT(ParseUnboundFuncSignature(&func_decl.sig));
+ decl->has_func_type = func_decl.has_func_type;
+ decl->type_var = func_decl.type_var;
+ decl->sig = func_decl.sig;
+ return Result::Ok;
+}
+
+Result WastParser::ParseBlock(Block* block) {
+ WABT_TRACE(ParseBlock);
+ CHECK_RESULT(ParseBlockDeclaration(&block->decl));
+ CHECK_RESULT(ParseInstrList(&block->exprs));
+ block->end_loc = GetLocation();
+ return Result::Ok;
+}
+
+Result WastParser::ParseExprList(ExprList* exprs) {
+ WABT_TRACE(ParseExprList);
+ ExprList new_exprs;
+ while (PeekMatchExpr()) {
+ if (Succeeded(ParseExpr(&new_exprs))) {
+ exprs->splice(exprs->end(), new_exprs);
+ } else {
+ CHECK_RESULT(Synchronize(IsExpr));
+ }
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseExpr(ExprList* exprs) {
+ WABT_TRACE(ParseExpr);
+ if (!PeekMatch(TokenType::Lpar)) {
+ return Result::Error;
+ }
+
+ if (IsPlainInstr(Peek(1))) {
+ Consume();
+ std::unique_ptr<Expr> expr;
+ CHECK_RESULT(ParsePlainInstr(&expr));
+ CHECK_RESULT(ParseExprList(exprs));
+ CHECK_RESULT(ErrorIfLpar({"an expr"}));
+ exprs->push_back(std::move(expr));
+ } else {
+ Location loc = GetLocation();
+
+ switch (Peek(1)) {
+ case TokenType::Block: {
+ Consume();
+ Consume();
+ auto expr = std::make_unique<BlockExpr>(loc);
+ CHECK_RESULT(ParseLabelOpt(&expr->block.label));
+ CHECK_RESULT(ParseBlock(&expr->block));
+ exprs->push_back(std::move(expr));
+ break;
+ }
+
+ case TokenType::Loop: {
+ Consume();
+ Consume();
+ auto expr = std::make_unique<LoopExpr>(loc);
+ CHECK_RESULT(ParseLabelOpt(&expr->block.label));
+ CHECK_RESULT(ParseBlock(&expr->block));
+ exprs->push_back(std::move(expr));
+ break;
+ }
+
+ case TokenType::If: {
+ Consume();
+ Consume();
+ auto expr = std::make_unique<IfExpr>(loc);
+
+ CHECK_RESULT(ParseLabelOpt(&expr->true_.label));
+ CHECK_RESULT(ParseBlockDeclaration(&expr->true_.decl));
+
+ if (PeekMatchExpr()) {
+ ExprList cond;
+ CHECK_RESULT(ParseExpr(&cond));
+ exprs->splice(exprs->end(), cond);
+ }
+
+ if (MatchLpar(TokenType::Then)) {
+ CHECK_RESULT(ParseTerminatingInstrList(&expr->true_.exprs));
+ expr->true_.end_loc = GetLocation();
+ EXPECT(Rpar);
+
+ if (MatchLpar(TokenType::Else)) {
+ CHECK_RESULT(ParseTerminatingInstrList(&expr->false_));
+ EXPECT(Rpar);
+ } else if (PeekMatchExpr()) {
+ CHECK_RESULT(ParseExpr(&expr->false_));
+ }
+ expr->false_end_loc = GetLocation();
+ } else if (PeekMatchExpr()) {
+ CHECK_RESULT(ParseExpr(&expr->true_.exprs));
+ expr->true_.end_loc = GetLocation();
+ if (PeekMatchExpr()) {
+ CHECK_RESULT(ParseExpr(&expr->false_));
+ expr->false_end_loc = GetLocation();
+ }
+ } else {
+ ConsumeIfLpar();
+ return ErrorExpected({"then block"}, "(then ...)");
+ }
+
+ exprs->push_back(std::move(expr));
+ break;
+ }
+
+ case TokenType::Try: {
+ Consume();
+ ErrorUnlessOpcodeEnabled(Consume());
+
+ auto expr = std::make_unique<TryExpr>(loc);
+ CHECK_RESULT(ParseLabelOpt(&expr->block.label));
+ CHECK_RESULT(ParseBlockDeclaration(&expr->block.decl));
+ EXPECT(Lpar);
+ EXPECT(Do);
+ CHECK_RESULT(ParseInstrList(&expr->block.exprs));
+ EXPECT(Rpar);
+ if (PeekMatch(TokenType::Lpar)) {
+ Consume();
+ TokenType type = Peek();
+ switch (type) {
+ case TokenType::Catch:
+ case TokenType::CatchAll:
+ CHECK_RESULT(ParseCatchExprList(&expr->catches));
+ expr->kind = TryKind::Catch;
+ break;
+ case TokenType::Delegate: {
+ Consume();
+ Var var;
+ CHECK_RESULT(ParseVar(&var));
+ expr->delegate_target = var;
+ expr->kind = TryKind::Delegate;
+ EXPECT(Rpar);
+ break;
+ }
+ default:
+ ErrorExpected({"catch", "catch_all", "delegate"});
+ break;
+ }
+ }
+ CHECK_RESULT(ErrorIfLpar({"a valid try clause"}));
+ expr->block.end_loc = GetLocation();
+ exprs->push_back(std::move(expr));
+ break;
+ }
+
+ default:
+ assert(!"ParseExpr should only be called when IsExpr() is true");
+ return Result::Error;
+ }
+ }
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseCatchInstrList(CatchVector* catches) {
+ WABT_TRACE(ParseCatchInstrList);
+ bool parsedCatch = false;
+ bool parsedCatchAll = false;
+
+ while (IsCatch(Peek())) {
+ Catch catch_(GetLocation());
+
+ auto token = Consume();
+ if (token.token_type() == TokenType::Catch) {
+ CHECK_RESULT(ParseVar(&catch_.var));
+ } else {
+ if (parsedCatchAll) {
+ Error(token.loc, "multiple catch_all clauses not allowed");
+ return Result::Error;
+ }
+ parsedCatchAll = true;
+ }
+
+ CHECK_RESULT(ParseInstrList(&catch_.exprs));
+ catches->push_back(std::move(catch_));
+ parsedCatch = true;
+ }
+
+ if (!parsedCatch) {
+ return ErrorExpected({"catch"});
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseCatchExprList(CatchVector* catches) {
+ WABT_TRACE(ParseCatchExprList);
+ bool parsedCatchAll = false;
+
+ do {
+ Catch catch_(GetLocation());
+
+ auto token = Consume();
+ if (token.token_type() == TokenType::Catch) {
+ CHECK_RESULT(ParseVar(&catch_.var));
+ } else {
+ if (parsedCatchAll) {
+ Error(token.loc, "multiple catch_all clauses not allowed");
+ return Result::Error;
+ }
+ parsedCatchAll = true;
+ }
+
+ CHECK_RESULT(ParseTerminatingInstrList(&catch_.exprs));
+ EXPECT(Rpar);
+ catches->push_back(std::move(catch_));
+ } while (Match(TokenType::Lpar) && IsCatch(Peek()));
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseGlobalType(Global* global) {
+ WABT_TRACE(ParseGlobalType);
+ if (MatchLpar(TokenType::Mut)) {
+ global->mutable_ = true;
+ Var type;
+ CHECK_RESULT(ParseValueType(&type));
+ global->type = Type(type.index());
+ CHECK_RESULT(ErrorIfLpar({"i32", "i64", "f32", "f64"}));
+ EXPECT(Rpar);
+ } else {
+ Var type;
+ CHECK_RESULT(ParseValueType(&type));
+ global->type = Type(type.index());
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseCommandList(Script* script,
+ CommandPtrVector* commands) {
+ WABT_TRACE(ParseCommandList);
+ while (IsCommand(PeekPair())) {
+ CommandPtr command;
+ if (Succeeded(ParseCommand(script, &command))) {
+ commands->push_back(std::move(command));
+ } else {
+ CHECK_RESULT(Synchronize(IsCommand));
+ }
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseCommand(Script* script, CommandPtr* out_command) {
+ WABT_TRACE(ParseCommand);
+ switch (Peek(1)) {
+ case TokenType::AssertException:
+ return ParseAssertExceptionCommand(out_command);
+
+ case TokenType::AssertExhaustion:
+ return ParseAssertExhaustionCommand(out_command);
+
+ case TokenType::AssertInvalid:
+ return ParseAssertInvalidCommand(out_command);
+
+ case TokenType::AssertMalformed:
+ return ParseAssertMalformedCommand(out_command);
+
+ case TokenType::AssertReturn:
+ return ParseAssertReturnCommand(out_command);
+
+ case TokenType::AssertTrap:
+ return ParseAssertTrapCommand(out_command);
+
+ case TokenType::AssertUnlinkable:
+ return ParseAssertUnlinkableCommand(out_command);
+
+ case TokenType::Get:
+ case TokenType::Invoke:
+ return ParseActionCommand(out_command);
+
+ case TokenType::Module:
+ return ParseModuleCommand(script, out_command);
+
+ case TokenType::Register:
+ return ParseRegisterCommand(out_command);
+
+ case TokenType::Input:
+ return ParseInputCommand(out_command);
+
+ case TokenType::Output:
+ return ParseOutputCommand(out_command);
+
+ default:
+ assert(!"ParseCommand should only be called when IsCommand() is true");
+ return Result::Error;
+ }
+}
+
+Result WastParser::ParseAssertExceptionCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertExceptionCommand);
+ return ParseAssertActionCommand<AssertExceptionCommand>(
+ TokenType::AssertException, out_command);
+}
+
+Result WastParser::ParseAssertExhaustionCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertExhaustionCommand);
+ return ParseAssertActionTextCommand<AssertExhaustionCommand>(
+ TokenType::AssertExhaustion, out_command);
+}
+
+Result WastParser::ParseAssertInvalidCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertInvalidCommand);
+ return ParseAssertScriptModuleCommand<AssertInvalidCommand>(
+ TokenType::AssertInvalid, out_command);
+}
+
+Result WastParser::ParseAssertMalformedCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertMalformedCommand);
+ return ParseAssertScriptModuleCommand<AssertMalformedCommand>(
+ TokenType::AssertMalformed, out_command);
+}
+
+Result WastParser::ParseAssertReturnCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertReturnCommand);
+ EXPECT(Lpar);
+ EXPECT(AssertReturn);
+ auto command = std::make_unique<AssertReturnCommand>();
+ CHECK_RESULT(ParseAction(&command->action));
+ CHECK_RESULT(ParseExpectedValues(&command->expected));
+ EXPECT(Rpar);
+ *out_command = std::move(command);
+ return Result::Ok;
+}
+
+Result WastParser::ParseAssertTrapCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertTrapCommand);
+ EXPECT(Lpar);
+ EXPECT(AssertTrap);
+ if (PeekMatchLpar(TokenType::Module)) {
+ auto command = std::make_unique<AssertUninstantiableCommand>();
+ CHECK_RESULT(ParseScriptModule(&command->module));
+ CHECK_RESULT(ParseQuotedText(&command->text));
+ *out_command = std::move(command);
+ } else {
+ auto command = std::make_unique<AssertTrapCommand>();
+ CHECK_RESULT(ParseAction(&command->action));
+ CHECK_RESULT(ParseQuotedText(&command->text));
+ *out_command = std::move(command);
+ }
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseAssertUnlinkableCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertUnlinkableCommand);
+ return ParseAssertScriptModuleCommand<AssertUnlinkableCommand>(
+ TokenType::AssertUnlinkable, out_command);
+}
+
+Result WastParser::ParseActionCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseActionCommand);
+ auto command = std::make_unique<ActionCommand>();
+ CHECK_RESULT(ParseAction(&command->action));
+ *out_command = std::move(command);
+ return Result::Ok;
+}
+
+Result WastParser::ParseModuleCommand(Script* script, CommandPtr* out_command) {
+ WABT_TRACE(ParseModuleCommand);
+ std::unique_ptr<ScriptModule> script_module;
+ CHECK_RESULT(ParseScriptModule(&script_module));
+
+ Module* module = nullptr;
+
+ switch (script_module->type()) {
+ case ScriptModuleType::Text: {
+ auto command = std::make_unique<ModuleCommand>();
+ module = &command->module;
+ *module = std::move(cast<TextScriptModule>(script_module.get())->module);
+ *out_command = std::move(command);
+ break;
+ }
+
+ case ScriptModuleType::Binary: {
+ auto command = std::make_unique<ScriptModuleCommand>();
+ module = &command->module;
+ auto* bsm = cast<BinaryScriptModule>(script_module.get());
+ ReadBinaryOptions options;
+#if WABT_TRACING
+ auto log_stream = FileStream::CreateStdout();
+ options.log_stream = log_stream.get();
+#endif
+ options.features = options_->features;
+ Errors errors;
+ const char* filename = "<text>";
+ ReadBinaryIr(filename, bsm->data.data(), bsm->data.size(), options,
+ &errors, module);
+ module->name = bsm->name;
+ module->loc = bsm->loc;
+ for (const auto& error : errors) {
+ assert(error.error_level == ErrorLevel::Error);
+ if (error.loc.offset == kInvalidOffset) {
+ Error(bsm->loc, "error in binary module: %s", error.message.c_str());
+ } else {
+ Error(bsm->loc, "error in binary module: @0x%08" PRIzx ": %s",
+ error.loc.offset, error.message.c_str());
+ }
+ }
+
+ command->script_module = std::move(script_module);
+ *out_command = std::move(command);
+ break;
+ }
+
+ case ScriptModuleType::Quoted:
+ return ErrorExpected({"a binary module", "a text module"});
+ }
+
+ // script is nullptr when ParseModuleCommand is called from ParseModule.
+ if (script) {
+ Index command_index = script->commands.size();
+
+ if (!module->name.empty()) {
+ script->module_bindings.emplace(module->name,
+ Binding(module->loc, command_index));
+ }
+
+ last_module_index_ = command_index;
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseRegisterCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseRegisterCommand);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Register);
+ std::string text;
+ Var var;
+ CHECK_RESULT(ParseQuotedText(&text));
+ ParseVarOpt(&var, Var(last_module_index_, loc));
+ EXPECT(Rpar);
+ out_command->reset(new RegisterCommand(text, var));
+ return Result::Ok;
+}
+
+Result WastParser::ParseInputCommand(CommandPtr*) {
+ // Parse the input command, but always fail since this command is not
+ // actually supported.
+ WABT_TRACE(ParseInputCommand);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Input);
+ Error(loc, "input command is not supported");
+ Var var;
+ std::string text;
+ ParseVarOpt(&var);
+ CHECK_RESULT(ParseQuotedText(&text));
+ EXPECT(Rpar);
+ return Result::Error;
+}
+
+Result WastParser::ParseOutputCommand(CommandPtr*) {
+ // Parse the output command, but always fail since this command is not
+ // actually supported.
+ WABT_TRACE(ParseOutputCommand);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Output);
+ Error(loc, "output command is not supported");
+ Var var;
+ std::string text;
+ ParseVarOpt(&var);
+ if (Peek() == TokenType::Text) {
+ CHECK_RESULT(ParseQuotedText(&text));
+ }
+ EXPECT(Rpar);
+ return Result::Error;
+}
+
+Result WastParser::ParseAction(ActionPtr* out_action) {
+ WABT_TRACE(ParseAction);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+
+ switch (Peek()) {
+ case TokenType::Invoke: {
+ Consume();
+ auto action = std::make_unique<InvokeAction>(loc);
+ ParseVarOpt(&action->module_var, Var(last_module_index_, loc));
+ CHECK_RESULT(ParseQuotedText(&action->name));
+ CHECK_RESULT(ParseConstList(&action->args, ConstType::Normal));
+ *out_action = std::move(action);
+ break;
+ }
+
+ case TokenType::Get: {
+ Consume();
+ auto action = std::make_unique<GetAction>(loc);
+ ParseVarOpt(&action->module_var, Var(last_module_index_, loc));
+ CHECK_RESULT(ParseQuotedText(&action->name));
+ *out_action = std::move(action);
+ break;
+ }
+
+ default:
+ return ErrorExpected({"invoke", "get"});
+ }
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseExpectedValues(ExpectationPtr* expectation) {
+ WABT_TRACE(ParseExpectedValues);
+ Location loc = GetLocation();
+ if (PeekMatchLpar(TokenType::Either)) {
+ auto either = std::make_unique<EitherExpectation>(loc);
+ CHECK_RESULT(ParseEither(&either->expected));
+ *expectation = std::move(either);
+ } else {
+ auto values = std::make_unique<ValueExpectation>(loc);
+ CHECK_RESULT(ParseConstList(&values->expected, ConstType::Expectation));
+ *expectation = std::move(values);
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseEither(ConstVector* alternatives) {
+ WABT_TRACE(ParseEither);
+ MatchLpar(TokenType::Either);
+ CHECK_RESULT(ParseConstList(alternatives, ConstType::Expectation));
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseScriptModule(
+ std::unique_ptr<ScriptModule>* out_module) {
+ WABT_TRACE(ParseScriptModule);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Module);
+ std::string name;
+ ParseBindVarOpt(&name);
+
+ switch (Peek()) {
+ case TokenType::Bin: {
+ Consume();
+ std::vector<uint8_t> data;
+ // TODO(binji): The spec allows this to be empty, switch to
+ // ParseTextListOpt.
+ CHECK_RESULT(ParseTextList(&data));
+
+ auto bsm = std::make_unique<BinaryScriptModule>();
+ bsm->name = name;
+ bsm->loc = loc;
+ bsm->data = std::move(data);
+ *out_module = std::move(bsm);
+ break;
+ }
+
+ case TokenType::Quote: {
+ Consume();
+ std::vector<uint8_t> data;
+ // TODO(binji): The spec allows this to be empty, switch to
+ // ParseTextListOpt.
+ CHECK_RESULT(ParseTextList(&data));
+
+ auto qsm = std::make_unique<QuotedScriptModule>();
+ qsm->name = name;
+ qsm->loc = loc;
+ qsm->data = std::move(data);
+ *out_module = std::move(qsm);
+ break;
+ }
+
+ default: {
+ auto tsm = std::make_unique<TextScriptModule>();
+ tsm->module.name = name;
+ tsm->module.loc = loc;
+ if (IsModuleField(PeekPair())) {
+ CHECK_RESULT(ParseModuleFieldList(&tsm->module));
+ } else if (!PeekMatch(TokenType::Rpar)) {
+ ConsumeIfLpar();
+ return ErrorExpected({"a module field"});
+ }
+ *out_module = std::move(tsm);
+ break;
+ }
+ }
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParseAssertActionCommand(TokenType token_type,
+ CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertActionCommand);
+ EXPECT(Lpar);
+ CHECK_RESULT(Expect(token_type));
+ auto command = std::make_unique<T>();
+ CHECK_RESULT(ParseAction(&command->action));
+ EXPECT(Rpar);
+ *out_command = std::move(command);
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParseAssertActionTextCommand(TokenType token_type,
+ CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertActionTextCommand);
+ EXPECT(Lpar);
+ CHECK_RESULT(Expect(token_type));
+ auto command = std::make_unique<T>();
+ CHECK_RESULT(ParseAction(&command->action));
+ CHECK_RESULT(ParseQuotedText(&command->text));
+ EXPECT(Rpar);
+ *out_command = std::move(command);
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParseAssertScriptModuleCommand(TokenType token_type,
+ CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertScriptModuleCommand);
+ EXPECT(Lpar);
+ CHECK_RESULT(Expect(token_type));
+ auto command = std::make_unique<T>();
+ CHECK_RESULT(ParseScriptModule(&command->module));
+ CHECK_RESULT(ParseQuotedText(&command->text));
+ EXPECT(Rpar);
+ *out_command = std::move(command);
+ return Result::Ok;
+}
+
+void WastParser::CheckImportOrdering(Module* module) {
+ if (module->funcs.size() != module->num_func_imports ||
+ module->tables.size() != module->num_table_imports ||
+ module->memories.size() != module->num_memory_imports ||
+ module->globals.size() != module->num_global_imports ||
+ module->tags.size() != module->num_tag_imports) {
+ Error(GetLocation(),
+ "imports must occur before all non-import definitions");
+ }
+}
+
+Result ParseWatModule(WastLexer* lexer,
+ std::unique_ptr<Module>* out_module,
+ Errors* errors,
+ WastParseOptions* options) {
+ assert(out_module != nullptr);
+ assert(options != nullptr);
+ WastParser parser(lexer, errors, options);
+ CHECK_RESULT(parser.ParseModule(out_module));
+ return Result::Ok;
+}
+
+Result ParseWastScript(WastLexer* lexer,
+ std::unique_ptr<Script>* out_script,
+ Errors* errors,
+ WastParseOptions* options) {
+ assert(out_script != nullptr);
+ assert(options != nullptr);
+ WastParser parser(lexer, errors, options);
+ CHECK_RESULT(parser.ParseScript(out_script));
+ CHECK_RESULT(ResolveNamesScript(out_script->get(), errors));
+ return Result::Ok;
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/wat-writer.cc b/third_party/wasm2c/src/wat-writer.cc
new file mode 100644
index 0000000000..ea70edb389
--- /dev/null
+++ b/third_party/wasm2c/src/wat-writer.cc
@@ -0,0 +1,1789 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/wat-writer.h"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cinttypes>
+#include <cstdarg>
+#include <cstdio>
+#include <iterator>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "wabt/cast.h"
+#include "wabt/common.h"
+#include "wabt/expr-visitor.h"
+#include "wabt/ir-util.h"
+#include "wabt/ir.h"
+#include "wabt/literal.h"
+#include "wabt/stream.h"
+
+#define WABT_TRACING 0
+#include "wabt/tracing.h"
+
+#define INDENT_SIZE 2
+#define NO_FORCE_NEWLINE 0
+#define FORCE_NEWLINE 1
+
+namespace wabt {
+
+namespace {
+
+static const uint8_t s_is_char_escaped[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+
+// This table matches the characters allowed by wast-lexer.cc for `symbol`.
+// The disallowed printable characters are: "(),;[]{} and <space>.
+static const uint8_t s_valid_name_chars[256] = {
+ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x20 */ 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1,
+ /* 0x30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
+ /* 0x40 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
+ /* 0x60 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x70 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,
+};
+
+enum class NextChar {
+ None,
+ Space,
+ Newline,
+ ForceNewline,
+};
+
+struct ExprTree {
+ explicit ExprTree(const Expr* expr, Index result_count)
+ : expr(expr), result_count(result_count) {}
+
+ const Expr* expr;
+ std::vector<ExprTree> children;
+ Index result_count;
+};
+
+class WatWriter : ModuleContext {
+ public:
+ WatWriter(Stream* stream,
+ const WriteWatOptions& options,
+ const Module& module)
+ : ModuleContext(module), options_(options), stream_(stream) {}
+
+ Result WriteModule();
+
+ private:
+ void Indent();
+ void Dedent();
+ void WriteIndent();
+ void WriteNextChar();
+ void WriteDataWithNextChar(const void* src, size_t size);
+ void Writef(const char* format, ...);
+ void WritePutc(char c);
+ void WritePuts(const char* s, NextChar next_char);
+ void WritePutsSpace(const char* s);
+ void WritePutsNewline(const char* s);
+ void WriteNewline(bool force);
+ void WriteOpen(const char* name, NextChar next_char);
+ void WriteOpenNewline(const char* name);
+ void WriteOpenSpace(const char* name);
+ void WriteClose(NextChar next_char);
+ void WriteCloseNewline();
+ void WriteCloseSpace();
+ void WriteString(const std::string& str, NextChar next_char);
+ void WriteName(std::string_view str, NextChar next_char);
+ void WriteNameOrIndex(std::string_view str, Index index, NextChar next_char);
+ void WriteQuotedData(const void* data, size_t length);
+ void WriteQuotedString(std::string_view str, NextChar next_char);
+ void WriteVar(const Var& var, NextChar next_char);
+ void WriteVarUnlessZero(const Var& var, NextChar next_char);
+ void WriteMemoryVarUnlessZero(const Var& memidx, NextChar next_char);
+ void WriteTwoMemoryVarsUnlessBothZero(const Var& srcmemidx,
+ const Var& destmemidx,
+ NextChar next_char);
+ void WriteBrVar(const Var& var, NextChar next_char);
+ void WriteRefKind(Type type, NextChar next_char);
+ void WriteType(Type type, NextChar next_char);
+ void WriteTypes(const TypeVector& types, const char* name);
+ void WriteFuncSigSpace(const FuncSignature& func_sig);
+ void WriteBeginBlock(LabelType label_type,
+ const Block& block,
+ const char* text);
+ void WriteEndBlock();
+ void WriteConst(const Const& const_);
+ void WriteExpr(const Expr* expr);
+ template <typename T>
+ void WriteLoadStoreExpr(const Expr* expr);
+ template <typename T>
+ void WriteMemoryLoadStoreExpr(const Expr* expr);
+ void WriteExprList(const ExprList& exprs);
+ void WriteInitExpr(const ExprList& expr);
+ template <typename T>
+ void WriteTypeBindings(const char* prefix,
+ const T& types,
+ const std::vector<std::string>& index_to_name,
+ Index binding_index_offset = 0);
+ void WriteBeginFunc(const Func& func);
+ void WriteFunc(const Func& func);
+ void WriteBeginGlobal(const Global& global);
+ void WriteGlobal(const Global& global);
+ void WriteTag(const Tag& tag);
+ void WriteLimits(const Limits& limits);
+ void WriteTable(const Table& table);
+ void WriteElemSegment(const ElemSegment& segment);
+ void WriteMemory(const Memory& memory);
+ void WriteDataSegment(const DataSegment& segment);
+ void WriteImport(const Import& import);
+ void WriteExport(const Export& export_);
+ void WriteTypeEntry(const TypeEntry& type);
+ void WriteField(const Field& field);
+ void WriteStartFunction(const Var& start);
+
+ class ExprVisitorDelegate;
+
+ void PushExpr(const Expr* expr, Index operand_count, Index result_count);
+ void FlushExprTree(const ExprTree& expr_tree);
+ void FlushExprTreeVector(const std::vector<ExprTree>&);
+ void FlushExprTreeStack();
+ void WriteFoldedExpr(const Expr*);
+ void WriteFoldedExprList(const ExprList&);
+
+ void BuildInlineExportMap();
+ void WriteInlineExports(ExternalKind, Index);
+ bool IsInlineExport(const Export& export_);
+ void BuildInlineImportMap();
+ void WriteInlineImport(ExternalKind, Index);
+
+ const WriteWatOptions& options_;
+ Stream* stream_ = nullptr;
+ Result result_ = Result::Ok;
+ int indent_ = 0;
+ NextChar next_char_ = NextChar::None;
+ std::vector<ExprTree> expr_tree_stack_;
+ std::multimap<std::pair<ExternalKind, Index>, const Export*>
+ inline_export_map_;
+ std::vector<const Import*> inline_import_map_[kExternalKindCount];
+
+ Index func_index_ = 0;
+ Index global_index_ = 0;
+ Index table_index_ = 0;
+ Index memory_index_ = 0;
+ Index type_index_ = 0;
+ Index tag_index_ = 0;
+ Index data_segment_index_ = 0;
+ Index elem_segment_index_ = 0;
+};
+
+void WatWriter::Indent() {
+ indent_ += INDENT_SIZE;
+}
+
+void WatWriter::Dedent() {
+ indent_ -= INDENT_SIZE;
+ assert(indent_ >= 0);
+}
+
+void WatWriter::WriteIndent() {
+ static char s_indent[] =
+ " "
+ " ";
+ static size_t s_indent_len = sizeof(s_indent) - 1;
+ size_t to_write = indent_;
+ while (to_write >= s_indent_len) {
+ stream_->WriteData(s_indent, s_indent_len);
+ to_write -= s_indent_len;
+ }
+ if (to_write > 0) {
+ stream_->WriteData(s_indent, to_write);
+ }
+}
+
+void WatWriter::WriteNextChar() {
+ switch (next_char_) {
+ case NextChar::Space:
+ stream_->WriteChar(' ');
+ break;
+ case NextChar::Newline:
+ case NextChar::ForceNewline:
+ stream_->WriteChar('\n');
+ WriteIndent();
+ break;
+ case NextChar::None:
+ break;
+ }
+ next_char_ = NextChar::None;
+}
+
+void WatWriter::WriteDataWithNextChar(const void* src, size_t size) {
+ WriteNextChar();
+ stream_->WriteData(src, size);
+}
+
+void WABT_PRINTF_FORMAT(2, 3) WatWriter::Writef(const char* format, ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ /* default to following space */
+ WriteDataWithNextChar(buffer, length);
+ next_char_ = NextChar::Space;
+}
+
+void WatWriter::WritePutc(char c) {
+ stream_->WriteChar(c);
+}
+
+void WatWriter::WritePuts(const char* s, NextChar next_char) {
+ size_t len = strlen(s);
+ WriteDataWithNextChar(s, len);
+ next_char_ = next_char;
+}
+
+void WatWriter::WritePutsSpace(const char* s) {
+ WritePuts(s, NextChar::Space);
+}
+
+void WatWriter::WritePutsNewline(const char* s) {
+ WritePuts(s, NextChar::Newline);
+}
+
+void WatWriter::WriteNewline(bool force) {
+ if (next_char_ == NextChar::ForceNewline) {
+ WriteNextChar();
+ }
+ next_char_ = force ? NextChar::ForceNewline : NextChar::Newline;
+}
+
+void WatWriter::WriteOpen(const char* name, NextChar next_char) {
+ WritePuts("(", NextChar::None);
+ WritePuts(name, next_char);
+ Indent();
+}
+
+void WatWriter::WriteOpenNewline(const char* name) {
+ WriteOpen(name, NextChar::Newline);
+}
+
+void WatWriter::WriteOpenSpace(const char* name) {
+ WriteOpen(name, NextChar::Space);
+}
+
+void WatWriter::WriteClose(NextChar next_char) {
+ if (next_char_ != NextChar::ForceNewline) {
+ next_char_ = NextChar::None;
+ }
+ Dedent();
+ WritePuts(")", next_char);
+}
+
+void WatWriter::WriteCloseNewline() {
+ WriteClose(NextChar::Newline);
+}
+
+void WatWriter::WriteCloseSpace() {
+ WriteClose(NextChar::Space);
+}
+
+void WatWriter::WriteString(const std::string& str, NextChar next_char) {
+ WritePuts(str.c_str(), next_char);
+}
+
+void WatWriter::WriteName(std::string_view str, NextChar next_char) {
+ // Debug names must begin with a $ for for wast file to be valid
+ assert(!str.empty() && str.front() == '$');
+ bool has_invalid_chars = std::any_of(
+ str.begin(), str.end(), [](uint8_t c) { return !s_valid_name_chars[c]; });
+
+ if (has_invalid_chars) {
+ std::string valid_str;
+ std::transform(str.begin(), str.end(), std::back_inserter(valid_str),
+ [](uint8_t c) { return s_valid_name_chars[c] ? c : '_'; });
+ WriteDataWithNextChar(valid_str.data(), valid_str.length());
+ } else {
+ WriteDataWithNextChar(str.data(), str.length());
+ }
+
+ next_char_ = next_char;
+}
+
+void WatWriter::WriteNameOrIndex(std::string_view str,
+ Index index,
+ NextChar next_char) {
+ if (!str.empty()) {
+ WriteName(str, next_char);
+ } else {
+ Writef("(;%u;)", index);
+ }
+}
+
+void WatWriter::WriteQuotedData(const void* data, size_t length) {
+ const uint8_t* u8_data = static_cast<const uint8_t*>(data);
+ static const char s_hexdigits[] = "0123456789abcdef";
+ WriteNextChar();
+ WritePutc('\"');
+ for (size_t i = 0; i < length; ++i) {
+ uint8_t c = u8_data[i];
+ if (s_is_char_escaped[c]) {
+ WritePutc('\\');
+ WritePutc(s_hexdigits[c >> 4]);
+ WritePutc(s_hexdigits[c & 0xf]);
+ } else {
+ WritePutc(c);
+ }
+ }
+ WritePutc('\"');
+ next_char_ = NextChar::Space;
+}
+
+void WatWriter::WriteQuotedString(std::string_view str, NextChar next_char) {
+ WriteQuotedData(str.data(), str.length());
+ next_char_ = next_char;
+}
+
+void WatWriter::WriteVar(const Var& var, NextChar next_char) {
+ if (var.is_index()) {
+ Writef("%" PRIindex, var.index());
+ next_char_ = next_char;
+ } else {
+ WriteName(var.name(), next_char);
+ }
+}
+
+bool VarIsZero(const Var& var) {
+ return var.is_index() && var.index() == 0;
+}
+
+void WatWriter::WriteVarUnlessZero(const Var& var, NextChar next_char) {
+ if (!VarIsZero(var)) {
+ WriteVar(var, next_char);
+ }
+}
+
+void WatWriter::WriteMemoryVarUnlessZero(const Var& memidx,
+ NextChar next_char) {
+ if (module.GetMemoryIndex(memidx) != 0) {
+ WriteVar(memidx, next_char);
+ } else {
+ next_char_ = next_char;
+ }
+}
+
+void WatWriter::WriteTwoMemoryVarsUnlessBothZero(const Var& srcmemidx,
+ const Var& destmemidx,
+ NextChar next_char) {
+ if (module.GetMemoryIndex(srcmemidx) != 0 ||
+ module.GetMemoryIndex(destmemidx) != 0) {
+ WriteVar(srcmemidx, NextChar::Space);
+ WriteVar(destmemidx, next_char);
+ } else {
+ next_char_ = next_char;
+ }
+}
+
+void WatWriter::WriteBrVar(const Var& var, NextChar next_char) {
+ if (var.is_index()) {
+ if (var.index() < GetLabelStackSize()) {
+ Writef("%" PRIindex " (;@%" PRIindex ";)", var.index(),
+ GetLabelStackSize() - var.index() - 1);
+ } else {
+ Writef("%" PRIindex " (; INVALID ;)", var.index());
+ }
+ next_char_ = next_char;
+ } else {
+ WriteString(var.name(), next_char);
+ }
+}
+
+void WatWriter::WriteRefKind(Type type, NextChar next_char) {
+ WritePuts(type.GetRefKindName(), next_char);
+}
+
+void WatWriter::WriteType(Type type, NextChar next_char) {
+ WritePuts(type.GetName().c_str(), next_char);
+}
+
+void WatWriter::WriteTypes(const TypeVector& types, const char* name) {
+ if (types.size()) {
+ if (name) {
+ WriteOpenSpace(name);
+ }
+ for (Type type : types) {
+ WriteType(type, NextChar::Space);
+ }
+ if (name) {
+ WriteCloseSpace();
+ }
+ }
+}
+
+void WatWriter::WriteFuncSigSpace(const FuncSignature& func_sig) {
+ WriteTypes(func_sig.param_types, "param");
+ WriteTypes(func_sig.result_types, "result");
+}
+
+void WatWriter::WriteBeginBlock(LabelType label_type,
+ const Block& block,
+ const char* text) {
+ WritePutsSpace(text);
+ bool has_label = !block.label.empty();
+ if (has_label) {
+ WriteString(block.label, NextChar::Space);
+ }
+ WriteTypes(block.decl.sig.param_types, "param");
+ WriteTypes(block.decl.sig.result_types, "result");
+ if (!has_label) {
+ Writef(" ;; label = @%" PRIindex, GetLabelStackSize());
+ }
+ WriteNewline(FORCE_NEWLINE);
+ BeginBlock(label_type, block);
+ Indent();
+}
+
+void WatWriter::WriteEndBlock() {
+ Dedent();
+ EndBlock();
+ WritePutsNewline(Opcode::End_Opcode.GetName());
+}
+
+void WatWriter::WriteConst(const Const& const_) {
+ switch (const_.type()) {
+ case Type::I32:
+ WritePutsSpace(Opcode::I32Const_Opcode.GetName());
+ Writef("%d", static_cast<int32_t>(const_.u32()));
+ WriteNewline(NO_FORCE_NEWLINE);
+ break;
+
+ case Type::I64:
+ WritePutsSpace(Opcode::I64Const_Opcode.GetName());
+ Writef("%" PRId64, static_cast<int64_t>(const_.u64()));
+ WriteNewline(NO_FORCE_NEWLINE);
+ break;
+
+ case Type::F32: {
+ WritePutsSpace(Opcode::F32Const_Opcode.GetName());
+ char buffer[128];
+ WriteFloatHex(buffer, 128, const_.f32_bits());
+ WritePutsSpace(buffer);
+ Writef("(;=%g;)", Bitcast<float>(const_.f32_bits()));
+ WriteNewline(NO_FORCE_NEWLINE);
+ break;
+ }
+
+ case Type::F64: {
+ WritePutsSpace(Opcode::F64Const_Opcode.GetName());
+ char buffer[128];
+ WriteDoubleHex(buffer, 128, const_.f64_bits());
+ WritePutsSpace(buffer);
+ Writef("(;=%g;)", Bitcast<double>(const_.f64_bits()));
+ WriteNewline(NO_FORCE_NEWLINE);
+ break;
+ }
+
+ case Type::V128: {
+ WritePutsSpace(Opcode::V128Const_Opcode.GetName());
+ auto vec = const_.vec128();
+ Writef("i32x4 0x%08x 0x%08x 0x%08x 0x%08x", vec.u32(0), vec.u32(1),
+ vec.u32(2), vec.u32(3));
+ WriteNewline(NO_FORCE_NEWLINE);
+ break;
+ }
+
+ default:
+ assert(0);
+ break;
+ }
+}
+
+template <typename T>
+void WatWriter::WriteLoadStoreExpr(const Expr* expr) {
+ auto typed_expr = cast<T>(expr);
+ WritePutsSpace(typed_expr->opcode.GetName());
+ if (typed_expr->offset) {
+ Writef("offset=%" PRIaddress, typed_expr->offset);
+ }
+ if (!typed_expr->opcode.IsNaturallyAligned(typed_expr->align)) {
+ Writef("align=%" PRIaddress, typed_expr->align);
+ }
+ WriteNewline(NO_FORCE_NEWLINE);
+}
+
+template <typename T>
+void WatWriter::WriteMemoryLoadStoreExpr(const Expr* expr) {
+ auto typed_expr = cast<T>(expr);
+ WritePutsSpace(typed_expr->opcode.GetName());
+ WriteMemoryVarUnlessZero(typed_expr->memidx, NextChar::Space);
+ if (typed_expr->offset) {
+ Writef("offset=%" PRIaddress, typed_expr->offset);
+ }
+ if (!typed_expr->opcode.IsNaturallyAligned(typed_expr->align)) {
+ Writef("align=%" PRIaddress, typed_expr->align);
+ }
+ WriteNewline(NO_FORCE_NEWLINE);
+}
+
+class WatWriter::ExprVisitorDelegate : public ExprVisitor::Delegate {
+ public:
+ explicit ExprVisitorDelegate(WatWriter* writer) : writer_(writer) {}
+
+ Result OnBinaryExpr(BinaryExpr*) override;
+ Result BeginBlockExpr(BlockExpr*) override;
+ Result EndBlockExpr(BlockExpr*) override;
+ Result OnBrExpr(BrExpr*) override;
+ Result OnBrIfExpr(BrIfExpr*) override;
+ Result OnBrTableExpr(BrTableExpr*) override;
+ 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;
+ Result OnDropExpr(DropExpr*) override;
+ Result OnGlobalGetExpr(GlobalGetExpr*) override;
+ Result OnGlobalSetExpr(GlobalSetExpr*) override;
+ Result BeginIfExpr(IfExpr*) override;
+ Result AfterIfTrueExpr(IfExpr*) override;
+ Result EndIfExpr(IfExpr*) override;
+ Result OnLoadExpr(LoadExpr*) override;
+ Result OnLocalGetExpr(LocalGetExpr*) override;
+ Result OnLocalSetExpr(LocalSetExpr*) override;
+ Result OnLocalTeeExpr(LocalTeeExpr*) override;
+ Result BeginLoopExpr(LoopExpr*) override;
+ Result EndLoopExpr(LoopExpr*) override;
+ Result OnMemoryCopyExpr(MemoryCopyExpr*) override;
+ Result OnDataDropExpr(DataDropExpr*) override;
+ Result OnMemoryFillExpr(MemoryFillExpr*) override;
+ Result OnMemoryGrowExpr(MemoryGrowExpr*) override;
+ Result OnMemoryInitExpr(MemoryInitExpr*) override;
+ Result OnMemorySizeExpr(MemorySizeExpr*) override;
+ Result OnTableCopyExpr(TableCopyExpr*) override;
+ Result OnElemDropExpr(ElemDropExpr*) override;
+ Result OnTableInitExpr(TableInitExpr*) override;
+ Result OnTableGetExpr(TableGetExpr*) override;
+ Result OnTableSetExpr(TableSetExpr*) override;
+ Result OnTableGrowExpr(TableGrowExpr*) override;
+ Result OnTableSizeExpr(TableSizeExpr*) override;
+ Result OnTableFillExpr(TableFillExpr*) override;
+ Result OnRefFuncExpr(RefFuncExpr*) override;
+ Result OnRefNullExpr(RefNullExpr*) override;
+ Result OnRefIsNullExpr(RefIsNullExpr*) override;
+ Result OnNopExpr(NopExpr*) override;
+ Result OnReturnExpr(ReturnExpr*) override;
+ Result OnReturnCallExpr(ReturnCallExpr*) override;
+ Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr*) override;
+ Result OnSelectExpr(SelectExpr*) override;
+ Result OnStoreExpr(StoreExpr*) override;
+ Result OnUnaryExpr(UnaryExpr*) override;
+ Result OnUnreachableExpr(UnreachableExpr*) override;
+ Result BeginTryExpr(TryExpr*) override;
+ Result OnCatchExpr(TryExpr*, Catch*) override;
+ Result OnDelegateExpr(TryExpr*) override;
+ Result EndTryExpr(TryExpr*) override;
+ Result OnThrowExpr(ThrowExpr*) override;
+ Result OnRethrowExpr(RethrowExpr*) override;
+ Result OnAtomicWaitExpr(AtomicWaitExpr*) override;
+ Result OnAtomicFenceExpr(AtomicFenceExpr*) override;
+ Result OnAtomicNotifyExpr(AtomicNotifyExpr*) override;
+ Result OnAtomicLoadExpr(AtomicLoadExpr*) override;
+ Result OnAtomicStoreExpr(AtomicStoreExpr*) override;
+ Result OnAtomicRmwExpr(AtomicRmwExpr*) override;
+ Result OnAtomicRmwCmpxchgExpr(AtomicRmwCmpxchgExpr*) override;
+ Result OnTernaryExpr(TernaryExpr*) override;
+ Result OnSimdLaneOpExpr(SimdLaneOpExpr*) override;
+ Result OnSimdLoadLaneExpr(SimdLoadLaneExpr*) override;
+ Result OnSimdStoreLaneExpr(SimdStoreLaneExpr*) override;
+ Result OnSimdShuffleOpExpr(SimdShuffleOpExpr*) override;
+ Result OnLoadSplatExpr(LoadSplatExpr*) override;
+ Result OnLoadZeroExpr(LoadZeroExpr*) override;
+
+ private:
+ WatWriter* writer_;
+};
+
+Result WatWriter::ExprVisitorDelegate::OnBinaryExpr(BinaryExpr* expr) {
+ writer_->WritePutsNewline(expr->opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::BeginBlockExpr(BlockExpr* expr) {
+ writer_->WriteBeginBlock(LabelType::Block, expr->block,
+ Opcode::Block_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::EndBlockExpr(BlockExpr* expr) {
+ writer_->WriteEndBlock();
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnBrExpr(BrExpr* expr) {
+ writer_->WritePutsSpace(Opcode::Br_Opcode.GetName());
+ writer_->WriteBrVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnBrIfExpr(BrIfExpr* expr) {
+ writer_->WritePutsSpace(Opcode::BrIf_Opcode.GetName());
+ writer_->WriteBrVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnBrTableExpr(BrTableExpr* expr) {
+ writer_->WritePutsSpace(Opcode::BrTable_Opcode.GetName());
+ for (const Var& var : expr->targets) {
+ writer_->WriteBrVar(var, NextChar::Space);
+ }
+ writer_->WriteBrVar(expr->default_target, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnCallExpr(CallExpr* expr) {
+ writer_->WritePutsSpace(Opcode::Call_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnCallIndirectExpr(
+ CallIndirectExpr* expr) {
+ writer_->WritePutsSpace(Opcode::CallIndirect_Opcode.GetName());
+ writer_->WriteVarUnlessZero(expr->table, NextChar::Space);
+ writer_->WriteOpenSpace("type");
+ writer_->WriteVar(expr->decl.type_var, NextChar::Newline);
+ writer_->WriteCloseNewline();
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnCallRefExpr(CallRefExpr* expr) {
+ writer_->WritePutsSpace(Opcode::CallRef_Opcode.GetName());
+ 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;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnConstExpr(ConstExpr* expr) {
+ writer_->WriteConst(expr->const_);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnConvertExpr(ConvertExpr* expr) {
+ writer_->WritePutsNewline(expr->opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnDropExpr(DropExpr* expr) {
+ writer_->WritePutsNewline(Opcode::Drop_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnGlobalGetExpr(GlobalGetExpr* expr) {
+ writer_->WritePutsSpace(Opcode::GlobalGet_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnGlobalSetExpr(GlobalSetExpr* expr) {
+ writer_->WritePutsSpace(Opcode::GlobalSet_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::BeginIfExpr(IfExpr* expr) {
+ writer_->WriteBeginBlock(LabelType::If, expr->true_,
+ Opcode::If_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::AfterIfTrueExpr(IfExpr* expr) {
+ if (!expr->false_.empty()) {
+ writer_->Dedent();
+ writer_->WritePutsSpace(Opcode::Else_Opcode.GetName());
+ writer_->Indent();
+ writer_->WriteNewline(FORCE_NEWLINE);
+ }
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::EndIfExpr(IfExpr* expr) {
+ writer_->WriteEndBlock();
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnLoadExpr(LoadExpr* expr) {
+ writer_->WriteMemoryLoadStoreExpr<LoadExpr>(expr);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnLocalGetExpr(LocalGetExpr* expr) {
+ writer_->WritePutsSpace(Opcode::LocalGet_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnLocalSetExpr(LocalSetExpr* expr) {
+ writer_->WritePutsSpace(Opcode::LocalSet_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnLocalTeeExpr(LocalTeeExpr* expr) {
+ writer_->WritePutsSpace(Opcode::LocalTee_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::BeginLoopExpr(LoopExpr* expr) {
+ writer_->WriteBeginBlock(LabelType::Loop, expr->block,
+ Opcode::Loop_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::EndLoopExpr(LoopExpr* expr) {
+ writer_->WriteEndBlock();
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnMemoryCopyExpr(MemoryCopyExpr* expr) {
+ writer_->WritePutsSpace(Opcode::MemoryCopy_Opcode.GetName());
+ writer_->WriteTwoMemoryVarsUnlessBothZero(expr->srcmemidx, expr->destmemidx,
+ NextChar::Space);
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnDataDropExpr(DataDropExpr* expr) {
+ writer_->WritePutsSpace(Opcode::DataDrop_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnMemoryFillExpr(MemoryFillExpr* expr) {
+ writer_->WritePutsSpace(Opcode::MemoryFill_Opcode.GetName());
+ writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space);
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnMemoryGrowExpr(MemoryGrowExpr* expr) {
+ writer_->WritePutsSpace(Opcode::MemoryGrow_Opcode.GetName());
+ writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space);
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnMemorySizeExpr(MemorySizeExpr* expr) {
+ writer_->WritePutsSpace(Opcode::MemorySize_Opcode.GetName());
+ writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space);
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnMemoryInitExpr(MemoryInitExpr* expr) {
+ writer_->WritePutsSpace(Opcode::MemoryInit_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Space);
+ writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space);
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnTableCopyExpr(TableCopyExpr* expr) {
+ writer_->WritePutsSpace(Opcode::TableCopy_Opcode.GetName());
+ if (!(VarIsZero(expr->dst_table) && VarIsZero(expr->src_table))) {
+ writer_->WriteVar(expr->dst_table, NextChar::Space);
+ writer_->WriteVar(expr->src_table, NextChar::Space);
+ }
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnElemDropExpr(ElemDropExpr* expr) {
+ writer_->WritePutsSpace(Opcode::ElemDrop_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnTableInitExpr(TableInitExpr* expr) {
+ writer_->WritePutsSpace(Opcode::TableInit_Opcode.GetName());
+ writer_->WriteVarUnlessZero(expr->table_index, NextChar::Space);
+ writer_->WriteVar(expr->segment_index, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnTableGetExpr(TableGetExpr* expr) {
+ writer_->WritePutsSpace(Opcode::TableGet_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnTableSetExpr(TableSetExpr* expr) {
+ writer_->WritePutsSpace(Opcode::TableSet_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnTableGrowExpr(TableGrowExpr* expr) {
+ writer_->WritePutsSpace(Opcode::TableGrow_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnTableSizeExpr(TableSizeExpr* expr) {
+ writer_->WritePutsSpace(Opcode::TableSize_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnTableFillExpr(TableFillExpr* expr) {
+ writer_->WritePutsSpace(Opcode::TableFill_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnRefFuncExpr(RefFuncExpr* expr) {
+ writer_->WritePutsSpace(Opcode::RefFunc_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnRefNullExpr(RefNullExpr* expr) {
+ writer_->WritePutsSpace(Opcode::RefNull_Opcode.GetName());
+ writer_->WriteRefKind(expr->type, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnRefIsNullExpr(RefIsNullExpr* expr) {
+ writer_->WritePutsNewline(Opcode::RefIsNull_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnNopExpr(NopExpr* expr) {
+ writer_->WritePutsNewline(Opcode::Nop_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnReturnExpr(ReturnExpr* expr) {
+ writer_->WritePutsNewline(Opcode::Return_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnReturnCallExpr(ReturnCallExpr* expr) {
+ writer_->WritePutsSpace(Opcode::ReturnCall_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnReturnCallIndirectExpr(
+ ReturnCallIndirectExpr* expr) {
+ writer_->WritePutsSpace(Opcode::ReturnCallIndirect_Opcode.GetName());
+ writer_->WriteOpenSpace("type");
+ writer_->WriteVar(expr->decl.type_var, NextChar::Space);
+ writer_->WriteCloseNewline();
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnSelectExpr(SelectExpr* expr) {
+ writer_->WritePutsSpace(Opcode::Select_Opcode.GetName());
+ if (!expr->result_type.empty()) {
+ writer_->WriteTypes(expr->result_type, "result");
+ }
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnStoreExpr(StoreExpr* expr) {
+ writer_->WriteMemoryLoadStoreExpr<StoreExpr>(expr);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnUnaryExpr(UnaryExpr* expr) {
+ writer_->WritePutsNewline(expr->opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnUnreachableExpr(
+ UnreachableExpr* expr) {
+ writer_->WritePutsNewline(Opcode::Unreachable_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::BeginTryExpr(TryExpr* expr) {
+ writer_->WriteBeginBlock(LabelType::Try, expr->block,
+ Opcode::Try_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnCatchExpr(TryExpr* expr,
+ Catch* catch_) {
+ writer_->Dedent();
+ if (catch_->IsCatchAll()) {
+ writer_->WritePutsNewline(Opcode::CatchAll_Opcode.GetName());
+ } else {
+ writer_->WritePutsSpace(Opcode::Catch_Opcode.GetName());
+ writer_->WriteVar(catch_->var, NextChar::Newline);
+ }
+ writer_->Indent();
+ writer_->SetTopLabelType(LabelType::Catch);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnDelegateExpr(TryExpr* expr) {
+ writer_->Dedent();
+ writer_->EndBlock();
+ writer_->WritePutsSpace(Opcode::Delegate_Opcode.GetName());
+ writer_->WriteVar(expr->delegate_target, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::EndTryExpr(TryExpr* expr) {
+ writer_->WriteEndBlock();
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnThrowExpr(ThrowExpr* expr) {
+ writer_->WritePutsSpace(Opcode::Throw_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnRethrowExpr(RethrowExpr* expr) {
+ writer_->WritePutsSpace(Opcode::Rethrow_Opcode.GetName());
+ writer_->WriteBrVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnAtomicWaitExpr(AtomicWaitExpr* expr) {
+ writer_->WriteLoadStoreExpr<AtomicWaitExpr>(expr);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnAtomicFenceExpr(
+ AtomicFenceExpr* expr) {
+ assert(expr->consistency_model == 0);
+ writer_->WritePutsNewline(Opcode::AtomicFence_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnAtomicNotifyExpr(
+ AtomicNotifyExpr* expr) {
+ writer_->WriteLoadStoreExpr<AtomicNotifyExpr>(expr);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnAtomicLoadExpr(AtomicLoadExpr* expr) {
+ writer_->WriteLoadStoreExpr<AtomicLoadExpr>(expr);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnAtomicStoreExpr(
+ AtomicStoreExpr* expr) {
+ writer_->WriteLoadStoreExpr<AtomicStoreExpr>(expr);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnAtomicRmwExpr(AtomicRmwExpr* expr) {
+ writer_->WriteLoadStoreExpr<AtomicRmwExpr>(expr);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnAtomicRmwCmpxchgExpr(
+ AtomicRmwCmpxchgExpr* expr) {
+ writer_->WriteLoadStoreExpr<AtomicRmwCmpxchgExpr>(expr);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnTernaryExpr(TernaryExpr* expr) {
+ writer_->WritePutsNewline(expr->opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnSimdLaneOpExpr(SimdLaneOpExpr* expr) {
+ writer_->WritePutsSpace(expr->opcode.GetName());
+ writer_->Writef("%" PRIu64, (expr->val));
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnSimdLoadLaneExpr(
+ SimdLoadLaneExpr* expr) {
+ writer_->WritePutsSpace(expr->opcode.GetName());
+ writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space);
+ if (expr->offset) {
+ writer_->Writef("offset=%" PRIaddress, expr->offset);
+ }
+ if (!expr->opcode.IsNaturallyAligned(expr->align)) {
+ writer_->Writef("align=%" PRIaddress, expr->align);
+ }
+ writer_->Writef("%" PRIu64, (expr->val));
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnSimdStoreLaneExpr(
+ SimdStoreLaneExpr* expr) {
+ writer_->WritePutsSpace(expr->opcode.GetName());
+ writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space);
+ if (expr->offset) {
+ writer_->Writef("offset=%" PRIaddress, expr->offset);
+ }
+ if (!expr->opcode.IsNaturallyAligned(expr->align)) {
+ writer_->Writef("align=%" PRIaddress, expr->align);
+ }
+ writer_->Writef("%" PRIu64, (expr->val));
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnSimdShuffleOpExpr(
+ SimdShuffleOpExpr* expr) {
+ writer_->WritePutsSpace(expr->opcode.GetName());
+ std::array<uint8_t, 16> values = Bitcast<std::array<uint8_t, 16>>(expr->val);
+ for (int32_t lane = 0; lane < 16; ++lane) {
+#if WABT_BIG_ENDIAN
+ writer_->Writef("%u", values[15 - lane]);
+#else
+ writer_->Writef("%u", values[lane]);
+#endif
+ }
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnLoadSplatExpr(LoadSplatExpr* expr) {
+ writer_->WriteLoadStoreExpr<LoadSplatExpr>(expr);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnLoadZeroExpr(LoadZeroExpr* expr) {
+ writer_->WriteLoadStoreExpr<LoadZeroExpr>(expr);
+ return Result::Ok;
+}
+
+void WatWriter::WriteExpr(const Expr* expr) {
+ WABT_TRACE(WriteExprList);
+ ExprVisitorDelegate delegate(this);
+ ExprVisitor visitor(&delegate);
+ visitor.VisitExpr(const_cast<Expr*>(expr));
+}
+
+void WatWriter::WriteExprList(const ExprList& exprs) {
+ WABT_TRACE(WriteExprList);
+ ExprVisitorDelegate delegate(this);
+ ExprVisitor visitor(&delegate);
+ visitor.VisitExprList(const_cast<ExprList&>(exprs));
+}
+
+void WatWriter::WriteFoldedExpr(const Expr* expr) {
+ WABT_TRACE_ARGS(WriteFoldedExpr, "%s", GetExprTypeName(*expr));
+ auto arity = GetExprArity(*expr);
+ PushExpr(expr, arity.nargs, arity.nreturns);
+}
+
+void WatWriter::WriteFoldedExprList(const ExprList& exprs) {
+ WABT_TRACE(WriteFoldedExprList);
+ for (const Expr& expr : exprs) {
+ WriteFoldedExpr(&expr);
+ }
+}
+
+void WatWriter::PushExpr(const Expr* expr,
+ Index operand_count,
+ Index result_count) {
+ WABT_TRACE_ARGS(PushExpr, "%s, %" PRIindex ", %" PRIindex "",
+ GetExprTypeName(*expr), operand_count, result_count);
+
+ // Try to pop operand off the expr stack to use as this expr's children. One
+ // expr can have multiple return values (with the multi-value extension), so
+ // we have to iterate over each in reverse.
+
+ auto first_operand = expr_tree_stack_.end();
+
+ Index current_count = 0;
+ if (operand_count > 0) {
+ for (auto iter = expr_tree_stack_.rbegin(); iter != expr_tree_stack_.rend();
+ ++iter) {
+ assert(iter->result_count > 0);
+ current_count += iter->result_count;
+
+ if (current_count == operand_count) {
+ first_operand = iter.base() - 1;
+ break;
+ } else if (current_count > operand_count) {
+ // We went over the number of operands this instruction wants; this can
+ // only happen when there are expressions on the stack with a
+ // result_count > 1. When this happens we can't fold, since any result
+ // we produce will not make sense.
+ break;
+ }
+ }
+ }
+
+ ExprTree tree(expr, result_count);
+
+ if (current_count == operand_count && operand_count > 0) {
+ auto last_operand = expr_tree_stack_.end();
+ std::move(first_operand, last_operand, std::back_inserter(tree.children));
+ expr_tree_stack_.erase(first_operand, last_operand);
+ }
+
+ expr_tree_stack_.emplace_back(std::move(tree));
+ if (current_count > operand_count || result_count == 0) {
+ FlushExprTreeStack();
+ }
+}
+
+void WatWriter::FlushExprTree(const ExprTree& expr_tree) {
+ WABT_TRACE_ARGS(FlushExprTree, "%s", GetExprTypeName(*expr_tree.expr));
+ switch (expr_tree.expr->type()) {
+ case ExprType::Block:
+ WritePuts("(", NextChar::None);
+ WriteBeginBlock(LabelType::Block, cast<BlockExpr>(expr_tree.expr)->block,
+ Opcode::Block_Opcode.GetName());
+ WriteFoldedExprList(cast<BlockExpr>(expr_tree.expr)->block.exprs);
+ FlushExprTreeStack();
+ WriteCloseNewline();
+ EndBlock();
+ break;
+
+ case ExprType::Loop:
+ WritePuts("(", NextChar::None);
+ WriteBeginBlock(LabelType::Loop, cast<LoopExpr>(expr_tree.expr)->block,
+ Opcode::Loop_Opcode.GetName());
+ WriteFoldedExprList(cast<LoopExpr>(expr_tree.expr)->block.exprs);
+ FlushExprTreeStack();
+ WriteCloseNewline();
+ EndBlock();
+ break;
+
+ case ExprType::If: {
+ auto if_expr = cast<IfExpr>(expr_tree.expr);
+ WritePuts("(", NextChar::None);
+ WriteBeginBlock(LabelType::If, if_expr->true_,
+ Opcode::If_Opcode.GetName());
+ FlushExprTreeVector(expr_tree.children);
+ WriteOpenNewline("then");
+ WriteFoldedExprList(if_expr->true_.exprs);
+ FlushExprTreeStack();
+ WriteCloseNewline();
+ if (!if_expr->false_.empty()) {
+ WriteOpenNewline("else");
+ WriteFoldedExprList(if_expr->false_);
+ FlushExprTreeStack();
+ WriteCloseNewline();
+ }
+ WriteCloseNewline();
+ EndBlock();
+ break;
+ }
+
+ case ExprType::Try: {
+ auto try_expr = cast<TryExpr>(expr_tree.expr);
+ WritePuts("(", NextChar::None);
+ WriteBeginBlock(LabelType::Try, try_expr->block,
+ Opcode::Try_Opcode.GetName());
+ WriteOpenNewline("do");
+ FlushExprTreeVector(expr_tree.children);
+ WriteFoldedExprList(try_expr->block.exprs);
+ FlushExprTreeStack();
+ WriteCloseNewline();
+ switch (try_expr->kind) {
+ case TryKind::Catch:
+ for (const Catch& catch_ : try_expr->catches) {
+ WritePuts("(", NextChar::None);
+ if (catch_.IsCatchAll()) {
+ WritePutsNewline("catch_all");
+ } else {
+ WritePutsSpace(Opcode::Catch_Opcode.GetName());
+ WriteVar(catch_.var, NextChar::Newline);
+ }
+ Indent();
+ WriteFoldedExprList(catch_.exprs);
+ FlushExprTreeStack();
+ WriteCloseNewline();
+ }
+ break;
+ case TryKind::Delegate:
+ WritePuts("(", NextChar::None);
+ WritePutsSpace(Opcode::Delegate_Opcode.GetName());
+ WriteVar(try_expr->delegate_target, NextChar::None);
+ WritePuts(")", NextChar::Newline);
+ break;
+ case TryKind::Plain:
+ // Nothing to do.
+ break;
+ }
+ WriteCloseNewline();
+ EndBlock();
+ break;
+ }
+
+ default: {
+ WritePuts("(", NextChar::None);
+ WriteExpr(expr_tree.expr);
+ Indent();
+ FlushExprTreeVector(expr_tree.children);
+ WriteCloseNewline();
+ break;
+ }
+ }
+}
+
+void WatWriter::FlushExprTreeVector(const std::vector<ExprTree>& expr_trees) {
+ WABT_TRACE_ARGS(FlushExprTreeVector, "%zu", expr_trees.size());
+ for (auto expr_tree : expr_trees) {
+ FlushExprTree(expr_tree);
+ }
+}
+
+void WatWriter::FlushExprTreeStack() {
+ std::vector<ExprTree> stack_copy(std::move(expr_tree_stack_));
+ expr_tree_stack_.clear();
+ FlushExprTreeVector(stack_copy);
+}
+
+void WatWriter::WriteInitExpr(const ExprList& expr) {
+ if (!expr.empty()) {
+ WritePuts("(", NextChar::None);
+ WriteExprList(expr);
+ /* clear the next char, so we don't write a newline after the expr */
+ next_char_ = NextChar::None;
+ WritePuts(")", NextChar::Space);
+ }
+}
+
+template <typename T>
+void WatWriter::WriteTypeBindings(const char* prefix,
+ const T& types,
+ const std::vector<std::string>& index_to_name,
+ Index binding_index_offset) {
+ /* named params/locals must be specified by themselves, but nameless
+ * params/locals can be compressed, e.g.:
+ * (param $foo i32)
+ * (param i32 i64 f32)
+ */
+ bool first = true;
+ bool prev_has_name = false;
+ size_t index = 0;
+ for (Type type : types) {
+ const std::string& name = index_to_name[binding_index_offset + index];
+ bool has_name = !name.empty();
+ if ((has_name || prev_has_name) && !first) {
+ WriteCloseSpace();
+ }
+ if (has_name || prev_has_name || first) {
+ WriteOpenSpace(prefix);
+ }
+ if (has_name) {
+ WriteString(name, NextChar::Space);
+ }
+ WriteType(type, NextChar::Space);
+ prev_has_name = has_name;
+ first = false;
+ ++index;
+ }
+ if (types.size() != 0) {
+ WriteCloseSpace();
+ }
+}
+
+void WatWriter::WriteBeginFunc(const Func& func) {
+ WriteOpenSpace("func");
+ WriteNameOrIndex(func.name, func_index_, NextChar::Space);
+ WriteInlineExports(ExternalKind::Func, func_index_);
+ WriteInlineImport(ExternalKind::Func, func_index_);
+ if (func.decl.has_func_type) {
+ WriteOpenSpace("type");
+ WriteVar(func.decl.type_var, NextChar::None);
+ WriteCloseSpace();
+ }
+
+ if (module.IsImport(ExternalKind::Func, Var(func_index_, Location()))) {
+ // Imported functions can be written a few ways:
+ //
+ // 1. (import "module" "field" (func (type 0)))
+ // 2. (import "module" "field" (func (param i32) (result i32)))
+ // 3. (func (import "module" "field") (type 0))
+ // 4. (func (import "module" "field") (param i32) (result i32))
+ // 5. (func (import "module" "field") (type 0) (param i32) (result i32))
+ //
+ // Note that the text format does not allow including the param/result
+ // explicitly when using the "(import..." syntax (#1 and #2).
+ if (options_.inline_import || !func.decl.has_func_type) {
+ WriteFuncSigSpace(func.decl.sig);
+ }
+ }
+ func_index_++;
+}
+
+void WatWriter::WriteFunc(const Func& func) {
+ WriteBeginFunc(func);
+ std::vector<std::string> index_to_name;
+ MakeTypeBindingReverseMapping(func.GetNumParamsAndLocals(), func.bindings,
+ &index_to_name);
+ WriteTypeBindings("param", func.decl.sig.param_types, index_to_name);
+ WriteTypes(func.decl.sig.result_types, "result");
+ WriteNewline(NO_FORCE_NEWLINE);
+ if (func.local_types.size()) {
+ WriteTypeBindings("local", func.local_types, index_to_name,
+ func.GetNumParams());
+ }
+ WriteNewline(NO_FORCE_NEWLINE);
+ BeginFunc(func);
+ if (options_.fold_exprs) {
+ WriteFoldedExprList(func.exprs);
+ FlushExprTreeStack();
+ } else {
+ WriteExprList(func.exprs);
+ }
+ EndFunc();
+ WriteCloseNewline();
+}
+
+void WatWriter::WriteBeginGlobal(const Global& global) {
+ WriteOpenSpace("global");
+ WriteNameOrIndex(global.name, global_index_, NextChar::Space);
+ WriteInlineExports(ExternalKind::Global, global_index_);
+ WriteInlineImport(ExternalKind::Global, global_index_);
+ if (global.mutable_) {
+ WriteOpenSpace("mut");
+ WriteType(global.type, NextChar::Space);
+ WriteCloseSpace();
+ } else {
+ WriteType(global.type, NextChar::Space);
+ }
+ global_index_++;
+}
+
+void WatWriter::WriteGlobal(const Global& global) {
+ WriteBeginGlobal(global);
+ WriteInitExpr(global.init_expr);
+ WriteCloseNewline();
+}
+
+void WatWriter::WriteTag(const Tag& tag) {
+ WriteOpenSpace("tag");
+ WriteNameOrIndex(tag.name, tag_index_, NextChar::Space);
+ WriteInlineExports(ExternalKind::Tag, tag_index_);
+ WriteInlineImport(ExternalKind::Tag, tag_index_);
+ if (tag.decl.has_func_type) {
+ WriteOpenSpace("type");
+ WriteVar(tag.decl.type_var, NextChar::None);
+ WriteCloseSpace();
+ }
+ WriteTypes(tag.decl.sig.param_types, "param");
+ ++tag_index_;
+ WriteCloseNewline();
+}
+
+void WatWriter::WriteLimits(const Limits& limits) {
+ if (limits.is_64) {
+ Writef("i64");
+ }
+ Writef("%" PRIu64, limits.initial);
+ if (limits.has_max) {
+ Writef("%" PRIu64, limits.max);
+ }
+ if (limits.is_shared) {
+ Writef("shared");
+ }
+}
+
+void WatWriter::WriteTable(const Table& table) {
+ WriteOpenSpace("table");
+ WriteNameOrIndex(table.name, table_index_, NextChar::Space);
+ WriteInlineExports(ExternalKind::Table, table_index_);
+ WriteInlineImport(ExternalKind::Table, table_index_);
+ WriteLimits(table.elem_limits);
+ WriteType(table.elem_type, NextChar::None);
+ WriteCloseNewline();
+ table_index_++;
+}
+
+void WatWriter::WriteElemSegment(const ElemSegment& segment) {
+ WriteOpenSpace("elem");
+ // The first name we encounter here, pre-bulk-memory, was intended to refer to
+ // the table while segment names were not supported at all. For this reason
+ // we cannot emit a segment name here without bulk-memory enabled, otherwise
+ // the name will be assumed to be the name of a table and parsing will fail.
+ if (options_.features.bulk_memory_enabled()) {
+ WriteNameOrIndex(segment.name, elem_segment_index_, NextChar::Space);
+ } else {
+ Writef("(;%u;)", elem_segment_index_);
+ }
+
+ uint8_t flags = segment.GetFlags(&module);
+
+ if ((flags & (SegPassive | SegExplicitIndex)) == SegExplicitIndex) {
+ WriteOpenSpace("table");
+ WriteVar(segment.table_var, NextChar::Space);
+ WriteCloseSpace();
+ }
+
+ if (!(flags & SegPassive)) {
+ WriteInitExpr(segment.offset);
+ }
+
+ if ((flags & SegDeclared) == SegDeclared) {
+ WritePuts("declare", NextChar::Space);
+ }
+
+ if (flags & SegUseElemExprs) {
+ WriteType(segment.elem_type, NextChar::Space);
+ } else {
+ assert(segment.elem_type == Type::FuncRef);
+ WritePuts("func", NextChar::Space);
+ }
+
+ for (const ExprList& expr : segment.elem_exprs) {
+ if (flags & SegUseElemExprs) {
+ WriteInitExpr(expr);
+ } else {
+ assert(expr.size() == 1);
+ assert(expr.front().type() == ExprType::RefFunc);
+ WriteVar(cast<const RefFuncExpr>(&expr.front())->var, NextChar::Space);
+ }
+ }
+ WriteCloseNewline();
+ elem_segment_index_++;
+}
+
+void WatWriter::WriteMemory(const Memory& memory) {
+ WriteOpenSpace("memory");
+ WriteNameOrIndex(memory.name, memory_index_, NextChar::Space);
+ WriteInlineExports(ExternalKind::Memory, memory_index_);
+ WriteInlineImport(ExternalKind::Memory, memory_index_);
+ WriteLimits(memory.page_limits);
+ WriteCloseNewline();
+ memory_index_++;
+}
+
+void WatWriter::WriteDataSegment(const DataSegment& segment) {
+ WriteOpenSpace("data");
+ WriteNameOrIndex(segment.name, data_segment_index_, NextChar::Space);
+ if (segment.kind != SegmentKind::Passive) {
+ WriteMemoryVarUnlessZero(segment.memory_var, NextChar::Space);
+ WriteInitExpr(segment.offset);
+ }
+ WriteQuotedData(segment.data.data(), segment.data.size());
+ WriteCloseNewline();
+ data_segment_index_++;
+}
+
+void WatWriter::WriteImport(const Import& import) {
+ if (!options_.inline_import) {
+ WriteOpenSpace("import");
+ WriteQuotedString(import.module_name, NextChar::Space);
+ WriteQuotedString(import.field_name, NextChar::Space);
+ }
+
+ switch (import.kind()) {
+ case ExternalKind::Func:
+ WriteBeginFunc(cast<FuncImport>(&import)->func);
+ WriteCloseSpace();
+ break;
+
+ case ExternalKind::Table:
+ WriteTable(cast<TableImport>(&import)->table);
+ break;
+
+ case ExternalKind::Memory:
+ WriteMemory(cast<MemoryImport>(&import)->memory);
+ break;
+
+ case ExternalKind::Global:
+ WriteBeginGlobal(cast<GlobalImport>(&import)->global);
+ WriteCloseSpace();
+ break;
+
+ case ExternalKind::Tag:
+ WriteTag(cast<TagImport>(&import)->tag);
+ break;
+ }
+
+ if (options_.inline_import) {
+ WriteNewline(NO_FORCE_NEWLINE);
+ } else {
+ WriteCloseNewline();
+ }
+}
+
+void WatWriter::WriteExport(const Export& export_) {
+ if (options_.inline_export && IsInlineExport(export_)) {
+ return;
+ }
+ WriteOpenSpace("export");
+ WriteQuotedString(export_.name, NextChar::Space);
+ WriteOpenSpace(GetKindName(export_.kind));
+ WriteVar(export_.var, NextChar::Space);
+ WriteCloseSpace();
+ WriteCloseNewline();
+}
+
+void WatWriter::WriteTypeEntry(const TypeEntry& type) {
+ WriteOpenSpace("type");
+ WriteNameOrIndex(type.name, type_index_++, NextChar::Space);
+ switch (type.kind()) {
+ case TypeEntryKind::Func:
+ WriteOpenSpace("func");
+ WriteFuncSigSpace(cast<FuncType>(&type)->sig);
+ WriteCloseSpace();
+ break;
+
+ case TypeEntryKind::Struct: {
+ auto* struct_type = cast<StructType>(&type);
+ WriteOpenSpace("struct");
+ Index field_index = 0;
+ for (auto&& field : struct_type->fields) {
+ // TODO: Write shorthand if there is no name.
+ WriteOpenSpace("field");
+ WriteNameOrIndex(field.name, field_index++, NextChar::Space);
+ WriteField(field);
+ WriteCloseSpace();
+ }
+ WriteCloseSpace();
+ break;
+ }
+
+ case TypeEntryKind::Array: {
+ auto* array_type = cast<ArrayType>(&type);
+ WriteOpenSpace("array");
+ WriteField(array_type->field);
+ WriteCloseSpace();
+ break;
+ }
+ }
+ WriteCloseNewline();
+}
+
+void WatWriter::WriteField(const Field& field) {
+ if (field.mutable_) {
+ WriteOpenSpace("mut");
+ }
+ WriteType(field.type, NextChar::Space);
+ if (field.mutable_) {
+ WriteCloseSpace();
+ }
+}
+
+void WatWriter::WriteStartFunction(const Var& start) {
+ WriteOpenSpace("start");
+ WriteVar(start, NextChar::None);
+ WriteCloseNewline();
+}
+
+Result WatWriter::WriteModule() {
+ BuildInlineExportMap();
+ BuildInlineImportMap();
+ WriteOpenSpace("module");
+ if (module.name.empty()) {
+ WriteNewline(NO_FORCE_NEWLINE);
+ } else {
+ WriteName(module.name, NextChar::Newline);
+ }
+ for (const ModuleField& field : module.fields) {
+ switch (field.type()) {
+ case ModuleFieldType::Func:
+ WriteFunc(cast<FuncModuleField>(&field)->func);
+ break;
+ case ModuleFieldType::Global:
+ WriteGlobal(cast<GlobalModuleField>(&field)->global);
+ break;
+ case ModuleFieldType::Import:
+ WriteImport(*cast<ImportModuleField>(&field)->import);
+ break;
+ case ModuleFieldType::Tag:
+ WriteTag(cast<TagModuleField>(&field)->tag);
+ break;
+ case ModuleFieldType::Export:
+ WriteExport(cast<ExportModuleField>(&field)->export_);
+ break;
+ case ModuleFieldType::Table:
+ WriteTable(cast<TableModuleField>(&field)->table);
+ break;
+ case ModuleFieldType::ElemSegment:
+ WriteElemSegment(cast<ElemSegmentModuleField>(&field)->elem_segment);
+ break;
+ case ModuleFieldType::Memory:
+ WriteMemory(cast<MemoryModuleField>(&field)->memory);
+ break;
+ case ModuleFieldType::DataSegment:
+ WriteDataSegment(cast<DataSegmentModuleField>(&field)->data_segment);
+ break;
+ case ModuleFieldType::Type:
+ WriteTypeEntry(*cast<TypeModuleField>(&field)->type);
+ break;
+ case ModuleFieldType::Start:
+ WriteStartFunction(cast<StartModuleField>(&field)->start);
+ break;
+ }
+ }
+ WriteCloseNewline();
+ /* force the newline to be written */
+ WriteNextChar();
+ return result_;
+}
+
+void WatWriter::BuildInlineExportMap() {
+ if (!options_.inline_export) {
+ return;
+ }
+
+ for (Export* export_ : module.exports) {
+ Index index = kInvalidIndex;
+
+ // Exported imports can't be written with inline exports, unless the
+ // imports are also inline. For example, the following is invalid:
+ //
+ // (import "module" "field" (func (export "e")))
+ //
+ // But this is valid:
+ //
+ // (func (export "e") (import "module" "field"))
+ //
+ if (!options_.inline_import && module.IsImport(*export_)) {
+ continue;
+ }
+
+ switch (export_->kind) {
+ case ExternalKind::Func:
+ index = module.GetFuncIndex(export_->var);
+ break;
+
+ case ExternalKind::Table:
+ index = module.GetTableIndex(export_->var);
+ break;
+
+ case ExternalKind::Memory:
+ index = module.GetMemoryIndex(export_->var);
+ break;
+
+ case ExternalKind::Global:
+ index = module.GetGlobalIndex(export_->var);
+ break;
+
+ case ExternalKind::Tag:
+ index = module.GetTagIndex(export_->var);
+ break;
+ }
+
+ if (index != kInvalidIndex) {
+ auto key = std::make_pair(export_->kind, index);
+ inline_export_map_.insert(std::make_pair(key, export_));
+ }
+ }
+}
+
+void WatWriter::WriteInlineExports(ExternalKind kind, Index index) {
+ if (!options_.inline_export) {
+ return;
+ }
+
+ auto iter_pair = inline_export_map_.equal_range(std::make_pair(kind, index));
+ for (auto iter = iter_pair.first; iter != iter_pair.second; ++iter) {
+ const Export* export_ = iter->second;
+ WriteOpenSpace("export");
+ WriteQuotedString(export_->name, NextChar::None);
+ WriteCloseSpace();
+ }
+}
+
+bool WatWriter::IsInlineExport(const Export& export_) {
+ Index index{};
+ switch (export_.kind) {
+ case ExternalKind::Func:
+ index = module.GetFuncIndex(export_.var);
+ break;
+
+ case ExternalKind::Table:
+ index = module.GetTableIndex(export_.var);
+ break;
+
+ case ExternalKind::Memory:
+ index = module.GetMemoryIndex(export_.var);
+ break;
+
+ case ExternalKind::Global:
+ index = module.GetGlobalIndex(export_.var);
+ break;
+
+ case ExternalKind::Tag:
+ index = module.GetTagIndex(export_.var);
+ break;
+ }
+
+ return inline_export_map_.find(std::make_pair(export_.kind, index)) !=
+ inline_export_map_.end();
+}
+
+void WatWriter::BuildInlineImportMap() {
+ if (!options_.inline_import) {
+ return;
+ }
+
+ for (const Import* import : module.imports) {
+ inline_import_map_[static_cast<size_t>(import->kind())].push_back(import);
+ }
+}
+
+void WatWriter::WriteInlineImport(ExternalKind kind, Index index) {
+ if (!options_.inline_import) {
+ return;
+ }
+
+ size_t kind_index = static_cast<size_t>(kind);
+
+ if (index >= inline_import_map_[kind_index].size()) {
+ return;
+ }
+
+ const Import* import = inline_import_map_[kind_index][index];
+ WriteOpenSpace("import");
+ WriteQuotedString(import->module_name, NextChar::Space);
+ WriteQuotedString(import->field_name, NextChar::Space);
+ WriteCloseSpace();
+}
+
+} // end anonymous namespace
+
+Result WriteWat(Stream* stream,
+ const Module* module,
+ const WriteWatOptions& options) {
+ WatWriter wat_writer(stream, options, *module);
+ return wat_writer.WriteModule();
+}
+
+} // namespace wabt