diff options
Diffstat (limited to 'third_party/wasm2c/src')
134 files changed, 50859 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..321e263e40 --- /dev/null +++ b/third_party/wasm2c/src/apply-names.cc @@ -0,0 +1,510 @@ +/* + * 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 "src/apply-names.h" + +#include <cassert> +#include <cstdio> +#include <vector> + +#include "src/expr-visitor.h" +#include "src/ir.h" +#include "src/string-view.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 OnLocalGetExpr(LocalGetExpr*) override; + Result OnLocalSetExpr(LocalSetExpr*) override; + Result OnLocalTeeExpr(LocalTeeExpr*) override; + Result BeginLoopExpr(LoopExpr*) override; + Result EndLoopExpr(LoopExpr*) override; + Result OnDataDropExpr(DataDropExpr*) override; + Result OnMemoryInitExpr(MemoryInitExpr*) 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 BeginTryExpr(TryExpr*) override; + Result EndTryExpr(TryExpr*) override; + Result OnCatchExpr(TryExpr*, Catch*) override; + Result OnDelegateExpr(TryExpr*) override; + Result OnThrowExpr(ThrowExpr*) override; + Result OnRethrowExpr(RethrowExpr*) override; + + private: + void PushLabel(const std::string& label); + void PopLabel(); + string_view FindLabelByVar(Var* var); + void UseNameForVar(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(); +} + +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 string_view(); + } else { + if (var->index() >= labels_.size()) { + return string_view(); + } + return labels_[labels_.size() - 1 - var->index()]; + } +} + +void NameApplier::UseNameForVar(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::OnMemoryInitExpr(MemoryInitExpr* expr) { + CHECK_RESULT(UseNameForDataSegmentVar(&expr->var)); + 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::OnBrExpr(BrExpr* expr) { + string_view label = FindLabelByVar(&expr->var); + UseNameForVar(label, &expr->var); + return Result::Ok; +} + +Result NameApplier::OnBrIfExpr(BrIfExpr* expr) { + string_view label = FindLabelByVar(&expr->var); + UseNameForVar(label, &expr->var); + return Result::Ok; +} + +Result NameApplier::OnBrTableExpr(BrTableExpr* expr) { + for (Var& target : expr->targets) { + string_view label = FindLabelByVar(&target); + UseNameForVar(label, &target); + } + + 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) { + 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) { + 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::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::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, + ¶m_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_) { + if (export_->kind == ExternalKind::Func) { + UseNameForFuncVar(&export_->var); + } + 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 (ElemExpr& elem_expr : segment->elem_exprs) { + if (elem_expr.kind == ElemExprKind::RefFunc) { + CHECK_RESULT(UseNameForFuncVar(&elem_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/apply-names.h b/third_party/wasm2c/src/apply-names.h new file mode 100644 index 0000000000..1837c37076 --- /dev/null +++ b/third_party/wasm2c/src/apply-names.h @@ -0,0 +1,45 @@ +/* + * 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_APPLY_NAMES_H_ +#define WABT_APPLY_NAMES_H_ + +#include "src/common.h" + +namespace wabt { + +struct Module; + +/* Use function, import, function type, parameter and local names in Vars + * that reference them. + * + * e.g. transform this: + * + * (func $foo ...) + * ... + * (call 0 ...) + * + * to this: + * + * (func $foo ...) + * ... + * (call $foo ...) + */ +Result ApplyNames(struct Module*); + +} // namespace wabt + +#endif /* WABT_APPLY_NAMES_H_ */ 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..8290b95f5c --- /dev/null +++ b/third_party/wasm2c/src/binary-reader-ir.cc @@ -0,0 +1,1570 @@ +/* + * 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 "src/binary-reader-ir.h" + +#include <cassert> +#include <cinttypes> +#include <cstdarg> +#include <cstdint> +#include <cstdio> +#include <vector> + +#include "src/binary-reader-nop.h" +#include "src/cast.h" +#include "src/common.h" +#include "src/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 BinaryReaderIR : public BinaryReaderNop { + 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, + string_view module_name, + string_view field_name, + Index func_index, + Index sig_index) override; + Result OnImportTable(Index import_index, + string_view module_name, + string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) override; + Result OnImportMemory(Index import_index, + string_view module_name, + string_view field_name, + Index memory_index, + const Limits* page_limits) override; + Result OnImportGlobal(Index import_index, + string_view module_name, + string_view field_name, + Index global_index, + Type type, + bool mutable_) override; + Result OnImportTag(Index import_index, + string_view module_name, + 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, + 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 OnAtomicLoadExpr(Opcode opcode, + Address alignment_log2, + Address offset) override; + Result OnAtomicStoreExpr(Opcode opcode, + Address alignment_log2, + Address offset) override; + Result OnAtomicRmwExpr(Opcode opcode, + Address alignment_log2, + Address offset) override; + Result OnAtomicRmwCmpxchgExpr(Opcode opcode, + Address alignment_log2, + Address offset) override; + Result OnAtomicWaitExpr(Opcode opcode, + Address alignment_log2, + Address offset) override; + Result OnAtomicFenceExpr(uint32_t consistency_model) override; + Result OnAtomicNotifyExpr(Opcode opcode, + 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, + 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() override; + Result OnDataDropExpr(Index segment_index) override; + Result OnMemoryFillExpr() override; + Result OnMemoryGrowExpr() override; + Result OnMemoryInitExpr(Index segment_index) override; + Result OnMemorySizeExpr() 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, + 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, + Address alignment_log2, + Address offset, + uint64_t value) override; + Result OnSimdStoreLaneExpr(Opcode opcode, + Address alignment_log2, + Address offset, + uint64_t value) override; + Result OnSimdShuffleOpExpr(Opcode opcode, v128 value) override; + Result OnLoadSplatExpr(Opcode opcode, + Address alignment_log2, + Address offset) override; + Result OnLoadZeroExpr(Opcode opcode, + 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(string_view module_name) override; + Result OnFunctionNamesCount(Index num_functions) override; + Result OnFunctionName(Index function_index, + string_view function_name) override; + Result OnLocalNameLocalCount(Index function_index, Index num_locals) override; + Result OnLocalName(Index function_index, + Index local_index, + string_view local_name) override; + Result OnNameEntry(NameSectionSubsection type, + Index index, + 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 OnInitExprF32ConstExpr(Index index, uint32_t value) override; + Result OnInitExprF64ConstExpr(Index index, uint64_t value) override; + Result OnInitExprV128ConstExpr(Index index, v128 value) override; + Result OnInitExprGlobalGetExpr(Index index, Index global_index) override; + Result OnInitExprI32ConstExpr(Index index, uint32_t value) override; + Result OnInitExprI64ConstExpr(Index index, uint64_t value) override; + Result OnInitExprRefNull(Index index, Type type) override; + Result OnInitExprRefFunc(Index index, Index func_index) override; + + Result OnDataSymbol(Index index, uint32_t flags, string_view name, + Index segment, uint32_t offset, uint32_t size) override; + Result OnFunctionSymbol(Index index, uint32_t flags, string_view name, + Index func_index) override; + Result OnGlobalSymbol(Index index, uint32_t flags, 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, + string_view name, + Index tag_index) override; + Result OnTableSymbol(Index index, uint32_t flags, string_view name, + Index table_index) override; + + private: + Location GetLocation() const; + void PrintError(const char* format, ...); + void PushLabel(LabelType label_type, + ExprList* first, + Expr* context = nullptr); + 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, string_view name); + Result SetTableName(Index index, string_view name); + Result SetFunctionName(Index index, string_view name); + Result SetGlobalName(Index index, string_view name); + Result SetDataSegmentName(Index index, string_view name); + Result SetElemSegmentName(Index index, 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_; + ExprList* current_init_expr_ = nullptr; + const char* filename_; +}; + +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); +} + +void BinaryReaderIR::PushLabel(LabelType label_type, + ExprList* first, + Expr* context) { + label_stack_.emplace_back(label_type, first, context); +} + +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)); + *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)); + } 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) { + auto field = MakeUnique<TypeModuleField>(GetLocation()); + auto func_type = MakeUnique<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); + 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 = MakeUnique<TypeModuleField>(GetLocation()); + auto struct_type = MakeUnique<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_; + } + field->type = std::move(struct_type); + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::OnArrayType(Index index, TypeMut type_mut) { + auto field = MakeUnique<TypeModuleField>(GetLocation()); + auto array_type = MakeUnique<ArrayType>(); + array_type->field.type = type_mut.type; + array_type->field.mutable_ = type_mut.mutable_; + 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, + string_view module_name, + string_view field_name, + Index func_index, + Index sig_index) { + auto import = MakeUnique<FuncImport>(); + import->module_name = module_name.to_string(); + import->field_name = field_name.to_string(); + SetFuncDeclaration(&import->func.decl, Var(sig_index, GetLocation())); + module_->AppendField( + MakeUnique<ImportModuleField>(std::move(import), GetLocation())); + return Result::Ok; +} + +Result BinaryReaderIR::OnImportTable(Index import_index, + string_view module_name, + string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) { + auto import = MakeUnique<TableImport>(); + import->module_name = module_name.to_string(); + import->field_name = field_name.to_string(); + import->table.elem_limits = *elem_limits; + import->table.elem_type = elem_type; + module_->AppendField( + MakeUnique<ImportModuleField>(std::move(import), GetLocation())); + return Result::Ok; +} + +Result BinaryReaderIR::OnImportMemory(Index import_index, + string_view module_name, + string_view field_name, + Index memory_index, + const Limits* page_limits) { + auto import = MakeUnique<MemoryImport>(); + import->module_name = module_name.to_string(); + import->field_name = field_name.to_string(); + import->memory.page_limits = *page_limits; + module_->AppendField( + MakeUnique<ImportModuleField>(std::move(import), GetLocation())); + return Result::Ok; +} + +Result BinaryReaderIR::OnImportGlobal(Index import_index, + string_view module_name, + string_view field_name, + Index global_index, + Type type, + bool mutable_) { + auto import = MakeUnique<GlobalImport>(); + import->module_name = module_name.to_string(); + import->field_name = field_name.to_string(); + import->global.type = type; + import->global.mutable_ = mutable_; + module_->AppendField( + MakeUnique<ImportModuleField>(std::move(import), GetLocation())); + return Result::Ok; +} + +Result BinaryReaderIR::OnImportTag(Index import_index, + string_view module_name, + string_view field_name, + Index tag_index, + Index sig_index) { + auto import = MakeUnique<TagImport>(); + import->module_name = module_name.to_string(); + import->field_name = field_name.to_string(); + SetFuncDeclaration(&import->tag.decl, Var(sig_index, GetLocation())); + module_->AppendField( + MakeUnique<ImportModuleField>(std::move(import), GetLocation())); + 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 = MakeUnique<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 = MakeUnique<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 = MakeUnique<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 = MakeUnique<GlobalModuleField>(GetLocation()); + Global& global = field->global; + global.type = type; + global.mutable_ = mutable_; + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::BeginGlobalInitExpr(Index index) { + assert(index == module_->globals.size() - 1); + Global* global = module_->globals[index]; + current_init_expr_ = &global->init_expr; + return Result::Ok; +} + +Result BinaryReaderIR::EndGlobalInitExpr(Index index) { + current_init_expr_ = nullptr; + return Result::Ok; +} + +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, + string_view name) { + auto field = MakeUnique<ExportModuleField>(GetLocation()); + Export& export_ = field->export_; + export_.name = name.to_string(); + 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(MakeUnique<StartModuleField>(start, GetLocation())); + return Result::Ok; +} + +Result BinaryReaderIR::OnFunctionBodyCount(Index count) { + assert(module_->num_func_imports + count == module_->funcs.size()); + return Result::Ok; +} + +Result BinaryReaderIR::BeginFunctionBody(Index index, Offset size) { + current_func_ = module_->funcs[index]; + PushLabel(LabelType::Func, ¤t_func_->exprs); + return Result::Ok; +} + +Result BinaryReaderIR::OnLocalDecl(Index decl_index, Index count, Type type) { + current_func_->local_types.AppendDecl(type, count); + return Result::Ok; +} + +Result BinaryReaderIR::OnAtomicLoadExpr(Opcode opcode, + Address alignment_log2, + Address offset) { + return AppendExpr( + MakeUnique<AtomicLoadExpr>(opcode, 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnAtomicStoreExpr(Opcode opcode, + Address alignment_log2, + Address offset) { + return AppendExpr( + MakeUnique<AtomicStoreExpr>(opcode, 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnAtomicRmwExpr(Opcode opcode, + Address alignment_log2, + Address offset) { + return AppendExpr( + MakeUnique<AtomicRmwExpr>(opcode, 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnAtomicRmwCmpxchgExpr(Opcode opcode, + Address alignment_log2, + Address offset) { + return AppendExpr( + MakeUnique<AtomicRmwCmpxchgExpr>(opcode, 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnAtomicWaitExpr(Opcode opcode, + Address alignment_log2, + Address offset) { + return AppendExpr( + MakeUnique<AtomicWaitExpr>(opcode, 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnAtomicFenceExpr(uint32_t consistency_model) { + return AppendExpr(MakeUnique<AtomicFenceExpr>(consistency_model)); +} + +Result BinaryReaderIR::OnAtomicNotifyExpr(Opcode opcode, + Address alignment_log2, + Address offset) { + return AppendExpr( + MakeUnique<AtomicNotifyExpr>(opcode, 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnBinaryExpr(Opcode opcode) { + return AppendExpr(MakeUnique<BinaryExpr>(opcode)); +} + +Result BinaryReaderIR::OnBlockExpr(Type sig_type) { + auto expr = MakeUnique<BlockExpr>(); + SetBlockDeclaration(&expr->block.decl, sig_type); + ExprList* expr_list = &expr->block.exprs; + CHECK_RESULT(AppendExpr(std::move(expr))); + PushLabel(LabelType::Block, expr_list); + return Result::Ok; +} + +Result BinaryReaderIR::OnBrExpr(Index depth) { + return AppendExpr(MakeUnique<BrExpr>(Var(depth))); +} + +Result BinaryReaderIR::OnBrIfExpr(Index depth) { + return AppendExpr(MakeUnique<BrIfExpr>(Var(depth))); +} + +Result BinaryReaderIR::OnBrTableExpr(Index num_targets, + Index* target_depths, + Index default_target_depth) { + auto expr = MakeUnique<BrTableExpr>(); + expr->default_target = Var(default_target_depth); + expr->targets.resize(num_targets); + for (Index i = 0; i < num_targets; ++i) { + expr->targets[i] = Var(target_depths[i]); + } + return AppendExpr(std::move(expr)); +} + +Result BinaryReaderIR::OnCallExpr(Index func_index) { + return AppendExpr(MakeUnique<CallExpr>(Var(func_index))); +} + +Result BinaryReaderIR::OnCallIndirectExpr(Index sig_index, Index table_index) { + auto expr = MakeUnique<CallIndirectExpr>(); + SetFuncDeclaration(&expr->decl, Var(sig_index, GetLocation())); + expr->table = Var(table_index); + return AppendExpr(std::move(expr)); +} + +Result BinaryReaderIR::OnCallRefExpr() { + return AppendExpr(MakeUnique<CallRefExpr>()); +} + +Result BinaryReaderIR::OnReturnCallExpr(Index func_index) { + return AppendExpr(MakeUnique<ReturnCallExpr>(Var(func_index))); +} + +Result BinaryReaderIR::OnReturnCallIndirectExpr(Index sig_index, Index table_index) { + auto expr = MakeUnique<ReturnCallIndirectExpr>(); + SetFuncDeclaration(&expr->decl, Var(sig_index, GetLocation())); + expr->table = Var(table_index); + return AppendExpr(std::move(expr)); +} + +Result BinaryReaderIR::OnCompareExpr(Opcode opcode) { + return AppendExpr(MakeUnique<CompareExpr>(opcode)); +} + +Result BinaryReaderIR::OnConvertExpr(Opcode opcode) { + return AppendExpr(MakeUnique<ConvertExpr>(opcode)); +} + +Result BinaryReaderIR::OnDropExpr() { + return AppendExpr(MakeUnique<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() { + 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::Func: + case LabelType::Catch: + break; + } + + return PopLabel(); +} + +Result BinaryReaderIR::OnF32ConstExpr(uint32_t value_bits) { + return AppendExpr( + MakeUnique<ConstExpr>(Const::F32(value_bits, GetLocation()))); +} + +Result BinaryReaderIR::OnF64ConstExpr(uint64_t value_bits) { + return AppendExpr( + MakeUnique<ConstExpr>(Const::F64(value_bits, GetLocation()))); +} + +Result BinaryReaderIR::OnV128ConstExpr(v128 value_bits) { + return AppendExpr( + MakeUnique<ConstExpr>(Const::V128(value_bits, GetLocation()))); +} + +Result BinaryReaderIR::OnGlobalGetExpr(Index global_index) { + return AppendExpr( + MakeUnique<GlobalGetExpr>(Var(global_index, GetLocation()))); +} + +Result BinaryReaderIR::OnLocalGetExpr(Index local_index) { + return AppendExpr(MakeUnique<LocalGetExpr>(Var(local_index, GetLocation()))); +} + +Result BinaryReaderIR::OnI32ConstExpr(uint32_t value) { + return AppendExpr(MakeUnique<ConstExpr>(Const::I32(value, GetLocation()))); +} + +Result BinaryReaderIR::OnI64ConstExpr(uint64_t value) { + return AppendExpr(MakeUnique<ConstExpr>(Const::I64(value, GetLocation()))); +} + +Result BinaryReaderIR::OnIfExpr(Type sig_type) { + auto expr = MakeUnique<IfExpr>(); + SetBlockDeclaration(&expr->true_.decl, sig_type); + ExprList* expr_list = &expr->true_.exprs; + CHECK_RESULT(AppendExpr(std::move(expr))); + PushLabel(LabelType::If, expr_list); + return Result::Ok; +} + +Result BinaryReaderIR::OnLoadExpr(Opcode opcode, + Address alignment_log2, + Address offset) { + return AppendExpr(MakeUnique<LoadExpr>(opcode, 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnLoopExpr(Type sig_type) { + auto expr = MakeUnique<LoopExpr>(); + SetBlockDeclaration(&expr->block.decl, sig_type); + ExprList* expr_list = &expr->block.exprs; + CHECK_RESULT(AppendExpr(std::move(expr))); + PushLabel(LabelType::Loop, expr_list); + return Result::Ok; +} + +Result BinaryReaderIR::OnMemoryCopyExpr() { + return AppendExpr(MakeUnique<MemoryCopyExpr>()); +} + +Result BinaryReaderIR::OnDataDropExpr(Index segment) { + return AppendExpr(MakeUnique<DataDropExpr>(Var(segment))); +} + +Result BinaryReaderIR::OnMemoryFillExpr() { + return AppendExpr(MakeUnique<MemoryFillExpr>()); +} + +Result BinaryReaderIR::OnMemoryGrowExpr() { + return AppendExpr(MakeUnique<MemoryGrowExpr>()); +} + +Result BinaryReaderIR::OnMemoryInitExpr(Index segment) { + return AppendExpr(MakeUnique<MemoryInitExpr>(Var(segment))); +} + +Result BinaryReaderIR::OnMemorySizeExpr() { + return AppendExpr(MakeUnique<MemorySizeExpr>()); +} + +Result BinaryReaderIR::OnTableCopyExpr(Index dst_index, Index src_index) { + return AppendExpr(MakeUnique<TableCopyExpr>(Var(dst_index), Var(src_index))); +} + +Result BinaryReaderIR::OnElemDropExpr(Index segment) { + return AppendExpr(MakeUnique<ElemDropExpr>(Var(segment))); +} + +Result BinaryReaderIR::OnTableInitExpr(Index segment, Index table_index) { + return AppendExpr(MakeUnique<TableInitExpr>(Var(segment), Var(table_index))); +} + +Result BinaryReaderIR::OnTableGetExpr(Index table_index) { + return AppendExpr(MakeUnique<TableGetExpr>(Var(table_index))); +} + +Result BinaryReaderIR::OnTableSetExpr(Index table_index) { + return AppendExpr(MakeUnique<TableSetExpr>(Var(table_index))); +} + +Result BinaryReaderIR::OnTableGrowExpr(Index table_index) { + return AppendExpr(MakeUnique<TableGrowExpr>(Var(table_index))); +} + +Result BinaryReaderIR::OnTableSizeExpr(Index table_index) { + return AppendExpr(MakeUnique<TableSizeExpr>(Var(table_index))); +} + +Result BinaryReaderIR::OnTableFillExpr(Index table_index) { + return AppendExpr(MakeUnique<TableFillExpr>(Var(table_index))); +} + +Result BinaryReaderIR::OnRefFuncExpr(Index func_index) { + return AppendExpr(MakeUnique<RefFuncExpr>(Var(func_index))); +} + +Result BinaryReaderIR::OnRefNullExpr(Type type) { + return AppendExpr(MakeUnique<RefNullExpr>(type)); +} + +Result BinaryReaderIR::OnRefIsNullExpr() { + return AppendExpr(MakeUnique<RefIsNullExpr>()); +} + +Result BinaryReaderIR::OnNopExpr() { + return AppendExpr(MakeUnique<NopExpr>()); +} + +Result BinaryReaderIR::OnRethrowExpr(Index depth) { + return AppendExpr(MakeUnique<RethrowExpr>(Var(depth, GetLocation()))); +} + +Result BinaryReaderIR::OnReturnExpr() { + return AppendExpr(MakeUnique<ReturnExpr>()); +} + +Result BinaryReaderIR::OnSelectExpr(Index result_count, Type* result_types) { + TypeVector results; + results.assign(result_types, result_types + result_count); + return AppendExpr(MakeUnique<SelectExpr>(results)); +} + +Result BinaryReaderIR::OnGlobalSetExpr(Index global_index) { + return AppendExpr( + MakeUnique<GlobalSetExpr>(Var(global_index, GetLocation()))); +} + +Result BinaryReaderIR::OnLocalSetExpr(Index local_index) { + return AppendExpr(MakeUnique<LocalSetExpr>(Var(local_index, GetLocation()))); +} + +Result BinaryReaderIR::OnStoreExpr(Opcode opcode, + Address alignment_log2, + Address offset) { + return AppendExpr(MakeUnique<StoreExpr>(opcode, 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnThrowExpr(Index tag_index) { + return AppendExpr(MakeUnique<ThrowExpr>(Var(tag_index, GetLocation()))); +} + +Result BinaryReaderIR::OnLocalTeeExpr(Index local_index) { + return AppendExpr(MakeUnique<LocalTeeExpr>(Var(local_index, GetLocation()))); +} + +Result BinaryReaderIR::OnTryExpr(Type sig_type) { + auto expr_ptr = MakeUnique<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))); + PushLabel(LabelType::Try, expr_list, expr); + return Result::Ok; +} + +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(MakeUnique<UnaryExpr>(opcode)); +} + +Result BinaryReaderIR::OnTernaryExpr(Opcode opcode) { + return AppendExpr(MakeUnique<TernaryExpr>(opcode)); +} + +Result BinaryReaderIR::OnUnreachableExpr() { + return AppendExpr(MakeUnique<UnreachableExpr>()); +} + +Result BinaryReaderIR::EndFunctionBody(Index index) { + CHECK_RESULT(PopLabel()); + current_func_ = nullptr; + return Result::Ok; +} + +Result BinaryReaderIR::OnSimdLaneOpExpr(Opcode opcode, uint64_t value) { + return AppendExpr(MakeUnique<SimdLaneOpExpr>(opcode, value)); +} + +Result BinaryReaderIR::OnSimdLoadLaneExpr(Opcode opcode, + Address alignment_log2, + Address offset, + uint64_t value) { + return AppendExpr( + MakeUnique<SimdLoadLaneExpr>(opcode, 1 << alignment_log2, offset, value)); +} + +Result BinaryReaderIR::OnSimdStoreLaneExpr(Opcode opcode, + Address alignment_log2, + Address offset, + uint64_t value) { + return AppendExpr( + MakeUnique<SimdStoreLaneExpr>(opcode, 1 << alignment_log2, offset, value)); +} + +Result BinaryReaderIR::OnSimdShuffleOpExpr(Opcode opcode, v128 value) { + return AppendExpr(MakeUnique<SimdShuffleOpExpr>(opcode, value)); +} + +Result BinaryReaderIR::OnLoadSplatExpr(Opcode opcode, + Address alignment_log2, + Address offset) { + return AppendExpr( + MakeUnique<LoadSplatExpr>(opcode, 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnLoadZeroExpr(Opcode opcode, + Address alignment_log2, + Address offset) { + return AppendExpr( + MakeUnique<LoadZeroExpr>(opcode, 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 = MakeUnique<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::BeginElemSegmentInitExpr(Index index) { + assert(index == module_->elem_segments.size() - 1); + ElemSegment* segment = module_->elem_segments[index]; + current_init_expr_ = &segment->offset; + return Result::Ok; +} + +Result BinaryReaderIR::EndElemSegmentInitExpr(Index index) { + current_init_expr_ = nullptr; + return Result::Ok; +} + +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]; + segment->elem_exprs.emplace_back(type); + 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]; + segment->elem_exprs.emplace_back(Var(func_index, GetLocation())); + 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 = MakeUnique<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]; + current_init_expr_ = &segment->offset; + return Result::Ok; +} + +Result BinaryReaderIR::EndDataSegmentInitExpr(Index index) { + current_init_expr_ = nullptr; + return Result::Ok; +} + +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(string_view name) { + return std::string("$") + name.to_string(); +} + +Result BinaryReaderIR::OnModuleName(string_view name) { + if (name.empty()) { + return Result::Ok; + } + + module_->name = MakeDollarName(name); + return Result::Ok; +} + +Result BinaryReaderIR::SetGlobalName(Index index, 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, 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::SetTableName(Index index, 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, 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, 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, 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::OnFunctionName(Index index, string_view name) { + return SetFunctionName(index, name); +} + +Result BinaryReaderIR::OnNameEntry(NameSectionSubsection type, + Index index, + 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: + case NameSectionSubsection::Type: + 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::OnInitExprF32ConstExpr(Index index, uint32_t value) { + Location loc = GetLocation(); + current_init_expr_->push_back( + MakeUnique<ConstExpr>(Const::F32(value, loc), loc)); + return Result::Ok; +} + +Result BinaryReaderIR::OnInitExprF64ConstExpr(Index index, uint64_t value) { + Location loc = GetLocation(); + current_init_expr_->push_back( + MakeUnique<ConstExpr>(Const::F64(value, loc), loc)); + return Result::Ok; +} + +Result BinaryReaderIR::OnInitExprV128ConstExpr(Index index, v128 value) { + Location loc = GetLocation(); + current_init_expr_->push_back( + MakeUnique<ConstExpr>(Const::V128(value, loc), loc)); + return Result::Ok; +} + +Result BinaryReaderIR::OnInitExprGlobalGetExpr(Index index, + Index global_index) { + Location loc = GetLocation(); + current_init_expr_->push_back( + MakeUnique<GlobalGetExpr>(Var(global_index, loc), loc)); + return Result::Ok; +} + +Result BinaryReaderIR::OnInitExprI32ConstExpr(Index index, uint32_t value) { + Location loc = GetLocation(); + current_init_expr_->push_back( + MakeUnique<ConstExpr>(Const::I32(value, loc), loc)); + return Result::Ok; +} + +Result BinaryReaderIR::OnInitExprI64ConstExpr(Index index, uint64_t value) { + Location loc = GetLocation(); + current_init_expr_->push_back( + MakeUnique<ConstExpr>(Const::I64(value, loc), loc)); + return Result::Ok; +} + +Result BinaryReaderIR::OnInitExprRefNull(Index index, Type type) { + Location loc = GetLocation(); + current_init_expr_->push_back(MakeUnique<RefNullExpr>(type, loc)); + return Result::Ok; +} + +Result BinaryReaderIR::OnInitExprRefFunc(Index index, Index func_index) { + Location loc = GetLocation(); + current_init_expr_->push_back( + MakeUnique<RefFuncExpr>(Var(func_index, loc), loc)); + return Result::Ok; +} + +Result BinaryReaderIR::OnLocalName(Index func_index, + Index local_index, + 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 = MakeUnique<TagModuleField>(GetLocation()); + Tag& tag = field->tag; + SetFuncDeclaration(&tag.decl, Var(sig_index, GetLocation())); + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::OnDataSymbol(Index index, uint32_t flags, + 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, + 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, + 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, + 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, + string_view name, Index table_index) { + return SetTableName(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-ir.h b/third_party/wasm2c/src/binary-reader-ir.h new file mode 100644 index 0000000000..18c15585b9 --- /dev/null +++ b/third_party/wasm2c/src/binary-reader-ir.h @@ -0,0 +1,37 @@ +/* + * 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_BINARY_READER_IR_H_ +#define WABT_BINARY_READER_IR_H_ + +#include "src/common.h" +#include "src/error.h" + +namespace wabt { + +struct Module; +struct ReadBinaryOptions; + +Result ReadBinaryIr(const char* filename, + const void* data, + size_t size, + const ReadBinaryOptions& options, + Errors*, + Module* out_module); + +} // namespace wabt + +#endif /* WABT_BINARY_READER_IR_H_ */ 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..77ee5736cf --- /dev/null +++ b/third_party/wasm2c/src/binary-reader-logging.cc @@ -0,0 +1,960 @@ +/* + * 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 "src/binary-reader-logging.h" + +#include <cinttypes> + +#include "src/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()); + } +} + +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, + 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, + string_view module_name, + 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, + string_view module_name, + 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, + string_view module_name, + 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(), buf); + return reader_->OnImportTable(import_index, module_name, field_name, + table_index, elem_type, elem_limits); +} + +Result BinaryReaderLogging::OnImportMemory(Index import_index, + string_view module_name, + 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, + string_view module_name, + 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(), mutable_ ? "true" : "false"); + return reader_->OnImportGlobal(import_index, module_name, field_name, + global_index, type, mutable_); +} + +Result BinaryReaderLogging::OnImportTag(Index import_index, + string_view module_name, + 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(), 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(), mutable_ ? "true" : "false"); + return reader_->BeginGlobal(index, type, mutable_); +} + +Result BinaryReaderLogging::OnExport(Index index, + ExternalKind kind, + Index item_index, + 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()); + 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()); + 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(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, 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, + 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, + 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::OnInitExprF32ConstExpr(Index index, + uint32_t value_bits) { + float value; + memcpy(&value, &value_bits, sizeof(value)); + LOGF("OnInitExprF32ConstExpr(index: %" PRIindex ", value: %g (0x04%x))\n", + index, value, value_bits); + return reader_->OnInitExprF32ConstExpr(index, value_bits); +} + +Result BinaryReaderLogging::OnInitExprF64ConstExpr(Index index, + uint64_t value_bits) { + double value; + memcpy(&value, &value_bits, sizeof(value)); + LOGF("OnInitExprF64ConstExpr(index: %" PRIindex " value: %g (0x08%" PRIx64 + "))\n", + index, value, value_bits); + return reader_->OnInitExprF64ConstExpr(index, value_bits); +} + +Result BinaryReaderLogging::OnInitExprV128ConstExpr(Index index, + v128 value_bits) { + LOGF("OnInitExprV128ConstExpr(index: %" PRIindex + " value: ( 0x%08x 0x%08x 0x%08x 0x%08x))\n", + index, value_bits.u32(0), value_bits.u32(1), value_bits.u32(2), + value_bits.u32(3)); + return reader_->OnInitExprV128ConstExpr(index, value_bits); +} + +Result BinaryReaderLogging::OnInitExprI32ConstExpr(Index index, + uint32_t value) { + LOGF("OnInitExprI32ConstExpr(index: %" PRIindex ", value: %u)\n", index, + value); + return reader_->OnInitExprI32ConstExpr(index, value); +} + +Result BinaryReaderLogging::OnInitExprI64ConstExpr(Index index, + uint64_t value) { + LOGF("OnInitExprI64ConstExpr(index: %" PRIindex ", value: %" PRIu64 ")\n", + index, value); + return reader_->OnInitExprI64ConstExpr(index, value); +} + +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(string_view so_name) { + LOGF("OnDylinkNeeded(name: " PRIstringview ")\n", + WABT_PRINTF_STRING_VIEW_ARG(so_name)); + return reader_->OnDylinkNeeded(so_name); +} + +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::OnDataSymbol(Index index, + uint32_t flags, + 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, + 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, + 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, + 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, + 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, + 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 func_index) { + LOGF("OnInitFunction(%d priority: %d)\n", func_index, priority); + return reader_->OnInitFunction(priority, func_index); +} + +Result BinaryReaderLogging::OnComdatBegin(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); +} + +#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()); \ + 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()); \ + 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, Address alignment_log2, \ + Address offset) { \ + LOGF(#name "(opcode: \"%s\" (%u), align log2: %" PRIaddress \ + ", offset: %" PRIaddress ")\n", \ + opcode.GetName(), opcode.GetCode(), alignment_log2, offset); \ + return reader_->name(opcode, alignment_log2, offset); \ + } + +#define DEFINE_SIMD_LOAD_STORE_LANE_OPCODE(name) \ + Result BinaryReaderLogging::name(Opcode opcode, Address alignment_log2, \ + Address offset, uint64_t value) { \ + LOGF(#name "(opcode: \"%s\" (%u), align log2: %" PRIaddress \ + ", offset: %" PRIaddress ", lane: %" PRIu64 ")\n", \ + opcode.GetName(), opcode.GetCode(), alignment_log2, offset, value); \ + return reader_->name(opcode, 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") +DEFINE0(OnMemoryCopyExpr) +DEFINE_INDEX(OnDataDropExpr) +DEFINE0(OnMemoryFillExpr) +DEFINE0(OnMemoryGrowExpr) +DEFINE_INDEX(OnMemoryInitExpr) +DEFINE0(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_INDEX_INDEX(OnInitExprGlobalGetExpr, "index", "global_index") +DEFINE_INDEX_TYPE(OnInitExprRefNull) +DEFINE_INDEX_INDEX(OnInitExprRefFunc, "index", "func_index") + +DEFINE_BEGIN(BeginDylinkSection) +DEFINE_INDEX(OnDylinkNeededCount) +DEFINE_END(EndDylinkSection) + +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); + +// 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::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); +} + +Result BinaryReaderLogging::OnEndFunc() { + return reader_->OnEndFunc(); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary-reader-logging.h b/third_party/wasm2c/src/binary-reader-logging.h new file mode 100644 index 0000000000..423a321cee --- /dev/null +++ b/third_party/wasm2c/src/binary-reader-logging.h @@ -0,0 +1,398 @@ +/* + * 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. + */ + +#ifndef WABT_BINARY_READER_LOGGING_H_ +#define WABT_BINARY_READER_LOGGING_H_ + +#include "src/binary-reader.h" + +namespace wabt { + +class Stream; + +class BinaryReaderLogging : public BinaryReaderDelegate { + public: + BinaryReaderLogging(Stream*, BinaryReaderDelegate* forward); + + bool OnError(const Error&) override; + void OnSetState(const State* s) override; + + Result BeginModule(uint32_t version) override; + Result EndModule() override; + + Result BeginSection(Index section_index, + BinarySection section_type, + Offset size) override; + + Result BeginCustomSection(Index section_index, + Offset size, + string_view section_name) override; + Result EndCustomSection() override; + + Result BeginTypeSection(Offset size) 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 EndTypeSection() override; + + Result BeginImportSection(Offset size) override; + Result OnImportCount(Index count) override; + Result OnImport(Index index, + ExternalKind kind, + string_view module_name, + string_view field_name) override; + Result OnImportFunc(Index import_index, + string_view module_name, + string_view field_name, + Index func_index, + Index sig_index) override; + Result OnImportTable(Index import_index, + string_view module_name, + string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) override; + Result OnImportMemory(Index import_index, + string_view module_name, + string_view field_name, + Index memory_index, + const Limits* page_limits) override; + Result OnImportGlobal(Index import_index, + string_view module_name, + string_view field_name, + Index global_index, + Type type, + bool mutable_) override; + Result OnImportTag(Index import_index, + string_view module_name, + string_view field_name, + Index tag_index, + Index sig_index) override; + Result EndImportSection() override; + + Result BeginFunctionSection(Offset size) override; + Result OnFunctionCount(Index count) override; + Result OnFunction(Index index, Index sig_index) override; + Result EndFunctionSection() override; + + Result BeginTableSection(Offset size) override; + Result OnTableCount(Index count) override; + Result OnTable(Index index, + Type elem_type, + const Limits* elem_limits) override; + Result EndTableSection() override; + + Result BeginMemorySection(Offset size) override; + Result OnMemoryCount(Index count) override; + Result OnMemory(Index index, const Limits* limits) override; + Result EndMemorySection() override; + + Result BeginGlobalSection(Offset size) 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 EndGlobal(Index index) override; + Result EndGlobalSection() override; + + Result BeginExportSection(Offset size) override; + Result OnExportCount(Index count) override; + Result OnExport(Index index, + ExternalKind kind, + Index item_index, + string_view name) override; + Result EndExportSection() override; + + Result BeginStartSection(Offset size) override; + Result OnStartFunction(Index func_index) override; + Result EndStartSection() override; + + Result BeginCodeSection(Offset size) override; + Result OnFunctionBodyCount(Index count) override; + Result BeginFunctionBody(Index index, Offset size) 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 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 OnOpcodeType(Type type) override; + Result OnAtomicLoadExpr(Opcode opcode, + Address alignment_log2, + Address offset) override; + Result OnAtomicStoreExpr(Opcode opcode, + Address alignment_log2, + Address offset) override; + Result OnAtomicRmwExpr(Opcode opcode, + Address alignment_log2, + Address offset) override; + Result OnAtomicRmwCmpxchgExpr(Opcode opcode, + 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 OnCompareExpr(Opcode opcode) override; + Result OnConvertExpr(Opcode opcode) override; + Result OnDelegateExpr(Index depth) override; + Result OnDropExpr() override; + Result OnElseExpr() override; + Result OnEndExpr() override; + Result OnEndFunc() 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, + 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() override; + Result OnDataDropExpr(Index segment_index) override; + Result OnMemoryFillExpr() override; + Result OnMemoryGrowExpr() override; + Result OnMemoryInitExpr(Index segment_index) override; + Result OnMemorySizeExpr() 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) override; + Result OnTableSetExpr(Index table) override; + Result OnTableGrowExpr(Index table) override; + Result OnTableSizeExpr(Index table) override; + Result OnTableFillExpr(Index table) override; + Result OnRefFuncExpr(Index index) override; + Result OnRefNullExpr(Type type) override; + Result OnRefIsNullExpr() override; + Result OnNopExpr() override; + Result OnRethrowExpr(Index depth) override; + Result OnReturnCallExpr(Index func_index) override; + Result OnReturnCallIndirectExpr(Index sig_index, Index table_index) override; + Result OnReturnExpr() override; + Result OnSelectExpr(Index result_count, Type* result_types) override; + Result OnStoreExpr(Opcode opcode, + 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 OnAtomicWaitExpr(Opcode opcode, + Address alignment_log2, + Address offset) override; + Result OnAtomicFenceExpr(uint32_t consistency_model) override; + Result OnAtomicNotifyExpr(Opcode opcode, + Address alignment_log2, + Address offset) override; + Result EndFunctionBody(Index index) override; + Result EndCodeSection() override; + Result OnSimdLaneOpExpr(Opcode opcode, uint64_t value) override; + Result OnSimdLoadLaneExpr(Opcode opcode, + Address alignment_log2, + Address offset, + uint64_t value) override; + Result OnSimdStoreLaneExpr(Opcode opcode, + Address alignment_log2, + Address offset, + uint64_t value) override; + Result OnSimdShuffleOpExpr(Opcode opcode, v128 value) override; + Result OnLoadSplatExpr(Opcode opcode, + Address alignment_log2, + Address offset) override; + Result OnLoadZeroExpr(Opcode opcode, + Address alignment_log2, + Address offset) override; + + Result BeginElemSection(Offset size) 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 EndElemSegment(Index index) override; + Result EndElemSection() override; + + Result BeginDataSection(Offset size) 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 EndDataSegment(Index index) override; + Result EndDataSection() override; + + Result BeginDataCountSection(Offset size) override; + Result OnDataCount(Index count) override; + Result EndDataCountSection() override; + + Result BeginNamesSection(Offset size) override; + Result OnModuleNameSubsection(Index index, + uint32_t name_type, + Offset subsection_size) override; + Result OnModuleName(string_view name) override; + Result OnFunctionNameSubsection(Index index, + uint32_t name_type, + Offset subsection_size) override; + Result OnFunctionNamesCount(Index num_functions) override; + Result OnFunctionName(Index function_index, + string_view function_name) override; + Result OnLocalNameSubsection(Index index, + uint32_t name_type, + Offset subsection_size) override; + Result OnLocalNameFunctionCount(Index num_functions) override; + Result OnLocalNameLocalCount(Index function_index, Index num_locals) override; + Result OnLocalName(Index function_index, + Index local_index, + string_view local_name) override; + Result OnNameSubsection(Index index, + NameSectionSubsection subsection_type, + Offset subsection_size) override; + Result OnNameEntry(NameSectionSubsection type, + Index index, + string_view name) override; + Result OnNameCount(Index num_names) override; + Result EndNamesSection() override; + + Result BeginRelocSection(Offset size) override; + Result OnRelocCount(Index count, Index section_index) override; + Result OnReloc(RelocType type, + Offset offset, + Index index, + uint32_t addend) override; + Result EndRelocSection() override; + + Result BeginDylinkSection(Offset size) override; + Result OnDylinkInfo(uint32_t mem_size, + uint32_t mem_align, + uint32_t table_size, + uint32_t table_align) override; + Result OnDylinkNeededCount(Index count) override; + Result OnDylinkNeeded(string_view needed) override; + Result EndDylinkSection() override; + + Result BeginLinkingSection(Offset size) override; + Result OnSymbolCount(Index count) override; + Result OnDataSymbol(Index index, + uint32_t flags, + string_view name, + Index segment, + uint32_t offset, + uint32_t size) override; + Result OnFunctionSymbol(Index index, + uint32_t flags, + string_view name, + Index func_index) override; + Result OnGlobalSymbol(Index index, + uint32_t flags, + 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, + string_view name, + Index tag_index) override; + Result OnTableSymbol(Index index, + uint32_t flags, + string_view name, + Index tag_index) override; + Result OnSegmentInfoCount(Index count) override; + Result OnSegmentInfo(Index index, + string_view name, + Address alignment, + uint32_t flags) override; + Result OnInitFunctionCount(Index count) override; + Result OnInitFunction(uint32_t priority, Index function_index) override; + Result OnComdatCount(Index count) override; + Result OnComdatBegin(string_view name, uint32_t flags, Index count) override; + Result OnComdatEntry(ComdatType kind, Index index) override; + Result EndLinkingSection() override; + + Result BeginTagSection(Offset size) override; + Result OnTagCount(Index count) override; + Result OnTagType(Index index, Index sig_index) override; + Result EndTagSection() override; + + Result OnInitExprF32ConstExpr(Index index, uint32_t value) override; + Result OnInitExprF64ConstExpr(Index index, uint64_t value) override; + Result OnInitExprV128ConstExpr(Index index, v128 value) override; + Result OnInitExprGlobalGetExpr(Index index, Index global_index) override; + Result OnInitExprI32ConstExpr(Index index, uint32_t value) override; + Result OnInitExprI64ConstExpr(Index index, uint64_t value) override; + Result OnInitExprRefNull(Index index, Type type) override; + Result OnInitExprRefFunc(Index index, Index func_index) override; + + private: + void Indent(); + void Dedent(); + void WriteIndent(); + void LogType(Type type); + void LogTypes(Index type_count, Type* types); + void LogTypes(TypeVector& types); + void LogField(TypeMut field); + + Stream* stream_; + BinaryReaderDelegate* reader_; + int indent_; +}; + +} // namespace wabt + +#endif // WABT_BINARY_READER_LOGGING_H_ diff --git a/third_party/wasm2c/src/binary-reader-nop.h b/third_party/wasm2c/src/binary-reader-nop.h new file mode 100644 index 0000000000..f22b05ad5b --- /dev/null +++ b/third_party/wasm2c/src/binary-reader-nop.h @@ -0,0 +1,560 @@ +/* + * 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_BINARY_READER_NOP_H_ +#define WABT_BINARY_READER_NOP_H_ + +#include "src/binary-reader.h" + +namespace wabt { + +class BinaryReaderNop : public BinaryReaderDelegate { + public: + bool OnError(const Error&) override { return false; } + + /* Module */ + Result BeginModule(uint32_t version) override { return Result::Ok; } + Result EndModule() override { return Result::Ok; } + + Result BeginSection(Index section_index, + BinarySection section_type, + Offset size) override { + return Result::Ok; + } + + /* Custom section */ + Result BeginCustomSection(Index section_index, + Offset size, + string_view section_name) override { + return Result::Ok; + } + Result EndCustomSection() override { return Result::Ok; } + + /* Type section */ + Result BeginTypeSection(Offset size) override { return Result::Ok; } + Result OnTypeCount(Index count) override { return Result::Ok; } + Result OnFuncType(Index index, + Index param_count, + Type* param_types, + Index result_count, + Type* result_types) override { + return Result::Ok; + } + Result OnStructType(Index index, + Index field_count, + TypeMut* fields) override { + return Result::Ok; + } + Result OnArrayType(Index index, TypeMut field) override { + return Result::Ok; + } + Result EndTypeSection() override { return Result::Ok; } + + /* Import section */ + Result BeginImportSection(Offset size) override { return Result::Ok; } + Result OnImportCount(Index count) override { return Result::Ok; } + Result OnImport(Index index, + ExternalKind kind, + string_view module_name, + string_view field_name) override { + return Result::Ok; + } + Result OnImportFunc(Index import_index, + string_view module_name, + string_view field_name, + Index func_index, + Index sig_index) override { + return Result::Ok; + } + Result OnImportTable(Index import_index, + string_view module_name, + string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) override { + return Result::Ok; + } + Result OnImportMemory(Index import_index, + string_view module_name, + string_view field_name, + Index memory_index, + const Limits* page_limits) override { + return Result::Ok; + } + Result OnImportGlobal(Index import_index, + string_view module_name, + string_view field_name, + Index global_index, + Type type, + bool mutable_) override { + return Result::Ok; + } + Result OnImportTag(Index import_index, + string_view module_name, + string_view field_name, + Index tag_index, + Index sig_index) override { + return Result::Ok; + } + Result EndImportSection() override { return Result::Ok; } + + /* Function section */ + Result BeginFunctionSection(Offset size) override { return Result::Ok; } + Result OnFunctionCount(Index count) override { return Result::Ok; } + Result OnFunction(Index index, Index sig_index) override { + return Result::Ok; + } + Result EndFunctionSection() override { return Result::Ok; } + + /* Table section */ + Result BeginTableSection(Offset size) override { return Result::Ok; } + Result OnTableCount(Index count) override { return Result::Ok; } + Result OnTable(Index index, + Type elem_type, + const Limits* elem_limits) override { + return Result::Ok; + } + Result EndTableSection() override { return Result::Ok; } + + /* Memory section */ + Result BeginMemorySection(Offset size) override { return Result::Ok; } + Result OnMemoryCount(Index count) override { return Result::Ok; } + Result OnMemory(Index index, const Limits* limits) override { + return Result::Ok; + } + Result EndMemorySection() override { return Result::Ok; } + + /* Global section */ + Result BeginGlobalSection(Offset size) override { return Result::Ok; } + Result OnGlobalCount(Index count) override { return Result::Ok; } + Result BeginGlobal(Index index, Type type, bool mutable_) override { + return Result::Ok; + } + Result BeginGlobalInitExpr(Index index) override { return Result::Ok; } + Result EndGlobalInitExpr(Index index) override { return Result::Ok; } + Result EndGlobal(Index index) override { return Result::Ok; } + Result EndGlobalSection() override { return Result::Ok; } + + /* Exports section */ + Result BeginExportSection(Offset size) override { return Result::Ok; } + Result OnExportCount(Index count) override { return Result::Ok; } + Result OnExport(Index index, + ExternalKind kind, + Index item_index, + string_view name) override { + return Result::Ok; + } + Result EndExportSection() override { return Result::Ok; } + + /* Start section */ + Result BeginStartSection(Offset size) override { return Result::Ok; } + Result OnStartFunction(Index func_index) override { return Result::Ok; } + Result EndStartSection() override { return Result::Ok; } + + /* Code section */ + Result BeginCodeSection(Offset size) override { return Result::Ok; } + Result OnFunctionBodyCount(Index count) override { return Result::Ok; } + Result BeginFunctionBody(Index index, Offset size) override { + return Result::Ok; + } + Result OnLocalDeclCount(Index count) override { return Result::Ok; } + Result OnLocalDecl(Index decl_index, Index count, Type type) override { + return Result::Ok; + } + + /* Function expressions; called between BeginFunctionBody and + EndFunctionBody */ + Result OnOpcode(Opcode Opcode) override { return Result::Ok; } + Result OnOpcodeBare() override { return Result::Ok; } + Result OnOpcodeIndex(Index value) override { return Result::Ok; } + Result OnOpcodeIndexIndex(Index value, Index value2) override { + return Result::Ok; + } + Result OnOpcodeUint32(uint32_t value) override { return Result::Ok; } + Result OnOpcodeUint32Uint32(uint32_t value, uint32_t value2) override { + return Result::Ok; + } + Result OnOpcodeUint32Uint32Uint32(uint32_t value, + uint32_t value2, + uint32_t value3) override { + return Result::Ok; + } + Result OnOpcodeUint64(uint64_t value) override { return Result::Ok; } + Result OnOpcodeF32(uint32_t value) override { return Result::Ok; } + Result OnOpcodeF64(uint64_t value) override { return Result::Ok; } + Result OnOpcodeV128(v128 value) override { return Result::Ok; } + Result OnOpcodeBlockSig(Type sig_type) override { return Result::Ok; } + Result OnOpcodeType(Type type) override { return Result::Ok; } + Result OnAtomicLoadExpr(Opcode opcode, + Address alignment_log2, + Address offset) override { + return Result::Ok; + } + Result OnAtomicStoreExpr(Opcode opcode, + Address alignment_log2, + Address offset) override { + return Result::Ok; + } + Result OnAtomicRmwExpr(Opcode opcode, + Address alignment_log2, + Address offset) override { + return Result::Ok; + } + Result OnAtomicRmwCmpxchgExpr(Opcode opcode, + Address alignment_log2, + Address offset) override { + return Result::Ok; + } + Result OnAtomicWaitExpr(Opcode, Address, Address) override { + return Result::Ok; + } + Result OnAtomicFenceExpr(uint32_t) override { + return Result::Ok; + } + Result OnAtomicNotifyExpr(Opcode, Address, Address) override { + return Result::Ok; + } + Result OnBinaryExpr(Opcode opcode) override { return Result::Ok; } + Result OnBlockExpr(Type sig_type) override { return Result::Ok; } + Result OnBrExpr(Index depth) override { return Result::Ok; } + Result OnBrIfExpr(Index depth) override { return Result::Ok; } + Result OnBrTableExpr(Index num_targets, + Index* target_depths, + Index default_target_depth) override { + return Result::Ok; + } + Result OnCallExpr(Index func_index) override { return Result::Ok; } + Result OnCallIndirectExpr(Index sig_index, Index table_index) override { return Result::Ok; } + Result OnCallRefExpr() override { return Result::Ok; } + Result OnCatchExpr(Index tag_index) override { return Result::Ok; } + Result OnCatchAllExpr() override { return Result::Ok; } + Result OnCompareExpr(Opcode opcode) override { return Result::Ok; } + Result OnConvertExpr(Opcode opcode) override { return Result::Ok; } + Result OnDelegateExpr(Index depth) override { return Result::Ok; } + Result OnDropExpr() override { return Result::Ok; } + Result OnElseExpr() override { return Result::Ok; } + Result OnEndExpr() override { return Result::Ok; } + Result OnEndFunc() override { return Result::Ok; } + Result OnF32ConstExpr(uint32_t value_bits) override { return Result::Ok; } + Result OnF64ConstExpr(uint64_t value_bits) override { return Result::Ok; } + Result OnV128ConstExpr(v128 value_bits) override { return Result::Ok; } + Result OnGlobalGetExpr(Index global_index) override { return Result::Ok; } + Result OnGlobalSetExpr(Index global_index) override { return Result::Ok; } + Result OnI32ConstExpr(uint32_t value) override { return Result::Ok; } + Result OnI64ConstExpr(uint64_t value) override { return Result::Ok; } + Result OnIfExpr(Type sig_type) override { return Result::Ok; } + Result OnLoadExpr(Opcode opcode, + Address alignment_log2, + Address offset) override { + return Result::Ok; + } + Result OnLocalGetExpr(Index local_index) override { return Result::Ok; } + Result OnLocalSetExpr(Index local_index) override { return Result::Ok; } + Result OnLocalTeeExpr(Index local_index) override { return Result::Ok; } + Result OnLoopExpr(Type sig_type) override { return Result::Ok; } + Result OnMemoryCopyExpr() override { return Result::Ok; } + Result OnDataDropExpr(Index segment_index) override { return Result::Ok; } + Result OnMemoryFillExpr() override { return Result::Ok; } + Result OnMemoryGrowExpr() override { return Result::Ok; } + Result OnMemoryInitExpr(Index segment_index) override { return Result::Ok; } + Result OnMemorySizeExpr() override { return Result::Ok; } + Result OnTableCopyExpr(Index dst_index, Index src_index) override { + return Result::Ok; + } + Result OnElemDropExpr(Index segment_index) override { return Result::Ok; } + Result OnTableInitExpr(Index segment_index, Index table_index) override { + return Result::Ok; + } + Result OnTableGetExpr(Index table_index) override { return Result::Ok; } + Result OnTableSetExpr(Index table_index) override { return Result::Ok; } + Result OnTableGrowExpr(Index table_index) override { return Result::Ok; } + Result OnTableSizeExpr(Index table_index) override { return Result::Ok; } + Result OnTableFillExpr(Index table_index) override { return Result::Ok; } + Result OnRefFuncExpr(Index func_index) override { return Result::Ok; } + Result OnRefNullExpr(Type type) override { return Result::Ok; } + Result OnRefIsNullExpr() override { return Result::Ok; } + Result OnNopExpr() override { return Result::Ok; } + Result OnRethrowExpr(Index depth) override { return Result::Ok; } + Result OnReturnCallExpr(Index sig_index) override { return Result::Ok; } + Result OnReturnCallIndirectExpr(Index sig_index, Index table_index) override { return Result::Ok; } + Result OnReturnExpr() override { return Result::Ok; } + Result OnSelectExpr(Index result_count, Type* result_types) override { + return Result::Ok; + } + Result OnStoreExpr(Opcode opcode, + Address alignment_log2, + Address offset) override { + return Result::Ok; + } + Result OnThrowExpr(Index depth) override { return Result::Ok; } + Result OnTryExpr(Type sig_type) override { return Result::Ok; } + Result OnUnaryExpr(Opcode opcode) override { return Result::Ok; } + Result OnTernaryExpr(Opcode opcode) override { return Result::Ok; } + Result OnUnreachableExpr() override { return Result::Ok; } + Result EndFunctionBody(Index index) override { return Result::Ok; } + Result EndCodeSection() override { return Result::Ok; } + Result OnSimdLaneOpExpr(Opcode opcode, uint64_t value) override { + return Result::Ok; + } + Result OnSimdLoadLaneExpr(Opcode opcode, + Address alignment_log2, + Address offset, + uint64_t value) override { + return Result::Ok; + } + Result OnSimdStoreLaneExpr(Opcode opcode, + Address alignment_log2, + Address offset, + uint64_t value) override { + return Result::Ok; + } + Result OnSimdShuffleOpExpr(Opcode opcode, v128 value) override { + return Result::Ok; + } + Result OnLoadSplatExpr(Opcode opcode, + Address alignment_log2, + Address offset) override { + return Result::Ok; + } + Result OnLoadZeroExpr(Opcode opcode, + Address alignment_log2, + Address offset) override { + return Result::Ok; + } + + /* Elem section */ + Result BeginElemSection(Offset size) override { return Result::Ok; } + Result OnElemSegmentCount(Index count) override { return Result::Ok; } + Result BeginElemSegment(Index index, + Index table_index, + uint8_t flags) override { + return Result::Ok; + } + Result BeginElemSegmentInitExpr(Index index) override { return Result::Ok; } + Result EndElemSegmentInitExpr(Index index) override { return Result::Ok; } + Result OnElemSegmentElemType(Index index, Type elem_type) override { + return Result::Ok; + } + Result OnElemSegmentElemExprCount(Index index, Index count) override { + return Result::Ok; + } + Result OnElemSegmentElemExpr_RefNull(Index segment_index, + Type type) override { + return Result::Ok; + } + Result OnElemSegmentElemExpr_RefFunc(Index segment_index, + Index func_index) override { + return Result::Ok; + } + Result EndElemSegment(Index index) override { return Result::Ok; } + Result EndElemSection() override { return Result::Ok; } + + /* Data section */ + Result BeginDataSection(Offset size) override { return Result::Ok; } + Result OnDataSegmentCount(Index count) override { return Result::Ok; } + Result BeginDataSegment(Index index, + Index memory_index, + uint8_t flags) override { + return Result::Ok; + } + Result BeginDataSegmentInitExpr(Index index) override { return Result::Ok; } + Result EndDataSegmentInitExpr(Index index) override { return Result::Ok; } + Result OnDataSegmentData(Index index, + const void* data, + Address size) override { + return Result::Ok; + } + Result EndDataSegment(Index index) override { return Result::Ok; } + Result EndDataSection() override { return Result::Ok; } + + /* DataCount section */ + Result BeginDataCountSection(Offset size) override { return Result::Ok; } + Result OnDataCount(Index count) override { return Result::Ok; } + Result EndDataCountSection() override { return Result::Ok; } + + /* Names section */ + Result BeginNamesSection(Offset size) override { return Result::Ok; } + Result OnModuleNameSubsection(Index index, + uint32_t name_type, + Offset subsection_size) override { + return Result::Ok; + } + Result OnModuleName(string_view name) override { return Result::Ok; } + Result OnFunctionNameSubsection(Index index, + uint32_t name_type, + Offset subsection_size) override { + return Result::Ok; + } + Result OnFunctionNamesCount(Index num_functions) override { + return Result::Ok; + } + Result OnFunctionName(Index function_index, + string_view function_name) override { + return Result::Ok; + } + Result OnLocalNameSubsection(Index index, + uint32_t name_type, + Offset subsection_size) override { + return Result::Ok; + } + Result OnLocalNameFunctionCount(Index num_functions) override { + return Result::Ok; + } + Result OnLocalNameLocalCount(Index function_index, + Index num_locals) override { + return Result::Ok; + } + Result OnLocalName(Index function_index, + Index local_index, + string_view local_name) override { + return Result::Ok; + } + Result EndNamesSection() override { return Result::Ok; } + + Result OnNameSubsection(Index index, + NameSectionSubsection subsection_type, + Offset subsection_size) override { + return Result::Ok; + } + Result OnNameCount(Index num_names) override { return Result::Ok; } + Result OnNameEntry(NameSectionSubsection type, + Index index, + string_view name) override { + return Result::Ok; + } + + /* Reloc section */ + Result BeginRelocSection(Offset size) override { return Result::Ok; } + Result OnRelocCount(Index count, Index section_code) override { + return Result::Ok; + } + Result OnReloc(RelocType type, + Offset offset, + Index index, + uint32_t addend) override { + return Result::Ok; + } + Result EndRelocSection() override { return Result::Ok; } + + /* Tag section */ + Result BeginTagSection(Offset size) override { return Result::Ok; } + Result OnTagCount(Index count) override { return Result::Ok; } + Result OnTagType(Index index, Index sig_index) override { return Result::Ok; } + Result EndTagSection() override { return Result::Ok; } + + /* Dylink section */ + Result BeginDylinkSection(Offset size) override { return Result::Ok; } + Result OnDylinkInfo(uint32_t mem_size, + uint32_t mem_align, + uint32_t table_size, + uint32_t table_align) override { + return Result::Ok; + } + Result OnDylinkNeededCount(Index count) override { return Result::Ok; } + Result OnDylinkNeeded(string_view so_name) override { return Result::Ok; } + Result EndDylinkSection() override { return Result::Ok; } + + /* Linking section */ + Result BeginLinkingSection(Offset size) override { return Result::Ok; } + Result OnSymbolCount(Index count) override { return Result::Ok; } + Result OnDataSymbol(Index index, + uint32_t flags, + string_view name, + Index segment, + uint32_t offset, + uint32_t size) override { + return Result::Ok; + } + Result OnFunctionSymbol(Index index, + uint32_t flags, + string_view name, + Index func_index) override { + return Result::Ok; + } + Result OnGlobalSymbol(Index index, + uint32_t flags, + string_view name, + Index global_index) override { + return Result::Ok; + } + Result OnSectionSymbol(Index index, + uint32_t flags, + Index section_index) override { + return Result::Ok; + } + Result OnTagSymbol(Index index, + uint32_t flags, + string_view name, + Index tag_index) override { + return Result::Ok; + } + Result OnTableSymbol(Index index, + uint32_t flags, + string_view name, + Index table_index) override { + return Result::Ok; + } + Result OnSegmentInfoCount(Index count) override { return Result::Ok; } + Result OnSegmentInfo(Index index, + string_view name, + Address alignment, + uint32_t flags) override { + return Result::Ok; + } + Result OnInitFunctionCount(Index count) override { return Result::Ok; } + Result OnInitFunction(uint32_t priority, Index function_index) override { + return Result::Ok; + } + Result OnComdatCount(Index count) override { return Result::Ok; } + Result OnComdatBegin(string_view name, uint32_t flags, Index count) override { + return Result::Ok; + } + Result OnComdatEntry(ComdatType kind, Index index) override { + return Result::Ok; + } + Result EndLinkingSection() override { return Result::Ok; } + + /* InitExpr - used by elem, data and global sections; these functions are + * only called between calls to Begin*InitExpr and End*InitExpr */ + Result OnInitExprF32ConstExpr(Index index, uint32_t value) override { + return Result::Ok; + } + Result OnInitExprF64ConstExpr(Index index, uint64_t value) override { + return Result::Ok; + } + Result OnInitExprV128ConstExpr(Index index, v128 value) override { + return Result::Ok; + } + Result OnInitExprGlobalGetExpr(Index index, Index global_index) override { + return Result::Ok; + } + Result OnInitExprI32ConstExpr(Index index, uint32_t value) override { + return Result::Ok; + } + Result OnInitExprI64ConstExpr(Index index, uint64_t value) override { + return Result::Ok; + } + Result OnInitExprRefNull(Index index, Type type) override { + return Result::Ok; + } + Result OnInitExprRefFunc(Index index, Index func_index) override { + return Result::Ok; + } +}; + +} // namespace wabt + +#endif /* WABT_BINARY_READER_H_ */ 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..188cd4271e --- /dev/null +++ b/third_party/wasm2c/src/binary-reader-objdump.cc @@ -0,0 +1,2085 @@ +/* + * 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 "src/binary-reader-objdump.h" + +#include <algorithm> +#include <cassert> +#include <cinttypes> +#include <cstdio> +#include <cstring> +#include <vector> + +#if HAVE_STRCASECMP +#include <strings.h> +#endif + +#include "src/binary-reader-nop.h" +#include "src/filenames.h" +#include "src/literal.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 OnRelocCount(Index count, Index section_index) override; + + protected: + string_view GetFunctionName(Index index) const; + string_view GetGlobalName(Index index) const; + string_view GetSectionName(Index index) const; + string_view GetTagName(Index index) const; + string_view GetSymbolName(Index index) const; + string_view GetSegmentName(Index index) const; + 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_; + + 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: { + string_view basename = GetBasename(options_->filename); + if (basename == "-") { + basename = "<stdin>"; + } + printf("%s:\tfile format wasm %#x\n", basename.to_string().c_str(), + version); + break; + } + case ObjdumpMode::RawData: + break; + } + + return Result::Ok; +} + +string_view BinaryReaderObjdumpBase::GetFunctionName(Index index) const { + return objdump_state_->function_names.Get(index); +} + +string_view BinaryReaderObjdumpBase::GetGlobalName(Index index) const { + return objdump_state_->global_names.Get(index); +} + +string_view BinaryReaderObjdumpBase::GetSectionName(Index index) const { + return objdump_state_->section_names.Get(index); +} + +string_view BinaryReaderObjdumpBase::GetTagName(Index index) const { + return objdump_state_->tag_names.Get(index); +} + +string_view BinaryReaderObjdumpBase::GetSegmentName(Index index) const { + return objdump_state_->segment_names.Get(index); +} + +string_view BinaryReaderObjdumpBase::GetTableName(Index index) const { + return objdump_state_->table_names.Get(index); +} + +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::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, + string_view section_name) override { + objdump_state_->section_names.Set(section_index, section_name); + return Result::Ok; + } + + Result OnFunctionName(Index index, string_view name) override { + SetFunctionName(index, name); + return Result::Ok; + } + + Result OnNameEntry(NameSectionSubsection type, + Index index, + 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::Global: + SetGlobalName(index, name); + break; + case NameSectionSubsection::Table: + SetTableName(index, name); + break; + case NameSectionSubsection::DataSegment: + SetSegmentName(index, name); + break; + default: + break; + } + return Result::Ok; + } + + Result OnSymbolCount(Index count) override { + objdump_state_->symtab.resize(count); + return Result::Ok; + } + + Result OnDataSymbol(Index index, + uint32_t flags, + string_view name, + Index segment, + uint32_t offset, + uint32_t size) override { + objdump_state_->symtab[index] = {SymbolType::Data, name.to_string(), 0}; + return Result::Ok; + } + + Result OnFunctionSymbol(Index index, + uint32_t flags, + string_view name, + Index func_index) override { + if (!name.empty()) { + SetFunctionName(func_index, name); + } + objdump_state_->symtab[index] = {SymbolType::Function, name.to_string(), + func_index}; + return Result::Ok; + } + + Result OnGlobalSymbol(Index index, + uint32_t flags, + string_view name, + Index global_index) override { + if (!name.empty()) { + SetGlobalName(global_index, name); + } + objdump_state_->symtab[index] = {SymbolType::Global, name.to_string(), + 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, + string_view name, + Index tag_index) override { + if (!name.empty()) { + SetTagName(tag_index, name); + } + objdump_state_->symtab[index] = {SymbolType::Tag, name.to_string(), + tag_index}; + return Result::Ok; + } + + Result OnTableSymbol(Index index, + uint32_t flags, + string_view name, + Index table_index) override { + if (!name.empty()) { + SetTableName(table_index, name); + } + objdump_state_->symtab[index] = {SymbolType::Table, name.to_string(), + table_index}; + return Result::Ok; + } + + Result OnImportFunc(Index import_index, + string_view module_name, + string_view field_name, + Index func_index, + Index sig_index) override { + SetFunctionName(func_index, + module_name.to_string() + "." + field_name.to_string()); + return Result::Ok; + } + + Result OnImportTag(Index import_index, + string_view module_name, + string_view field_name, + Index tag_index, + Index sig_index) override { + SetTagName(tag_index, + module_name.to_string() + "." + field_name.to_string()); + return Result::Ok; + } + + Result OnImportGlobal(Index import_index, + string_view module_name, + string_view field_name, + Index global_index, + Type type, + bool mutable_) override { + SetGlobalName(global_index, + module_name.to_string() + "." + field_name.to_string()); + return Result::Ok; + } + + Result OnImportTable(Index import_index, + string_view module_name, + string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) override { + SetTableName(table_index, + module_name.to_string() + "." + field_name.to_string()); + return Result::Ok; + } + + Result OnExport(Index index, + ExternalKind kind, + Index item_index, + 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(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, + string_view name, + Address alignment_log2, + uint32_t flags) override { + SetSegmentName(index, name); + return Result::Ok; + } + + protected: + void SetFunctionName(Index index, string_view name); + void SetGlobalName(Index index, string_view name); + void SetTagName(Index index, string_view name); + void SetTableName(Index index, string_view name); + void SetSegmentName(Index index, string_view name); +}; + +void BinaryReaderObjdumpPrepass::SetFunctionName(Index index, + string_view name) { + objdump_state_->function_names.Set(index, name); +} + +void BinaryReaderObjdumpPrepass::SetGlobalName(Index index, string_view name) { + objdump_state_->global_names.Set(index, name); +} + +void BinaryReaderObjdumpPrepass::SetTagName(Index index, string_view name) { + objdump_state_->tag_names.Set(index, name); +} + +void BinaryReaderObjdumpPrepass::SetTableName(Index index, string_view name) { + objdump_state_->table_names.Set(index, name); +} + +void BinaryReaderObjdumpPrepass::SetSegmentName(Index index, 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 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 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 OnOpcodeType(Type type) override; + + Result OnBrTableExpr(Index num_targets, + Index* target_depths, + Index default_target_depth) override; + Result OnDelegateExpr(Index) override; + Result OnEndExpr() override; + Result OnEndFunc() override; + + private: + void LogOpcode(size_t data_size, const char* fmt, ...); + + Opcode current_opcode = Opcode::Unreachable; + Offset current_opcode_offset = 0; + Offset last_opcode_end = 0; + int indent_level = 0; + Index next_reloc = 0; + Index local_index_ = 0; +}; + +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) { + 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) { + 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, + "warning: %#" 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; + current_opcode = opcode; + return Result::Ok; +} + +#define IMMEDIATE_OCTET_COUNT 9 + +Result BinaryReaderObjdumpDisassemble::OnLocalDeclCount(Index count) { + local_index_ = 0; + current_opcode_offset = state->offset; + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnLocalDecl(Index decl_index, + Index count, + Type type) { + 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()); + + last_opcode_end = current_opcode_offset + data_size; + current_opcode_offset = last_opcode_end; + + return Result::Ok; +} + +void BinaryReaderObjdumpDisassemble::LogOpcode(size_t data_size, + const char* fmt, + ...) { + const Offset opcode_size = current_opcode.GetLength(); + const Offset total_size = opcode_size + data_size; + // 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--; + 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 = offset_end; + + // 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() { + LogOpcode(0, nullptr); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeIndex(Index value) { + Offset immediate_len = state->offset - current_opcode_offset; + string_view name; + if (current_opcode == Opcode::Call && + !(name = GetFunctionName(value)).empty()) { + LogOpcode(immediate_len, "%d <" PRIstringview ">", value, + WABT_PRINTF_STRING_VIEW_ARG(name)); + } else if ((current_opcode == Opcode::GlobalGet || + current_opcode == Opcode::GlobalSet) && + !(name = GetGlobalName(value)).empty()) { + LogOpcode(immediate_len, "%d <" PRIstringview ">", value, + WABT_PRINTF_STRING_VIEW_ARG(name)); + } else { + LogOpcode(immediate_len, "%d", value); + } + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeIndexIndex(Index value, + Index value2) { + Offset immediate_len = state->offset - current_opcode_offset; + LogOpcode(immediate_len, "%" PRIindex " %" PRIindex, value, value2); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32(uint32_t value) { + Offset immediate_len = state->offset - current_opcode_offset; + string_view name; + if (current_opcode == Opcode::DataDrop && + !(name = GetSegmentName(value)).empty()) { + LogOpcode(immediate_len, "%d <" PRIstringview ">", value, + WABT_PRINTF_STRING_VIEW_ARG(name)); + } else { + LogOpcode(immediate_len, "%u", value); + } + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32Uint32(uint32_t value, + uint32_t value2) { + Offset immediate_len = state->offset - current_opcode_offset; + string_view name; + if (current_opcode == Opcode::MemoryInit && + !(name = GetSegmentName(value)).empty()) { + LogOpcode(immediate_len, "%u %u <" PRIstringview ">", value, value2, + WABT_PRINTF_STRING_VIEW_ARG(name)); + } else { + LogOpcode(immediate_len, "%u %u", value, value2); + } + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32Uint32Uint32( + uint32_t value, + uint32_t value2, + uint32_t value3) { + Offset immediate_len = state->offset - current_opcode_offset; + LogOpcode(immediate_len, "%u %u %u", value, value2, value3); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeUint64(uint64_t value) { + Offset immediate_len = state->offset - current_opcode_offset; + LogOpcode(immediate_len, "%" PRId64, value); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeF32(uint32_t value) { + Offset immediate_len = state->offset - current_opcode_offset; + char buffer[WABT_MAX_FLOAT_HEX]; + WriteFloatHex(buffer, sizeof(buffer), value); + LogOpcode(immediate_len, buffer); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeF64(uint64_t value) { + Offset immediate_len = state->offset - current_opcode_offset; + char buffer[WABT_MAX_DOUBLE_HEX]; + WriteDoubleHex(buffer, sizeof(buffer), value); + LogOpcode(immediate_len, buffer); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeV128(v128 value) { + Offset immediate_len = state->offset - current_opcode_offset; + // v128 is always dumped as i32x4: + LogOpcode(immediate_len, "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) { + Offset immediate_len = state->offset - current_opcode_offset; + LogOpcode(immediate_len, type.GetRefKindName()); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnBrTableExpr( + Index num_targets, + Index* target_depths, + Index default_target_depth) { + Offset immediate_len = state->offset - current_opcode_offset; + + 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(immediate_len, "%s", buffer.c_str()); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnDelegateExpr(Index depth) { + // 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::OnEndFunc() { + LogOpcode(0, nullptr); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnEndExpr() { + 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; + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeBlockSig(Type sig_type) { + Offset immediate_len = state->offset - current_opcode_offset; + if (sig_type != Type::Void) { + LogOpcode(immediate_len, "%s", BlockSigToString(sig_type).c_str()); + } else { + LogOpcode(immediate_len, 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 InitExpr { + InitExprType type; + union { + Index index; + uint32_t i32; + uint32_t f32; + uint64_t i64; + uint64_t f64; + v128 v128_v; + Type type; + } value; +}; + +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, + 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, + string_view module_name, + string_view field_name, + Index func_index, + Index sig_index) override; + Result OnImportTable(Index import_index, + string_view module_name, + string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) override; + Result OnImportMemory(Index import_index, + string_view module_name, + string_view field_name, + Index memory_index, + const Limits* page_limits) override; + Result OnImportGlobal(Index import_index, + string_view module_name, + string_view field_name, + Index global_index, + Type type, + bool mutable_) override; + Result OnImportTag(Index import_index, + string_view module_name, + 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, + 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 BeginElemSection(Offset size) override { + in_elem_section_ = true; + return Result::Ok; + } + Result EndElemSection() override { + in_elem_section_ = false; + return Result::Ok; + } + + 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; + + Result BeginDataSection(Offset size) override { + in_data_section_ = true; + return Result::Ok; + } + Result EndDataSection() override { + in_data_section_ = false; + return Result::Ok; + } + + 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(string_view name) override; + Result OnFunctionName(Index function_index, + string_view function_name) override; + Result OnLocalName(Index function_index, + Index local_index, + string_view local_name) override; + Result OnNameEntry(NameSectionSubsection type, + Index index, + string_view name) override; + + Result OnInitExprF32ConstExpr(Index index, uint32_t value) override; + Result OnInitExprF64ConstExpr(Index index, uint64_t value) override; + Result OnInitExprV128ConstExpr(Index index, v128 value) override; + Result OnInitExprGlobalGetExpr(Index index, Index global_index) override; + Result OnInitExprI32ConstExpr(Index index, uint32_t value) override; + Result OnInitExprI64ConstExpr(Index index, uint64_t value) override; + Result OnInitExprRefNull(Index index, Type type) override; + Result OnInitExprRefFunc(Index index, Index func_index) 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(string_view so_name) override; + + Result OnRelocCount(Index count, Index section_index) override; + Result OnReloc(RelocType type, + Offset offset, + Index index, + uint32_t addend) override; + + Result OnSymbolCount(Index count) override; + Result OnDataSymbol(Index index, + uint32_t flags, + string_view name, + Index segment, + uint32_t offset, + uint32_t size) override; + Result OnFunctionSymbol(Index index, + uint32_t flags, + string_view name, + Index func_index) override; + Result OnGlobalSymbol(Index index, + uint32_t flags, + 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, + string_view name, + Index tag_index) override; + Result OnTableSymbol(Index index, + uint32_t flags, + string_view name, + Index table_index) override; + Result OnSegmentInfoCount(Index count) override; + Result OnSegmentInfo(Index index, + string_view name, + Address alignment_log2, + uint32_t flags) override; + Result OnInitFunctionCount(Index count) override; + Result OnInitFunction(uint32_t priority, Index function_index) override; + Result OnComdatCount(Index count) override; + Result OnComdatBegin(string_view name, uint32_t flags, Index count) override; + Result OnComdatEntry(ComdatType kind, Index index) override; + Result EndLinkingSection() override { return Result::Ok; } + + Result OnTagCount(Index count) override; + Result OnTagType(Index index, Index sig_index) override; + + private: + Result InitExprToConstOffset(const InitExpr& expr, uint32_t* out_offset); + Result HandleInitExpr(const InitExpr& expr); + bool ShouldPrintDetails(); + void PrintDetails(const char* fmt, ...); + Result PrintSymbolFlags(uint32_t flags); + Result PrintSegmentFlags(uint32_t flags); + void PrintInitExpr(const InitExpr& expr); + Result OnCount(Index count); + + std::unique_ptr<FileStream> out_stream_; + Index elem_index_ = 0; + Index table_index_ = 0; + Index next_data_reloc_ = 0; + bool in_data_section_ = false; + bool in_elem_section_ = false; + InitExpr data_init_expr_; + InitExpr elem_init_expr_; + uint8_t data_flags_ = 0; + uint8_t elem_flags_ = 0; + Index data_mem_index_ = 0; + uint32_t data_offset_ = 0; + uint32_t elem_offset_ = 0; +}; + +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, + 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).to_string(); + + 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) { + if (next_data_reloc_ != objdump_state_->data_relocations.size()) { + err_stream_->Writef("Data reloctions outside of segments\n"); + 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()); + } + printf(") -> "); + switch (result_count) { + case 0: + printf("nil"); + break; + case 1: + printf("%s", result_types[0].GetName()); + break; + default: + printf("("); + for (Index i = 0; i < result_count; i++) { + if (i != 0) { + printf(", "); + } + printf("%s", result_types[i].GetName()); + } + 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()); + 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()); + 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, + string_view module_name, + 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, + string_view module_name, + 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(), 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, + string_view module_name, + 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, + string_view module_name, + string_view field_name, + Index global_index, + Type type, + bool mutable_) { + PrintDetails(" - global[%" PRIindex "] %s mutable=%d", global_index, + type.GetName(), 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, + string_view module_name, + 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(), 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, + 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[%" PRIindex "] = ref.null %s\n", + elem_offset_ + elem_index_, type.GetName()); + elem_index_++; + return Result::Ok; +} + +Result BinaryReaderObjdump::OnElemSegmentElemExpr_RefFunc(Index segment_index, + Index func_index) { + PrintDetails(" - elem[%" PRIindex "] = 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(elem_init_expr_); + } + 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(), + mutable_); + 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) { + switch (expr.type) { + case InitExprType::I32: + PrintDetails(" - init i32=%d\n", expr.value.i32); + break; + case InitExprType::I64: + PrintDetails(" - init i64=%" PRId64 "\n", expr.value.i64); + break; + case InitExprType::F64: { + char buffer[WABT_MAX_DOUBLE_HEX]; + WriteDoubleHex(buffer, sizeof(buffer), expr.value.f64); + PrintDetails(" - init f64=%s\n", buffer); + break; + } + case InitExprType::F32: { + char buffer[WABT_MAX_FLOAT_HEX]; + WriteFloatHex(buffer, sizeof(buffer), expr.value.f32); + PrintDetails(" - init f32=%s\n", buffer); + break; + } + case InitExprType::V128: { + PrintDetails(" - init v128=0x%08x 0x%08x 0x%08x 0x%08x \n", + expr.value.v128_v.u32(0), expr.value.v128_v.u32(1), + expr.value.v128_v.u32(2), expr.value.v128_v.u32(3)); + break; + } + case InitExprType::Global: { + PrintDetails(" - init global=%" PRIindex, expr.value.index); + string_view name = GetGlobalName(expr.value.index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails("\n"); + break; + } + case InitExprType::FuncRef: { + PrintDetails(" - init ref.func:%" PRIindex, expr.value.index); + string_view name = GetFunctionName(expr.value.index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails("\n"); + break; + } + case InitExprType::NullRef: { + PrintDetails(" - init null\n"); + break; + } + } +} + +Result BinaryReaderObjdump::InitExprToConstOffset(const InitExpr& expr, + uint32_t* out_offset) { + switch (expr.type) { + case InitExprType::I32: + *out_offset = expr.value.i32; + break; + case InitExprType::Global: + *out_offset = 0; + break; + case InitExprType::I64: + case InitExprType::F32: + case InitExprType::F64: + case InitExprType::V128: + case InitExprType::FuncRef: + case InitExprType::NullRef: + err_stream_->Writef("Segment/Elem offset must be an i32 init expr"); + return Result::Error; + break; + } + return Result::Ok; +} + +Result BinaryReaderObjdump::HandleInitExpr(const InitExpr& expr) { + if (in_data_section_) { + data_init_expr_ = expr; + return InitExprToConstOffset(expr, &data_offset_); + } else if (in_elem_section_) { + elem_init_expr_ = expr; + return InitExprToConstOffset(expr, &elem_offset_); + } else { + PrintInitExpr(expr); + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnInitExprF32ConstExpr(Index index, + uint32_t value) { + InitExpr expr; + expr.type = InitExprType::F32; + expr.value.f32 = value; + HandleInitExpr(expr); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnInitExprF64ConstExpr(Index index, + uint64_t value) { + InitExpr expr; + expr.type = InitExprType::F64; + expr.value.f64 = value; + HandleInitExpr(expr); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnInitExprV128ConstExpr(Index index, v128 value) { + InitExpr expr; + expr.type = InitExprType::V128; + expr.value.v128_v = value; + HandleInitExpr(expr); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnInitExprGlobalGetExpr(Index index, + Index global_index) { + InitExpr expr; + expr.type = InitExprType::Global; + expr.value.index = global_index; + HandleInitExpr(expr); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnInitExprI32ConstExpr(Index index, + uint32_t value) { + InitExpr expr; + expr.type = InitExprType::I32; + expr.value.i32 = value; + HandleInitExpr(expr); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnInitExprI64ConstExpr(Index index, + uint64_t value) { + InitExpr expr; + expr.type = InitExprType::I64; + expr.value.i64 = value; + HandleInitExpr(expr); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnInitExprRefNull(Index index, Type type) { + InitExpr expr; + expr.type = InitExprType::NullRef; + expr.value.type = type; + HandleInitExpr(expr); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnInitExprRefFunc(Index index, Index func_index) { + InitExpr expr{InitExprType::FuncRef, {func_index}}; + HandleInitExpr(expr); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnModuleName(string_view name) { + PrintDetails(" - module <" PRIstringview ">\n", + WABT_PRINTF_STRING_VIEW_ARG(name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnFunctionName(Index index, 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, + 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, + 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(data_init_expr_); + } + + 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::OnDylinkNeeded(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::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, + 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, + 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, + 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, + 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, + 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, + 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 function_index) { + PrintDetails(" - %d: priority=%d\n", function_index, priority); + return Result::Ok; +} +Result BinaryReaderObjdump::OnComdatCount(Index count) { + PrintDetails(" - comdat groups [count=%d]\n", count); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnComdatBegin(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; +} + +} // end anonymous namespace + +string_view ObjdumpNames::Get(Index index) const { + auto iter = names.find(index); + if (iter == names.end()) + return string_view(); + return iter->second; +} + +void ObjdumpNames::Set(Index index, string_view name) { + names[index] = name.to_string(); +} + +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: { + 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: { + BinaryReaderObjdump reader(data, size, options, state); + return ReadBinary(data, size, &reader, read_options); + } + } +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary-reader-objdump.h b/third_party/wasm2c/src/binary-reader-objdump.h new file mode 100644 index 0000000000..1c3d119e97 --- /dev/null +++ b/third_party/wasm2c/src/binary-reader-objdump.h @@ -0,0 +1,88 @@ +/* + * 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_BINARY_READER_OBJDUMP_H_ +#define WABT_BINARY_READER_OBJDUMP_H_ + +#include <map> +#include <string> + +#include "src/common.h" +#include "src/feature.h" +#include "src/stream.h" + +namespace wabt { + +struct Module; +struct ReadBinaryOptions; + +enum class ObjdumpMode { + Prepass, + Headers, + Details, + Disassemble, + RawData, +}; + +struct ObjdumpOptions { + Stream* log_stream; + bool headers; + bool details; + bool raw; + bool disassemble; + bool debug; + bool relocs; + bool section_offsets; + ObjdumpMode mode; + const char* filename; + const char* section_name; +}; + +struct ObjdumpSymbol { + wabt::SymbolType kind; + std::string name; + Index index; +}; + +struct ObjdumpNames { + string_view Get(Index index) const; + void Set(Index index, string_view name); + + std::map<Index, std::string> names; +}; + +// read_binary_objdump uses this state to store information from previous runs +// and use it to display more useful information. +struct ObjdumpState { + std::vector<Reloc> code_relocations; + std::vector<Reloc> data_relocations; + ObjdumpNames function_names; + ObjdumpNames global_names; + ObjdumpNames section_names; + ObjdumpNames tag_names; + ObjdumpNames segment_names; + ObjdumpNames table_names; + std::vector<ObjdumpSymbol> symtab; +}; + +Result ReadBinaryObjdump(const uint8_t* data, + size_t size, + ObjdumpOptions* options, + ObjdumpState* state); + +} // namespace wabt + +#endif /* WABT_BINARY_READER_OBJDUMP_H_ */ 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..3908660d00 --- /dev/null +++ b/third_party/wasm2c/src/binary-reader-opcnt.cc @@ -0,0 +1,290 @@ +/* + * 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 "src/binary-reader-opcnt.h" + +#include <cassert> +#include <cinttypes> +#include <cstdarg> +#include <cstdint> +#include <cstdio> + +#include "src/binary-reader-nop.h" +#include "src/common.h" +#include "src/literal.h" +#include "src/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 pair = GetDataArray<T>(); + assert(pair.second == expected_size); + return pair.first; +} + +template <typename T, typename F> +void OpcodeInfo::WriteArray(Stream& stream, F&& write_func) { + auto pair = GetDataArray<T>(); + for (size_t i = 0; i < pair.second; ++i) { + // Write an initial space (to separate from the opcode name) first, then + // comma-separate. + stream.Writef("%s", i == 0 ? " " : ", "); + write_func(pair.first[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::Uint32Uint32: + 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()); + } + 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 OnOpcodeUint64(uint64_t value) override; + Result OnOpcodeF32(uint32_t value) override; + Result OnOpcodeF64(uint64_t value) override; + Result OnOpcodeBlockSig(Type sig_type) override; + Result OnBrTableExpr(Index num_targets, + Index* target_depths, + Index default_target_depth) override; + Result OnEndExpr() override; + Result OnEndFunc() 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::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::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); +} + +Result BinaryReaderOpcnt::OnEndFunc() { + 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-opcnt.h b/third_party/wasm2c/src/binary-reader-opcnt.h new file mode 100644 index 0000000000..cfdd570c0e --- /dev/null +++ b/third_party/wasm2c/src/binary-reader-opcnt.h @@ -0,0 +1,93 @@ +/* + * 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_BINARY_READER_OPCNT_H_ +#define WABT_BINARY_READER_OPCNT_H_ + +#include <map> +#include <vector> + +#include "src/common.h" +#include "src/opcode.h" + +namespace wabt { + +struct Module; +struct ReadBinaryOptions; +class Stream; + +class OpcodeInfo { + public: + enum class Kind { + Bare, + Uint32, + Uint64, + Index, + Float32, + Float64, + Uint32Uint32, + BlockSig, + BrTable, + }; + + explicit OpcodeInfo(Opcode, Kind); + template <typename T> + OpcodeInfo(Opcode, Kind, T* data, size_t count = 1); + template <typename T> + OpcodeInfo(Opcode, Kind, T* data, size_t count, T extra); + + Opcode opcode() const { return opcode_; } + + void Write(Stream&); + + private: + template <typename T> + std::pair<const T*, size_t> GetDataArray() const; + template <typename T> + const T* GetData(size_t expected_size = 1) const; + + template <typename T, typename F> + void WriteArray(Stream& stream, F&& write_func); + + Opcode opcode_; + Kind kind_; + std::vector<uint8_t> data_; + + friend bool operator==(const OpcodeInfo&, const OpcodeInfo&); + friend bool operator!=(const OpcodeInfo&, const OpcodeInfo&); + friend bool operator<(const OpcodeInfo&, const OpcodeInfo&); + friend bool operator<=(const OpcodeInfo&, const OpcodeInfo&); + friend bool operator>(const OpcodeInfo&, const OpcodeInfo&); + friend bool operator>=(const OpcodeInfo&, const OpcodeInfo&); +}; + +bool operator==(const OpcodeInfo&, const OpcodeInfo&); +bool operator!=(const OpcodeInfo&, const OpcodeInfo&); +bool operator<(const OpcodeInfo&, const OpcodeInfo&); +bool operator<=(const OpcodeInfo&, const OpcodeInfo&); +bool operator>(const OpcodeInfo&, const OpcodeInfo&); +bool operator>=(const OpcodeInfo&, const OpcodeInfo&); + +typedef std::map<OpcodeInfo, size_t> OpcodeInfoCounts; + +Result ReadBinaryOpcnt(const void* data, + size_t size, + const ReadBinaryOptions& options, + OpcodeInfoCounts* opcode_counts); + +} // namespace wabt + +#endif /* WABT_BINARY_READER_OPCNT_H_ */ diff --git a/third_party/wasm2c/src/binary-reader.cc b/third_party/wasm2c/src/binary-reader.cc new file mode 100644 index 0000000000..b5a5b2fd2a --- /dev/null +++ b/third_party/wasm2c/src/binary-reader.cc @@ -0,0 +1,2821 @@ +/* + * 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 "src/binary-reader.h" + +#include <cassert> +#include <cinttypes> +#include <cstdarg> +#include <cstdint> +#include <cstdio> +#include <cstring> +#include <vector> + +#include "config.h" + +#include "src/binary-reader-logging.h" +#include "src/binary.h" +#include "src/leb128.h" +#include "src/stream.h" +#include "src/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: + BinaryReader(const void* data, + size_t size, + BinaryReaderDelegate* delegate, + const ReadBinaryOptions& options); + + Result ReadModule(); + + 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_; + }; + + void WABT_PRINTF_FORMAT(2, 3) PrintError(const char* format, ...); + Result ReadOpcode(Opcode* out_value, const char* desc) WABT_WARN_UNUSED; + template <typename T> + Result ReadT(T* out_value, + const char* type_name, + const char* desc) WABT_WARN_UNUSED; + Result ReadU8(uint8_t* out_value, const char* desc) WABT_WARN_UNUSED; + Result ReadU32(uint32_t* out_value, const char* desc) WABT_WARN_UNUSED; + Result ReadF32(uint32_t* out_value, const char* desc) WABT_WARN_UNUSED; + Result ReadF64(uint64_t* out_value, const char* desc) WABT_WARN_UNUSED; + Result ReadV128(v128* out_value, const char* desc) WABT_WARN_UNUSED; + Result ReadU32Leb128(uint32_t* out_value, const char* desc) WABT_WARN_UNUSED; + Result ReadU64Leb128(uint64_t* out_value, const char* desc) WABT_WARN_UNUSED; + Result ReadS32Leb128(uint32_t* out_value, const char* desc) WABT_WARN_UNUSED; + Result ReadS64Leb128(uint64_t* out_value, const char* desc) WABT_WARN_UNUSED; + Result ReadType(Type* out_value, const char* desc) WABT_WARN_UNUSED; + Result ReadRefType(Type* out_value, const char* desc) WABT_WARN_UNUSED; + Result ReadExternalKind(ExternalKind* out_value, + const char* desc) WABT_WARN_UNUSED; + Result ReadStr(string_view* out_str, const char* desc) WABT_WARN_UNUSED; + Result ReadBytes(const void** out_data, + Address* out_data_size, + const char* desc) WABT_WARN_UNUSED; + Result ReadIndex(Index* index, const char* desc) WABT_WARN_UNUSED; + Result ReadOffset(Offset* offset, const char* desc) WABT_WARN_UNUSED; + Result ReadAlignment(Address* align_log2, const char* desc) WABT_WARN_UNUSED; + Result ReadCount(Index* index, const char* desc) WABT_WARN_UNUSED; + Result ReadField(TypeMut* out_value) WABT_WARN_UNUSED; + + bool IsConcreteType(Type); + bool IsBlockType(Type); + + Index NumTotalFuncs(); + + Result ReadInitExpr(Index index, Type required = Type::Any) WABT_WARN_UNUSED; + Result ReadTable(Type* out_elem_type, + Limits* out_elem_limits) WABT_WARN_UNUSED; + Result ReadMemory(Limits* out_page_limits) WABT_WARN_UNUSED; + Result ReadGlobalHeader(Type* out_type, bool* out_mutable) WABT_WARN_UNUSED; + Result ReadTagType(Index* out_sig_index) WABT_WARN_UNUSED; + Result ReadAddress(Address* out_value, + Index memory, + const char* desc) WABT_WARN_UNUSED; + Result ReadFunctionBody(Offset end_offset) WABT_WARN_UNUSED; + Result ReadNameSection(Offset section_size) WABT_WARN_UNUSED; + Result ReadRelocSection(Offset section_size) WABT_WARN_UNUSED; + Result ReadDylinkSection(Offset section_size) WABT_WARN_UNUSED; + Result ReadDylink0Section(Offset section_size) WABT_WARN_UNUSED; + Result ReadLinkingSection(Offset section_size) WABT_WARN_UNUSED; + Result ReadCustomSection(Index section_index, + Offset section_size) WABT_WARN_UNUSED; + Result ReadTypeSection(Offset section_size) WABT_WARN_UNUSED; + Result ReadImportSection(Offset section_size) WABT_WARN_UNUSED; + Result ReadFunctionSection(Offset section_size) WABT_WARN_UNUSED; + Result ReadTableSection(Offset section_size) WABT_WARN_UNUSED; + Result ReadMemorySection(Offset section_size) WABT_WARN_UNUSED; + Result ReadGlobalSection(Offset section_size) WABT_WARN_UNUSED; + Result ReadExportSection(Offset section_size) WABT_WARN_UNUSED; + Result ReadStartSection(Offset section_size) WABT_WARN_UNUSED; + Result ReadElemSection(Offset section_size) WABT_WARN_UNUSED; + Result ReadCodeSection(Offset section_size) WABT_WARN_UNUSED; + Result ReadDataSection(Offset section_size) WABT_WARN_UNUSED; + Result ReadDataCountSection(Offset section_size) WABT_WARN_UNUSED; + Result ReadTagSection(Offset section_size) WABT_WARN_UNUSED; + Result ReadSections() WABT_WARN_UNUSED; + 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)); + *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(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 = 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 >= 32) { + PrintError("invalid %s: %u", desc, value); + return Result::Error; + } + *alignment_log2 = value; + 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(); + + 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, Type required) { + Opcode opcode; + CHECK_RESULT(ReadOpcode(&opcode, "opcode")); + ERROR_UNLESS_OPCODE_ENABLED(opcode); + + switch (opcode) { + case Opcode::I32Const: { + uint32_t value = 0; + CHECK_RESULT(ReadS32Leb128(&value, "init_expr i32.const value")); + CALLBACK(OnInitExprI32ConstExpr, index, value); + break; + } + + case Opcode::I64Const: { + uint64_t value = 0; + CHECK_RESULT(ReadS64Leb128(&value, "init_expr i64.const value")); + CALLBACK(OnInitExprI64ConstExpr, index, value); + break; + } + + case Opcode::F32Const: { + uint32_t value_bits = 0; + CHECK_RESULT(ReadF32(&value_bits, "init_expr f32.const value")); + CALLBACK(OnInitExprF32ConstExpr, index, value_bits); + break; + } + + case Opcode::F64Const: { + uint64_t value_bits = 0; + CHECK_RESULT(ReadF64(&value_bits, "init_expr f64.const value")); + CALLBACK(OnInitExprF64ConstExpr, index, value_bits); + break; + } + + case Opcode::V128Const: { + v128 value_bits; + ZeroMemory(value_bits); + CHECK_RESULT(ReadV128(&value_bits, "init_expr v128.const value")); + CALLBACK(OnInitExprV128ConstExpr, index, value_bits); + break; + } + + case Opcode::GlobalGet: { + Index global_index; + CHECK_RESULT(ReadIndex(&global_index, "init_expr global.get index")); + CALLBACK(OnInitExprGlobalGetExpr, index, global_index); + break; + } + + case Opcode::RefNull: { + Type type; + CHECK_RESULT(ReadRefType(&type, "ref.null type")); + CALLBACK(OnInitExprRefNull, index, type); + break; + } + + case Opcode::RefFunc: { + Index func_index; + CHECK_RESULT(ReadIndex(&func_index, "init_expr ref.func index")); + CALLBACK(OnInitExprRefFunc, index, func_index); + break; + } + + case Opcode::End: + return Result::Ok; + + default: + return ReportUnexpectedOpcode(opcode, "in initializer expression"); + } + + if (required == Type::I32 && opcode != Opcode::I32Const && + opcode != Opcode::GlobalGet) { + PrintError("expected i32 init_expr"); + return Result::Error; + } + if (required == Type::I64 && opcode != Opcode::I64Const && + opcode != Opcode::GlobalGet) { + PrintError("expected i64 init_expr"); + return Result::Error; + } + + CHECK_RESULT(ReadOpcode(&opcode, "opcode")); + ERROR_UNLESS(opcode == Opcode::End, + "expected END opcode after initializer expression"); + 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) { + bool seen_end_opcode = false; + while (state_.offset < end_offset) { + Opcode opcode; + CHECK_RESULT(ReadOpcode(&opcode, "opcode")); + CALLBACK(OnOpcode, opcode); + ERROR_UNLESS_OPCODE_ENABLED(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; + } + + Type* result_types = num_results ? result_types_.data() : nullptr; + CALLBACK(OnSelectExpr, num_results, result_types); + 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: + if (state_.offset == end_offset) { + seen_end_opcode = true; + CALLBACK0(OnEndFunc); + } else { + CALLBACK0(OnEndExpr); + } + 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; + CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment")); + Address offset; + CHECK_RESULT(ReadAddress(&offset, 0, "load offset")); + CALLBACK(OnLoadExpr, opcode, alignment_log2, offset); + CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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; + CHECK_RESULT(ReadAlignment(&alignment_log2, "store alignment")); + Address offset; + CHECK_RESULT(ReadAddress(&offset, 0, "store offset")); + + CALLBACK(OnStoreExpr, opcode, alignment_log2, offset); + CALLBACK(OnOpcodeUint32Uint32, alignment_log2, offset); + break; + } + + case Opcode::MemorySize: { + uint8_t reserved; + CHECK_RESULT(ReadU8(&reserved, "memory.size reserved")); + ERROR_UNLESS(reserved == 0, "memory.size reserved value must be 0"); + CALLBACK0(OnMemorySizeExpr); + CALLBACK(OnOpcodeUint32, reserved); + break; + } + + case Opcode::MemoryGrow: { + uint8_t reserved; + CHECK_RESULT(ReadU8(&reserved, "memory.grow reserved")); + ERROR_UNLESS(reserved == 0, "memory.grow reserved value must be 0"); + CALLBACK0(OnMemoryGrowExpr); + CALLBACK(OnOpcodeUint32, reserved); + 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: + 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: + CALLBACK(OnUnaryExpr, opcode); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::V128BitSelect: + 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; + CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment")); + Address offset; + CHECK_RESULT(ReadAddress(&offset, 0, "load offset")); + + CALLBACK(OnLoadSplatExpr, opcode, alignment_log2, offset); + CALLBACK(OnOpcodeUint32Uint32, alignment_log2, offset); + break; + } + case Opcode::V128Load8Lane: + case Opcode::V128Load16Lane: + case Opcode::V128Load32Lane: + case Opcode::V128Load64Lane: { + Address alignment_log2; + CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment")); + Address offset; + CHECK_RESULT(ReadAddress(&offset, 0, "load offset")); + uint8_t lane_val; + CHECK_RESULT(ReadU8(&lane_val, "Lane idx")); + + CALLBACK(OnSimdLoadLaneExpr, opcode, alignment_log2, offset, lane_val); + CALLBACK(OnOpcodeUint32Uint32Uint32, alignment_log2, offset, lane_val); + break; + } + case Opcode::V128Store8Lane: + case Opcode::V128Store16Lane: + case Opcode::V128Store32Lane: + case Opcode::V128Store64Lane: { + Address alignment_log2; + CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment")); + Address offset; + CHECK_RESULT(ReadAddress(&offset, 0, "load offset")); + uint8_t lane_val; + CHECK_RESULT(ReadU8(&lane_val, "Lane idx")); + + CALLBACK(OnSimdStoreLaneExpr, opcode, alignment_log2, offset, lane_val); + CALLBACK(OnOpcodeUint32Uint32Uint32, alignment_log2, offset, lane_val); + break; + } + case Opcode::V128Load32Zero: + case Opcode::V128Load64Zero: { + Address alignment_log2; + CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment")); + Address offset; + CHECK_RESULT(ReadAddress(&offset, 0, "load offset")); + + CALLBACK(OnLoadZeroExpr, opcode, alignment_log2, offset); + CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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; + CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment")); + Address offset; + CHECK_RESULT(ReadAddress(&offset, 0, "load offset")); + + CALLBACK(OnAtomicNotifyExpr, opcode, alignment_log2, offset); + CALLBACK(OnOpcodeUint32Uint32, alignment_log2, offset); + break; + } + + case Opcode::MemoryAtomicWait32: + case Opcode::MemoryAtomicWait64: { + Address alignment_log2; + CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment")); + Address offset; + CHECK_RESULT(ReadAddress(&offset, 0, "load offset")); + + CALLBACK(OnAtomicWaitExpr, opcode, alignment_log2, offset); + CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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; + CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment")); + Address offset; + CHECK_RESULT(ReadAddress(&offset, 0, "load offset")); + + CALLBACK(OnAtomicLoadExpr, opcode, alignment_log2, offset); + CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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; + CHECK_RESULT(ReadAlignment(&alignment_log2, "store alignment")); + Address offset; + CHECK_RESULT(ReadAddress(&offset, 0, "store offset")); + + CALLBACK(OnAtomicStoreExpr, opcode, alignment_log2, offset); + CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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; + CHECK_RESULT(ReadAlignment(&alignment_log2, "memory alignment")); + Address offset; + CHECK_RESULT(ReadAddress(&offset, 0, "memory offset")); + + CALLBACK(OnAtomicRmwExpr, opcode, alignment_log2, offset); + CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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; + CHECK_RESULT(ReadAlignment(&alignment_log2, "memory alignment")); + Address offset; + CHECK_RESULT(ReadAddress(&offset, 0, "memory offset")); + + CALLBACK(OnAtomicRmwCmpxchgExpr, opcode, alignment_log2, offset); + CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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")); + uint8_t reserved; + CHECK_RESULT(ReadU8(&reserved, "reserved memory index")); + ERROR_UNLESS(reserved == 0, "reserved value must be 0"); + CALLBACK(OnMemoryInitExpr, segment); + CALLBACK(OnOpcodeUint32Uint32, segment, reserved); + 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: { + uint8_t reserved; + CHECK_RESULT(ReadU8(&reserved, "reserved memory index")); + ERROR_UNLESS(reserved == 0, "reserved value must be 0"); + CALLBACK(OnMemoryFillExpr); + CALLBACK(OnOpcodeUint32, reserved); + break; + } + case Opcode::MemoryCopy: { + 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"); + CALLBACK(OnMemoryCopyExpr); + CALLBACK(OnOpcodeUint32Uint32, reserved, reserved); + 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); + } + } + ERROR_UNLESS(state_.offset == end_offset, + "function body longer than given size"); + ERROR_UNLESS(seen_end_opcode, "function body must end with END 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) { + 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; + 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; + 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: + 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; + 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(§ion_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; + + 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: { + uint32_t count; + CHECK_RESULT(ReadU32Leb128(&count, "needed_dynlibs")); + CALLBACK(OnDylinkNeededCount, count); + while (count--) { + string_view so_name; + CHECK_RESULT(ReadStr(&so_name, "dylib so_name")); + CALLBACK(OnDylinkNeeded, so_name); + } + 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--) { + string_view so_name; + CHECK_RESULT(ReadStr(&so_name, "dylib so_name")); + CALLBACK(OnDylinkNeeded, so_name); + } + + CALLBACK0(EndDylinkSection); + 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) { + 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++) { + 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 func; + CHECK_RESULT(ReadU32Leb128(&priority, "priority")); + CHECK_RESULT(ReadU32Leb128(&func, "function index")); + CALLBACK(OnInitFunction, priority, func); + } + break; + case LinkingEntryType::ComdatInfo: + CHECK_RESULT(ReadU32Leb128(&count, "count")); + CALLBACK(OnComdatCount, count); + while (count--) { + uint32_t flags; + uint32_t entry_count; + 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::ReadCustomSection(Index section_index, + Offset section_size) { + string_view section_name; + CHECK_RESULT(ReadStr(§ion_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_LINKING) { + CHECK_RESULT(ReadLinkingSection(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 " PRItypecode ")", + WABT_PRINTF_TYPE_CODE(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(¶m_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) { + string_view module_name; + CHECK_RESULT(ReadStr(&module_name, "import module name")); + 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) { + 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, Type::I32)); + 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()); + 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 < 0x10000000"); + 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); + } + + 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)) { + CALLBACK(BeginDataSegmentInitExpr, i); + CHECK_RESULT(ReadInitExpr(i, memories[0].IndexType())); + 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() { + 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(§ion_code, "section code")); + CHECK_RESULT(ReadOffset(§ion_size, "section size")); + ReadEndRestoreGuard guard(this); + read_end_ = state_.offset + section_size; + if (section_code >= kBinarySectionCount) { + PrintError("invalid section code: %u", section_code); + return Result::Error; + } + + 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() { + 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()); + // 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(); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary-reader.h b/third_party/wasm2c/src/binary-reader.h new file mode 100644 index 0000000000..2871d80fd6 --- /dev/null +++ b/third_party/wasm2c/src/binary-reader.h @@ -0,0 +1,479 @@ +/* + * 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_BINARY_READER_H_ +#define WABT_BINARY_READER_H_ + +#include <stddef.h> +#include <stdint.h> + +#include "src/binary.h" +#include "src/common.h" +#include "src/error.h" +#include "src/feature.h" +#include "src/opcode.h" +#include "src/string-view.h" + +namespace wabt { + +class Stream; + +struct ReadBinaryOptions { + ReadBinaryOptions() = default; + ReadBinaryOptions(const Features& features, + Stream* log_stream, + bool read_debug_names, + bool stop_on_first_error, + bool fail_on_custom_section_error) + : features(features), + log_stream(log_stream), + read_debug_names(read_debug_names), + stop_on_first_error(stop_on_first_error), + fail_on_custom_section_error(fail_on_custom_section_error) {} + + Features features; + Stream* log_stream = nullptr; + bool read_debug_names = false; + bool stop_on_first_error = true; + bool fail_on_custom_section_error = true; +}; + +// TODO: Move somewhere else? +struct TypeMut { + Type type; + bool mutable_; +}; +using TypeMutVector = std::vector<TypeMut>; + +class BinaryReaderDelegate { + public: + struct State { + State(const uint8_t* data, Offset size) + : data(data), size(size), offset(0) {} + + const uint8_t* data; + Offset size; + Offset offset; + }; + + virtual ~BinaryReaderDelegate() {} + + virtual bool OnError(const Error&) = 0; + virtual void OnSetState(const State* s) { state = s; } + + /* Module */ + virtual Result BeginModule(uint32_t version) = 0; + virtual Result EndModule() = 0; + + virtual Result BeginSection(Index section_index, + BinarySection section_type, + Offset size) = 0; + + /* Custom section */ + virtual Result BeginCustomSection(Index section_index, + Offset size, + string_view section_name) = 0; + virtual Result EndCustomSection() = 0; + + /* Type section */ + virtual Result BeginTypeSection(Offset size) = 0; + virtual Result OnTypeCount(Index count) = 0; + virtual Result OnFuncType(Index index, + Index param_count, + Type* param_types, + Index result_count, + Type* result_types) = 0; + virtual Result OnStructType(Index index, + Index field_count, + TypeMut* fields) = 0; + virtual Result OnArrayType(Index index, TypeMut field) = 0; + virtual Result EndTypeSection() = 0; + + /* Import section */ + virtual Result BeginImportSection(Offset size) = 0; + virtual Result OnImportCount(Index count) = 0; + virtual Result OnImport(Index index, + ExternalKind kind, + string_view module_name, + string_view field_name) = 0; + virtual Result OnImportFunc(Index import_index, + string_view module_name, + string_view field_name, + Index func_index, + Index sig_index) = 0; + virtual Result OnImportTable(Index import_index, + string_view module_name, + string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) = 0; + virtual Result OnImportMemory(Index import_index, + string_view module_name, + string_view field_name, + Index memory_index, + const Limits* page_limits) = 0; + virtual Result OnImportGlobal(Index import_index, + string_view module_name, + string_view field_name, + Index global_index, + Type type, + bool mutable_) = 0; + virtual Result OnImportTag(Index import_index, + string_view module_name, + string_view field_name, + Index tag_index, + Index sig_index) = 0; + virtual Result EndImportSection() = 0; + + /* Function section */ + virtual Result BeginFunctionSection(Offset size) = 0; + virtual Result OnFunctionCount(Index count) = 0; + virtual Result OnFunction(Index index, Index sig_index) = 0; + virtual Result EndFunctionSection() = 0; + + /* Table section */ + virtual Result BeginTableSection(Offset size) = 0; + virtual Result OnTableCount(Index count) = 0; + virtual Result OnTable(Index index, + Type elem_type, + const Limits* elem_limits) = 0; + virtual Result EndTableSection() = 0; + + /* Memory section */ + virtual Result BeginMemorySection(Offset size) = 0; + virtual Result OnMemoryCount(Index count) = 0; + virtual Result OnMemory(Index index, const Limits* limits) = 0; + virtual Result EndMemorySection() = 0; + + /* Global section */ + virtual Result BeginGlobalSection(Offset size) = 0; + virtual Result OnGlobalCount(Index count) = 0; + virtual Result BeginGlobal(Index index, Type type, bool mutable_) = 0; + virtual Result BeginGlobalInitExpr(Index index) = 0; + virtual Result EndGlobalInitExpr(Index index) = 0; + virtual Result EndGlobal(Index index) = 0; + virtual Result EndGlobalSection() = 0; + + /* Exports section */ + virtual Result BeginExportSection(Offset size) = 0; + virtual Result OnExportCount(Index count) = 0; + virtual Result OnExport(Index index, + ExternalKind kind, + Index item_index, + string_view name) = 0; + virtual Result EndExportSection() = 0; + + /* Start section */ + virtual Result BeginStartSection(Offset size) = 0; + virtual Result OnStartFunction(Index func_index) = 0; + virtual Result EndStartSection() = 0; + + /* Code section */ + virtual Result BeginCodeSection(Offset size) = 0; + virtual Result OnFunctionBodyCount(Index count) = 0; + virtual Result BeginFunctionBody(Index index, Offset size) = 0; + virtual Result OnLocalDeclCount(Index count) = 0; + virtual Result OnLocalDecl(Index decl_index, Index count, Type type) = 0; + + /* Function expressions; called between BeginFunctionBody and + EndFunctionBody */ + virtual Result OnOpcode(Opcode Opcode) = 0; + virtual Result OnOpcodeBare() = 0; + virtual Result OnOpcodeUint32(uint32_t value) = 0; + virtual Result OnOpcodeIndex(Index value) = 0; + virtual Result OnOpcodeIndexIndex(Index value, Index value2) = 0; + virtual Result OnOpcodeUint32Uint32(uint32_t value, uint32_t value2) = 0; + virtual Result OnOpcodeUint32Uint32Uint32(uint32_t value, + uint32_t value2, + uint32_t value3) = 0; + virtual Result OnOpcodeUint64(uint64_t value) = 0; + virtual Result OnOpcodeF32(uint32_t value) = 0; + virtual Result OnOpcodeF64(uint64_t value) = 0; + virtual Result OnOpcodeV128(v128 value) = 0; + virtual Result OnOpcodeBlockSig(Type sig_type) = 0; + virtual Result OnOpcodeType(Type type) = 0; + virtual Result OnAtomicLoadExpr(Opcode opcode, + Address alignment_log2, + Address offset) = 0; + virtual Result OnAtomicStoreExpr(Opcode opcode, + Address alignment_log2, + Address offset) = 0; + virtual Result OnAtomicRmwExpr(Opcode opcode, + Address alignment_log2, + Address offset) = 0; + virtual Result OnAtomicRmwCmpxchgExpr(Opcode opcode, + Address alignment_log2, + Address offset) = 0; + virtual Result OnAtomicWaitExpr(Opcode opcode, + Address alignment_log2, + Address offset) = 0; + virtual Result OnAtomicFenceExpr(uint32_t consistency_model) = 0; + virtual Result OnAtomicNotifyExpr(Opcode opcode, + Address alignment_log2, + Address offset) = 0; + virtual Result OnBinaryExpr(Opcode opcode) = 0; + virtual Result OnBlockExpr(Type sig_type) = 0; + virtual Result OnBrExpr(Index depth) = 0; + virtual Result OnBrIfExpr(Index depth) = 0; + virtual Result OnBrTableExpr(Index num_targets, + Index* target_depths, + Index default_target_depth) = 0; + virtual Result OnCallExpr(Index func_index) = 0; + virtual Result OnCallIndirectExpr(Index sig_index, Index table_index) = 0; + virtual Result OnCallRefExpr() = 0; + virtual Result OnCatchExpr(Index tag_index) = 0; + virtual Result OnCatchAllExpr() = 0; + virtual Result OnCompareExpr(Opcode opcode) = 0; + virtual Result OnConvertExpr(Opcode opcode) = 0; + virtual Result OnDelegateExpr(Index depth) = 0; + virtual Result OnDropExpr() = 0; + virtual Result OnElseExpr() = 0; + virtual Result OnEndExpr() = 0; + virtual Result OnEndFunc() = 0; + virtual Result OnF32ConstExpr(uint32_t value_bits) = 0; + virtual Result OnF64ConstExpr(uint64_t value_bits) = 0; + virtual Result OnV128ConstExpr(v128 value_bits) = 0; + virtual Result OnGlobalGetExpr(Index global_index) = 0; + virtual Result OnGlobalSetExpr(Index global_index) = 0; + virtual Result OnI32ConstExpr(uint32_t value) = 0; + virtual Result OnI64ConstExpr(uint64_t value) = 0; + virtual Result OnIfExpr(Type sig_type) = 0; + virtual Result OnLoadExpr(Opcode opcode, + Address alignment_log2, + Address offset) = 0; + virtual Result OnLocalGetExpr(Index local_index) = 0; + virtual Result OnLocalSetExpr(Index local_index) = 0; + virtual Result OnLocalTeeExpr(Index local_index) = 0; + virtual Result OnLoopExpr(Type sig_type) = 0; + virtual Result OnMemoryCopyExpr() = 0; + virtual Result OnDataDropExpr(Index segment_index) = 0; + virtual Result OnMemoryFillExpr() = 0; + virtual Result OnMemoryGrowExpr() = 0; + virtual Result OnMemoryInitExpr(Index segment_index) = 0; + virtual Result OnMemorySizeExpr() = 0; + virtual Result OnTableCopyExpr(Index dst_index, Index src_index) = 0; + virtual Result OnElemDropExpr(Index segment_index) = 0; + virtual Result OnTableInitExpr(Index segment_index, Index table_index) = 0; + virtual Result OnTableGetExpr(Index table_index) = 0; + virtual Result OnTableSetExpr(Index table_index) = 0; + virtual Result OnTableGrowExpr(Index table_index) = 0; + virtual Result OnTableSizeExpr(Index table_index) = 0; + virtual Result OnTableFillExpr(Index table_index) = 0; + virtual Result OnRefFuncExpr(Index func_index) = 0; + virtual Result OnRefNullExpr(Type type) = 0; + virtual Result OnRefIsNullExpr() = 0; + virtual Result OnNopExpr() = 0; + virtual Result OnRethrowExpr(Index depth) = 0; + virtual Result OnReturnExpr() = 0; + virtual Result OnReturnCallExpr(Index func_index) = 0; + virtual Result OnReturnCallIndirectExpr(Index sig_index, + Index table_index) = 0; + virtual Result OnSelectExpr(Index result_count, Type* result_types) = 0; + virtual Result OnStoreExpr(Opcode opcode, + Address alignment_log2, + Address offset) = 0; + virtual Result OnThrowExpr(Index tag_index) = 0; + virtual Result OnTryExpr(Type sig_type) = 0; + + virtual Result OnUnaryExpr(Opcode opcode) = 0; + virtual Result OnTernaryExpr(Opcode opcode) = 0; + virtual Result OnUnreachableExpr() = 0; + virtual Result EndFunctionBody(Index index) = 0; + virtual Result EndCodeSection() = 0; + + /* Simd instructions with Lane Imm operand*/ + virtual Result OnSimdLaneOpExpr(Opcode opcode, uint64_t value) = 0; + virtual Result OnSimdShuffleOpExpr(Opcode opcode, v128 value) = 0; + virtual Result OnSimdLoadLaneExpr(Opcode opcode, + Address alignment_log2, + Address offset, + uint64_t value) = 0; + virtual Result OnSimdStoreLaneExpr(Opcode opcode, + Address alignment_log2, + Address offset, + uint64_t value) = 0; + + virtual Result OnLoadSplatExpr(Opcode opcode, + Address alignment_log2, + Address offset) = 0; + virtual Result OnLoadZeroExpr(Opcode opcode, + Address alignment_log2, + Address offset) = 0; + + /* Elem section */ + virtual Result BeginElemSection(Offset size) = 0; + virtual Result OnElemSegmentCount(Index count) = 0; + virtual Result BeginElemSegment(Index index, + Index table_index, + uint8_t flags) = 0; + virtual Result BeginElemSegmentInitExpr(Index index) = 0; + virtual Result EndElemSegmentInitExpr(Index index) = 0; + virtual Result OnElemSegmentElemType(Index index, Type elem_type) = 0; + virtual Result OnElemSegmentElemExprCount(Index index, Index count) = 0; + virtual Result OnElemSegmentElemExpr_RefNull(Index segment_index, + Type type) = 0; + virtual Result OnElemSegmentElemExpr_RefFunc(Index segment_index, + Index func_index) = 0; + virtual Result EndElemSegment(Index index) = 0; + virtual Result EndElemSection() = 0; + + /* Data section */ + virtual Result BeginDataSection(Offset size) = 0; + virtual Result OnDataSegmentCount(Index count) = 0; + virtual Result BeginDataSegment(Index index, + Index memory_index, + uint8_t flags) = 0; + virtual Result BeginDataSegmentInitExpr(Index index) = 0; + virtual Result EndDataSegmentInitExpr(Index index) = 0; + virtual Result OnDataSegmentData(Index index, + const void* data, + Address size) = 0; + virtual Result EndDataSegment(Index index) = 0; + virtual Result EndDataSection() = 0; + + /* DataCount section */ + virtual Result BeginDataCountSection(Offset size) = 0; + virtual Result OnDataCount(Index count) = 0; + virtual Result EndDataCountSection() = 0; + + /* Names section */ + virtual Result BeginNamesSection(Offset size) = 0; + virtual Result OnModuleNameSubsection(Index index, + uint32_t name_type, + Offset subsection_size) = 0; + virtual Result OnModuleName(string_view name) = 0; + virtual Result OnFunctionNameSubsection(Index index, + uint32_t name_type, + Offset subsection_size) = 0; + virtual Result OnFunctionNamesCount(Index num_functions) = 0; + virtual Result OnFunctionName(Index function_index, + string_view function_name) = 0; + virtual Result OnLocalNameSubsection(Index index, + uint32_t name_type, + Offset subsection_size) = 0; + virtual Result OnLocalNameFunctionCount(Index num_functions) = 0; + virtual Result OnLocalNameLocalCount(Index function_index, + Index num_locals) = 0; + virtual Result OnLocalName(Index function_index, + Index local_index, + string_view local_name) = 0; + virtual Result OnNameSubsection(Index index, + NameSectionSubsection subsection_type, + Offset subsection_size) = 0; + virtual Result OnNameCount(Index num_names) = 0; + virtual Result OnNameEntry(NameSectionSubsection type, + Index index, + string_view name) = 0; + virtual Result EndNamesSection() = 0; + + /* Reloc section */ + virtual Result BeginRelocSection(Offset size) = 0; + virtual Result OnRelocCount(Index count, + Index section_index) = 0; + virtual Result OnReloc(RelocType type, + Offset offset, + Index index, + uint32_t addend) = 0; + virtual Result EndRelocSection() = 0; + + /* Dylink section */ + virtual Result BeginDylinkSection(Offset size) = 0; + virtual Result OnDylinkInfo(uint32_t mem_size, + uint32_t mem_align_log2, + uint32_t table_size, + uint32_t table_align_log2) = 0; + virtual Result OnDylinkNeededCount(Index count) = 0; + virtual Result OnDylinkNeeded(string_view so_name) = 0; + virtual Result EndDylinkSection() = 0; + + /* Linking section */ + virtual Result BeginLinkingSection(Offset size) = 0; + virtual Result OnSymbolCount(Index count) = 0; + virtual Result OnDataSymbol(Index index, + uint32_t flags, + string_view name, + Index segment, + uint32_t offset, + uint32_t size) = 0; + virtual Result OnFunctionSymbol(Index index, + uint32_t flags, + string_view name, + Index function_index) = 0; + virtual Result OnGlobalSymbol(Index index, + uint32_t flags, + string_view name, + Index global_index) = 0; + virtual Result OnSectionSymbol(Index index, + uint32_t flags, + Index section_index) = 0; + virtual Result OnTagSymbol(Index index, + uint32_t flags, + string_view name, + Index tag_index) = 0; + virtual Result OnTableSymbol(Index index, + uint32_t flags, + string_view name, + Index table_index) = 0; + virtual Result OnSegmentInfoCount(Index count) = 0; + virtual Result OnSegmentInfo(Index index, + string_view name, + Address alignment_log2, + uint32_t flags) = 0; + virtual Result OnInitFunctionCount(Index count) = 0; + virtual Result OnInitFunction(uint32_t priority, Index function_index) = 0; + virtual Result OnComdatCount(Index count) = 0; + virtual Result OnComdatBegin(string_view name, + uint32_t flags, + Index count) = 0; + virtual Result OnComdatEntry(ComdatType kind, Index index) = 0; + virtual Result EndLinkingSection() = 0; + + /* Tag section */ + virtual Result BeginTagSection(Offset size) = 0; + virtual Result OnTagCount(Index count) = 0; + virtual Result OnTagType(Index index, Index sig_index) = 0; + virtual Result EndTagSection() = 0; + + /* InitExpr - used by elem, data and global sections; these functions are + * only called between calls to Begin*InitExpr and End*InitExpr */ + virtual Result OnInitExprF32ConstExpr(Index index, uint32_t value) = 0; + virtual Result OnInitExprF64ConstExpr(Index index, uint64_t value) = 0; + virtual Result OnInitExprV128ConstExpr(Index index, v128 value) = 0; + virtual Result OnInitExprGlobalGetExpr(Index index, Index global_index) = 0; + virtual Result OnInitExprI32ConstExpr(Index index, uint32_t value) = 0; + virtual Result OnInitExprI64ConstExpr(Index index, uint64_t value) = 0; + virtual Result OnInitExprRefNull(Index index, Type type) = 0; + virtual Result OnInitExprRefFunc(Index index, Index func_index) = 0; + + const State* state = nullptr; +}; + +Result ReadBinary(const void* data, + size_t size, + BinaryReaderDelegate* reader, + const ReadBinaryOptions& options); + +size_t ReadU32Leb128(const uint8_t* ptr, + const uint8_t* end, + uint32_t* out_value); + +size_t ReadI32Leb128(const uint8_t* ptr, + const uint8_t* end, + uint32_t* out_value); + +} // namespace wabt + +#endif /* WABT_BINARY_READER_H_ */ 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..13e71122e5 --- /dev/null +++ b/third_party/wasm2c/src/binary-writer-spec.cc @@ -0,0 +1,630 @@ +/* + * 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 "src/binary-writer-spec.h" + +#include <cassert> +#include <cinttypes> + +#include "config.h" + +#include "src/binary-writer.h" +#include "src/binary.h" +#include "src/cast.h" +#include "src/filenames.h" +#include "src/ir.h" +#include "src/literal.h" +#include "src/stream.h" +#include "src/string-view.h" + +namespace wabt { + +namespace { + +class BinaryWriterSpec { + public: + BinaryWriterSpec(Stream* json_stream, + WriteBinarySpecStreamFactory module_stream_factory, + string_view source_filename, + 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(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(string_view filename, const Module& module); + void WriteScriptModule(string_view filename, + const ScriptModule& script_module); + void WriteInvalidModule(const ScriptModule& module, 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, + string_view source_filename, + 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(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", + "action", + "register", + "assert_malformed", + "assert_invalid", + "assert_unlinkable", + "assert_uninstantiable", + "assert_return", + "assert_trap", + "assert_exhaustion", + }; + 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()); + 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()); + 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(string_view filename, const Module& module) { + result_ |= + WriteBinaryModule(module_stream_factory_(filename), &module, options_); +} + +void BinaryWriterSpec::WriteScriptModule(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, + 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::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(); + WriteKey("expected"); + WriteConstVector(assert_return_command->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; + } + } + + 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, + string_view source_filename, + 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, + string_view source_filename, + string_view module_filename_noext, + const WriteBinaryOptions& options, + std::vector<FilenameMemoryStreamPair>* out_module_streams, + Stream* log_stream) { + WriteBinarySpecStreamFactory module_stream_factory = + [&](string_view filename) { + out_module_streams->emplace_back(filename, + MakeUnique<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-spec.h b/third_party/wasm2c/src/binary-writer-spec.h new file mode 100644 index 0000000000..197d6f2c3a --- /dev/null +++ b/third_party/wasm2c/src/binary-writer-spec.h @@ -0,0 +1,60 @@ +/* + * 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_BINARY_WRITER_SPEC_H_ +#define WABT_BINARY_WRITER_SPEC_H_ + +#include <functional> +#include <utility> +#include <vector> + +#include "src/binary-writer.h" +#include "src/common.h" +#include "src/ir.h" + +namespace wabt { + +struct FilenameMemoryStreamPair { + FilenameMemoryStreamPair(string_view filename, + std::unique_ptr<MemoryStream> stream) + : filename(filename), stream(std::move(stream)) {} + std::string filename; + std::unique_ptr<MemoryStream> stream; +}; + +typedef std::function<Stream*(string_view filename)> + WriteBinarySpecStreamFactory; + +Result WriteBinarySpecScript(Stream* json_stream, + WriteBinarySpecStreamFactory module_stream_factory, + Script*, + string_view source_filename, + string_view module_filename_noext, + const WriteBinaryOptions&); + +// Convenience function for producing MemoryStream outputs all modules. +Result WriteBinarySpecScript( + Stream* json_stream, + Script*, + string_view source_filename, + string_view module_filename_noext, + const WriteBinaryOptions&, + std::vector<FilenameMemoryStreamPair>* out_module_streams, + Stream* log_stream = nullptr); + +} // namespace wabt + +#endif /* WABT_BINARY_WRITER_SPEC_H_ */ diff --git a/third_party/wasm2c/src/binary-writer.cc b/third_party/wasm2c/src/binary-writer.cc new file mode 100644 index 0000000000..aae2b4bf5e --- /dev/null +++ b/third_party/wasm2c/src/binary-writer.cc @@ -0,0 +1,1675 @@ +/* + * 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 "src/binary-writer.h" + +#include <cassert> +#include <cmath> +#include <cstdarg> +#include <cstdint> +#include <cstdio> +#include <set> +#include <vector> + +#include "config.h" + +#include "src/binary.h" +#include "src/cast.h" +#include "src/ir.h" +#include "src/leb128.h" +#include "src/stream.h" +#include "src/string-view.h" + +#define PRINT_HEADER_NO_INDEX -1 +#define MAX_U32_LEB128_BYTES 5 + +namespace wabt { + +void WriteStr(Stream* stream, + 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()); +} + +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, string_view name, const char* desc) { + 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. */ +static const 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_; + string_view name_; + uint8_t flags_; + union { + Function function_; + Data data_; + Global global_; + Section section_; + Tag tag_; + Table table_; + }; + + public: + Symbol(const string_view& name, uint8_t flags, const Function& f) + : type_(Function::type), name_(name), flags_(flags), function_(f) {} + Symbol(const string_view& name, uint8_t flags, const Data& d) + : type_(Data::type), name_(name), flags_(flags), data_(d) {} + Symbol(const string_view& name, uint8_t flags, const Global& g) + : type_(Global::type), name_(name), flags_(flags), global_(g) {} + Symbol(const string_view& name, uint8_t flags, const Section& s) + : type_(Section::type), name_(name), flags_(flags), section_(s) {} + Symbol(const string_view& name, uint8_t flags, const Tag& e) + : type_(Tag::type), name_(name), flags_(flags), tag_(e) {} + Symbol(const string_view& name, uint8_t flags, const Table& t) + : type_(Table::type), name_(name), flags_(flags), table_(t) {} + + SymbolType type() const { return type_; } + const 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<string_view> seen_names_; + + Result EnsureUnique(const 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, 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 = 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); + } +}; + +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); + + 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; +}; + +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); + 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) { + auto* typed_expr = cast<T>(expr); + WriteOpcode(stream_, typed_expr->opcode); + Address align = typed_expr->opcode.GetAlignment(typed_expr->align); + stream_->WriteU8(log2_u32(align), "alignment"); + WriteU32Leb128(stream_, typed_expr->offset, desc); + 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: + WriteOpcode(stream_, Opcode::MemoryCopy); + WriteU32Leb128(stream_, 0, "memory.copy reserved"); + WriteU32Leb128(stream_, 0, "memory.copy reserved"); + 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: + WriteOpcode(stream_, Opcode::MemoryFill); + WriteU32Leb128(stream_, 0, "memory.fill reserved"); + break; + case ExprType::MemoryGrow: + WriteOpcode(stream_, Opcode::MemoryGrow); + WriteU32Leb128(stream_, 0, "memory.grow reserved"); + break; + case ExprType::MemoryInit: { + Index index = + module_->GetDataSegmentIndex(cast<MemoryInitExpr>(expr)->var); + WriteOpcode(stream_, Opcode::MemoryInit); + WriteU32Leb128(stream_, index, "memory.init segment"); + WriteU32Leb128(stream_, 0, "memory.init reserved"); + has_data_segment_instruction_ = true; + break; + } + case ExprType::MemorySize: + WriteOpcode(stream_, Opcode::MemorySize); + WriteU32Leb128(stream_, 0, "memory.size reserved"); + 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; + } +} + +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 ElemExpr& elem_expr : segment->elem_exprs) { + switch (elem_expr.kind) { + case ElemExprKind::RefNull: + WriteOpcode(stream_, Opcode::RefNull); + WriteType(stream_, elem_expr.type, "elem expr ref.null type"); + break; + + case ElemExprKind::RefFunc: + WriteOpcode(stream_, Opcode::RefFunc); + WriteU32Leb128(stream_, module_->GetFuncIndex(elem_expr.var), "elem expr function index"); + break; + } + WriteOpcode(stream_, Opcode::End); + } + } else { + for (const ElemExpr& elem_expr : segment->elem_exprs) { + assert(elem_expr.kind == ElemExprKind::RefFunc); + WriteU32Leb128(stream_, module_->GetFuncIndex(elem_expr.var), "elem function index"); + } + } + } + EndSection(); + } + + if (options_.features.bulk_memory_enabled()) { + // 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) { + WriteHeader("function body", i); + const Func* func = module_->funcs[i + module_->num_func_imports]; + + /* 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)"); + 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() && + !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); + } + 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; + } + } + } + + 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)) { + 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(); + + WriteU32Leb128(stream_, i, "function index"); + WriteU32Leb128(stream_, num_params_and_locals, "num locals"); + + MakeTypeBindingReverseMapping(num_params_and_locals, func->bindings, + &index_to_name); + for (size_t j = 0; j < num_params_and_locals; ++j) { + const std::string& name = index_to_name[j]; + 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); + + EndSection(); + } + + if (options_.relocatable) { + WriteLinkingSection(); + for (RelocSection& section : reloc_sections_) { + WriteRelocSection(§ion); + } + } + + return stream_->result(); +} + +} // 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-writer.h b/third_party/wasm2c/src/binary-writer.h new file mode 100644 index 0000000000..4304a0a70e --- /dev/null +++ b/third_party/wasm2c/src/binary-writer.h @@ -0,0 +1,62 @@ +/* + * 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_BINARY_WRITER_H_ +#define WABT_BINARY_WRITER_H_ + +#include "src/common.h" +#include "src/feature.h" +#include "src/opcode.h" +#include "src/stream.h" + +namespace wabt { + +struct Module; +struct Script; + +struct WriteBinaryOptions { + WriteBinaryOptions() = default; + WriteBinaryOptions(const Features& features, + bool canonicalize_lebs, + bool relocatable, + bool write_debug_names) + : features(features), + canonicalize_lebs(canonicalize_lebs), + relocatable(relocatable), + write_debug_names(write_debug_names) {} + + Features features; + bool canonicalize_lebs = true; + bool relocatable = false; + bool write_debug_names = false; +}; + +Result WriteBinaryModule(Stream*, const Module*, const WriteBinaryOptions&); + +void WriteType(Stream* stream, Type type, const char* desc = nullptr); + +void WriteStr(Stream* stream, + string_view s, + const char* desc, + PrintChars print_chars = PrintChars::No); + +void WriteOpcode(Stream* stream, Opcode opcode); + +void WriteLimits(Stream* stream, const Limits* limits); + +} // namespace wabt + +#endif /* WABT_BINARY_WRITER_H_ */ diff --git a/third_party/wasm2c/src/binary.cc b/third_party/wasm2c/src/binary.cc new file mode 100644 index 0000000000..60b20cbb6a --- /dev/null +++ b/third_party/wasm2c/src/binary.cc @@ -0,0 +1,62 @@ +/* + * 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 "src/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; + } +} + +const char* NameSubsectionName[] = { + "module", + "function", + "local", + "label", + "type", + "table", + "memory", + "global", + "elemseg", + "dataseg", +}; + +const char* GetNameSectionSubsectionName(NameSectionSubsection subsec) { + return NameSubsectionName[size_t(subsec)]; +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary.h b/third_party/wasm2c/src/binary.h new file mode 100644 index 0000000000..c6757820b7 --- /dev/null +++ b/third_party/wasm2c/src/binary.h @@ -0,0 +1,94 @@ +/* + * 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_BINARY_H_ +#define WABT_BINARY_H_ + +#include "src/common.h" + +#define WABT_BINARY_MAGIC 0x6d736100 +#define WABT_BINARY_VERSION 1 +#define WABT_BINARY_LIMITS_HAS_MAX_FLAG 0x1 +#define WABT_BINARY_LIMITS_IS_SHARED_FLAG 0x2 +#define WABT_BINARY_LIMITS_IS_64_FLAG 0x4 +#define WABT_BINARY_LIMITS_ALL_FLAGS \ + (WABT_BINARY_LIMITS_HAS_MAX_FLAG | WABT_BINARY_LIMITS_IS_SHARED_FLAG | \ + WABT_BINARY_LIMITS_IS_64_FLAG) + +#define WABT_BINARY_SECTION_NAME "name" +#define WABT_BINARY_SECTION_RELOC "reloc" +#define WABT_BINARY_SECTION_LINKING "linking" +#define WABT_BINARY_SECTION_DYLINK "dylink" +#define WABT_BINARY_SECTION_DYLINK0 "dylink.0" + +#define WABT_FOREACH_BINARY_SECTION(V) \ + V(Custom, custom, 0) \ + V(Type, type, 1) \ + V(Import, import, 2) \ + V(Function, function, 3) \ + V(Table, table, 4) \ + V(Memory, memory, 5) \ + V(Tag, tag, 13) \ + V(Global, global, 6) \ + V(Export, export, 7) \ + V(Start, start, 8) \ + V(Elem, elem, 9) \ + V(DataCount, data_count, 12) \ + V(Code, code, 10) \ + V(Data, data, 11) + +namespace wabt { + +/* clang-format off */ +enum class BinarySection { +#define V(Name, name, code) Name = code, + WABT_FOREACH_BINARY_SECTION(V) +#undef V + Invalid = ~0, + + First = Custom, + Last = Tag, +}; +/* clang-format on */ +static const int kBinarySectionCount = WABT_ENUM_COUNT(BinarySection); + +enum class BinarySectionOrder { +#define V(Name, name, code) Name, + WABT_FOREACH_BINARY_SECTION(V) +#undef V +}; + +BinarySectionOrder GetSectionOrder(BinarySection); +const char* GetSectionName(BinarySection); + +enum class NameSectionSubsection { + Module = 0, + Function = 1, + Local = 2, + Label = 3, + Type = 4, + Table = 5, + Memory = 6, + Global = 7, + ElemSegment = 8, + DataSegment = 9, + Last = DataSegment, +}; +const char* GetNameSectionSubsectionName(NameSectionSubsection subsec); + +} // namespace wabt + +#endif /* WABT_BINARY_H_ */ diff --git a/third_party/wasm2c/src/binding-hash.cc b/third_party/wasm2c/src/binding-hash.cc new file mode 100644 index 0000000000..f52682d7fe --- /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 "src/binding-hash.h" + +#include <algorithm> +#include <vector> + +#include "src/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/binding-hash.h b/third_party/wasm2c/src/binding-hash.h new file mode 100644 index 0000000000..077952700d --- /dev/null +++ b/third_party/wasm2c/src/binding-hash.h @@ -0,0 +1,72 @@ +/* + * 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_BINDING_HASH_H_ +#define WABT_BINDING_HASH_H_ + +#include <functional> +#include <string> +#include <unordered_map> +#include <vector> + +#include "src/common.h" +#include "src/string-view.h" + +namespace wabt { + +struct Var; + +struct Binding { + explicit Binding(Index index) : index(index) {} + Binding(const Location& loc, Index index) : loc(loc), index(index) {} + + Location loc; + Index index; +}; + +// This class derives from a C++ container, which is usually not advisable +// because they don't have virtual destructors. So don't delete a BindingHash +// object through a pointer to std::unordered_multimap. +class BindingHash : public std::unordered_multimap<std::string, Binding> { + public: + typedef std::function<void(const value_type&, const value_type&)> + DuplicateCallback; + + void FindDuplicates(DuplicateCallback callback) const; + + Index FindIndex(const Var&) const; + + Index FindIndex(const std::string& name) const { + auto iter = find(name); + return iter != end() ? iter->second.index : kInvalidIndex; + } + + Index FindIndex(string_view name) const { + return FindIndex(name.to_string()); + } + + private: + typedef std::vector<const value_type*> ValueTypeVector; + + void CreateDuplicatesVector(ValueTypeVector* out_duplicates) const; + void SortDuplicatesVectorByLocation(ValueTypeVector* duplicates) const; + void CallCallbacks(const ValueTypeVector& duplicates, + DuplicateCallback callback) const; +}; + +} // namespace wabt + +#endif /* WABT_BINDING_HASH_H_ */ diff --git a/third_party/wasm2c/src/c-writer.cc b/third_party/wasm2c/src/c-writer.cc new file mode 100644 index 0000000000..c008474aeb --- /dev/null +++ b/third_party/wasm2c/src/c-writer.cc @@ -0,0 +1,2595 @@ +/* + * 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 "src/c-writer.h" + +#include <cctype> +#include <cinttypes> +#include <map> +#include <set> + +#include "src/cast.h" +#include "src/common.h" +#include "src/ir.h" +#include "src/literal.h" +#include "src/stream.h" +#include "src/string-view.h" + +#define INDENT_SIZE 2 + +#define UNIMPLEMENTED(x) printf("unimplemented: %s\n", (x)), abort() + +namespace wabt { + +namespace { + +struct Label { + Label(LabelType label_type, + const std::string& name, + const TypeVector& sig, + size_t type_stack_size, + bool used = false) + : label_type(label_type), + name(name), + sig(sig), + type_stack_size(type_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; + bool used = false; +}; + +template <int> +struct Name { + explicit Name(const std::string& name) : name(name) {} + const std::string& name; +}; + +typedef Name<0> LocalName; +typedef Name<1> GlobalName; +typedef Name<2> ExternalPtr; +typedef Name<3> ExternalRef; + +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 GlobalVar { + explicit GlobalVar(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 Newline {}; +struct OpenBrace {}; +struct CloseBrace {}; + +int GetShiftMask(Type type) { + switch (type) { + case Type::I32: return 31; + case Type::I64: return 63; + default: WABT_UNREACHABLE; return 0; + } +} + +class CWriter { + public: + CWriter(Stream* c_stream, + Stream* h_stream, + const char* header_name, + const WriteCOptions& options) + : options_(options), + c_stream_(c_stream), + h_stream_(h_stream), + header_name_(header_name) {} + + Result WriteModule(const Module&); + + private: + typedef std::set<std::string> SymbolSet; + typedef std::map<std::string, std::string> SymbolMap; + typedef std::pair<Index, Type> StackTypePair; + typedef std::map<StackTypePair, std::string> StackVarSymbolMap; + + void UseStream(Stream*); + + 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 IsTopLabelUsed() const; + void PopLabel(); + + static std::string AddressOf(const std::string&); + + static char MangleType(Type); + static std::string MangleTypes(const TypeVector&); + static std::string MangleMultivalueTypes(const TypeVector&); + static std::string MangleName(string_view); + static std::string MangleFuncName(string_view, + const TypeVector& param_types, + const TypeVector& result_types); + static std::string MangleGlobalName(string_view, Type); + static std::string LegalizeName(string_view); + std::string DefineName(SymbolSet*, string_view); + std::string DefineImportName(const std::string& name, + string_view module_name, + string_view mangled_field_name); + std::string DefineGlobalScopeName(const std::string&); + std::string DefineLocalScopeName(const std::string&); + std::string DefineGlobalVarName(const std::string& name); + std::string DefineStackVarName(Index, Type, string_view); + + void Indent(int size = INDENT_SIZE); + void Dedent(int size = INDENT_SIZE); + void WriteIndent(); + void WriteData(const void* 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)...); + } + + std::string GetGlobalName(const std::string&) const; + std::string GetGlobalVarName(const std::string& name) const; + + void Write() {} + void Write(Newline); + void Write(OpenBrace); + void Write(CloseBrace); + void Write(Index); + void Write(string_view); + void Write(const LocalName&); + void Write(const GlobalName&); + void Write(const ExternalPtr&); + void Write(const ExternalRef&); + void Write(Type); + void Write(SignedType); + void Write(TypeEnum); + void Write(const Var&); + void Write(const GotoLabel&); + void Write(const LabelDecl&); + void Write(const GlobalVar&); + void Write(const StackVar&); + void Write(const ResultType&); + void Write(const Const&); + void WriteInitExpr(const ExprList&); + std::string GenerateHeaderGuard() const; + void WriteSourceTop(); + void WriteMultivalueTypes(); + void WriteSandboxStruct(); + void WriteFuncTypes(); + void WriteImports(); + bool IsFuncStatic(std::string name); + std::string GetFuncStaticOrExport(std::string); + void WriteFuncDeclarations(bool for_header); + void WriteFuncDeclaration(const FuncDeclaration&, const std::string&, bool add_storage_class); + void WriteEntryFuncs(); + void WriteEntryFunc(const FuncDeclaration&, const std::string&, bool add_storage_class); + void WriteImportFuncDeclaration(const FuncDeclaration&, const std::string&); + std::string GetMainMemoryName(); + void WriteGlobalInitializers(); + void WriteGlobals(); + void WriteGlobalsExport(); + void WriteGlobal(const Global&, const std::string&); + void WriteMemories(); + void WriteMemoriesExport(); + void WriteMemory(const std::string&); + void WriteTables(); + void WriteTablesExport(); + void WriteTable(const std::string&); + void WriteDataInitializers(); + void WriteElemInitializers(); + void WriteExportLookup(); + void WriteCallbackAddRemove(); + void WriteInit(); + void WriteFuncs(); + void Write(const Func&); + void WriteParamsAndLocals(); + void WriteParams(const std::vector<std::string>& index_to_name); + 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&); + + const WriteCOptions& options_; + const Module* module_ = nullptr; + const Func* func_ = nullptr; + Stream* stream_ = nullptr; + MemoryStream func_stream_; + Stream* c_stream_ = nullptr; + Stream* h_stream_ = nullptr; + std::string header_name_; + Result result_ = Result::Ok; + int indent_ = 0; + bool should_write_indent_next_ = false; + + SymbolMap global_sym_map_; + SymbolMap local_sym_map_; + SymbolMap globalvars_sym_map_; + StackVarSymbolMap stack_var_sym_map_; + SymbolSet global_syms_; + SymbolSet local_syms_; + SymbolSet globalvars_syms_; + SymbolSet import_syms_; + TypeVector type_stack_; + std::vector<Label> label_stack_; +}; + +static const char kImplicitFuncLabel[] = "$Bfunc"; + +#define SECTION_NAME(x) s_header_##x +#include "src/prebuilt/wasm2c.include.h" +#undef SECTION_NAME + +#define SECTION_NAME(x) s_source_##x +#include "src/prebuilt/wasm2c.include.c" +#undef SECTION_NAME + +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(), used); + else + label_stack_.emplace_back(label_type, name, sig.result_types, + type_stack_.size(), used); +} + +const Label* CWriter::FindLabel(const Var& var) { + 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); + 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 +std::string CWriter::AddressOf(const std::string& s) { + return "(&" + s + ")"; +} + +// static +char CWriter::MangleType(Type type) { + switch (type) { + case Type::I32: return 'i'; + case Type::I64: return 'j'; + case Type::F32: return 'f'; + case Type::F64: return 'd'; + default: WABT_UNREACHABLE; + } +} + +// static +std::string CWriter::MangleTypes(const TypeVector& types) { + if (types.empty()) + return std::string("v"); + + std::string result; + for (auto type : types) { + result += MangleType(type); + } + 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::MangleName(string_view name) { + const char kPrefix = 'Z'; + std::string result = "Z_"; + + if (!name.empty()) { + for (char c : name) { + if ((isalnum(c) && c != kPrefix) || c == '_') { + result += c; + } else { + result += kPrefix; + result += StringPrintf("%02X", static_cast<uint8_t>(c)); + } + } + } + + return result; +} + +// static +std::string CWriter::MangleFuncName(string_view name, + const TypeVector& param_types, + const TypeVector& result_types) { + std::string sig = MangleTypes(result_types) + MangleTypes(param_types); + return MangleName(name) + MangleName(sig); +} + +// static +std::string CWriter::MangleGlobalName(string_view name, Type type) { + std::string sig(1, MangleType(type)); + return MangleName(name) + MangleName(sig); +} + +// static +std::string CWriter::LegalizeName(string_view name) { + if (name.empty()) + return "_"; + + std::string result; + result = isalpha(name[0]) ? name[0] : '_'; + for (size_t i = 1; i < name.size(); ++i) + result += isalnum(name[i]) ? name[i] : '_'; + + // In addition to containing valid characters for C, we must also avoid + // colliding with things C cares about, such as reserved words (e.g. "void") + // or a function name like main() (which a compiler will complain about if we + // define it with another type). To avoid such problems, prefix. + result = "w2c_" + result; + + return result; +} + +std::string CWriter::DefineName(SymbolSet* set, string_view name) { + std::string legal = LegalizeName(name); + if (set->find(legal) != set->end()) { + std::string base = legal + "_"; + size_t count = 0; + do { + legal = base + std::to_string(count++); + } while (set->find(legal) != set->end()); + } + set->insert(legal); + return legal; +} + +string_view StripLeadingDollar(string_view name) { + if (!name.empty() && name[0] == '$') { + name.remove_prefix(1); + } + return name; +} + +std::string CWriter::DefineImportName(const std::string& name, + string_view module, + string_view mangled_field_name) { + std::string mangled = MangleName(module) + mangled_field_name.to_string(); + import_syms_.insert(name); + global_syms_.insert(mangled); + global_sym_map_.insert(SymbolMap::value_type(name, mangled)); + return mangled; +} + +std::string CWriter::DefineGlobalScopeName(const std::string& name) { + std::string unique = DefineName(&global_syms_, StripLeadingDollar(name)); + global_sym_map_.insert(SymbolMap::value_type(name, unique)); + return unique; +} + +std::string CWriter::DefineLocalScopeName(const std::string& name) { + std::string unique = DefineName(&local_syms_, StripLeadingDollar(name)); + local_sym_map_.insert(SymbolMap::value_type(name, unique)); + return unique; +} + +std::string CWriter::DefineGlobalVarName(const std::string& name) { + std::string unique = DefineName(&globalvars_syms_, StripLeadingDollar(name)); + globalvars_sym_map_.insert(SymbolMap::value_type(name, unique)); + return unique; +} + +std::string CWriter::DefineStackVarName(Index index, + Type type, + string_view name) { + std::string unique = DefineName(&local_syms_, name); + StackTypePair stp = {index, type}; + stack_var_sym_map_.insert(StackVarSymbolMap::value_type(stp, unique)); + return unique; +} + +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 void* src, size_t size) { + if (should_write_indent_next_) { + WriteIndent(); + should_write_indent_next_ = false; + } + 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) { + Write("\n"); + should_write_indent_next_ = true; +} + +void CWriter::Write(OpenBrace) { + Write("{"); + Indent(); + Write(Newline()); +} + +void CWriter::Write(CloseBrace) { + Dedent(); + Write("}"); +} + +void CWriter::Write(Index index) { + Writef("%" PRIindex, index); +} + +void CWriter::Write(string_view s) { + WriteData(s.data(), s.size()); +} + +void CWriter::Write(const LocalName& name) { + assert(local_sym_map_.count(name.name) == 1); + Write(local_sym_map_[name.name]); +} + +std::string CWriter::GetGlobalName(const std::string& name) const { + assert(global_sym_map_.count(name) == 1); + auto iter = global_sym_map_.find(name); + assert(iter != global_sym_map_.end()); + return iter->second; +} + +std::string CWriter::GetGlobalVarName(const std::string& name) const { + assert(globalvars_sym_map_.count(name) == 1); + auto iter = globalvars_sym_map_.find(name); + assert(iter != globalvars_sym_map_.end()); + return iter->second; +} + +void CWriter::Write(const GlobalName& name) { + Write(GetGlobalName(name.name)); +} + +void CWriter::Write(const ExternalPtr& name) { + bool is_import = import_syms_.count(name.name) != 0; + if (is_import) { + Write(GetGlobalName(name.name)); + } else { + Write(AddressOf(GetGlobalName(name.name))); + } +} + +void CWriter::Write(const ExternalRef& name) { + Write(GetGlobalName(name.name)); +} + +void CWriter::Write(const Var& var) { + assert(var.is_name()); + Write(LocalName(var.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), "; "); + } + } + } + + if (goto_label.var.is_name()) { + Write("goto ", goto_label.var, ";"); + } 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 ", Var(kImplicitFuncLabel), ";"); + } +} + +void CWriter::Write(const LabelDecl& label) { + if (IsTopLabelUsed()) + Write(label.name, ":;", Newline()); +} + +void CWriter::Write(const GlobalVar& var) { + assert(var.var.is_name()); + Write(GetGlobalVarName(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); + } +} + +void CWriter::Write(Type type) { + switch (type) { + case Type::I32: Write("u32"); break; + case Type::I64: Write("u64"); break; + case Type::F32: Write("f32"); break; + case Type::F64: Write("f64"); break; + default: + WABT_UNREACHABLE; + } +} + +void CWriter::Write(TypeEnum type) { + 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; + default: + WABT_UNREACHABLE; + } +} + +void CWriter::Write(SignedType type) { + switch (type.type) { + case Type::I32: Write("s32"); break; + case Type::I64: Write("s64"); break; + default: + WABT_UNREACHABLE; + } +} + +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; + } + + default: + WABT_UNREACHABLE; + } +} + +void CWriter::WriteInitExpr(const ExprList& expr_list) { + if (expr_list.empty()) + return; + + assert(expr_list.size() == 1); + const Expr* expr = &expr_list.front(); + switch (expr_list.front().type()) { + case ExprType::Const: + Write(cast<ConstExpr>(expr)->const_); + break; + + case ExprType::GlobalGet: + Write(GlobalVar(cast<GlobalGetExpr>(expr)->var)); + 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); +} + +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, " {", Newline()); + 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("};", Newline(), "#endif /* ", name, " */", Newline()); + } +} + +void CWriter::WriteSandboxStruct() { + Write("struct wasm2c_sandbox_t {", Newline()); + Indent(2); + + Write("wasm_sandbox_wasi_data wasi_data;", Newline()); + + WriteMemories(); + WriteTables(); + + { + Write("wasm_func_type_t* func_type_structs;"); + Write(Newline()); + Write("u32 func_type_count;"); + Write(Newline()); + Writef("u32 func_types[%" PRIzd "];", module_->types.size()); + Write(Newline()); + } + + WriteGlobals(); + + Dedent(2); + Write("};", Newline(), Newline()); +} + +void CWriter::WriteFuncTypes() { + Write(Newline()); + Write("static void init_func_types(wasm2c_sandbox_t* const sbx) ", OpenBrace()); + Index func_type_index = 0; + for (TypeEntry* type : module_->types) { + FuncType* func_type = cast<FuncType>(type); + Index num_params = func_type->GetNumParams(); + Index num_results = func_type->GetNumResults(); + Write(OpenBrace()); + Write("wasm_rt_type_t param_ret_types[] = { "); + bool first = true; + if (num_results == 0 && num_params == 0) { + // Make sure array has at least one element + Write("/* void(*)(void) */ WASM_RT_I32 "); + } else { + for (Index i = 0; i < num_params; ++i) { + if (!first) { + Write(", "); + } + Write(TypeEnum(func_type->GetParamType(i))); + first = false; + } + for (Index i = 0; i < num_results; ++i) { + if (!first) { + Write(", "); + } + Write(TypeEnum(func_type->GetResultType(i))); + first = false; + } + } + Write(" };", Newline()); + Write("sbx->func_types[", func_type_index, "] = wasm_rt_register_func_type(&sbx->func_type_structs, &sbx->func_type_count, ", + num_params, ", ", num_results, ", param_ret_types"); + Write(");", Newline()); + Write(CloseBrace(), Newline()); + ++func_type_index; + } + Write(CloseBrace(), Newline()); + + Write("static void cleanup_func_types(wasm2c_sandbox_t* const sbx) ", OpenBrace()); + { + Write("wasm_rt_cleanup_func_types(&sbx->func_type_structs, &sbx->func_type_count);", Newline()); + } + Write(CloseBrace(), Newline()); +} + +void CWriter::WriteImports() { + if (module_->imports.empty()) + return; + + Write(Newline()); + + // TODO(binji): Write imports ordered by type. + for (const Import* import : module_->imports) { + Write("/* import: '", import->module_name, "' '", import->field_name, + "' */", Newline()); + Write("extern "); + switch (import->kind()) { + case ExternalKind::Func: { + const Func& func = cast<FuncImport>(import)->func; + WriteImportFuncDeclaration( + func.decl, + DefineImportName( + func.name, import->module_name, + MangleFuncName(import->field_name, func.decl.sig.param_types, + func.decl.sig.result_types))); + Write(";"); + break; + } + + case ExternalKind::Global: { + const Global& global = cast<GlobalImport>(import)->global; + WriteGlobal(global, + DefineImportName( + global.name, import->module_name, + MangleGlobalName(import->field_name, global.type))); + Write(";"); + break; + } + + case ExternalKind::Memory: { + const Memory& memory = cast<MemoryImport>(import)->memory; + WriteMemory(DefineImportName(memory.name, import->module_name, + MangleName(import->field_name))); + break; + } + + case ExternalKind::Table: { + const Table& table = cast<TableImport>(import)->table; + WriteTable(DefineImportName(table.name, import->module_name, + MangleName(import->field_name))); + break; + } + + default: + WABT_UNREACHABLE; + } + + Write(Newline()); + } +} + +void CWriter::WriteFuncDeclarations(bool for_header) { + if (module_->funcs.size() == module_->num_func_imports) + return; + + Write(Newline()); + + Index func_index = 0; + for (const Func* func : module_->funcs) { + std::string global_func_name; + if (for_header) { + global_func_name = DefineGlobalScopeName(func->name); + } else { + global_func_name = GetGlobalName(func->name); + } + bool is_import = func_index < module_->num_func_imports; + bool static_exclude = for_header && IsFuncStatic(global_func_name); + if (!is_import && !static_exclude) { + WriteFuncDeclaration(func->decl, global_func_name, true /* add_storage_class */); + Write(";", Newline()); + } + ++func_index; + } +} + + +void CWriter::WriteEntryFuncs() { + if (module_->funcs.size() == module_->num_func_imports) + return; + + Write(Newline()); + Write("#if defined(ENTRY_PROLOGUE) || defined(ENTRY_EPILOGUE)", Newline()); + + Index func_index = 0; + for (const Func* func : module_->funcs) { + bool is_import = func_index < module_->num_func_imports; + if (!is_import) { + WriteEntryFunc(func->decl, GetGlobalName(func->name), true /* add_storage_class */); + Write(Newline()); + } + ++func_index; + } + + Write("#endif", Newline()); +} + + +bool CWriter::IsFuncStatic(std::string name) { + // static functions starts with prefix __ + return name.rfind("w2c___", 0) == 0 || name == "w2c_main"; +} + +std::string CWriter::GetFuncStaticOrExport(std::string name) { + std::string static_export_string = IsFuncStatic(name)? "static " : "FUNC_EXPORT "; + return static_export_string; +} + +void CWriter::WriteFuncDeclaration(const FuncDeclaration& decl, + const std::string& name, + bool add_storage_class) { + // LLVM adds some extra function calls to all wasm objects prefixed with "__". + // Keep this static (private), else we cause symbol collisions when linking multiple wasm modules + // Additionally windows dlls have to export functions explicitly + if (add_storage_class) { + Write(GetFuncStaticOrExport(name)); + } + Write(ResultType(decl.sig.result_types), " ", name, "(wasm2c_sandbox_t* const"); + for (Index i = 0; i < decl.GetNumParams(); ++i) { + Write(", ", decl.GetParamType(i)); + } + Write(")"); +} + +void CWriter::WriteEntryFunc(const FuncDeclaration& decl, + const std::string& name, + bool add_storage_class) { + // LLVM adds some extra function calls to all wasm objects prefixed with "__". + // Keep this static (private), else we cause symbol collisions when linking multiple wasm modules + // Additionally windows dlls have to export functions explicitly + if (add_storage_class) { + Write(GetFuncStaticOrExport(name)); + } + Write(ResultType(decl.sig.result_types), " w2centry_", name, "(wasm2c_sandbox_t* const sbx"); + for (Index i = 0; i < decl.GetNumParams(); ++i) { + Write(", ", decl.GetParamType(i), " p", std::to_string(i)); + } + Write(") ", OpenBrace()); + { + Write("ENTRY_PROLOGUE;", Newline()); + if (!decl.sig.result_types.empty()) { + Write(ResultType(decl.sig.result_types), " ret = "); + } + Write(name, "(sbx"); + for (Index i = 0; i < decl.GetNumParams(); ++i) { + Write(", p", std::to_string(i)); + } + Write(");", Newline()); + Write("ENTRY_EPILOGUE;", Newline()); + if (!decl.sig.result_types.empty()) { + Write("return ret;", Newline()); + } + } + Write(CloseBrace(), Newline()); +} + +void CWriter::WriteImportFuncDeclaration(const FuncDeclaration& decl, + const std::string& name) { + Write(ResultType(decl.sig.result_types), " ", name, "(void*"); + for (Index i = 0; i < decl.GetNumParams(); ++i) { + Write(", ", decl.GetParamType(i)); + } + Write(")"); +} + +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, DefineGlobalVarName(global->name)); + Write(";", Newline()); + } + ++global_index; + } + } +} + +void CWriter::WriteGlobalsExport() { + 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) { + std::string curr_global_name = GetGlobalVarName(global->name); + Writef("if (strcmp(\"%s\", name) == 0)", curr_global_name.c_str()); + Write(OpenBrace()); + Write("return &(sbx->", curr_global_name, ");", Newline()); + Write(CloseBrace(), Newline()); + } + ++global_index; + } + } +} + +std::string CWriter::GetMainMemoryName() { + assert (!(module_->memories.size() == module_->num_memory_imports)); + assert(module_->memories.size() <= 1); + + std::string ret = GetGlobalName(module_->memories[0]->name); + return ret; +} + +void CWriter::WriteGlobalInitializers() { + + Write(Newline(), "static void init_globals(wasm2c_sandbox_t* const sbx) ", 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("sbx->", GetGlobalVarName(global->name), " = "); + WriteInitExpr(global->init_expr); + Write(";", Newline()); + } + ++global_index; + } + } + + { + Index global_index = 0; + std::string memory_name = GetMainMemoryName(); + for (const Global* global : module_->globals) { + bool is_import = global_index < module_->num_global_imports; + if (!is_import) { + std::string global_name = GetGlobalVarName(global->name); + std::string global_name_expr = "sbx->" + global_name; + Write("WASM2C_SHADOW_MEMORY_RESERVE(&(sbx->", memory_name ,"), ", global_name_expr, ", sizeof(", global_name_expr, "));", Newline()); + if (global_name == "w2c___heap_base") { + Write("WASM2C_SHADOW_MEMORY_MARK_GLOBALS_HEAP_BOUNDARY(&(sbx->", memory_name, "), ", global_name_expr, ");", Newline()); + } + } + + ++global_index; + } + } + + Write(CloseBrace(), Newline()); +} + +void CWriter::WriteGlobal(const Global& global, const std::string& name) { + Write(global.type, " ", name); +} + +void CWriter::WriteMemories() { + if (module_->memories.size() == module_->num_memory_imports) + return; + + assert(module_->memories.size() <= 1); + Index memory_index = 0; + for (const Memory* memory : module_->memories) { + bool is_import = memory_index < module_->num_memory_imports; + if (!is_import) { + WriteMemory(DefineGlobalScopeName(memory->name)); + Write(Newline()); + } + ++memory_index; + } +} + +void CWriter::WriteMemoriesExport() { + if (module_->memories.size() == module_->num_memory_imports) + return; + + assert(module_->memories.size() <= 1); + Index memory_index = 0; + for (const Memory* memory : module_->memories) { + bool is_import = memory_index < module_->num_memory_imports; + if (!is_import) { + std::string curr_memory_name = GetGlobalName(memory->name); + Writef("if (strcmp(\"%s\", name) == 0)", curr_memory_name.c_str()); + Write(OpenBrace()); + Write("return &(sbx->", curr_memory_name, ");", Newline()); + Write(CloseBrace(), Newline()); + } + ++memory_index; + } +} + +void CWriter::WriteMemory(const std::string& name) { + Write("wasm_rt_memory_t ", name, ";"); +} + +void CWriter::WriteTables() { + if (module_->tables.size() == module_->num_table_imports) + return; + + assert(module_->tables.size() <= 1); + Index table_index = 0; + for (const Table* table : module_->tables) { + bool is_import = table_index < module_->num_table_imports; + if (!is_import) { + std::string curr_table_name = DefineGlobalScopeName(table->name); + Writef("u32 %s_current_index;", curr_table_name.c_str()); + Write(Newline()); + WriteTable(curr_table_name); + Write(Newline()); + } + ++table_index; + } +} + +void CWriter::WriteTablesExport() { + if (module_->tables.size() == module_->num_table_imports) + return; + + assert(module_->tables.size() <= 1); + Index table_index = 0; + for (const Table* table : module_->tables) { + bool is_import = table_index < module_->num_table_imports; + if (!is_import) { + std::string curr_table_name = GetGlobalName(table->name); + Writef("if (strcmp(\"%s\", name) == 0)", curr_table_name.c_str()); + Write(OpenBrace()); + Write("return &(sbx->", curr_table_name, ");", Newline()); + Write(CloseBrace(), Newline()); + } + ++table_index; + } +} + +void CWriter::WriteTable(const std::string& name) { + Write("wasm_rt_table_t ", name, ";"); +} + +void CWriter::WriteDataInitializers() { + const Memory* memory = nullptr; + Index data_segment_index = 0; + + if (!module_->memories.empty()) { + if (module_->data_segments.empty()) { + Write(Newline()); + } else { + for (const DataSegment* data_segment : module_->data_segments) { + Write(Newline(), "static const u8 data_segment_data_", + data_segment_index, "[] = ", 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()); + ++data_segment_index; + } + } + + memory = module_->memories[0]; + } + + Write(Newline(), "static bool init_memory(wasm2c_sandbox_t* const sbx, uint32_t max_wasm_pages_from_rt) ", OpenBrace()); + if (memory && module_->num_memory_imports == 0) { + Write("const uint32_t max_pages_specified_in_module = ", memory->page_limits.has_max ? memory->page_limits.max : 0, ";", Newline()); + Write("const uint32_t max_pages = max_wasm_pages_from_rt == 0? max_pages_specified_in_module : max_wasm_pages_from_rt;", Newline()); + Write("const bool success = wasm_rt_allocate_memory(&(sbx->", ExternalRef(memory->name), "), ", + memory->page_limits.initial, ", max_pages);", Newline()); + Write("if (!success) { return false; }", Newline(), Newline()); + } + data_segment_index = 0; + for (const DataSegment* data_segment : module_->data_segments) { + Write("LOAD_DATA(sbx->", ExternalRef(memory->name), ", "); + WriteInitExpr(data_segment->offset); + Write(", data_segment_data_", data_segment_index, ", ", + data_segment->data.size(), ");", Newline()); + ++data_segment_index; + } + + Write("sbx->wasi_data.heap_memory = &(sbx->", ExternalRef(memory->name), ");", Newline()); + Write("return true;", Newline()); + Write(CloseBrace(), Newline()); + + Write(Newline(), "static void cleanup_memory(wasm2c_sandbox_t* const sbx) ", OpenBrace()); + Write("wasm_rt_deallocate_memory(&(sbx->", ExternalRef(memory->name), "));", Newline()); + Write(CloseBrace(), Newline()); +} + +void CWriter::WriteElemInitializers() { + const Table* table = module_->tables.empty() ? nullptr : module_->tables[0]; + + Write(Newline(), "static void init_table(wasm2c_sandbox_t* const sbx) ", OpenBrace()); + Write("uint32_t offset = 0;", Newline()); + if (table && module_->num_table_imports == 0) { + uint32_t max = + table->elem_limits.has_max ? table->elem_limits.max : UINT32_MAX; + Write("wasm_rt_allocate_table(&(sbx->", ExternalRef(table->name), "), ", + table->elem_limits.initial, ", ", max, ");", Newline()); + } + Index elem_segment_index = 0; + size_t first_unused_elem = 0; + for (const ElemSegment* elem_segment : module_->elem_segments) { + Write("offset = "); + WriteInitExpr(elem_segment->offset); + Write(";", Newline()); + + size_t i = 0; + for (const ElemExpr& elem_expr : elem_segment->elem_exprs) { + // We don't support the bulk-memory proposal here, so we know that we + // don't have any passive segments (where ref.null can be used). + assert(elem_expr.kind == ElemExprKind::RefFunc); + const Func* func = module_->GetFunc(elem_expr.var); + Index func_type_index = module_->GetFuncTypeIndex(func->decl.type_var); + + Write("sbx->",ExternalRef(table->name), ".data[offset + ", i, + "] = (wasm_rt_elem_t){ WASM_RT_INTERNAL_FUNCTION, sbx->func_types[", func_type_index, + "], (wasm_rt_anyfunc_t)", ExternalPtr(func->name), " };", Newline()); + if (i >= first_unused_elem) { + first_unused_elem = i+1; + } + ++i; + } + ++elem_segment_index; + } + Write("sbx->", ExternalRef(table->name), "_current_index = offset + ", std::to_string(first_unused_elem), ";", Newline()); + Write(CloseBrace(), Newline()); + + Write(Newline(), "static void cleanup_table(wasm2c_sandbox_t* const sbx) ", OpenBrace()); + if (table && module_->num_table_imports == 0) { + Write("wasm_rt_deallocate_table(&(sbx->", ExternalRef(table->name), "));", Newline()); + } + Write(CloseBrace(), Newline()); +} + +void CWriter::WriteExportLookup() { + Write(Newline(), "static void* lookup_wasm2c_nonfunc_export(void* sbx_ptr, const char* name) ", OpenBrace()); + Write("wasm2c_sandbox_t* const sbx = (wasm2c_sandbox_t* const) sbx_ptr;", Newline()); + + WriteMemoriesExport(); + WriteTablesExport(); + WriteGlobalsExport(); + + Write("return 0;", Newline()); + Write(CloseBrace(), Newline()); +} + +void CWriter::WriteCallbackAddRemove() { + const Table* table = module_->tables.empty() ? nullptr : module_->tables[0]; + + Write(Newline(), "static wasm_rt_table_t* get_wasm2c_callback_table(void* sbx_ptr)", OpenBrace()); + Write("wasm2c_sandbox_t* const sbx = (wasm2c_sandbox_t* const) sbx_ptr;", Newline()); + Write("return &(sbx->", ExternalRef(table->name), ");", Newline()); + Write(CloseBrace(), Newline()); +} + +void CWriter::WriteInit() { + Write(Newline(), "static void init_module_starts(void) ", OpenBrace()); + for (Var* var : module_->starts) { + Write(ExternalRef(module_->GetFunc(*var)->name), "();", Newline()); + } + Write(CloseBrace(), Newline()); + + Write(s_source_sandboxapis); +} + +void CWriter::WriteFuncs() { + Index func_index = 0; + for (const Func* func : module_->funcs) { + bool is_import = func_index < module_->num_func_imports; + if (!is_import) + Write(Newline(), *func, Newline()); + ++func_index; + } +} + +void CWriter::Write(const Func& func) { + func_ = &func; + // Copy symbols from global symbol table so we don't shadow them. + local_syms_ = global_syms_; + local_sym_map_.clear(); + stack_var_sym_map_.clear(); + + std::string func_name_suffix; + auto out_func_name = GetGlobalName(func.name); + + if (out_func_name == "w2c_dlmalloc" || out_func_name == "w2c_dlfree") + { + func_name_suffix = "_wrapped"; + } + + Write(GetFuncStaticOrExport(out_func_name), ResultType(func.decl.sig.result_types), " ", + out_func_name + func_name_suffix, "("); + WriteParamsAndLocals(); + Write("FUNC_PROLOGUE;", Newline()); + + stream_ = &func_stream_; + stream_->ClearOffset(); + + std::string label = DefineLocalScopeName(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_ = c_stream_; + + WriteStackVarDeclarations(); + + std::unique_ptr<OutputBuffer> buf = func_stream_.ReleaseOutputBuffer(); + stream_->WriteData(buf->data.data(), buf->data.size()); + + Write(CloseBrace()); + + std::string memory_name = GetMainMemoryName(); + if (out_func_name == "w2c_dlmalloc") { + Write(Newline(), Newline()); + Write(GetFuncStaticOrExport(out_func_name), "u32 w2c_dlmalloc(wasm2c_sandbox_t* const sbx, u32 ptr_size) ", OpenBrace()); + Write("u32 ret = w2c_dlmalloc_wrapped(sbx, ptr_size);", Newline()); + Write("WASM2C_SHADOW_MEMORY_DLMALLOC(&(sbx->", memory_name, "), ret, ptr_size);", Newline()); + Write("WASM2C_MALLOC_FAIL_CHECK(ret, ptr_size);", Newline()); + Write("return ret;", Newline()); + Write(CloseBrace()); + } else if (out_func_name == "w2c_dlfree") { + Write(Newline(), Newline()); + Write(GetFuncStaticOrExport(out_func_name), "void w2c_dlfree(wasm2c_sandbox_t* const sbx, u32 ptr) ", OpenBrace()); + Write("WASM2C_SHADOW_MEMORY_DLFREE(&(sbx->", memory_name, "), ptr);", Newline()); + Write("w2c_dlfree_wrapped(sbx, ptr);", Newline()); + Write(CloseBrace()); + } + + func_stream_.Clear(); + func_ = nullptr; +} + +void CWriter::WriteParamsAndLocals() { + std::vector<std::string> index_to_name; + MakeTypeBindingReverseMapping(func_->GetNumParamsAndLocals(), func_->bindings, + &index_to_name); + WriteParams(index_to_name); + WriteLocals(index_to_name); +} + +void CWriter::WriteParams(const std::vector<std::string>& index_to_name) { + Indent(4); + Write("wasm2c_sandbox_t* const sbx"); + for (Index i = 0; i < func_->GetNumParams(); ++i) { + Write(", "); + if (i != 0) { + if ((i % 8) == 0) + Write(Newline()); + } + Write(func_->GetParamType(i), " ", + DefineLocalScopeName(index_to_name[i])); + } + Dedent(4); + Write(") ", OpenBrace()); +} + +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}) { + 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(DefineLocalScopeName(index_to_name[num_params + local_index]), + " = 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}) { + size_t count = 0; + for (const auto& pair : stack_var_sym_map_) { + Type stp_type = pair.first.second; + const std::string& name = 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 ExprList& exprs) { + for (const Expr& expr : exprs) { + switch (expr.type()) { + case ExprType::Binary: + Write(*cast<BinaryExpr>(&expr)); + break; + + case ExprType::Block: { + const Block& block = cast<BlockExpr>(&expr)->block; + std::string label = DefineLocalScopeName(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); + 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)), " = "); + } + + Write(GlobalName(var.name()), "(sbx"); + for (Index i = 0; i < num_params; ++i) { + 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)," tmp;"); + } + + assert(module_->tables.size() == 1); + const Table* table = module_->tables[0]; + + assert(decl.has_func_type); + Index func_type_index = module_->GetFuncTypeIndex(decl.type_var); + + if (num_results > 0) { + assert(num_results == 1); + Write("CALL_INDIRECT_RES("); + if (num_results > 1) { + Write("tmp, "); + } else { + Write(StackVar(num_params, decl.GetResultType(0)), ", "); + } + } else { + Write("CALL_INDIRECT_VOID("); + } + + Write("sbx->", ExternalRef(table->name), ", "); + WriteFuncDeclaration(decl, "(*)", false /* add_storage_class*/); + Write(", ", func_type_index, ", ", StackVar(0)); + Write(", sbx->func_types, sbx"); + 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::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), " = ", "sbx->", GlobalVar(var), ";", Newline()); + break; + } + + case ExprType::GlobalSet: { + const Var& var = cast<GlobalSetExpr>(&expr)->var; + Write("sbx->", GlobalVar(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 = DefineLocalScopeName(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(DefineLocalScopeName(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::MemoryCopy: + case ExprType::DataDrop: + case ExprType::MemoryInit: + case ExprType::MemoryFill: + case ExprType::TableCopy: + case ExprType::ElemDrop: + case ExprType::TableInit: + case ExprType::TableGet: + case ExprType::TableSet: + case ExprType::TableGrow: + case ExprType::TableSize: + case ExprType::TableFill: + case ExprType::RefFunc: + case ExprType::RefNull: + case ExprType::RefIsNull: + UNIMPLEMENTED("..."); + break; + + case ExprType::MemoryGrow: { + assert(module_->memories.size() == 1); + Memory* memory = module_->memories[0]; + + Write(StackVar(0), " = wasm_rt_grow_memory((&sbx->", ExternalRef(memory->name), + "), ", StackVar(0), ");", Newline()); + break; + } + + case ExprType::MemorySize: { + assert(module_->memories.size() == 1); + Memory* memory = module_->memories[0]; + + PushType(Type::I32); + Write(StackVar(0), " = sbx->", ExternalRef(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::AtomicLoad: + case ExprType::AtomicRmw: + case ExprType::AtomicRmwCmpxchg: + case ExprType::AtomicStore: + case ExprType::AtomicWait: + case ExprType::AtomicFence: + case ExprType::AtomicNotify: + case ExprType::Rethrow: + case ExprType::ReturnCall: + case ExprType::ReturnCallIndirect: + case ExprType::Throw: + case ExprType::Try: + 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; + + 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; + + 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: + case Opcode::F32DemoteF64: + WriteSimpleUnaryExpr(expr.opcode, "(f32)"); + 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: + case Opcode::F64PromoteF32: + WriteSimpleUnaryExpr(expr.opcode, "(f64)"); + 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; + + default: + WABT_UNREACHABLE; + } +} + +void CWriter::Write(const LoadExpr& expr) { + const char* func = nullptr; + 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; + + default: + WABT_UNREACHABLE; + } + + assert(module_->memories.size() == 1); + Memory* memory = module_->memories[0]; + + Type result_type = expr.opcode.GetResultType(); + Write(StackVar(0, result_type), " = ", func, "(&(sbx->", ExternalRef(memory->name), + "), (u64)(", StackVar(0), ")"); + if (expr.offset != 0) + Write(" + ", expr.offset, "u"); + Write(", \"", GetGlobalName(func_->name), "\""); + Write(");", Newline()); + DropTypes(1); + PushType(result_type); +} + +void CWriter::Write(const StoreExpr& expr) { + const char* func = nullptr; + 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; + + default: + WABT_UNREACHABLE; + } + + assert(module_->memories.size() == 1); + Memory* memory = module_->memories[0]; + + Write(func, "(&(sbx->", ExternalRef(memory->name), "), (u64)(", StackVar(1), ")"); + if (expr.offset != 0) + Write(" + ", expr.offset); + Write(", ", StackVar(0)); + Write(", \"", GetGlobalName(func_->name), "\""); + Write(");", 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, "fabsf"); + break; + + case Opcode::F64Abs: + WriteSimpleUnaryExpr(expr.opcode, "fabs"); + break; + + case Opcode::F32Sqrt: + WriteSimpleUnaryExpr(expr.opcode, "sqrtf"); + break; + + case Opcode::F64Sqrt: + WriteSimpleUnaryExpr(expr.opcode, "sqrt"); + break; + + case Opcode::F32Ceil: + WriteSimpleUnaryExpr(expr.opcode, "ceilf"); + break; + + case Opcode::F64Ceil: + WriteSimpleUnaryExpr(expr.opcode, "ceil"); + break; + + case Opcode::F32Floor: + WriteSimpleUnaryExpr(expr.opcode, "floorf"); + break; + + case Opcode::F64Floor: + WriteSimpleUnaryExpr(expr.opcode, "floor"); + break; + + case Opcode::F32Trunc: + WriteSimpleUnaryExpr(expr.opcode, "truncf"); + break; + + case Opcode::F64Trunc: + WriteSimpleUnaryExpr(expr.opcode, "trunc"); + break; + + case Opcode::F32Nearest: + WriteSimpleUnaryExpr(expr.opcode, "nearbyintf"); + break; + + case Opcode::F64Nearest: + WriteSimpleUnaryExpr(expr.opcode, "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; + + 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), " = ", "v128.bitselect", "(", StackVar(0), + ", ", StackVar(1), ", ", StackVar(2), ");", 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: + case Opcode::I8X16ExtractLaneU: + case Opcode::I16X8ExtractLaneS: + case Opcode::I16X8ExtractLaneU: + case Opcode::I32X4ExtractLane: + case Opcode::I64X2ExtractLane: + case Opcode::F32X4ExtractLane: + case Opcode::F64X2ExtractLane: { + Write(StackVar(0, result_type), " = ", expr.opcode.GetName(), "(", + StackVar(0), ", lane Imm: ", expr.val, ");", Newline()); + DropTypes(1); + break; + } + case Opcode::I8X16ReplaceLane: + case Opcode::I16X8ReplaceLane: + case Opcode::I32X4ReplaceLane: + case Opcode::I64X2ReplaceLane: + case Opcode::F32X4ReplaceLane: + case Opcode::F64X2ReplaceLane: { + Write(StackVar(1, result_type), " = ", expr.opcode.GetName(), "(", + StackVar(0), ", ", StackVar(1), ", lane Imm: ", expr.val, ");", + Newline()); + DropTypes(2); + break; + } + default: + WABT_UNREACHABLE; + } + + PushType(result_type); +} + +void CWriter::Write(const SimdLoadLaneExpr& expr) { + UNIMPLEMENTED("SIMD support"); +} + +void CWriter::Write(const SimdStoreLaneExpr& expr) { + UNIMPLEMENTED("SIMD support"); +} + +void CWriter::Write(const SimdShuffleOpExpr& expr) { + Type result_type = expr.opcode.GetResultType(); + Write(StackVar(1, result_type), " = ", expr.opcode.GetName(), "(", + StackVar(1), " ", StackVar(0), ", lane Imm: $0x%08x %08x %08x %08x", + expr.val.u32(0), expr.val.u32(1), expr.val.u32(2), expr.val.u32(3), ")", + Newline()); + DropTypes(2); + PushType(result_type); +} + +void CWriter::Write(const LoadSplatExpr& expr) { + assert(module_->memories.size() == 1); + Memory* memory = module_->memories[0]; + + Type result_type = expr.opcode.GetResultType(); + Write(StackVar(0, result_type), " = ", expr.opcode.GetName(), "(", + ExternalPtr(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) { + UNIMPLEMENTED("SIMD support"); +} + +void CWriter::WriteCHeader() { + stream_ = h_stream_; + std::string guard = GenerateHeaderGuard(); + Write("#ifndef ", guard, Newline()); + Write("#define ", guard, Newline(), Newline()); + Write("#define WASM_CURR_MODULE_PREFIX ", options_.mod_name, Newline()); + Write(s_header_top); + WriteMultivalueTypes(); + WriteImports(); + WriteFuncDeclarations(true /* for_header */); + Write(s_header_bottom, Newline()); + Write("#endif /* ", guard, " */", Newline()); +} + +void CWriter::WriteCSource() { + stream_ = c_stream_; + WriteSourceTop(); + WriteSandboxStruct(); + WriteFuncTypes(); + WriteFuncDeclarations(false /* for_header */); + WriteEntryFuncs(); + WriteGlobalInitializers(); + WriteFuncs(); + WriteDataInitializers(); + WriteElemInitializers(); + WriteExportLookup(); + WriteCallbackAddRemove(); + WriteInit(); +} + +Result CWriter::WriteModule(const Module& module) { + WABT_USE(options_); + module_ = &module; + WriteCHeader(); + WriteCSource(); + return result_; +} + +} // end anonymous namespace + +Result WriteC(Stream* c_stream, + Stream* h_stream, + const char* header_name, + const Module* module, + const WriteCOptions& options) { + CWriter c_writer(c_stream, h_stream, header_name, options); + return c_writer.WriteModule(*module); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/c-writer.h b/third_party/wasm2c/src/c-writer.h new file mode 100644 index 0000000000..8aa13b2e2b --- /dev/null +++ b/third_party/wasm2c/src/c-writer.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#ifndef WABT_C_WRITER_H_ +#define WABT_C_WRITER_H_ + +#include "src/common.h" + +#include <string> + +namespace wabt { + +struct Module; +class Stream; + +struct WriteCOptions { + std::string mod_name; +}; + +Result WriteC(Stream* c_stream, + Stream* h_stream, + const char* header_name, + const Module*, + const WriteCOptions&); + +} // namespace wabt + +#endif /* WABT_C_WRITER_H_ */ diff --git a/third_party/wasm2c/src/cast.h b/third_party/wasm2c/src/cast.h new file mode 100644 index 0000000000..7275415027 --- /dev/null +++ b/third_party/wasm2c/src/cast.h @@ -0,0 +1,109 @@ +/* + * 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. + */ + +#ifndef WABT_CAST_H_ +#define WABT_CAST_H_ + +#include <memory> +#include <type_traits> + +#include "src/common.h" + +// Modeled after LLVM's dynamic casts: +// http://llvm.org/docs/ProgrammersManual.html#the-isa-cast-and-dyn-cast-templates +// +// Use isa<T>(foo) to check whether foo is a T*: +// +// if (isa<Minivan>(car)) { +// ... +// } +// +// Use cast<T>(foo) when you know that foo is a T* -- it will assert that the +// type matches: +// +// switch (car.type) { +// case CarType::Minivan: { +// auto minivan = cast<Minivan>(car); +// ... +// } +// } +// +// Use dyn_cast<T>(foo) as a combination if isa and cast, it will return +// nullptr if the type doesn't match: +// +// if (auto minivan = dyn_cast<Minivan>(car)) { +// ... +// } +// +// +// To use these classes in a type hierarchy, you must implement classof: +// +// enum CarType { Minivan, ... }; +// struct Car { CarType type; ... }; +// struct Minivan : Car { +// static bool classof(const Car* car) { return car->type == Minivan; } +// ... +// }; +// + +namespace wabt { + +template <typename Derived, typename Base> +bool isa(const Base* base) { + WABT_STATIC_ASSERT((std::is_base_of<Base, Derived>::value)); + return Derived::classof(base); +} + +template <typename Derived, typename Base> +const Derived* cast(const Base* base) { + assert(isa<Derived>(base)); + return static_cast<const Derived*>(base); +}; + +template <typename Derived, typename Base> +Derived* cast(Base* base) { + assert(isa<Derived>(base)); + return static_cast<Derived*>(base); +}; + +template <typename Derived, typename Base> +const Derived* dyn_cast(const Base* base) { + return isa<Derived>(base) ? static_cast<const Derived*>(base) : nullptr; +}; + +template <typename Derived, typename Base> +Derived* dyn_cast(Base* base) { + return isa<Derived>(base) ? static_cast<Derived*>(base) : nullptr; +}; + +// Cast functionality for unique_ptr. isa and dyn_cast are not included because +// they won't always pass ownership back to the caller. + +template <typename Derived, typename Base> +std::unique_ptr<const Derived> cast(std::unique_ptr<const Base>&& base) { + assert(isa<Derived>(base.get())); + return std::unique_ptr<Derived>(static_cast<const Derived*>(base.release())); +}; + +template <typename Derived, typename Base> +std::unique_ptr<Derived> cast(std::unique_ptr<Base>&& base) { + assert(isa<Derived>(base.get())); + return std::unique_ptr<Derived>(static_cast<Derived*>(base.release())); +}; + +} // namespace wabt + +#endif // WABT_CAST_H_ diff --git a/third_party/wasm2c/src/circular-array.h b/third_party/wasm2c/src/circular-array.h new file mode 100644 index 0000000000..afdead77de --- /dev/null +++ b/third_party/wasm2c/src/circular-array.h @@ -0,0 +1,123 @@ +/* + * 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. + */ + +#ifndef WABT_CIRCULAR_ARRAY_H_ +#define WABT_CIRCULAR_ARRAY_H_ + +#include <array> +#include <cassert> +#include <cstddef> +#include <type_traits> + +namespace wabt { + +// TODO(karlschimpf) Complete the API +// Note: Capacity must be a power of 2. +template <class T, size_t kCapacity> +class CircularArray { + public: + typedef T value_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + CircularArray() { + static_assert(kCapacity && ((kCapacity & (kCapacity - 1)) == 0), + "Capacity must be a power of 2."); + } + + CircularArray(const CircularArray&) = default; + CircularArray& operator=(const CircularArray&) = default; + + CircularArray(CircularArray&&) = default; + CircularArray& operator=(CircularArray&&) = default; + + ~CircularArray() { clear(); } + + reference at(size_type index) { + assert(index < size_); + return (*this)[index]; + } + + const_reference at(size_type index) const { + assert(index < size_); + return (*this)[index]; + } + + reference operator[](size_type index) { return contents_[position(index)]; } + + const_reference operator[](size_type index) const { + return contents_[position(index)]; + } + + reference back() { return at(size_ - 1); } + + const_reference back() const { return at(size_ - 1); } + + bool empty() const { return size_ == 0; } + + reference front() { return at(0); } + + const_reference front() const { return at(0); } + + size_type max_size() const { return kCapacity; } + + void pop_back() { + assert(size_ > 0); + SetElement(back()); + --size_; + } + + void pop_front() { + assert(size_ > 0); + SetElement(front()); + front_ = (front_ + 1) & kMask; + --size_; + } + + void push_back(const value_type& value) { + assert(size_ < kCapacity); + SetElement(at(size_++), value); + } + + size_type size() const { return size_; } + + void clear() { + while (!empty()) { + pop_back(); + } + } + + private: + static const size_type kMask = kCapacity - 1; + + size_t position(size_t index) const { return (front_ + index) & kMask; } + + template <typename... Args> + void SetElement(reference element, Args&&... args) { + element.~T(); + new (&element) T(std::forward<Args>(args)...); + } + + std::array<T, kCapacity> contents_; + size_type size_ = 0; + size_type front_ = 0; +}; + +} // namespace wabt + +#endif // WABT_CIRCULAR_ARRAY_H_ diff --git a/third_party/wasm2c/src/color.cc b/third_party/wasm2c/src/color.cc new file mode 100644 index 0000000000..8fdc5d00f8 --- /dev/null +++ b/third_party/wasm2c/src/color.cc @@ -0,0 +1,84 @@ +/* + * 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 "src/color.h" + +#include <cstdlib> + +#include "src/common.h" + +#if _WIN32 +#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 _WIN32 + + { +#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; +#else + // TODO(binji): Support older Windows by using SetConsoleTextAttribute? + return false; +#endif + } + +#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/color.h b/third_party/wasm2c/src/color.h new file mode 100644 index 0000000000..8af57f33f7 --- /dev/null +++ b/third_party/wasm2c/src/color.h @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#ifndef WABT_COLOR_H_ +#define WABT_COLOR_H_ + +#include <cstdio> + +namespace wabt { + +#define WABT_FOREACH_COLOR_CODE(V) \ + V(Default, "\x1b[0m") \ + V(Bold, "\x1b[1m") \ + V(NoBold, "\x1b[22m") \ + V(Black, "\x1b[30m") \ + V(Red, "\x1b[31m") \ + V(Green, "\x1b[32m") \ + V(Yellow, "\x1b[33m") \ + V(Blue, "\x1b[34m") \ + V(Magenta, "\x1b[35m") \ + V(Cyan, "\x1b[36m") \ + V(White, "\x1b[37m") + +class Color { + public: + Color() : file_(nullptr), enabled_(false) {} + Color(FILE*, bool enabled = true); + +// Write the given color to the file, if enabled. +#define WABT_COLOR(Name, code) \ + void Name() const { WriteCode(Name##Code()); } + WABT_FOREACH_COLOR_CODE(WABT_COLOR) +#undef WABT_COLOR + +// Get the color code as a string, if enabled. +#define WABT_COLOR(Name, code) \ + const char* Maybe##Name##Code() const { return enabled_ ? Name##Code() : ""; } + WABT_FOREACH_COLOR_CODE(WABT_COLOR) +#undef WABT_COLOR + +// Get the color code as a string. +#define WABT_COLOR(Name, code) \ + static const char* Name##Code() { return code; } + WABT_FOREACH_COLOR_CODE(WABT_COLOR) +#undef WABT_COLOR + + private: + static bool SupportsColor(FILE*); + void WriteCode(const char*) const; + + FILE* file_; + bool enabled_; +}; + +#undef WABT_FOREACH_COLOR_CODE + +} // namespace wabt + +#endif // WABT_COLOR_H_ diff --git a/third_party/wasm2c/src/common.cc b/third_party/wasm2c/src/common.cc new file mode 100644 index 0000000000..6fe754a2b2 --- /dev/null +++ b/third_party/wasm2c/src/common.cc @@ -0,0 +1,147 @@ +/* + * 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 "src/common.h" + +#include <cassert> +#include <climits> +#include <cstdint> +#include <cstdio> +#include <cstring> + +#include <sys/types.h> +#include <sys/stat.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(string_view filename, std::vector<uint8_t>* out_data) { + std::string filename_str = filename.to_string(); + 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/common.h b/third_party/wasm2c/src/common.h new file mode 100644 index 0000000000..de5e44af36 --- /dev/null +++ b/third_party/wasm2c/src/common.h @@ -0,0 +1,490 @@ +/* + * 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_COMMON_H_ +#define WABT_COMMON_H_ + +#include <algorithm> +#include <cassert> +#include <cstdarg> +#include <cstddef> +#include <cstdint> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <memory> +#include <string> +#include <type_traits> +#include <vector> + +#include "config.h" + +#include "src/make-unique.h" +#include "src/result.h" +#include "src/string-view.h" +#include "src/type.h" + +#define WABT_FATAL(...) fprintf(stderr, __VA_ARGS__), exit(1) +#define WABT_ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +#define WABT_USE(x) static_cast<void>(x) + +#define WABT_PAGE_SIZE 0x10000 /* 64k */ +#define WABT_MAX_PAGES32 0x10000 /* # of pages that fit in 32-bit address \ + space */ +#define WABT_MAX_PAGES64 0x1000000000000 /* # of pages that fit in 64-bit \ + address space */ +#define WABT_BYTES_TO_PAGES(x) ((x) >> 16) +#define WABT_ALIGN_UP_TO_PAGE(x) \ + (((x) + WABT_PAGE_SIZE - 1) & ~(WABT_PAGE_SIZE - 1)) + +#define PRIstringview "%.*s" +#define WABT_PRINTF_STRING_VIEW_ARG(x) \ + static_cast<int>((x).length()), (x).data() + +#define PRItypecode "%s%#x" +#define WABT_PRINTF_TYPE_CODE(x) \ + (static_cast<int32_t>(x) < 0 ? "-" : ""), std::abs(static_cast<int32_t>(x)) + +#define WABT_DEFAULT_SNPRINTF_ALLOCA_BUFSIZE 128 +#define WABT_SNPRINTF_ALLOCA(buffer, len, format) \ + va_list args; \ + va_list args_copy; \ + va_start(args, format); \ + va_copy(args_copy, args); \ + char fixed_buf[WABT_DEFAULT_SNPRINTF_ALLOCA_BUFSIZE]; \ + char* buffer = fixed_buf; \ + size_t len = wabt_vsnprintf(fixed_buf, sizeof(fixed_buf), format, args); \ + va_end(args); \ + if (len + 1 > sizeof(fixed_buf)) { \ + buffer = static_cast<char*>(alloca(len + 1)); \ + len = wabt_vsnprintf(buffer, len + 1, format, args_copy); \ + } \ + va_end(args_copy) + +#define WABT_ENUM_COUNT(name) \ + (static_cast<int>(name::Last) - static_cast<int>(name::First) + 1) + +#define WABT_DISALLOW_COPY_AND_ASSIGN(type) \ + type(const type&) = delete; \ + type& operator=(const type&) = delete; + +#if WITH_EXCEPTIONS +#define WABT_TRY try { +#define WABT_CATCH_BAD_ALLOC \ + } \ + catch (std::bad_alloc&) { \ + } +#define WABT_CATCH_BAD_ALLOC_AND_EXIT \ + } \ + catch (std::bad_alloc&) { \ + WABT_FATAL("Memory allocation failure.\n"); \ + } +#else +#define WABT_TRY +#define WABT_CATCH_BAD_ALLOC +#define WABT_CATCH_BAD_ALLOC_AND_EXIT +#endif + +#define PRIindex "u" +#define PRIaddress PRIu64 +#define PRIoffset PRIzx + +namespace wabt { +#if WABT_BIG_ENDIAN + inline void MemcpyEndianAware(void *dst, const void *src, size_t dsize, size_t ssize, size_t doff, size_t soff, size_t len) { + memcpy(static_cast<char*>(dst) + (dsize) - (len) - (doff), + static_cast<const char*>(src) + (ssize) - (len) - (soff), + (len)); + } +#else + inline void MemcpyEndianAware(void *dst, const void *src, size_t dsize, size_t ssize, size_t doff, size_t soff, size_t len) { + memcpy(static_cast<char*>(dst) + (doff), + static_cast<const char*>(src) + (soff), + (len)); + } +#endif +} + +struct v128 { + v128() = default; + v128(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3) { + set_u32(0, x0); + set_u32(1, x1); + set_u32(2, x2); + set_u32(3, x3); + } + + bool operator==(const v128& other) const { + return std::equal(std::begin(v), std::end(v), std::begin(other.v)); + } + bool operator!=(const v128& other) const { return !(*this == other); } + + uint8_t u8(int lane) const { return To<uint8_t>(lane); } + uint16_t u16(int lane) const { return To<uint16_t>(lane); } + uint32_t u32(int lane) const { return To<uint32_t>(lane); } + uint64_t u64(int lane) const { return To<uint64_t>(lane); } + uint32_t f32_bits(int lane) const { return To<uint32_t>(lane); } + uint64_t f64_bits(int lane) const { return To<uint64_t>(lane); } + + void set_u8(int lane, uint8_t x) { return From<uint8_t>(lane, x); } + void set_u16(int lane, uint16_t x) { return From<uint16_t>(lane, x); } + void set_u32(int lane, uint32_t x) { return From<uint32_t>(lane, x); } + void set_u64(int lane, uint64_t x) { return From<uint64_t>(lane, x); } + void set_f32_bits(int lane, uint32_t x) { return From<uint32_t>(lane, x); } + void set_f64_bits(int lane, uint64_t x) { return From<uint64_t>(lane, x); } + + bool is_zero() const { + return std::all_of(std::begin(v), std::end(v), + [](uint8_t x) { return x == 0; }); + } + void set_zero() { std::fill(std::begin(v), std::end(v), 0); } + + template <typename T> + T To(int lane) const { + static_assert(sizeof(T) <= sizeof(v), "Invalid cast!"); + assert((lane + 1) * sizeof(T) <= sizeof(v)); + T result; + wabt::MemcpyEndianAware(&result, v, sizeof(result), sizeof(v), 0, lane * sizeof(T), sizeof(result)); + return result; + } + + template <typename T> + void From(int lane, T data) { + static_assert(sizeof(T) <= sizeof(v), "Invalid cast!"); + assert((lane + 1) * sizeof(T) <= sizeof(v)); + wabt::MemcpyEndianAware(v, &data, sizeof(v), sizeof(data), lane * sizeof(T), 0, sizeof(data)); + } + + uint8_t v[16]; +}; + +namespace wabt { + +typedef uint32_t Index; // An index into one of the many index spaces. +typedef uint64_t Address; // An address or size in linear memory. +typedef size_t Offset; // An offset into a host's file or memory buffer. + +static const Address kInvalidAddress = ~0; +static const Index kInvalidIndex = ~0; +static const Offset kInvalidOffset = ~0; + +template <typename Dst, typename Src> +Dst WABT_VECTORCALL Bitcast(Src&& value) { + static_assert(sizeof(Src) == sizeof(Dst), "Bitcast sizes must match."); + Dst result; + memcpy(&result, &value, sizeof(result)); + return result; +} + +template <typename T> +void ZeroMemory(T& v) { + WABT_STATIC_ASSERT(std::is_pod<T>::value); + memset(&v, 0, sizeof(v)); +} + +// Placement construct +template <typename T, typename... Args> +void Construct(T& placement, Args&&... args) { + new (&placement) T(std::forward<Args>(args)...); +} + +// Placement destruct +template <typename T> +void Destruct(T& placement) { + placement.~T(); +} + +inline std::string WABT_PRINTF_FORMAT(1, 2) + StringPrintf(const char* format, ...) { + va_list args; + va_list args_copy; + va_start(args, format); + va_copy(args_copy, args); + size_t len = wabt_vsnprintf(nullptr, 0, format, args) + 1; // For \0. + std::vector<char> buffer(len); + va_end(args); + wabt_vsnprintf(buffer.data(), len, format, args_copy); + va_end(args_copy); + return std::string(buffer.data(), len - 1); +} + +enum class LabelType { + Func, + Block, + Loop, + If, + Else, + Try, + Catch, + + First = Func, + Last = Catch, +}; +static const int kLabelTypeCount = WABT_ENUM_COUNT(LabelType); + +struct Location { + enum class Type { + Text, + Binary, + }; + + Location() : line(0), first_column(0), last_column(0) {} + Location(string_view filename, int line, int first_column, int last_column) + : filename(filename), + line(line), + first_column(first_column), + last_column(last_column) {} + explicit Location(size_t offset) : offset(offset) {} + + string_view filename; + union { + // For text files. + struct { + int line; + int first_column; + int last_column; + }; + // For binary files. + struct { + size_t offset; + }; + }; +}; + +enum class SegmentKind { + Active, + Passive, + Declared, +}; + +// Used in test asserts for special expected values "nan:canonical" and +// "nan:arithmetic" +enum class ExpectedNan { + None, + Canonical, + Arithmetic, +}; + +// Matches binary format, do not change. +enum SegmentFlags : uint8_t { + SegFlagsNone = 0, + SegPassive = 1, // bit 0: Is passive + SegExplicitIndex = 2, // bit 1: Has explict index (Implies table 0 if absent) + SegDeclared = 3, // Only used for declared segments + SegUseElemExprs = 4, // bit 2: Is elemexpr (Or else index sequence) + + SegFlagMax = (SegUseElemExprs << 1) - 1, // All bits set. +}; + +enum class RelocType { + FuncIndexLEB = 0, // e.g. Immediate of call instruction + TableIndexSLEB = 1, // e.g. Loading address of function + TableIndexI32 = 2, // e.g. Function address in DATA + MemoryAddressLEB = 3, // e.g. Memory address in load/store offset immediate + MemoryAddressSLEB = 4, // e.g. Memory address in i32.const + MemoryAddressI32 = 5, // e.g. Memory address in DATA + TypeIndexLEB = 6, // e.g. Immediate type in call_indirect + GlobalIndexLEB = 7, // e.g. Immediate of get_global inst + FunctionOffsetI32 = 8, // e.g. Code offset in DWARF metadata + SectionOffsetI32 = 9, // e.g. Section offset in DWARF metadata + TagIndexLEB = 10, // Used in throw instructions + MemoryAddressRelSLEB = 11, // In PIC code, addr relative to __memory_base + TableIndexRelSLEB = 12, // In PIC code, table index relative to __table_base + GlobalIndexI32 = 13, // e.g. Global index in data (e.g. DWARF) + MemoryAddressLEB64 = 14, // Memory64: Like MemoryAddressLEB + MemoryAddressSLEB64 = 15, // Memory64: Like MemoryAddressSLEB + MemoryAddressI64 = 16, // Memory64: Like MemoryAddressI32 + MemoryAddressRelSLEB64 = 17, // Memory64: Like MemoryAddressRelSLEB + TableIndexSLEB64 = 18, // Memory64: Like TableIndexSLEB + TableIndexI64 = 19, // Memory64: Like TableIndexI32 + TableNumberLEB = 20, // e.g. Immediate of table.get + MemoryAddressTLSSLEB = 21, // Address relative to __tls_base + MemoryAddressTLSI32 = 22, // Address relative to __tls_base + + First = FuncIndexLEB, + Last = MemoryAddressTLSI32, +}; +static const int kRelocTypeCount = WABT_ENUM_COUNT(RelocType); + +struct Reloc { + Reloc(RelocType, size_t offset, Index index, int32_t addend = 0); + + RelocType type; + size_t offset; + Index index; + int32_t addend; +}; + +enum class LinkingEntryType { + SegmentInfo = 5, + InitFunctions = 6, + ComdatInfo = 7, + SymbolTable = 8, +}; + +enum class DylinkEntryType { + MemInfo = 1, + Needed = 2, +}; + +enum class SymbolType { + Function = 0, + Data = 1, + Global = 2, + Section = 3, + Tag = 4, + Table = 5, +}; + +enum class ComdatType { + Data = 0x0, + Function = 0x1, +}; + +#define WABT_SYMBOL_MASK_VISIBILITY 0x4 +#define WABT_SYMBOL_MASK_BINDING 0x3 +#define WABT_SYMBOL_FLAG_UNDEFINED 0x10 +#define WABT_SYMBOL_FLAG_EXPORTED 0x20 +#define WABT_SYMBOL_FLAG_EXPLICIT_NAME 0x40 +#define WABT_SYMBOL_FLAG_NO_STRIP 0x80 +#define WABT_SYMBOL_FLAG_TLS 0x100 +#define WABT_SYMBOL_FLAG_MAX 0x1ff + +#define WABT_SEGMENT_FLAG_STRINGS 0x1 +#define WABT_SEGMENT_FLAG_TLS 0x2 +#define WABT_SEGMENT_FLAG_MAX 0xff + +enum class SymbolVisibility { + Default = 0, + Hidden = 4, +}; + +enum class SymbolBinding { + Global = 0, + Weak = 1, + Local = 2, +}; + +/* matches binary format, do not change */ +enum class ExternalKind { + Func = 0, + Table = 1, + Memory = 2, + Global = 3, + Tag = 4, + + First = Func, + Last = Tag, +}; +static const int kExternalKindCount = WABT_ENUM_COUNT(ExternalKind); + +struct Limits { + Limits() = default; + explicit Limits(uint64_t initial) : initial(initial) {} + Limits(uint64_t initial, uint64_t max) + : initial(initial), max(max), has_max(true) {} + Limits(uint64_t initial, uint64_t max, bool is_shared) + : initial(initial), max(max), has_max(true), is_shared(is_shared) {} + Limits(uint64_t initial, uint64_t max, bool is_shared, bool is_64) + : initial(initial), + max(max), + has_max(true), + is_shared(is_shared), + is_64(is_64) {} + Type IndexType() const { return is_64 ? Type::I64 : Type::I32; } + + uint64_t initial = 0; + uint64_t max = 0; + bool has_max = false; + bool is_shared = false; + bool is_64 = false; +}; + +enum { WABT_USE_NATURAL_ALIGNMENT = 0xFFFFFFFFFFFFFFFF }; + +Result ReadFile(string_view filename, std::vector<uint8_t>* out_data); + +void InitStdio(); + +/* external kind */ + +extern const char* g_kind_name[]; + +static WABT_INLINE const char* GetKindName(ExternalKind kind) { + return static_cast<size_t>(kind) < kExternalKindCount + ? g_kind_name[static_cast<size_t>(kind)] + : "<error_kind>"; +} + +/* reloc */ + +extern const char* g_reloc_type_name[]; + +static WABT_INLINE const char* GetRelocTypeName(RelocType reloc) { + return static_cast<size_t>(reloc) < kRelocTypeCount + ? g_reloc_type_name[static_cast<size_t>(reloc)] + : "<error_reloc_type>"; +} + +/* symbol */ + +static WABT_INLINE const char* GetSymbolTypeName(SymbolType type) { + switch (type) { + case SymbolType::Function: + return "func"; + case SymbolType::Global: + return "global"; + case SymbolType::Data: + return "data"; + case SymbolType::Section: + return "section"; + case SymbolType::Tag: + return "tag"; + case SymbolType::Table: + return "table"; + default: + return "<error_symbol_type>"; + } +} + +template <typename T> +void ConvertBackslashToSlash(T begin, T end) { + std::replace(begin, end, '\\', '/'); +} + +inline void ConvertBackslashToSlash(char* s, size_t length) { + ConvertBackslashToSlash(s, s + length); +} + +inline void ConvertBackslashToSlash(char* s) { + ConvertBackslashToSlash(s, strlen(s)); +} + +inline void ConvertBackslashToSlash(std::string* s) { + ConvertBackslashToSlash(s->begin(), s->end()); +} + +inline void SwapBytesSized(void *addr, size_t size) { + auto bytes = static_cast<uint8_t*>(addr); + for (size_t i = 0; i < size / 2; i++) { + std::swap(bytes[i], bytes[size-1-i]); + } +} + +} // namespace wabt + +#endif // WABT_COMMON_H_ diff --git a/third_party/wasm2c/src/config.cc b/third_party/wasm2c/src/config.cc new file mode 100644 index 0000000000..9347c860a9 --- /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 "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..5960310084 --- /dev/null +++ b/third_party/wasm2c/src/config.h.in @@ -0,0 +1,316 @@ +/* + * 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 CMAKE_PROJECT_VERSION "${CMAKE_PROJECT_VERSION}" + +/* 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 + +#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_UNUSED __attribute__((unused)) +#define WABT_WARN_UNUSED __attribute__((warn_unused_result)) +#define WABT_INLINE inline +#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 +#if __cplusplus >= 201103L +#define WABT_STATIC_ASSERT(x) static_assert((x), #x) +#else +#define WABT_STATIC_ASSERT__(x, c) \ + static int static_assert_##c[(x ? 0 : -1)] WABT_UNUSED +#define WABT_STATIC_ASSERT_(x, c) WABT_STATIC_ASSERT__(x, c) +#define WABT_STATIC_ASSERT(x) WABT_STATIC_ASSERT_(x, __COUNTER__) +#endif +#else +#define WABT_STATIC_ASSERT(x) _Static_assert((x), #x) +#endif + +#elif COMPILER_IS_MSVC + +#include <intrin.h> +#include <string.h> + +#define WABT_UNUSED +#define WABT_WARN_UNUSED +#define WABT_INLINE __inline +#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 + 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 + 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 +} + +inline int Popcount(uint8_t value) { + return __popcnt(value); +} + +inline int Popcount(unsigned long value) { + return __popcnt(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)); +#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-ast.h b/third_party/wasm2c/src/decompiler-ast.h new file mode 100644 index 0000000000..ec5b3880ca --- /dev/null +++ b/third_party/wasm2c/src/decompiler-ast.h @@ -0,0 +1,386 @@ +/* + * 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_DECOMPILER_AST_H_ +#define WABT_DECOMPILER_AST_H_ + +#include "src/cast.h" +#include "src/generate-names.h" +#include "src/ir.h" +#include "src/ir-util.h" + +#include <map> + +namespace wabt { + +enum class NodeType { + Uninitialized, + FlushToVars, + FlushedVar, + Statements, + EndReturn, + Decl, + DeclInit, + Expr +}; + +// The AST we're going to convert the standard IR into. +struct Node { + NodeType ntype; + ExprType etype; // Only if ntype == Expr. + const Expr* e; + std::vector<Node> children; + // Node specific annotations. + union { + struct { Index var_start, var_count; }; // FlushedVar/FlushToVars + const Var* var; // Decl/DeclInit. + LabelType lt; // br/br_if target. + } u; + + Node() : ntype(NodeType::Uninitialized), etype(ExprType::Nop), e(nullptr) { + } + Node(NodeType ntype, ExprType etype, const Expr* e, const Var* v) + : ntype(ntype), etype(etype), e(e) { + u.var = v; + } + + // This value should really never be copied, only moved. + Node(const Node& rhs) = delete; + Node& operator=(const Node& rhs) = delete; + + Node(Node&& rhs) { *this = std::move(rhs); } + Node& operator=(Node&& rhs) { + ntype = rhs.ntype; + // Reset ntype to avoid moved from values still being used. + rhs.ntype = NodeType::Uninitialized; + etype = rhs.etype; + rhs.etype = ExprType::Nop; + e = rhs.e; + std::swap(children, rhs.children); + u = rhs.u; + return *this; + } +}; + +struct AST { + AST(ModuleContext& mc, const Func *f) : mc(mc), f(f) { + if (f) { + mc.BeginFunc(*f); + for (Index i = 0; i < f->GetNumParams(); i++) { + auto name = "$" + IndexToAlphaName(i); + vars_defined.insert({ name, { 0, false }}); + } + } + } + + ~AST() { + if (f) mc.EndFunc(); + } + + // Create a new node, take nargs existing nodes on the exp stack as children. + Node& InsertNode(NodeType ntype, ExprType etype, const Expr* e, Index nargs) { + assert(exp_stack.size() >= nargs); + Node n { ntype, etype, e, nullptr }; + n.children.reserve(nargs); + std::move(exp_stack.end() - nargs, exp_stack.end(), + std::back_inserter(n.children)); + exp_stack.erase(exp_stack.end() - nargs, exp_stack.end()); + exp_stack.push_back(std::move(n)); + return exp_stack.back(); + } + + template<ExprType T> void PreDecl(const VarExpr<T>& ve) { + // FIXME: this is slow, and would be better to avoid in callers. + // See https://github.com/WebAssembly/wabt/issues/1565 + // And https://github.com/WebAssembly/wabt/issues/1665 + for (auto& n : predecls) { + if (n.u.var->name() == ve.var.name()) { + return; + } + } + predecls.emplace_back(NodeType::Decl, ExprType::Nop, nullptr, &ve.var); + } + + template<ExprType T> void Get(const VarExpr<T>& ve, bool local) { + if (local) { + auto ret = vars_defined.insert({ ve.var.name(), { cur_block_id, false }}); + if (ret.second) { + // Use before def, may happen since locals are guaranteed 0. + PreDecl(ve); + } else if (blocks_closed[ret.first->second.block_id]) { + // This is a use of a variable that was defined in a block that has + // already ended. This happens rarely, but we should cater for this + // case by lifting it to the top scope. + PreDecl(ve); + } + } + InsertNode(NodeType::Expr, T, &ve, 0); + } + + template<ExprType T> void Set(const VarExpr<T>& ve, bool local) { + // Seen this var before? + if (local && + vars_defined.insert({ ve.var.name(), { cur_block_id, false }}).second) { + if (value_stack_depth == 1) { + // Top level, declare it here. + InsertNode(NodeType::DeclInit, ExprType::Nop, nullptr, 1).u.var = + &ve.var; + return; + } else { + // Inside exp, better leave it as assignment exp and lift the decl out. + PreDecl(ve); + } + } + InsertNode(NodeType::Expr, T, &ve, 1); + } + + template<ExprType T> void Block(const BlockExprBase<T>& be, LabelType label) { + mc.BeginBlock(label, be.block); + Construct(be.block.exprs, be.block.decl.GetNumResults(), be.block.decl.GetNumParams(), false); + mc.EndBlock(); + InsertNode(NodeType::Expr, T, &be, 1); + } + + void Construct(const Expr& e) { + auto arity = mc.GetExprArity(e); + switch (e.type()) { + case ExprType::LocalGet: { + Get(*cast<LocalGetExpr>(&e), true); + return; + } + case ExprType::GlobalGet: { + Get(*cast<GlobalGetExpr>(&e), false); + return; + } + case ExprType::LocalSet: { + Set(*cast<LocalSetExpr>(&e), true); + return; + } + case ExprType::GlobalSet: { + Set(*cast<GlobalSetExpr>(&e), false); + return; + } + case ExprType::LocalTee: { + auto& lt = *cast<LocalTeeExpr>(&e); + Set(lt, true); + if (value_stack_depth == 1) { // Tee is the only thing on there. + Get(lt, true); // Now Set + Get instead. + } else { + // Things are above us on the stack so the Tee can't be eliminated. + // The Set makes this work as a Tee when consumed by a parent. + } + return; + } + case ExprType::If: { + auto ife = cast<IfExpr>(&e); + value_stack_depth--; // Condition. + mc.BeginBlock(LabelType::Block, ife->true_); + Construct(ife->true_.exprs, ife->true_.decl.GetNumResults(), ife->true_.decl.GetNumParams(), false); + if (!ife->false_.empty()) { + Construct(ife->false_, ife->true_.decl.GetNumResults(), ife->true_.decl.GetNumParams(), false); + } + mc.EndBlock(); + value_stack_depth++; // Put Condition back. + InsertNode(NodeType::Expr, ExprType::If, &e, ife->false_.empty() ? 2 : 3); + return; + } + case ExprType::Block: { + Block(*cast<BlockExpr>(&e), LabelType::Block); + return; + } + case ExprType::Loop: { + Block(*cast<LoopExpr>(&e), LabelType::Loop); + return; + } + case ExprType::Br: { + InsertNode(NodeType::Expr, ExprType::Br, &e, 0).u.lt = + mc.GetLabel(cast<BrExpr>(&e)->var)->label_type; + return; + } + case ExprType::BrIf: { + InsertNode(NodeType::Expr, ExprType::BrIf, &e, 1).u.lt = + mc.GetLabel(cast<BrIfExpr>(&e)->var)->label_type; + return; + } + default: { + InsertNode(NodeType::Expr, e.type(), &e, arity.nargs); + return; + } + } + } + + void Construct(const ExprList& es, Index nresults, Index nparams, bool is_function_body) { + block_stack.push_back(cur_block_id); + cur_block_id = blocks_closed.size(); + blocks_closed.push_back(false); + auto start = exp_stack.size(); + int value_stack_depth_start = value_stack_depth - nparams; + auto value_stack_in_variables = value_stack_depth; + bool unreachable = false; + for (auto& e : es) { + Construct(e); + auto arity = mc.GetExprArity(e); + value_stack_depth -= arity.nargs; + value_stack_in_variables = std::min(value_stack_in_variables, + value_stack_depth); + unreachable = unreachable || arity.unreachable; + assert(unreachable || value_stack_depth >= value_stack_depth_start); + value_stack_depth += arity.nreturns; + // We maintain the invariant that a value_stack_depth of N is represented + // by the last N exp_stack items (each of which returning exactly 1 + // value), all exp_stack items before it return void ("statements"). + // In particular for the wasm stack: + // - The values between 0 and value_stack_depth_start are part of the + // parent block, and not touched here. + // - The values from there up to value_stack_in_variables are variables + // to be used, representing previous statements that flushed the + // stack into variables. + // - Values on top of that up to value_stack_depth are exps returning + // a single value. + // The code below maintains the above invariants. With this in place + // code "falls into place" the way you expect it. + if (arity.nreturns != 1) { + auto num_vars = value_stack_in_variables - value_stack_depth_start; + auto num_vals = value_stack_depth - value_stack_in_variables; + auto GenFlushVars = [&](int nargs) { + auto& ftv = InsertNode(NodeType::FlushToVars, ExprType::Nop, nullptr, + nargs); + ftv.u.var_start = flushed_vars; + ftv.u.var_count = num_vals; + }; + auto MoveStatementsBelowVars = [&](size_t amount) { + std::rotate(exp_stack.end() - num_vars - amount, + exp_stack.end() - amount, + exp_stack.end()); + }; + auto GenFlushedVars = [&]() { + // Re-generate these values as vars. + for (int i = 0; i < num_vals; i++) { + auto& fv = InsertNode(NodeType::FlushedVar, ExprType::Nop, nullptr, + 0); + fv.u.var_start = flushed_vars++; + fv.u.var_count = 1; + } + }; + if (arity.nreturns == 0 && + value_stack_depth > value_stack_depth_start) { + // We have a void item on top of the exp_stack, so we must "lift" the + // previous values around it. + // We track what part of the stack is in variables to avoid doing + // this unnecessarily. + if (num_vals > 0) { + // We have actual new values that need lifting. + // This puts the part of the stack that wasn't already a variable + // (before the current void exp) into a FlushToVars. + auto void_exp = std::move(exp_stack.back()); + exp_stack.pop_back(); + GenFlushVars(num_vals); + exp_stack.push_back(std::move(void_exp)); + // Put this flush statement + void statement before any + // existing variables. + MoveStatementsBelowVars(2); + // Now re-generate these values after the void exp as vars. + GenFlushedVars(); + } else { + // We have existing variables that need lifting, but no need to + // create them anew. + std::rotate(exp_stack.end() - num_vars - 1, exp_stack.end() - 1, + exp_stack.end()); + } + value_stack_in_variables = value_stack_depth; + } else if (arity.nreturns > 1) { + // Multivalue: we flush the stack also. + // Number of other non-variable values that may be present: + assert(num_vals >= static_cast<int>(arity.nreturns)); + // Flush multi-value exp + others. + GenFlushVars(num_vals - arity.nreturns + 1); + // Put this flush statement before any existing variables. + MoveStatementsBelowVars(1); + GenFlushedVars(); + value_stack_in_variables = value_stack_depth; + } + } else { + // Special optimisation: for constant instructions, we can mark these + // as if they were variables, so they can be re-ordered for free with + // the above code, without needing new variables! + // TODO: this would be nice to also do for get_local and maybe others, + // though that needs a way to ensure there's no set_local in between + // when they get lifted, so complicates matters a bit. + if (e.type() == ExprType::Const && + value_stack_in_variables == value_stack_depth - 1) { + value_stack_in_variables++; + } + } + } + assert(unreachable || + value_stack_depth - value_stack_depth_start == + static_cast<int>(nresults)); + // Undo any changes to value_stack_depth, since parent takes care of arity + // changes. + value_stack_depth = value_stack_depth_start; + auto end = exp_stack.size(); + assert(end >= start); + if (is_function_body) { + if (!exp_stack.empty()) { + if (exp_stack.back().etype == ExprType::Return) { + if (exp_stack.back().children.empty()) { + // Return statement at the end of a void function. + exp_stack.pop_back(); + } + } else if (nresults) { + // Combine nresults into a return statement, for when this is used as + // a function body. + // TODO: if this is some other kind of block and >1 value is being + // returned, probably need some kind of syntax to make that clearer. + InsertNode(NodeType::EndReturn, ExprType::Nop, nullptr, nresults); + } + } + // TODO: these predecls are always at top level, but in the case of + // use inside an exp, it be nice to do it in the current block. Can't + // do that for predecls that are "out if scope" however. + std::move(predecls.begin(), predecls.end(), + std::back_inserter(exp_stack)); + std::rotate(exp_stack.begin(), exp_stack.end() - predecls.size(), + exp_stack.end()); + predecls.clear(); + } + end = exp_stack.size(); + assert(end >= start); + auto size = end - start; + if (size != 1) { + InsertNode(NodeType::Statements, ExprType::Nop, nullptr, size); + } + blocks_closed[cur_block_id] = true; + cur_block_id = block_stack.back(); + block_stack.pop_back(); + } + + ModuleContext& mc; + std::vector<Node> exp_stack; + std::vector<Node> predecls; + const Func *f; + int value_stack_depth = 0; + struct Variable { size_t block_id; bool defined; }; + std::map<std::string, Variable> vars_defined; + Index flushed_vars = 0; + size_t cur_block_id = 0; + std::vector<size_t> block_stack; + std::vector<bool> blocks_closed; +}; + +} // namespace wabt + +#endif // WABT_DECOMPILER_AST_H_ diff --git a/third_party/wasm2c/src/decompiler-ls.h b/third_party/wasm2c/src/decompiler-ls.h new file mode 100644 index 0000000000..f622e3fd9f --- /dev/null +++ b/third_party/wasm2c/src/decompiler-ls.h @@ -0,0 +1,265 @@ +/* + * 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. + */ + +#ifndef WABT_DECOMPILER_LS_H_ +#define WABT_DECOMPILER_LS_H_ + +#include "src/decompiler-ast.h" + +#include <map> + +namespace wabt { + +// Names starting with "u" are unsigned, the rest are "signed or doesn't matter" +inline const char *GetDecompTypeName(Type t) { + switch (t) { + case Type::I8: return "byte"; + case Type::I8U: return "ubyte"; + case Type::I16: return "short"; + case Type::I16U: return "ushort"; + case Type::I32: return "int"; + case Type::I32U: return "uint"; + case Type::I64: return "long"; + case Type::F32: return "float"; + case Type::F64: return "double"; + case Type::V128: return "simd"; + case Type::Func: return "func"; + case Type::FuncRef: return "funcref"; + case Type::ExternRef: return "externref"; + case Type::Void: return "void"; + default: return "ILLEGAL"; + } +} + +inline Type GetMemoryType(Type operand_type, Opcode opc) { + // TODO: something something SIMD. + // TODO: this loses information of the type it is read into. + // That may well not be the biggest deal since that is usually obvious + // from context, if not, we should probably represent that as a cast around + // the access, since it should not be part of the field type. + if (operand_type == Type::I32 || operand_type == Type::I64) { + auto name = string_view(opc.GetName()); + // FIXME: change into a new column in opcode.def instead? + auto is_unsigned = name.substr(name.size() - 2) == "_u"; + switch (opc.GetMemorySize()) { + case 1: return is_unsigned ? Type::I8U : Type::I8; + case 2: return is_unsigned ? Type::I16U : Type::I16; + case 4: return is_unsigned ? Type::I32U : Type::I32; + } + } + return operand_type; +} + +// Track all loads and stores inside a single function, to be able to detect +// struct layouts we can use to annotate variables with, to make code more +// readable. +struct LoadStoreTracking { + struct LSAccess { + Address byte_size = 0; + Type type = Type::Any; + Address align = 0; + uint32_t idx = 0; + bool is_uniform = true; + }; + + struct LSVar { + std::map<uint64_t, LSAccess> accesses; + bool struct_layout = true; + Type same_type = Type::Any; + Address same_align = kInvalidAddress; + Opcode last_opc; + }; + + void Track(const Node& n) { + for (auto& c : n.children) Track(c); + switch (n.etype) { + case ExprType::Load: { + auto& le = *cast<LoadExpr>(n.e); + LoadStore(le.offset, le.opcode, le.opcode.GetResultType(), + le.align, n.children[0]); + break; + } + case ExprType::Store: { + auto& se = *cast<StoreExpr>(n.e); + LoadStore(se.offset, se.opcode, se.opcode.GetParamType2(), + se.align, n.children[0]); + break; + } + default: + break; + } + } + + const std::string AddrExpName(const Node& addr_exp) const { + // TODO: expand this to more kinds of address expressions. + switch (addr_exp.etype) { + case ExprType::LocalGet: + return cast<LocalGetExpr>(addr_exp.e)->var.name(); + break; + case ExprType::LocalTee: + return cast<LocalTeeExpr>(addr_exp.e)->var.name(); + break; + default: + return ""; + } + } + + void LoadStore(uint64_t offset, Opcode opc, Type type, Address align, + const Node& addr_exp) { + auto byte_size = opc.GetMemorySize(); + type = GetMemoryType(type, opc); + // We want to associate memory ops of a certain offset & size as being + // relative to a uniquely identifiable pointer, such as a local. + auto name = AddrExpName(addr_exp); + if (name.empty()) { + return; + } + auto& var = vars[name]; + auto& access = var.accesses[offset]; + // Check if previous access at this offset (if any) is of same size + // and type (see Checklayouts below). + if (access.byte_size && + ((access.byte_size != byte_size) || + (access.type != type) || + (access.align != align))) + access.is_uniform = false; + // Also exclude weird alignment accesses from structs. + if (!opc.IsNaturallyAligned(align)) + access.is_uniform = false; + access.byte_size = byte_size; + access.type = type; + access.align = align; + // Additionally, check if all accesses are to the same type, so + // if layout check fails, we can at least declare it as pointer to + // a type. + if ((var.same_type == type || var.same_type == Type::Any) && + (var.same_align == align || var.same_align == kInvalidAddress)) { + var.same_type = type; + var.same_align = align; + var.last_opc = opc; + } else { + var.same_type = Type::Void; + var.same_align = kInvalidAddress; + } + } + + void CheckLayouts() { + // Here we check if the set of accesses we have collected form a sequence + // we could declare as a struct, meaning they are properly aligned, + // contiguous, and have no overlaps between different types and sizes. + // We do this because an int access of size 2 at offset 0 followed by + // a float access of size 4 at offset 4 can compactly represented as a + // struct { short, float }, whereas something that reads from overlapping + // or discontinuous offsets would need a more complicated syntax that + // involves explicit offsets. + // We assume that the bulk of memory accesses are of this very regular kind, + // so we choose not to even emit struct layouts for irregular ones, + // given that they are rare and confusing, and thus do not benefit from + // being represented as if they were structs. + for (auto& var : vars) { + if (var.second.accesses.size() == 1) { + // If we have just one access, this is better represented as a pointer + // than a struct. + var.second.struct_layout = false; + continue; + } + uint64_t cur_offset = 0; + uint32_t idx = 0; + for (auto& access : var.second.accesses) { + access.second.idx = idx++; + if (!access.second.is_uniform) { + var.second.struct_layout = false; + break; + } + // Align to next access: all elements are expected to be aligned to + // a memory address thats a multiple of their own size. + auto mask = static_cast<uint64_t>(access.second.byte_size - 1); + cur_offset = (cur_offset + mask) & ~mask; + if (cur_offset != access.first) { + var.second.struct_layout = false; + break; + } + cur_offset += access.second.byte_size; + } + } + } + + std::string IdxToName(uint32_t idx) const { + return IndexToAlphaName(idx); // TODO: more descriptive names? + } + + std::string GenAlign(Address align, Opcode opc) const { + return opc.IsNaturallyAligned(align) + ? "" + : cat("@", std::to_string(align)); + } + + std::string GenTypeDecl(const std::string& name) const { + auto it = vars.find(name); + if (it == vars.end()) { + return ""; + } + if (it->second.struct_layout) { + std::string s = "{ "; + for (auto& access : it->second.accesses) { + if (access.second.idx) s += ", "; + s += IdxToName(access.second.idx); + s += ':'; + s += GetDecompTypeName(access.second.type); + } + s += " }"; + return s; + } + // We don't have a struct layout, or the struct has just one field, + // so maybe we can just declare it as a pointer to one type? + if (it->second.same_type != Type::Void) { + return cat(GetDecompTypeName(it->second.same_type), "_ptr", + GenAlign(it->second.same_align, it->second.last_opc)); + } + return ""; + } + + std::string GenAccess(uint64_t offset, const Node& addr_exp) const { + auto name = AddrExpName(addr_exp); + if (name.empty()) { + return ""; + } + auto it = vars.find(name); + if (it == vars.end()) { + return ""; + } + if (it->second.struct_layout) { + auto ait = it->second.accesses.find(offset); + assert (ait != it->second.accesses.end()); + return IdxToName(ait->second.idx); + } + // Not a struct, see if it is a typed pointer. + if (it->second.same_type != Type::Void) { + return "*"; + } + return ""; + } + + void Clear() { + vars.clear(); + } + + std::map<std::string, LSVar> vars; +}; + +} // namespace wabt + +#endif // WABT_DECOMPILER_LS_H_ diff --git a/third_party/wasm2c/src/decompiler-naming.h b/third_party/wasm2c/src/decompiler-naming.h new file mode 100644 index 0000000000..6f34831702 --- /dev/null +++ b/third_party/wasm2c/src/decompiler-naming.h @@ -0,0 +1,211 @@ +/* + * 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. + */ + +#ifndef WABT_DECOMPILER_NAMING_H_ +#define WABT_DECOMPILER_NAMING_H_ + +#include "src/decompiler-ast.h" + +#include <set> + +namespace wabt { + +inline void RenameToIdentifier(std::string& name, Index i, + BindingHash& bh, + const std::set<string_view>* filter) { + // Filter out non-identifier characters, and try to reduce the size of + // gigantic C++ signature names. + std::string s; + size_t nesting = 0; + size_t read = 0; + size_t word_start = 0; + for (auto c : name) { + read++; + // We most certainly don't want to parse the entirety of C++ signatures, + // but these names are sometimes several lines long, so would be great + // to trim down. One quick way to do that is to remove anything between + // nested (), which usually means the parameter list. + if (c == '(') { + nesting++; + } + if (c == ')') { + nesting--; + } + if (nesting) { + continue; + } + if (!isalnum(static_cast<unsigned char>(c))) { + c = '_'; + } + if (c == '_') { + if (s.empty()) { + continue; // Skip leading. + } + if (s.back() == '_') { + continue; // Consecutive. + } + } + s += c; + if (filter && (c == '_' || read == name.size())) { + // We found a "word" inside a snake_case identifier. + auto word_end = s.size(); + if (c == '_') { + word_end--; + } + assert(word_end > word_start); + auto word = string_view(s.c_str() + word_start, word_end - word_start); + if (filter->find(word) != filter->end()) { + s.resize(word_start); + } + word_start = s.size(); + } + } + if (!s.empty() && s.back() == '_') { + s.pop_back(); // Trailing. + } + // If after all this culling, we're still gigantic (STL identifier can + // easily be hundreds of chars in size), just cut the identifier + // down, it will be disambiguated below, if needed. + const size_t max_identifier_length = 100; + if (s.size() > max_identifier_length) { + s.resize(max_identifier_length); + } + // Remove original binding first, such that it doesn't match with our + // new name. + bh.erase(name); + // Find a unique name. + Index disambiguator = 0; + auto base_len = s.size(); + for (;;) { + if (bh.count(s) == 0) { + break; + } + disambiguator++; + s.resize(base_len); + s += '_'; + s += std::to_string(disambiguator); + } + // Replace name in bindings. + name = s; + bh.emplace(s, Binding(i)); +} + +template<typename T> +void RenameToIdentifiers(std::vector<T*>& things, BindingHash& bh, + const std::set<string_view>* filter) { + Index i = 0; + for (auto thing : things) { + RenameToIdentifier(thing->name, i++, bh, filter); + } +} + +enum { + // This a bit arbitrary, change at will. + min_content_identifier_size = 7, + max_content_identifier_size = 30 +}; + +void RenameToContents(std::vector<DataSegment*>& segs, BindingHash& bh) { + std::string s; + for (auto seg : segs) { + if (seg->name.substr(0, 2) != "d_") { + // This segment was named explicitly by a symbol. + // FIXME: this is not a great check, a symbol could start with d_. + continue; + } + s = "d_"; + for (auto c : seg->data) { + if (isalnum(c) || c == '_') { + s += static_cast<char>(c); + } + if (s.size() >= max_content_identifier_size) { + // We truncate any very long names, since those make for hard to + // format output. They can be somewhat long though, since data segment + // references tend to not occur that often. + break; + } + } + if (s.size() < min_content_identifier_size) { + // It is useful to have a minimum, since if there few printable characters + // in a data section, that is probably a sign of binary, and those few + // characters are not going to be very significant. + continue; + } + // We could do the same disambiguition as RenameToIdentifier and + // GenerateNames do, but if we come up with a clashing name here it is + // likely a sign of not very meaningful binary data, so it is easier to + // just keep the original generated name in that case. + if (bh.count(s) != 0) { + continue; + } + // Remove original entry. + bh.erase(seg->name); + seg->name = s; + bh.emplace(s, Binding(static_cast<Index>(&seg - &segs[0]))); + } +} + +// Function names may contain arbitrary C++ syntax, so we want to +// filter those to look like identifiers. A function name may be set +// by a name section (applied in ReadBinaryIr, called before this function) +// or by an export (applied by GenerateNames, called before this function), +// to both the Func and func_bindings. +// Those names then further perculate down the IR in ApplyNames (called after +// this function). +// To not have to add too many decompiler-specific code into those systems +// (using a callback??) we instead rename everything here. +// Also do data section renaming here. +void RenameAll(Module& module) { + // We also filter common C++ keywords/STL idents that make for huge + // identifiers. + // FIXME: this can obviously give bad results if the input is not C++.. + std::set<string_view> filter = { + { "const" }, + { "std" }, + { "allocator" }, + { "char" }, + { "basic" }, + { "traits" }, + { "wchar" }, + { "t" }, + { "void" }, + { "int" }, + { "unsigned" }, + { "2" }, + { "cxxabiv1" }, + { "short" }, + { "4096ul" }, + }; + RenameToIdentifiers(module.funcs, module.func_bindings, &filter); + // Also do this for some other kinds of names, but without the keyword + // substitution. + RenameToIdentifiers(module.globals, module.global_bindings, nullptr); + RenameToIdentifiers(module.tables, module.table_bindings, nullptr); + RenameToIdentifiers(module.tags, module.tag_bindings, nullptr); + RenameToIdentifiers(module.exports, module.export_bindings, nullptr); + RenameToIdentifiers(module.types, module.type_bindings, nullptr); + RenameToIdentifiers(module.memories, module.memory_bindings, nullptr); + RenameToIdentifiers(module.data_segments, module.data_segment_bindings, + nullptr); + RenameToIdentifiers(module.elem_segments, module.elem_segment_bindings, + nullptr); + // Special purpose naming for data segments. + RenameToContents(module.data_segments, module.data_segment_bindings); +} + +} // namespace wabt + +#endif // WABT_DECOMPILER_NAMING_H_ diff --git a/third_party/wasm2c/src/decompiler.cc b/third_party/wasm2c/src/decompiler.cc new file mode 100644 index 0000000000..25fd75a1bb --- /dev/null +++ b/third_party/wasm2c/src/decompiler.cc @@ -0,0 +1,832 @@ +/* + * 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 "src/decompiler.h" + +#include "src/decompiler-ast.h" +#include "src/decompiler-ls.h" +#include "src/decompiler-naming.h" + +#include "src/stream.h" + +#define WABT_TRACING 0 +#include "src/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, string_view first_indent) { + auto indent = Indent(amount); + for (auto& stat : val.v) { + auto is = (&stat != &val.v[0] || first_indent.empty()) + ? string_view(indent) + : first_indent; + stat.insert(0, is.data(), is.size()); + } + } + + Value WrapChild(Value &child, string_view prefix, 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, 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, string_view prefix, + 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 : 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; + } + } + + string_view VarName(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 = 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); + } + 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, + string_view name) { + // Figure out if this thing is imported, exported, or neither. + auto is_import = mc.module.IsImport(kind, Var(index)); + // 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 = * + 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/decompiler.h b/third_party/wasm2c/src/decompiler.h new file mode 100644 index 0000000000..89bfe78d15 --- /dev/null +++ b/third_party/wasm2c/src/decompiler.h @@ -0,0 +1,36 @@ +/* + * 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_DECOMPILER_H_ +#define WABT_DECOMPILER_H_ + +#include "src/common.h" + +namespace wabt { + +struct Module; +class Stream; + +struct DecompileOptions { +}; + +void RenameAll(Module&); + +std::string Decompile(const Module&, const DecompileOptions&); + +} // namespace wabt + +#endif /* WABT_DECOMPILER_H_ */ diff --git a/third_party/wasm2c/src/emscripten-exported.json b/third_party/wasm2c/src/emscripten-exported.json new file mode 100644 index 0000000000..a35a276ced --- /dev/null +++ b/third_party/wasm2c/src/emscripten-exported.json @@ -0,0 +1,59 @@ +[ +"_free", +"_malloc", +"_wabt_apply_names_module", +"_wabt_bulk_memory_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_format_binary_errors", +"_wabt_format_text_errors", +"_wabt_generate_names_module", +"_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_sat_float_to_int_enabled", +"_wabt_set_bulk_memory_enabled", +"_wabt_set_exceptions_enabled", +"_wabt_set_multi_value_enabled", +"_wabt_set_mutable_globals_enabled", +"_wabt_set_reference_types_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", +"_dummy_workaround_for_emscripten_issue_7073" +] diff --git a/third_party/wasm2c/src/emscripten-helpers.cc b/third_party/wasm2c/src/emscripten-helpers.cc new file mode 100644 index 0000000000..f7c3b4597d --- /dev/null +++ b/third_party/wasm2c/src/emscripten-helpers.cc @@ -0,0 +1,406 @@ +/* + * 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 "src/apply-names.h" +#include "src/binary-reader-ir.h" +#include "src/binary-reader.h" +#include "src/binary-writer-spec.h" +#include "src/binary-writer.h" +#include "src/common.h" +#include "src/error-formatter.h" +#include "src/feature.h" +#include "src/filenames.h" +#include "src/generate-names.h" +#include "src/ir.h" +#include "src/stream.h" +#include "src/validator.h" +#include "src/wast-lexer.h" +#include "src/wast-parser.h" +#include "src/wat-writer.h" + +typedef std::unique_ptr<wabt::OutputBuffer> WabtOutputBufferPtr; +typedef std::pair<std::string, WabtOutputBufferPtr> + WabtFilenameOutputBufferPair; + +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 "src/feature.def" +#undef WABT_FEATURE + +wabt::WastLexer* wabt_new_wast_buffer_lexer(const char* filename, + const void* data, + size_t size) { + std::unique_ptr<wabt::WastLexer> lexer = + wabt::WastLexer::CreateBufferLexer(filename, data, size); + 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) + .to_string(); + + 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; +} + +// See https://github.com/kripken/emscripten/issues/7073. +void dummy_workaround_for_emscripten_issue_7073(void) {} + +} // 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..14c5d92185 --- /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 "src/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.to_string(); + 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/error-formatter.h b/third_party/wasm2c/src/error-formatter.h new file mode 100644 index 0000000000..cd2ed91261 --- /dev/null +++ b/third_party/wasm2c/src/error-formatter.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#ifndef WABT_ERROR_FORMATTER_H_ +#define WABT_ERROR_FORMATTER_H_ + +#include <cstdio> +#include <string> +#include <memory> + +#include "src/color.h" +#include "src/error.h" +#include "src/lexer-source-line-finder.h" + +namespace wabt { + +enum class PrintHeader { + Never, + Once, + Always, +}; + +std::string FormatErrorsToString(const Errors&, + Location::Type, + LexerSourceLineFinder* = nullptr, + const Color& color = Color(nullptr, false), + const std::string& header = {}, + PrintHeader print_header = PrintHeader::Never, + int source_line_max_length = 80); + +void FormatErrorsToFile(const Errors&, + Location::Type, + LexerSourceLineFinder* = nullptr, + FILE* = stderr, + const std::string& header = {}, + PrintHeader print_header = PrintHeader::Never, + int source_line_max_length = 80); + +} // namespace wabt + +#endif // WABT_ERROR_FORMATTER_H_ diff --git a/third_party/wasm2c/src/error.h b/third_party/wasm2c/src/error.h new file mode 100644 index 0000000000..cffca1e5dd --- /dev/null +++ b/third_party/wasm2c/src/error.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#ifndef WABT_ERROR_H_ +#define WABT_ERROR_H_ + +#include <string> +#include <vector> + +#include "src/common.h" +#include "src/string-view.h" + +namespace wabt { + +enum class ErrorLevel { + Warning, + Error, +}; + +static WABT_INLINE const char* GetErrorLevelName(ErrorLevel error_level) { + switch (error_level) { + case ErrorLevel::Warning: + return "warning"; + case ErrorLevel::Error: + return "error"; + } + WABT_UNREACHABLE; +} + +class Error { + public: + Error() : error_level(ErrorLevel::Error) {} + Error(ErrorLevel error_level, Location loc, string_view message) + : error_level(error_level), loc(loc), message(message.to_string()) {} + + ErrorLevel error_level; + Location loc; + std::string message; +}; + +using Errors = std::vector<Error>; + +} // namespace wabt + +#endif // WABT_ERROR_H_ diff --git a/third_party/wasm2c/src/expr-visitor.cc b/third_party/wasm2c/src/expr-visitor.cc new file mode 100644 index 0000000000..b50400c4bd --- /dev/null +++ b/third_party/wasm2c/src/expr-visitor.cc @@ -0,0 +1,469 @@ +/* + * 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 "src/expr-visitor.h" + +#include "src/cast.h" +#include "src/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::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/expr-visitor.h b/third_party/wasm2c/src/expr-visitor.h new file mode 100644 index 0000000000..ab7dc9e418 --- /dev/null +++ b/third_party/wasm2c/src/expr-visitor.h @@ -0,0 +1,220 @@ +/* + * 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. + */ + +#ifndef WABT_EXPR_VISITOR_H_ +#define WABT_EXPR_VISITOR_H_ + +#include "src/common.h" +#include "src/ir.h" + +namespace wabt { + +class ExprVisitor { + public: + class Delegate; + class DelegateNop; + + explicit ExprVisitor(Delegate* delegate); + + Result VisitExpr(Expr*); + Result VisitExprList(ExprList&); + Result VisitFunc(Func*); + + private: + enum class State { + Default, + Block, + IfTrue, + IfFalse, + Loop, + Try, + Catch, + }; + + Result HandleDefaultState(Expr*); + void PushDefault(Expr*); + void PopDefault(); + void PushExprlist(State state, Expr*, ExprList&); + void PopExprlist(); + void PushCatch(Expr*, Index catch_index, ExprList&); + void PopCatch(); + + Delegate* delegate_; + + // Use parallel arrays instead of array of structs so we can avoid allocating + // unneeded objects. ExprList::iterator has no default constructor, so it + // must only be allocated for states that use it. + std::vector<State> state_stack_; + std::vector<Expr*> expr_stack_; + std::vector<ExprList::iterator> expr_iter_stack_; + std::vector<Index> catch_index_stack_; +}; + +class ExprVisitor::Delegate { + public: + virtual ~Delegate() {} + + virtual Result OnBinaryExpr(BinaryExpr*) = 0; + virtual Result BeginBlockExpr(BlockExpr*) = 0; + virtual Result EndBlockExpr(BlockExpr*) = 0; + virtual Result OnBrExpr(BrExpr*) = 0; + virtual Result OnBrIfExpr(BrIfExpr*) = 0; + virtual Result OnBrTableExpr(BrTableExpr*) = 0; + virtual Result OnCallExpr(CallExpr*) = 0; + virtual Result OnCallIndirectExpr(CallIndirectExpr*) = 0; + virtual Result OnCallRefExpr(CallRefExpr*) = 0; + virtual Result OnCompareExpr(CompareExpr*) = 0; + virtual Result OnConstExpr(ConstExpr*) = 0; + virtual Result OnConvertExpr(ConvertExpr*) = 0; + virtual Result OnDropExpr(DropExpr*) = 0; + virtual Result OnGlobalGetExpr(GlobalGetExpr*) = 0; + virtual Result OnGlobalSetExpr(GlobalSetExpr*) = 0; + virtual Result BeginIfExpr(IfExpr*) = 0; + virtual Result AfterIfTrueExpr(IfExpr*) = 0; + virtual Result EndIfExpr(IfExpr*) = 0; + virtual Result OnLoadExpr(LoadExpr*) = 0; + virtual Result OnLocalGetExpr(LocalGetExpr*) = 0; + virtual Result OnLocalSetExpr(LocalSetExpr*) = 0; + virtual Result OnLocalTeeExpr(LocalTeeExpr*) = 0; + virtual Result BeginLoopExpr(LoopExpr*) = 0; + virtual Result EndLoopExpr(LoopExpr*) = 0; + virtual Result OnMemoryCopyExpr(MemoryCopyExpr*) = 0; + virtual Result OnDataDropExpr(DataDropExpr*) = 0; + virtual Result OnMemoryFillExpr(MemoryFillExpr*) = 0; + virtual Result OnMemoryGrowExpr(MemoryGrowExpr*) = 0; + virtual Result OnMemoryInitExpr(MemoryInitExpr*) = 0; + virtual Result OnMemorySizeExpr(MemorySizeExpr*) = 0; + virtual Result OnTableCopyExpr(TableCopyExpr*) = 0; + virtual Result OnElemDropExpr(ElemDropExpr*) = 0; + virtual Result OnTableInitExpr(TableInitExpr*) = 0; + virtual Result OnTableGetExpr(TableGetExpr*) = 0; + virtual Result OnTableSetExpr(TableSetExpr*) = 0; + virtual Result OnTableGrowExpr(TableGrowExpr*) = 0; + virtual Result OnTableSizeExpr(TableSizeExpr*) = 0; + virtual Result OnTableFillExpr(TableFillExpr*) = 0; + virtual Result OnRefFuncExpr(RefFuncExpr*) = 0; + virtual Result OnRefNullExpr(RefNullExpr*) = 0; + virtual Result OnRefIsNullExpr(RefIsNullExpr*) = 0; + virtual Result OnNopExpr(NopExpr*) = 0; + virtual Result OnReturnExpr(ReturnExpr*) = 0; + virtual Result OnReturnCallExpr(ReturnCallExpr*) = 0; + virtual Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr*) = 0; + virtual Result OnSelectExpr(SelectExpr*) = 0; + virtual Result OnStoreExpr(StoreExpr*) = 0; + virtual Result OnUnaryExpr(UnaryExpr*) = 0; + virtual Result OnUnreachableExpr(UnreachableExpr*) = 0; + virtual Result BeginTryExpr(TryExpr*) = 0; + virtual Result OnCatchExpr(TryExpr*, Catch*) = 0; + virtual Result OnDelegateExpr(TryExpr*) = 0; + virtual Result EndTryExpr(TryExpr*) = 0; + virtual Result OnThrowExpr(ThrowExpr*) = 0; + virtual Result OnRethrowExpr(RethrowExpr*) = 0; + virtual Result OnAtomicWaitExpr(AtomicWaitExpr*) = 0; + virtual Result OnAtomicFenceExpr(AtomicFenceExpr*) = 0; + virtual Result OnAtomicNotifyExpr(AtomicNotifyExpr*) = 0; + virtual Result OnAtomicLoadExpr(AtomicLoadExpr*) = 0; + virtual Result OnAtomicStoreExpr(AtomicStoreExpr*) = 0; + virtual Result OnAtomicRmwExpr(AtomicRmwExpr*) = 0; + virtual Result OnAtomicRmwCmpxchgExpr(AtomicRmwCmpxchgExpr*) = 0; + virtual Result OnTernaryExpr(TernaryExpr*) = 0; + virtual Result OnSimdLaneOpExpr(SimdLaneOpExpr*) = 0; + virtual Result OnSimdLoadLaneExpr(SimdLoadLaneExpr*) = 0; + virtual Result OnSimdStoreLaneExpr(SimdStoreLaneExpr*) = 0; + virtual Result OnSimdShuffleOpExpr(SimdShuffleOpExpr*) = 0; + virtual Result OnLoadSplatExpr(LoadSplatExpr*) = 0; + virtual Result OnLoadZeroExpr(LoadZeroExpr*) = 0; +}; + +class ExprVisitor::DelegateNop : public ExprVisitor::Delegate { + public: + Result OnBinaryExpr(BinaryExpr*) override { return Result::Ok; } + Result BeginBlockExpr(BlockExpr*) override { return Result::Ok; } + Result EndBlockExpr(BlockExpr*) override { return Result::Ok; } + Result OnBrExpr(BrExpr*) override { return Result::Ok; } + Result OnBrIfExpr(BrIfExpr*) override { return Result::Ok; } + Result OnBrTableExpr(BrTableExpr*) override { return Result::Ok; } + Result OnCallExpr(CallExpr*) override { return Result::Ok; } + Result OnCallIndirectExpr(CallIndirectExpr*) override { return Result::Ok; } + Result OnCallRefExpr(CallRefExpr*) override { return Result::Ok; } + Result OnCompareExpr(CompareExpr*) override { return Result::Ok; } + Result OnConstExpr(ConstExpr*) override { return Result::Ok; } + Result OnConvertExpr(ConvertExpr*) override { return Result::Ok; } + Result OnDropExpr(DropExpr*) override { return Result::Ok; } + Result OnGlobalGetExpr(GlobalGetExpr*) override { return Result::Ok; } + Result OnGlobalSetExpr(GlobalSetExpr*) override { return Result::Ok; } + Result BeginIfExpr(IfExpr*) override { return Result::Ok; } + Result AfterIfTrueExpr(IfExpr*) override { return Result::Ok; } + Result EndIfExpr(IfExpr*) override { return Result::Ok; } + Result OnLoadExpr(LoadExpr*) override { return Result::Ok; } + Result OnLocalGetExpr(LocalGetExpr*) override { return Result::Ok; } + Result OnLocalSetExpr(LocalSetExpr*) override { return Result::Ok; } + Result OnLocalTeeExpr(LocalTeeExpr*) override { return Result::Ok; } + Result BeginLoopExpr(LoopExpr*) override { return Result::Ok; } + Result EndLoopExpr(LoopExpr*) override { return Result::Ok; } + Result OnMemoryCopyExpr(MemoryCopyExpr*) override { return Result::Ok; } + Result OnDataDropExpr(DataDropExpr*) override { return Result::Ok; } + Result OnMemoryFillExpr(MemoryFillExpr*) override { return Result::Ok; } + Result OnMemoryGrowExpr(MemoryGrowExpr*) override { return Result::Ok; } + Result OnMemoryInitExpr(MemoryInitExpr*) override { return Result::Ok; } + Result OnMemorySizeExpr(MemorySizeExpr*) override { return Result::Ok; } + Result OnTableCopyExpr(TableCopyExpr*) override { return Result::Ok; } + Result OnElemDropExpr(ElemDropExpr*) override { return Result::Ok; } + Result OnTableInitExpr(TableInitExpr*) override { return Result::Ok; } + Result OnTableGetExpr(TableGetExpr*) override { return Result::Ok; } + Result OnTableSetExpr(TableSetExpr*) override { return Result::Ok; } + Result OnTableGrowExpr(TableGrowExpr*) override { return Result::Ok; } + Result OnTableSizeExpr(TableSizeExpr*) override { return Result::Ok; } + Result OnTableFillExpr(TableFillExpr*) override { return Result::Ok; } + Result OnRefFuncExpr(RefFuncExpr*) override { return Result::Ok; } + Result OnRefNullExpr(RefNullExpr*) override { return Result::Ok; } + Result OnRefIsNullExpr(RefIsNullExpr*) override { return Result::Ok; } + Result OnNopExpr(NopExpr*) override { return Result::Ok; } + Result OnReturnExpr(ReturnExpr*) override { return Result::Ok; } + Result OnReturnCallExpr(ReturnCallExpr*) override { return Result::Ok; } + Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr*) override { + return Result::Ok; + } + Result OnSelectExpr(SelectExpr*) override { return Result::Ok; } + Result OnStoreExpr(StoreExpr*) override { return Result::Ok; } + Result OnUnaryExpr(UnaryExpr*) override { return Result::Ok; } + Result OnUnreachableExpr(UnreachableExpr*) override { return Result::Ok; } + Result BeginTryExpr(TryExpr*) override { return Result::Ok; } + Result OnCatchExpr(TryExpr*, Catch*) override { return Result::Ok; } + Result OnDelegateExpr(TryExpr*) override { return Result::Ok; } + Result EndTryExpr(TryExpr*) override { return Result::Ok; } + Result OnThrowExpr(ThrowExpr*) override { return Result::Ok; } + Result OnRethrowExpr(RethrowExpr*) override { return Result::Ok; } + Result OnAtomicWaitExpr(AtomicWaitExpr*) override { return Result::Ok; } + Result OnAtomicFenceExpr(AtomicFenceExpr*) override { return Result::Ok; } + Result OnAtomicNotifyExpr(AtomicNotifyExpr*) override { return Result::Ok; } + Result OnAtomicLoadExpr(AtomicLoadExpr*) override { return Result::Ok; } + Result OnAtomicStoreExpr(AtomicStoreExpr*) override { return Result::Ok; } + Result OnAtomicRmwExpr(AtomicRmwExpr*) override { return Result::Ok; } + Result OnAtomicRmwCmpxchgExpr(AtomicRmwCmpxchgExpr*) override { + return Result::Ok; + } + Result OnTernaryExpr(TernaryExpr*) override { return Result::Ok; } + Result OnSimdLaneOpExpr(SimdLaneOpExpr*) override { return Result::Ok; } + Result OnSimdLoadLaneExpr(SimdLoadLaneExpr*) override { return Result::Ok; } + Result OnSimdStoreLaneExpr(SimdStoreLaneExpr*) override { return Result::Ok; } + Result OnSimdShuffleOpExpr(SimdShuffleOpExpr*) override { return Result::Ok; } + Result OnLoadSplatExpr(LoadSplatExpr*) override { return Result::Ok; } + Result OnLoadZeroExpr(LoadZeroExpr*) override { return Result::Ok; } +}; + +} // namespace wabt + +#endif // WABT_EXPR_VISITOR_H_ diff --git a/third_party/wasm2c/src/feature.cc b/third_party/wasm2c/src/feature.cc new file mode 100644 index 0000000000..9d7878e18b --- /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 "src/feature.h" + +#include "src/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 "src/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 (reference_types_enabled_) { + bulk_memory_enabled_ = true; + } +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/feature.def b/third_party/wasm2c/src/feature.def new file mode 100644 index 0000000000..f6f90f614d --- /dev/null +++ b/third_party/wasm2c/src/feature.def @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#ifndef WABT_FEATURE +#error "You must define WABT_FEATURE before including this file." +#endif + +/* + * variable flag default help + * ========================================================================= */ + +WABT_FEATURE(exceptions, "exceptions", false, "Experimental exception handling") +WABT_FEATURE(mutable_globals, "mutable-globals", true, "Import/export mutable globals") +WABT_FEATURE(sat_float_to_int, "saturating-float-to-int", true, "Saturating float-to-int operators") +WABT_FEATURE(sign_extension, "sign-extension", true, "Sign-extension operators") +WABT_FEATURE(simd, "simd", true, "SIMD support") +WABT_FEATURE(threads, "threads", false, "Threading support") +WABT_FEATURE(function_references, "function-references", false, "Typed function references") +WABT_FEATURE(multi_value, "multi-value", true, "Multi-value") +WABT_FEATURE(tail_call, "tail-call", false, "Tail-call support") +WABT_FEATURE(bulk_memory, "bulk-memory", false, "Bulk-memory operations") +WABT_FEATURE(reference_types, "reference-types", false, "Reference types (externref)") +WABT_FEATURE(annotations, "annotations", false, "Custom annotation syntax") +WABT_FEATURE(gc, "gc", false, "Garbage collection") +WABT_FEATURE(memory64, "memory64", false, "64-bit memory") diff --git a/third_party/wasm2c/src/feature.h b/third_party/wasm2c/src/feature.h new file mode 100644 index 0000000000..7ebc9ef85b --- /dev/null +++ b/third_party/wasm2c/src/feature.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#ifndef WABT_FEATURE_H_ +#define WABT_FEATURE_H_ + +#include "src/common.h" + +namespace wabt { + +class OptionParser; + +class Features { + public: + void AddOptions(OptionParser*); + + void EnableAll() { +#define WABT_FEATURE(variable, flag, default_, help) enable_##variable(); +#include "src/feature.def" +#undef WABT_FEATURE + } + +#define WABT_FEATURE(variable, flag, default_, help) \ + bool variable##_enabled() const { return variable##_enabled_; } \ + void enable_##variable() { set_##variable##_enabled(true); } \ + void disable_##variable() { set_##variable##_enabled(false); } \ + void set_##variable##_enabled(bool value) { \ + variable##_enabled_ = value; \ + UpdateDependencies(); \ + } +#include "src/feature.def" +#undef WABT_FEATURE + + private: + void UpdateDependencies(); + +#define WABT_FEATURE(variable, flag, default_, help) \ + bool variable##_enabled_ = default_; +#include "src/feature.def" +#undef WABT_FEATURE +}; + +} // namespace wabt + +#endif // WABT_FEATURE_H_ diff --git a/third_party/wasm2c/src/filenames.cc b/third_party/wasm2c/src/filenames.cc new file mode 100644 index 0000000000..989453c7dd --- /dev/null +++ b/third_party/wasm2c/src/filenames.cc @@ -0,0 +1,56 @@ +/* + * 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 "src/filenames.h" + +namespace wabt { + +const char* kWasmExtension = ".wasm"; + +const char* kWatExtension = ".wat"; + +string_view StripExtension(string_view filename) { + return filename.substr(0, filename.find_last_of('.')); +} + +string_view GetBasename(string_view filename) { + size_t last_slash = filename.find_last_of('/'); + size_t last_backslash = filename.find_last_of('\\'); + if (last_slash == string_view::npos && last_backslash == string_view::npos) { + return filename; + } + + if (last_slash == string_view::npos) { + if (last_backslash == string_view::npos) { + return filename; + } + last_slash = last_backslash; + } else if (last_backslash != string_view::npos) { + last_slash = std::max(last_slash, last_backslash); + } + + return filename.substr(last_slash + 1); +} + +string_view GetExtension(string_view filename) { + size_t pos = filename.find_last_of('.'); + if (pos == string_view::npos) { + return ""; + } + return filename.substr(pos); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/filenames.h b/third_party/wasm2c/src/filenames.h new file mode 100644 index 0000000000..87529aa29c --- /dev/null +++ b/third_party/wasm2c/src/filenames.h @@ -0,0 +1,51 @@ +/* + * 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_FILENAMES_H_ +#define WABT_FILENAMES_H_ + +#include "src/common.h" + +namespace wabt { + +extern const char* kWasmExtension; +extern const char* kWatExtension; + +// Return only the file extension, e.g.: +// +// "foo.txt", => ".txt" +// "foo" => "" +// "/foo/bar/foo.wasm" => ".wasm" +string_view GetExtension(string_view filename); + +// Strip extension, e.g.: +// +// "foo", => "foo" +// "foo.bar" => "foo" +// "/path/to/foo.bar" => "/path/to/foo" +// "\\path\\to\\foo.bar" => "\\path\\to\\foo" +string_view StripExtension(string_view s); + +// Strip everything up to and including the last slash, e.g.: +// +// "/foo/bar/baz", => "baz" +// "/usr/local/include/stdio.h", => "stdio.h" +// "foo.bar", => "foo.bar" +string_view GetBasename(string_view filename); + +} // namespace wabt + +#endif /* WABT_FILENAMES_H_ */ diff --git a/third_party/wasm2c/src/generate-names.cc b/third_party/wasm2c/src/generate-names.cc new file mode 100644 index 0000000000..5f99f0e84c --- /dev/null +++ b/third_party/wasm2c/src/generate-names.cc @@ -0,0 +1,430 @@ +/* + * 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 "src/generate-names.h" + +#include <cassert> +#include <cstdio> +#include <string> +#include <vector> + +#include "src/cast.h" +#include "src/expr-visitor.h" +#include "src/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 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::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/generate-names.h b/third_party/wasm2c/src/generate-names.h new file mode 100644 index 0000000000..9cd926e462 --- /dev/null +++ b/third_party/wasm2c/src/generate-names.h @@ -0,0 +1,46 @@ +/* + * 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_GENERATE_NAMES_H_ +#define WABT_GENERATE_NAMES_H_ + +#include "src/common.h" + +namespace wabt { + +struct Module; + +enum NameOpts { + None = 0, + AlphaNames = 1 << 0, +}; + +Result GenerateNames(struct Module*, NameOpts opts = NameOpts::None); + +inline std::string IndexToAlphaName(Index index) { + std::string s; + do { + // For multiple chars, put most frequently changing char first. + s += 'a' + (index % 26); + index /= 26; + // Continue remaining sequence with 'a' rather than 'b'. + } while (index--); + return s; +} + +} // namespace wabt + +#endif /* WABT_GENERATE_NAMES_H_ */ diff --git a/third_party/wasm2c/src/hash-util.cc b/third_party/wasm2c/src/hash-util.cc new file mode 100644 index 0000000000..efb8427028 --- /dev/null +++ b/third_party/wasm2c/src/hash-util.cc @@ -0,0 +1,38 @@ +/* + * 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 "src/hash-util.h" + +#include "config.h" + +namespace wabt { + +// Hash combiner from: +// http://stackoverflow.com/questions/4948780/magic-number-in-boosthash-combine + +hash_code HashCombine(hash_code seed, hash_code y) { +#if SIZEOF_SIZE_T == 4 + constexpr hash_code magic = 0x9e3779b9; +#elif SIZEOF_SIZE_T == 8 + constexpr hash_code magic = 0x9e3779b97f4a7c16; +#else +#error "weird sizeof size_t" +#endif + seed ^= y + magic + (seed << 6) + (seed >> 2); + return seed; +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/hash-util.h b/third_party/wasm2c/src/hash-util.h new file mode 100644 index 0000000000..0a135d5e68 --- /dev/null +++ b/third_party/wasm2c/src/hash-util.h @@ -0,0 +1,51 @@ +/* + * 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. + */ + +#ifndef WABT_HASH_UTIL_H_ +#define WABT_HASH_UTIL_H_ + +#include <cstdlib> +#include <functional> + +namespace wabt { + +typedef std::size_t hash_code; + +inline hash_code HashCombine() { + return 0; +} +inline hash_code HashCombine(hash_code seed) { + return seed; +} +hash_code HashCombine(hash_code x, hash_code y); + +template <typename T, typename... U> +inline hash_code HashCombine(const T& first, const U&... rest) { + return HashCombine(HashCombine(rest...), std::hash<T>()(first)); +} + +template <typename It> +inline hash_code HashRange(It first, It last) { + hash_code result = 0; + for (auto iter = first; iter != last; ++iter) { + result = HashCombine(result, *iter); + } + return result; +} + +} // namespace wabt + +#endif // WABT_HASH_UTIL_H_ diff --git a/third_party/wasm2c/src/intrusive-list.h b/third_party/wasm2c/src/intrusive-list.h new file mode 100644 index 0000000000..7d9611a74a --- /dev/null +++ b/third_party/wasm2c/src/intrusive-list.h @@ -0,0 +1,633 @@ +/* + * 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. + */ + +#ifndef WABT_INTRUSIVE_LIST_H_ +#define WABT_INTRUSIVE_LIST_H_ + +#include <cassert> +#include <iterator> +#include <memory> + +#include "src/make-unique.h" + +// This uses a similar interface as std::list, but is missing the following +// features: +// +// * Add "extract_" functions that remove an element from the list and return +// it. +// * Only supports move-only operations +// * No allocator support +// * No initializer lists +// * Asserts instead of exceptions +// * Some functions are not implemented (merge, remove, remove_if, reverse, +// unique, sort, non-member comparison operators) + +namespace wabt { + +template <typename T> +class intrusive_list; + +template <typename T> +class intrusive_list_base { + private: + friend class intrusive_list<T>; + + mutable T* next_ = nullptr; + mutable T* prev_ = nullptr; +}; + +template <typename T> +class intrusive_list { + public: + // types: + typedef T value_type; + typedef value_type& reference; + typedef const value_type& const_reference; + class iterator; + class const_iterator; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef std::reverse_iterator<iterator> reverse_iterator; + typedef std::reverse_iterator<const_iterator> const_reverse_iterator; + + // construct/copy/destroy: + intrusive_list(); + explicit intrusive_list(std::unique_ptr<T> node); + explicit intrusive_list(T&& node); + intrusive_list(const intrusive_list&) = delete; + intrusive_list(intrusive_list&&); + ~intrusive_list(); + intrusive_list& operator=(const intrusive_list& other) = delete; + intrusive_list& operator=(intrusive_list&& other); + + // iterators: + iterator begin() noexcept; + const_iterator begin() const noexcept; + iterator end() noexcept; + const_iterator end() const noexcept; + + reverse_iterator rbegin() noexcept; + const_reverse_iterator rbegin() const noexcept; + reverse_iterator rend() noexcept; + const_reverse_iterator rend() const noexcept; + + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + const_reverse_iterator crbegin() const noexcept; + const_reverse_iterator crend() const noexcept; + + // capacity: + size_type size() const noexcept; + bool empty() const noexcept; + + // element access: + reference front(); + const_reference front() const; + reference back(); + const_reference back() const; + + // modifiers: + template <class... Args> + void emplace_front(Args&&... args); + template <class... Args> + void emplace_back(Args&&... args); + void push_front(std::unique_ptr<T> node); + void push_front(T&& node); + void push_back(std::unique_ptr<T> node); + void push_back(T&& node); + void pop_front(); + void pop_back(); + std::unique_ptr<T> extract_front(); + std::unique_ptr<T> extract_back(); + + template <class... Args> + iterator emplace(iterator pos, Args&&... args); + iterator insert(iterator pos, std::unique_ptr<T> node); + iterator insert(iterator pos, T&& node); + std::unique_ptr<T> extract(iterator it); + + iterator erase(iterator pos); + iterator erase(iterator first, iterator last); + void swap(intrusive_list&); + void clear() noexcept; + + void splice(iterator pos, intrusive_list& node); + void splice(iterator pos, intrusive_list&& node); + void splice(iterator pos, intrusive_list& node, iterator it); + void splice(iterator pos, + intrusive_list& node, + iterator first, + iterator last); + + private: + T* first_ = nullptr; + T* last_ = nullptr; + size_t size_ = 0; +}; + +/// iterator +template <typename T> +class intrusive_list<T>::iterator { + public: + typedef std::ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + typedef T value_type; + typedef T* pointer; + typedef T& reference; + + iterator(const intrusive_list<T>& list, T* node) + : list_(&list), node_(node) {} + + reference operator*() const { + assert(node_); + return *node_; + } + + pointer operator->() const { + assert(node_); + return node_; + } + + iterator& operator++() { + assert(node_); + node_ = node_->next_; + return *this; + } + + iterator operator++(int) { + iterator tmp = *this; + operator++(); + return tmp; + } + + iterator& operator--() { + node_ = node_ ? node_->prev_ : list_->last_; + return *this; + } + + iterator operator--(int) { + iterator tmp = *this; + operator--(); + return tmp; + } + + bool operator==(iterator rhs) const { + assert(list_ == rhs.list_); + return node_ == rhs.node_; + } + + bool operator!=(iterator rhs) const { + assert(list_ == rhs.list_); + return node_ != rhs.node_; + } + + private: + friend class const_iterator; + + const intrusive_list<T>* list_; + T* node_; +}; + +/// const_iterator +template <typename T> +class intrusive_list<T>::const_iterator { + public: + typedef std::ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + typedef T value_type; + typedef const T* pointer; + typedef const T& reference; + + const_iterator(const intrusive_list<T>& list, T* node) + : list_(&list), node_(node) {} + + const_iterator(const iterator& other) + : list_(other.list_), node_(other.node_) {} + + reference operator*() const { + assert(node_); + return *node_; + } + + pointer operator->() const { + assert(node_); + return node_; + } + + const_iterator& operator++() { + assert(node_); + node_ = node_->next_; + return *this; + } + + const_iterator operator++(int) { + const_iterator tmp = *this; + operator++(); + return tmp; + } + + const_iterator& operator--() { + node_ = node_ ? node_->prev_ : list_->last_; + return *this; + } + + const_iterator operator--(int) { + const_iterator tmp = *this; + operator--(); + return tmp; + } + + bool operator==(const_iterator rhs) const { + assert(list_ == rhs.list_); + return node_ == rhs.node_; + } + + bool operator!=(const_iterator rhs) const { + assert(list_ == rhs.list_); + return node_ != rhs.node_; + } + + private: + const intrusive_list<T>* list_; + T* node_; +}; + +template <typename T> +inline intrusive_list<T>::intrusive_list() {} + +template <typename T> +inline intrusive_list<T>::intrusive_list(std::unique_ptr<T> node) { + push_back(std::move(node)); +} + +template <typename T> +inline intrusive_list<T>::intrusive_list(T&& node) { + push_back(std::move(node)); +} + +template <typename T> +inline intrusive_list<T>::intrusive_list(intrusive_list&& other) + : first_(other.first_), last_(other.last_), size_(other.size_) { + other.first_ = other.last_ = nullptr; + other.size_ = 0; +} + +template <typename T> +inline intrusive_list<T>::~intrusive_list() { + clear(); +} + +template <typename T> +inline intrusive_list<T>& intrusive_list<T>::operator=( + intrusive_list<T>&& other) { + clear(); + first_ = other.first_; + last_ = other.last_; + size_ = other.size_; + other.first_ = other.last_ = nullptr; + other.size_ = 0; + return *this; +} + +template <typename T> +inline typename intrusive_list<T>::iterator +intrusive_list<T>::begin() noexcept { + return iterator(*this, first_); +} + +template <typename T> +inline typename intrusive_list<T>::const_iterator intrusive_list<T>::begin() + const noexcept { + return const_iterator(*this, first_); +} + +template <typename T> +inline typename intrusive_list<T>::iterator intrusive_list<T>::end() noexcept { + return iterator(*this, nullptr); +} + +template <typename T> +inline typename intrusive_list<T>::const_iterator intrusive_list<T>::end() const + noexcept { + return const_iterator(*this, nullptr); +} + +template <typename T> +inline typename intrusive_list<T>::reverse_iterator +intrusive_list<T>::rbegin() noexcept { + return reverse_iterator(iterator(*this, nullptr)); +} + +template <typename T> +inline typename intrusive_list<T>::const_reverse_iterator +intrusive_list<T>::rbegin() const noexcept { + return const_reverse_iterator(const_iterator(*this, nullptr)); +} + +template <typename T> +inline typename intrusive_list<T>::reverse_iterator +intrusive_list<T>::rend() noexcept { + return reverse_iterator(iterator(*this, first_)); +} + +template <typename T> +inline typename intrusive_list<T>::const_reverse_iterator +intrusive_list<T>::rend() const noexcept { + return const_reverse_iterator(const_iterator(*this, first_)); +} + +template <typename T> +inline typename intrusive_list<T>::const_iterator intrusive_list<T>::cbegin() + const noexcept { + return const_iterator(*this, first_); +} + +template <typename T> +inline typename intrusive_list<T>::const_iterator intrusive_list<T>::cend() + const noexcept { + return const_iterator(*this, nullptr); +} + +template <typename T> +inline typename intrusive_list<T>::const_reverse_iterator +intrusive_list<T>::crbegin() const noexcept { + return const_reverse_iterator(const_iterator(*this, nullptr)); +} + +template <typename T> +inline typename intrusive_list<T>::const_reverse_iterator +intrusive_list<T>::crend() const noexcept { + return const_reverse_iterator(const_iterator(*this, first_)); +} + +template <typename T> +inline typename intrusive_list<T>::size_type intrusive_list<T>::size() const + noexcept { + return size_; +} + +template <typename T> +inline bool intrusive_list<T>::empty() const noexcept { + return size_ == 0; +} + +template <typename T> +inline typename intrusive_list<T>::reference intrusive_list<T>::front() { + assert(!empty()); + return *first_; +} + +template <typename T> +inline typename intrusive_list<T>::const_reference intrusive_list<T>::front() + const { + assert(!empty()); + return *first_; +} + +template <typename T> +inline typename intrusive_list<T>::reference intrusive_list<T>::back() { + assert(!empty()); + return *last_; +} + +template <typename T> +inline typename intrusive_list<T>::const_reference intrusive_list<T>::back() + const { + assert(!empty()); + return *last_; +} + +template <typename T> +template <class... Args> +inline void intrusive_list<T>::emplace_front(Args&&... args) { + push_front(MakeUnique<T>(std::forward<Args>(args)...)); +} + +template <typename T> +template <class... Args> +inline void intrusive_list<T>::emplace_back(Args&&... args) { + push_back(MakeUnique<T>(std::forward<Args>(args)...)); +} + +template <typename T> +inline void intrusive_list<T>::push_front(std::unique_ptr<T> node) { + assert(node->prev_ == nullptr && node->next_ == nullptr); + + T* node_p = node.release(); + if (first_) { + node_p->next_ = first_; + first_->prev_ = node_p; + } else { + last_ = node_p; + } + first_ = node_p; + size_++; +} + +template <typename T> +inline void intrusive_list<T>::push_front(T&& node) { + push_front(MakeUnique<T>(std::move(node))); +} + +template <typename T> +inline void intrusive_list<T>::push_back(std::unique_ptr<T> node) { + assert(node->prev_ == nullptr && node->next_ == nullptr); + + T* node_p = node.release(); + if (last_) { + node_p->prev_ = last_; + last_->next_ = node_p; + } else { + first_ = node_p; + } + last_ = node_p; + size_++; +} + +template <typename T> +inline void intrusive_list<T>::push_back(T&& node) { + push_back(MakeUnique<T>(std::move(node))); +} + +template <typename T> +inline void intrusive_list<T>::pop_front() { + extract_front(); +} + +template <typename T> +inline void intrusive_list<T>::pop_back() { + extract_back(); +} + +template <typename T> +inline std::unique_ptr<T> intrusive_list<T>::extract_front() { + assert(!empty()); + T* node = first_; + if (first_ == last_) { + first_ = last_ = nullptr; + } else { + first_ = first_->next_; + first_->prev_ = nullptr; + } + node->next_ = node->prev_ = nullptr; + size_--; + return std::unique_ptr<T>(node); +} + +template <typename T> +inline std::unique_ptr<T> intrusive_list<T>::extract_back() { + assert(!empty()); + T* node = last_; + if (first_ == last_) { + first_ = last_ = nullptr; + } else { + last_ = last_->prev_; + last_->next_ = nullptr; + } + node->next_ = node->prev_ = nullptr; + size_--; + return std::unique_ptr<T>(node); +} + +template <typename T> +template <class... Args> +inline typename intrusive_list<T>::iterator intrusive_list<T>::emplace( + iterator pos, + Args&&... args) { + return insert(pos, MakeUnique<T>(std::forward<Args>(args)...)); +} + +template <typename T> +inline typename intrusive_list<T>::iterator intrusive_list<T>::insert( + iterator pos, + std::unique_ptr<T> node) { + assert(node->prev_ == nullptr && node->next_ == nullptr); + + T* node_p; + if (pos == end()) { + push_back(std::move(node)); + node_p = &back(); + } else { + node_p = node.release(); + node_p->prev_ = pos->prev_; + node_p->next_ = &*pos; + if (pos->prev_) { + pos->prev_->next_ = node_p; + } else { + first_ = node_p; + } + pos->prev_ = node_p; + size_++; + } + return iterator(*this, node_p); +} + +template <typename T> +inline typename intrusive_list<T>::iterator intrusive_list<T>::insert( + iterator pos, + T&& node) { + return insert(pos, MakeUnique<T>(std::move(node))); +} + +template <typename T> +inline std::unique_ptr<T> intrusive_list<T>::extract(iterator pos) { + assert(!empty()); + assert(pos != end()); + T* node = &*pos; + if (first_ == last_) { + first_ = last_ = nullptr; + } else { + if (node->prev_) { + node->prev_->next_ = node->next_; + } else { + first_ = node->next_; + } + + if (node->next_) { + node->next_->prev_ = node->prev_; + } else { + last_ = node->prev_; + } + } + node->next_ = node->prev_ = nullptr; + size_--; + return std::unique_ptr<T>(node); +} + +template <typename T> +inline typename intrusive_list<T>::iterator intrusive_list<T>::erase( + iterator pos) { + iterator next = std::next(pos); + extract(pos); + return next; +} + +template <typename T> +inline typename intrusive_list<T>::iterator intrusive_list<T>::erase( + iterator first, + iterator last) { + while (first != last) + first = erase(first); + return first; +} + +template <typename T> +inline void intrusive_list<T>::swap(intrusive_list& other) { + std::swap(first_, other.first_); + std::swap(last_, other.last_); + std::swap(size_, other.size_); +} + +template <typename T> +inline void intrusive_list<T>::clear() noexcept { + for (T* iter = first_; iter;) { + T* next = iter->next_; + delete iter; + iter = next; + } + first_ = last_ = nullptr; + size_ = 0; +} + +template <typename T> +inline void intrusive_list<T>::splice(iterator pos, intrusive_list& other) { + splice(pos, other, other.begin(), other.end()); +} + +template <typename T> +inline void intrusive_list<T>::splice(iterator pos, intrusive_list&& other) { + splice(pos, other, other.begin(), other.end()); +} + +template <typename T> +inline void intrusive_list<T>::splice(iterator pos, + intrusive_list& other, + iterator it) { + insert(pos, other.extract(it)); +} + +template <typename T> +inline void intrusive_list<T>::splice(iterator pos, + intrusive_list& other, + iterator first, + iterator last) { + while (first != last) + insert(pos, other.extract(first++)); +} + +} // namespace wabt + +#endif // WABT_INTRUSIVE_LIST_H_ diff --git a/third_party/wasm2c/src/ir-util.cc b/third_party/wasm2c/src/ir-util.cc new file mode 100644 index 0000000000..9b66461188 --- /dev/null +++ b/third_party/wasm2c/src/ir-util.cc @@ -0,0 +1,272 @@ +/* + * 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 "src/ir-util.h" + +#include <algorithm> +#include <array> +#include <cassert> +#include <cinttypes> +#include <cstdarg> +#include <cstdio> +#include <iterator> +#include <map> +#include <string> +#include <vector> + +#include "src/cast.h" +#include "src/common.h" +#include "src/expr-visitor.h" +#include "src/ir.h" +#include "src/ir-util.h" +#include "src/literal.h" +#include "src/stream.h" + +#define WABT_TRACING 0 +#include "src/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: + 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-util.h b/third_party/wasm2c/src/ir-util.h new file mode 100644 index 0000000000..226e81aad9 --- /dev/null +++ b/third_party/wasm2c/src/ir-util.h @@ -0,0 +1,76 @@ +/* + * 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_IR_UTIL_H_ +#define WABT_IR_UTIL_H_ + +#include "src/common.h" +#include "src/ir.h" + +namespace wabt { + +struct Label { + Label(LabelType label_type, + const std::string& name, + const TypeVector& param_types, + const TypeVector& result_types) + : name(name), + label_type(label_type), + param_types(param_types), + result_types(result_types) {} + + std::string name; + LabelType label_type; + TypeVector param_types; + TypeVector result_types; +}; + +struct ModuleContext { + ModuleContext(const Module &module) : module(module) {} + + Index GetLabelStackSize() const { return label_stack_.size(); } + const Label* GetLabel(const Var& var) const; + Index GetLabelArity(const Var& var) const; + void SetTopLabelType(LabelType label_type) { + label_stack_.back().label_type = label_type; + } + + Index GetFuncParamCount(const Var& var) const; + Index GetFuncResultCount(const Var& var) const; + + void BeginBlock(LabelType label_type, const Block& block); + void EndBlock(); + void BeginFunc(const Func& func); + void EndFunc(); + + struct Arities { + Index nargs; + Index nreturns; + bool unreachable; + Arities(Index na, Index nr, bool ur = false) + : nargs(na), nreturns(nr), unreachable(ur) {} + }; + Arities GetExprArity(const Expr& expr) const; + + const Module &module; + private: + const Func* current_func_ = nullptr; + std::vector<Label> label_stack_; +}; + +} // namespace wabt + +#endif /* WABT_IR_UTIL_H_ */ diff --git a/third_party/wasm2c/src/ir.cc b/third_party/wasm2c/src/ir.cc new file mode 100644 index 0000000000..ddfd490de2 --- /dev/null +++ b/third_party/wasm2c/src/ir.cc @@ -0,0 +1,695 @@ +/* + * 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 "src/ir.h" + +#include <cassert> +#include <cstddef> +#include <numeric> + +#include "src/cast.h" + +namespace { + +const char* ExprTypeName[] = { + "AtomicFence", + "AtomicLoad", + "AtomicRmw", + "AtomicRmwCmpxchg", + "AtomicStore", + "AtomicNotify", + "AtomicWait", + "Binary", + "Block", + "Br", + "BrIf", + "BrTable", + "Call", + "CallIndirect", + "CallRef", + "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(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 = cast<ModuleCommand>(commands[index].get()); + return &command->module; +} + +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& pair : bindings) { + assert(static_cast<size_t>(pair.second.index) < + out_reverse_mapping->size()); + (*out_reverse_mapping)[pair.second.index] = pair.first; + } +} + +Var::Var(Index index, const Location& loc) + : loc(loc), type_(VarType::Index), index_(index) {} + +Var::Var(string_view name, const Location& loc) + : loc(loc), type_(VarType::Name), name_(name) {} + +Var::Var(Var&& rhs) : Var(kInvalidIndex) { + *this = std::move(rhs); +} + +Var::Var(const Var& rhs) : Var(kInvalidIndex) { + *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(string_view name) { + set_name(name.to_string()); +} + +void Var::Destroy() { + if (is_name()) { + Destruct(name_); + } +} + +uint8_t ElemSegment::GetFlags(const Module* module) const { + uint8_t flags = 0; + + bool all_ref_func = elem_type == Type::FuncRef; + + switch (kind) { + case SegmentKind::Active: { + Index table_index = module->GetTableIndex(table_var); + if (table_index != 0) { + flags |= SegExplicitIndex; + } + break; + } + + case SegmentKind::Passive: + flags |= SegPassive; + break; + + case SegmentKind::Declared: + flags |= SegDeclared; + break; + } + + all_ref_func = all_ref_func && + std::all_of(elem_exprs.begin(), elem_exprs.end(), + [](const ElemExpr& elem_expr) { + return elem_expr.kind == ElemExprKind::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/ir.h b/third_party/wasm2c/src/ir.h new file mode 100644 index 0000000000..3c63128456 --- /dev/null +++ b/third_party/wasm2c/src/ir.h @@ -0,0 +1,1354 @@ +/* + * 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_IR_H_ +#define WABT_IR_H_ + +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <memory> +#include <string> +#include <type_traits> +#include <vector> + +#include "src/binding-hash.h" +#include "src/common.h" +#include "src/intrusive-list.h" +#include "src/opcode.h" +#include "src/string-view.h" + +namespace wabt { + +struct Module; + +enum class VarType { + Index, + Name, +}; + +struct Var { + explicit Var(Index index = kInvalidIndex, const Location& loc = Location()); + explicit Var(string_view name, const Location& loc = Location()); + Var(Var&&); + Var(const Var&); + Var& operator=(const Var&); + Var& operator=(Var&&); + ~Var(); + + VarType type() const { return type_; } + bool is_index() const { return type_ == VarType::Index; } + bool is_name() const { return type_ == VarType::Name; } + + Index index() const { assert(is_index()); return index_; } + const std::string& name() const { assert(is_name()); return name_; } + + void set_index(Index); + void set_name(std::string&&); + void set_name(string_view); + + Location loc; + + private: + void Destroy(); + + VarType type_; + union { + Index index_; + std::string name_; + }; +}; +typedef std::vector<Var> VarVector; + +struct Const { + static constexpr uintptr_t kRefNullBits = ~uintptr_t(0); + + Const() : Const(Type::I32, uint32_t(0)) {} + + static Const I32(uint32_t val = 0, const Location& loc = Location()) { + return Const(Type::I32, val, loc); + } + static Const I64(uint64_t val = 0, const Location& loc = Location()) { + return Const(Type::I64, val, loc); + } + static Const F32(uint32_t val = 0, const Location& loc = Location()) { + return Const(Type::F32, val, loc); + } + static Const F64(uint64_t val = 0, const Location& loc = Location()) { + return Const(Type::F64, val, loc); + } + static Const V128(v128 val, const Location& loc = Location()) { + return Const(Type::V128, val, loc); + } + + Type type() const { return type_; } + Type lane_type() const { assert(type_ == Type::V128); return lane_type_; } + + int lane_count() const { + switch (lane_type()) { + case Type::I8: return 16; + case Type::I16: return 8; + case Type::I32: return 4; + case Type::I64: return 2; + case Type::F32: return 4; + case Type::F64: return 2; + default: WABT_UNREACHABLE; + } + } + + uint32_t u32() const { return data_.u32(0); } + uint64_t u64() const { return data_.u64(0); } + uint32_t f32_bits() const { return data_.f32_bits(0); } + uint64_t f64_bits() const { return data_.f64_bits(0); } + uintptr_t ref_bits() const { return data_.To<uintptr_t>(0); } + v128 vec128() const { return data_; } + + template <typename T> + T v128_lane(int lane) const { return data_.To<T>(lane); } + + void set_u32(uint32_t x) { From(Type::I32, x); } + void set_u64(uint64_t x) { From(Type::I64, x); } + void set_f32(uint32_t x) { From(Type::F32, x); } + void set_f64(uint64_t x) { From(Type::F64, x); } + + void set_v128_u8(int lane, uint8_t x) { set_v128_lane(lane, Type::I8, x); } + void set_v128_u16(int lane, uint16_t x) { set_v128_lane(lane, Type::I16, x); } + void set_v128_u32(int lane, uint32_t x) { set_v128_lane(lane, Type::I32, x); } + void set_v128_u64(int lane, uint64_t x) { set_v128_lane(lane, Type::I64, x); } + void set_v128_f32(int lane, uint32_t x) { set_v128_lane(lane, Type::F32, x); } + void set_v128_f64(int lane, uint64_t x) { set_v128_lane(lane, Type::F64, x); } + + // Only used for expectations. (e.g. wast assertions) + void set_f32(ExpectedNan nan) { set_f32(0); set_expected_nan(0, nan); } + void set_f64(ExpectedNan nan) { set_f64(0); set_expected_nan(0, nan); } + void set_funcref() { From<uintptr_t>(Type::FuncRef, 0); } + void set_externref(uintptr_t x) { From(Type::ExternRef, x); } + void set_null(Type type) { From<uintptr_t>(type, kRefNullBits); } + + bool is_expected_nan(int lane = 0) const { + return expected_nan(lane) != ExpectedNan::None; + } + + ExpectedNan expected_nan(int lane = 0) const { + return lane < 4 ? nan_[lane] : ExpectedNan::None; + } + + void set_expected_nan(int lane, ExpectedNan nan) { + if (lane < 4) { + nan_[lane] = nan; + } + } + + // v128 support + Location loc; + + private: + template <typename T> + void set_v128_lane(int lane, Type lane_type, T x) { + lane_type_ = lane_type; + From(Type::V128, x, lane); + set_expected_nan(lane, ExpectedNan::None); + } + + template <typename T> + Const(Type type, T data, const Location& loc = Location()) : loc(loc) { + From<T>(type, data); + } + + template <typename T> + void From(Type type, T data, int lane = 0) { + static_assert(sizeof(T) <= sizeof(data_), "Invalid cast!"); + assert((lane + 1) * sizeof(T) <= sizeof(data_)); + type_ = type; + data_.From<T>(lane, data); + set_expected_nan(lane, ExpectedNan::None); + } + + Type type_; + Type lane_type_; // Only valid if type_ == Type::V128. + v128 data_; + ExpectedNan nan_[4]; +}; +typedef std::vector<Const> ConstVector; + +struct FuncSignature { + TypeVector param_types; + TypeVector result_types; + + Index GetNumParams() const { return param_types.size(); } + Index GetNumResults() const { return result_types.size(); } + Type GetParamType(Index index) const { return param_types[index]; } + Type GetResultType(Index index) const { return result_types[index]; } + + bool operator==(const FuncSignature&) const; +}; + +enum class TypeEntryKind { + Func, + Struct, + Array, +}; + +class TypeEntry { + public: + WABT_DISALLOW_COPY_AND_ASSIGN(TypeEntry); + + virtual ~TypeEntry() = default; + + TypeEntryKind kind() const { return kind_; } + + Location loc; + std::string name; + + protected: + explicit TypeEntry(TypeEntryKind kind, + string_view name = string_view(), + const Location& loc = Location()) + : loc(loc), name(name.to_string()), kind_(kind) {} + + TypeEntryKind kind_; +}; + +class FuncType : public TypeEntry { + public: + static bool classof(const TypeEntry* entry) { + return entry->kind() == TypeEntryKind::Func; + } + + explicit FuncType(string_view name = string_view()) + : TypeEntry(TypeEntryKind::Func, name) {} + + Index GetNumParams() const { return sig.GetNumParams(); } + Index GetNumResults() const { return sig.GetNumResults(); } + Type GetParamType(Index index) const { return sig.GetParamType(index); } + Type GetResultType(Index index) const { return sig.GetResultType(index); } + + FuncSignature sig; +}; + +struct Field { + std::string name; + Type type = Type::Void; + bool mutable_ = false; +}; + +class StructType : public TypeEntry { + public: + static bool classof(const TypeEntry* entry) { + return entry->kind() == TypeEntryKind::Struct; + } + + explicit StructType(string_view name = string_view()) + : TypeEntry(TypeEntryKind::Struct) {} + + std::vector<Field> fields; +}; + +class ArrayType : public TypeEntry { + public: + static bool classof(const TypeEntry* entry) { + return entry->kind() == TypeEntryKind::Array; + } + + explicit ArrayType(string_view name = string_view()) + : TypeEntry(TypeEntryKind::Array) {} + + Field field; +}; + +struct FuncDeclaration { + Index GetNumParams() const { return sig.GetNumParams(); } + Index GetNumResults() const { return sig.GetNumResults(); } + Type GetParamType(Index index) const { return sig.GetParamType(index); } + Type GetResultType(Index index) const { return sig.GetResultType(index); } + + bool has_func_type = false; + Var type_var; + FuncSignature sig; +}; + +enum class ExprType { + AtomicLoad, + AtomicRmw, + AtomicRmwCmpxchg, + AtomicStore, + AtomicNotify, + AtomicFence, + AtomicWait, + Binary, + Block, + Br, + BrIf, + BrTable, + Call, + CallIndirect, + CallRef, + 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, + + First = AtomicLoad, + Last = Unreachable +}; + +const char* GetExprTypeName(ExprType type); + +class Expr; +typedef intrusive_list<Expr> ExprList; + +typedef FuncDeclaration BlockDeclaration; + +struct Block { + Block() = default; + explicit Block(ExprList exprs) : exprs(std::move(exprs)) {} + + std::string label; + BlockDeclaration decl; + ExprList exprs; + Location end_loc; +}; + +struct Catch { + explicit Catch(const Location& loc = Location()) : loc(loc) {} + explicit Catch(const Var& var, const Location& loc = Location()) + : loc(loc), var(var) {} + Location loc; + Var var; + ExprList exprs; + bool IsCatchAll() const { + return var.is_index() && var.index() == kInvalidIndex; + } +}; +typedef std::vector<Catch> CatchVector; + +enum class TryKind { + Plain, + Catch, + Delegate +}; + +class Expr : public intrusive_list_base<Expr> { + public: + WABT_DISALLOW_COPY_AND_ASSIGN(Expr); + Expr() = delete; + virtual ~Expr() = default; + + ExprType type() const { return type_; } + + Location loc; + + protected: + explicit Expr(ExprType type, const Location& loc = Location()) + : loc(loc), type_(type) {} + + ExprType type_; +}; + +const char* GetExprTypeName(const Expr& expr); + +template <ExprType TypeEnum> +class ExprMixin : public Expr { + public: + static bool classof(const Expr* expr) { return expr->type() == TypeEnum; } + + explicit ExprMixin(const Location& loc = Location()) : Expr(TypeEnum, loc) {} +}; + +typedef ExprMixin<ExprType::Drop> DropExpr; +typedef ExprMixin<ExprType::MemoryGrow> MemoryGrowExpr; +typedef ExprMixin<ExprType::MemorySize> MemorySizeExpr; +typedef ExprMixin<ExprType::MemoryCopy> MemoryCopyExpr; +typedef ExprMixin<ExprType::MemoryFill> MemoryFillExpr; +typedef ExprMixin<ExprType::Nop> NopExpr; +typedef ExprMixin<ExprType::Return> ReturnExpr; +typedef ExprMixin<ExprType::Unreachable> UnreachableExpr; + +template <ExprType TypeEnum> +class RefTypeExpr : public ExprMixin<TypeEnum> { + public: + RefTypeExpr(Type type, const Location& loc = Location()) + : ExprMixin<TypeEnum>(loc), type(type) {} + + Type type; +}; + +typedef RefTypeExpr<ExprType::RefNull> RefNullExpr; +typedef ExprMixin<ExprType::RefIsNull> RefIsNullExpr; + +template <ExprType TypeEnum> +class OpcodeExpr : public ExprMixin<TypeEnum> { + public: + OpcodeExpr(Opcode opcode, const Location& loc = Location()) + : ExprMixin<TypeEnum>(loc), opcode(opcode) {} + + Opcode opcode; +}; + +typedef OpcodeExpr<ExprType::Binary> BinaryExpr; +typedef OpcodeExpr<ExprType::Compare> CompareExpr; +typedef OpcodeExpr<ExprType::Convert> ConvertExpr; +typedef OpcodeExpr<ExprType::Unary> UnaryExpr; +typedef OpcodeExpr<ExprType::Ternary> TernaryExpr; + +class SimdLaneOpExpr : public ExprMixin<ExprType::SimdLaneOp> { + public: + SimdLaneOpExpr(Opcode opcode, uint64_t val, const Location& loc = Location()) + : ExprMixin<ExprType::SimdLaneOp>(loc), opcode(opcode), val(val) {} + + Opcode opcode; + uint64_t val; +}; + +class SimdLoadLaneExpr : public OpcodeExpr<ExprType::SimdLoadLane> { + public: + SimdLoadLaneExpr(Opcode opcode, + Address align, + Address offset, + uint64_t val, + const Location& loc = Location()) + : OpcodeExpr<ExprType::SimdLoadLane>(opcode, loc), + align(align), + offset(offset), + val(val) {} + + Address align; + Address offset; + uint64_t val; +}; + +class SimdStoreLaneExpr : public OpcodeExpr<ExprType::SimdStoreLane> { + public: + SimdStoreLaneExpr(Opcode opcode, + Address align, + Address offset, + uint64_t val, + const Location& loc = Location()) + : OpcodeExpr<ExprType::SimdStoreLane>(opcode, loc), + align(align), + offset(offset), + val(val) {} + + Address align; + Address offset; + uint64_t val; +}; + +class SimdShuffleOpExpr : public ExprMixin<ExprType::SimdShuffleOp> { + public: + SimdShuffleOpExpr(Opcode opcode, v128 val, const Location& loc = Location()) + : ExprMixin<ExprType::SimdShuffleOp>(loc), opcode(opcode), val(val) {} + + Opcode opcode; + v128 val; +}; + +template <ExprType TypeEnum> +class VarExpr : public ExprMixin<TypeEnum> { + public: + VarExpr(const Var& var, const Location& loc = Location()) + : ExprMixin<TypeEnum>(loc), var(var) {} + + Var var; +}; + +typedef VarExpr<ExprType::Br> BrExpr; +typedef VarExpr<ExprType::BrIf> BrIfExpr; +typedef VarExpr<ExprType::Call> CallExpr; +typedef VarExpr<ExprType::RefFunc> RefFuncExpr; +typedef VarExpr<ExprType::GlobalGet> GlobalGetExpr; +typedef VarExpr<ExprType::GlobalSet> GlobalSetExpr; +typedef VarExpr<ExprType::LocalGet> LocalGetExpr; +typedef VarExpr<ExprType::LocalSet> LocalSetExpr; +typedef VarExpr<ExprType::LocalTee> LocalTeeExpr; +typedef VarExpr<ExprType::ReturnCall> ReturnCallExpr; +typedef VarExpr<ExprType::Throw> ThrowExpr; +typedef VarExpr<ExprType::Rethrow> RethrowExpr; + +typedef VarExpr<ExprType::MemoryInit> MemoryInitExpr; +typedef VarExpr<ExprType::DataDrop> DataDropExpr; +typedef VarExpr<ExprType::ElemDrop> ElemDropExpr; +typedef VarExpr<ExprType::TableGet> TableGetExpr; +typedef VarExpr<ExprType::TableSet> TableSetExpr; +typedef VarExpr<ExprType::TableGrow> TableGrowExpr; +typedef VarExpr<ExprType::TableSize> TableSizeExpr; +typedef VarExpr<ExprType::TableFill> TableFillExpr; + +class SelectExpr : public ExprMixin<ExprType::Select> { + public: + SelectExpr(TypeVector type, const Location& loc = Location()) + : ExprMixin<ExprType::Select>(loc), result_type(type) {} + TypeVector result_type; +}; + +class TableInitExpr : public ExprMixin<ExprType::TableInit> { + public: + TableInitExpr(const Var& segment_index, + const Var& table_index, + const Location& loc = Location()) + : ExprMixin<ExprType::TableInit>(loc), + segment_index(segment_index), + table_index(table_index) {} + + Var segment_index; + Var table_index; +}; + +class TableCopyExpr : public ExprMixin<ExprType::TableCopy> { + public: + TableCopyExpr(const Var& dst, + const Var& src, + const Location& loc = Location()) + : ExprMixin<ExprType::TableCopy>(loc), dst_table(dst), src_table(src) {} + + Var dst_table; + Var src_table; +}; + +class CallIndirectExpr : public ExprMixin<ExprType::CallIndirect> { + public: + explicit CallIndirectExpr(const Location& loc = Location()) + : ExprMixin<ExprType::CallIndirect>(loc) {} + + FuncDeclaration decl; + Var table; +}; + +class ReturnCallIndirectExpr : public ExprMixin<ExprType::ReturnCallIndirect> { + public: + explicit ReturnCallIndirectExpr(const Location &loc = Location()) + : ExprMixin<ExprType::ReturnCallIndirect>(loc) {} + + FuncDeclaration decl; + Var table; +}; + +class CallRefExpr : public ExprMixin<ExprType::CallRef> { + public: + explicit CallRefExpr(const Location &loc = Location()) + : ExprMixin<ExprType::CallRef>(loc) {} + + // This field is setup only during Validate phase, + // so keep that in mind when you use it. + Var function_type_index; +}; + +template <ExprType TypeEnum> +class BlockExprBase : public ExprMixin<TypeEnum> { + public: + explicit BlockExprBase(const Location& loc = Location()) + : ExprMixin<TypeEnum>(loc) {} + + Block block; +}; + +typedef BlockExprBase<ExprType::Block> BlockExpr; +typedef BlockExprBase<ExprType::Loop> LoopExpr; + +class IfExpr : public ExprMixin<ExprType::If> { + public: + explicit IfExpr(const Location& loc = Location()) + : ExprMixin<ExprType::If>(loc) {} + + Block true_; + ExprList false_; + Location false_end_loc; +}; + +class TryExpr : public ExprMixin<ExprType::Try> { + public: + explicit TryExpr(const Location& loc = Location()) + : ExprMixin<ExprType::Try>(loc), kind(TryKind::Plain) {} + + TryKind kind; + Block block; + CatchVector catches; + Var delegate_target; +}; + +class BrTableExpr : public ExprMixin<ExprType::BrTable> { + public: + BrTableExpr(const Location& loc = Location()) + : ExprMixin<ExprType::BrTable>(loc) {} + + VarVector targets; + Var default_target; +}; + +class ConstExpr : public ExprMixin<ExprType::Const> { + public: + ConstExpr(const Const& c, const Location& loc = Location()) + : ExprMixin<ExprType::Const>(loc), const_(c) {} + + Const const_; +}; + +// TODO(binji): Rename this, it is used for more than loads/stores now. +template <ExprType TypeEnum> +class LoadStoreExpr : public ExprMixin<TypeEnum> { + public: + LoadStoreExpr(Opcode opcode, + Address align, + Address offset, + const Location& loc = Location()) + : ExprMixin<TypeEnum>(loc), + opcode(opcode), + align(align), + offset(offset) {} + + Opcode opcode; + Address align; + Address offset; +}; + +typedef LoadStoreExpr<ExprType::Load> LoadExpr; +typedef LoadStoreExpr<ExprType::Store> StoreExpr; +typedef LoadStoreExpr<ExprType::AtomicLoad> AtomicLoadExpr; +typedef LoadStoreExpr<ExprType::AtomicStore> AtomicStoreExpr; +typedef LoadStoreExpr<ExprType::AtomicRmw> AtomicRmwExpr; +typedef LoadStoreExpr<ExprType::AtomicRmwCmpxchg> AtomicRmwCmpxchgExpr; +typedef LoadStoreExpr<ExprType::AtomicWait> AtomicWaitExpr; +typedef LoadStoreExpr<ExprType::AtomicNotify> AtomicNotifyExpr; +typedef LoadStoreExpr<ExprType::LoadSplat> LoadSplatExpr; +typedef LoadStoreExpr<ExprType::LoadZero> LoadZeroExpr; + +class AtomicFenceExpr : public ExprMixin<ExprType::AtomicFence> { + public: + explicit AtomicFenceExpr(uint32_t consistency_model, + const Location& loc = Location()) + : ExprMixin<ExprType::AtomicFence>(loc), + consistency_model(consistency_model) {} + + uint32_t consistency_model; +}; + +struct Tag { + explicit Tag(string_view name) : name(name.to_string()) {} + + std::string name; + FuncDeclaration decl; +}; + +class LocalTypes { + public: + typedef std::pair<Type, Index> Decl; + typedef std::vector<Decl> Decls; + + struct const_iterator { + const_iterator(Decls::const_iterator decl, Index index) + : decl(decl), index(index) {} + Type operator*() const { return decl->first; } + const_iterator& operator++(); + const_iterator operator++(int); + + Decls::const_iterator decl; + Index index; + }; + + void Set(const TypeVector&); + + const Decls& decls() const { return decls_; } + + void AppendDecl(Type type, Index count) { + if (count != 0) { + decls_.emplace_back(type, count); + } + } + + Index size() const; + Type operator[](Index) const; + + const_iterator begin() const { return {decls_.begin(), 0}; } + const_iterator end() const { return {decls_.end(), 0}; } + + private: + Decls decls_; +}; + +inline LocalTypes::const_iterator& LocalTypes::const_iterator::operator++() { + ++index; + if (index >= decl->second) { + ++decl; + index = 0; + } + return *this; +} + +inline LocalTypes::const_iterator LocalTypes::const_iterator::operator++(int) { + const_iterator result = *this; + operator++(); + return result; +} + +inline bool operator==(const LocalTypes::const_iterator& lhs, + const LocalTypes::const_iterator& rhs) { + return lhs.decl == rhs.decl && lhs.index == rhs.index; +} + +inline bool operator!=(const LocalTypes::const_iterator& lhs, + const LocalTypes::const_iterator& rhs) { + return !operator==(lhs, rhs); +} + +struct Func { + explicit Func(string_view name) : name(name.to_string()) {} + + Type GetParamType(Index index) const { return decl.GetParamType(index); } + Type GetResultType(Index index) const { return decl.GetResultType(index); } + Type GetLocalType(Index index) const; + Type GetLocalType(const Var& var) const; + Index GetNumParams() const { return decl.GetNumParams(); } + Index GetNumLocals() const { return local_types.size(); } + Index GetNumParamsAndLocals() const { + return GetNumParams() + GetNumLocals(); + } + Index GetNumResults() const { return decl.GetNumResults(); } + Index GetLocalIndex(const Var&) const; + + std::string name; + FuncDeclaration decl; + LocalTypes local_types; + BindingHash bindings; + ExprList exprs; +}; + +struct Global { + explicit Global(string_view name) : name(name.to_string()) {} + + std::string name; + Type type = Type::Void; + bool mutable_ = false; + ExprList init_expr; +}; + +struct Table { + explicit Table(string_view name) + : name(name.to_string()), elem_type(Type::FuncRef) {} + + std::string name; + Limits elem_limits; + Type elem_type; +}; + +enum class ElemExprKind { + RefNull, + RefFunc, +}; + +struct ElemExpr { + ElemExpr() : kind(ElemExprKind::RefNull), type(Type::FuncRef) {} + explicit ElemExpr(Var var) : kind(ElemExprKind::RefFunc), var(var) {} + explicit ElemExpr(Type type) : kind(ElemExprKind::RefNull), type(type) {} + + ElemExprKind kind; + Var var; // Only used when kind == RefFunc. + Type type; // Only used when kind == RefNull +}; + +typedef std::vector<ElemExpr> ElemExprVector; + +struct ElemSegment { + explicit ElemSegment(string_view name) : name(name.to_string()) {} + uint8_t GetFlags(const Module*) const; + + SegmentKind kind = SegmentKind::Active; + std::string name; + Var table_var; + Type elem_type; + ExprList offset; + ElemExprVector elem_exprs; +}; + +struct Memory { + explicit Memory(string_view name) : name(name.to_string()) {} + + std::string name; + Limits page_limits; +}; + +struct DataSegment { + explicit DataSegment(string_view name) : name(name.to_string()) {} + uint8_t GetFlags(const Module*) const; + + SegmentKind kind = SegmentKind::Active; + std::string name; + Var memory_var; + ExprList offset; + std::vector<uint8_t> data; +}; + +class Import { + public: + WABT_DISALLOW_COPY_AND_ASSIGN(Import); + Import() = delete; + virtual ~Import() = default; + + ExternalKind kind() const { return kind_; } + + std::string module_name; + std::string field_name; + + protected: + Import(ExternalKind kind) : kind_(kind) {} + + ExternalKind kind_; +}; + +template <ExternalKind TypeEnum> +class ImportMixin : public Import { + public: + static bool classof(const Import* import) { + return import->kind() == TypeEnum; + } + + ImportMixin() : Import(TypeEnum) {} +}; + +class FuncImport : public ImportMixin<ExternalKind::Func> { + public: + explicit FuncImport(string_view name = string_view()) + : ImportMixin<ExternalKind::Func>(), func(name) {} + + Func func; +}; + +class TableImport : public ImportMixin<ExternalKind::Table> { + public: + explicit TableImport(string_view name = string_view()) + : ImportMixin<ExternalKind::Table>(), table(name) {} + + Table table; +}; + +class MemoryImport : public ImportMixin<ExternalKind::Memory> { + public: + explicit MemoryImport(string_view name = string_view()) + : ImportMixin<ExternalKind::Memory>(), memory(name) {} + + Memory memory; +}; + +class GlobalImport : public ImportMixin<ExternalKind::Global> { + public: + explicit GlobalImport(string_view name = string_view()) + : ImportMixin<ExternalKind::Global>(), global(name) {} + + Global global; +}; + +class TagImport : public ImportMixin<ExternalKind::Tag> { + public: + explicit TagImport(string_view name = string_view()) + : ImportMixin<ExternalKind::Tag>(), tag(name) {} + + Tag tag; +}; + +struct Export { + std::string name; + ExternalKind kind; + Var var; +}; + +enum class ModuleFieldType { + Func, + Global, + Import, + Export, + Type, + Table, + ElemSegment, + Memory, + DataSegment, + Start, + Tag +}; + +class ModuleField : public intrusive_list_base<ModuleField> { + public: + WABT_DISALLOW_COPY_AND_ASSIGN(ModuleField); + ModuleField() = delete; + virtual ~ModuleField() = default; + + ModuleFieldType type() const { return type_; } + + Location loc; + + protected: + ModuleField(ModuleFieldType type, const Location& loc) + : loc(loc), type_(type) {} + + ModuleFieldType type_; +}; + +typedef intrusive_list<ModuleField> ModuleFieldList; + +template <ModuleFieldType TypeEnum> +class ModuleFieldMixin : public ModuleField { + public: + static bool classof(const ModuleField* field) { + return field->type() == TypeEnum; + } + + explicit ModuleFieldMixin(const Location& loc) : ModuleField(TypeEnum, loc) {} +}; + +class FuncModuleField : public ModuleFieldMixin<ModuleFieldType::Func> { + public: + explicit FuncModuleField(const Location& loc = Location(), + string_view name = string_view()) + : ModuleFieldMixin<ModuleFieldType::Func>(loc), func(name) {} + + Func func; +}; + +class GlobalModuleField : public ModuleFieldMixin<ModuleFieldType::Global> { + public: + explicit GlobalModuleField(const Location& loc = Location(), + string_view name = string_view()) + : ModuleFieldMixin<ModuleFieldType::Global>(loc), global(name) {} + + Global global; +}; + +class ImportModuleField : public ModuleFieldMixin<ModuleFieldType::Import> { + public: + explicit ImportModuleField(const Location& loc = Location()) + : ModuleFieldMixin<ModuleFieldType::Import>(loc) {} + explicit ImportModuleField(std::unique_ptr<Import> import, + const Location& loc = Location()) + : ModuleFieldMixin<ModuleFieldType::Import>(loc), + import(std::move(import)) {} + + std::unique_ptr<Import> import; +}; + +class ExportModuleField : public ModuleFieldMixin<ModuleFieldType::Export> { + public: + explicit ExportModuleField(const Location& loc = Location()) + : ModuleFieldMixin<ModuleFieldType::Export>(loc) {} + + Export export_; +}; + +class TypeModuleField : public ModuleFieldMixin<ModuleFieldType::Type> { + public: + explicit TypeModuleField(const Location& loc = Location()) + : ModuleFieldMixin<ModuleFieldType::Type>(loc) {} + + std::unique_ptr<TypeEntry> type; +}; + +class TableModuleField : public ModuleFieldMixin<ModuleFieldType::Table> { + public: + explicit TableModuleField(const Location& loc = Location(), + string_view name = string_view()) + : ModuleFieldMixin<ModuleFieldType::Table>(loc), table(name) {} + + Table table; +}; + +class ElemSegmentModuleField + : public ModuleFieldMixin<ModuleFieldType::ElemSegment> { + public: + explicit ElemSegmentModuleField(const Location& loc = Location(), + string_view name = string_view()) + : ModuleFieldMixin<ModuleFieldType::ElemSegment>(loc), + elem_segment(name) {} + + ElemSegment elem_segment; +}; + +class MemoryModuleField : public ModuleFieldMixin<ModuleFieldType::Memory> { + public: + explicit MemoryModuleField(const Location& loc = Location(), + string_view name = string_view()) + : ModuleFieldMixin<ModuleFieldType::Memory>(loc), memory(name) {} + + Memory memory; +}; + +class DataSegmentModuleField + : public ModuleFieldMixin<ModuleFieldType::DataSegment> { + public: + explicit DataSegmentModuleField(const Location& loc = Location(), + string_view name = string_view()) + : ModuleFieldMixin<ModuleFieldType::DataSegment>(loc), + data_segment(name) {} + + DataSegment data_segment; +}; + +class TagModuleField : public ModuleFieldMixin<ModuleFieldType::Tag> { + public: + explicit TagModuleField(const Location& loc = Location(), + string_view name = string_view()) + : ModuleFieldMixin<ModuleFieldType::Tag>(loc), tag(name) {} + + Tag tag; +}; + +class StartModuleField : public ModuleFieldMixin<ModuleFieldType::Start> { + public: + explicit StartModuleField(Var start = Var(), const Location& loc = Location()) + : ModuleFieldMixin<ModuleFieldType::Start>(loc), start(start) {} + + Var start; +}; + +struct Module { + Index GetFuncTypeIndex(const Var&) const; + Index GetFuncTypeIndex(const FuncDeclaration&) const; + Index GetFuncTypeIndex(const FuncSignature&) const; + const FuncType* GetFuncType(const Var&) const; + FuncType* GetFuncType(const Var&); + Index GetFuncIndex(const Var&) const; + const Func* GetFunc(const Var&) const; + Func* GetFunc(const Var&); + Index GetTableIndex(const Var&) const; + const Table* GetTable(const Var&) const; + Table* GetTable(const Var&); + Index GetMemoryIndex(const Var&) const; + const Memory* GetMemory(const Var&) const; + Memory* GetMemory(const Var&); + Index GetGlobalIndex(const Var&) const; + const Global* GetGlobal(const Var&) const; + Global* GetGlobal(const Var&); + const Export* GetExport(string_view) const; + Tag* GetTag(const Var&) const; + Index GetTagIndex(const Var&) const; + const DataSegment* GetDataSegment(const Var&) const; + DataSegment* GetDataSegment(const Var&); + Index GetDataSegmentIndex(const Var&) const; + const ElemSegment* GetElemSegment(const Var&) const; + ElemSegment* GetElemSegment(const Var&); + Index GetElemSegmentIndex(const Var&) const; + + bool IsImport(ExternalKind kind, const Var&) const; + bool IsImport(const Export& export_) const { + return IsImport(export_.kind, export_.var); + } + + // TODO(binji): move this into a builder class? + void AppendField(std::unique_ptr<DataSegmentModuleField>); + void AppendField(std::unique_ptr<ElemSegmentModuleField>); + void AppendField(std::unique_ptr<TagModuleField>); + void AppendField(std::unique_ptr<ExportModuleField>); + void AppendField(std::unique_ptr<FuncModuleField>); + void AppendField(std::unique_ptr<TypeModuleField>); + void AppendField(std::unique_ptr<GlobalModuleField>); + void AppendField(std::unique_ptr<ImportModuleField>); + void AppendField(std::unique_ptr<MemoryModuleField>); + void AppendField(std::unique_ptr<StartModuleField>); + void AppendField(std::unique_ptr<TableModuleField>); + void AppendField(std::unique_ptr<ModuleField>); + void AppendFields(ModuleFieldList*); + + Location loc; + std::string name; + ModuleFieldList fields; + + Index num_tag_imports = 0; + Index num_func_imports = 0; + Index num_table_imports = 0; + Index num_memory_imports = 0; + Index num_global_imports = 0; + + // Cached for convenience; the pointers are shared with values that are + // stored in either ModuleField or Import. + std::vector<Tag*> tags; + std::vector<Func*> funcs; + std::vector<Global*> globals; + std::vector<Import*> imports; + std::vector<Export*> exports; + std::vector<TypeEntry*> types; + std::vector<Table*> tables; + std::vector<ElemSegment*> elem_segments; + std::vector<Memory*> memories; + std::vector<DataSegment*> data_segments; + std::vector<Var*> starts; + + BindingHash tag_bindings; + BindingHash func_bindings; + BindingHash global_bindings; + BindingHash export_bindings; + BindingHash type_bindings; + BindingHash table_bindings; + BindingHash memory_bindings; + BindingHash data_segment_bindings; + BindingHash elem_segment_bindings; +}; + +enum class ScriptModuleType { + Text, + Binary, + Quoted, +}; + +// A ScriptModule is a module that may not yet be decoded. This allows for text +// and binary parsing errors to be deferred until validation time. +class ScriptModule { + public: + WABT_DISALLOW_COPY_AND_ASSIGN(ScriptModule); + ScriptModule() = delete; + virtual ~ScriptModule() = default; + + ScriptModuleType type() const { return type_; } + virtual const Location& location() const = 0; + + protected: + explicit ScriptModule(ScriptModuleType type) : type_(type) {} + + ScriptModuleType type_; +}; + +template <ScriptModuleType TypeEnum> +class ScriptModuleMixin : public ScriptModule { + public: + static bool classof(const ScriptModule* script_module) { + return script_module->type() == TypeEnum; + } + + ScriptModuleMixin() : ScriptModule(TypeEnum) {} +}; + +class TextScriptModule : public ScriptModuleMixin<ScriptModuleType::Text> { + public: + const Location& location() const override { return module.loc; } + + Module module; +}; + +template <ScriptModuleType TypeEnum> +class DataScriptModule : public ScriptModuleMixin<TypeEnum> { + public: + const Location& location() const override { return loc; } + + Location loc; + std::string name; + std::vector<uint8_t> data; +}; + +typedef DataScriptModule<ScriptModuleType::Binary> BinaryScriptModule; +typedef DataScriptModule<ScriptModuleType::Quoted> QuotedScriptModule; + +enum class ActionType { + Invoke, + Get, +}; + +class Action { + public: + WABT_DISALLOW_COPY_AND_ASSIGN(Action); + Action() = delete; + virtual ~Action() = default; + + ActionType type() const { return type_; } + + Location loc; + Var module_var; + std::string name; + + protected: + explicit Action(ActionType type, const Location& loc = Location()) + : loc(loc), type_(type) {} + + ActionType type_; +}; + +typedef std::unique_ptr<Action> ActionPtr; + +template <ActionType TypeEnum> +class ActionMixin : public Action { + public: + static bool classof(const Action* action) { + return action->type() == TypeEnum; + } + + explicit ActionMixin(const Location& loc = Location()) + : Action(TypeEnum, loc) {} +}; + +class GetAction : public ActionMixin<ActionType::Get> { + public: + explicit GetAction(const Location& loc = Location()) + : ActionMixin<ActionType::Get>(loc) {} +}; + +class InvokeAction : public ActionMixin<ActionType::Invoke> { + public: + explicit InvokeAction(const Location& loc = Location()) + : ActionMixin<ActionType::Invoke>(loc) {} + + ConstVector args; +}; + +enum class CommandType { + Module, + Action, + Register, + AssertMalformed, + AssertInvalid, + AssertUnlinkable, + AssertUninstantiable, + AssertReturn, + AssertTrap, + AssertExhaustion, + + First = Module, + Last = AssertExhaustion, +}; +static const int kCommandTypeCount = WABT_ENUM_COUNT(CommandType); + +class Command { + public: + WABT_DISALLOW_COPY_AND_ASSIGN(Command); + Command() = delete; + virtual ~Command() = default; + + CommandType type; + + protected: + explicit Command(CommandType type) : type(type) {} +}; + +template <CommandType TypeEnum> +class CommandMixin : public Command { + public: + static bool classof(const Command* cmd) { return cmd->type == TypeEnum; } + CommandMixin() : Command(TypeEnum) {} +}; + +class ModuleCommand : public CommandMixin<CommandType::Module> { + public: + Module module; +}; + +template <CommandType TypeEnum> +class ActionCommandBase : public CommandMixin<TypeEnum> { + public: + ActionPtr action; +}; + +typedef ActionCommandBase<CommandType::Action> ActionCommand; + +class RegisterCommand : public CommandMixin<CommandType::Register> { + public: + RegisterCommand(string_view module_name, const Var& var) + : module_name(module_name), var(var) {} + + std::string module_name; + Var var; +}; + +class AssertReturnCommand : public CommandMixin<CommandType::AssertReturn> { + public: + ActionPtr action; + ConstVector expected; +}; + +template <CommandType TypeEnum> +class AssertTrapCommandBase : public CommandMixin<TypeEnum> { + public: + ActionPtr action; + std::string text; +}; + +typedef AssertTrapCommandBase<CommandType::AssertTrap> AssertTrapCommand; +typedef AssertTrapCommandBase<CommandType::AssertExhaustion> + AssertExhaustionCommand; + +template <CommandType TypeEnum> +class AssertModuleCommand : public CommandMixin<TypeEnum> { + public: + std::unique_ptr<ScriptModule> module; + std::string text; +}; + +typedef AssertModuleCommand<CommandType::AssertMalformed> + AssertMalformedCommand; +typedef AssertModuleCommand<CommandType::AssertInvalid> AssertInvalidCommand; +typedef AssertModuleCommand<CommandType::AssertUnlinkable> + AssertUnlinkableCommand; +typedef AssertModuleCommand<CommandType::AssertUninstantiable> + AssertUninstantiableCommand; + +typedef std::unique_ptr<Command> CommandPtr; +typedef std::vector<CommandPtr> CommandPtrVector; + +struct Script { + WABT_DISALLOW_COPY_AND_ASSIGN(Script); + Script() = default; + + const Module* GetFirstModule() const; + Module* GetFirstModule(); + const Module* GetModule(const Var&) const; + + CommandPtrVector commands; + BindingHash module_bindings; +}; + +void MakeTypeBindingReverseMapping( + size_t num_types, + const BindingHash& bindings, + std::vector<std::string>* out_reverse_mapping); + +} // namespace wabt + +#endif /* WABT_IR_H_ */ diff --git a/third_party/wasm2c/src/leb128.cc b/third_party/wasm2c/src/leb128.cc new file mode 100644 index 0000000000..6d15e2f363 --- /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 "src/leb128.h" + +#include <type_traits> + +#include "src/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/leb128.h b/third_party/wasm2c/src/leb128.h new file mode 100644 index 0000000000..56f803ccc0 --- /dev/null +++ b/third_party/wasm2c/src/leb128.h @@ -0,0 +1,70 @@ +/* + * 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. + */ + +#ifndef WABT_LEB128_H_ +#define WABT_LEB128_H_ + +#include <cstdint> + +#include "src/common.h" + +namespace wabt { + +class Stream; + +// Returns the length of the leb128. +Offset U32Leb128Length(uint32_t value); + +void WriteU32Leb128(Stream* stream, uint32_t value, const char* desc); +void WriteS32Leb128(Stream* stream, uint32_t value, const char* desc); +void WriteU64Leb128(Stream* stream, uint64_t value, const char* desc); +void WriteS64Leb128(Stream* stream, uint64_t value, const char* desc); +void WriteFixedS32Leb128(Stream* stream, uint32_t value, const char* desc); +void WriteFixedU32Leb128(Stream* stream, uint32_t value, const char* desc); + +Offset WriteU32Leb128At(Stream* stream, + Offset offset, + uint32_t value, + const char* desc); + +Offset WriteFixedU32Leb128At(Stream* stream, + Offset offset, + uint32_t value, + const char* desc); + +Offset WriteU32Leb128Raw(uint8_t* data, uint8_t* end, uint32_t value); +Offset WriteFixedU32Leb128Raw(uint8_t* data, uint8_t* end, uint32_t value); + +// Convenience functions for writing enums as LEB128s. +template <typename T> +void WriteU32Leb128(Stream* stream, T value, const char* desc) { + WriteU32Leb128(stream, static_cast<uint32_t>(value), desc); +} + +template <typename T> +void WriteS32Leb128(Stream* stream, T value, const char* desc) { + WriteS32Leb128(stream, static_cast<uint32_t>(value), desc); +} + +// Returns the length of the leb128. +size_t ReadU32Leb128(const uint8_t* p, const uint8_t* end, uint32_t* out_value); +size_t ReadU64Leb128(const uint8_t* p, const uint8_t* end, uint64_t* out_value); +size_t ReadS32Leb128(const uint8_t* p, const uint8_t* end, uint32_t* out_value); +size_t ReadS64Leb128(const uint8_t* p, const uint8_t* end, uint64_t* out_value); + +} // namespace wabt + +#endif // WABT_LEB128_H_ diff --git a/third_party/wasm2c/src/lexer-keywords.txt b/third_party/wasm2c/src/lexer-keywords.txt new file mode 100644 index 0000000000..1ff631ba4d --- /dev/null +++ b/third_party/wasm2c/src/lexer-keywords.txt @@ -0,0 +1,631 @@ +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_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 +current_memory, TokenType::MemorySize, Opcode::MemorySize +data.drop, TokenType::DataDrop, Opcode::DataDrop +data, TokenType::Data +declare, TokenType::Declare +delegate, TokenType::Delegate +do, TokenType::Do +drop, TokenType::Drop, Opcode::Drop +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.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.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 +grow_memory, TokenType::MemoryGrow, Opcode::MemoryGrow +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.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.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.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 +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.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.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.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 +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 +# Deprecated names. +atomic.notify, TokenType::AtomicNotify, Opcode::MemoryAtomicNotify +i32.atomic.wait, TokenType::AtomicWait, Opcode::MemoryAtomicWait32 +i64.atomic.wait, TokenType::AtomicWait, Opcode::MemoryAtomicWait64 +anyfunc, Type::FuncRef +f32.convert_s/i32, TokenType::Convert, Opcode::F32ConvertI32S +f32.convert_s/i64, TokenType::Convert, Opcode::F32ConvertI64S +f32.convert_u/i32, TokenType::Convert, Opcode::F32ConvertI32U +f32.convert_u/i64, TokenType::Convert, Opcode::F32ConvertI64U +f32.demote/f64, TokenType::Convert, Opcode::F32DemoteF64 +f32.reinterpret/i32, TokenType::Convert, Opcode::F32ReinterpretI32 +f64.convert_s/i32, TokenType::Convert, Opcode::F64ConvertI32S +f64.convert_s/i64, TokenType::Convert, Opcode::F64ConvertI64S +f64.convert_u/i32, TokenType::Convert, Opcode::F64ConvertI32U +f64.convert_u/i64, TokenType::Convert, Opcode::F64ConvertI64U +f64.promote/f32, TokenType::Convert, Opcode::F64PromoteF32 +f64.reinterpret/i64, TokenType::Convert, Opcode::F64ReinterpretI64 +get_global, TokenType::GlobalGet, Opcode::GlobalGet +get_local, TokenType::LocalGet, Opcode::LocalGet +i32.reinterpret/f32, TokenType::Convert, Opcode::I32ReinterpretF32 +i32.trunc_s/f32, TokenType::Convert, Opcode::I32TruncF32S +i32.trunc_s/f64, TokenType::Convert, Opcode::I32TruncF64S +i32.trunc_s:sat/f32, TokenType::Convert, Opcode::I32TruncSatF32S +i32.trunc_s:sat/f64, TokenType::Convert, Opcode::I32TruncSatF64S +i32.trunc_u/f32, TokenType::Convert, Opcode::I32TruncF32U +i32.trunc_u/f64, TokenType::Convert, Opcode::I32TruncF64U +i32.trunc_u:sat/f32, TokenType::Convert, Opcode::I32TruncSatF32U +i32.trunc_u:sat/f64, TokenType::Convert, Opcode::I32TruncSatF64U +i32.wrap/i64, TokenType::Convert, Opcode::I32WrapI64 +i64.extend_s/i32, TokenType::Convert, Opcode::I64ExtendI32S +i64.extend_u/i32, TokenType::Convert, Opcode::I64ExtendI32U +i64.reinterpret/f64, TokenType::Convert, Opcode::I64ReinterpretF64 +i64.trunc_s/f32, TokenType::Convert, Opcode::I64TruncF32S +i64.trunc_s/f64, TokenType::Convert, Opcode::I64TruncF64S +i64.trunc_s:sat/f32, TokenType::Convert, Opcode::I64TruncSatF32S +i64.trunc_s:sat/f64, TokenType::Convert, Opcode::I64TruncSatF64S +i64.trunc_u/f32, TokenType::Convert, Opcode::I64TruncF32U +i64.trunc_u/f64, TokenType::Convert, Opcode::I64TruncF64U +i64.trunc_u:sat/f32, TokenType::Convert, Opcode::I64TruncSatF32U +i64.trunc_u:sat/f64, TokenType::Convert, Opcode::I64TruncSatF64U +set_global, TokenType::GlobalSet, Opcode::GlobalSet +set_local, TokenType::LocalSet, Opcode::LocalSet +tee_local, TokenType::LocalTee, Opcode::LocalTee 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..833cb9009d --- /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 "src/lexer-source-line-finder.h" + +#include <algorithm> + +#include "src/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-line-finder.h b/third_party/wasm2c/src/lexer-source-line-finder.h new file mode 100644 index 0000000000..baf87b4ea7 --- /dev/null +++ b/third_party/wasm2c/src/lexer-source-line-finder.h @@ -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. + */ + +#ifndef WABT_LEXER_SOURCE_LINE_FINDER_H_ +#define WABT_LEXER_SOURCE_LINE_FINDER_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "src/common.h" +#include "src/lexer-source.h" +#include "src/range.h" + +namespace wabt { + +class LexerSourceLineFinder { + public: + struct SourceLine { + std::string line; + int column_offset; + }; + + explicit LexerSourceLineFinder(std::unique_ptr<LexerSource>); + + Result GetSourceLine(const Location& loc, + Offset max_line_length, + SourceLine* out_source_line); + Result GetLineOffsets(int line, OffsetRange* out_offsets); + + private: + static OffsetRange ClampSourceLineOffsets(OffsetRange line_offset_range, + ColumnRange column_range, + Offset max_line_length); + + bool IsLineCached(int line) const; + OffsetRange GetCachedLine(int line) const; + + std::unique_ptr<LexerSource> source_; + std::vector<OffsetRange> line_ranges_; + Offset next_line_start_; + bool last_cr_; // Last read character was a '\r' (carriage return). + bool eof_; +}; + +} // namespace wabt + +#endif // WABT_LEXER_SOURCE_LINE_FINDER_H_ diff --git a/third_party/wasm2c/src/lexer-source.cc b/third_party/wasm2c/src/lexer-source.cc new file mode 100644 index 0000000000..896a52e938 --- /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 "src/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/lexer-source.h b/third_party/wasm2c/src/lexer-source.h new file mode 100644 index 0000000000..d49d5c6e71 --- /dev/null +++ b/third_party/wasm2c/src/lexer-source.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#ifndef WABT_LEXER_SOURCE_H_ +#define WABT_LEXER_SOURCE_H_ + +#include <cstddef> +#include <memory> +#include <string> +#include <vector> + +#include "src/common.h" +#include "src/range.h" + +namespace wabt { + +class LexerSource { + public: + LexerSource(const void* data, Offset size); + + std::unique_ptr<LexerSource> Clone(); + Result Tell(Offset* out_offset); + size_t Fill(void* dest, size_t size); + Result ReadRange(OffsetRange, std::vector<char>* out_data); + Result Seek(Offset offset); + + WABT_DISALLOW_COPY_AND_ASSIGN(LexerSource); + + const void* data() { return data_; } + Offset size() { return size_; } + + private: + const void* data_; + Offset size_; + Offset read_offset_; +}; + +} // namespace wabt + +#endif // WABT_LEXER_SOURCE_H_ diff --git a/third_party/wasm2c/src/literal.cc b/third_party/wasm2c/src/literal.cc new file mode 100644 index 0000000000..0061772e2d --- /dev/null +++ b/third_party/wasm2c/src/literal.cc @@ -0,0 +1,830 @@ +/* + * 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 "src/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> { + typedef uint32_t Uint; + 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> { + typedef uint64_t Uint; + 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> { + typedef typename FloatTraitsBase<T>::Uint 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: + typedef FloatTraits<T> Traits; + typedef typename Traits::Uint Uint; + typedef T Float; + + 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: + typedef FloatTraits<T> Traits; + typedef typename Traits::Uint 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); +} +} + +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) { + typedef typename std::make_signed<U>::type S; + 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/literal.h b/third_party/wasm2c/src/literal.h new file mode 100644 index 0000000000..b6982613db --- /dev/null +++ b/third_party/wasm2c/src/literal.h @@ -0,0 +1,84 @@ +/* + * 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_LITERAL_H_ +#define WABT_LITERAL_H_ + +#include <cstdint> + +#include "src/common.h" + +namespace wabt { + +// These functions all return Result::Ok on success and Result::Error on +// failure. +// +// NOTE: the functions are written for use with wast-lexer, assuming that the +// literal has already matched the patterns defined there. As a result, the +// only validation that is done is for overflow, not for otherwise bogus input. + +enum class LiteralType { + Int, + Float, + Hexfloat, + Infinity, + Nan, +}; + +enum class ParseIntType { + UnsignedOnly = 0, + SignedAndUnsigned = 1, +}; + +/* Size of char buffer required to hold hex representation of a float/double */ +#define WABT_MAX_FLOAT_HEX 20 +#define WABT_MAX_DOUBLE_HEX 40 + +Result ParseHexdigit(char c, uint32_t* out); +Result ParseInt8(const char* s, + const char* end, + uint8_t* out, + ParseIntType parse_type); +Result ParseInt16(const char* s, + const char* end, + uint16_t* out, + ParseIntType parse_type); +Result ParseInt32(const char* s, + const char* end, + uint32_t* out, + ParseIntType parse_type); +Result ParseInt64(const char* s, + const char* end, + uint64_t* out, + ParseIntType parse_type); +Result ParseUint64(const char* s, const char* end, uint64_t* out); +Result ParseUint128(const char* s, const char* end, v128* out); +Result ParseFloat(LiteralType literal_type, + const char* s, + const char* end, + uint32_t* out_bits); +Result ParseDouble(LiteralType literal_type, + const char* s, + const char* end, + uint64_t* out_bits); + +void WriteFloatHex(char* buffer, size_t size, uint32_t bits); +void WriteDoubleHex(char* buffer, size_t size, uint64_t bits); +void WriteUint128(char* buffer, size_t size, v128 bits); + +} // namespace wabt + +#endif /* WABT_LITERAL_H_ */ diff --git a/third_party/wasm2c/src/make-unique.h b/third_party/wasm2c/src/make-unique.h new file mode 100644 index 0000000000..46d3ac14a5 --- /dev/null +++ b/third_party/wasm2c/src/make-unique.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#ifndef WABT_MAKE_UNIQUE_H_ +#define WABT_MAKE_UNIQUE_H_ + +#include <memory> + +namespace wabt { + +// This is named MakeUnique instead of make_unique because make_unique has the +// potential to conflict with std::make_unique if it is defined. +// +// On gcc/clang, we currently compile with c++11, which doesn't define +// std::make_unique, but on MSVC the newest C++ version is always used, which +// includes std::make_unique. If an argument from the std namespace is used, it +// will cause ADL to find std::make_unique, and an unqualified call to +// make_unique will be ambiguous. We can work around this by fully qualifying +// the call (i.e. wabt::make_unique), but it's simpler to just use a different +// name. It's also more consistent with other names in the wabt namespace, +// which use CamelCase. +template <typename T, typename... Args> +std::unique_ptr<T> MakeUnique(Args&&... args) { + return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); +} + +} // namespace wabt + +#endif // WABT_MAKE_UNIQUE_H_ 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..c3e06d036e --- /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 "src/opcode-code-table.h" + +#include "config.h" + +#include <stdint.h> + +typedef enum WabtOpcodeEnum { +#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \ + text, decomp) \ + Name, +#include "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 << 8) + code] = Name, +#include "opcode.def" +#undef WABT_OPCODE +}; diff --git a/third_party/wasm2c/src/opcode-code-table.h b/third_party/wasm2c/src/opcode-code-table.h new file mode 100644 index 0000000000..b223e161da --- /dev/null +++ b/third_party/wasm2c/src/opcode-code-table.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#ifndef WABT_OPCODE_CODE_TABLE_H_ +#define WABT_OPCODE_CODE_TABLE_H_ + +#include <stdlib.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define WABT_OPCODE_CODE_TABLE_SIZE 65536 + +/* This structure is defined in C because C++ doesn't (yet) allow you to use + * designated array initializers, i.e. [10] = {foo}. + */ +extern uint32_t WabtOpcodeCodeTable[WABT_OPCODE_CODE_TABLE_SIZE]; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* WABT_OPCODE_CODE_TABLE_H_ */ diff --git a/third_party/wasm2c/src/opcode.cc b/third_party/wasm2c/src/opcode.cc new file mode 100644 index 0000000000..6a70eb4fab --- /dev/null +++ b/third_party/wasm2c/src/opcode.cc @@ -0,0 +1,407 @@ +/* + * 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 "src/opcode.h" + +#include "src/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 "src/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 "src/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::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/opcode.def b/third_party/wasm2c/src/opcode.def new file mode 100644 index 0000000000..ce80f895bd --- /dev/null +++ b/third_party/wasm2c/src/opcode.def @@ -0,0 +1,569 @@ +/* + * 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. + */ + +#ifndef WABT_OPCODE +#error "You must define WABT_OPCODE before including this file." +#endif + +/* *** NOTE *** This list must be kept sorted so it can be binary searched */ + +/* + * tr: result type + * t1: type of the 1st parameter + * t2: type of the 2nd parameter + * t3: type of the 3rd parameter + * m: memory size of the operation, if any + * prefix: the 1-byte opcode prefix, if any + * code: opcode + * Name: used to generate the opcode enum + * text: a string of the opcode name in the text format + * decomp: an optional friendly version of text, used for decompilation. + * + * tr t1 t2 t3 m prefix code Name text + * ========================================================== */ + +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x00, Unreachable, "unreachable", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x01, Nop, "nop", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x02, Block, "block", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x03, Loop, "loop", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x04, If, "if", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x05, Else, "else", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x06, Try, "try", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x07, Catch, "catch", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x08, Throw, "throw", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x09, Rethrow, "rethrow", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x0b, End, "end", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x0c, Br, "br", "") +WABT_OPCODE(___, I32, ___, ___, 0, 0, 0x0d, BrIf, "br_if", "") +WABT_OPCODE(___, I32, ___, ___, 0, 0, 0x0e, BrTable, "br_table", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x0f, Return, "return", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x10, Call, "call", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x11, CallIndirect, "call_indirect", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x12, ReturnCall, "return_call", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x13, ReturnCallIndirect, "return_call_indirect", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x14, CallRef, "call_ref", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x18, Delegate, "delegate", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x19, CatchAll, "catch_all", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x1a, Drop, "drop", "") +WABT_OPCODE(___, ___, ___, I32, 0, 0, 0x1b, Select, "select", "") +WABT_OPCODE(___, ___, ___, I32, 0, 0, 0x1c, SelectT, "select", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x20, LocalGet, "local.get", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x21, LocalSet, "local.set", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x22, LocalTee, "local.tee", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x23, GlobalGet, "global.get", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x24, GlobalSet, "global.set", "") +WABT_OPCODE(I32, I32, ___, ___, 4, 0, 0x28, I32Load, "i32.load", "") +WABT_OPCODE(I64, I32, ___, ___, 8, 0, 0x29, I64Load, "i64.load", "") +WABT_OPCODE(F32, I32, ___, ___, 4, 0, 0x2a, F32Load, "f32.load", "") +WABT_OPCODE(F64, I32, ___, ___, 8, 0, 0x2b, F64Load, "f64.load", "") +WABT_OPCODE(I32, I32, ___, ___, 1, 0, 0x2c, I32Load8S, "i32.load8_s", "") +WABT_OPCODE(I32, I32, ___, ___, 1, 0, 0x2d, I32Load8U, "i32.load8_u", "") +WABT_OPCODE(I32, I32, ___, ___, 2, 0, 0x2e, I32Load16S, "i32.load16_s", "") +WABT_OPCODE(I32, I32, ___, ___, 2, 0, 0x2f, I32Load16U, "i32.load16_u", "") +WABT_OPCODE(I64, I32, ___, ___, 1, 0, 0x30, I64Load8S, "i64.load8_s", "") +WABT_OPCODE(I64, I32, ___, ___, 1, 0, 0x31, I64Load8U, "i64.load8_u", "") +WABT_OPCODE(I64, I32, ___, ___, 2, 0, 0x32, I64Load16S, "i64.load16_s", "") +WABT_OPCODE(I64, I32, ___, ___, 2, 0, 0x33, I64Load16U, "i64.load16_u", "") +WABT_OPCODE(I64, I32, ___, ___, 4, 0, 0x34, I64Load32S, "i64.load32_s", "") +WABT_OPCODE(I64, I32, ___, ___, 4, 0, 0x35, I64Load32U, "i64.load32_u", "") +WABT_OPCODE(___, I32, I32, ___, 4, 0, 0x36, I32Store, "i32.store", "") +WABT_OPCODE(___, I32, I64, ___, 8, 0, 0x37, I64Store, "i64.store", "") +WABT_OPCODE(___, I32, F32, ___, 4, 0, 0x38, F32Store, "f32.store", "") +WABT_OPCODE(___, I32, F64, ___, 8, 0, 0x39, F64Store, "f64.store", "") +WABT_OPCODE(___, I32, I32, ___, 1, 0, 0x3a, I32Store8, "i32.store8", "") +WABT_OPCODE(___, I32, I32, ___, 2, 0, 0x3b, I32Store16, "i32.store16", "") +WABT_OPCODE(___, I32, I64, ___, 1, 0, 0x3c, I64Store8, "i64.store8", "") +WABT_OPCODE(___, I32, I64, ___, 2, 0, 0x3d, I64Store16, "i64.store16", "") +WABT_OPCODE(___, I32, I64, ___, 4, 0, 0x3e, I64Store32, "i64.store32", "") +WABT_OPCODE(I32, ___, ___, ___, 0, 0, 0x3f, MemorySize, "memory.size", "") +WABT_OPCODE(I32, I32, ___, ___, 0, 0, 0x40, MemoryGrow, "memory.grow", "") +WABT_OPCODE(I32, ___, ___, ___, 0, 0, 0x41, I32Const, "i32.const", "") +WABT_OPCODE(I64, ___, ___, ___, 0, 0, 0x42, I64Const, "i64.const", "") +WABT_OPCODE(F32, ___, ___, ___, 0, 0, 0x43, F32Const, "f32.const", "") +WABT_OPCODE(F64, ___, ___, ___, 0, 0, 0x44, F64Const, "f64.const", "") +WABT_OPCODE(I32, I32, ___, ___, 0, 0, 0x45, I32Eqz, "i32.eqz", "eqz") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x46, I32Eq, "i32.eq", "==") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x47, I32Ne, "i32.ne", "!=") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x48, I32LtS, "i32.lt_s", "<") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x49, I32LtU, "i32.lt_u", "<") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x4a, I32GtS, "i32.gt_s", ">") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x4b, I32GtU, "i32.gt_u", ">") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x4c, I32LeS, "i32.le_s", "<=") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x4d, I32LeU, "i32.le_u", "<=") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x4e, I32GeS, "i32.ge_s", ">=") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x4f, I32GeU, "i32.ge_u", ">=") +WABT_OPCODE(I32, I64, ___, ___, 0, 0, 0x50, I64Eqz, "i64.eqz", "eqz") +WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x51, I64Eq, "i64.eq", "==") +WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x52, I64Ne, "i64.ne", "!=") +WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x53, I64LtS, "i64.lt_s", "<") +WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x54, I64LtU, "i64.lt_u", "<") +WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x55, I64GtS, "i64.gt_s", ">") +WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x56, I64GtU, "i64.gt_u", ">") +WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x57, I64LeS, "i64.le_s", "<=") +WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x58, I64LeU, "i64.le_u", "<=") +WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x59, I64GeS, "i64.ge_s", ">=") +WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x5a, I64GeU, "i64.ge_u", ">=") +WABT_OPCODE(I32, F32, F32, ___, 0, 0, 0x5b, F32Eq, "f32.eq", "==") +WABT_OPCODE(I32, F32, F32, ___, 0, 0, 0x5c, F32Ne, "f32.ne", "!=") +WABT_OPCODE(I32, F32, F32, ___, 0, 0, 0x5d, F32Lt, "f32.lt", "<") +WABT_OPCODE(I32, F32, F32, ___, 0, 0, 0x5e, F32Gt, "f32.gt", ">") +WABT_OPCODE(I32, F32, F32, ___, 0, 0, 0x5f, F32Le, "f32.le", "<=") +WABT_OPCODE(I32, F32, F32, ___, 0, 0, 0x60, F32Ge, "f32.ge", ">=") +WABT_OPCODE(I32, F64, F64, ___, 0, 0, 0x61, F64Eq, "f64.eq", "==") +WABT_OPCODE(I32, F64, F64, ___, 0, 0, 0x62, F64Ne, "f64.ne", "!=") +WABT_OPCODE(I32, F64, F64, ___, 0, 0, 0x63, F64Lt, "f64.lt", "<") +WABT_OPCODE(I32, F64, F64, ___, 0, 0, 0x64, F64Gt, "f64.gt", ">") +WABT_OPCODE(I32, F64, F64, ___, 0, 0, 0x65, F64Le, "f64.le", "<=") +WABT_OPCODE(I32, F64, F64, ___, 0, 0, 0x66, F64Ge, "f64.ge", ">=") +WABT_OPCODE(I32, I32, ___, ___, 0, 0, 0x67, I32Clz, "i32.clz", "clz") +WABT_OPCODE(I32, I32, ___, ___, 0, 0, 0x68, I32Ctz, "i32.ctz", "ctz") +WABT_OPCODE(I32, I32, ___, ___, 0, 0, 0x69, I32Popcnt, "i32.popcnt", "popcnt") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x6a, I32Add, "i32.add", "+") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x6b, I32Sub, "i32.sub", "-") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x6c, I32Mul, "i32.mul", "*") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x6d, I32DivS, "i32.div_s", "/") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x6e, I32DivU, "i32.div_u", "/") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x6f, I32RemS, "i32.rem_s", "%") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x70, I32RemU, "i32.rem_u", "%") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x71, I32And, "i32.and", "&") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x72, I32Or, "i32.or", "|") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x73, I32Xor, "i32.xor", "^") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x74, I32Shl, "i32.shl", "<<") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x75, I32ShrS, "i32.shr_s", ">>") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x76, I32ShrU, "i32.shr_u", ">>") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x77, I32Rotl, "i32.rotl", "<<") +WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x78, I32Rotr, "i32.rotr", ">>") +WABT_OPCODE(I64, I64, ___, ___, 0, 0, 0x79, I64Clz, "i64.clz", "clz") +WABT_OPCODE(I64, I64, ___, ___, 0, 0, 0x7a, I64Ctz, "i64.ctz", "ctz") +WABT_OPCODE(I64, I64, ___, ___, 0, 0, 0x7b, I64Popcnt, "i64.popcnt", "popcnt") +WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x7c, I64Add, "i64.add", "+") +WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x7d, I64Sub, "i64.sub", "-") +WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x7e, I64Mul, "i64.mul", "*") +WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x7f, I64DivS, "i64.div_s", "/") +WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x80, I64DivU, "i64.div_u", "/") +WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x81, I64RemS, "i64.rem_s", "%") +WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x82, I64RemU, "i64.rem_u", "%") +WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x83, I64And, "i64.and", "&") +WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x84, I64Or, "i64.or", "|") +WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x85, I64Xor, "i64.xor", "^") +WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x86, I64Shl, "i64.shl", "<<") +WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x87, I64ShrS, "i64.shr_s", ">>") +WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x88, I64ShrU, "i64.shr_u", ">>") +WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x89, I64Rotl, "i64.rotl", "<<") +WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x8a, I64Rotr, "i64.rotr", ">>") +WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x8b, F32Abs, "f32.abs", "abs") +WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x8c, F32Neg, "f32.neg", "-") +WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x8d, F32Ceil, "f32.ceil", "ceil") +WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x8e, F32Floor, "f32.floor", "floor") +WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x8f, F32Trunc, "f32.trunc", "trunc") +WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x90, F32Nearest, "f32.nearest", "nearest") +WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x91, F32Sqrt, "f32.sqrt", "sqrt") +WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x92, F32Add, "f32.add", "+") +WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x93, F32Sub, "f32.sub", "-") +WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x94, F32Mul, "f32.mul", "*") +WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x95, F32Div, "f32.div", "/") +WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x96, F32Min, "f32.min", "min") +WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x97, F32Max, "f32.max", "max") +WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x98, F32Copysign, "f32.copysign", "copysign") +WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0x99, F64Abs, "f64.abs", "abs") +WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0x9a, F64Neg, "f64.neg", "-") +WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0x9b, F64Ceil, "f64.ceil", "ceil") +WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0x9c, F64Floor, "f64.floor", "floor") +WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0x9d, F64Trunc, "f64.trunc", "trunc") +WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0x9e, F64Nearest, "f64.nearest", "nearest") +WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0x9f, F64Sqrt, "f64.sqrt", "sqrt") +WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0xa0, F64Add, "f64.add", "+") +WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0xa1, F64Sub, "f64.sub", "-") +WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0xa2, F64Mul, "f64.mul", "*") +WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0xa3, F64Div, "f64.div", "/") +WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0xa4, F64Min, "f64.min", "min") +WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0xa5, F64Max, "f64.max", "max") +WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0xa6, F64Copysign, "f64.copysign", "copysign") +WABT_OPCODE(I32, I64, ___, ___, 0, 0, 0xa7, I32WrapI64, "i32.wrap_i64", "") +WABT_OPCODE(I32, F32, ___, ___, 0, 0, 0xa8, I32TruncF32S, "i32.trunc_f32_s", "") +WABT_OPCODE(I32, F32, ___, ___, 0, 0, 0xa9, I32TruncF32U, "i32.trunc_f32_u", "") +WABT_OPCODE(I32, F64, ___, ___, 0, 0, 0xaa, I32TruncF64S, "i32.trunc_f64_s", "") +WABT_OPCODE(I32, F64, ___, ___, 0, 0, 0xab, I32TruncF64U, "i32.trunc_f64_u", "") +WABT_OPCODE(I64, I32, ___, ___, 0, 0, 0xac, I64ExtendI32S, "i64.extend_i32_s", "") +WABT_OPCODE(I64, I32, ___, ___, 0, 0, 0xad, I64ExtendI32U, "i64.extend_i32_u", "") +WABT_OPCODE(I64, F32, ___, ___, 0, 0, 0xae, I64TruncF32S, "i64.trunc_f32_s", "") +WABT_OPCODE(I64, F32, ___, ___, 0, 0, 0xaf, I64TruncF32U, "i64.trunc_f32_u", "") +WABT_OPCODE(I64, F64, ___, ___, 0, 0, 0xb0, I64TruncF64S, "i64.trunc_f64_s", "") +WABT_OPCODE(I64, F64, ___, ___, 0, 0, 0xb1, I64TruncF64U, "i64.trunc_f64_u", "") +WABT_OPCODE(F32, I32, ___, ___, 0, 0, 0xb2, F32ConvertI32S, "f32.convert_i32_s", "") +WABT_OPCODE(F32, I32, ___, ___, 0, 0, 0xb3, F32ConvertI32U, "f32.convert_i32_u", "") +WABT_OPCODE(F32, I64, ___, ___, 0, 0, 0xb4, F32ConvertI64S, "f32.convert_i64_s", "") +WABT_OPCODE(F32, I64, ___, ___, 0, 0, 0xb5, F32ConvertI64U, "f32.convert_i64_u", "") +WABT_OPCODE(F32, F64, ___, ___, 0, 0, 0xb6, F32DemoteF64, "f32.demote_f64", "") +WABT_OPCODE(F64, I32, ___, ___, 0, 0, 0xb7, F64ConvertI32S, "f64.convert_i32_s", "") +WABT_OPCODE(F64, I32, ___, ___, 0, 0, 0xb8, F64ConvertI32U, "f64.convert_i32_u", "") +WABT_OPCODE(F64, I64, ___, ___, 0, 0, 0xb9, F64ConvertI64S, "f64.convert_i64_s", "") +WABT_OPCODE(F64, I64, ___, ___, 0, 0, 0xba, F64ConvertI64U, "f64.convert_i64_u", "") +WABT_OPCODE(F64, F32, ___, ___, 0, 0, 0xbb, F64PromoteF32, "f64.promote_f32", "") +WABT_OPCODE(I32, F32, ___, ___, 0, 0, 0xbc, I32ReinterpretF32, "i32.reinterpret_f32", "") +WABT_OPCODE(I64, F64, ___, ___, 0, 0, 0xbd, I64ReinterpretF64, "i64.reinterpret_f64", "") +WABT_OPCODE(F32, I32, ___, ___, 0, 0, 0xbe, F32ReinterpretI32, "f32.reinterpret_i32", "") +WABT_OPCODE(F64, I64, ___, ___, 0, 0, 0xbf, F64ReinterpretI64, "f64.reinterpret_i64", "") + +/* Sign-extension opcodes (--enable-sign-extension) */ +WABT_OPCODE(I32, I32, ___, ___, 0, 0, 0xC0, I32Extend8S, "i32.extend8_s", "") +WABT_OPCODE(I32, I32, ___, ___, 0, 0, 0xC1, I32Extend16S, "i32.extend16_s", "") +WABT_OPCODE(I64, I64, ___, ___, 0, 0, 0xC2, I64Extend8S, "i64.extend8_s", "") +WABT_OPCODE(I64, I64, ___, ___, 0, 0, 0xC3, I64Extend16S, "i64.extend16_s", "") +WABT_OPCODE(I64, I64, ___, ___, 0, 0, 0xC4, I64Extend32S, "i64.extend32_s", "") + +/* Interpreter-only opcodes */ +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe0, InterpAlloca, "alloca", "") +WABT_OPCODE(___, I32, ___, ___, 0, 0, 0xe1, InterpBrUnless, "br_unless", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe2, InterpCallImport, "call_import", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe3, InterpData, "data", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe4, InterpDropKeep, "drop_keep", "") + +/* Saturating float-to-int opcodes (--enable-saturating-float-to-int) */ +WABT_OPCODE(I32, F32, ___, ___, 0, 0xfc, 0x00, I32TruncSatF32S, "i32.trunc_sat_f32_s", "") +WABT_OPCODE(I32, F32, ___, ___, 0, 0xfc, 0x01, I32TruncSatF32U, "i32.trunc_sat_f32_u", "") +WABT_OPCODE(I32, F64, ___, ___, 0, 0xfc, 0x02, I32TruncSatF64S, "i32.trunc_sat_f64_s", "") +WABT_OPCODE(I32, F64, ___, ___, 0, 0xfc, 0x03, I32TruncSatF64U, "i32.trunc_sat_f64_u", "") +WABT_OPCODE(I64, F32, ___, ___, 0, 0xfc, 0x04, I64TruncSatF32S, "i64.trunc_sat_f32_s", "") +WABT_OPCODE(I64, F32, ___, ___, 0, 0xfc, 0x05, I64TruncSatF32U, "i64.trunc_sat_f32_u", "") +WABT_OPCODE(I64, F64, ___, ___, 0, 0xfc, 0x06, I64TruncSatF64S, "i64.trunc_sat_f64_s", "") +WABT_OPCODE(I64, F64, ___, ___, 0, 0xfc, 0x07, I64TruncSatF64U, "i64.trunc_sat_f64_u", "") + +/* Bulk-memory (--enable-bulk-memory) */ +WABT_OPCODE(___, I32, I32, I32, 0, 0xfc, 0x08, MemoryInit, "memory.init", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0xfc, 0x09, DataDrop, "data.drop", "") +WABT_OPCODE(___, I32, I32, I32, 0, 0xfc, 0x0a, MemoryCopy,"memory.copy", "") +WABT_OPCODE(___, I32, I32, I32, 0, 0xfc, 0x0b, MemoryFill, "memory.fill", "") +WABT_OPCODE(___, I32, I32, I32, 0, 0xfc, 0x0c, TableInit, "table.init", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0xfc, 0x0d, ElemDrop, "elem.drop", "") +WABT_OPCODE(___, I32, I32, I32, 0, 0xfc, 0x0e, TableCopy, "table.copy", "") + +/* Reference types (--enable-reference-types) */ +WABT_OPCODE(___, I32, ___, ___, 0, 0, 0x25, TableGet, "table.get", "") +WABT_OPCODE(___, I32, ___, ___, 0, 0, 0x26, TableSet, "table.set", "") +WABT_OPCODE(___, ___, I32, ___, 0, 0xfc, 0x0f, TableGrow, "table.grow", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0xfc, 0x10, TableSize, "table.size", "") +WABT_OPCODE(___, I32, ___, I32, 0, 0xfc, 0x11, TableFill, "table.fill", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xd0, RefNull, "ref.null", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xd1, RefIsNull, "ref.is_null", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xd2, RefFunc, "ref.func", "") + +/* Simd opcodes */ +WABT_OPCODE(V128, I32, ___, ___, 16, 0xfd, 0x00, V128Load, "v128.load", "") +WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x01, V128Load8X8S, "v128.load8x8_s", "") +WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x02, V128Load8X8U, "v128.load8x8_u", "") +WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x03, V128Load16X4S, "v128.load16x4_s", "") +WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x04, V128Load16X4U, "v128.load16x4_u", "") +WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x05, V128Load32X2S, "v128.load32x2_s", "") +WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x06, V128Load32X2U, "v128.load32x2_u", "") +WABT_OPCODE(V128, I32, ___, ___, 1, 0xfd, 0x07, V128Load8Splat, "v128.load8_splat", "") +WABT_OPCODE(V128, I32, ___, ___, 2, 0xfd, 0x08, V128Load16Splat, "v128.load16_splat", "") +WABT_OPCODE(V128, I32, ___, ___, 4, 0xfd, 0x09, V128Load32Splat, "v128.load32_splat", "") +WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x0a, V128Load64Splat, "v128.load64_splat", "") +WABT_OPCODE(___, I32, V128, ___, 16, 0xfd, 0x0b, V128Store, "v128.store", "") +WABT_OPCODE(V128, ___, ___, ___, 0, 0xfd, 0x0c, V128Const, "v128.const", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x0d, I8X16Shuffle, "i8x16.shuffle", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x0e, I8X16Swizzle, "i8x16.swizzle", "") +WABT_OPCODE(V128, I32, ___, ___, 0, 0xfd, 0x0f, I8X16Splat, "i8x16.splat", "") +WABT_OPCODE(V128, I32, ___, ___, 0, 0xfd, 0x10, I16X8Splat, "i16x8.splat", "") +WABT_OPCODE(V128, I32, ___, ___, 0, 0xfd, 0x11, I32X4Splat, "i32x4.splat", "") +WABT_OPCODE(V128, I64, ___, ___, 0, 0xfd, 0x12, I64X2Splat, "i64x2.splat", "") +WABT_OPCODE(V128, F32, ___, ___, 0, 0xfd, 0x13, F32X4Splat, "f32x4.splat", "") +WABT_OPCODE(V128, F64, ___, ___, 0, 0xfd, 0x14, F64X2Splat, "f64x2.splat", "") +WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x15, I8X16ExtractLaneS, "i8x16.extract_lane_s", "") +WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x16, I8X16ExtractLaneU, "i8x16.extract_lane_u", "") +WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0x17, I8X16ReplaceLane, "i8x16.replace_lane", "") +WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x18, I16X8ExtractLaneS, "i16x8.extract_lane_s", "") +WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x19, I16X8ExtractLaneU, "i16x8.extract_lane_u", "") +WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0x1a, I16X8ReplaceLane, "i16x8.replace_lane", "") +WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x1b, I32X4ExtractLane, "i32x4.extract_lane", "") +WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0x1c, I32X4ReplaceLane, "i32x4.replace_lane", "") +WABT_OPCODE(I64, V128, ___, ___, 0, 0xfd, 0x1d, I64X2ExtractLane, "i64x2.extract_lane", "") +WABT_OPCODE(V128, V128, I64, ___, 0, 0xfd, 0x1e, I64X2ReplaceLane, "i64x2.replace_lane", "") +WABT_OPCODE(F32, V128, ___, ___, 0, 0xfd, 0x1f, F32X4ExtractLane, "f32x4.extract_lane", "") +WABT_OPCODE(V128, V128, F32, ___, 0, 0xfd, 0x20, F32X4ReplaceLane, "f32x4.replace_lane", "") +WABT_OPCODE(F64, V128, ___, ___, 0, 0xfd, 0x21, F64X2ExtractLane, "f64x2.extract_lane", "") +WABT_OPCODE(V128, V128, F64, ___, 0, 0xfd, 0x22, F64X2ReplaceLane, "f64x2.replace_lane", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x23, I8X16Eq, "i8x16.eq", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x24, I8X16Ne, "i8x16.ne", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x25, I8X16LtS, "i8x16.lt_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x26, I8X16LtU, "i8x16.lt_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x27, I8X16GtS, "i8x16.gt_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x28, I8X16GtU, "i8x16.gt_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x29, I8X16LeS, "i8x16.le_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x2a, I8X16LeU, "i8x16.le_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x2b, I8X16GeS, "i8x16.ge_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x2c, I8X16GeU, "i8x16.ge_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x2d, I16X8Eq, "i16x8.eq", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x2e, I16X8Ne, "i16x8.ne", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x2f, I16X8LtS, "i16x8.lt_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x30, I16X8LtU, "i16x8.lt_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x31, I16X8GtS, "i16x8.gt_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x32, I16X8GtU, "i16x8.gt_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x33, I16X8LeS, "i16x8.le_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x34, I16X8LeU, "i16x8.le_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x35, I16X8GeS, "i16x8.ge_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x36, I16X8GeU, "i16x8.ge_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x37, I32X4Eq, "i32x4.eq", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x38, I32X4Ne, "i32x4.ne", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x39, I32X4LtS, "i32x4.lt_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x3a, I32X4LtU, "i32x4.lt_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x3b, I32X4GtS, "i32x4.gt_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x3c, I32X4GtU, "i32x4.gt_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x3d, I32X4LeS, "i32x4.le_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x3e, I32X4LeU, "i32x4.le_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x3f, I32X4GeS, "i32x4.ge_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x40, I32X4GeU, "i32x4.ge_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x41, F32X4Eq, "f32x4.eq", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x42, F32X4Ne, "f32x4.ne", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x43, F32X4Lt, "f32x4.lt", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x44, F32X4Gt, "f32x4.gt", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x45, F32X4Le, "f32x4.le", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x46, F32X4Ge, "f32x4.ge", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x47, F64X2Eq, "f64x2.eq", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x48, F64X2Ne, "f64x2.ne", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x49, F64X2Lt, "f64x2.lt", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x4a, F64X2Gt, "f64x2.gt", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x4b, F64X2Le, "f64x2.le", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x4c, F64X2Ge, "f64x2.ge", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x4d, V128Not, "v128.not", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x4e, V128And, "v128.and", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x4f, V128Andnot, "v128.andnot", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x50, V128Or, "v128.or", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x51, V128Xor, "v128.xor", "") +WABT_OPCODE(V128, V128, V128, V128, 0, 0xfd, 0x52, V128BitSelect, "v128.bitselect", "") +WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x53, V128AnyTrue, "v128.any_true", "") +WABT_OPCODE(V128, I32, V128, ___, 1, 0xfd, 0x54, V128Load8Lane, "v128.load8_lane", "") +WABT_OPCODE(V128, I32, V128, ___, 2, 0xfd, 0x55, V128Load16Lane, "v128.load16_lane", "") +WABT_OPCODE(V128, I32, V128, ___, 4, 0xfd, 0x56, V128Load32Lane, "v128.load32_lane", "") +WABT_OPCODE(V128, I32, V128, ___, 8, 0xfd, 0x57, V128Load64Lane, "v128.load64_lane", "") +WABT_OPCODE(___, I32, V128, ___, 1, 0xfd, 0x58, V128Store8Lane, "v128.store8_lane", "") +WABT_OPCODE(___, I32, V128, ___, 2, 0xfd, 0x59, V128Store16Lane, "v128.store16_lane", "") +WABT_OPCODE(___, I32, V128, ___, 4, 0xfd, 0x5a, V128Store32Lane, "v128.store32_lane", "") +WABT_OPCODE(___, I32, V128, ___, 8, 0xfd, 0x5b, V128Store64Lane, "v128.store64_lane", "") +WABT_OPCODE(V128, I32, ___, ___, 4, 0xfd, 0x5c, V128Load32Zero, "v128.load32_zero", "") +WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x5d, V128Load64Zero, "v128.load64_zero", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x5e, F32X4DemoteF64X2Zero, "f32x4.demote_f64x2_zero", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x5f, F64X2PromoteLowF32X4, "f64x2.promote_low_f32x4", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x60, I8X16Abs, "i8x16.abs", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x61, I8X16Neg, "i8x16.neg", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x62, I8X16Popcnt, "i8x16.popcnt", "") +WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x63, I8X16AllTrue, "i8x16.all_true", "") +WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x64, I8X16Bitmask, "i8x16.bitmask", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x65, I8X16NarrowI16X8S, "i8x16.narrow_i16x8_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x66, I8X16NarrowI16X8U, "i8x16.narrow_i16x8_u", "") +WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0x6b, I8X16Shl, "i8x16.shl", "") +WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0x6c, I8X16ShrS, "i8x16.shr_s", "") +WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0x6d, I8X16ShrU, "i8x16.shr_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x6e, I8X16Add, "i8x16.add", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x6f, I8X16AddSatS, "i8x16.add_sat_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x70, I8X16AddSatU, "i8x16.add_sat_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x71, I8X16Sub, "i8x16.sub", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x72, I8X16SubSatS, "i8x16.sub_sat_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x73, I8X16SubSatU, "i8x16.sub_sat_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x76, I8X16MinS, "i8x16.min_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x77, I8X16MinU, "i8x16.min_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x78, I8X16MaxS, "i8x16.max_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x79, I8X16MaxU, "i8x16.max_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x7b, I8X16AvgrU, "i8x16.avgr_u", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x7c, I16X8ExtaddPairwiseI8X16S, "i16x8.extadd_pairwise_i8x16_s", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x7d, I16X8ExtaddPairwiseI8X16U, "i16x8.extadd_pairwise_i8x16_u", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x7e, I32X4ExtaddPairwiseI16X8S, "i32x4.extadd_pairwise_i16x8_s", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x7f, I32X4ExtaddPairwiseI16X8U, "i32x4.extadd_pairwise_i16x8_u", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x80, I16X8Abs, "i16x8.abs", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x81, I16X8Neg, "i16x8.neg", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x82, I16X8Q15mulrSatS, "i16x8.q15mulr_sat_s", "") +WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x83, I16X8AllTrue, "i16x8.all_true", "") +WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x84, I16X8Bitmask, "i16x8.bitmask", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x85, I16X8NarrowI32X4S, "i16x8.narrow_i32x4_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x86, I16X8NarrowI32X4U, "i16x8.narrow_i32x4_u", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x87, I16X8ExtendLowI8X16S, "i16x8.extend_low_i8x16_s", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x88, I16X8ExtendHighI8X16S, "i16x8.extend_high_i8x16_s", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x89, I16X8ExtendLowI8X16U, "i16x8.extend_low_i8x16_u", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x8a, I16X8ExtendHighI8X16U, "i16x8.extend_high_i8x16_u", "") +WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0x8b, I16X8Shl, "i16x8.shl", "") +WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0x8c, I16X8ShrS, "i16x8.shr_s", "") +WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0x8d, I16X8ShrU, "i16x8.shr_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x8e, I16X8Add, "i16x8.add", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x8f, I16X8AddSatS, "i16x8.add_sat_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x90, I16X8AddSatU, "i16x8.add_sat_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x91, I16X8Sub, "i16x8.sub", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x92, I16X8SubSatS, "i16x8.sub_sat_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x93, I16X8SubSatU, "i16x8.sub_sat_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x95, I16X8Mul, "i16x8.mul", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x96, I16X8MinS, "i16x8.min_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x97, I16X8MinU, "i16x8.min_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x98, I16X8MaxS, "i16x8.max_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x99, I16X8MaxU, "i16x8.max_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x9b, I16X8AvgrU, "i16x8.avgr_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x9c, I16X8ExtmulLowI8X16S, "i16x8.extmul_low_i8x16_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x9d, I16X8ExtmulHighI8X16S, "i16x8.extmul_high_i8x16_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x9e, I16X8ExtmulLowI8X16U, "i16x8.extmul_low_i8x16_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x9f, I16X8ExtmulHighI8X16U, "i16x8.extmul_high_i8x16_u", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xa0, I32X4Abs, "i32x4.abs", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xa1, I32X4Neg, "i32x4.neg", "") +WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0xa3, I32X4AllTrue, "i32x4.all_true", "") +WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0xa4, I32X4Bitmask, "i32x4.bitmask", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xa7, I32X4ExtendLowI16X8S, "i32x4.extend_low_i16x8_s", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xa8, I32X4ExtendHighI16X8S, "i32x4.extend_high_i16x8_s", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xa9, I32X4ExtendLowI16X8U, "i32x4.extend_low_i16x8_u", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xaa, I32X4ExtendHighI16X8U, "i32x4.extend_high_i16x8_u", "") +WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0xab, I32X4Shl, "i32x4.shl", "") +WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0xac, I32X4ShrS, "i32x4.shr_s", "") +WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0xad, I32X4ShrU, "i32x4.shr_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xae, I32X4Add, "i32x4.add", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xb1, I32X4Sub, "i32x4.sub", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xb5, I32X4Mul, "i32x4.mul", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xb6, I32X4MinS, "i32x4.min_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xb7, I32X4MinU, "i32x4.min_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xb8, I32X4MaxS, "i32x4.max_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xb9, I32X4MaxU, "i32x4.max_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xba, I32X4DotI16X8S, "i32x4.dot_i16x8_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xbc, I32X4ExtmulLowI16X8S, "i32x4.extmul_low_i16x8_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xbd, I32X4ExtmulHighI16X8S, "i32x4.extmul_high_i16x8_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xbe, I32X4ExtmulLowI16X8U, "i32x4.extmul_low_i16x8_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xbf, I32X4ExtmulHighI16X8U, "i32x4.extmul_high_i16x8_u", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xc0, I64X2Abs, "i64x2.abs", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xc1, I64X2Neg, "i64x2.neg", "") +WABT_OPCODE( I32, V128, ___, ___, 0, 0xfd, 0xc3, I64X2AllTrue, "i64x2.all_true", "") +WABT_OPCODE( I32, V128, ___, ___, 0, 0xfd, 0xc4, I64X2Bitmask, "i64x2.bitmask", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xc7, I64X2ExtendLowI32X4S, "i64x2.extend_low_i32x4_s", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xc8, I64X2ExtendHighI32X4S, "i64x2.extend_high_i32x4_s", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xc9, I64X2ExtendLowI32X4U, "i64x2.extend_low_i32x4_u", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xca, I64X2ExtendHighI32X4U, "i64x2.extend_high_i32x4_u", "") +WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0xcb, I64X2Shl, "i64x2.shl", "") +WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0xcc, I64X2ShrS, "i64x2.shr_s", "") +WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0xcd, I64X2ShrU, "i64x2.shr_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xce, I64X2Add, "i64x2.add", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xd1, I64X2Sub, "i64x2.sub", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xd5, I64X2Mul, "i64x2.mul", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xd6, I64X2Eq, "i64x2.eq", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xd7, I64X2Ne, "i64x2.ne", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xd8, I64X2LtS, "i64x2.lt_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xd9, I64X2GtS, "i64x2.gt_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xda, I64X2LeS, "i64x2.le_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xdb, I64X2GeS, "i64x2.ge_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xdc, I64X2ExtmulLowI32X4S, "i64x2.extmul_low_i32x4_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xdd, I64X2ExtmulHighI32X4S, "i64x2.extmul_high_i32x4_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xde, I64X2ExtmulLowI32X4U, "i64x2.extmul_low_i32x4_u", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xdf, I64X2ExtmulHighI32X4U, "i64x2.extmul_high_i32x4_u", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x67, F32X4Ceil, "f32x4.ceil", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x68, F32X4Floor, "f32x4.floor", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x69, F32X4Trunc, "f32x4.trunc", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x6a, F32X4Nearest, "f32x4.nearest", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x74, F64X2Ceil, "f64x2.ceil", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x75, F64X2Floor, "f64x2.floor", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x7a, F64X2Trunc, "f64x2.trunc", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x94, F64X2Nearest, "f64x2.nearest", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xe0, F32X4Abs, "f32x4.abs", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xe1, F32X4Neg, "f32x4.neg", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xe3, F32X4Sqrt, "f32x4.sqrt", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xe4, F32X4Add, "f32x4.add", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xe5, F32X4Sub, "f32x4.sub", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xe6, F32X4Mul, "f32x4.mul", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xe7, F32X4Div, "f32x4.div", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xe8, F32X4Min, "f32x4.min", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xe9, F32X4Max, "f32x4.max", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xea, F32X4PMin, "f32x4.pmin", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xeb, F32X4PMax, "f32x4.pmax", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xec, F64X2Abs, "f64x2.abs", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xed, F64X2Neg, "f64x2.neg", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xef, F64X2Sqrt, "f64x2.sqrt", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xf0, F64X2Add, "f64x2.add", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xf1, F64X2Sub, "f64x2.sub", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xf2, F64X2Mul, "f64x2.mul", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xf3, F64X2Div, "f64x2.div", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xf4, F64X2Min, "f64x2.min", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xf5, F64X2Max, "f64x2.max", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xf6, F64X2PMin, "f64x2.pmin", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xf7, F64X2PMax, "f64x2.pmax", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xf8, I32X4TruncSatF32X4S,"i32x4.trunc_sat_f32x4_s", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xf9, I32X4TruncSatF32X4U,"i32x4.trunc_sat_f32x4_u", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xfa, F32X4ConvertI32X4S, "f32x4.convert_i32x4_s", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xfb, F32X4ConvertI32X4U, "f32x4.convert_i32x4_u", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xfc, I32X4TruncSatF64X2SZero, "i32x4.trunc_sat_f64x2_s_zero", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xfd, I32X4TruncSatF64X2UZero, "i32x4.trunc_sat_f64x2_u_zero", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xfe, F64X2ConvertLowI32X4S, "f64x2.convert_low_i32x4_s", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xff, F64X2ConvertLowI32X4U, "f64x2.convert_low_i32x4_u", "") + +/* Thread opcodes (--enable-threads) */ +WABT_OPCODE(I32, I32, I32, ___, 4, 0xfe, 0x00, MemoryAtomicNotify, "memory.atomic.notify", "") +WABT_OPCODE(I32, I32, I32, I64, 4, 0xfe, 0x01, MemoryAtomicWait32, "memory.atomic.wait32", "") +WABT_OPCODE(I32, I32, I64, I64, 8, 0xfe, 0x02, MemoryAtomicWait64, "memory.atomic.wait64", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0xfe, 0x03, AtomicFence, "atomic.fence", "") +WABT_OPCODE(I32, I32, ___, ___, 4, 0xfe, 0x10, I32AtomicLoad, "i32.atomic.load", "") +WABT_OPCODE(I64, I32, ___, ___, 8, 0xfe, 0x11, I64AtomicLoad, "i64.atomic.load", "") +WABT_OPCODE(I32, I32, ___, ___, 1, 0xfe, 0x12, I32AtomicLoad8U, "i32.atomic.load8_u", "") +WABT_OPCODE(I32, I32, ___, ___, 2, 0xfe, 0x13, I32AtomicLoad16U, "i32.atomic.load16_u", "") +WABT_OPCODE(I64, I32, ___, ___, 1, 0xfe, 0x14, I64AtomicLoad8U, "i64.atomic.load8_u", "") +WABT_OPCODE(I64, I32, ___, ___, 2, 0xfe, 0x15, I64AtomicLoad16U, "i64.atomic.load16_u", "") +WABT_OPCODE(I64, I32, ___, ___, 4, 0xfe, 0x16, I64AtomicLoad32U, "i64.atomic.load32_u", "") +WABT_OPCODE(___, I32, I32, ___, 4, 0xfe, 0x17, I32AtomicStore, "i32.atomic.store", "") +WABT_OPCODE(___, I32, I64, ___, 8, 0xfe, 0x18, I64AtomicStore, "i64.atomic.store", "") +WABT_OPCODE(___, I32, I32, ___, 1, 0xfe, 0x19, I32AtomicStore8, "i32.atomic.store8", "") +WABT_OPCODE(___, I32, I32, ___, 2, 0xfe, 0x1a, I32AtomicStore16, "i32.atomic.store16", "") +WABT_OPCODE(___, I32, I64, ___, 1, 0xfe, 0x1b, I64AtomicStore8, "i64.atomic.store8", "") +WABT_OPCODE(___, I32, I64, ___, 2, 0xfe, 0x1c, I64AtomicStore16, "i64.atomic.store16", "") +WABT_OPCODE(___, I32, I64, ___, 4, 0xfe, 0x1d, I64AtomicStore32, "i64.atomic.store32", "") +WABT_OPCODE(I32, I32, I32, ___, 4, 0xfe, 0x1e, I32AtomicRmwAdd, "i32.atomic.rmw.add", "") +WABT_OPCODE(I64, I32, I64, ___, 8, 0xfe, 0x1f, I64AtomicRmwAdd, "i64.atomic.rmw.add", "") +WABT_OPCODE(I32, I32, I32, ___, 1, 0xfe, 0x20, I32AtomicRmw8AddU, "i32.atomic.rmw8.add_u", "") +WABT_OPCODE(I32, I32, I32, ___, 2, 0xfe, 0x21, I32AtomicRmw16AddU, "i32.atomic.rmw16.add_u", "") +WABT_OPCODE(I64, I32, I64, ___, 1, 0xfe, 0x22, I64AtomicRmw8AddU, "i64.atomic.rmw8.add_u", "") +WABT_OPCODE(I64, I32, I64, ___, 2, 0xfe, 0x23, I64AtomicRmw16AddU, "i64.atomic.rmw16.add_u", "") +WABT_OPCODE(I64, I32, I64, ___, 4, 0xfe, 0x24, I64AtomicRmw32AddU, "i64.atomic.rmw32.add_u", "") +WABT_OPCODE(I32, I32, I32, ___, 4, 0xfe, 0x25, I32AtomicRmwSub, "i32.atomic.rmw.sub", "") +WABT_OPCODE(I64, I32, I64, ___, 8, 0xfe, 0x26, I64AtomicRmwSub, "i64.atomic.rmw.sub", "") +WABT_OPCODE(I32, I32, I32, ___, 1, 0xfe, 0x27, I32AtomicRmw8SubU, "i32.atomic.rmw8.sub_u", "") +WABT_OPCODE(I32, I32, I32, ___, 2, 0xfe, 0x28, I32AtomicRmw16SubU, "i32.atomic.rmw16.sub_u", "") +WABT_OPCODE(I64, I32, I64, ___, 1, 0xfe, 0x29, I64AtomicRmw8SubU, "i64.atomic.rmw8.sub_u", "") +WABT_OPCODE(I64, I32, I64, ___, 2, 0xfe, 0x2a, I64AtomicRmw16SubU, "i64.atomic.rmw16.sub_u", "") +WABT_OPCODE(I64, I32, I64, ___, 4, 0xfe, 0x2b, I64AtomicRmw32SubU, "i64.atomic.rmw32.sub_u", "") +WABT_OPCODE(I32, I32, I32, ___, 4, 0xfe, 0x2c, I32AtomicRmwAnd, "i32.atomic.rmw.and", "") +WABT_OPCODE(I64, I32, I64, ___, 8, 0xfe, 0x2d, I64AtomicRmwAnd, "i64.atomic.rmw.and", "") +WABT_OPCODE(I32, I32, I32, ___, 1, 0xfe, 0x2e, I32AtomicRmw8AndU, "i32.atomic.rmw8.and_u", "") +WABT_OPCODE(I32, I32, I32, ___, 2, 0xfe, 0x2f, I32AtomicRmw16AndU, "i32.atomic.rmw16.and_u", "") +WABT_OPCODE(I64, I32, I64, ___, 1, 0xfe, 0x30, I64AtomicRmw8AndU, "i64.atomic.rmw8.and_u", "") +WABT_OPCODE(I64, I32, I64, ___, 2, 0xfe, 0x31, I64AtomicRmw16AndU, "i64.atomic.rmw16.and_u", "") +WABT_OPCODE(I64, I32, I64, ___, 4, 0xfe, 0x32, I64AtomicRmw32AndU, "i64.atomic.rmw32.and_u", "") +WABT_OPCODE(I32, I32, I32, ___, 4, 0xfe, 0x33, I32AtomicRmwOr, "i32.atomic.rmw.or", "") +WABT_OPCODE(I64, I32, I64, ___, 8, 0xfe, 0x34, I64AtomicRmwOr, "i64.atomic.rmw.or", "") +WABT_OPCODE(I32, I32, I32, ___, 1, 0xfe, 0x35, I32AtomicRmw8OrU, "i32.atomic.rmw8.or_u", "") +WABT_OPCODE(I32, I32, I32, ___, 2, 0xfe, 0x36, I32AtomicRmw16OrU, "i32.atomic.rmw16.or_u", "") +WABT_OPCODE(I64, I32, I64, ___, 1, 0xfe, 0x37, I64AtomicRmw8OrU, "i64.atomic.rmw8.or_u", "") +WABT_OPCODE(I64, I32, I64, ___, 2, 0xfe, 0x38, I64AtomicRmw16OrU, "i64.atomic.rmw16.or_u", "") +WABT_OPCODE(I64, I32, I64, ___, 4, 0xfe, 0x39, I64AtomicRmw32OrU, "i64.atomic.rmw32.or_u", "") +WABT_OPCODE(I32, I32, I32, ___, 4, 0xfe, 0x3a, I32AtomicRmwXor, "i32.atomic.rmw.xor", "") +WABT_OPCODE(I64, I32, I64, ___, 8, 0xfe, 0x3b, I64AtomicRmwXor, "i64.atomic.rmw.xor", "") +WABT_OPCODE(I32, I32, I32, ___, 1, 0xfe, 0x3c, I32AtomicRmw8XorU, "i32.atomic.rmw8.xor_u", "") +WABT_OPCODE(I32, I32, I32, ___, 2, 0xfe, 0x3d, I32AtomicRmw16XorU, "i32.atomic.rmw16.xor_u", "") +WABT_OPCODE(I64, I32, I64, ___, 1, 0xfe, 0x3e, I64AtomicRmw8XorU, "i64.atomic.rmw8.xor_u", "") +WABT_OPCODE(I64, I32, I64, ___, 2, 0xfe, 0x3f, I64AtomicRmw16XorU, "i64.atomic.rmw16.xor_u", "") +WABT_OPCODE(I64, I32, I64, ___, 4, 0xfe, 0x40, I64AtomicRmw32XorU, "i64.atomic.rmw32.xor_u", "") +WABT_OPCODE(I32, I32, I32, ___, 4, 0xfe, 0x41, I32AtomicRmwXchg, "i32.atomic.rmw.xchg", "") +WABT_OPCODE(I64, I32, I64, ___, 8, 0xfe, 0x42, I64AtomicRmwXchg, "i64.atomic.rmw.xchg", "") +WABT_OPCODE(I32, I32, I32, ___, 1, 0xfe, 0x43, I32AtomicRmw8XchgU, "i32.atomic.rmw8.xchg_u", "") +WABT_OPCODE(I32, I32, I32, ___, 2, 0xfe, 0x44, I32AtomicRmw16XchgU, "i32.atomic.rmw16.xchg_u", "") +WABT_OPCODE(I64, I32, I64, ___, 1, 0xfe, 0x45, I64AtomicRmw8XchgU, "i64.atomic.rmw8.xchg_u", "") +WABT_OPCODE(I64, I32, I64, ___, 2, 0xfe, 0x46, I64AtomicRmw16XchgU, "i64.atomic.rmw16.xchg_u", "") +WABT_OPCODE(I64, I32, I64, ___, 4, 0xfe, 0x47, I64AtomicRmw32XchgU, "i64.atomic.rmw32.xchg_u", "") +WABT_OPCODE(I32, I32, I32, I32, 4, 0xfe, 0x48, I32AtomicRmwCmpxchg, "i32.atomic.rmw.cmpxchg", "") +WABT_OPCODE(I64, I32, I64, I64, 8, 0xfe, 0x49, I64AtomicRmwCmpxchg, "i64.atomic.rmw.cmpxchg", "") +WABT_OPCODE(I32, I32, I32, I32, 1, 0xfe, 0x4a, I32AtomicRmw8CmpxchgU, "i32.atomic.rmw8.cmpxchg_u", "") +WABT_OPCODE(I32, I32, I32, I32, 2, 0xfe, 0x4b, I32AtomicRmw16CmpxchgU, "i32.atomic.rmw16.cmpxchg_u", "") +WABT_OPCODE(I64, I32, I64, I64, 1, 0xfe, 0x4c, I64AtomicRmw8CmpxchgU, "i64.atomic.rmw8.cmpxchg_u", "") +WABT_OPCODE(I64, I32, I64, I64, 2, 0xfe, 0x4d, I64AtomicRmw16CmpxchgU, "i64.atomic.rmw16.cmpxchg_u", "") +WABT_OPCODE(I64, I32, I64, I64, 4, 0xfe, 0x4e, I64AtomicRmw32CmpxchgU, "i64.atomic.rmw32.cmpxchg_u", "") diff --git a/third_party/wasm2c/src/opcode.h b/third_party/wasm2c/src/opcode.h new file mode 100644 index 0000000000..94cd4cb8f6 --- /dev/null +++ b/third_party/wasm2c/src/opcode.h @@ -0,0 +1,182 @@ +/* + * 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. + */ + +#ifndef WABT_OPCODE_H_ +#define WABT_OPCODE_H_ + +#include <vector> + +#include "src/common.h" +#include "src/opcode-code-table.h" +#include "src/leb128.h" + +namespace wabt { + +class Features; + +struct Opcode { + // Opcode enumerations. + // + // NOTE: this enum does not match the binary encoding. + // + enum Enum : uint32_t { +#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \ + text, decomp) \ + Name, +#include "src/opcode.def" +#undef WABT_OPCODE + Invalid, + }; + +// Static opcode objects. +#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \ + text, decomp) \ + static Opcode Name##_Opcode; +#include "src/opcode.def" +#undef WABT_OPCODE + + Opcode() = default; // Provided so Opcode can be member of a union. + Opcode(Enum e) : enum_(e) {} + operator Enum() const { return enum_; } + + static Opcode FromCode(uint32_t); + static Opcode FromCode(uint8_t prefix, uint32_t code); + bool HasPrefix() const { return GetInfo().prefix != 0; } + uint8_t GetPrefix() const { return GetInfo().prefix; } + uint32_t GetCode() const { return GetInfo().code; } + size_t GetLength() const { return GetBytes().size(); } + const char* GetName() const { return GetInfo().name; } + const char* GetDecomp() const { + return *GetInfo().decomp ? GetInfo().decomp : GetInfo().name; + } + Type GetResultType() const { return GetInfo().result_type; } + Type GetParamType1() const { return GetInfo().param_types[0]; } + Type GetParamType2() const { return GetInfo().param_types[1]; } + Type GetParamType3() const { return GetInfo().param_types[2]; } + Type GetParamType(int n) const { return GetInfo().param_types[n - 1]; } + Address GetMemorySize() const { return GetInfo().memory_size; } + + // If this is a load/store op, the type depends on the memory used. + Type GetMemoryParam(Type param, + const Limits* limits, + bool has_address_operands) { + return limits && limits->is_64 && has_address_operands ? Type(Type::I64) + : param; + } + + // Get the byte sequence for this opcode, including prefix. + std::vector<uint8_t> GetBytes() const; + + // Get the lane count of an extract/replace simd op. + uint32_t GetSimdLaneCount() const; + + // Return 1 if |alignment| matches the alignment of |opcode|, or if + // |alignment| is WABT_USE_NATURAL_ALIGNMENT. + bool IsNaturallyAligned(Address alignment) const; + + // If |alignment| is WABT_USE_NATURAL_ALIGNMENT, return the alignment of + // |opcode|, else return |alignment|. + Address GetAlignment(Address alignment) const; + + static bool IsPrefixByte(uint8_t byte) { + return byte == kMathPrefix || byte == kThreadsPrefix || byte == kSimdPrefix; + } + + bool IsEnabled(const Features& features) const; + bool IsInvalid() const { return enum_ >= Invalid; } + + private: + static const uint32_t kMathPrefix = 0xfc; + static const uint32_t kThreadsPrefix = 0xfe; + static const uint32_t kSimdPrefix = 0xfd; + + struct Info { + const char* name; + const char* decomp; + Type result_type; + Type param_types[3]; + Address memory_size; + uint8_t prefix; + uint32_t code; + uint32_t prefix_code; // See PrefixCode below. Used for fast lookup. + }; + + static uint32_t PrefixCode(uint8_t prefix, uint32_t code) { + // For now, 8 bits is enough for all codes. + if (code >= 0x100) { + // Clamp to 0xff, since we know that it is an invalid code. + code = 0xff; + } + return (prefix << 8) | code; + } + + // The Opcode struct only stores an enumeration (Opcode::Enum) of all valid + // opcodes, densely packed. We want to be able to store invalid opcodes as + // well, for display to the user. To encode these, we use PrefixCode() to + // generate a uint32_t of the prefix/code pair, then negate the value so it + // doesn't overlap with the valid enum values. The negation is done using + // `~code + 1` since prefix_code is unsigned, and MSVC warns if you use - on + // an unsigned value. + // + // | 0 | Opcode::Invalid | INT32_MAX+1 UINT32_MAX | + // |---------------|-------------------------|---------------------------| + // | valid opcodes | unused space | invalid opcodes | + // + static Enum EncodeInvalidOpcode(uint32_t prefix_code) { + Enum result = static_cast<Enum>(~prefix_code + 1); + assert(result >= Invalid); + return result; + } + + static void DecodeInvalidOpcode(Enum e, + uint8_t* out_prefix, + uint32_t* out_code) { + uint32_t prefix_code = ~static_cast<uint32_t>(e) + 1; + *out_prefix = prefix_code >> 8; + *out_code = prefix_code & 0xff; + } + + Info GetInfo() const; + static Info infos_[]; + + Enum enum_; +}; + +// static +inline Opcode Opcode::FromCode(uint32_t code) { + return FromCode(0, code); +} + +// static +inline Opcode Opcode::FromCode(uint8_t prefix, uint32_t code) { + uint32_t prefix_code = PrefixCode(prefix, code); + + if (WABT_LIKELY(prefix_code < WABT_ARRAY_SIZE(WabtOpcodeCodeTable))) { + uint32_t value = WabtOpcodeCodeTable[prefix_code]; + // The default value in the table is 0. That's a valid value, but only if + // the code is 0 (for nop). + if (WABT_LIKELY(value != 0 || code == 0)) { + return Opcode(static_cast<Enum>(value)); + } + } + + return Opcode(EncodeInvalidOpcode(prefix_code)); +} + + +} // namespace wabt + +#endif // WABT_OPCODE_H_ diff --git a/third_party/wasm2c/src/option-parser.cc b/third_party/wasm2c/src/option-parser.cc new file mode 100644 index 0000000000..d6a38ae8d9 --- /dev/null +++ b/third_party/wasm2c/src/option-parser.cc @@ -0,0 +1,356 @@ +/* + * 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 "src/option-parser.h" + +#include <cstdarg> +#include <cstdio> +#include <cstring> + +#include "config.h" + +#if HAVE_ALLOCA +#include <alloca.h> +#endif + +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", CMAKE_PROJECT_VERSION); + 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/option-parser.h b/third_party/wasm2c/src/option-parser.h new file mode 100644 index 0000000000..051ce784e8 --- /dev/null +++ b/third_party/wasm2c/src/option-parser.h @@ -0,0 +1,99 @@ +/* + * 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_OPTION_PARSER_H_ +#define WABT_OPTION_PARSER_H_ + +#include <functional> +#include <string> +#include <vector> + +#include "src/common.h" + +namespace wabt { + +class OptionParser { + public: + enum class HasArgument { No, Yes }; + enum class ArgumentCount { One, OneOrMore, ZeroOrMore }; + + struct Option; + typedef std::function<void(const char*)> Callback; + typedef std::function<void()> NullCallback; + + struct Option { + Option(char short_name, + const std::string& long_name, + const std::string& metavar, + HasArgument has_argument, + const std::string& help, + const Callback&); + + char short_name; + std::string long_name; + std::string metavar; + bool has_argument; + std::string help; + Callback callback; + }; + + struct Argument { + Argument(const std::string& name, ArgumentCount, const Callback&); + + std::string name; + ArgumentCount count; + Callback callback; + int handled_count = 0; + }; + + explicit OptionParser(const char* program_name, const char* description); + + void AddOption(const Option&); + void AddArgument(const std::string& name, ArgumentCount, const Callback&); + void SetErrorCallback(const Callback&); + void Parse(int argc, char* argv[]); + void PrintHelp(); + + // Helper functions. + void AddOption(char short_name, + const char* long_name, + const char* help, + const NullCallback&); + void AddOption(const char* long_name, const char* help, const NullCallback&); + void AddOption(char short_name, + const char* long_name, + const char* metavar, + const char* help, + const Callback&); + + private: + static int Match(const char* s, const std::string& full, bool has_argument); + void WABT_PRINTF_FORMAT(2, 3) Errorf(const char* format, ...); + void HandleArgument(size_t* arg_index, const char* arg_value); + + // Print the error and exit(1). + void DefaultError(const std::string&); + + std::string program_name_; + std::string description_; + std::vector<Option> options_; + std::vector<Argument> arguments_; + Callback on_error_; +}; + +} // namespace wabt + +#endif /* WABT_OPTION_PARSER_H_ */ 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..9f1f68ed8c --- /dev/null +++ b/third_party/wasm2c/src/prebuilt/lexer-keywords.cc @@ -0,0 +1,1856 @@ +/* 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-9,11-14,16-19,23,$' */ + +#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 = 2613, 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[] = + { + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 12, 111, 2632, 60, + 8, 44, 7, 294, 126, 601, 359, 575, 17, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 8, 30, 67, 24, 77, + 27, 13, 7, 561, 624, 10, 13, 25, 40, 8, + 9, 53, 353, 537, 140, 7, 7, 9, 162, 131, + 317, 751, 95, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, + 2632, 2632, 2632, 2632, 2632, 2632, 2632 + }; + unsigned int hval = len; + + switch (hval) + { + default: + 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]+1)]; + /*FALLTHROUGH*/ + case 16: + hval += asso_values[static_cast<unsigned char>(str[15])]; + /*FALLTHROUGH*/ + case 15: + case 14: + hval += asso_values[static_cast<unsigned char>(str[13])]; + /*FALLTHROUGH*/ + case 13: + hval += asso_values[static_cast<unsigned char>(str[12])]; + /*FALLTHROUGH*/ + case 12: + hval += asso_values[static_cast<unsigned char>(str[11])]; + /*FALLTHROUGH*/ + case 11: + hval += asso_values[static_cast<unsigned char>(str[10])]; + /*FALLTHROUGH*/ + case 10: + case 9: + hval += asso_values[static_cast<unsigned char>(str[8])]; + /*FALLTHROUGH*/ + 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]+1)]; + /*FALLTHROUGH*/ + case 4: + case 3: + hval += asso_values[static_cast<unsigned char>(str[2])]; + /*FALLTHROUGH*/ + case 2: + case 1: + hval += asso_values[static_cast<unsigned char>(str[0])]; + 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 = 611, + MIN_WORD_LENGTH = 2, + MAX_WORD_LENGTH = 29, + MIN_HASH_VALUE = 19, + MAX_HASH_VALUE = 2631 + }; + + static struct TokenInfo wordlist[] = + { + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, +#line 507 "src/lexer-keywords.txt" + {"if", TokenType::If, Opcode::If}, + {""}, {""}, {""}, {""}, +#line 140 "src/lexer-keywords.txt" + {"f64", Type::F64}, +#line 527 "src/lexer-keywords.txt" + {"mut", TokenType::Mut}, +#line 82 "src/lexer-keywords.txt" + {"f32", Type::F32}, +#line 439 "src/lexer-keywords.txt" + {"i64", Type::I64}, + {""}, +#line 301 "src/lexer-keywords.txt" + {"i32", Type::I32}, + {""}, {""}, {""}, +#line 557 "src/lexer-keywords.txt" + {"then", TokenType::Then}, + {""}, +#line 511 "src/lexer-keywords.txt" + {"item", TokenType::Item}, + {""}, +#line 47 "src/lexer-keywords.txt" + {"else", TokenType::Else, Opcode::Else}, +#line 46 "src/lexer-keywords.txt" + {"elem", TokenType::Elem}, + {""}, {""}, {""}, +#line 127 "src/lexer-keywords.txt" + {"f64.lt", TokenType::Compare, Opcode::F64Lt}, +#line 70 "src/lexer-keywords.txt" + {"f32.lt", TokenType::Compare, Opcode::F32Lt}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 50 "src/lexer-keywords.txt" + {"extern", Type::ExternRef, TokenType::Extern}, + {""}, {""}, +#line 125 "src/lexer-keywords.txt" + {"f64.le", TokenType::Compare, Opcode::F64Le}, +#line 68 "src/lexer-keywords.txt" + {"f32.le", TokenType::Compare, Opcode::F32Le}, +#line 556 "src/lexer-keywords.txt" + {"table", TokenType::Table}, +#line 170 "src/lexer-keywords.txt" + {"funcref", Type::FuncRef}, +#line 129 "src/lexer-keywords.txt" + {"f64.min", TokenType::Binary, Opcode::F64Min}, +#line 72 "src/lexer-keywords.txt" + {"f32.min", TokenType::Binary, Opcode::F32Min}, + {""}, {""}, +#line 412 "src/lexer-keywords.txt" + {"i64.lt_s", TokenType::Compare, Opcode::I64LtS}, +#line 275 "src/lexer-keywords.txt" + {"i32.lt_s", TokenType::Compare, Opcode::I32LtS}, + {""}, +#line 169 "src/lexer-keywords.txt" + {"field", TokenType::Field}, +#line 413 "src/lexer-keywords.txt" + {"i64.lt_u", TokenType::Compare, Opcode::I64LtU}, +#line 276 "src/lexer-keywords.txt" + {"i32.lt_u", TokenType::Compare, Opcode::I32LtU}, +#line 403 "src/lexer-keywords.txt" + {"i64.le_s", TokenType::Compare, Opcode::I64LeS}, +#line 268 "src/lexer-keywords.txt" + {"i32.le_s", TokenType::Compare, Opcode::I32LeS}, +#line 48 "src/lexer-keywords.txt" + {"end", TokenType::End, Opcode::End}, +#line 168 "src/lexer-keywords.txt" + {"f64x2", TokenType::F64X2}, +#line 404 "src/lexer-keywords.txt" + {"i64.le_u", TokenType::Compare, Opcode::I64LeU}, +#line 269 "src/lexer-keywords.txt" + {"i32.le_u", TokenType::Compare, Opcode::I32LeU}, +#line 469 "src/lexer-keywords.txt" + {"i64x2", TokenType::I64X2}, +#line 526 "src/lexer-keywords.txt" + {"module", TokenType::Module}, +#line 419 "src/lexer-keywords.txt" + {"i64.rem_s", TokenType::Binary, Opcode::I64RemS}, +#line 282 "src/lexer-keywords.txt" + {"i32.rem_s", TokenType::Binary, Opcode::I32RemS}, + {""}, {""}, +#line 420 "src/lexer-keywords.txt" + {"i64.rem_u", TokenType::Binary, Opcode::I64RemU}, +#line 283 "src/lexer-keywords.txt" + {"i32.rem_u", TokenType::Binary, Opcode::I32RemU}, +#line 43 "src/lexer-keywords.txt" + {"do", TokenType::Do}, +#line 111 "src/lexer-keywords.txt" + {"f64.abs", TokenType::Unary, Opcode::F64Abs}, +#line 53 "src/lexer-keywords.txt" + {"f32.abs", TokenType::Unary, Opcode::F32Abs}, +#line 138 "src/lexer-keywords.txt" + {"f64.sub", TokenType::Binary, Opcode::F64Sub}, +#line 80 "src/lexer-keywords.txt" + {"f32.sub", TokenType::Binary, Opcode::F32Sub}, +#line 531 "src/lexer-keywords.txt" + {"offset", TokenType::Offset}, +#line 430 "src/lexer-keywords.txt" + {"i64.sub", TokenType::Binary, Opcode::I64Sub}, +#line 292 "src/lexer-keywords.txt" + {"i32.sub", TokenType::Binary, Opcode::I32Sub}, + {""}, {""}, {""}, +#line 554 "src/lexer-keywords.txt" + {"table.set", TokenType::TableSet, Opcode::TableSet}, +#line 545 "src/lexer-keywords.txt" + {"select", TokenType::Select, Opcode::Select}, +#line 547 "src/lexer-keywords.txt" + {"start", TokenType::Start}, +#line 553 "src/lexer-keywords.txt" + {"table.init", TokenType::TableInit, Opcode::TableInit}, +#line 171 "src/lexer-keywords.txt" + {"func", Type::FuncRef, TokenType::Func}, + {""}, +#line 133 "src/lexer-keywords.txt" + {"f64.ne", TokenType::Compare, Opcode::F64Ne}, +#line 76 "src/lexer-keywords.txt" + {"f32.ne", TokenType::Compare, Opcode::F32Ne}, + {""}, +#line 415 "src/lexer-keywords.txt" + {"i64.ne", TokenType::Compare, Opcode::I64Ne}, +#line 278 "src/lexer-keywords.txt" + {"i32.ne", TokenType::Compare, Opcode::I32Ne}, + {""}, +#line 40 "src/lexer-keywords.txt" + {"data", TokenType::Data}, + {""}, {""}, {""}, {""}, {""}, +#line 350 "src/lexer-keywords.txt" + {"i64.and", TokenType::Binary, Opcode::I64And}, +#line 227 "src/lexer-keywords.txt" + {"i32.and", TokenType::Binary, Opcode::I32And}, +#line 157 "src/lexer-keywords.txt" + {"f64x2.ne", TokenType::Compare, Opcode::F64X2Ne}, + {""}, +#line 153 "src/lexer-keywords.txt" + {"f64x2.min", TokenType::Binary, Opcode::F64X2Min}, +#line 446 "src/lexer-keywords.txt" + {"i64x2.ne", TokenType::Binary, Opcode::I64X2Ne}, + {""}, {""}, +#line 130 "src/lexer-keywords.txt" + {"f64.mul", TokenType::Binary, Opcode::F64Mul}, +#line 73 "src/lexer-keywords.txt" + {"f32.mul", TokenType::Binary, Opcode::F32Mul}, + {""}, +#line 414 "src/lexer-keywords.txt" + {"i64.mul", TokenType::Binary, Opcode::I64Mul}, +#line 277 "src/lexer-keywords.txt" + {"i32.mul", TokenType::Binary, Opcode::I32Mul}, + {""}, {""}, +#line 112 "src/lexer-keywords.txt" + {"f64.add", TokenType::Binary, Opcode::F64Add}, +#line 54 "src/lexer-keywords.txt" + {"f32.add", TokenType::Binary, Opcode::F32Add}, + {""}, +#line 349 "src/lexer-keywords.txt" + {"i64.add", TokenType::Binary, Opcode::I64Add}, +#line 226 "src/lexer-keywords.txt" + {"i32.add", TokenType::Binary, Opcode::I32Add}, + {""}, +#line 151 "src/lexer-keywords.txt" + {"f64x2.lt", TokenType::Compare, Opcode::F64X2Lt}, +#line 114 "src/lexer-keywords.txt" + {"f64.const", TokenType::Const, Opcode::F64Const}, +#line 56 "src/lexer-keywords.txt" + {"f32.const", TokenType::Const, Opcode::F32Const}, + {""}, +#line 388 "src/lexer-keywords.txt" + {"i64.const", TokenType::Const, Opcode::I64Const}, +#line 256 "src/lexer-keywords.txt" + {"i32.const", TokenType::Const, Opcode::I32Const}, + {""}, {""}, {""}, +#line 546 "src/lexer-keywords.txt" + {"shared", TokenType::Shared}, + {""}, +#line 163 "src/lexer-keywords.txt" + {"f64x2.sub", TokenType::Binary, Opcode::F64X2Sub}, +#line 150 "src/lexer-keywords.txt" + {"f64x2.le", TokenType::Compare, Opcode::F64X2Le}, +#line 447 "src/lexer-keywords.txt" + {"i64x2.lt_s", TokenType::Binary, Opcode::I64X2LtS}, +#line 464 "src/lexer-keywords.txt" + {"i64x2.sub", TokenType::Binary, Opcode::I64X2Sub}, +#line 29 "src/lexer-keywords.txt" + {"block", TokenType::Block, Opcode::Block}, + {""}, {""}, {""}, +#line 449 "src/lexer-keywords.txt" + {"i64x2.le_s", TokenType::Binary, Opcode::I64X2LeS}, +#line 113 "src/lexer-keywords.txt" + {"f64.ceil", TokenType::Unary, Opcode::F64Ceil}, +#line 55 "src/lexer-keywords.txt" + {"f32.ceil", TokenType::Unary, Opcode::F32Ceil}, +#line 31 "src/lexer-keywords.txt" + {"br_table", TokenType::BrTable, Opcode::BrTable}, + {""}, {""}, +#line 550 "src/lexer-keywords.txt" + {"table.fill", TokenType::TableFill, Opcode::TableFill}, + {""}, {""}, {""}, +#line 35 "src/lexer-keywords.txt" + {"call", TokenType::Call, Opcode::Call}, + {""}, {""}, {""}, {""}, +#line 32 "src/lexer-keywords.txt" + {"br", TokenType::Br, Opcode::Br}, + {""}, {""}, {""}, +#line 515 "src/lexer-keywords.txt" + {"local", TokenType::Local}, + {""}, +#line 421 "src/lexer-keywords.txt" + {"i64.rotl", TokenType::Binary, Opcode::I64Rotl}, +#line 284 "src/lexer-keywords.txt" + {"i32.rotl", TokenType::Binary, Opcode::I32Rotl}, + {""}, +#line 540 "src/lexer-keywords.txt" + {"result", TokenType::Result}, +#line 154 "src/lexer-keywords.txt" + {"f64x2.mul", TokenType::Binary, Opcode::F64X2Mul}, + {""}, +#line 544 "src/lexer-keywords.txt" + {"return", TokenType::Return, Opcode::Return}, +#line 444 "src/lexer-keywords.txt" + {"i64x2.mul", TokenType::Binary, Opcode::I64X2Mul}, +#line 513 "src/lexer-keywords.txt" + {"local.set", TokenType::LocalSet, Opcode::LocalSet}, + {""}, {""}, {""}, +#line 141 "src/lexer-keywords.txt" + {"f64x2.abs", TokenType::Unary, Opcode::F64X2Abs}, +#line 555 "src/lexer-keywords.txt" + {"table.size", TokenType::TableSize, Opcode::TableSize}, + {""}, +#line 451 "src/lexer-keywords.txt" + {"i64x2.abs", TokenType::Unary, Opcode::I64X2Abs}, + {""}, {""}, {""}, {""}, +#line 514 "src/lexer-keywords.txt" + {"local.tee", TokenType::LocalTee, Opcode::LocalTee}, + {""}, +#line 548 "src/lexer-keywords.txt" + {"struct", Type::Struct, TokenType::Struct}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 126 "src/lexer-keywords.txt" + {"f64.load", TokenType::Load, Opcode::F64Load}, +#line 69 "src/lexer-keywords.txt" + {"f32.load", TokenType::Load, Opcode::F32Load}, +#line 155 "src/lexer-keywords.txt" + {"f64x2.nearest", TokenType::Unary, Opcode::F64X2Nearest}, +#line 411 "src/lexer-keywords.txt" + {"i64.load", TokenType::Load, Opcode::I64Load}, +#line 274 "src/lexer-keywords.txt" + {"i32.load", TokenType::Load, Opcode::I32Load}, + {""}, {""}, {""}, +#line 51 "src/lexer-keywords.txt" + {"externref", Type::ExternRef}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 143 "src/lexer-keywords.txt" + {"f64x2.ceil", TokenType::Unary, Opcode::F64X2Ceil}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 142 "src/lexer-keywords.txt" + {"f64x2.add", TokenType::Binary, Opcode::F64X2Add}, + {""}, {""}, +#line 440 "src/lexer-keywords.txt" + {"i64x2.add", TokenType::Binary, Opcode::I64X2Add}, + {""}, {""}, +#line 390 "src/lexer-keywords.txt" + {"i64.div_s", TokenType::Binary, Opcode::I64DivS}, +#line 258 "src/lexer-keywords.txt" + {"i32.div_s", TokenType::Binary, Opcode::I32DivS}, + {""}, {""}, +#line 391 "src/lexer-keywords.txt" + {"i64.div_u", TokenType::Binary, Opcode::I64DivU}, +#line 259 "src/lexer-keywords.txt" + {"i32.div_u", TokenType::Binary, Opcode::I32DivU}, + {""}, +#line 386 "src/lexer-keywords.txt" + {"i64.atomic.store", TokenType::AtomicStore, Opcode::I64AtomicStore}, +#line 254 "src/lexer-keywords.txt" + {"i32.atomic.store", TokenType::AtomicStore, Opcode::I32AtomicStore}, +#line 532 "src/lexer-keywords.txt" + {"output", TokenType::Output}, + {""}, +#line 510 "src/lexer-keywords.txt" + {"invoke", TokenType::Invoke}, + {""}, {""}, {""}, +#line 389 "src/lexer-keywords.txt" + {"i64.ctz", TokenType::Unary, Opcode::I64Ctz}, +#line 257 "src/lexer-keywords.txt" + {"i32.ctz", TokenType::Unary, Opcode::I32Ctz}, +#line 407 "src/lexer-keywords.txt" + {"i64.load32_s", TokenType::Load, Opcode::I64Load32S}, +#line 454 "src/lexer-keywords.txt" + {"i64x2.bitmask", TokenType::Unary, Opcode::I64X2Bitmask}, +#line 384 "src/lexer-keywords.txt" + {"i64.atomic.store32", TokenType::AtomicStore, Opcode::I64AtomicStore32}, + {""}, +#line 408 "src/lexer-keywords.txt" + {"i64.load32_u", TokenType::Load, Opcode::I64Load32U}, + {""}, +#line 137 "src/lexer-keywords.txt" + {"f64.store", TokenType::Store, Opcode::F64Store}, +#line 79 "src/lexer-keywords.txt" + {"f32.store", TokenType::Store, Opcode::F32Store}, + {""}, +#line 429 "src/lexer-keywords.txt" + {"i64.store", TokenType::Store, Opcode::I64Store}, +#line 291 "src/lexer-keywords.txt" + {"i32.store", TokenType::Store, Opcode::I32Store}, + {""}, {""}, {""}, +#line 427 "src/lexer-keywords.txt" + {"i64.store32", TokenType::Store, Opcode::I64Store32}, + {""}, +#line 405 "src/lexer-keywords.txt" + {"i64.load16_s", TokenType::Load, Opcode::I64Load16S}, +#line 270 "src/lexer-keywords.txt" + {"i32.load16_s", TokenType::Load, Opcode::I32Load16S}, + {""}, {""}, +#line 406 "src/lexer-keywords.txt" + {"i64.load16_u", TokenType::Load, Opcode::I64Load16U}, +#line 271 "src/lexer-keywords.txt" + {"i32.load16_u", TokenType::Load, Opcode::I32Load16U}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 62 "src/lexer-keywords.txt" + {"f32.demote_f64", TokenType::Convert, Opcode::F32DemoteF64}, + {""}, {""}, +#line 387 "src/lexer-keywords.txt" + {"i64.clz", TokenType::Unary, Opcode::I64Clz}, +#line 255 "src/lexer-keywords.txt" + {"i32.clz", TokenType::Unary, Opcode::I32Clz}, + {""}, +#line 537 "src/lexer-keywords.txt" + {"ref.is_null", TokenType::RefIsNull, Opcode::RefIsNull}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, +#line 37 "src/lexer-keywords.txt" + {"catch_all", TokenType::CatchAll, Opcode::CatchAll}, + {""}, +#line 22 "src/lexer-keywords.txt" + {"assert_invalid", TokenType::AssertInvalid}, +#line 41 "src/lexer-keywords.txt" + {"declare", TokenType::Declare}, +#line 26 "src/lexer-keywords.txt" + {"assert_unlinkable", TokenType::AssertUnlinkable}, + {""}, {""}, {""}, {""}, {""}, +#line 630 "src/lexer-keywords.txt" + {"set_local", TokenType::LocalSet, Opcode::LocalSet}, + {""}, {""}, {""}, {""}, {""}, +#line 631 "src/lexer-keywords.txt" + {"tee_local", TokenType::LocalTee, Opcode::LocalTee}, + {""}, {""}, {""}, {""}, {""}, +#line 33 "src/lexer-keywords.txt" + {"call_indirect", TokenType::CallIndirect, Opcode::CallIndirect}, +#line 110 "src/lexer-keywords.txt" + {"f32x4", TokenType::F32X4}, +#line 34 "src/lexer-keywords.txt" + {"call_ref", TokenType::CallRef, Opcode::CallRef}, + {""}, +#line 339 "src/lexer-keywords.txt" + {"i32x4", TokenType::I32X4}, +#line 131 "src/lexer-keywords.txt" + {"f64.nearest", TokenType::Unary, Opcode::F64Nearest}, +#line 74 "src/lexer-keywords.txt" + {"f32.nearest", TokenType::Unary, Opcode::F32Nearest}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 354 "src/lexer-keywords.txt" + {"i64.atomic.load", TokenType::AtomicLoad, Opcode::I64AtomicLoad}, +#line 230 "src/lexer-keywords.txt" + {"i32.atomic.load", TokenType::AtomicLoad, Opcode::I32AtomicLoad}, + {""}, {""}, +#line 538 "src/lexer-keywords.txt" + {"ref.null", TokenType::RefNull, Opcode::RefNull}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 139 "src/lexer-keywords.txt" + {"f64.trunc", TokenType::Unary, Opcode::F64Trunc}, +#line 81 "src/lexer-keywords.txt" + {"f32.trunc", TokenType::Unary, Opcode::F32Trunc}, + {""}, {""}, {""}, {""}, +#line 431 "src/lexer-keywords.txt" + {"i64.trunc_f32_s", TokenType::Convert, Opcode::I64TruncF32S}, +#line 293 "src/lexer-keywords.txt" + {"i32.trunc_f32_s", TokenType::Convert, Opcode::I32TruncF32S}, +#line 432 "src/lexer-keywords.txt" + {"i64.trunc_f32_u", TokenType::Convert, Opcode::I64TruncF32U}, +#line 294 "src/lexer-keywords.txt" + {"i32.trunc_f32_u", TokenType::Convert, Opcode::I32TruncF32U}, + {""}, {""}, {""}, +#line 27 "src/lexer-keywords.txt" + {"atomic.fence", TokenType::AtomicFence, Opcode::AtomicFence}, + {""}, {""}, {""}, +#line 592 "src/lexer-keywords.txt" + {"i64.atomic.wait", TokenType::AtomicWait, Opcode::MemoryAtomicWait64}, +#line 591 "src/lexer-keywords.txt" + {"i32.atomic.wait", TokenType::AtomicWait, Opcode::MemoryAtomicWait32}, + {""}, +#line 101 "src/lexer-keywords.txt" + {"f32x4.ne", TokenType::Compare, Opcode::F32X4Ne}, +#line 567 "src/lexer-keywords.txt" + {"v128.not", TokenType::Unary, Opcode::V128Not}, +#line 97 "src/lexer-keywords.txt" + {"f32x4.min", TokenType::Binary, Opcode::F32X4Min}, +#line 326 "src/lexer-keywords.txt" + {"i32x4.ne", TokenType::Compare, Opcode::I32X4Ne}, +#line 120 "src/lexer-keywords.txt" + {"f64.div", TokenType::Binary, Opcode::F64Div}, +#line 63 "src/lexer-keywords.txt" + {"f32.div", TokenType::Binary, Opcode::F32Div}, + {""}, {""}, +#line 422 "src/lexer-keywords.txt" + {"i64.rotr", TokenType::Binary, Opcode::I64Rotr}, +#line 285 "src/lexer-keywords.txt" + {"i32.rotr", TokenType::Binary, Opcode::I32Rotr}, + {""}, {""}, +#line 321 "src/lexer-keywords.txt" + {"i32x4.min_s", TokenType::Binary, Opcode::I32X4MinS}, +#line 560 "src/lexer-keywords.txt" + {"type", TokenType::Type}, + {""}, {""}, +#line 322 "src/lexer-keywords.txt" + {"i32x4.min_u", TokenType::Binary, Opcode::I32X4MinU}, +#line 598 "src/lexer-keywords.txt" + {"f32.demote/f64", TokenType::Convert, Opcode::F32DemoteF64}, + {""}, +#line 95 "src/lexer-keywords.txt" + {"f32x4.lt", TokenType::Compare, Opcode::F32X4Lt}, +#line 509 "src/lexer-keywords.txt" + {"input", TokenType::Input}, + {""}, +#line 115 "src/lexer-keywords.txt" + {"f64.convert_i32_s", TokenType::Convert, Opcode::F64ConvertI32S}, +#line 57 "src/lexer-keywords.txt" + {"f32.convert_i32_s", TokenType::Convert, Opcode::F32ConvertI32S}, + {""}, {""}, +#line 508 "src/lexer-keywords.txt" + {"import", TokenType::Import}, +#line 164 "src/lexer-keywords.txt" + {"f64x2.trunc", TokenType::Unary, Opcode::F64X2Trunc}, + {""}, +#line 52 "src/lexer-keywords.txt" + {"export", TokenType::Export}, +#line 107 "src/lexer-keywords.txt" + {"f32x4.sub", TokenType::Binary, Opcode::F32X4Sub}, +#line 94 "src/lexer-keywords.txt" + {"f32x4.le", TokenType::Compare, Opcode::F32X4Le}, +#line 317 "src/lexer-keywords.txt" + {"i32x4.lt_s", TokenType::Compare, Opcode::I32X4LtS}, +#line 332 "src/lexer-keywords.txt" + {"i32x4.sub", TokenType::Binary, Opcode::I32X4Sub}, +#line 318 "src/lexer-keywords.txt" + {"i32x4.lt_u", TokenType::Compare, Opcode::I32X4LtU}, + {""}, {""}, {""}, +#line 313 "src/lexer-keywords.txt" + {"i32x4.le_s", TokenType::Compare, Opcode::I32X4LeS}, + {""}, +#line 314 "src/lexer-keywords.txt" + {"i32x4.le_u", TokenType::Compare, Opcode::I32X4LeU}, + {""}, +#line 543 "src/lexer-keywords.txt" + {"return_call", TokenType::ReturnCall, Opcode::ReturnCall}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 453 "src/lexer-keywords.txt" + {"i64x2.all_true", TokenType::Unary, Opcode::I64X2AllTrue}, + {""}, +#line 562 "src/lexer-keywords.txt" + {"v128.andnot", TokenType::Binary, Opcode::V128Andnot}, +#line 563 "src/lexer-keywords.txt" + {"v128.and", TokenType::Binary, Opcode::V128And}, +#line 529 "src/lexer-keywords.txt" + {"nan:canonical", TokenType::NanCanonical}, + {""}, {""}, {""}, +#line 352 "src/lexer-keywords.txt" + {"i64.atomic.load32_u", TokenType::AtomicLoad, Opcode::I64AtomicLoad32U}, + {""}, {""}, +#line 98 "src/lexer-keywords.txt" + {"f32x4.mul", TokenType::Binary, Opcode::F32X4Mul}, + {""}, +#line 24 "src/lexer-keywords.txt" + {"assert_return", TokenType::AssertReturn}, +#line 324 "src/lexer-keywords.txt" + {"i32x4.mul", TokenType::Binary, Opcode::I32X4Mul}, +#line 433 "src/lexer-keywords.txt" + {"i64.trunc_f64_s", TokenType::Convert, Opcode::I64TruncF64S}, +#line 295 "src/lexer-keywords.txt" + {"i32.trunc_f64_s", TokenType::Convert, Opcode::I32TruncF64S}, +#line 434 "src/lexer-keywords.txt" + {"i64.trunc_f64_u", TokenType::Convert, Opcode::I64TruncF64U}, +#line 296 "src/lexer-keywords.txt" + {"i32.trunc_f64_u", TokenType::Convert, Opcode::I32TruncF64U}, +#line 83 "src/lexer-keywords.txt" + {"f32x4.abs", TokenType::Unary, Opcode::F32X4Abs}, + {""}, +#line 44 "src/lexer-keywords.txt" + {"drop", TokenType::Drop, Opcode::Drop}, +#line 303 "src/lexer-keywords.txt" + {"i32x4.abs", TokenType::Unary, Opcode::I32X4Abs}, + {""}, +#line 144 "src/lexer-keywords.txt" + {"f64x2.div", TokenType::Binary, Opcode::F64X2Div}, + {""}, {""}, {""}, +#line 565 "src/lexer-keywords.txt" + {"v128.const", TokenType::Const, Opcode::V128Const}, + {""}, {""}, {""}, {""}, +#line 23 "src/lexer-keywords.txt" + {"assert_malformed", TokenType::AssertMalformed}, +#line 516 "src/lexer-keywords.txt" + {"loop", TokenType::Loop, Opcode::Loop}, + {""}, {""}, +#line 621 "src/lexer-keywords.txt" + {"i64.trunc_s/f32", TokenType::Convert, Opcode::I64TruncF32S}, +#line 609 "src/lexer-keywords.txt" + {"i32.trunc_s/f32", TokenType::Convert, Opcode::I32TruncF32S}, +#line 625 "src/lexer-keywords.txt" + {"i64.trunc_u/f32", TokenType::Convert, Opcode::I64TruncF32U}, +#line 613 "src/lexer-keywords.txt" + {"i32.trunc_u/f32", TokenType::Convert, Opcode::I32TruncF32U}, +#line 99 "src/lexer-keywords.txt" + {"f32x4.nearest", TokenType::Unary, Opcode::F32X4Nearest}, +#line 395 "src/lexer-keywords.txt" + {"i64.extend32_s", TokenType::Unary, Opcode::I64Extend32S}, + {""}, +#line 159 "src/lexer-keywords.txt" + {"f64x2.pmin", TokenType::Binary, Opcode::F64X2PMin}, +#line 147 "src/lexer-keywords.txt" + {"f64x2.floor", TokenType::Unary, Opcode::F64X2Floor}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 117 "src/lexer-keywords.txt" + {"f64.convert_i64_s", TokenType::Convert, Opcode::F64ConvertI64S}, +#line 59 "src/lexer-keywords.txt" + {"f32.convert_i64_s", TokenType::Convert, Opcode::F32ConvertI64S}, +#line 397 "src/lexer-keywords.txt" + {"i64.extend_i32_s", TokenType::Convert, Opcode::I64ExtendI32S}, +#line 85 "src/lexer-keywords.txt" + {"f32x4.ceil", TokenType::Unary, Opcode::F32X4Ceil}, + {""}, {""}, +#line 398 "src/lexer-keywords.txt" + {"i64.extend_i32_u", TokenType::Convert, Opcode::I64ExtendI32U}, + {""}, +#line 541 "src/lexer-keywords.txt" + {"rethrow", TokenType::Rethrow, Opcode::Rethrow}, + {""}, +#line 84 "src/lexer-keywords.txt" + {"f32x4.add", TokenType::Binary, Opcode::F32X4Add}, +#line 435 "src/lexer-keywords.txt" + {"i64.trunc_sat_f32_s", TokenType::Convert, Opcode::I64TruncSatF32S}, +#line 297 "src/lexer-keywords.txt" + {"i32.trunc_sat_f32_s", TokenType::Convert, Opcode::I32TruncSatF32S}, +#line 304 "src/lexer-keywords.txt" + {"i32x4.add", TokenType::Binary, Opcode::I32X4Add}, + {""}, +#line 436 "src/lexer-keywords.txt" + {"i64.trunc_sat_f32_u", TokenType::Convert, Opcode::I64TruncSatF32U}, +#line 298 "src/lexer-keywords.txt" + {"i32.trunc_sat_f32_u", TokenType::Convert, Opcode::I32TruncSatF32U}, + {""}, {""}, +#line 564 "src/lexer-keywords.txt" + {"v128.bitselect", TokenType::Ternary, Opcode::V128BitSelect}, + {""}, +#line 383 "src/lexer-keywords.txt" + {"i64.atomic.store16", TokenType::AtomicStore, Opcode::I64AtomicStore16}, +#line 252 "src/lexer-keywords.txt" + {"i32.atomic.store16", TokenType::AtomicStore, Opcode::I32AtomicStore16}, + {""}, +#line 373 "src/lexer-keywords.txt" + {"i64.atomic.rmw8.sub_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8SubU}, +#line 242 "src/lexer-keywords.txt" + {"i32.atomic.rmw8.sub_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8SubU}, + {""}, +#line 161 "src/lexer-keywords.txt" + {"f64x2.splat", TokenType::Unary, Opcode::F64X2Splat}, + {""}, {""}, +#line 463 "src/lexer-keywords.txt" + {"i64x2.splat", TokenType::Unary, Opcode::I64X2Splat}, + {""}, +#line 426 "src/lexer-keywords.txt" + {"i64.store16", TokenType::Store, Opcode::I64Store16}, +#line 289 "src/lexer-keywords.txt" + {"i32.store16", TokenType::Store, Opcode::I32Store16}, +#line 306 "src/lexer-keywords.txt" + {"i32x4.bitmask", TokenType::Unary, Opcode::I32X4Bitmask}, + {""}, +#line 566 "src/lexer-keywords.txt" + {"v128.load", TokenType::Load, Opcode::V128Load}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 572 "src/lexer-keywords.txt" + {"v128.store", TokenType::Store, Opcode::V128Store}, +#line 370 "src/lexer-keywords.txt" + {"i64.atomic.rmw8.and_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8AndU}, +#line 239 "src/lexer-keywords.txt" + {"i32.atomic.rmw8.and_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8AndU}, + {""}, +#line 533 "src/lexer-keywords.txt" + {"param", TokenType::Param}, +#line 542 "src/lexer-keywords.txt" + {"return_call_indirect", TokenType::ReturnCallIndirect, Opcode::ReturnCallIndirect}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 600 "src/lexer-keywords.txt" + {"f64.convert_s/i32", TokenType::Convert, Opcode::F64ConvertI32S}, +#line 594 "src/lexer-keywords.txt" + {"f32.convert_s/i32", TokenType::Convert, Opcode::F32ConvertI32S}, +#line 602 "src/lexer-keywords.txt" + {"f64.convert_u/i32", TokenType::Convert, Opcode::F64ConvertI32U}, +#line 596 "src/lexer-keywords.txt" + {"f32.convert_u/i32", TokenType::Convert, Opcode::F32ConvertI32U}, + {""}, {""}, +#line 369 "src/lexer-keywords.txt" + {"i64.atomic.rmw8.add_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8AddU}, +#line 238 "src/lexer-keywords.txt" + {"i32.atomic.rmw8.add_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8AddU}, + {""}, +#line 573 "src/lexer-keywords.txt" + {"v128", Type::V128}, +#line 622 "src/lexer-keywords.txt" + {"i64.trunc_s/f64", TokenType::Convert, Opcode::I64TruncF64S}, +#line 610 "src/lexer-keywords.txt" + {"i32.trunc_s/f64", TokenType::Convert, Opcode::I32TruncF64S}, +#line 626 "src/lexer-keywords.txt" + {"i64.trunc_u/f64", TokenType::Convert, Opcode::I64TruncF64U}, +#line 614 "src/lexer-keywords.txt" + {"i32.trunc_u/f64", TokenType::Convert, Opcode::I32TruncF64U}, +#line 618 "src/lexer-keywords.txt" + {"i64.extend_s/i32", TokenType::Convert, Opcode::I64ExtendI32S}, + {""}, +#line 619 "src/lexer-keywords.txt" + {"i64.extend_u/i32", TokenType::Convert, Opcode::I64ExtendI32U}, + {""}, {""}, +#line 116 "src/lexer-keywords.txt" + {"f64.convert_i32_u", TokenType::Convert, Opcode::F64ConvertI32U}, +#line 58 "src/lexer-keywords.txt" + {"f32.convert_i32_u", TokenType::Convert, Opcode::F32ConvertI32U}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, +#line 409 "src/lexer-keywords.txt" + {"i64.load8_s", TokenType::Load, Opcode::I64Load8S}, +#line 272 "src/lexer-keywords.txt" + {"i32.load8_s", TokenType::Load, Opcode::I32Load8S}, + {""}, {""}, +#line 410 "src/lexer-keywords.txt" + {"i64.load8_u", TokenType::Load, Opcode::I64Load8U}, +#line 273 "src/lexer-keywords.txt" + {"i32.load8_u", TokenType::Load, Opcode::I32Load8U}, +#line 376 "src/lexer-keywords.txt" + {"i64.atomic.rmw.add", TokenType::AtomicRmw, Opcode::I64AtomicRmwAdd}, +#line 245 "src/lexer-keywords.txt" + {"i32.atomic.rmw.add", TokenType::AtomicRmw, Opcode::I32AtomicRmwAdd}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, +#line 366 "src/lexer-keywords.txt" + {"i64.atomic.rmw32.sub_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32SubU}, + {""}, +#line 172 "src/lexer-keywords.txt" + {"get", TokenType::Get}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, +#line 394 "src/lexer-keywords.txt" + {"i64.extend16_s", TokenType::Unary, Opcode::I64Extend16S}, +#line 262 "src/lexer-keywords.txt" + {"i32.extend16_s", TokenType::Unary, Opcode::I32Extend16S}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 558 "src/lexer-keywords.txt" + {"throw", TokenType::Throw, Opcode::Throw}, + {""}, +#line 377 "src/lexer-keywords.txt" + {"i64.atomic.rmw.and", TokenType::AtomicRmw, Opcode::I64AtomicRmwAnd}, +#line 246 "src/lexer-keywords.txt" + {"i32.atomic.rmw.and", TokenType::AtomicRmw, Opcode::I32AtomicRmwAnd}, + {""}, +#line 30 "src/lexer-keywords.txt" + {"br_if", TokenType::BrIf, Opcode::BrIf}, +#line 428 "src/lexer-keywords.txt" + {"i64.store8", TokenType::Store, Opcode::I64Store8}, +#line 290 "src/lexer-keywords.txt" + {"i32.store8", TokenType::Store, Opcode::I32Store8}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 534 "src/lexer-keywords.txt" + {"quote", TokenType::Quote}, + {""}, {""}, {""}, {""}, +#line 579 "src/lexer-keywords.txt" + {"v128.load8_lane", TokenType::SimdLoadLane, Opcode::V128Load8Lane}, +#line 568 "src/lexer-keywords.txt" + {"v128.or", TokenType::Binary, Opcode::V128Or}, + {""}, {""}, {""}, +#line 118 "src/lexer-keywords.txt" + {"f64.convert_i64_u", TokenType::Convert, Opcode::F64ConvertI64U}, +#line 60 "src/lexer-keywords.txt" + {"f32.convert_i64_u", TokenType::Convert, Opcode::F32ConvertI64U}, +#line 379 "src/lexer-keywords.txt" + {"i64.atomic.rmw.or", TokenType::AtomicRmw, Opcode::I64AtomicRmwOr}, +#line 248 "src/lexer-keywords.txt" + {"i32.atomic.rmw.or", TokenType::AtomicRmw, Opcode::I32AtomicRmwOr}, + {""}, {""}, {""}, +#line 582 "src/lexer-keywords.txt" + {"v128.load64_lane", TokenType::SimdLoadLane, Opcode::V128Load64Lane}, +#line 581 "src/lexer-keywords.txt" + {"v128.load32_lane", TokenType::SimdLoadLane, Opcode::V128Load32Lane}, +#line 585 "src/lexer-keywords.txt" + {"v128.store32_lane", TokenType::SimdStoreLane, Opcode::V128Store32Lane}, + {""}, +#line 363 "src/lexer-keywords.txt" + {"i64.atomic.rmw32.and_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32AndU}, + {""}, {""}, {""}, {""}, {""}, +#line 108 "src/lexer-keywords.txt" + {"f32x4.trunc", TokenType::Unary, Opcode::F32X4Trunc}, + {""}, {""}, +#line 380 "src/lexer-keywords.txt" + {"i64.atomic.rmw.sub", TokenType::AtomicRmw, Opcode::I64AtomicRmwSub}, +#line 249 "src/lexer-keywords.txt" + {"i32.atomic.rmw.sub", TokenType::AtomicRmw, Opcode::I32AtomicRmwSub}, +#line 551 "src/lexer-keywords.txt" + {"table.get", TokenType::TableGet, Opcode::TableGet}, +#line 21 "src/lexer-keywords.txt" + {"assert_exhaustion", TokenType::AssertExhaustion}, + {""}, +#line 535 "src/lexer-keywords.txt" + {"ref.extern", TokenType::RefExtern}, + {""}, {""}, +#line 149 "src/lexer-keywords.txt" + {"f64x2.gt", TokenType::Compare, Opcode::F64X2Gt}, +#line 362 "src/lexer-keywords.txt" + {"i64.atomic.rmw32.add_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32AddU}, + {""}, +#line 416 "src/lexer-keywords.txt" + {"i64.or", TokenType::Binary, Opcode::I64Or}, +#line 279 "src/lexer-keywords.txt" + {"i32.or", TokenType::Binary, Opcode::I32Or}, +#line 124 "src/lexer-keywords.txt" + {"f64.gt", TokenType::Compare, Opcode::F64Gt}, +#line 67 "src/lexer-keywords.txt" + {"f32.gt", TokenType::Compare, Opcode::F32Gt}, + {""}, {""}, {""}, {""}, {""}, +#line 148 "src/lexer-keywords.txt" + {"f64x2.ge", TokenType::Compare, Opcode::F64X2Ge}, +#line 448 "src/lexer-keywords.txt" + {"i64x2.gt_s", TokenType::Binary, Opcode::I64X2GtS}, +#line 305 "src/lexer-keywords.txt" + {"i32x4.all_true", TokenType::Unary, Opcode::I32X4AllTrue}, + {""}, {""}, +#line 123 "src/lexer-keywords.txt" + {"f64.ge", TokenType::Compare, Opcode::F64Ge}, +#line 66 "src/lexer-keywords.txt" + {"f32.ge", TokenType::Compare, Opcode::F32Ge}, +#line 450 "src/lexer-keywords.txt" + {"i64x2.ge_s", TokenType::Binary, Opcode::I64X2GeS}, + {""}, {""}, {""}, {""}, {""}, +#line 401 "src/lexer-keywords.txt" + {"i64.gt_s", TokenType::Compare, Opcode::I64GtS}, +#line 266 "src/lexer-keywords.txt" + {"i32.gt_s", TokenType::Compare, Opcode::I32GtS}, + {""}, {""}, +#line 402 "src/lexer-keywords.txt" + {"i64.gt_u", TokenType::Compare, Opcode::I64GtU}, +#line 267 "src/lexer-keywords.txt" + {"i32.gt_u", TokenType::Compare, Opcode::I32GtU}, +#line 399 "src/lexer-keywords.txt" + {"i64.ge_s", TokenType::Compare, Opcode::I64GeS}, +#line 264 "src/lexer-keywords.txt" + {"i32.ge_s", TokenType::Compare, Opcode::I32GeS}, + {""}, {""}, +#line 400 "src/lexer-keywords.txt" + {"i64.ge_u", TokenType::Compare, Opcode::I64GeU}, +#line 265 "src/lexer-keywords.txt" + {"i32.ge_u", TokenType::Compare, Opcode::I32GeU}, + {""}, +#line 88 "src/lexer-keywords.txt" + {"f32x4.div", TokenType::Binary, Opcode::F32X4Div}, + {""}, {""}, +#line 359 "src/lexer-keywords.txt" + {"i64.atomic.rmw16.sub_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16SubU}, +#line 235 "src/lexer-keywords.txt" + {"i32.atomic.rmw16.sub_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16SubU}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 103 "src/lexer-keywords.txt" + {"f32x4.pmin", TokenType::Binary, Opcode::F32X4PMin}, +#line 91 "src/lexer-keywords.txt" + {"f32x4.floor", TokenType::Unary, Opcode::F32X4Floor}, +#line 571 "src/lexer-keywords.txt" + {"v128.load64_zero", TokenType::Load, Opcode::V128Load64Zero}, +#line 570 "src/lexer-keywords.txt" + {"v128.load32_zero", TokenType::Load, Opcode::V128Load32Zero}, +#line 586 "src/lexer-keywords.txt" + {"v128.store64_lane", TokenType::SimdStoreLane, Opcode::V128Store64Lane}, +#line 353 "src/lexer-keywords.txt" + {"i64.atomic.load8_u", TokenType::AtomicLoad, Opcode::I64AtomicLoad8U}, +#line 229 "src/lexer-keywords.txt" + {"i32.atomic.load8_u", TokenType::AtomicLoad, Opcode::I32AtomicLoad8U}, +#line 530 "src/lexer-keywords.txt" + {"nop", TokenType::Nop, Opcode::Nop}, + {""}, +#line 136 "src/lexer-keywords.txt" + {"f64.sqrt", TokenType::Unary, Opcode::F64Sqrt}, +#line 78 "src/lexer-keywords.txt" + {"f32.sqrt", TokenType::Unary, Opcode::F32Sqrt}, + {""}, +#line 36 "src/lexer-keywords.txt" + {"catch", TokenType::Catch, Opcode::Catch}, +#line 175 "src/lexer-keywords.txt" + {"global", TokenType::Global}, + {""}, {""}, +#line 174 "src/lexer-keywords.txt" + {"global.set", TokenType::GlobalSet, Opcode::GlobalSet}, + {""}, {""}, +#line 146 "src/lexer-keywords.txt" + {"f64x2.extract_lane", TokenType::SimdLaneOp, Opcode::F64X2ExtractLane}, +#line 128 "src/lexer-keywords.txt" + {"f64.max", TokenType::Binary, Opcode::F64Max}, +#line 71 "src/lexer-keywords.txt" + {"f32.max", TokenType::Binary, Opcode::F32Max}, +#line 441 "src/lexer-keywords.txt" + {"i64x2.extract_lane", TokenType::SimdLaneOp, Opcode::I64X2ExtractLane}, +#line 512 "src/lexer-keywords.txt" + {"local.get", TokenType::LocalGet, Opcode::LocalGet}, +#line 423 "src/lexer-keywords.txt" + {"i64.shl", TokenType::Binary, Opcode::I64Shl}, +#line 286 "src/lexer-keywords.txt" + {"i32.shl", TokenType::Binary, Opcode::I32Shl}, + {""}, {""}, {""}, {""}, +#line 319 "src/lexer-keywords.txt" + {"i32x4.max_s", TokenType::Binary, Opcode::I32X4MaxS}, + {""}, {""}, {""}, +#line 320 "src/lexer-keywords.txt" + {"i32x4.max_u", TokenType::Binary, Opcode::I32X4MaxU}, +#line 105 "src/lexer-keywords.txt" + {"f32x4.splat", TokenType::Unary, Opcode::F32X4Splat}, + {""}, +#line 455 "src/lexer-keywords.txt" + {"i64x2.extend_low_i32x4_s", TokenType::Unary, Opcode::I64X2ExtendLowI32X4S}, +#line 331 "src/lexer-keywords.txt" + {"i32x4.splat", TokenType::Unary, Opcode::I32X4Splat}, +#line 457 "src/lexer-keywords.txt" + {"i64x2.extend_low_i32x4_u", TokenType::Unary, Opcode::I64X2ExtendLowI32X4U}, +#line 580 "src/lexer-keywords.txt" + {"v128.load16_lane", TokenType::SimdLoadLane, Opcode::V128Load16Lane}, +#line 365 "src/lexer-keywords.txt" + {"i64.atomic.rmw32.or_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32OrU}, + {""}, +#line 356 "src/lexer-keywords.txt" + {"i64.atomic.rmw16.and_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16AndU}, +#line 232 "src/lexer-keywords.txt" + {"i32.atomic.rmw16.and_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16AndU}, + {""}, {""}, +#line 393 "src/lexer-keywords.txt" + {"i64.eqz", TokenType::Convert, Opcode::I64Eqz}, +#line 261 "src/lexer-keywords.txt" + {"i32.eqz", TokenType::Convert, Opcode::I32Eqz}, + {""}, +#line 465 "src/lexer-keywords.txt" + {"i64x2.extmul_low_i32x4_s", TokenType::Binary, Opcode::I64X2ExtmulLowI32X4S}, + {""}, +#line 467 "src/lexer-keywords.txt" + {"i64x2.extmul_low_i32x4_u", TokenType::Binary, Opcode::I64X2ExtmulLowI32X4U}, +#line 396 "src/lexer-keywords.txt" + {"i64.extend8_s", TokenType::Unary, Opcode::I64Extend8S}, +#line 263 "src/lexer-keywords.txt" + {"i32.extend8_s", TokenType::Unary, Opcode::I32Extend8S}, +#line 206 "src/lexer-keywords.txt" + {"i16x8.ne", TokenType::Compare, Opcode::I16X8Ne}, + {""}, +#line 584 "src/lexer-keywords.txt" + {"v128.store16_lane", TokenType::SimdStoreLane, Opcode::V128Store16Lane}, + {""}, {""}, +#line 162 "src/lexer-keywords.txt" + {"f64x2.sqrt", TokenType::Unary, Opcode::F64X2Sqrt}, +#line 355 "src/lexer-keywords.txt" + {"i64.atomic.rmw16.add_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16AddU}, +#line 231 "src/lexer-keywords.txt" + {"i32.atomic.rmw16.add_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16AddU}, + {""}, +#line 199 "src/lexer-keywords.txt" + {"i16x8.min_s", TokenType::Binary, Opcode::I16X8MinS}, + {""}, {""}, {""}, +#line 200 "src/lexer-keywords.txt" + {"i16x8.min_u", TokenType::Binary, Opcode::I16X8MinU}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 152 "src/lexer-keywords.txt" + {"f64x2.max", TokenType::Binary, Opcode::F64X2Max}, + {""}, {""}, {""}, {""}, +#line 460 "src/lexer-keywords.txt" + {"i64x2.shl", TokenType::Binary, Opcode::I64X2Shl}, + {""}, +#line 195 "src/lexer-keywords.txt" + {"i16x8.lt_s", TokenType::Compare, Opcode::I16X8LtS}, +#line 214 "src/lexer-keywords.txt" + {"i16x8.sub", TokenType::Binary, Opcode::I16X8Sub}, +#line 196 "src/lexer-keywords.txt" + {"i16x8.lt_u", TokenType::Compare, Opcode::I16X8LtU}, + {""}, +#line 42 "src/lexer-keywords.txt" + {"delegate", TokenType::Delegate}, + {""}, +#line 191 "src/lexer-keywords.txt" + {"i16x8.le_s", TokenType::Compare, Opcode::I16X8LeS}, + {""}, +#line 192 "src/lexer-keywords.txt" + {"i16x8.le_u", TokenType::Compare, Opcode::I16X8LeU}, +#line 165 "src/lexer-keywords.txt" + {"f64x2.convert_low_i32x4_s", TokenType::Unary, Opcode::F64X2ConvertLowI32X4S}, + {""}, +#line 166 "src/lexer-keywords.txt" + {"f64x2.convert_low_i32x4_u", TokenType::Unary, Opcode::F64X2ConvertLowI32X4U}, +#line 77 "src/lexer-keywords.txt" + {"f32.reinterpret_i32", TokenType::Convert, Opcode::F32ReinterpretI32}, + {""}, {""}, {""}, +#line 437 "src/lexer-keywords.txt" + {"i64.trunc_sat_f64_s", TokenType::Convert, Opcode::I64TruncSatF64S}, +#line 299 "src/lexer-keywords.txt" + {"i32.trunc_sat_f64_s", TokenType::Convert, Opcode::I32TruncSatF64S}, + {""}, {""}, +#line 438 "src/lexer-keywords.txt" + {"i64.trunc_sat_f64_u", TokenType::Convert, Opcode::I64TruncSatF64U}, +#line 300 "src/lexer-keywords.txt" + {"i32.trunc_sat_f64_u", TokenType::Convert, Opcode::I32TruncSatF64U}, + {""}, {""}, +#line 424 "src/lexer-keywords.txt" + {"i64.shr_s", TokenType::Binary, Opcode::I64ShrS}, +#line 287 "src/lexer-keywords.txt" + {"i32.shr_s", TokenType::Binary, Opcode::I32ShrS}, + {""}, {""}, +#line 425 "src/lexer-keywords.txt" + {"i64.shr_u", TokenType::Binary, Opcode::I64ShrU}, +#line 288 "src/lexer-keywords.txt" + {"i32.shr_u", TokenType::Binary, Opcode::I32ShrU}, +#line 158 "src/lexer-keywords.txt" + {"f64x2.pmax", TokenType::Binary, Opcode::F64X2PMax}, + {""}, {""}, {""}, +#line 201 "src/lexer-keywords.txt" + {"i16x8.mul", TokenType::Binary, Opcode::I16X8Mul}, +#line 523 "src/lexer-keywords.txt" + {"memory.init", TokenType::MemoryInit, Opcode::MemoryInit}, +#line 442 "src/lexer-keywords.txt" + {"v128.load32x2_s", TokenType::Load, Opcode::V128Load32X2S}, + {""}, +#line 443 "src/lexer-keywords.txt" + {"v128.load32x2_u", TokenType::Load, Opcode::V128Load32X2U}, + {""}, {""}, {""}, +#line 177 "src/lexer-keywords.txt" + {"i16x8.abs", TokenType::Unary, Opcode::I16X8Abs}, + {""}, {""}, +#line 524 "src/lexer-keywords.txt" + {"memory.size", TokenType::MemorySize, Opcode::MemorySize}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, +#line 601 "src/lexer-keywords.txt" + {"f64.convert_s/i64", TokenType::Convert, Opcode::F64ConvertI64S}, +#line 595 "src/lexer-keywords.txt" + {"f32.convert_s/i64", TokenType::Convert, Opcode::F32ConvertI64S}, +#line 603 "src/lexer-keywords.txt" + {"f64.convert_u/i64", TokenType::Convert, Opcode::F64ConvertI64U}, +#line 597 "src/lexer-keywords.txt" + {"f32.convert_u/i64", TokenType::Convert, Opcode::F32ConvertI64U}, + {""}, {""}, {""}, +#line 607 "src/lexer-keywords.txt" + {"get_local", TokenType::LocalGet, Opcode::LocalGet}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 461 "src/lexer-keywords.txt" + {"i64x2.shr_s", TokenType::Binary, Opcode::I64X2ShrS}, +#line 358 "src/lexer-keywords.txt" + {"i64.atomic.rmw16.or_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16OrU}, +#line 234 "src/lexer-keywords.txt" + {"i32.atomic.rmw16.or_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16OrU}, +#line 629 "src/lexer-keywords.txt" + {"set_global", TokenType::GlobalSet, Opcode::GlobalSet}, +#line 462 "src/lexer-keywords.txt" + {"i64x2.shr_u", TokenType::Binary, Opcode::I64X2ShrU}, +#line 212 "src/lexer-keywords.txt" + {"i16x8.sub_sat_s", TokenType::Binary, Opcode::I16X8SubSatS}, + {""}, +#line 213 "src/lexer-keywords.txt" + {"i16x8.sub_sat_u", TokenType::Binary, Opcode::I16X8SubSatU}, + {""}, {""}, {""}, +#line 180 "src/lexer-keywords.txt" + {"i16x8.add", TokenType::Binary, Opcode::I16X8Add}, + {""}, {""}, {""}, {""}, {""}, +#line 135 "src/lexer-keywords.txt" + {"f64.reinterpret_i64", TokenType::Convert, Opcode::F64ReinterpretI64}, + {""}, +#line 536 "src/lexer-keywords.txt" + {"ref.func", TokenType::RefFunc, Opcode::RefFunc}, + {""}, {""}, {""}, {""}, {""}, +#line 521 "src/lexer-keywords.txt" + {"memory.fill", TokenType::MemoryFill, Opcode::MemoryFill}, + {""}, {""}, {""}, {""}, {""}, +#line 134 "src/lexer-keywords.txt" + {"f64.promote_f32", TokenType::Convert, Opcode::F64PromoteF32}, +#line 183 "src/lexer-keywords.txt" + {"i16x8.bitmask", TokenType::Unary, Opcode::I16X8Bitmask}, + {""}, {""}, +#line 93 "src/lexer-keywords.txt" + {"f32x4.gt", TokenType::Compare, Opcode::F32X4Gt}, +#line 160 "src/lexer-keywords.txt" + {"f64x2.replace_lane", TokenType::SimdLaneOp, Opcode::F64X2ReplaceLane}, +#line 578 "src/lexer-keywords.txt" + {"v128.load8_splat", TokenType::Load, Opcode::V128Load8Splat}, + {""}, +#line 459 "src/lexer-keywords.txt" + {"i64x2.replace_lane", TokenType::SimdLaneOp, Opcode::I64X2ReplaceLane}, + {""}, +#line 599 "src/lexer-keywords.txt" + {"f32.reinterpret/i32", TokenType::Convert, Opcode::F32ReinterpretI32}, + {""}, {""}, {""}, {""}, {""}, +#line 92 "src/lexer-keywords.txt" + {"f32x4.ge", TokenType::Compare, Opcode::F32X4Ge}, +#line 311 "src/lexer-keywords.txt" + {"i32x4.gt_s", TokenType::Compare, Opcode::I32X4GtS}, + {""}, +#line 312 "src/lexer-keywords.txt" + {"i32x4.gt_u", TokenType::Compare, Opcode::I32X4GtU}, + {""}, {""}, {""}, +#line 309 "src/lexer-keywords.txt" + {"i32x4.ge_s", TokenType::Compare, Opcode::I32X4GeS}, + {""}, +#line 310 "src/lexer-keywords.txt" + {"i32x4.ge_u", TokenType::Compare, Opcode::I32X4GeU}, + {""}, {""}, {""}, {""}, +#line 368 "src/lexer-keywords.txt" + {"i64.atomic.rmw32.xor_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32XorU}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 574 "src/lexer-keywords.txt" + {"v128.xor", TokenType::Binary, Opcode::V128Xor}, + {""}, {""}, {""}, +#line 577 "src/lexer-keywords.txt" + {"v128.load64_splat", TokenType::Load, Opcode::V128Load64Splat}, +#line 576 "src/lexer-keywords.txt" + {"v128.load32_splat", TokenType::Load, Opcode::V128Load32Splat}, +#line 552 "src/lexer-keywords.txt" + {"table.grow", TokenType::TableGrow, Opcode::TableGrow}, + {""}, {""}, +#line 315 "src/lexer-keywords.txt" + {"v128.load16x4_s", TokenType::Load, Opcode::V128Load16X4S}, + {""}, +#line 316 "src/lexer-keywords.txt" + {"v128.load16x4_u", TokenType::Load, Opcode::V128Load16X4U}, + {""}, +#line 372 "src/lexer-keywords.txt" + {"i64.atomic.rmw8.or_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8OrU}, +#line 241 "src/lexer-keywords.txt" + {"i32.atomic.rmw8.or_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8OrU}, + {""}, +#line 178 "src/lexer-keywords.txt" + {"i16x8.add_sat_s", TokenType::Binary, Opcode::I16X8AddSatS}, + {""}, +#line 179 "src/lexer-keywords.txt" + {"i16x8.add_sat_u", TokenType::Binary, Opcode::I16X8AddSatU}, + {""}, {""}, +#line 25 "src/lexer-keywords.txt" + {"assert_trap", TokenType::AssertTrap}, + {""}, {""}, +#line 323 "src/lexer-keywords.txt" + {"i32x4.dot_i16x8_s", TokenType::Binary, Opcode::I32X4DotI16X8S}, +#line 367 "src/lexer-keywords.txt" + {"i64.atomic.rmw32.xchg_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32XchgU}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 90 "src/lexer-keywords.txt" + {"f32x4.extract_lane", TokenType::SimdLaneOp, Opcode::F32X4ExtractLane}, + {""}, +#line 497 "src/lexer-keywords.txt" + {"i8x16.ne", TokenType::Compare, Opcode::I8X16Ne}, +#line 308 "src/lexer-keywords.txt" + {"i32x4.extract_lane", TokenType::SimdLaneOp, Opcode::I32X4ExtractLane}, + {""}, {""}, {""}, {""}, +#line 605 "src/lexer-keywords.txt" + {"f64.reinterpret/i64", TokenType::Convert, Opcode::F64ReinterpretI64}, + {""}, {""}, +#line 491 "src/lexer-keywords.txt" + {"i8x16.min_s", TokenType::Binary, Opcode::I8X16MinS}, + {""}, {""}, {""}, +#line 492 "src/lexer-keywords.txt" + {"i8x16.min_u", TokenType::Binary, Opcode::I8X16MinU}, +#line 351 "src/lexer-keywords.txt" + {"i64.atomic.load16_u", TokenType::AtomicLoad, Opcode::I64AtomicLoad16U}, +#line 228 "src/lexer-keywords.txt" + {"i32.atomic.load16_u", TokenType::AtomicLoad, Opcode::I32AtomicLoad16U}, + {""}, {""}, {""}, +#line 561 "src/lexer-keywords.txt" + {"unreachable", TokenType::Unreachable, Opcode::Unreachable}, +#line 604 "src/lexer-keywords.txt" + {"f64.promote/f32", TokenType::Convert, Opcode::F64PromoteF32}, +#line 583 "src/lexer-keywords.txt" + {"v128.store8_lane", TokenType::SimdStoreLane, Opcode::V128Store8Lane}, + {""}, {""}, {""}, {""}, {""}, +#line 122 "src/lexer-keywords.txt" + {"f64.floor", TokenType::Unary, Opcode::F64Floor}, +#line 65 "src/lexer-keywords.txt" + {"f32.floor", TokenType::Unary, Opcode::F32Floor}, +#line 487 "src/lexer-keywords.txt" + {"i8x16.lt_s", TokenType::Compare, Opcode::I8X16LtS}, +#line 505 "src/lexer-keywords.txt" + {"i8x16.sub", TokenType::Binary, Opcode::I8X16Sub}, +#line 488 "src/lexer-keywords.txt" + {"i8x16.lt_u", TokenType::Compare, Opcode::I8X16LtU}, +#line 344 "src/lexer-keywords.txt" + {"i32x4.extend_low_i16x8_s", TokenType::Unary, Opcode::I32X4ExtendLowI16X8S}, +#line 539 "src/lexer-keywords.txt" + {"register", TokenType::Register}, +#line 345 "src/lexer-keywords.txt" + {"i32x4.extend_low_i16x8_u", TokenType::Unary, Opcode::I32X4ExtendLowI16X8U}, +#line 485 "src/lexer-keywords.txt" + {"i8x16.le_s", TokenType::Compare, Opcode::I8X16LeS}, + {""}, +#line 486 "src/lexer-keywords.txt" + {"i8x16.le_u", TokenType::Compare, Opcode::I8X16LeU}, +#line 528 "src/lexer-keywords.txt" + {"nan:arithmetic", TokenType::NanArithmetic}, +#line 106 "src/lexer-keywords.txt" + {"f32x4.sqrt", TokenType::Unary, Opcode::F32X4Sqrt}, + {""}, {""}, {""}, {""}, {""}, +#line 335 "src/lexer-keywords.txt" + {"i32x4.extmul_low_i16x8_s", TokenType::Binary, Opcode::I32X4ExtmulLowI16X8S}, + {""}, +#line 337 "src/lexer-keywords.txt" + {"i32x4.extmul_low_i16x8_u", TokenType::Binary, Opcode::I32X4ExtmulLowI16X8U}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 96 "src/lexer-keywords.txt" + {"f32x4.max", TokenType::Binary, Opcode::F32X4Max}, + {""}, {""}, {""}, {""}, +#line 328 "src/lexer-keywords.txt" + {"i32x4.shl", TokenType::Binary, Opcode::I32X4Shl}, + {""}, {""}, {""}, +#line 361 "src/lexer-keywords.txt" + {"i64.atomic.rmw16.xor_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16XorU}, +#line 237 "src/lexer-keywords.txt" + {"i32.atomic.rmw16.xor_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16XorU}, + {""}, {""}, {""}, {""}, +#line 471 "src/lexer-keywords.txt" + {"i8x16.abs", TokenType::Unary, Opcode::I8X16Abs}, +#line 302 "src/lexer-keywords.txt" + {"i32.wrap_i64", TokenType::Convert, Opcode::I32WrapI64}, + {""}, {""}, +#line 20 "src/lexer-keywords.txt" + {"array", Type::Array, TokenType::Array}, +#line 506 "src/lexer-keywords.txt" + {"i8x16", TokenType::I8X16}, + {""}, {""}, +#line 575 "src/lexer-keywords.txt" + {"v128.load16_splat", TokenType::Load, Opcode::V128Load16Splat}, +#line 417 "src/lexer-keywords.txt" + {"i64.popcnt", TokenType::Unary, Opcode::I64Popcnt}, +#line 280 "src/lexer-keywords.txt" + {"i32.popcnt", TokenType::Unary, Opcode::I32Popcnt}, + {""}, +#line 181 "src/lexer-keywords.txt" + {"i16x8.all_true", TokenType::Unary, Opcode::I16X8AllTrue}, + {""}, +#line 86 "src/lexer-keywords.txt" + {"f32x4.convert_i32x4_s", TokenType::Unary, Opcode::F32X4ConvertI32X4S}, + {""}, +#line 87 "src/lexer-keywords.txt" + {"f32x4.convert_i32x4_u", TokenType::Unary, Opcode::F32X4ConvertI32X4U}, + {""}, +#line 45 "src/lexer-keywords.txt" + {"elem.drop", TokenType::ElemDrop, Opcode::ElemDrop}, + {""}, {""}, +#line 221 "src/lexer-keywords.txt" + {"i16x8", TokenType::I16X8}, +#line 102 "src/lexer-keywords.txt" + {"f32x4.pmax", TokenType::Binary, Opcode::F32X4PMax}, + {""}, {""}, {""}, +#line 39 "src/lexer-keywords.txt" + {"data.drop", TokenType::DataDrop, Opcode::DataDrop}, + {""}, +#line 360 "src/lexer-keywords.txt" + {"i64.atomic.rmw16.xchg_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16XchgU}, +#line 236 "src/lexer-keywords.txt" + {"i32.atomic.rmw16.xchg_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16XchgU}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 503 "src/lexer-keywords.txt" + {"i8x16.sub_sat_s", TokenType::Binary, Opcode::I8X16SubSatS}, + {""}, +#line 504 "src/lexer-keywords.txt" + {"i8x16.sub_sat_u", TokenType::Binary, Opcode::I8X16SubSatU}, + {""}, {""}, {""}, +#line 474 "src/lexer-keywords.txt" + {"i8x16.add", TokenType::Binary, Opcode::I8X16Add}, + {""}, {""}, {""}, +#line 121 "src/lexer-keywords.txt" + {"f64.eq", TokenType::Compare, Opcode::F64Eq}, +#line 64 "src/lexer-keywords.txt" + {"f32.eq", TokenType::Compare, Opcode::F32Eq}, + {""}, +#line 392 "src/lexer-keywords.txt" + {"i64.eq", TokenType::Compare, Opcode::I64Eq}, +#line 260 "src/lexer-keywords.txt" + {"i32.eq", TokenType::Compare, Opcode::I32Eq}, + {""}, {""}, +#line 470 "src/lexer-keywords.txt" + {"i64.xor", TokenType::Binary, Opcode::I64Xor}, +#line 348 "src/lexer-keywords.txt" + {"i32.xor", TokenType::Binary, Opcode::I32Xor}, +#line 623 "src/lexer-keywords.txt" + {"i64.trunc_s:sat/f32", TokenType::Convert, Opcode::I64TruncSatF32S}, +#line 611 "src/lexer-keywords.txt" + {"i32.trunc_s:sat/f32", TokenType::Convert, Opcode::I32TruncSatF32S}, +#line 627 "src/lexer-keywords.txt" + {"i64.trunc_u:sat/f32", TokenType::Convert, Opcode::I64TruncSatF32U}, +#line 615 "src/lexer-keywords.txt" + {"i32.trunc_u:sat/f32", TokenType::Convert, Opcode::I32TruncSatF32U}, + {""}, {""}, {""}, {""}, +#line 477 "src/lexer-keywords.txt" + {"i8x16.bitmask", TokenType::Unary, Opcode::I8X16Bitmask}, +#line 167 "src/lexer-keywords.txt" + {"f64x2.promote_low_f32x4", TokenType::Unary, Opcode::F64X2PromoteLowF32X4}, +#line 329 "src/lexer-keywords.txt" + {"i32x4.shr_s", TokenType::Binary, Opcode::I32X4ShrS}, + {""}, {""}, {""}, +#line 330 "src/lexer-keywords.txt" + {"i32x4.shr_u", TokenType::Binary, Opcode::I32X4ShrU}, + {""}, +#line 518 "src/lexer-keywords.txt" + {"memory.atomic.wait32", TokenType::AtomicWait, Opcode::MemoryAtomicWait32}, + {""}, {""}, {""}, {""}, {""}, +#line 49 "src/lexer-keywords.txt" + {"tag", TokenType::Tag}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 197 "src/lexer-keywords.txt" + {"i16x8.max_s", TokenType::Binary, Opcode::I16X8MaxS}, + {""}, {""}, {""}, +#line 198 "src/lexer-keywords.txt" + {"i16x8.max_u", TokenType::Binary, Opcode::I16X8MaxU}, + {""}, {""}, {""}, +#line 211 "src/lexer-keywords.txt" + {"i16x8.splat", TokenType::Unary, Opcode::I16X8Splat}, + {""}, +#line 593 "src/lexer-keywords.txt" + {"anyfunc", Type::FuncRef}, + {""}, {""}, {""}, {""}, {""}, +#line 104 "src/lexer-keywords.txt" + {"f32x4.replace_lane", TokenType::SimdLaneOp, Opcode::F32X4ReplaceLane}, + {""}, +#line 617 "src/lexer-keywords.txt" + {"i32.wrap/i64", TokenType::Convert, Opcode::I32WrapI64}, +#line 327 "src/lexer-keywords.txt" + {"i32x4.replace_lane", TokenType::SimdLaneOp, Opcode::I32X4ReplaceLane}, + {""}, {""}, +#line 385 "src/lexer-keywords.txt" + {"i64.atomic.store8", TokenType::AtomicStore, Opcode::I64AtomicStore8}, +#line 253 "src/lexer-keywords.txt" + {"i32.atomic.store8", TokenType::AtomicStore, Opcode::I32AtomicStore8}, + {""}, +#line 145 "src/lexer-keywords.txt" + {"f64x2.eq", TokenType::Compare, Opcode::F64X2Eq}, + {""}, {""}, +#line 445 "src/lexer-keywords.txt" + {"i64x2.eq", TokenType::Binary, Opcode::I64X2Eq}, + {""}, {""}, {""}, +#line 472 "src/lexer-keywords.txt" + {"i8x16.add_sat_s", TokenType::Binary, Opcode::I8X16AddSatS}, + {""}, +#line 473 "src/lexer-keywords.txt" + {"i8x16.add_sat_u", TokenType::Binary, Opcode::I8X16AddSatU}, + {""}, {""}, {""}, {""}, {""}, +#line 193 "src/lexer-keywords.txt" + {"v128.load8x8_s", TokenType::Load, Opcode::V128Load8X8S}, + {""}, {""}, {""}, +#line 194 "src/lexer-keywords.txt" + {"v128.load8x8_u", TokenType::Load, Opcode::V128Load8X8U}, + {""}, {""}, {""}, {""}, {""}, +#line 624 "src/lexer-keywords.txt" + {"i64.trunc_s:sat/f64", TokenType::Convert, Opcode::I64TruncSatF64S}, +#line 612 "src/lexer-keywords.txt" + {"i32.trunc_s:sat/f64", TokenType::Convert, Opcode::I32TruncSatF64S}, +#line 628 "src/lexer-keywords.txt" + {"i64.trunc_u:sat/f64", TokenType::Convert, Opcode::I64TruncSatF64U}, +#line 616 "src/lexer-keywords.txt" + {"i32.trunc_u:sat/f64", TokenType::Convert, Opcode::I32TruncSatF64U}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, +#line 519 "src/lexer-keywords.txt" + {"memory.atomic.wait64", TokenType::AtomicWait, Opcode::MemoryAtomicWait64}, + {""}, +#line 132 "src/lexer-keywords.txt" + {"f64.neg", TokenType::Unary, Opcode::F64Neg}, +#line 75 "src/lexer-keywords.txt" + {"f32.neg", TokenType::Unary, Opcode::F32Neg}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, +#line 156 "src/lexer-keywords.txt" + {"f64x2.neg", TokenType::Unary, Opcode::F64X2Neg}, + {""}, {""}, +#line 452 "src/lexer-keywords.txt" + {"i64x2.neg", TokenType::Unary, Opcode::I64X2Neg}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 588 "src/lexer-keywords.txt" + {"i8x16.swizzle", TokenType::Binary, Opcode::I8X16Swizzle}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, +#line 364 "src/lexer-keywords.txt" + {"i64.atomic.rmw32.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw32CmpxchgU}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 333 "src/lexer-keywords.txt" + {"i32x4.extadd_pairwise_i16x8_s", TokenType::Unary, Opcode::I32X4ExtaddPairwiseI16X8S}, +#line 173 "src/lexer-keywords.txt" + {"global.get", TokenType::GlobalGet, Opcode::GlobalGet}, +#line 334 "src/lexer-keywords.txt" + {"i32x4.extadd_pairwise_i16x8_u", TokenType::Unary, Opcode::I32X4ExtaddPairwiseI16X8U}, +#line 475 "src/lexer-keywords.txt" + {"i8x16.all_true", TokenType::Unary, Opcode::I8X16AllTrue}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, +#line 549 "src/lexer-keywords.txt" + {"table.copy", TokenType::TableCopy, Opcode::TableCopy}, + {""}, {""}, +#line 340 "src/lexer-keywords.txt" + {"i32x4.trunc_sat_f32x4_s", TokenType::Unary, Opcode::I32X4TruncSatF32X4S}, + {""}, {""}, {""}, +#line 341 "src/lexer-keywords.txt" + {"i32x4.trunc_sat_f32x4_u", TokenType::Unary, Opcode::I32X4TruncSatF32X4U}, + {""}, {""}, +#line 569 "src/lexer-keywords.txt" + {"v128.any_true", TokenType::Unary, Opcode::V128AnyTrue}, + {""}, {""}, +#line 109 "src/lexer-keywords.txt" + {"f32x4.demote_f64x2_zero", TokenType::Unary, Opcode::F32X4DemoteF64X2Zero}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 189 "src/lexer-keywords.txt" + {"i16x8.gt_s", TokenType::Compare, Opcode::I16X8GtS}, + {""}, +#line 190 "src/lexer-keywords.txt" + {"i16x8.gt_u", TokenType::Compare, Opcode::I16X8GtU}, + {""}, {""}, {""}, +#line 187 "src/lexer-keywords.txt" + {"i16x8.ge_s", TokenType::Compare, Opcode::I16X8GeS}, + {""}, +#line 188 "src/lexer-keywords.txt" + {"i16x8.ge_u", TokenType::Compare, Opcode::I16X8GeU}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, +#line 489 "src/lexer-keywords.txt" + {"i8x16.max_s", TokenType::Binary, Opcode::I8X16MaxS}, +#line 281 "src/lexer-keywords.txt" + {"i32.reinterpret_f32", TokenType::Convert, Opcode::I32ReinterpretF32}, + {""}, {""}, +#line 490 "src/lexer-keywords.txt" + {"i8x16.max_u", TokenType::Binary, Opcode::I8X16MaxU}, + {""}, {""}, {""}, +#line 502 "src/lexer-keywords.txt" + {"i8x16.splat", TokenType::Unary, Opcode::I8X16Splat}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, +#line 382 "src/lexer-keywords.txt" + {"i64.atomic.rmw.xor", TokenType::AtomicRmw, Opcode::I64AtomicRmwXor}, +#line 251 "src/lexer-keywords.txt" + {"i32.atomic.rmw.xor", TokenType::AtomicRmw, Opcode::I32AtomicRmwXor}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 185 "src/lexer-keywords.txt" + {"i16x8.extract_lane_s", TokenType::SimdLaneOp, Opcode::I16X8ExtractLaneS}, + {""}, +#line 186 "src/lexer-keywords.txt" + {"i16x8.extract_lane_u", TokenType::SimdLaneOp, Opcode::I16X8ExtractLaneU}, + {""}, +#line 357 "src/lexer-keywords.txt" + {"i64.atomic.rmw16.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw16CmpxchgU}, +#line 233 "src/lexer-keywords.txt" + {"i32.atomic.rmw16.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmw16CmpxchgU}, + {""}, {""}, {""}, +#line 375 "src/lexer-keywords.txt" + {"i64.atomic.rmw8.xor_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8XorU}, +#line 244 "src/lexer-keywords.txt" + {"i32.atomic.rmw8.xor_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8XorU}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 371 "src/lexer-keywords.txt" + {"i64.atomic.rmw8.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw8CmpxchgU}, +#line 240 "src/lexer-keywords.txt" + {"i32.atomic.rmw8.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmw8CmpxchgU}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 89 "src/lexer-keywords.txt" + {"f32x4.eq", TokenType::Compare, Opcode::F32X4Eq}, + {""}, {""}, +#line 307 "src/lexer-keywords.txt" + {"i32x4.eq", TokenType::Compare, Opcode::I32X4Eq}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 606 "src/lexer-keywords.txt" + {"get_global", TokenType::GlobalGet, Opcode::GlobalGet}, + {""}, {""}, +#line 346 "src/lexer-keywords.txt" + {"i32x4.trunc_sat_f64x2_s_zero", TokenType::Unary, Opcode::I32X4TruncSatF64X2SZero}, + {""}, +#line 347 "src/lexer-keywords.txt" + {"i32x4.trunc_sat_f64x2_u_zero", TokenType::Unary, Opcode::I32X4TruncSatF64X2UZero}, + {""}, {""}, {""}, {""}, {""}, +#line 418 "src/lexer-keywords.txt" + {"i64.reinterpret_f64", TokenType::Convert, Opcode::I64ReinterpretF64}, + {""}, {""}, {""}, {""}, {""}, +#line 208 "src/lexer-keywords.txt" + {"i16x8.shl", TokenType::Binary, Opcode::I16X8Shl}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 608 "src/lexer-keywords.txt" + {"i32.reinterpret/f32", TokenType::Convert, Opcode::I32ReinterpretF32}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, +#line 100 "src/lexer-keywords.txt" + {"f32x4.neg", TokenType::Unary, Opcode::F32X4Neg}, + {""}, {""}, +#line 325 "src/lexer-keywords.txt" + {"i32x4.neg", TokenType::Unary, Opcode::I32X4Neg}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 559 "src/lexer-keywords.txt" + {"try", TokenType::Try, Opcode::Try}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 209 "src/lexer-keywords.txt" + {"i16x8.shr_s", TokenType::Binary, Opcode::I16X8ShrS}, + {""}, {""}, {""}, +#line 210 "src/lexer-keywords.txt" + {"i16x8.shr_u", TokenType::Binary, Opcode::I16X8ShrU}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 525 "src/lexer-keywords.txt" + {"memory", TokenType::Memory}, + {""}, +#line 483 "src/lexer-keywords.txt" + {"i8x16.gt_s", TokenType::Compare, Opcode::I8X16GtS}, + {""}, +#line 484 "src/lexer-keywords.txt" + {"i8x16.gt_u", TokenType::Compare, Opcode::I8X16GtU}, + {""}, {""}, {""}, +#line 481 "src/lexer-keywords.txt" + {"i8x16.ge_s", TokenType::Compare, Opcode::I8X16GeS}, +#line 620 "src/lexer-keywords.txt" + {"i64.reinterpret/f64", TokenType::Convert, Opcode::I64ReinterpretF64}, +#line 482 "src/lexer-keywords.txt" + {"i8x16.ge_u", TokenType::Compare, Opcode::I8X16GeU}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 28 "src/lexer-keywords.txt" + {"binary", TokenType::Bin}, + {""}, {""}, +#line 182 "src/lexer-keywords.txt" + {"i16x8.avgr_u", TokenType::Binary, Opcode::I16X8AvgrU}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 207 "src/lexer-keywords.txt" + {"i16x8.replace_lane", TokenType::SimdLaneOp, Opcode::I16X8ReplaceLane}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 479 "src/lexer-keywords.txt" + {"i8x16.extract_lane_s", TokenType::SimdLaneOp, Opcode::I8X16ExtractLaneS}, + {""}, +#line 480 "src/lexer-keywords.txt" + {"i8x16.extract_lane_u", TokenType::SimdLaneOp, Opcode::I8X16ExtractLaneU}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 499 "src/lexer-keywords.txt" + {"i8x16.shl", TokenType::Binary, Opcode::I8X16Shl}, + {""}, {""}, {""}, {""}, {""}, +#line 587 "src/lexer-keywords.txt" + {"i8x16.shuffle", TokenType::SimdShuffleOp, Opcode::I8X16Shuffle}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, +#line 215 "src/lexer-keywords.txt" + {"i16x8.extadd_pairwise_i8x16_s", TokenType::Unary, Opcode::I16X8ExtaddPairwiseI8X16S}, + {""}, +#line 216 "src/lexer-keywords.txt" + {"i16x8.extadd_pairwise_i8x16_u", TokenType::Unary, Opcode::I16X8ExtaddPairwiseI8X16U}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 456 "src/lexer-keywords.txt" + {"i64x2.extend_high_i32x4_s", TokenType::Unary, Opcode::I64X2ExtendHighI32X4S}, + {""}, +#line 458 "src/lexer-keywords.txt" + {"i64x2.extend_high_i32x4_u", TokenType::Unary, Opcode::I64X2ExtendHighI32X4U}, + {""}, {""}, {""}, {""}, +#line 224 "src/lexer-keywords.txt" + {"i16x8.extend_low_i8x16_s", TokenType::Unary, Opcode::I16X8ExtendLowI8X16S}, + {""}, +#line 225 "src/lexer-keywords.txt" + {"i16x8.extend_low_i8x16_u", TokenType::Unary, Opcode::I16X8ExtendLowI8X16U}, + {""}, {""}, {""}, +#line 466 "src/lexer-keywords.txt" + {"i64x2.extmul_high_i32x4_s", TokenType::Binary, Opcode::I64X2ExtmulHighI32X4S}, + {""}, +#line 468 "src/lexer-keywords.txt" + {"i64x2.extmul_high_i32x4_u", TokenType::Binary, Opcode::I64X2ExtmulHighI32X4U}, + {""}, {""}, {""}, {""}, +#line 217 "src/lexer-keywords.txt" + {"i16x8.extmul_low_i8x16_s", TokenType::Binary, Opcode::I16X8ExtmulLowI8X16S}, + {""}, +#line 219 "src/lexer-keywords.txt" + {"i16x8.extmul_low_i8x16_u", TokenType::Binary, Opcode::I16X8ExtmulLowI8X16U}, + {""}, {""}, {""}, {""}, {""}, +#line 496 "src/lexer-keywords.txt" + {"i8x16.popcnt", TokenType::Unary, Opcode::I8X16Popcnt}, + {""}, +#line 500 "src/lexer-keywords.txt" + {"i8x16.shr_s", TokenType::Binary, Opcode::I8X16ShrS}, + {""}, {""}, {""}, +#line 501 "src/lexer-keywords.txt" + {"i8x16.shr_u", TokenType::Binary, Opcode::I8X16ShrU}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 378 "src/lexer-keywords.txt" + {"i64.atomic.rmw.cmpxchg", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmwCmpxchg}, +#line 247 "src/lexer-keywords.txt" + {"i32.atomic.rmw.cmpxchg", TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmwCmpxchg}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 522 "src/lexer-keywords.txt" + {"memory.grow", TokenType::MemoryGrow, Opcode::MemoryGrow}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 476 "src/lexer-keywords.txt" + {"i8x16.avgr_u", TokenType::Binary, Opcode::I8X16AvgrU}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 498 "src/lexer-keywords.txt" + {"i8x16.replace_lane", TokenType::SimdLaneOp, Opcode::I8X16ReplaceLane}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, +#line 119 "src/lexer-keywords.txt" + {"f64.copysign", TokenType::Binary, Opcode::F64Copysign}, +#line 61 "src/lexer-keywords.txt" + {"f32.copysign", TokenType::Binary, Opcode::F32Copysign}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 590 "src/lexer-keywords.txt" + {"atomic.notify", TokenType::AtomicNotify, Opcode::MemoryAtomicNotify}, +#line 517 "src/lexer-keywords.txt" + {"memory.atomic.notify", TokenType::AtomicNotify, Opcode::MemoryAtomicNotify}, +#line 184 "src/lexer-keywords.txt" + {"i16x8.eq", TokenType::Compare, Opcode::I16X8Eq}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, +#line 204 "src/lexer-keywords.txt" + {"i16x8.neg", TokenType::Unary, Opcode::I16X8Neg}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, +#line 374 "src/lexer-keywords.txt" + {"i64.atomic.rmw8.xchg_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8XchgU}, +#line 243 "src/lexer-keywords.txt" + {"i32.atomic.rmw8.xchg_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8XchgU}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, +#line 205 "src/lexer-keywords.txt" + {"i16x8.q15mulr_sat_s", TokenType::Binary, Opcode::I16X8Q15mulrSatS}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, +#line 202 "src/lexer-keywords.txt" + {"i16x8.narrow_i32x4_s", TokenType::Binary, Opcode::I16X8NarrowI32X4S}, + {""}, +#line 203 "src/lexer-keywords.txt" + {"i16x8.narrow_i32x4_u", TokenType::Binary, Opcode::I16X8NarrowI32X4U}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, +#line 38 "src/lexer-keywords.txt" + {"current_memory", TokenType::MemorySize, Opcode::MemorySize}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 478 "src/lexer-keywords.txt" + {"i8x16.eq", TokenType::Compare, Opcode::I8X16Eq}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, +#line 495 "src/lexer-keywords.txt" + {"i8x16.neg", TokenType::Unary, Opcode::I8X16Neg}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, +#line 176 "src/lexer-keywords.txt" + {"grow_memory", TokenType::MemoryGrow, Opcode::MemoryGrow}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 342 "src/lexer-keywords.txt" + {"i32x4.extend_high_i16x8_s", TokenType::Unary, Opcode::I32X4ExtendHighI16X8S}, + {""}, +#line 343 "src/lexer-keywords.txt" + {"i32x4.extend_high_i16x8_u", TokenType::Unary, Opcode::I32X4ExtendHighI16X8U}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, +#line 336 "src/lexer-keywords.txt" + {"i32x4.extmul_high_i16x8_s", TokenType::Binary, Opcode::I32X4ExtmulHighI16X8S}, + {""}, +#line 338 "src/lexer-keywords.txt" + {"i32x4.extmul_high_i16x8_u", TokenType::Binary, Opcode::I32X4ExtmulHighI16X8U}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, +#line 520 "src/lexer-keywords.txt" + {"memory.copy", TokenType::MemoryCopy, Opcode::MemoryCopy}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 222 "src/lexer-keywords.txt" + {"i16x8.extend_high_i8x16_s", TokenType::Unary, Opcode::I16X8ExtendHighI8X16S}, + {""}, +#line 223 "src/lexer-keywords.txt" + {"i16x8.extend_high_i8x16_u", TokenType::Unary, Opcode::I16X8ExtendHighI8X16U}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, +#line 218 "src/lexer-keywords.txt" + {"i16x8.extmul_high_i8x16_s", TokenType::Binary, Opcode::I16X8ExtmulHighI8X16S}, + {""}, +#line 220 "src/lexer-keywords.txt" + {"i16x8.extmul_high_i8x16_u", TokenType::Binary, Opcode::I16X8ExtmulHighI8X16U}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, +#line 381 "src/lexer-keywords.txt" + {"i64.atomic.rmw.xchg", TokenType::AtomicRmw, Opcode::I64AtomicRmwXchg}, +#line 250 "src/lexer-keywords.txt" + {"i32.atomic.rmw.xchg", TokenType::AtomicRmw, Opcode::I32AtomicRmwXchg}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 493 "src/lexer-keywords.txt" + {"i8x16.narrow_i16x8_s", TokenType::Binary, Opcode::I8X16NarrowI16X8S}, + {""}, +#line 494 "src/lexer-keywords.txt" + {"i8x16.narrow_i16x8_u", TokenType::Binary, Opcode::I8X16NarrowI16X8U} + }; + + 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.include.c b/third_party/wasm2c/src/prebuilt/wasm2c.include.c new file mode 100644 index 0000000000..de62978629 --- /dev/null +++ b/third_party/wasm2c/src/prebuilt/wasm2c.include.c @@ -0,0 +1,439 @@ +/* Generated from 'wasm2c.c.tmpl' by wasm2c_tmpl.py, do not edit! */ +const char SECTION_NAME(includes)[] = +"/* Automically generated by wasm2c */\n" +"#include <math.h>\n" +"#include <string.h>\n" +"#include <stdlib.h>\n" +; + +const char SECTION_NAME(declarations)[] = +"#if defined(_MSC_VER)\n" +"# define UNLIKELY(x) (x)\n" +"# define LIKELY(x) (x)\n" +"#else\n" +"# define UNLIKELY(x) __builtin_expect(!!(x), 0)\n" +"# define LIKELY(x) __builtin_expect(!!(x), 1)\n" +"#endif\n" +"\n" +"#define TRAP(x) (wasm_rt_trap(WASM_RT_TRAP_##x), 0)\n" +"\n" +"#ifndef FUNC_PROLOGUE\n" +"#define FUNC_PROLOGUE\n" +"#endif\n" +"\n" +"#ifndef FUNC_EPILOGUE\n" +"#define FUNC_EPILOGUE\n" +"#endif\n" +"\n" +"#ifdef EXTERNAL_CALLBACK_PROLOGUE\n" +"#define EXTERNAL_CALLBACK_PROLOGUE_EXEC(table, x) \\\n" +" if (UNLIKELY(table.data[x].func_class == WASM_RT_EXTERNAL_FUNCTION)) { \\\n" +" EXTERNAL_CALLBACK_PROLOGUE; \\\n" +" }\n" +"#else\n" +"#define EXTERNAL_CALLBACK_PROLOGUE_EXEC(table, x)\n" +"#endif\n" +"\n" +"#ifdef EXTERNAL_CALLBACK_EPILOGUE\n" +"#define EXTERNAL_CALLBACK_EPILOGUE_EXEC(table, x) \\\n" +" if (UNLIKELY(table.data[x].func_class == WASM_RT_EXTERNAL_FUNCTION)) { \\\n" +" EXTERNAL_CALLBACK_EPILOGUE; \\\n" +" }\n" +"#else\n" +"#define EXTERNAL_CALLBACK_EPILOGUE_EXEC(table, x)\n" +"#endif\n" +"\n" +"#define UNREACHABLE (void) TRAP(UNREACHABLE)\n" +"\n" +"#define CALL_INDIRECT_VOID(table, t, ft, x, func_types, ...) \\\n" +" if (LIKELY((x) < table.size && table.data[x].func && table.data[x].func_type == func_types[ft])) { \\\n" +" EXTERNAL_CALLBACK_PROLOGUE_EXEC(table, x); \\\n" +" ((t)table.data[x].func)(__VA_ARGS__); \\\n" +" EXTERNAL_CALLBACK_EPILOGUE_EXEC(table, x); \\\n" +" } else { \\\n" +" wasm_rt_callback_error_trap(&table, x, func_types[ft]); \\\n" +" }\n" +"\n" +"#define CALL_INDIRECT_RES(res, table, t, ft, x, func_types, ...) \\\n" +" if (LIKELY((x) < table.size && table.data[x].func && table.data[x].func_type == func_types[ft])) { \\\n" +" EXTERNAL_CALLBACK_PROLOGUE_EXEC(table, x); \\\n" +" res = ((t)table.data[x].func)(__VA_ARGS__); \\\n" +" EXTERNAL_CALLBACK_EPILOGUE_EXEC(table, x); \\\n" +" } else { \\\n" +" wasm_rt_callback_error_trap(&table, x, func_types[ft]); \\\n" +" }\n" +"\n" +"#if defined(WASM2C_MALLOC_FAIL_CALLBACK)\n" +"void WASM2C_MALLOC_FAIL_CALLBACK(u32 ptr_size);\n" +"# define WASM2C_MALLOC_FAIL_CHECK(ptr, ptr_size) \\\n" +" if (!ptr) { \\\n" +" WASM2C_MALLOC_FAIL_CALLBACK(ptr_size); \\\n" +" }\n" +"#else\n" +"# define WASM2C_MALLOC_FAIL_CHECK(ptr, ptr_size)\n" +"#endif\n" +"\n" +"#if defined(WASM_CHECK_SHADOW_MEMORY)\n" +"# define WASM2C_SHADOW_MEMORY_LOAD(mem, func_name, ptr, ptr_size) wasm2c_shadow_memory_load(mem, func_name, ptr, ptr_size)\n" +"# define WASM2C_SHADOW_MEMORY_STORE(mem, func_name, ptr, ptr_size) wasm2c_shadow_memory_store(mem, func_name, ptr, ptr_size)\n" +"# define WASM2C_SHADOW_MEMORY_RESERVE(mem, ptr, ptr_size) wasm2c_shadow_memory_reserve(mem, ptr, ptr_size)\n" +"# define WASM2C_SHADOW_MEMORY_DLMALLOC(mem, ptr, ptr_size) wasm2c_shadow_memory_dlmalloc(mem, ptr, ptr_size)\n" +"# define WASM2C_SHADOW_MEMORY_DLFREE(mem, ptr) wasm2c_shadow_memory_dlfree(mem, ptr)\n" +"# define WASM2C_SHADOW_MEMORY_MARK_GLOBALS_HEAP_BOUNDARY(mem, ptr) wasm2c_shadow_memory_mark_globals_heap_boundary(mem, ptr)\n" +"#else\n" +"# define WASM2C_SHADOW_MEMORY_LOAD(mem, func_name, ptr, ptr_size)\n" +"# define WASM2C_SHADOW_MEMORY_STORE(mem, func_name, ptr, ptr_size)\n" +"# define WASM2C_SHADOW_MEMORY_RESERVE(mem, ptr, ptr_size)\n" +"# define WASM2C_SHADOW_MEMORY_DLMALLOC(mem, ptr, ptr_size)\n" +"# define WASM2C_SHADOW_MEMORY_DLFREE(mem, ptr)\n" +"# define WASM2C_SHADOW_MEMORY_MARK_GLOBALS_HEAP_BOUNDARY(mem, ptr)\n" +"#endif\n" +"\n" +"#ifdef WASM_USE_GUARD_PAGES\n" +"# define MEMCHECK(mem, a, t)\n" +"#else\n" +"# define MEMCHECK(mem, a, t) if (UNLIKELY((a) + sizeof(t) > mem->size)) { (void) TRAP(OOB); }\n" +"#endif\n" +"\n" +"#if defined(WASM_USE_GUARD_PAGES) && UINTPTR_MAX == 0xffffffff\n" +"// on 32-bit platforms we have to mask memory access into range\n" +"# define MEM_ACCESS_REF(mem, addr) &mem->data[addr & mem->mem_mask]\n" +"#else\n" +"# define MEM_ACCESS_REF(mem, addr) &mem->data[addr]\n" +"#endif\n" +"\n" +"#if defined(WASM_USING_GLOBAL_HEAP)\n" +"# undef MEM_ACCESS_REF\n" +"# define MEM_ACCESS_REF(mem, addr) (char*) addr\n" +"#endif\n" +"\n" +"#ifdef __GNUC__\n" +"#define wasm_asm __asm__\n" +"#else\n" +"#define wasm_asm(X)\n" +"#endif\n" +"\n" +"#if WABT_BIG_ENDIAN\n" +"static inline void load_data(void *dest, const void *src, size_t n) {\n" +" size_t i = 0;\n" +" u8 *dest_chars = dest;\n" +" memcpy(dest, src, n);\n" +" for (i = 0; i < (n>>1); i++) {\n" +" u8 cursor = dest_chars[i];\n" +" dest_chars[i] = dest_chars[n - i - 1];\n" +" dest_chars[n - i - 1] = cursor;\n" +" }\n" +"}\n" +"#define LOAD_DATA(m, o, i, s) { load_data(&(m.data[m.size - o - s]), i, s); \\\n" +" WASM2C_SHADOW_MEMORY_RESERVE(&m, m.size - o - s, s); \\\n" +" WASM2C_SHADOW_MEMORY_STORE(&m, \"GlobalDataLoad\", m.size - o - s, s); \\\n" +"}\n" +"\n" +"#define DEFINE_LOAD(name, t1, t2, t3) \\\n" +" static inline t3 name(wasm_rt_memory_t* mem, u64 addr, const char* func_name) { \\\n" +" MEMCHECK(mem, addr, t1); \\\n" +" t1 result; \\\n" +" memcpy(&result, MEM_ACCESS_REF(mem, mem->size - addr - sizeof(t1)), sizeof(t1)); \\\n" +" WASM2C_SHADOW_MEMORY_LOAD(mem, func_name, mem->size - addr - sizeof(t1), sizeof(t1)); \\\n" +" wasm_asm(\"\" ::\"r\"(result)); \\\n" +" return (t3)(t2)result; \\\n" +" }\n" +"\n" +"#define DEFINE_STORE(name, t1, t2) \\\n" +" static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value, const char* func_name) { \\\n" +" MEMCHECK(mem, addr, t1); \\\n" +" t1 wrapped = (t1)value; \\\n" +" memcpy(MEM_ACCESS_REF(mem, mem->size - addr - sizeof(t1), &wrapped, sizeof(t1)); \\\n" +" WASM2C_SHADOW_MEMORY_STORE(mem, func_name, mem->size - addr - sizeof(t1)), sizeof(t1)); \\\n" +" }\n" +"#else\n" +"static inline void load_data(void *dest, const void *src, size_t n) {\n" +" memcpy(dest, src, n);\n" +"}\n" +"#define LOAD_DATA(m, o, i, s) { load_data(&(m.data[o]), i, s); \\\n" +" WASM2C_SHADOW_MEMORY_RESERVE(&m, o, s); \\\n" +" WASM2C_SHADOW_MEMORY_STORE(&m, \"GlobalDataLoad\", o, s); \\\n" +"}\n" +"\n" +"#define DEFINE_LOAD(name, t1, t2, t3) \\\n" +" static inline t3 name(wasm_rt_memory_t* mem, u64 addr, const char* func_name) { \\\n" +" MEMCHECK(mem, addr, t1); \\\n" +" t1 result; \\\n" +" memcpy(&result, MEM_ACCESS_REF(mem, addr), sizeof(t1)); \\\n" +" WASM2C_SHADOW_MEMORY_LOAD(mem, func_name, addr, sizeof(t1)); \\\n" +" wasm_asm(\"\" ::\"r\"(result)); \\\n" +" return (t3)(t2)result; \\\n" +" }\n" +"\n" +"#define DEFINE_STORE(name, t1, t2) \\\n" +" static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value, const char* func_name) { \\\n" +" MEMCHECK(mem, addr, t1); \\\n" +" t1 wrapped = (t1)value; \\\n" +" memcpy(MEM_ACCESS_REF(mem, addr), &wrapped, sizeof(t1)); \\\n" +" WASM2C_SHADOW_MEMORY_STORE(mem, func_name, addr, sizeof(t1)); \\\n" +" }\n" +"#endif\n" +"\n" +"DEFINE_LOAD(i32_load, u32, u32, u32);\n" +"DEFINE_LOAD(i64_load, u64, u64, u64);\n" +"DEFINE_LOAD(f32_load, f32, f32, f32);\n" +"DEFINE_LOAD(f64_load, f64, f64, f64);\n" +"DEFINE_LOAD(i32_load8_s, s8, s32, u32);\n" +"DEFINE_LOAD(i64_load8_s, s8, s64, u64);\n" +"DEFINE_LOAD(i32_load8_u, u8, u32, u32);\n" +"DEFINE_LOAD(i64_load8_u, u8, u64, u64);\n" +"DEFINE_LOAD(i32_load16_s, s16, s32, u32);\n" +"DEFINE_LOAD(i64_load16_s, s16, s64, u64);\n" +"DEFINE_LOAD(i32_load16_u, u16, u32, u32);\n" +"DEFINE_LOAD(i64_load16_u, u16, u64, u64);\n" +"DEFINE_LOAD(i64_load32_s, s32, s64, u64);\n" +"DEFINE_LOAD(i64_load32_u, u32, u64, u64);\n" +"DEFINE_STORE(i32_store, u32, u32);\n" +"DEFINE_STORE(i64_store, u64, u64);\n" +"DEFINE_STORE(f32_store, f32, f32);\n" +"DEFINE_STORE(f64_store, f64, f64);\n" +"DEFINE_STORE(i32_store8, u8, u32);\n" +"DEFINE_STORE(i32_store16, u16, u32);\n" +"DEFINE_STORE(i64_store8, u8, u64);\n" +"DEFINE_STORE(i64_store16, u16, u64);\n" +"DEFINE_STORE(i64_store32, u32, u64);\n" +"\n" +"#if defined(_MSC_VER)\n" +"#include <intrin.h>\n" +"\n" +"// Adapted from https://github.com/nemequ/portable-snippets/blob/master/builtin/builtin.h\n" +"\n" +"static inline int I64_CLZ(unsigned long long v) {\n" +" unsigned long r = 0;\n" +"#if defined(_M_AMD64) || defined(_M_ARM)\n" +" if (_BitScanReverse64(&r, v)) {\n" +" return 63 - r;\n" +" }\n" +"#else\n" +" if (_BitScanReverse(&r, (unsigned long) (v >> 32))) {\n" +" return 31 - r;\n" +" } else if (_BitScanReverse(&r, (unsigned long) v)) {\n" +" return 63 - r;\n" +" }\n" +"#endif\n" +" return 64;\n" +"}\n" +"\n" +"static inline int I32_CLZ(unsigned long v) {\n" +" unsigned long r = 0;\n" +" if (_BitScanReverse(&r, v)) {\n" +" return 31 - r;\n" +" }\n" +" return 32;\n" +"}\n" +"\n" +"static inline int I64_CTZ(unsigned long long v) {\n" +" if (!v) {\n" +" return 64;\n" +" }\n" +" unsigned long r = 0;\n" +"#if defined(_M_AMD64) || defined(_M_ARM)\n" +" _BitScanForward64(&r, v);\n" +" return (int) r;\n" +"#else\n" +" if (_BitScanForward(&r, (unsigned int) (v))) {\n" +" return (int) (r);\n" +" }\n" +"\n" +" _BitScanForward(&r, (unsigned int) (v >> 32));\n" +" return (int) (r + 32);\n" +"#endif\n" +"}\n" +"\n" +"static inline int I32_CTZ(unsigned long v) {\n" +" if (!v) {\n" +" return 32;\n" +" }\n" +" unsigned long r = 0;\n" +" _BitScanForward(&r, v);\n" +" return (int) r;\n" +"}\n" +"\n" +"#define POPCOUNT_DEFINE_PORTABLE(f_n, T) \\\n" +" static inline u32 f_n(T x) { \\\n" +" x = x - ((x >> 1) & (T)~(T)0/3); \\\n" +" x = (x & (T)~(T)0/15*3) + ((x >> 2) & (T)~(T)0/15*3); \\\n" +" x = (x + (x >> 4)) & (T)~(T)0/255*15; \\\n" +" return (T)(x * ((T)~(T)0/255)) >> (sizeof(T) - 1) * 8; \\\n" +" }\n" +"\n" +"POPCOUNT_DEFINE_PORTABLE(I32_POPCNT, u32)\n" +"POPCOUNT_DEFINE_PORTABLE(I64_POPCNT, u64)\n" +"\n" +"#undef POPCOUNT_DEFINE_PORTABLE\n" +"\n" +"#else\n" +"# define I32_CLZ(x) ((x) ? __builtin_clz(x) : 32)\n" +"# define I64_CLZ(x) ((x) ? __builtin_clzll(x) : 64)\n" +"# define I32_CTZ(x) ((x) ? __builtin_ctz(x) : 32)\n" +"# define I64_CTZ(x) ((x) ? __builtin_ctzll(x) : 64)\n" +"# define I32_POPCNT(x) (__builtin_popcount(x))\n" +"# define I64_POPCNT(x) (__builtin_popcountll(x))\n" +"#endif\n" +"\n" +"#define DIV_S(ut, min, x, y) \\\n" +" ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \\\n" +" : (UNLIKELY((x) == min && (y) == -1)) ? TRAP(INT_OVERFLOW) \\\n" +" : (ut)((x) / (y)))\n" +"\n" +"#define REM_S(ut, min, x, y) \\\n" +" ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \\\n" +" : (UNLIKELY((x) == min && (y) == -1)) ? 0 \\\n" +" : (ut)((x) % (y)))\n" +"\n" +"#define I32_DIV_S(x, y) DIV_S(u32, INT32_MIN, (s32)x, (s32)y)\n" +"#define I64_DIV_S(x, y) DIV_S(u64, INT64_MIN, (s64)x, (s64)y)\n" +"#define I32_REM_S(x, y) REM_S(u32, INT32_MIN, (s32)x, (s32)y)\n" +"#define I64_REM_S(x, y) REM_S(u64, INT64_MIN, (s64)x, (s64)y)\n" +"\n" +"#define DIVREM_U(op, x, y) \\\n" +" ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) : ((x) op (y)))\n" +"\n" +"#define DIV_U(x, y) DIVREM_U(/, x, y)\n" +"#define REM_U(x, y) DIVREM_U(%, x, y)\n" +"\n" +"#define ROTL(x, y, mask) \\\n" +" (((x) << ((y) & (mask))) | ((x) >> (((mask) - (y) + 1) & (mask))))\n" +"#define ROTR(x, y, mask) \\\n" +" (((x) >> ((y) & (mask))) | ((x) << (((mask) - (y) + 1) & (mask))))\n" +"\n" +"#define I32_ROTL(x, y) ROTL(x, y, 31)\n" +"#define I64_ROTL(x, y) ROTL(x, y, 63)\n" +"#define I32_ROTR(x, y) ROTR(x, y, 31)\n" +"#define I64_ROTR(x, y) ROTR(x, y, 63)\n" +"\n" +"#define FMIN(x, y) \\\n" +" ((UNLIKELY((x) != (x))) ? NAN \\\n" +" : (UNLIKELY((y) != (y))) ? NAN \\\n" +" : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? x : y) \\\n" +" : (x < y) ? x : y)\n" +"\n" +"#define FMAX(x, y) \\\n" +" ((UNLIKELY((x) != (x))) ? NAN \\\n" +" : (UNLIKELY((y) != (y))) ? NAN \\\n" +" : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? y : x) \\\n" +" : (x > y) ? x : y)\n" +"\n" +"#define TRUNC_S(ut, st, ft, min, minop, max, x) \\\n" +" ((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \\\n" +" : (UNLIKELY(!((x)minop(min) && (x) < (max)))) ? TRAP(INT_OVERFLOW) \\\n" +" : (ut)(st)(x))\n" +"\n" +"#define I32_TRUNC_S_F32(x) TRUNC_S(u32, s32, f32, (f32)INT32_MIN, >=, 2147483648.f, x)\n" +"#define I64_TRUNC_S_F32(x) TRUNC_S(u64, s64, f32, (f32)INT64_MIN, >=, (f32)INT64_MAX, x)\n" +"#define I32_TRUNC_S_F64(x) TRUNC_S(u32, s32, f64, -2147483649., >, 2147483648., x)\n" +"#define I64_TRUNC_S_F64(x) TRUNC_S(u64, s64, f64, (f64)INT64_MIN, >=, (f64)INT64_MAX, x)\n" +"\n" +"#define TRUNC_U(ut, ft, max, x) \\\n" +" ((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \\\n" +" : (UNLIKELY(!((x) > (ft)-1 && (x) < (max)))) ? TRAP(INT_OVERFLOW) \\\n" +" : (ut)(x))\n" +"\n" +"#define I32_TRUNC_U_F32(x) TRUNC_U(u32, f32, 4294967296.f, x)\n" +"#define I64_TRUNC_U_F32(x) TRUNC_U(u64, f32, (f32)UINT64_MAX, x)\n" +"#define I32_TRUNC_U_F64(x) TRUNC_U(u32, f64, 4294967296., x)\n" +"#define I64_TRUNC_U_F64(x) TRUNC_U(u64, f64, (f64)UINT64_MAX, x)\n" +"\n" +"#define TRUNC_SAT_S(ut, st, ft, min, smin, minop, max, smax, x) \\\n" +" ((UNLIKELY((x) != (x))) ? 0 \\\n" +" : (UNLIKELY(!((x)minop(min)))) ? smin \\\n" +" : (UNLIKELY(!((x) < (max)))) ? smax \\\n" +" : (ut)(st)(x))\n" +"\n" +"#define I32_TRUNC_SAT_S_F32(x) TRUNC_SAT_S(u32, s32, f32, (f32)INT32_MIN, INT32_MIN, >=, 2147483648.f, INT32_MAX, x)\n" +"#define I64_TRUNC_SAT_S_F32(x) TRUNC_SAT_S(u64, s64, f32, (f32)INT64_MIN, INT64_MIN, >=, (f32)INT64_MAX, INT64_MAX, x)\n" +"#define I32_TRUNC_SAT_S_F64(x) TRUNC_SAT_S(u32, s32, f64, -2147483649., INT32_MIN, >, 2147483648., INT32_MAX, x)\n" +"#define I64_TRUNC_SAT_S_F64(x) TRUNC_SAT_S(u64, s64, f64, (f64)INT64_MIN, INT64_MIN, >=, (f64)INT64_MAX, INT64_MAX, x)\n" +"\n" +"#define TRUNC_SAT_U(ut, ft, max, smax, x) \\\n" +" ((UNLIKELY((x) != (x))) ? 0 \\\n" +" : (UNLIKELY(!((x) > (ft)-1))) ? 0 \\\n" +" : (UNLIKELY(!((x) < (max)))) ? smax \\\n" +" : (ut)(x))\n" +"\n" +"#define I32_TRUNC_SAT_U_F32(x) TRUNC_SAT_U(u32, f32, 4294967296.f, UINT32_MAX, x)\n" +"#define I64_TRUNC_SAT_U_F32(x) TRUNC_SAT_U(u64, f32, (f32)UINT64_MAX, UINT64_MAX, x)\n" +"#define I32_TRUNC_SAT_U_F64(x) TRUNC_SAT_U(u32, f64, 4294967296., UINT32_MAX, x)\n" +"#define I64_TRUNC_SAT_U_F64(x) TRUNC_SAT_U(u64, f64, (f64)UINT64_MAX, UINT64_MAX, x)\n" +"\n" +"#define DEFINE_REINTERPRET(name, t1, t2) \\\n" +" static inline t2 name(t1 x) { \\\n" +" t2 result; \\\n" +" memcpy(&result, &x, sizeof(result)); \\\n" +" return result; \\\n" +" }\n" +"\n" +"DEFINE_REINTERPRET(f32_reinterpret_i32, u32, f32)\n" +"DEFINE_REINTERPRET(i32_reinterpret_f32, f32, u32)\n" +"DEFINE_REINTERPRET(f64_reinterpret_i64, u64, f64)\n" +"DEFINE_REINTERPRET(i64_reinterpret_f64, f64, u64)\n" +; + +const char SECTION_NAME(sandboxapis)[] = +"//test\n" +"\n" +"static u32 add_wasm2c_callback(void* sbx_ptr, u32 func_type_idx, void* func_ptr, wasm_rt_elem_target_class_t func_class) {\n" +" wasm_rt_table_t* table = get_wasm2c_callback_table(sbx_ptr);\n" +" for (u32 i = 1; i < table->max_size; i++) {\n" +" if (i >= table->size) {\n" +" wasm_rt_expand_table(table);\n" +" }\n" +" if (table->data[i].func == 0) {\n" +" table->data[i] = (wasm_rt_elem_t){ func_class, func_type_idx, (wasm_rt_anyfunc_t) func_ptr };\n" +" return i;\n" +" }\n" +" }\n" +" (void) TRAP(CALL_INDIRECT_TABLE_EXPANSION);\n" +"}\n" +"\n" +"static void remove_wasm2c_callback(void* sbx_ptr, u32 callback_idx) {\n" +" wasm_rt_table_t* table = get_wasm2c_callback_table(sbx_ptr);\n" +" table->data[callback_idx].func = 0;\n" +"}\n" +"\n" +"static u32 lookup_wasm2c_func_index(void* sbx_ptr, u32 param_count, u32 result_count, wasm_rt_type_t* types) {\n" +" wasm2c_sandbox_t* const sbx = (wasm2c_sandbox_t* const) sbx_ptr;\n" +" return wasm_rt_register_func_type(&sbx->func_type_structs, &sbx->func_type_count, param_count, result_count, types);\n" +"}\n" +"\n" +"static void* create_wasm2c_sandbox(uint32_t max_wasm_pages) {\n" +" wasm2c_sandbox_t* const sbx = (wasm2c_sandbox_t* const) calloc(sizeof(wasm2c_sandbox_t), 1);\n" +" if (!init_memory(sbx, max_wasm_pages)) {\n" +" free(sbx);\n" +" return 0;\n" +" }\n" +" init_func_types(sbx);\n" +" init_globals(sbx);\n" +" init_table(sbx);\n" +" wasm_rt_init_wasi(&(sbx->wasi_data));\n" +" init_module_starts();\n" +" // w2c___wasm_call_ctors(sbx);\n" +" return sbx;\n" +"}\n" +"\n" +"static void destroy_wasm2c_sandbox(void* aSbx) {\n" +" wasm2c_sandbox_t* const sbx = (wasm2c_sandbox_t* const) aSbx;\n" +" cleanup_memory(sbx);\n" +" cleanup_func_types(sbx);\n" +" cleanup_table(sbx);\n" +" wasm_rt_cleanup_wasi(&(sbx->wasi_data));\n" +" free(sbx);\n" +"}\n" +"\n" +"FUNC_EXPORT wasm2c_sandbox_funcs_t WASM_CURR_ADD_PREFIX(get_wasm2c_sandbox_info)() {\n" +" wasm2c_sandbox_funcs_t ret;\n" +" ret.wasm_rt_sys_init = &wasm_rt_sys_init;\n" +" ret.create_wasm2c_sandbox = &create_wasm2c_sandbox;\n" +" ret.destroy_wasm2c_sandbox = &destroy_wasm2c_sandbox;\n" +" ret.lookup_wasm2c_nonfunc_export = &lookup_wasm2c_nonfunc_export;\n" +" ret.lookup_wasm2c_func_index = &lookup_wasm2c_func_index;\n" +" ret.add_wasm2c_callback = &add_wasm2c_callback;\n" +" ret.remove_wasm2c_callback = &remove_wasm2c_callback;\n" +" return ret;\n" +"}\n" +; diff --git a/third_party/wasm2c/src/prebuilt/wasm2c.include.h b/third_party/wasm2c/src/prebuilt/wasm2c.include.h new file mode 100644 index 0000000000..fcd2ff4b55 --- /dev/null +++ b/third_party/wasm2c/src/prebuilt/wasm2c.include.h @@ -0,0 +1,55 @@ +/* Generated from 'wasm2c.h.tmpl' by wasm2c_tmpl.py, do not edit! */ +const char SECTION_NAME(top)[] = +"/* Automically generated by wasm2c */\n" +"#ifdef __cplusplus\n" +"extern \"C\" {\n" +"#endif\n" +"\n" +"#include <stdint.h>\n" +"\n" +"#include \"wasm-rt.h\"\n" +"\n" +"#ifndef WASM_RT_MODULE_PREFIX\n" +"#define WASM_RT_MODULE_PREFIX\n" +"#endif\n" +"\n" +"#define WASM_RT_PASTE_(x, y) x ## y\n" +"#define WASM_RT_PASTE(x, y) WASM_RT_PASTE_(x, y)\n" +"#define WASM_RT_ADD_PREFIX(x) WASM_RT_PASTE(WASM_RT_MODULE_PREFIX, x)\n" +"\n" +"#define WASM_CURR_ADD_PREFIX(x) WASM_RT_PASTE(WASM_CURR_MODULE_PREFIX, x)\n" +"\n" +"/* TODO(binji): only use stdint.h types in header */\n" +"typedef uint8_t u8;\n" +"typedef int8_t s8;\n" +"typedef uint16_t u16;\n" +"typedef int16_t s16;\n" +"typedef uint32_t u32;\n" +"typedef int32_t s32;\n" +"typedef uint64_t u64;\n" +"typedef int64_t s64;\n" +"typedef float f32;\n" +"typedef double f64;\n" +"\n" +"#ifndef WASM_DONT_EXPORT_FUNCS\n" +"# if defined(_WIN32)\n" +"# define FUNC_EXPORT __declspec(dllexport)\n" +"# else\n" +"# define FUNC_EXPORT\n" +"# endif\n" +"#else\n" +"# define FUNC_EXPORT\n" +"#endif\n" +"\n" +"FUNC_EXPORT wasm2c_sandbox_funcs_t WASM_CURR_ADD_PREFIX(get_wasm2c_sandbox_info)();\n" +"\n" +"struct wasm2c_sandbox_t;\n" +"typedef struct wasm2c_sandbox_t wasm2c_sandbox_t;\n" +; + +const char SECTION_NAME(bottom)[] = +"\n" +"#ifdef __cplusplus\n" +"}\n" +"#endif\n" +; diff --git a/third_party/wasm2c/src/range.h b/third_party/wasm2c/src/range.h new file mode 100644 index 0000000000..fa0310ee9a --- /dev/null +++ b/third_party/wasm2c/src/range.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#ifndef WABT_RANGE_H_ +#define WABT_RANGE_H_ + +namespace wabt { + +template <typename T> +struct Range { + Range() : start(0), end(0) {} + Range(T start, T end) : start(start), end(end) {} + T start; + T end; + + T size() const { return end - start; } +}; + +typedef Range<Offset> OffsetRange; +typedef Range<int> ColumnRange; + +} // namespace wabt + +#endif // WABT_RANGE_H_ diff --git a/third_party/wasm2c/src/resolve-names.cc b/third_party/wasm2c/src/resolve-names.cc new file mode 100644 index 0000000000..099a8e5754 --- /dev/null +++ b/third_party/wasm2c/src/resolve-names.cc @@ -0,0 +1,595 @@ +/* + * 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 "src/resolve-names.h" + +#include <cassert> +#include <cstdio> + +#include "src/cast.h" +#include "src/expr-visitor.h" +#include "src/ir.h" +#include "src/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 OnLocalGetExpr(LocalGetExpr*) override; + Result OnLocalSetExpr(LocalSetExpr*) override; + Result OnLocalTeeExpr(LocalTeeExpr*) override; + Result BeginLoopExpr(LoopExpr*) override; + Result EndLoopExpr(LoopExpr*) override; + Result OnDataDropExpr(DataDropExpr*) override; + Result OnMemoryInitExpr(MemoryInitExpr*) 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 BeginTryExpr(TryExpr*) override; + Result EndTryExpr(TryExpr*) override; + Result OnThrowExpr(ThrowExpr*) override; + Result OnRethrowExpr(RethrowExpr*) 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(¤t_module_->func_bindings, var, "function"); +} + +void NameResolver::ResolveGlobalVar(Var* var) { + ResolveVar(¤t_module_->global_bindings, var, "global"); +} + +void NameResolver::ResolveFuncTypeVar(Var* var) { + ResolveVar(¤t_module_->type_bindings, var, "type"); +} + +void NameResolver::ResolveTableVar(Var* var) { + ResolveVar(¤t_module_->table_bindings, var, "table"); +} + +void NameResolver::ResolveMemoryVar(Var* var) { + ResolveVar(¤t_module_->memory_bindings, var, "memory"); +} + +void NameResolver::ResolveTagVar(Var* var) { + ResolveVar(¤t_module_->tag_bindings, var, "tag"); +} + +void NameResolver::ResolveDataSegmentVar(Var* var) { + ResolveVar(¤t_module_->data_segment_bindings, var, "data segment"); +} + +void NameResolver::ResolveElemSegmentVar(Var* var) { + ResolveVar(¤t_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::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::OnDataDropExpr(DataDropExpr* expr) { + ResolveDataSegmentVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnMemoryInitExpr(MemoryInitExpr* expr) { + ResolveDataSegmentVar(&expr->var); + 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::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; +} + +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 (ElemExpr& elem_expr : segment->elem_exprs) { + if (elem_expr.kind == ElemExprKind::RefFunc) { + ResolveFuncVar(&elem_expr.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::Action: + case CommandType::AssertReturn: + case CommandType::AssertTrap: + case CommandType::AssertExhaustion: + 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/resolve-names.h b/third_party/wasm2c/src/resolve-names.h new file mode 100644 index 0000000000..04f2115c6d --- /dev/null +++ b/third_party/wasm2c/src/resolve-names.h @@ -0,0 +1,33 @@ +/* + * 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_RESOLVE_NAMES_H_ +#define WABT_RESOLVE_NAMES_H_ + +#include "src/common.h" +#include "src/error.h" + +namespace wabt { + +struct Module; +struct Script; + +Result ResolveNamesModule(Module*, Errors*); +Result ResolveNamesScript(Script*, Errors*); + +} // namespace wabt + +#endif /* WABT_RESOLVE_NAMES_H_ */ diff --git a/third_party/wasm2c/src/result.h b/third_party/wasm2c/src/result.h new file mode 100644 index 0000000000..a40faab78b --- /dev/null +++ b/third_party/wasm2c/src/result.h @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#ifndef WABT_RESULT_H_ +#define WABT_RESULT_H_ + +namespace wabt { + +struct Result { + enum Enum { + Ok, + Error, + }; + + Result() : Result(Ok) {} + Result(Enum e) : enum_(e) {} + operator Enum() const { return enum_; } + Result& operator|=(Result rhs); + + private: + Enum enum_; +}; + +inline Result operator|(Result lhs, Result rhs) { + return (lhs == Result::Error || rhs == Result::Error) ? Result::Error + : Result::Ok; +} + +inline Result& Result::operator|=(Result rhs) { + enum_ = *this | rhs; + return *this; +} + +inline bool Succeeded(Result result) { + return result == Result::Ok; +} +inline bool Failed(Result result) { + return result == Result::Error; +} + +#define CHECK_RESULT(expr) \ + do { \ + if (Failed(expr)) { \ + return ::wabt::Result::Error; \ + } \ + } while (0) + +} // namespace wabt + +#endif // WABT_RESULT_H_ diff --git a/third_party/wasm2c/src/shared-validator.cc b/third_party/wasm2c/src/shared-validator.cc new file mode 100644 index 0000000000..51c0edea6e --- /dev/null +++ b/third_party/wasm2c/src/shared-validator.cc @@ -0,0 +1,1261 @@ +/* + * 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 "src/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) { + Result result = Result::Ok; + if (!options_.features.multi_value_enabled() && result_count > 1) { + result |= + PrintError(loc, "multiple result values not currently supported."); + } + func_types_.emplace(num_types_++, + FuncType{ToTypeVector(param_count, param_types), + ToTypeVector(result_count, result_types)}); + 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) { + 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(), expected.GetName()); + return Result::Error; + } + return Result::Ok; +} + +Result SharedValidator::OnGlobalInitExpr_Const(const Location& loc, + Type actual) { + return CheckType(loc, actual, globals_.back().type, + "global initializer expression"); +} + +Result SharedValidator::OnGlobalInitExpr_GlobalGet(const Location& loc, + Var ref_global_var) { + Result result = Result::Ok; + GlobalType ref_global; + CHECK_RESULT(CheckGlobalIndex(ref_global_var, &ref_global)); + + if (ref_global_var.index() >= num_imported_globals_) { + result |= PrintError( + ref_global_var.loc, + "initializer expression can only reference an imported global"); + } + + if (ref_global.mutable_) { + result |= PrintError( + loc, "initializer expression cannot reference a mutable global"); + } + + result |= CheckType(loc, ref_global.type, globals_.back().type, + "global initializer expression"); + return result; +} + +Result SharedValidator::OnGlobalInitExpr_RefNull(const Location& loc, + Type type) { + return CheckType(loc, type, globals_.back().type, + "global initializer expression"); +} + +Result SharedValidator::OnGlobalInitExpr_RefFunc(const Location& loc, + Var func_var) { + Result result = Result::Ok; + result |= CheckFuncIndex(func_var); + init_expr_funcs_.push_back(func_var); + result |= CheckType(loc, Type::FuncRef, globals_.back().type, + "global initializer expression"); + return result; +} + +Result SharedValidator::OnGlobalInitExpr_Other(const Location& loc) { + return PrintError( + loc, + "invalid global initializer expression, must be a constant expression"); +} + +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, + string_view name) { + Result result = Result::Ok; + auto name_str = name.to_string(); + 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); + 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; + if (kind == SegmentKind::Active) { + result |= CheckTableIndex(table_var); + } + elems_.push_back(ElemType{Type::Void}); // Updated in OnElemSegmentElemType. + return result; +} + +void SharedValidator::OnElemSegmentElemType(Type elem_type) { + elems_.back().element = elem_type; +} + +Result SharedValidator::OnElemSegmentInitExpr_Const(const Location& loc, + Type type) { + return CheckType(loc, type, Type::I32, "elem segment offset"); +} + +Result SharedValidator::OnElemSegmentInitExpr_GlobalGet(const Location& loc, + Var global_var) { + Result result = Result::Ok; + GlobalType ref_global; + result |= CheckGlobalIndex(global_var, &ref_global); + + if (ref_global.mutable_) { + result |= PrintError( + loc, "initializer expression cannot reference a mutable global"); + } + + result |= CheckType(loc, ref_global.type, Type::I32, "elem segment offset"); + return result; +} + +Result SharedValidator::OnElemSegmentInitExpr_Other(const Location& loc) { + return PrintError(loc, + "invalid elem segment offset, must be a constant " + "expression; either i32.const or " + "global.get."); +} + +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 |= 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::OnDataSegmentInitExpr_Const(const Location& loc, + Type type) { + auto required = + memories_.empty() ? Type(Type::I32) : memories_[0].limits.IndexType(); + return CheckType(loc, type, required, "data segment offset"); +} + +Result SharedValidator::OnDataSegmentInitExpr_GlobalGet(const Location& loc, + Var global_var) { + Result result = Result::Ok; + GlobalType ref_global; + result |= CheckGlobalIndex(global_var, &ref_global); + + if (ref_global.mutable_) { + result |= PrintError( + loc, "initializer expression cannot reference a mutable global"); + } + + auto required = + memories_.empty() ? Type(Type::I32) : memories_[0].limits.IndexType(); + result |= CheckType(loc, ref_global.type, required, "data segment offset"); + return result; +} + +Result SharedValidator::OnDataSegmentInitExpr_Other(const Location& loc) { + return PrintError(loc, + "invalid data segment offset, must be a constant " + "expression; either iXX.const or " + "global.get."); +} + +Result SharedValidator::CheckDeclaredFunc(Var func_var) { + if (declared_funcs_.count(func_var.index()) == 0) { + return PrintError(func_var.loc, + "function is not declared in any elem sections"); + } + 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 : init_expr_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; +} + +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) { + 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; +} + +Result SharedValidator::OnAtomicFence(const Location& loc, + uint32_t consistency_model) { + Result result = Result::Ok; + 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, + Address alignment) { + Result result = Result::Ok; + MemoryType mt; + expr_loc_ = &loc; + result |= CheckMemoryIndex(Var(0, loc), &mt); + result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnAtomicLoad(opcode, mt.limits); + return result; +} + +Result SharedValidator::OnAtomicNotify(const Location& loc, + Opcode opcode, + Address alignment) { + Result result = Result::Ok; + MemoryType mt; + expr_loc_ = &loc; + result |= CheckMemoryIndex(Var(0, loc), &mt); + result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnAtomicNotify(opcode, mt.limits); + return result; +} + +Result SharedValidator::OnAtomicRmwCmpxchg(const Location& loc, + Opcode opcode, + Address alignment) { + Result result = Result::Ok; + MemoryType mt; + expr_loc_ = &loc; + result |= CheckMemoryIndex(Var(0, loc), &mt); + result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnAtomicRmwCmpxchg(opcode, mt.limits); + return result; +} + +Result SharedValidator::OnAtomicRmw(const Location& loc, + Opcode opcode, + Address alignment) { + Result result = Result::Ok; + MemoryType mt; + expr_loc_ = &loc; + result |= CheckMemoryIndex(Var(0, loc), &mt); + result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnAtomicRmw(opcode, mt.limits); + return result; +} + +Result SharedValidator::OnAtomicStore(const Location& loc, + Opcode opcode, + Address alignment) { + Result result = Result::Ok; + MemoryType mt; + expr_loc_ = &loc; + result |= CheckMemoryIndex(Var(0, loc), &mt); + result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnAtomicStore(opcode, mt.limits); + return result; +} + +Result SharedValidator::OnAtomicWait(const Location& loc, + Opcode opcode, + Address alignment) { + Result result = Result::Ok; + MemoryType mt; + expr_loc_ = &loc; + result |= CheckMemoryIndex(Var(0, loc), &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 = Result::Ok; + expr_loc_ = &loc; + result |= typechecker_.OnBinary(opcode); + return result; +} + +Result SharedValidator::OnBlock(const Location& loc, Type sig_type) { + Result result = Result::Ok; + TypeVector param_types, result_types; + expr_loc_ = &loc; + result |= CheckBlockSignature(loc, Opcode::Block, sig_type, ¶m_types, + &result_types); + result |= typechecker_.OnBlock(param_types, result_types); + return result; +} + +Result SharedValidator::OnBr(const Location& loc, Var depth) { + Result result = Result::Ok; + expr_loc_ = &loc; + result |= typechecker_.OnBr(depth.index()); + return result; +} + +Result SharedValidator::OnBrIf(const Location& loc, Var depth) { + Result result = Result::Ok; + expr_loc_ = &loc; + result |= typechecker_.OnBrIf(depth.index()); + return result; +} + +Result SharedValidator::BeginBrTable(const Location& loc) { + Result result = Result::Ok; + expr_loc_ = &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 = Result::Ok; + expr_loc_ = &loc; + result |= typechecker_.EndBrTable(); + return result; +} + +Result SharedValidator::OnCall(const Location& loc, Var func_var) { + Result result = Result::Ok; + expr_loc_ = &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 = Result::Ok; + expr_loc_ = &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 = Result::Ok; + expr_loc_ = &loc; + Index func_index; + result |= typechecker_.OnFuncRef(&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 = Result::Ok; + expr_loc_ = &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 = Result::Ok; + expr_loc_ = &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 = Result::Ok; + expr_loc_ = &loc; + result |= typechecker_.OnConvert(opcode); + return result; +} + +Result SharedValidator::OnDataDrop(const Location& loc, Var segment_var) { + Result result = Result::Ok; + expr_loc_ = &loc; + result |= CheckDataSegmentIndex(segment_var); + result |= typechecker_.OnDataDrop(segment_var.index()); + return result; +} + +Result SharedValidator::OnDelegate(const Location& loc, Var depth) { + Result result = Result::Ok; + expr_loc_ = &loc; + result |= typechecker_.OnDelegate(depth.index()); + return result; +} + +Result SharedValidator::OnDrop(const Location& loc) { + Result result = Result::Ok; + expr_loc_ = &loc; + result |= typechecker_.OnDrop(); + return result; +} + +Result SharedValidator::OnElemDrop(const Location& loc, Var segment_var) { + Result result = Result::Ok; + expr_loc_ = &loc; + result |= CheckElemSegmentIndex(segment_var); + result |= typechecker_.OnElemDrop(segment_var.index()); + return result; +} + +Result SharedValidator::OnElse(const Location& loc) { + Result result = Result::Ok; + result |= typechecker_.OnElse(); + return result; +} + +Result SharedValidator::OnEnd(const Location& loc) { + Result result = Result::Ok; + expr_loc_ = &loc; + result |= typechecker_.OnEnd(); + return result; +} + +Result SharedValidator::OnGlobalGet(const Location& loc, Var global_var) { + Result result = Result::Ok; + expr_loc_ = &loc; + GlobalType global_type; + result |= CheckGlobalIndex(global_var, &global_type); + result |= typechecker_.OnGlobalGet(global_type.type); + return result; +} + +Result SharedValidator::OnGlobalSet(const Location& loc, Var global_var) { + Result result = Result::Ok; + 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()); + } + expr_loc_ = &loc; + result |= typechecker_.OnGlobalSet(global_type.type); + return result; +} + +Result SharedValidator::OnIf(const Location& loc, Type sig_type) { + Result result = Result::Ok; + TypeVector param_types, result_types; + expr_loc_ = &loc; + result |= CheckBlockSignature(loc, Opcode::If, sig_type, ¶m_types, + &result_types); + result |= typechecker_.OnIf(param_types, result_types); + return result; +} + +Result SharedValidator::OnLoad(const Location& loc, + Opcode opcode, + Address alignment) { + Result result = Result::Ok; + MemoryType mt; + expr_loc_ = &loc; + result |= CheckMemoryIndex(Var(0, loc), &mt); + result |= CheckAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnLoad(opcode, mt.limits); + return result; +} + +Result SharedValidator::OnLoadSplat(const Location& loc, + Opcode opcode, + Address alignment) { + Result result = Result::Ok; + MemoryType mt; + expr_loc_ = &loc; + result |= CheckMemoryIndex(Var(0, loc), &mt); + result |= CheckAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnLoad(opcode, mt.limits); + return result; +} + +Result SharedValidator::OnLoadZero(const Location& loc, + Opcode opcode, + Address alignment) { + Result result = Result::Ok; + MemoryType mt; + expr_loc_ = &loc; + result |= CheckMemoryIndex(Var(0, loc), &mt); + result |= CheckAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnLoad(opcode, mt.limits); + return result; +} + +Result SharedValidator::OnLocalGet(const Location& loc, Var local_var) { + Result result = Result::Ok; + Type type = Type::Any; + expr_loc_ = &loc; + result |= CheckLocalIndex(local_var, &type); + result |= typechecker_.OnLocalGet(type); + return result; +} + +Result SharedValidator::OnLocalSet(const Location& loc, Var local_var) { + Result result = Result::Ok; + Type type = Type::Any; + expr_loc_ = &loc; + result |= CheckLocalIndex(local_var, &type); + result |= typechecker_.OnLocalSet(type); + return result; +} + +Result SharedValidator::OnLocalTee(const Location& loc, Var local_var) { + Result result = Result::Ok; + Type type = Type::Any; + expr_loc_ = &loc; + result |= CheckLocalIndex(local_var, &type); + result |= typechecker_.OnLocalTee(type); + return result; +} + +Result SharedValidator::OnLoop(const Location& loc, Type sig_type) { + Result result = Result::Ok; + TypeVector param_types, result_types; + expr_loc_ = &loc; + result |= CheckBlockSignature(loc, Opcode::Loop, sig_type, ¶m_types, + &result_types); + result |= typechecker_.OnLoop(param_types, result_types); + return result; +} + +Result SharedValidator::OnMemoryCopy(const Location& loc) { + Result result = Result::Ok; + MemoryType mt; + expr_loc_ = &loc; + result |= CheckMemoryIndex(Var(0, loc), &mt); + result |= typechecker_.OnMemoryCopy(mt.limits); + return result; +} + +Result SharedValidator::OnMemoryFill(const Location& loc) { + Result result = Result::Ok; + MemoryType mt; + expr_loc_ = &loc; + result |= CheckMemoryIndex(Var(0, loc), &mt); + result |= typechecker_.OnMemoryFill(mt.limits); + return result; +} + +Result SharedValidator::OnMemoryGrow(const Location& loc) { + Result result = Result::Ok; + MemoryType mt; + expr_loc_ = &loc; + result |= CheckMemoryIndex(Var(0, loc), &mt); + result |= typechecker_.OnMemoryGrow(mt.limits); + return result; +} + +Result SharedValidator::OnMemoryInit(const Location& loc, Var segment_var) { + Result result = Result::Ok; + MemoryType mt; + expr_loc_ = &loc; + result |= CheckMemoryIndex(Var(0, loc), &mt); + result |= CheckDataSegmentIndex(segment_var); + result |= typechecker_.OnMemoryInit(segment_var.index(), mt.limits); + return result; +} + +Result SharedValidator::OnMemorySize(const Location& loc) { + Result result = Result::Ok; + MemoryType mt; + expr_loc_ = &loc; + result |= CheckMemoryIndex(Var(0, loc), &mt); + result |= typechecker_.OnMemorySize(mt.limits); + return result; +} + +Result SharedValidator::OnNop(const Location& loc) { + expr_loc_ = &loc; + return Result::Ok; +} + +Result SharedValidator::OnRefFunc(const Location& loc, Var func_var) { + Result result = Result::Ok; + expr_loc_ = &loc; + result |= CheckDeclaredFunc(func_var); + result |= typechecker_.OnRefFuncExpr(func_var.index()); + return result; +} + +Result SharedValidator::OnRefIsNull(const Location& loc) { + Result result = Result::Ok; + expr_loc_ = &loc; + result |= typechecker_.OnRefIsNullExpr(); + return result; +} + +Result SharedValidator::OnRefNull(const Location& loc, Type type) { + Result result = Result::Ok; + expr_loc_ = &loc; + result |= typechecker_.OnRefNullExpr(type); + return result; +} + +Result SharedValidator::OnRethrow(const Location& loc, Var depth) { + Result result = Result::Ok; + expr_loc_ = &loc; + result |= typechecker_.OnRethrow(depth.index()); + return result; +} + +Result SharedValidator::OnReturnCall(const Location& loc, Var func_var) { + Result result = Result::Ok; + expr_loc_ = &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 = Result::Ok; + expr_loc_ = &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 = Result::Ok; + expr_loc_ = &loc; + result |= typechecker_.OnReturn(); + return result; +} + +Result SharedValidator::OnSelect(const Location& loc, + Index result_count, + Type* result_types) { + Result result = Result::Ok; + expr_loc_ = &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 = Result::Ok; + expr_loc_ = &loc; + result |= typechecker_.OnSimdLaneOp(opcode, value); + return result; +} + +Result SharedValidator::OnSimdLoadLane(const Location& loc, + Opcode opcode, + Address alignment, + uint64_t value) { + Result result = Result::Ok; + MemoryType mt; + expr_loc_ = &loc; + result |= CheckMemoryIndex(Var(0, loc), &mt); + result |= CheckAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnSimdLoadLane(opcode, mt.limits, value); + return result; +} + +Result SharedValidator::OnSimdStoreLane(const Location& loc, + Opcode opcode, + Address alignment, + uint64_t value) { + Result result = Result::Ok; + MemoryType mt; + expr_loc_ = &loc; + result |= CheckMemoryIndex(Var(0, loc), &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 = Result::Ok; + expr_loc_ = &loc; + result |= typechecker_.OnSimdShuffleOp(opcode, value); + return result; +} + +Result SharedValidator::OnStore(const Location& loc, + Opcode opcode, + Address alignment) { + Result result = Result::Ok; + MemoryType mt; + expr_loc_ = &loc; + result |= CheckMemoryIndex(Var(0, loc), &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 = Result::Ok; + expr_loc_ = &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 = Result::Ok; + expr_loc_ = &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 = Result::Ok; + expr_loc_ = &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 = Result::Ok; + expr_loc_ = &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 = Result::Ok; + expr_loc_ = &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 = Result::Ok; + expr_loc_ = &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 = Result::Ok; + expr_loc_ = &loc; + result |= CheckTableIndex(table_var); + result |= typechecker_.OnTableSize(); + return result; +} + +Result SharedValidator::OnTernary(const Location& loc, Opcode opcode) { + Result result = Result::Ok; + expr_loc_ = &loc; + result |= typechecker_.OnTernary(opcode); + return result; +} + +Result SharedValidator::OnThrow(const Location& loc, Var tag_var) { + Result result = Result::Ok; + expr_loc_ = &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 = Result::Ok; + TypeVector param_types, result_types; + expr_loc_ = &loc; + result |= CheckBlockSignature(loc, Opcode::Try, sig_type, ¶m_types, + &result_types); + result |= typechecker_.OnTry(param_types, result_types); + return result; +} + +Result SharedValidator::OnUnary(const Location& loc, Opcode opcode) { + Result result = Result::Ok; + expr_loc_ = &loc; + result |= typechecker_.OnUnary(opcode); + return result; +} + +Result SharedValidator::OnUnreachable(const Location& loc) { + Result result = Result::Ok; + expr_loc_ = &loc; + result |= typechecker_.OnUnreachable(); + return result; +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/shared-validator.h b/third_party/wasm2c/src/shared-validator.h new file mode 100644 index 0000000000..415e484eae --- /dev/null +++ b/third_party/wasm2c/src/shared-validator.h @@ -0,0 +1,314 @@ +/* + * 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. + */ + +#ifndef WABT_SHARED_VALIDATOR_H_ +#define WABT_SHARED_VALIDATOR_H_ + +#include <map> +#include <set> +#include <string> +#include <vector> + +#include "src/common.h" +#include "src/error.h" +#include "src/feature.h" +#include "src/ir.h" +#include "src/opcode.h" +#include "src/type-checker.h" + +#include "src/binary-reader.h" // For TypeMut. + +namespace wabt { + +struct ValidateOptions { + ValidateOptions() = default; + ValidateOptions(const Features& features) : features(features) {} + + Features features; +}; + +class SharedValidator { + public: + WABT_DISALLOW_COPY_AND_ASSIGN(SharedValidator); + SharedValidator(Errors*, const ValidateOptions& options); + + // TODO: Move into SharedValidator? + using Label = TypeChecker::Label; + size_t type_stack_size() const { return typechecker_.type_stack_size(); } + Result GetLabel(Index depth, Label** out_label) { + return typechecker_.GetLabel(depth, out_label); + } + + Result WABT_PRINTF_FORMAT(3, 4) + PrintError(const Location& loc, const char* fmt, ...); + + void OnTypecheckerError(const char* msg); + + Index GetLocalCount() const; + + Result EndModule(); + + Result OnFuncType(const Location&, + Index param_count, + const Type* param_types, + Index result_count, + const Type* result_types); + Result OnStructType(const Location&, Index field_count, TypeMut* fields); + Result OnArrayType(const Location&, TypeMut field); + + Result OnFunction(const Location&, Var sig_var); + Result OnTable(const Location&, Type elem_type, const Limits&); + Result OnMemory(const Location&, const Limits&); + Result OnGlobalImport(const Location&, Type type, bool mutable_); + Result OnGlobal(const Location&, Type type, bool mutable_); + Result OnGlobalInitExpr_Const(const Location&, Type); + Result OnGlobalInitExpr_GlobalGet(const Location&, Var global_var); + Result OnGlobalInitExpr_RefNull(const Location&, Type type); + Result OnGlobalInitExpr_RefFunc(const Location&, Var func_var); + Result OnGlobalInitExpr_Other(const Location&); + Result OnTag(const Location&, Var sig_var); + + Result OnExport(const Location&, + ExternalKind, + Var item_var, + string_view name); + + Result OnStart(const Location&, Var func_var); + + Result OnElemSegment(const Location&, Var table_var, SegmentKind); + void OnElemSegmentElemType(Type elem_type); + Result OnElemSegmentInitExpr_Const(const Location&, Type); + Result OnElemSegmentInitExpr_GlobalGet(const Location&, Var global_var); + Result OnElemSegmentInitExpr_Other(const Location&); + Result OnElemSegmentElemExpr_RefNull(const Location&, Type type); + Result OnElemSegmentElemExpr_RefFunc(const Location&, Var func_var); + Result OnElemSegmentElemExpr_Other(const Location&); + + void OnDataCount(Index count); + + Result OnDataSegment(const Location&, Var memory_var, SegmentKind); + Result OnDataSegmentInitExpr_Const(const Location&, Type); + Result OnDataSegmentInitExpr_GlobalGet(const Location&, Var global_var); + Result OnDataSegmentInitExpr_Other(const Location&); + + Result BeginFunctionBody(const Location&, Index func_index); + Result EndFunctionBody(const Location&); + Result OnLocalDecl(const Location&, Index count, Type type); + + Result OnAtomicFence(const Location&, uint32_t consistency_model); + Result OnAtomicLoad(const Location&, Opcode, Address align); + Result OnAtomicNotify(const Location&, Opcode, Address align); + Result OnAtomicRmwCmpxchg(const Location&, Opcode, Address align); + Result OnAtomicRmw(const Location&, Opcode, Address align); + Result OnAtomicStore(const Location&, Opcode, Address align); + Result OnAtomicWait(const Location&, Opcode, Address align); + Result OnBinary(const Location&, Opcode); + Result OnBlock(const Location&, Type sig_type); + Result OnBr(const Location&, Var depth); + Result OnBrIf(const Location&, Var depth); + Result BeginBrTable(const Location&); + Result OnBrTableTarget(const Location&, Var depth); + Result EndBrTable(const Location&); + Result OnCall(const Location&, Var func_var); + Result OnCallIndirect(const Location&, Var sig_var, Var table_var); + Result OnCallRef(const Location&, Index* function_type_index); + Result OnCatch(const Location&, Var tag_var, bool is_catch_all); + Result OnCompare(const Location&, Opcode); + Result OnConst(const Location&, Type); + Result OnConvert(const Location&, Opcode); + Result OnDataDrop(const Location&, Var segment_var); + Result OnDelegate(const Location&, Var depth); + Result OnDrop(const Location&); + Result OnElemDrop(const Location&, Var segment_var); + Result OnElse(const Location&); + Result OnEnd(const Location&); + Result OnGlobalGet(const Location&, Var); + Result OnGlobalSet(const Location&, Var); + Result OnIf(const Location&, Type sig_type); + Result OnLoad(const Location&, Opcode, Address align); + Result OnLoadSplat(const Location&, Opcode, Address align); + Result OnLoadZero(const Location&, Opcode, Address align); + Result OnLocalGet(const Location&, Var); + Result OnLocalSet(const Location&, Var); + Result OnLocalTee(const Location&, Var); + Result OnLoop(const Location&, Type sig_type); + Result OnMemoryCopy(const Location&); + Result OnMemoryFill(const Location&); + Result OnMemoryGrow(const Location&); + Result OnMemoryInit(const Location&, Var segment_var); + Result OnMemorySize(const Location&); + Result OnNop(const Location&); + Result OnRefFunc(const Location&, Var func_var); + Result OnRefIsNull(const Location&); + Result OnRefNull(const Location&, Type type); + Result OnRethrow(const Location&, Var depth); + Result OnReturnCall(const Location&, Var func_var); + Result OnReturnCallIndirect(const Location&, Var sig_var, Var table_var); + Result OnReturn(const Location&); + Result OnSelect(const Location&, Index result_count, Type* result_types); + Result OnSimdLaneOp(const Location&, Opcode, uint64_t lane_idx); + Result OnSimdLoadLane(const Location&, Opcode, Address align, uint64_t lane_idx); + Result OnSimdStoreLane(const Location&, Opcode, Address align, uint64_t lane_idx); + Result OnSimdShuffleOp(const Location&, Opcode, v128 lane_idx); + Result OnStore(const Location&, Opcode, Address align); + Result OnTableCopy(const Location&, Var dst_var, Var src_var); + Result OnTableFill(const Location&, Var table_var); + Result OnTableGet(const Location&, Var table_var); + Result OnTableGrow(const Location&, Var table_var); + Result OnTableInit(const Location&, Var segment_var, Var table_var); + Result OnTableSet(const Location&, Var table_var); + Result OnTableSize(const Location&, Var table_var); + Result OnTernary(const Location&, Opcode); + Result OnThrow(const Location&, Var tag_var); + Result OnTry(const Location&, Type sig_type); + Result OnUnary(const Location&, Opcode); + Result OnUnreachable(const Location&); + + private: + struct FuncType { + FuncType() = default; + FuncType(const TypeVector& params, const TypeVector& results) + : params(params), results(results) {} + + TypeVector params; + TypeVector results; + }; + + struct StructType { + StructType() = default; + StructType(const TypeMutVector& fields) : fields(fields) {} + + TypeMutVector fields; + }; + + struct ArrayType { + ArrayType() = default; + ArrayType(TypeMut field) : field(field) {} + + TypeMut field; + }; + + struct TableType { + TableType() = default; + TableType(Type element, Limits limits) : element(element), limits(limits) {} + + Type element = Type::Any; + Limits limits; + }; + + struct MemoryType { + MemoryType() = default; + MemoryType(Limits limits) : limits(limits) {} + + Limits limits; + }; + + struct GlobalType { + GlobalType() = default; + GlobalType(Type type, bool mutable_) : type(type), mutable_(mutable_) {} + + Type type = Type::Any; + bool mutable_ = true; + }; + + struct TagType { + TypeVector params; + }; + + struct ElemType { + ElemType() = default; + ElemType(Type element) : element(element) {} + + Type element; + }; + + struct LocalDecl { + Type type; + Index end; + }; + + Result CheckType(const Location&, + Type actual, + Type expected, + const char* desc); + Result CheckLimits(const Location&, + const Limits&, + uint64_t absolute_max, + const char* desc); + + Result CheckLocalIndex(Var local_var, Type* out_type); + + Result CheckDeclaredFunc(Var func_var); + + Result CheckIndex(Var var, Index max_index, const char* desc); + template <typename T> + Result CheckIndexWithValue(Var var, + const std::vector<T>& values, + T* out, + const char* desc); + Result CheckFuncTypeIndex(Var sig_var, FuncType* out = nullptr); + Result CheckFuncIndex(Var func_var, FuncType* out = nullptr); + Result CheckTableIndex(Var table_var, TableType* out = nullptr); + Result CheckMemoryIndex(Var memory_var, MemoryType* out = nullptr); + Result CheckGlobalIndex(Var global_var, GlobalType* out = nullptr); + Result CheckTagIndex(Var tag_var, TagType* out = nullptr); + Result CheckElemSegmentIndex(Var elem_segment_var, ElemType* out = nullptr); + Result CheckDataSegmentIndex(Var data_segment_var); + + Result CheckAlign(const Location&, Address align, Address natural_align); + Result CheckAtomicAlign(const Location&, Address align, Address natural_align); + + Result CheckBlockSignature(const Location&, + Opcode, + Type sig_type, + TypeVector* out_param_types, + TypeVector* out_result_types); + + TypeVector ToTypeVector(Index count, const Type* types); + + ValidateOptions options_; + Errors* errors_; + TypeChecker typechecker_; // TODO: Move into SharedValidator. + // Cached for access by OnTypecheckerError. + const Location* expr_loc_ = nullptr; + + Index num_types_ = 0; + std::map<Index, FuncType> func_types_; + std::map<Index, StructType> struct_types_; + std::map<Index, ArrayType> array_types_; + + std::vector<FuncType> funcs_; // Includes imported and defined. + std::vector<TableType> tables_; // Includes imported and defined. + std::vector<MemoryType> memories_; // Includes imported and defined. + std::vector<GlobalType> globals_; // Includes imported and defined. + std::vector<TagType> tags_; // Includes imported and defined. + std::vector<ElemType> elems_; + Index starts_ = 0; + Index num_imported_globals_ = 0; + Index data_segments_ = 0; + + // Includes parameters, since this is only used for validating + // local.{get,set,tee} instructions. + std::vector<LocalDecl> locals_; + + std::set<std::string> export_names_; // Used to check for duplicates. + std::set<Index> declared_funcs_; // TODO: optimize? + std::vector<Var> init_expr_funcs_; +}; + +} // namespace wabt + +#endif // WABT_SHARED_VALIDATOR_H_ diff --git a/third_party/wasm2c/src/stream.cc b/third_party/wasm2c/src/stream.cc new file mode 100644 index 0000000000..e52c51b94a --- /dev/null +++ b/third_party/wasm2c/src/stream.cc @@ -0,0 +1,316 @@ +/* + * 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 "src/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(string_view filename) const { + std::string filename_str = filename.to_string(); + 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; +} + +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(string_view filename, Stream* log_stream) + : Stream(log_stream), file_(nullptr), offset_(0), should_close_(false) { + std::string filename_str = filename.to_string(); + 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/stream.h b/third_party/wasm2c/src/stream.h new file mode 100644 index 0000000000..66582fa605 --- /dev/null +++ b/third_party/wasm2c/src/stream.h @@ -0,0 +1,227 @@ +/* + * 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_STREAM_H_ +#define WABT_STREAM_H_ + +#include <cassert> +#include <memory> +#include <vector> + +#include "src/common.h" + +namespace wabt { + +/* whether to display the ASCII characters in the debug output for + * write_memory */ +enum class PrintChars { + No = 0, + Yes = 1, +}; + +class Stream { + public: + explicit Stream(Stream* log_stream = nullptr); + virtual ~Stream() = default; + + size_t offset() { return offset_; } + Result result() { return result_; } + + void set_log_stream(Stream* stream) { + assert(stream); + log_stream_ = stream; + } + + Stream& log_stream() { + assert(log_stream_); + return *log_stream_; + } + + bool has_log_stream() const { return log_stream_ != nullptr; } + + void ClearOffset() { offset_ = 0; } + void AddOffset(ssize_t delta); + + void WriteData(const void* src, + size_t size, + const char* desc = nullptr, + PrintChars = PrintChars::No); + + template <typename T> + void WriteData(const std::vector<T> src, + const char* desc, + PrintChars print_chars = PrintChars::No) { + if (!src.empty()) { + WriteData(src.data(), src.size() * sizeof(T), desc, print_chars); + } + } + + void WriteDataAt(size_t offset, + const void* src, + size_t size, + const char* desc = nullptr, + PrintChars = PrintChars::No); + + void MoveData(size_t dst_offset, size_t src_offset, size_t size); + + void Truncate(size_t size); + + void WABT_PRINTF_FORMAT(2, 3) Writef(const char* format, ...); + + // Specified as uint32_t instead of uint8_t so we can check if the value + // given is in range before wrapping. + void WriteU8(uint32_t value, + const char* desc = nullptr, + PrintChars print_chars = PrintChars::No) { + assert(value <= UINT8_MAX); + Write(static_cast<uint8_t>(value), desc, print_chars); + } + void WriteU32(uint32_t value, + const char* desc = nullptr, + PrintChars print_chars = PrintChars::No) { + Write(value, desc, print_chars); + } + void WriteU64(uint64_t value, + const char* desc = nullptr, + PrintChars print_chars = PrintChars::No) { + Write(value, desc, print_chars); + } + void WriteU128(v128 value, + const char* desc = nullptr, + PrintChars print_chars = PrintChars::No) { + Write(value, desc, print_chars); + } + + void WriteChar(char c, + const char* desc = nullptr, + PrintChars print_chars = PrintChars::No) { + WriteU8(static_cast<unsigned char>(c), desc, print_chars); + } + + // Dump memory as text, similar to the xxd format. + void WriteMemoryDump(const void* start, + size_t size, + size_t offset = 0, + PrintChars print_chars = PrintChars::No, + const char* prefix = nullptr, + const char* desc = nullptr); + + // Convenience functions for writing enums. + template <typename T> + void WriteU8Enum(T value, + const char* desc = nullptr, + PrintChars print_chars = PrintChars::No) { + WriteU8(static_cast<uint32_t>(value), desc, print_chars); + } + + virtual void Flush() {} + + protected: + virtual Result WriteDataImpl(size_t offset, + const void* data, + size_t size) = 0; + virtual Result MoveDataImpl(size_t dst_offset, + size_t src_offset, + size_t size) = 0; + virtual Result TruncateImpl(size_t size) = 0; + + private: + template <typename T> + void Write(const T& data, const char* desc, PrintChars print_chars) { +#if WABT_BIG_ENDIAN + char tmp[sizeof(T)]; + memcpy(tmp, &data, sizeof(tmp)); + SwapBytesSized(tmp, sizeof(tmp)); + WriteData(tmp, sizeof(tmp), desc, print_chars); +#else + WriteData(&data, sizeof(data), desc, print_chars); +#endif + } + + size_t offset_; + Result result_; + // Not owned. If non-null, log all writes to this stream. + Stream* log_stream_; +}; + +struct OutputBuffer { + Result WriteToFile(string_view filename) const; + + void clear() { data.clear(); } + size_t size() const { return data.size(); } + + std::vector<uint8_t> data; +}; + +class MemoryStream : public Stream { + public: + WABT_DISALLOW_COPY_AND_ASSIGN(MemoryStream); + explicit MemoryStream(Stream* log_stream = nullptr); + explicit MemoryStream(std::unique_ptr<OutputBuffer>&&, + Stream* log_stream = nullptr); + + OutputBuffer& output_buffer() { return *buf_; } + std::unique_ptr<OutputBuffer> ReleaseOutputBuffer(); + + void Clear(); + + Result WriteToFile(string_view filename) { + return buf_->WriteToFile(filename); + } + + protected: + Result WriteDataImpl(size_t offset, const void* data, size_t size) override; + Result MoveDataImpl(size_t dst_offset, + size_t src_offset, + size_t size) override; + Result TruncateImpl(size_t size) override; + + private: + std::unique_ptr<OutputBuffer> buf_; +}; + +class FileStream : public Stream { + public: + WABT_DISALLOW_COPY_AND_ASSIGN(FileStream); + explicit FileStream(string_view filename, Stream* log_stream = nullptr); + explicit FileStream(FILE*, Stream* log_stream = nullptr); + FileStream(FileStream&&); + FileStream& operator=(FileStream&&); + ~FileStream() override; + + static std::unique_ptr<FileStream> CreateStdout(); + static std::unique_ptr<FileStream> CreateStderr(); + + bool is_open() const { return file_ != nullptr; } + + void Flush() override; + + protected: + Result WriteDataImpl(size_t offset, const void* data, size_t size) override; + Result MoveDataImpl(size_t dst_offset, + size_t src_offset, + size_t size) override; + Result TruncateImpl(size_t size) override; + + private: + FILE* file_; + size_t offset_; + bool should_close_; +}; + +} // namespace wabt + +#endif /* WABT_STREAM_H_ */ diff --git a/third_party/wasm2c/src/string-view.cc b/third_party/wasm2c/src/string-view.cc new file mode 100644 index 0000000000..bb32410a8e --- /dev/null +++ b/third_party/wasm2c/src/string-view.cc @@ -0,0 +1,196 @@ +/* + * 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 "src/string-view.h" + +#include <algorithm> +#include <limits> + +namespace wabt { + +void string_view::remove_prefix(size_type n) { + assert(n <= size_); + data_ += n; + size_ -= n; +} + +void string_view::remove_suffix(size_type n) { + assert(n <= size_); + size_ -= n; +} + +void string_view::swap(string_view& s) noexcept { + std::swap(data_, s.data_); + std::swap(size_, s.size_); +} + +string_view::operator std::string() const { + return std::string(data_, size_); +} + +std::string string_view::to_string() const { + return std::string(data_, size_); +} + +constexpr string_view::size_type string_view::max_size() const noexcept { + return std::numeric_limits<size_type>::max(); +} + +string_view::size_type string_view::copy(char* s, + size_type n, + size_type pos) const { + assert(pos <= size_); + size_t count = std::min(n, size_ - pos); + traits_type::copy(s, data_ + pos, count); + return count; +} + +string_view string_view::substr(size_type pos, size_type n) const { + assert(pos <= size_); + size_t count = std::min(n, size_ - pos); + return string_view(data_ + pos, count); +} + +int string_view::compare(string_view s) const noexcept { + size_type rlen = std::min(size_, s.size_); + int result = traits_type::compare(data_, s.data_, rlen); + if (result != 0 || size_ == s.size_) { + return result; + } + return size_ < s.size_ ? -1 : 1; +} + +int string_view::compare(size_type pos1, size_type n1, string_view s) const { + return substr(pos1, n1).compare(s); +} + +int string_view::compare(size_type pos1, + size_type n1, + string_view s, + size_type pos2, + size_type n2) const { + return substr(pos1, n1).compare(s.substr(pos2, n2)); +} + +int string_view::compare(const char* s) const { + return compare(string_view(s)); +} + +int string_view::compare(size_type pos1, size_type n1, const char* s) const { + return substr(pos1, n1).compare(string_view(s)); +} + +int string_view::compare(size_type pos1, + size_type n1, + const char* s, + size_type n2) const { + return substr(pos1, n1).compare(string_view(s, n2)); +} + +string_view::size_type string_view::find(string_view s, size_type pos) const + noexcept { + pos = std::min(pos, size_); + const_iterator iter = std::search(begin() + pos, end(), s.begin(), s.end()); + return iter == end() ? npos : iter - begin(); +} + +string_view::size_type string_view::find(char c, size_type pos) const noexcept { + return find(string_view(&c, 1), pos); +} + +string_view::size_type string_view::find(const char* s, + size_type pos, + size_type n) const { + return find(string_view(s, n), pos); +} + +string_view::size_type string_view::find(const char* s, size_type pos) const { + return find(string_view(s), pos); +} + +string_view::size_type string_view::rfind(string_view s, size_type pos) const + noexcept { + pos = std::min(std::min(pos, size_ - s.size_) + s.size_, size_); + reverse_iterator iter = std::search(reverse_iterator(begin() + pos), rend(), + s.rbegin(), s.rend()); + return iter == rend() ? npos : (rend() - iter - s.size_); +} + +string_view::size_type string_view::rfind(char c, size_type pos) const + noexcept { + return rfind(string_view(&c, 1), pos); +} + +string_view::size_type string_view::rfind(const char* s, + size_type pos, + size_type n) const { + return rfind(string_view(s, n), pos); +} + +string_view::size_type string_view::rfind(const char* s, size_type pos) const { + return rfind(string_view(s), pos); +} + +string_view::size_type string_view::find_first_of(string_view s, + size_type pos) const + noexcept { + pos = std::min(pos, size_); + const_iterator iter = + std::find_first_of(begin() + pos, end(), s.begin(), s.end()); + return iter == end() ? npos : iter - begin(); +} + +string_view::size_type string_view::find_first_of(char c, size_type pos) const + noexcept { + return find_first_of(string_view(&c, 1), pos); +} + +string_view::size_type string_view::find_first_of(const char* s, + size_type pos, + size_type n) const { + return find_first_of(string_view(s, n), pos); +} + +string_view::size_type string_view::find_first_of(const char* s, + size_type pos) const { + return find_first_of(string_view(s), pos); +} + +string_view::size_type string_view::find_last_of(string_view s, + size_type pos) const noexcept { + pos = std::min(pos, size_ - 1); + reverse_iterator iter = std::find_first_of( + reverse_iterator(begin() + (pos + 1)), rend(), s.begin(), s.end()); + return iter == rend() ? npos : (rend() - iter - 1); +} + +string_view::size_type string_view::find_last_of(char c, size_type pos) const + noexcept { + return find_last_of(string_view(&c, 1), pos); +} + +string_view::size_type string_view::find_last_of(const char* s, + size_type pos, + size_type n) const { + return find_last_of(string_view(s, n), pos); +} + +string_view::size_type string_view::find_last_of(const char* s, + size_type pos) const { + return find_last_of(string_view(s), pos); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/string-view.h b/third_party/wasm2c/src/string-view.h new file mode 100644 index 0000000000..534e675c73 --- /dev/null +++ b/third_party/wasm2c/src/string-view.h @@ -0,0 +1,347 @@ +/* + * 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. + */ + +#ifndef WABT_STRING_VIEW_H_ +#define WABT_STRING_VIEW_H_ + +#include <cassert> +#include <functional> +#include <iterator> +#include <ostream> +#include <string> + +#include "src/hash-util.h" + +namespace wabt { + +// This is a simplified implemention of C++17's basic_string_view template. +// +// Missing features: +// * Not a template +// * No allocator support +// * Some functions are not implemented +// * Asserts instead of exceptions +// * Some functions are not constexpr because we don't compile in C++17 mode + +class string_view { + public: + typedef std::char_traits<char> traits_type; + typedef char value_type; + typedef char* pointer; + typedef const char* const_pointer; + typedef char& reference; + typedef const char& const_reference; + typedef const char* const_iterator; + typedef const_iterator iterator; + typedef std::reverse_iterator<const_iterator> const_reverse_iterator; + typedef const_reverse_iterator reverse_iterator; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + static const size_type npos = size_type(-1); + + // construction and assignment + constexpr string_view() noexcept; + constexpr string_view(const string_view&) noexcept = default; + string_view& operator=(const string_view&) noexcept = default; + string_view(const std::string& str) noexcept; + /*constexpr*/ string_view(const char* str); + constexpr string_view(const char* str, size_type len); + + // iterator support + constexpr const_iterator begin() const noexcept; + constexpr const_iterator end() const noexcept; + constexpr const_iterator cbegin() const noexcept; + constexpr const_iterator cend() const noexcept; + const_reverse_iterator rbegin() const noexcept; + const_reverse_iterator rend() const noexcept; + const_reverse_iterator crbegin() const noexcept; + const_reverse_iterator crend() const noexcept; + + // capacity + constexpr size_type size() const noexcept; + constexpr size_type length() const noexcept; + constexpr size_type max_size() const noexcept; + constexpr bool empty() const noexcept; + + // element access + constexpr const_reference operator[](size_type pos) const; + /*constexpr*/ const_reference at(size_type pos) const; + /*constexpr*/ const_reference front() const; + /*constexpr*/ const_reference back() const; + constexpr const_pointer data() const noexcept; + + // modifiers + /*constexpr*/ void remove_prefix(size_type n); + /*constexpr*/ void remove_suffix(size_type n); + /*constexpr*/ void swap(string_view& s) noexcept; + + // string operations + explicit operator std::string() const; + std::string to_string() const; + size_type copy(char* s, size_type n, size_type pos = 0) const; + /*constexpr*/ string_view substr(size_type pos = 0, size_type n = npos) const; + /*constexpr*/ int compare(string_view s) const noexcept; + /*constexpr*/ int compare(size_type pos1, size_type n1, string_view s) const; + /*constexpr*/ int compare(size_type pos1, + size_type n1, + string_view s, + size_type pos2, + size_type n2) const; + /*constexpr*/ int compare(const char* s) const; + /*constexpr*/ int compare(size_type pos1, size_type n1, const char* s) const; + /*constexpr*/ int compare(size_type pos1, + size_type n1, + const char* s, + size_type n2) const; + /*constexpr*/ size_type find(string_view s, size_type pos = 0) const noexcept; + /*constexpr*/ size_type find(char c, size_type pos = 0) const noexcept; + /*constexpr*/ size_type find(const char* s, size_type pos, size_type n) const; + /*constexpr*/ size_type find(const char* s, size_type pos = 0) const; + /*constexpr*/ size_type rfind(string_view s, size_type pos = npos) const + noexcept; + /*constexpr*/ size_type rfind(char c, size_type pos = npos) const noexcept; + /*constexpr*/ size_type rfind(const char* s, + size_type pos, + size_type n) const; + /*constexpr*/ size_type rfind(const char* s, size_type pos = npos) const; + /*constexpr*/ size_type find_first_of(string_view s, size_type pos = 0) const + noexcept; + /*constexpr*/ size_type find_first_of(char c, size_type pos = 0) const + noexcept; + /*constexpr*/ size_type find_first_of(const char* s, + size_type pos, + size_type n) const; + /*constexpr*/ size_type find_first_of(const char* s, size_type pos = 0) const; + /*constexpr*/ size_type find_last_of(string_view s, + size_type pos = npos) const noexcept; + /*constexpr*/ size_type find_last_of(char c, size_type pos = npos) const + noexcept; + /*constexpr*/ size_type find_last_of(const char* s, + size_type pos, + size_type n) const; + /*constexpr*/ size_type find_last_of(const char* s, + size_type pos = npos) const; + +// TODO(binji): These are defined by C++17 basic_string_view but not +// implemented here. +#if 0 + constexpr size_type find_first_not_of(string_view s, size_type pos = 0) const + noexcept; + constexpr size_type find_first_not_of(char c, size_type pos = 0) const + noexcept; + constexpr size_type find_first_not_of(const char* s, + size_type pos, + size_type n) const; + constexpr size_type find_first_not_of(const char* s, size_type pos = 0) const; + constexpr size_type find_last_not_of(string_view s, + size_type pos = npos) const noexcept; + constexpr size_type find_last_not_of(char c, size_type pos = npos) const + noexcept; + constexpr size_type find_last_not_of(const char* s, + size_type pos, + size_type n) const; + constexpr size_type find_last_not_of(const char* s, + size_type pos = npos) const; +#endif + + private: + const char* data_; + size_type size_; +}; + +// non-member comparison functions +inline bool operator==(string_view x, string_view y) noexcept { + return x.compare(y) == 0; +} + +inline bool operator!=(string_view x, string_view y) noexcept { + return x.compare(y) != 0; +} + +inline bool operator<(string_view x, string_view y) noexcept { + return x.compare(y) < 0; +} + +inline bool operator>(string_view x, string_view y) noexcept { + return x.compare(y) > 0; +} + +inline bool operator<=(string_view x, string_view y) noexcept { + return x.compare(y) <= 0; +} + +inline bool operator>=(string_view x, string_view y) noexcept { + return x.compare(y) >= 0; +} + +// non-member append. +inline std::string& operator+=(std::string& x, string_view y) { + x.append(y.data(), y.size()); + return x; +} + +inline std::string operator+(string_view x, string_view y) { + std::string s; + s.reserve(x.size() + y.size()); + s.append(x.data(), x.size()); + s.append(y.data(), y.size()); + return s; +} + +inline std::string operator+(const std::string& x, string_view y) { + return string_view(x) + y; +} + +inline std::string operator+(string_view x, const std::string& y) { + return x + string_view(y); +} + +inline std::string operator+(const char* x, string_view y) { + return string_view(x) + y; +} + +inline std::string operator+(string_view x, const char* y) { + return x + string_view(y); +} + +inline void cat_concatenate(std::string&) {} + +template<typename T, typename ...Ts> +void cat_concatenate(std::string& s, const T& t, const Ts&... args) { + s += t; + cat_concatenate(s, args...); +} + +inline size_t cat_compute_size() { return 0; } + +template<typename T, typename ...Ts> +size_t cat_compute_size(const T& t, const Ts&... args) { + return string_view(t).size() + cat_compute_size(args...); +} + +// Is able to concatenate any combination of string/string_view/char* +template<typename ...Ts> std::string cat(const Ts&... args) { + std::string s; + s.reserve(cat_compute_size(args...)); + cat_concatenate(s, args...); + return s; +} + +inline constexpr string_view::string_view() noexcept + : data_(nullptr), size_(0) {} + +inline string_view::string_view(const std::string& str) noexcept + : data_(str.data()), size_(str.size()) {} + +inline string_view::string_view(const char* str) + : data_(str), size_(traits_type::length(str)) {} + +inline constexpr string_view::string_view(const char* str, size_type len) + : data_(str), size_(len) {} + +inline constexpr string_view::const_iterator string_view::begin() const + noexcept { + return data_; +} + +inline constexpr string_view::const_iterator string_view::end() const noexcept { + return data_ + size_; +} + +inline constexpr string_view::const_iterator string_view::cbegin() const + noexcept { + return data_; +} + +inline constexpr string_view::const_iterator string_view::cend() const + noexcept { + return data_ + size_; +} + +inline string_view::const_reverse_iterator string_view::rbegin() const + noexcept { + return const_reverse_iterator(end()); +} + +inline string_view::const_reverse_iterator string_view::rend() const noexcept { + return const_reverse_iterator(begin()); +} + +inline string_view::const_reverse_iterator string_view::crbegin() const + noexcept { + return const_reverse_iterator(cend()); +} + +inline string_view::const_reverse_iterator string_view::crend() const noexcept { + return const_reverse_iterator(cbegin()); +} + +constexpr inline string_view::size_type string_view::size() const noexcept { + return size_; +} + +constexpr inline string_view::size_type string_view::length() const noexcept { + return size_; +} + +constexpr inline bool string_view::empty() const noexcept { + return size_ == 0; +} + +constexpr inline string_view::const_reference string_view::operator[]( + size_type pos) const { + return data_[pos]; +} + +inline string_view::const_reference string_view::at(size_type pos) const { + assert(pos < size_); + return data_[pos]; +} + +inline string_view::const_reference string_view::front() const { + assert(!empty()); + return *data_; +} + +inline string_view::const_reference string_view::back() const { + assert(!empty()); + return data_[size_ - 1]; +} + +constexpr inline string_view::const_pointer string_view::data() const noexcept { + return data_; +} + +inline std::ostream& operator<<(std::ostream& os, string_view sv) { + os.write(sv.data(), sv.size()); + return os; +} + +} // namespace wabt + +namespace std { + +// hash support +template <> +struct hash<::wabt::string_view> { + ::wabt::hash_code operator()(const ::wabt::string_view& sv) { + return ::wabt::HashRange(sv.begin(), sv.end()); + } +}; + +} // namespace std + +#endif // WABT_STRING_VIEW_H_ 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..a96ccdb7a3 --- /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 "src/binary-reader.h" +#include "src/binary-reader-nop.h" +#include "src/leb128.h" +#include "src/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..aefea514e2 --- /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 "src/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..5b0282fce7 --- /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 "src/filenames.h" + +using namespace wabt; + +namespace { + +void assert_string_view_eq(const char* s, 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..7b493518ec --- /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 "src/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 const 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..3a77194cad --- /dev/null +++ b/third_party/wasm2c/src/test-interp.cc @@ -0,0 +1,693 @@ +/* + * 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 "src/binary-reader.h" +#include "src/error-formatter.h" + +#include "src/interp/binary-reader-interp.h" +#include "src/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(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, + }); + + auto thread = Thread::New(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 (get_local $c) (i32.const 65)) + // (return (get_local $c))) + // + // ;; Clear 5th bit of c, to force uppercase. 0xdf = 0b11011111 + // (set_local $uc (i32.and (get_local $c) (i32.const 0xdf))) + // + // ;; In range ['A', 'M'] return |c| + 13. + // (if (i32.le_u (get_local $uc) (i32.const 77)) + // (return (i32.add (get_local $c) (i32.const 13)))) + // + // ;; In range ['N', 'Z'] return |c| - 13. + // (if (i32.le_u (get_local $uc) (i32.const 90)) + // (return (i32.sub (get_local $c) (i32.const 13)))) + // + // ;; No change for everything else. + // (return (get_local $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. + // (set_local $size) + // + // ;; Loop over all bytes and rot13 them. + // (block $exit + // (loop $top + // ;; if (i >= size) break + // (if (i32.ge_u (get_local $i) (get_local $size)) (br $exit)) + // + // ;; mem[i] = rot13c(mem[i]) + // (i32.store8 + // (get_local $i) + // (call $rot13c + // (i32.load8_u (get_local $i)))) + // + // ;; i++ + // (set_local $i (i32.add (get_local $i) (i32.const 1))) + // (br $top) + // ) + // ) + // + // (call $buf_done (i32.const 0) (get_local $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 + 6, after_new); // module, instance, f, t, m, g + + // Instance keeps all exports alive. + store_.Collect(); + EXPECT_EQ(after_new, 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..dfc3dbb378 --- /dev/null +++ b/third_party/wasm2c/src/test-intrusive-list.cc @@ -0,0 +1,584 @@ +/* + * 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 "src/intrusive-list.h" +#include "src/make-unique.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; + +typedef intrusive_list<TestObject> TestObjectList; + +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(MakeUnique<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(MakeUnique<TestObject>(1)); + list.push_front(MakeUnique<TestObject>(2)); + list.push_front(MakeUnique<TestObject>(3)); + + AssertListEq(list, {3, 2, 1}); +} + +TEST_F(IntrusiveListTest, push_back_pointer) { + TestObjectList list; + + list.push_back(MakeUnique<TestObject>(1)); + list.push_back(MakeUnique<TestObject>(2)); + list.push_back(MakeUnique<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(), MakeUnique<TestObject>(2)); + list.insert(list.end(), MakeUnique<TestObject>(4)); + list.insert(std::next(list.begin()), MakeUnique<TestObject>(3)); + list.insert(list.begin(), MakeUnique<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..bce7a0b8f0 --- /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 "src/literal.h" + +using namespace wabt; + +namespace { + +enum ParseIntTypeCombo { + UnsignedOnly, + SignedAndUnsigned, + Both, +}; + +template <typename T, typename F> +void AssertIntEquals(T expected, + const char* s, + F&& parse_int, + 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(-0x80000000u, "-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(-0x0bcdefabcdefabcdull, "-0x0BCDEFABCDEFABCD", + SignedAndUnsigned); + AssertInt64Equals(0xabcdefabcdefabcdull, "+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..9295c58157 --- /dev/null +++ b/third_party/wasm2c/src/test-option-parser.cc @@ -0,0 +1,181 @@ +// 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 "src/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-string-view.cc b/third_party/wasm2c/src/test-string-view.cc new file mode 100644 index 0000000000..4a4499dd57 --- /dev/null +++ b/third_party/wasm2c/src/test-string-view.cc @@ -0,0 +1,415 @@ +/* + * 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 "src/string-view.h" + +#include <cstring> +#include <functional> + +using namespace wabt; + +namespace { + +void assert_string_view_eq(const char* s, 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]); + } +} + +constexpr string_view::size_type npos = string_view::npos; + +} // end anonymous namespace + +TEST(string_view, default_constructor) { + assert_string_view_eq("", string_view()); +} + +TEST(string_view, copy_constructor) { + string_view sv1("copy"); + assert_string_view_eq("copy", string_view(sv1)); + + string_view sv2; + assert_string_view_eq("", string_view(sv2)); +} + +TEST(string_view, assignment_operator) { + string_view sv1; + sv1 = string_view("assign"); + assert_string_view_eq("assign", sv1); + + string_view sv2; + sv2 = string_view(); + assert_string_view_eq("", sv2); +} + +TEST(string_view, string_constructor) { + assert_string_view_eq("", string_view(std::string())); + assert_string_view_eq("string", string_view(std::string("string"))); +} + +TEST(string_view, cstr_constructor) { + assert_string_view_eq("", string_view("")); + assert_string_view_eq("cstr", string_view("cstr")); +} + +TEST(string_view, cstr_len_constructor) { + assert_string_view_eq("", string_view("foo-bar-baz", 0)); + assert_string_view_eq("foo", string_view("foo-bar-baz", 3)); + assert_string_view_eq("foo-bar", string_view("foo-bar-baz", 7)); +} + +TEST(string_view, begin_end) { + string_view sv("012345"); + + char count = 0; + for (auto iter = sv.begin(), end = sv.end(); iter != end; ++iter) { + ASSERT_EQ('0' + count, *iter); + ++count; + } + ASSERT_EQ(6, count); +} + +TEST(string_view, cbegin_cend) { + const string_view sv("012345"); + + char count = 0; + for (auto iter = sv.cbegin(), end = sv.cend(); iter != end; ++iter) { + ASSERT_EQ('0' + count, *iter); + ++count; + } + ASSERT_EQ(6, count); +} + +TEST(string_view, rbegin_rend) { + string_view sv("012345"); + + char count = 0; + for (auto iter = sv.rbegin(), end = sv.rend(); iter != end; ++iter) { + ASSERT_EQ('5' - count, *iter); + ++count; + } + ASSERT_EQ(6, count); +} + +TEST(string_view, crbegin_crend) { + const string_view sv("012345"); + + char count = 0; + for (auto iter = sv.crbegin(), end = sv.crend(); iter != end; ++iter) { + ASSERT_EQ('5' - count, *iter); + ++count; + } + ASSERT_EQ(6, count); +} + +TEST(string_view, size) { + string_view sv1; + ASSERT_EQ(0U, sv1.size()); + + string_view sv2(""); + ASSERT_EQ(0U, sv2.size()); + + string_view sv3("hello"); + ASSERT_EQ(5U, sv3.size()); +} + +TEST(string_view, length) { + string_view sv1; + ASSERT_EQ(0U, sv1.length()); + + string_view sv2("hello"); + ASSERT_EQ(5U, sv2.length()); +} + +TEST(string_view, empty) { + string_view sv1; + ASSERT_TRUE(sv1.empty()); + + string_view sv2("bye"); + ASSERT_FALSE(sv2.empty()); +} + +TEST(string_view, operator_bracket) { + string_view sv("words"); + ASSERT_EQ('w', sv[0]); + ASSERT_EQ('o', sv[1]); + ASSERT_EQ('r', sv[2]); + ASSERT_EQ('d', sv[3]); + ASSERT_EQ('s', sv[4]); +} + +TEST(string_view, at) { + string_view sv("words"); + ASSERT_EQ('w', sv.at(0)); + ASSERT_EQ('o', sv.at(1)); + ASSERT_EQ('r', sv.at(2)); + ASSERT_EQ('d', sv.at(3)); + ASSERT_EQ('s', sv.at(4)); +} + +TEST(string_view, front) { + string_view sv("words"); + ASSERT_EQ('w', sv.front()); +} + +TEST(string_view, back) { + string_view sv("words"); + ASSERT_EQ('s', sv.back()); +} + +TEST(string_view, data) { + const char* cstr = "words"; + string_view sv(cstr); + ASSERT_EQ(cstr, sv.data()); +} + +TEST(string_view, remove_prefix) { + string_view sv("words"); + sv.remove_prefix(2); + assert_string_view_eq("rds", sv); +} + +TEST(string_view, remove_suffix) { + string_view sv("words"); + sv.remove_suffix(2); + assert_string_view_eq("wor", sv); +} + +TEST(string_view, swap) { + string_view sv1("hello"); + string_view sv2("bye"); + + sv1.swap(sv2); + + assert_string_view_eq("bye", sv1); + assert_string_view_eq("hello", sv2); +} + +TEST(string_view, operator_std_string) { + string_view sv1("hi"); + std::string s(sv1); + + ASSERT_EQ(2U, s.size()); + ASSERT_EQ('h', s[0]); + ASSERT_EQ('i', s[1]); +} + +TEST(string_view, copy) { + string_view sv("words"); + char buffer[10] = {0}; + + sv.copy(buffer, 10, 2); + ASSERT_EQ('r', buffer[0]); + ASSERT_EQ('d', buffer[1]); + ASSERT_EQ('s', buffer[2]); + for (int i = 3; i < 10; ++i) { + ASSERT_EQ(0, buffer[i]); + } +} + +TEST(string_view, substr) { + string_view sv1("abcdefghij"); + string_view sv2 = sv1.substr(2, 3); + assert_string_view_eq("cde", sv2); +} + +TEST(string_view, compare0) { + ASSERT_TRUE(string_view("meat").compare(string_view("meet")) < 0); + ASSERT_TRUE(string_view("rest").compare(string_view("rate")) > 0); + ASSERT_TRUE(string_view("equal").compare(string_view("equal")) == 0); + ASSERT_TRUE(string_view("star").compare(string_view("start")) < 0); + ASSERT_TRUE(string_view("finished").compare(string_view("fin")) > 0); +} + +TEST(string_view, compare1) { + ASSERT_TRUE(string_view("abcdef").compare(2, 2, string_view("ca")) > 0); + ASSERT_TRUE(string_view("abcdef").compare(2, 2, string_view("cd")) == 0); + ASSERT_TRUE(string_view("abcdef").compare(2, 2, string_view("cz")) < 0); +} + +TEST(string_view, compare2) { + ASSERT_TRUE(string_view("abcdef").compare(2, 2, string_view("_ca__"), 1, 2) > + 0); + ASSERT_TRUE(string_view("abcdef").compare(2, 2, string_view("_cd__"), 1, 2) == + 0); + ASSERT_TRUE(string_view("abcdef").compare(2, 2, string_view("_cz__"), 1, 2) < + 0); +} + +TEST(string_view, compare3) { + ASSERT_TRUE(string_view("abcdef").compare("aaaa") > 0); + ASSERT_TRUE(string_view("abcdef").compare("abcdef") == 0); + ASSERT_TRUE(string_view("abcdef").compare("zzzz") < 0); +} + +TEST(string_view, compare4) { + ASSERT_TRUE(string_view("abcdef").compare(2, 2, "ca") > 0); + ASSERT_TRUE(string_view("abcdef").compare(2, 2, "cd") == 0); + ASSERT_TRUE(string_view("abcdef").compare(2, 2, "cz") < 0); +} + +TEST(string_view, compare5) { + ASSERT_TRUE(string_view("abcdef").compare(2, 2, "ca____", 2) > 0); + ASSERT_TRUE(string_view("abcdef").compare(2, 2, "cd___", 2) == 0); + ASSERT_TRUE(string_view("abcdef").compare(2, 2, "cz__", 2) < 0); +} + +TEST(string_view, find0) { + ASSERT_EQ(0U, string_view("find fins").find(string_view("fin"))); + ASSERT_EQ(5U, string_view("find fins").find(string_view("fin"), 1)); + ASSERT_EQ(npos, string_view("find fins").find(string_view("fin"), 6)); +} + +TEST(string_view, find1) { + ASSERT_EQ(0U, string_view("012340123").find('0')); + ASSERT_EQ(5U, string_view("012340123").find('0', 2)); + ASSERT_EQ(npos, string_view("012340123").find('0', 6)); +} + +TEST(string_view, find2) { + ASSERT_EQ(1U, string_view("012340123").find("12345", 0, 2)); + ASSERT_EQ(6U, string_view("012340123").find("12345", 3, 2)); + ASSERT_EQ(npos, string_view("012340123").find("12345", 10, 2)); +} + +TEST(string_view, find3) { + ASSERT_EQ(1U, string_view("012340123").find("12")); + ASSERT_EQ(6U, string_view("012340123").find("12", 2)); + ASSERT_EQ(npos, string_view("012340123").find("12", 10)); +} + +TEST(string_view, rfind0) { + ASSERT_EQ(5U, string_view("find fins").rfind(string_view("fin"))); + ASSERT_EQ(0U, string_view("find fins").rfind(string_view("fin"), 4)); + ASSERT_EQ(npos, string_view("find fins").rfind(string_view("no"))); + ASSERT_EQ(npos, string_view("foo").rfind(string_view("foobar"))); +} + +TEST(string_view, rfind1) { + ASSERT_EQ(5U, string_view("012340123").rfind('0')); + ASSERT_EQ(0U, string_view("012340123").rfind('0', 2)); + ASSERT_EQ(npos, string_view("012340123").rfind('9')); +} + +TEST(string_view, rfind2) { + ASSERT_EQ(6U, string_view("012340123").rfind("12345", npos, 2)); + ASSERT_EQ(1U, string_view("012340123").rfind("12345", 4, 2)); + ASSERT_EQ(npos, string_view("012340123").rfind("12345", npos, 5)); + ASSERT_EQ(npos, string_view("012").rfind("12345", npos, 5)); +} + +TEST(string_view, rfind3) { + ASSERT_EQ(6U, string_view("012340123").rfind("12")); + ASSERT_EQ(1U, string_view("012340123").rfind("12", 2)); + ASSERT_EQ(npos, string_view("012340123").rfind("12", 0)); + ASSERT_EQ(npos, string_view("012").rfind("12345")); +} + +TEST(string_view, find_first_of0) { + ASSERT_EQ(0U, string_view("0123abc").find_first_of(string_view("0a"))); + ASSERT_EQ(4U, string_view("0123abc").find_first_of(string_view("0a"), 1)); + ASSERT_EQ(npos, string_view("0123abc").find_first_of(string_view("xyz"))); +} + +TEST(string_view, find_first_of1) { + ASSERT_EQ(1U, string_view("ahellohi").find_first_of('h')); + ASSERT_EQ(6U, string_view("ahellohi").find_first_of('h', 2)); + ASSERT_EQ(npos, string_view("ahellohi").find_first_of('z', 2)); +} + +TEST(string_view, find_first_of2) { + ASSERT_EQ(0U, string_view("0123abc").find_first_of("0a1b", 0, 2)); + ASSERT_EQ(4U, string_view("0123abc").find_first_of("0a1b", 1, 2)); + ASSERT_EQ(npos, string_view("0123abc").find_first_of("0a1b", 5, 2)); +} + +TEST(string_view, find_first_of3) { + ASSERT_EQ(0U, string_view("0123abc").find_first_of("0a")); + ASSERT_EQ(0U, string_view("0123abc").find_first_of("0a", 0)); + ASSERT_EQ(4U, string_view("0123abc").find_first_of("0a", 1)); + ASSERT_EQ(npos, string_view("0123abc").find_first_of("0a", 5)); +} + +TEST(string_view, find_last_of0) { + ASSERT_EQ(4U, string_view("0123abc").find_last_of(string_view("0a"))); + ASSERT_EQ(0U, string_view("0123abc").find_last_of(string_view("0a"), 1)); + ASSERT_EQ(npos, string_view("0123abc").find_last_of(string_view("xyz"))); +} + +TEST(string_view, find_last_of1) { + ASSERT_EQ(6U, string_view("ahellohi").find_last_of('h')); + ASSERT_EQ(1U, string_view("ahellohi").find_last_of('h', 2)); + ASSERT_EQ(npos, string_view("ahellohi").find_last_of('z', 2)); +} + +TEST(string_view, find_last_of2) { + ASSERT_EQ(4U, string_view("0123abc").find_last_of("0a1b", npos, 2)); + ASSERT_EQ(0U, string_view("0123abc").find_last_of("0a1b", 1, 2)); + ASSERT_EQ(npos, string_view("0123abc").find_last_of("a1b", 0, 2)); + ASSERT_EQ(npos, string_view("0123abc").find_last_of("xyz", npos, 0)); +} + +TEST(string_view, find_last_of3) { + ASSERT_EQ(4U, string_view("0123abc").find_last_of("0a")); + ASSERT_EQ(4U, string_view("0123abc").find_last_of("0a", npos)); + ASSERT_EQ(0U, string_view("0123abc").find_last_of("0a", 1)); + ASSERT_EQ(npos, string_view("0123abc").find_last_of("a1", 0)); +} + +TEST(string_view, operator_equal) { + ASSERT_TRUE(string_view("this") == string_view("this")); + ASSERT_FALSE(string_view("this") == string_view("that")); +} + +TEST(string_view, operator_not_equal) { + ASSERT_FALSE(string_view("here") != string_view("here")); + ASSERT_TRUE(string_view("here") != string_view("there")); +} + +TEST(string_view, operator_less_than) { + ASSERT_TRUE(string_view("abc") < string_view("xyz")); + ASSERT_FALSE(string_view("later") < string_view("earlier")); + ASSERT_FALSE(string_view("one") < string_view("one")); +} + +TEST(string_view, operator_greater_than) { + ASSERT_TRUE(string_view("much") > string_view("little")); + ASSERT_FALSE(string_view("future") > string_view("past")); + ASSERT_FALSE(string_view("now") > string_view("now")); +} + +TEST(string_view, operator_less_than_or_equal) { + ASSERT_TRUE(string_view("abc") <= string_view("xyz")); + ASSERT_FALSE(string_view("later") <= string_view("earlier")); + ASSERT_TRUE(string_view("one") <= string_view("one")); +} + +TEST(string_view, operator_greater_than_or_equal) { + ASSERT_TRUE(string_view("much") >= string_view("little")); + ASSERT_FALSE(string_view("future") >= string_view("past")); + ASSERT_TRUE(string_view("now") >= string_view("now")); +} + +TEST(string_view, hash) { + std::hash<string_view> hasher; + + ASSERT_NE(hasher(string_view("hello")), hasher(string_view("goodbye"))); + ASSERT_EQ(hasher(string_view("same")), hasher(string_view("same"))); +} diff --git a/third_party/wasm2c/src/test-utf8.cc b/third_party/wasm2c/src/test-utf8.cc new file mode 100644 index 0000000000..8e420628bc --- /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 "src/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..00a27bf56a --- /dev/null +++ b/third_party/wasm2c/src/test-wast-parser.cc @@ -0,0 +1,87 @@ +/* + * 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 "src/wast-lexer.h" +#include "src/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) { + auto lexer = WastLexer::CreateBufferLexer("test", text.c_str(), text.size()); + Errors 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..657d37bed5 --- /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 "src/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 "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, 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 literal_.text.to_string(); + } else if (HasOpcode()) { + return opcode_.GetName(); + } else if (HasText()) { + return text_.to_string(); + } 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/token.def b/third_party/wasm2c/src/token.def new file mode 100644 index 0000000000..3238675d88 --- /dev/null +++ b/third_party/wasm2c/src/token.def @@ -0,0 +1,172 @@ +/* + * 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. + */ + +#ifndef WABT_TOKEN +#error "You must define WABT_TOKEN before including this file." +#endif + +/* Tokens with no additional data (i.e. bare). */ +WABT_TOKEN(Invalid, "Invalid") +WABT_TOKEN(Array, "array") +WABT_TOKEN(AssertExhaustion, "assert_exhaustion") +WABT_TOKEN(AssertInvalid, "assert_invalid") +WABT_TOKEN(AssertMalformed, "assert_malformed") +WABT_TOKEN(AssertReturn, "assert_return") +WABT_TOKEN(AssertTrap, "assert_trap") +WABT_TOKEN(AssertUnlinkable, "assert_unlinkable") +WABT_TOKEN(Bin, "bin") +WABT_TOKEN(Item, "item") +WABT_TOKEN(Data, "data") +WABT_TOKEN(Declare, "declare") +WABT_TOKEN(Delegate, "delegate") +WABT_TOKEN(Do, "do") +WABT_TOKEN(Elem, "elem") +WABT_TOKEN(Eof, "EOF") +WABT_TOKEN(Tag, "tag") +WABT_TOKEN(Export, "export") +WABT_TOKEN(Field, "field") +WABT_TOKEN(Get, "get") +WABT_TOKEN(Global, "global") +WABT_TOKEN(Import, "import") +WABT_TOKEN(Invoke, "invoke") +WABT_TOKEN(Input, "input") +WABT_TOKEN(Local, "local") +WABT_TOKEN(Lpar, "(") +WABT_TOKEN(Memory, "memory") +WABT_TOKEN(Module, "module") +WABT_TOKEN(Mut, "mut") +WABT_TOKEN(NanArithmetic, "nan:arithmetic") +WABT_TOKEN(NanCanonical, "nan:canonical") +WABT_TOKEN(Offset, "offset") +WABT_TOKEN(Output, "output") +WABT_TOKEN(Param, "param") +WABT_TOKEN(Quote, "quote") +WABT_TOKEN(Register, "register") +WABT_TOKEN(Result, "result") +WABT_TOKEN(Rpar, ")") +WABT_TOKEN(Shared, "shared") +WABT_TOKEN(Start, "start") +WABT_TOKEN(Struct, "struct") +WABT_TOKEN(Table, "table") +WABT_TOKEN(Then, "then") +WABT_TOKEN(Type, "type") +WABT_TOKEN(I8X16, "i8x16") +WABT_TOKEN(I16X8, "i16x8") +WABT_TOKEN(I32X4, "i32x4") +WABT_TOKEN(I64X2, "i64x2") +WABT_TOKEN(F32X4, "f32x4") +WABT_TOKEN(F64X2, "f64x2") +WABT_TOKEN_FIRST(Bare, Invalid) +WABT_TOKEN_LAST(Bare, F64X2) + +/* Tokens with Literal data. */ +WABT_TOKEN(Float, "FLOAT") +WABT_TOKEN(Int, "INT") +WABT_TOKEN(Nat, "NAT") +WABT_TOKEN_FIRST(Literal, Float) +WABT_TOKEN_LAST(Literal, Nat) + +/* Tokens with Opcode data. */ +WABT_TOKEN(AtomicFence, "atomic.fence") +WABT_TOKEN(AtomicLoad, "ATOMIC_LOAD") +WABT_TOKEN(AtomicNotify, "ATOMIC_NOTIFY") +WABT_TOKEN(AtomicRmw, "ATOMIC_RMW") +WABT_TOKEN(AtomicRmwCmpxchg, "ATOMIC_RMW_CMPXCHG") +WABT_TOKEN(AtomicStore, "ATOMIC_STORE") +WABT_TOKEN(AtomicWait, "ATOMIC_WAIT") +WABT_TOKEN(Binary, "BINARY") +WABT_TOKEN(Block, "block") +WABT_TOKEN(Br, "br") +WABT_TOKEN(BrIf, "br_if") +WABT_TOKEN(BrTable, "br_table") +WABT_TOKEN(Call, "call") +WABT_TOKEN(CallIndirect, "call_indirect") +WABT_TOKEN(CallRef, "call_ref") +WABT_TOKEN(Catch, "catch") +WABT_TOKEN(CatchAll, "catch_all") +WABT_TOKEN(Compare, "COMPARE") +WABT_TOKEN(Const, "CONST") +WABT_TOKEN(Convert, "CONVERT") +WABT_TOKEN(DataDrop, "data.drop") +WABT_TOKEN(Drop, "drop") +WABT_TOKEN(ElemDrop, "elem.drop") +WABT_TOKEN(Else, "else") +WABT_TOKEN(End, "end") +WABT_TOKEN(GlobalGet, "global.get") +WABT_TOKEN(GlobalSet, "global.set") +WABT_TOKEN(If, "if") +WABT_TOKEN(Load, "LOAD") +WABT_TOKEN(LocalGet, "local.get") +WABT_TOKEN(LocalSet, "local.set") +WABT_TOKEN(LocalTee, "local.tee") +WABT_TOKEN(Loop, "loop") +WABT_TOKEN(MemoryCopy, "memory.copy") +WABT_TOKEN(MemoryFill, "memory.fill") +WABT_TOKEN(MemoryGrow, "memory.grow") +WABT_TOKEN(MemoryInit, "memory.init") +WABT_TOKEN(MemorySize, "memory.size") +WABT_TOKEN(Nop, "nop") +WABT_TOKEN(RefExtern, "ref.extern") +WABT_TOKEN(RefFunc, "ref.func") +WABT_TOKEN(RefIsNull, "ref.is_null") +WABT_TOKEN(RefNull, "ref.null") +WABT_TOKEN(Rethrow, "rethrow") +WABT_TOKEN(ReturnCallIndirect, "return_call_indirect") +WABT_TOKEN(ReturnCall, "return_call") +WABT_TOKEN(Return, "return") +WABT_TOKEN(Select, "select") +WABT_TOKEN(SimdLaneOp, "SIMDLANEOP") +WABT_TOKEN(SimdLoadSplat, "SIMDLOADSPLAT") +WABT_TOKEN(SimdLoadLane, "SIMDLOADLANE") +WABT_TOKEN(SimdStoreLane, "SIMDSTORELANE") +WABT_TOKEN(SimdShuffleOp, "i8x16.shuffle") +WABT_TOKEN(Store, "STORE") +WABT_TOKEN(TableCopy, "table.copy") +WABT_TOKEN(TableFill, "table.full") +WABT_TOKEN(TableGet, "table.get") +WABT_TOKEN(TableGrow, "table.grow") +WABT_TOKEN(TableInit, "table.init") +WABT_TOKEN(TableSet, "table.set") +WABT_TOKEN(TableSize, "table.size") +WABT_TOKEN(Ternary, "TERNARY") +WABT_TOKEN(Throw, "throw") +WABT_TOKEN(Try, "try") +WABT_TOKEN(Unary, "UNARY") +WABT_TOKEN(Unreachable, "unreachable") +WABT_TOKEN_FIRST(Opcode, AtomicFence) +WABT_TOKEN_LAST(Opcode, Unreachable) + +/* Tokens with string data. */ +WABT_TOKEN(AlignEqNat, "align=") +WABT_TOKEN(LparAnn, "Annotation") +WABT_TOKEN(OffsetEqNat, "offset=") +WABT_TOKEN(Reserved, "Reserved") +WABT_TOKEN(Text, "TEXT") +WABT_TOKEN(Var, "VAR") +WABT_TOKEN_FIRST(String, AlignEqNat) +WABT_TOKEN_LAST(String, Var) + +/* Tokens with Type data. */ +WABT_TOKEN(ValueType, "VALUETYPE") +WABT_TOKEN_FIRST(Type, ValueType) +WABT_TOKEN_LAST(Type, ValueType) + +/* Tokens with Type data, but are reference kinds. */ +WABT_TOKEN(Func, "func") +WABT_TOKEN(Extern, "extern") +WABT_TOKEN(Exn, "exn") +WABT_TOKEN_FIRST(RefKind, Func) +WABT_TOKEN_LAST(RefKind, Exn) diff --git a/third_party/wasm2c/src/token.h b/third_party/wasm2c/src/token.h new file mode 100644 index 0000000000..719e1aee50 --- /dev/null +++ b/third_party/wasm2c/src/token.h @@ -0,0 +1,133 @@ +/* + * 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. + */ + +#ifndef WABT_TOKEN_H_ +#define WABT_TOKEN_H_ + +#include "src/literal.h" +#include "src/opcode.h" +#include "src/string-view.h" + +namespace wabt { + +struct Literal { + Literal() = default; + Literal(LiteralType type, string_view text) : type(type), text(text) {} + + LiteralType type; + string_view text; +}; + +enum class TokenType { +#define WABT_TOKEN(name, string) name, +#define WABT_TOKEN_FIRST(group, first) First_##group = first, +#define WABT_TOKEN_LAST(group, last) Last_##group = last, +#include "token.def" +#undef WABT_TOKEN +#undef WABT_TOKEN_FIRST +#undef WABT_TOKEN_LAST + + First = First_Bare, + Last = Last_RefKind, +}; + +const char* GetTokenTypeName(TokenType); + +inline bool IsTokenTypeBare(TokenType token_type) { + return token_type >= TokenType::First_Bare && + token_type <= TokenType::Last_Bare; +} + +inline bool IsTokenTypeString(TokenType token_type) { + return token_type >= TokenType::First_String && + token_type <= TokenType::Last_String; +} + +inline bool IsTokenTypeType(TokenType token_type) { + return token_type == TokenType::ValueType; +} + +inline bool IsTokenTypeOpcode(TokenType token_type) { + return token_type >= TokenType::First_Opcode && + token_type <= TokenType::Last_Opcode; +} + +inline bool IsTokenTypeLiteral(TokenType token_type) { + return token_type >= TokenType::First_Literal && + token_type <= TokenType::Last_Literal; +} + +inline bool IsTokenTypeRefKind(TokenType token_type) { + return token_type >= TokenType::First_RefKind && + token_type <= TokenType::Last_RefKind; +} + +struct Token { + Token() : token_type_(TokenType::Invalid) {} + Token(Location, TokenType); + Token(Location, TokenType, Type); + Token(Location, TokenType, string_view); + Token(Location, TokenType, Opcode); + Token(Location, TokenType, const Literal&); + + Location loc; + + TokenType token_type() const { return token_type_; } + + bool HasText() const { return IsTokenTypeString(token_type_); } + bool HasType() const { + return IsTokenTypeType(token_type_) || IsTokenTypeRefKind(token_type_); + } + bool HasOpcode() const { return IsTokenTypeOpcode(token_type_); } + bool HasLiteral() const { return IsTokenTypeLiteral(token_type_); } + + string_view text() const { + assert(HasText()); + return text_; + } + + Type type() const { + assert(HasType()); + return type_; + } + + Opcode opcode() const { + assert(HasOpcode()); + return opcode_; + } + + const Literal& literal() const { + assert(HasLiteral()); + return literal_; + } + + std::string to_string() const; + std::string to_string_clamp(size_t max_length) const; + + private: + TokenType token_type_; + + union { + string_view text_; + Type type_; + Opcode opcode_; + Literal literal_; + }; +}; + +} // namespace wabt + +#endif // WABT_TOKEN_H_ diff --git a/third_party/wasm2c/src/tools/spectest-interp.cc b/third_party/wasm2c/src/tools/spectest-interp.cc new file mode 100644 index 0000000000..c27e7a9f69 --- /dev/null +++ b/third_party/wasm2c/src/tools/spectest-interp.cc @@ -0,0 +1,1852 @@ +/* + * 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 <algorithm> +#include <cassert> +#include <cinttypes> +#include <cstdio> +#include <cstdlib> +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "src/binary-reader.h" +#include "src/cast.h" +#include "src/common.h" +#include "src/error-formatter.h" +#include "src/feature.h" +#include "src/interp/binary-reader-interp.h" +#include "src/interp/interp-util.h" +#include "src/interp/interp.h" +#include "src/literal.h" +#include "src/option-parser.h" +#include "src/stream.h" +#include "src/validator.h" +#include "src/wast-lexer.h" +#include "src/wast-parser.h" + +using namespace wabt; +using namespace wabt::interp; + +static int s_verbose; +static const char* s_infile; +static Thread::Options s_thread_options; +static Stream* s_trace_stream; +static Features s_features; + +static std::unique_ptr<FileStream> s_log_stream; +static std::unique_ptr<FileStream> s_stdout_stream; + +enum class RunVerbosity { + Quiet = 0, + Verbose = 1, +}; + +static const char s_description[] = + R"( read a Spectest JSON file, and run its tests in the interpreter. + +examples: + # parse test.json and run the spec tests + $ spectest-interp test.json +)"; + +static void ParseOptions(int argc, char** argv) { + OptionParser parser("spectest-interp", s_description); + + parser.AddOption('v', "verbose", "Use multiple times for more info", []() { + s_verbose++; + s_log_stream = FileStream::CreateStderr(); + }); + s_features.AddOptions(&parser); + parser.AddOption('V', "value-stack-size", "SIZE", + "Size in elements of the value stack", + [](const std::string& argument) { + // TODO(binji): validate. + s_thread_options.value_stack_size = atoi(argument.c_str()); + }); + parser.AddOption('C', "call-stack-size", "SIZE", + "Size in elements of the call stack", + [](const std::string& argument) { + // TODO(binji): validate. + s_thread_options.call_stack_size = atoi(argument.c_str()); + }); + parser.AddOption('t', "trace", "Trace execution", + []() { s_trace_stream = s_stdout_stream.get(); }); + + parser.AddArgument("filename", OptionParser::ArgumentCount::One, + [](const char* argument) { s_infile = argument; }); + parser.Parse(argc, argv); +} + +namespace spectest { + +class Command; +typedef std::unique_ptr<Command> CommandPtr; +typedef std::vector<CommandPtr> CommandPtrVector; + +class Script { + public: + std::string filename; + CommandPtrVector commands; +}; + +class Command { + public: + WABT_DISALLOW_COPY_AND_ASSIGN(Command); + Command() = delete; + virtual ~Command() = default; + + CommandType type; + uint32_t line = 0; + + protected: + explicit Command(CommandType type) : type(type) {} +}; + +template <CommandType TypeEnum> +class CommandMixin : public Command { + public: + static bool classof(const Command* cmd) { return cmd->type == TypeEnum; } + CommandMixin() : Command(TypeEnum) {} +}; + +enum class ModuleType { + Text, + Binary, +}; + +class ModuleCommand : public CommandMixin<CommandType::Module> { + public: + ModuleType module = ModuleType::Binary; + std::string filename; + std::string name; +}; + +class Action { + public: + ActionType type = ActionType::Invoke; + std::string module_name; + std::string field_name; + ValueTypes types; + Values args; +}; + +template <CommandType TypeEnum> +class ActionCommandBase : public CommandMixin<TypeEnum> { + public: + Action action; +}; + +typedef ActionCommandBase<CommandType::Action> ActionCommand; + +class RegisterCommand : public CommandMixin<CommandType::Register> { + public: + std::string as; + std::string name; +}; + +struct ExpectedValue { + TypedValue value; + Type lane_type; // Only valid if value.type == Type::V128. + // Up to 4 NaN values used, depending on |value.type| and |lane_type|: + // | type | lane_type | valid | + // | f32 | | nan[0] | + // | f64 | | nan[0] | + // | v128 | f32 | nan[0] through nan[3] | + // | v128 | f64 | nan[0],nan[1] | + // | * | * | none valid | + ExpectedNan nan[4]; +}; + +int LaneCountFromType(Type type) { + switch (type) { + case Type::I8: return 16; + case Type::I16: return 8; + case Type::I32: return 4; + case Type::I64: return 2; + case Type::F32: return 4; + case Type::F64: return 2; + default: assert(false); return 0; + } +} + +ExpectedValue GetLane(const ExpectedValue& ev, int lane) { + int lane_count = LaneCountFromType(ev.lane_type); + assert(ev.value.type == Type::V128); + assert(lane < lane_count); + + ExpectedValue result; + result.value.type = ev.lane_type; + + v128 vec = ev.value.value.Get<v128>(); + + for (int lane = 0; lane < lane_count; ++lane) { + switch (ev.lane_type) { + case Type::I8: + result.nan[0] = ExpectedNan::None; + result.value.value.Set<u32>(vec.u8(lane)); + break; + + case Type::I16: + result.nan[0] = ExpectedNan::None; + result.value.value.Set<u32>(vec.u16(lane)); + break; + + case Type::I32: + result.nan[0] = ExpectedNan::None; + result.value.value.Set<u32>(vec.u32(lane)); + break; + + case Type::I64: + result.nan[0] = ExpectedNan::None; + result.value.value.Set<u64>(vec.u64(lane)); + break; + + case Type::F32: + result.nan[0] = ev.nan[lane]; + result.value.value.Set<f32>(Bitcast<f32>(vec.f32_bits(lane))); + break; + + case Type::F64: + result.nan[0] = ev.nan[lane]; + result.value.value.Set<f64>(Bitcast<f64>(vec.f64_bits(lane))); + break; + + default: + WABT_UNREACHABLE; + } + } + return result; +} + +TypedValue GetLane(const TypedValue& tv, Type lane_type, int lane) { + int lane_count = LaneCountFromType(lane_type); + assert(tv.type == Type::V128); + assert(lane < lane_count); + + TypedValue result; + result.type = lane_type; + + v128 vec = tv.value.Get<v128>(); + + for (int lane = 0; lane < lane_count; ++lane) { + switch (lane_type) { + case Type::I8: + result.value.Set<u32>(vec.u8(lane)); + break; + + case Type::I16: + result.value.Set<u32>(vec.u16(lane)); + break; + + case Type::I32: + result.value.Set<u32>(vec.u32(lane)); + break; + + case Type::I64: + result.value.Set<u64>(vec.u64(lane)); + break; + + case Type::F32: + result.value.Set<f32>(Bitcast<f32>(vec.f32_bits(lane))); + break; + + case Type::F64: + result.value.Set<f64>(Bitcast<f64>(vec.f64_bits(lane))); + break; + + default: + WABT_UNREACHABLE; + } + } + return result; +} + +class AssertReturnCommand : public CommandMixin<CommandType::AssertReturn> { + public: + Action action; + std::vector<ExpectedValue> expected; +}; + +template <CommandType TypeEnum> +class AssertTrapCommandBase : public CommandMixin<TypeEnum> { + public: + Action action; + std::string text; +}; + +typedef AssertTrapCommandBase<CommandType::AssertTrap> AssertTrapCommand; +typedef AssertTrapCommandBase<CommandType::AssertExhaustion> + AssertExhaustionCommand; + +template <CommandType TypeEnum> +class AssertModuleCommand : public CommandMixin<TypeEnum> { + public: + ModuleType type = ModuleType::Binary; + std::string filename; + std::string text; +}; + +typedef AssertModuleCommand<CommandType::AssertMalformed> + AssertMalformedCommand; +typedef AssertModuleCommand<CommandType::AssertInvalid> AssertInvalidCommand; +typedef AssertModuleCommand<CommandType::AssertUnlinkable> + AssertUnlinkableCommand; +typedef AssertModuleCommand<CommandType::AssertUninstantiable> + AssertUninstantiableCommand; + +// An extremely simple JSON parser that only knows how to parse the expected +// format from wat2wasm. +class JSONParser { + public: + JSONParser() {} + + wabt::Result ReadFile(string_view spec_json_filename); + wabt::Result ParseScript(Script* out_script); + + private: + void WABT_PRINTF_FORMAT(2, 3) PrintError(const char* format, ...); + + // Whether to allow parsing of expectation-only forms (e.g. `nan:canonical`, + // `nan:arithmetic`, etc.) + enum class AllowExpected { No, Yes }; + + void PutbackChar(); + int ReadChar(); + void SkipWhitespace(); + bool Match(const char* s); + wabt::Result Expect(const char* s); + wabt::Result ExpectKey(const char* key); + wabt::Result ParseUint32(uint32_t* out_int); + wabt::Result ParseString(std::string* out_string); + wabt::Result ParseKeyStringValue(const char* key, std::string* out_string); + wabt::Result ParseOptNameStringValue(std::string* out_string); + wabt::Result ParseLine(uint32_t* out_line_number); + wabt::Result ParseType(Type* out_type); + wabt::Result ParseTypeObject(Type* out_type); + wabt::Result ParseTypeVector(TypeVector* out_types); + wabt::Result ParseConst(TypedValue* out_value); + wabt::Result ParseI32Value(uint32_t* out_value, string_view value_str); + wabt::Result ParseI64Value(uint64_t* out_value, string_view value_str); + wabt::Result ParseF32Value(uint32_t* out_value, + ExpectedNan* out_nan, + string_view value_str, + AllowExpected); + wabt::Result ParseF64Value(uint64_t* out_value, + ExpectedNan* out_nan, + string_view value_str, + AllowExpected); + wabt::Result ParseLaneConstValue(Type lane_type, + int lane, + ExpectedValue* out_value, + string_view value_str, + AllowExpected); + wabt::Result ParseConstValue(Type type, + Value* out_value, + ExpectedNan* out_nan, + string_view value_str, + AllowExpected); + wabt::Result ParseConstVector(ValueTypes* out_types, Values* out_values); + wabt::Result ParseExpectedValue(ExpectedValue* out_value, AllowExpected); + wabt::Result ParseExpectedValues(std::vector<ExpectedValue>* out_values); + wabt::Result ParseAction(Action* out_action); + wabt::Result ParseActionResult(); + wabt::Result ParseModuleType(ModuleType* out_type); + + std::string CreateModulePath(string_view filename); + wabt::Result ParseFilename(std::string* out_filename); + wabt::Result ParseCommand(CommandPtr* out_command); + + // Parsing info. + std::vector<uint8_t> json_data_; + size_t json_offset_ = 0; + Location loc_; + Location prev_loc_; + bool has_prev_loc_ = false; +}; + +#define EXPECT(x) CHECK_RESULT(Expect(x)) +#define EXPECT_KEY(x) CHECK_RESULT(ExpectKey(x)) +#define PARSE_KEY_STRING_VALUE(key, value) \ + CHECK_RESULT(ParseKeyStringValue(key, value)) + +wabt::Result JSONParser::ReadFile(string_view spec_json_filename) { + loc_.filename = spec_json_filename; + loc_.line = 1; + loc_.first_column = 1; + + return wabt::ReadFile(spec_json_filename, &json_data_); +} + +void JSONParser::PrintError(const char* format, ...) { + WABT_SNPRINTF_ALLOCA(buffer, length, format); + fprintf(stderr, "%s:%d:%d: %s\n", loc_.filename.to_string().c_str(), + loc_.line, loc_.first_column, buffer); +} + +void JSONParser::PutbackChar() { + assert(has_prev_loc_); + json_offset_--; + loc_ = prev_loc_; + has_prev_loc_ = false; +} + +int JSONParser::ReadChar() { + if (json_offset_ >= json_data_.size()) { + return -1; + } + prev_loc_ = loc_; + char c = json_data_[json_offset_++]; + if (c == '\n') { + loc_.line++; + loc_.first_column = 1; + } else { + loc_.first_column++; + } + has_prev_loc_ = true; + return c; +} + +void JSONParser::SkipWhitespace() { + while (1) { + switch (ReadChar()) { + case -1: + return; + + case ' ': + case '\t': + case '\n': + case '\r': + break; + + default: + PutbackChar(); + return; + } + } +} + +bool JSONParser::Match(const char* s) { + SkipWhitespace(); + Location start_loc = loc_; + size_t start_offset = json_offset_; + while (*s && *s == ReadChar()) + s++; + + if (*s == 0) { + return true; + } else { + json_offset_ = start_offset; + loc_ = start_loc; + return false; + } +} + +wabt::Result JSONParser::Expect(const char* s) { + if (Match(s)) { + return wabt::Result::Ok; + } else { + PrintError("expected %s", s); + return wabt::Result::Error; + } +} + +wabt::Result JSONParser::ExpectKey(const char* key) { + size_t keylen = strlen(key); + size_t quoted_len = keylen + 2 + 1; + char* quoted = static_cast<char*>(alloca(quoted_len)); + snprintf(quoted, quoted_len, "\"%s\"", key); + EXPECT(quoted); + EXPECT(":"); + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseUint32(uint32_t* out_int) { + uint32_t result = 0; + SkipWhitespace(); + while (1) { + int c = ReadChar(); + if (c >= '0' && c <= '9') { + uint32_t last_result = result; + result = result * 10 + static_cast<uint32_t>(c - '0'); + if (result < last_result) { + PrintError("uint32 overflow"); + return wabt::Result::Error; + } + } else { + PutbackChar(); + break; + } + } + *out_int = result; + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseString(std::string* out_string) { + out_string->clear(); + + SkipWhitespace(); + if (ReadChar() != '"') { + PrintError("expected string"); + return wabt::Result::Error; + } + + while (1) { + int c = ReadChar(); + if (c == '"') { + break; + } else if (c == '\\') { + /* The only escape supported is \uxxxx. */ + c = ReadChar(); + if (c != 'u') { + PrintError("expected escape: \\uxxxx"); + return wabt::Result::Error; + } + uint16_t code = 0; + for (int i = 0; i < 4; ++i) { + c = ReadChar(); + int cval; + if (c >= '0' && c <= '9') { + cval = c - '0'; + } else if (c >= 'a' && c <= 'f') { + cval = c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + cval = c - 'A' + 10; + } else { + PrintError("expected hex char"); + return wabt::Result::Error; + } + code = (code << 4) + cval; + } + + if (code < 256) { + *out_string += code; + } else { + PrintError("only escape codes < 256 allowed, got %u\n", code); + } + } else { + *out_string += c; + } + } + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseKeyStringValue(const char* key, + std::string* out_string) { + out_string->clear(); + EXPECT_KEY(key); + return ParseString(out_string); +} + +wabt::Result JSONParser::ParseOptNameStringValue(std::string* out_string) { + out_string->clear(); + if (Match("\"name\"")) { + EXPECT(":"); + CHECK_RESULT(ParseString(out_string)); + EXPECT(","); + } + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseLine(uint32_t* out_line_number) { + EXPECT_KEY("line"); + CHECK_RESULT(ParseUint32(out_line_number)); + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseType(Type* out_type) { + std::string type_str; + CHECK_RESULT(ParseString(&type_str)); + + if (type_str == "i32") { + *out_type = Type::I32; + } else if (type_str == "f32") { + *out_type = Type::F32; + } else if (type_str == "i64") { + *out_type = Type::I64; + } else if (type_str == "f64") { + *out_type = Type::F64; + } else if (type_str == "v128") { + *out_type = Type::V128; + } else if (type_str == "i8") { + *out_type = Type::I8; + } else if (type_str == "i16") { + *out_type = Type::I16; + } else if (type_str == "funcref") { + *out_type = Type::FuncRef; + } else if (type_str == "externref") { + *out_type = Type::ExternRef; + } else { + PrintError("unknown type: \"%s\"", type_str.c_str()); + return wabt::Result::Error; + } + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseTypeObject(Type* out_type) { + EXPECT("{"); + EXPECT_KEY("type"); + CHECK_RESULT(ParseType(out_type)); + EXPECT("}"); + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseTypeVector(TypeVector* out_types) { + out_types->clear(); + EXPECT("["); + bool first = true; + while (!Match("]")) { + if (!first) { + EXPECT(","); + } + Type type; + CHECK_RESULT(ParseTypeObject(&type)); + first = false; + out_types->push_back(type); + } + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseConst(TypedValue* out_value) { + ExpectedValue expected; + CHECK_RESULT(ParseExpectedValue(&expected, AllowExpected::No)); + *out_value = expected.value; + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseI32Value(uint32_t* out_value, + string_view value_str) { + if (Failed(ParseInt32(value_str.begin(), value_str.end(), out_value, + ParseIntType::UnsignedOnly))) { + PrintError("invalid i32 literal"); + return wabt::Result::Error; + } + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseI64Value(uint64_t* out_value, + string_view value_str) { + if (Failed(ParseInt64(value_str.begin(), value_str.end(), out_value, + ParseIntType::UnsignedOnly))) { + PrintError("invalid i64 literal"); + return wabt::Result::Error; + } + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseF32Value(uint32_t* out_value, + ExpectedNan* out_nan, + string_view value_str, + AllowExpected allow_expected) { + if (allow_expected == AllowExpected::Yes) { + *out_value = 0; + if (value_str == "nan:canonical") { + *out_nan = ExpectedNan::Canonical; + return wabt::Result::Ok; + } else if (value_str == "nan:arithmetic") { + *out_nan = ExpectedNan::Arithmetic; + return wabt::Result::Ok; + } + } + + *out_nan = ExpectedNan::None; + if (Failed(ParseInt32(value_str.begin(), value_str.end(), out_value, + ParseIntType::UnsignedOnly))) { + PrintError("invalid f32 literal"); + return wabt::Result::Error; + } + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseF64Value(uint64_t* out_value, + ExpectedNan* out_nan, + string_view value_str, + AllowExpected allow_expected) { + if (allow_expected == AllowExpected::Yes) { + *out_value = 0; + if (value_str == "nan:canonical") { + *out_nan = ExpectedNan::Canonical; + return wabt::Result::Ok; + } else if (value_str == "nan:arithmetic") { + *out_nan = ExpectedNan::Arithmetic; + return wabt::Result::Ok; + } + } + + *out_nan = ExpectedNan::None; + if (Failed(ParseInt64(value_str.begin(), value_str.end(), out_value, + ParseIntType::UnsignedOnly))) { + PrintError("invalid f64 literal"); + return wabt::Result::Error; + } + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseLaneConstValue(Type lane_type, + int lane, + ExpectedValue* out_value, + string_view value_str, + AllowExpected allow_expected) { + v128 v = out_value->value.value.Get<v128>(); + + switch (lane_type) { + case Type::I8: { + uint32_t value; + CHECK_RESULT(ParseI32Value(&value, value_str)); + v.set_u8(lane, value); + break; + } + + case Type::I16: { + uint32_t value; + CHECK_RESULT(ParseI32Value(&value, value_str)); + v.set_u16(lane, value); + break; + } + + case Type::I32: { + uint32_t value; + CHECK_RESULT(ParseI32Value(&value, value_str)); + v.set_u32(lane, value); + break; + } + + case Type::I64: { + uint64_t value; + CHECK_RESULT(ParseI64Value(&value, value_str)); + v.set_u64(lane, value); + break; + } + + case Type::F32: { + ExpectedNan nan; + uint32_t value_bits; + CHECK_RESULT(ParseF32Value(&value_bits, &nan, value_str, allow_expected)); + v.set_f32_bits(lane, value_bits); + assert(lane < 4); + out_value->nan[lane] = nan; + break; + } + + case Type::F64: { + ExpectedNan nan; + uint64_t value_bits; + CHECK_RESULT(ParseF64Value(&value_bits, &nan, value_str, allow_expected)); + v.set_f64_bits(lane, value_bits); + assert(lane < 2); + out_value->nan[lane] = nan; + break; + } + + default: + PrintError("unknown concrete type: \"%s\"", lane_type.GetName()); + return wabt::Result::Error; + } + + out_value->value.value.Set<v128>(v); + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseConstValue(Type type, + Value* out_value, + ExpectedNan* out_nan, + string_view value_str, + AllowExpected allow_expected) { + *out_nan = ExpectedNan::None; + + switch (type) { + case Type::I32: { + uint32_t value; + CHECK_RESULT(ParseI32Value(&value, value_str)); + out_value->Set(value); + break; + } + + case Type::F32: { + uint32_t value_bits; + CHECK_RESULT( + ParseF32Value(&value_bits, out_nan, value_str, allow_expected)); + out_value->Set(Bitcast<f32>(value_bits)); + break; + } + + case Type::I64: { + uint64_t value; + CHECK_RESULT(ParseI64Value(&value, value_str)); + out_value->Set(value); + break; + } + + case Type::F64: { + uint64_t value_bits; + CHECK_RESULT( + ParseF64Value(&value_bits, out_nan, value_str, allow_expected)); + out_value->Set(Bitcast<f64>(value_bits)); + break; + } + + case Type::V128: + assert(false); // Should use ParseLaneConstValue instead. + break; + + case Type::FuncRef: + if (value_str == "null") { + out_value->Set(Ref::Null); + } else { + assert(allow_expected == AllowExpected::Yes); + out_value->Set(Ref{1}); + } + break; + + case Type::ExternRef: + if (value_str == "null") { + out_value->Set(Ref::Null); + } else { + uint32_t value; + CHECK_RESULT(ParseI32Value(&value, value_str)); + // TODO: hack, just whatever ref is at this index; but skip null (which + // is always 0). + out_value->Set(Ref{value + 1}); + } + break; + + default: + PrintError("unknown concrete type: \"%s\"", type.GetName()); + return wabt::Result::Error; + } + + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseExpectedValue(ExpectedValue* out_value, + AllowExpected allow_expected) { + Type type; + std::string value_str; + EXPECT("{"); + EXPECT_KEY("type"); + CHECK_RESULT(ParseType(&type)); + EXPECT(","); + if (type == Type::V128) { + Type lane_type; + EXPECT_KEY("lane_type"); + CHECK_RESULT(ParseType(&lane_type)); + EXPECT(","); + EXPECT_KEY("value"); + EXPECT("["); + + int lane_count = LaneCountFromType(lane_type); + for (int lane = 0; lane < lane_count; ++lane) { + CHECK_RESULT(ParseString(&value_str)); + CHECK_RESULT(ParseLaneConstValue(lane_type, lane, out_value, value_str, + allow_expected)); + if (lane < lane_count - 1) { + EXPECT(","); + } + } + EXPECT("]"); + out_value->value.type = type; + out_value->lane_type = lane_type; + } else { + PARSE_KEY_STRING_VALUE("value", &value_str); + CHECK_RESULT(ParseConstValue(type, &out_value->value.value, + &out_value->nan[0], value_str, + allow_expected)); + out_value->value.type = type; + } + EXPECT("}"); + + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseExpectedValues( + std::vector<ExpectedValue>* out_values) { + out_values->clear(); + EXPECT("["); + bool first = true; + while (!Match("]")) { + if (!first) { + EXPECT(","); + } + ExpectedValue value; + CHECK_RESULT(ParseExpectedValue(&value, AllowExpected::Yes)); + out_values->push_back(value); + first = false; + } + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseConstVector(ValueTypes* out_types, Values* out_values) { + out_values->clear(); + EXPECT("["); + bool first = true; + while (!Match("]")) { + if (!first) { + EXPECT(","); + } + TypedValue tv; + CHECK_RESULT(ParseConst(&tv)); + out_types->push_back(tv.type); + out_values->push_back(tv.value); + first = false; + } + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseAction(Action* out_action) { + EXPECT_KEY("action"); + EXPECT("{"); + EXPECT_KEY("type"); + if (Match("\"invoke\"")) { + out_action->type = ActionType::Invoke; + } else { + EXPECT("\"get\""); + out_action->type = ActionType::Get; + } + EXPECT(","); + if (Match("\"module\"")) { + EXPECT(":"); + CHECK_RESULT(ParseString(&out_action->module_name)); + EXPECT(","); + } + PARSE_KEY_STRING_VALUE("field", &out_action->field_name); + if (out_action->type == ActionType::Invoke) { + EXPECT(","); + EXPECT_KEY("args"); + CHECK_RESULT(ParseConstVector(&out_action->types, &out_action->args)); + } + EXPECT("}"); + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseActionResult() { + // Not needed for wabt-interp, but useful for other parsers. + EXPECT_KEY("expected"); + TypeVector expected; + CHECK_RESULT(ParseTypeVector(&expected)); + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseModuleType(ModuleType* out_type) { + std::string module_type_str; + + PARSE_KEY_STRING_VALUE("module_type", &module_type_str); + if (module_type_str == "text") { + *out_type = ModuleType::Text; + return wabt::Result::Ok; + } else if (module_type_str == "binary") { + *out_type = ModuleType::Binary; + return wabt::Result::Ok; + } else { + PrintError("unknown module type: \"%s\"", module_type_str.c_str()); + return wabt::Result::Error; + } +} + +static string_view GetDirname(string_view path) { + // Strip everything after and including the last slash (or backslash), e.g.: + // + // s = "foo/bar/baz", => "foo/bar" + // s = "/usr/local/include/stdio.h", => "/usr/local/include" + // s = "foo.bar", => "" + // s = "some\windows\directory", => "some\windows" + size_t last_slash = path.find_last_of('/'); + size_t last_backslash = path.find_last_of('\\'); + if (last_slash == string_view::npos) { + last_slash = 0; + } + if (last_backslash == string_view::npos) { + last_backslash = 0; + } + + return path.substr(0, std::max(last_slash, last_backslash)); +} + +std::string JSONParser::CreateModulePath(string_view filename) { + string_view spec_json_filename = loc_.filename; + string_view dirname = GetDirname(spec_json_filename); + std::string path; + + if (dirname.size() == 0) { + path = filename.to_string(); + } else { + path = dirname.to_string(); + path += '/'; + path += filename.to_string(); + } + + ConvertBackslashToSlash(&path); + return path; +} + +wabt::Result JSONParser::ParseFilename(std::string* out_filename) { + PARSE_KEY_STRING_VALUE("filename", out_filename); + *out_filename = CreateModulePath(*out_filename); + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseCommand(CommandPtr* out_command) { + EXPECT("{"); + EXPECT_KEY("type"); + if (Match("\"module\"")) { + auto command = MakeUnique<ModuleCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseOptNameStringValue(&command->name)); + CHECK_RESULT(ParseFilename(&command->filename)); + *out_command = std::move(command); + } else if (Match("\"action\"")) { + auto command = MakeUnique<ActionCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseAction(&command->action)); + EXPECT(","); + CHECK_RESULT(ParseActionResult()); + *out_command = std::move(command); + } else if (Match("\"register\"")) { + auto command = MakeUnique<RegisterCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseOptNameStringValue(&command->name)); + PARSE_KEY_STRING_VALUE("as", &command->as); + *out_command = std::move(command); + } else if (Match("\"assert_malformed\"")) { + auto command = MakeUnique<AssertMalformedCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseFilename(&command->filename)); + EXPECT(","); + PARSE_KEY_STRING_VALUE("text", &command->text); + EXPECT(","); + CHECK_RESULT(ParseModuleType(&command->type)); + *out_command = std::move(command); + } else if (Match("\"assert_invalid\"")) { + auto command = MakeUnique<AssertInvalidCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseFilename(&command->filename)); + EXPECT(","); + PARSE_KEY_STRING_VALUE("text", &command->text); + EXPECT(","); + CHECK_RESULT(ParseModuleType(&command->type)); + *out_command = std::move(command); + } else if (Match("\"assert_unlinkable\"")) { + auto command = MakeUnique<AssertUnlinkableCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseFilename(&command->filename)); + EXPECT(","); + PARSE_KEY_STRING_VALUE("text", &command->text); + EXPECT(","); + CHECK_RESULT(ParseModuleType(&command->type)); + *out_command = std::move(command); + } else if (Match("\"assert_uninstantiable\"")) { + auto command = MakeUnique<AssertUninstantiableCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseFilename(&command->filename)); + EXPECT(","); + PARSE_KEY_STRING_VALUE("text", &command->text); + EXPECT(","); + CHECK_RESULT(ParseModuleType(&command->type)); + *out_command = std::move(command); + } else if (Match("\"assert_return\"")) { + auto command = MakeUnique<AssertReturnCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseAction(&command->action)); + EXPECT(","); + EXPECT_KEY("expected"); + CHECK_RESULT(ParseExpectedValues(&command->expected)); + *out_command = std::move(command); + } else if (Match("\"assert_trap\"")) { + auto command = MakeUnique<AssertTrapCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseAction(&command->action)); + EXPECT(","); + PARSE_KEY_STRING_VALUE("text", &command->text); + EXPECT(","); + CHECK_RESULT(ParseActionResult()); + *out_command = std::move(command); + } else if (Match("\"assert_exhaustion\"")) { + auto command = MakeUnique<AssertExhaustionCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseAction(&command->action)); + EXPECT(","); + PARSE_KEY_STRING_VALUE("text", &command->text); + EXPECT(","); + CHECK_RESULT(ParseActionResult()); + *out_command = std::move(command); + } else { + PrintError("unknown command type"); + return wabt::Result::Error; + } + EXPECT("}"); + return wabt::Result::Ok; +} + +wabt::Result JSONParser::ParseScript(Script* out_script) { + EXPECT("{"); + PARSE_KEY_STRING_VALUE("source_filename", &out_script->filename); + EXPECT(","); + EXPECT_KEY("commands"); + EXPECT("["); + bool first = true; + while (!Match("]")) { + CommandPtr command; + if (!first) { + EXPECT(","); + } + CHECK_RESULT(ParseCommand(&command)); + out_script->commands.push_back(std::move(command)); + first = false; + } + EXPECT("}"); + return wabt::Result::Ok; +} + +struct ActionResult { + ValueTypes types; + Values values; + Trap::Ptr trap; +}; + +class CommandRunner { + public: + CommandRunner(); + wabt::Result Run(const Script& script); + + int passed() const { return passed_; } + int total() const { return total_; } + + private: + using ExportMap = std::map<std::string, Extern::Ptr>; + using Registry = std::map<std::string, ExportMap>; + + void WABT_PRINTF_FORMAT(3, 4) + PrintError(uint32_t line_number, const char* format, ...); + ActionResult RunAction(int line_number, + const Action* action, + RunVerbosity verbose); + + interp::Module::Ptr ReadModule(string_view module_filename, Errors* errors); + Extern::Ptr GetImport(const std::string&, const std::string&); + void PopulateImports(const interp::Module::Ptr&, RefVec*); + void PopulateExports(const Instance::Ptr&, ExportMap*); + + wabt::Result OnModuleCommand(const ModuleCommand*); + wabt::Result OnActionCommand(const ActionCommand*); + wabt::Result OnRegisterCommand(const RegisterCommand*); + wabt::Result OnAssertMalformedCommand(const AssertMalformedCommand*); + wabt::Result OnAssertUnlinkableCommand(const AssertUnlinkableCommand*); + wabt::Result OnAssertInvalidCommand(const AssertInvalidCommand*); + wabt::Result OnAssertUninstantiableCommand( + const AssertUninstantiableCommand*); + wabt::Result OnAssertReturnCommand(const AssertReturnCommand*); + wabt::Result OnAssertTrapCommand(const AssertTrapCommand*); + wabt::Result OnAssertExhaustionCommand(const AssertExhaustionCommand*); + + wabt::Result CheckAssertReturnResult(const AssertReturnCommand* command, + int index, + ExpectedValue expected, + TypedValue actual, + bool print_error); + + void TallyCommand(wabt::Result); + + wabt::Result ReadInvalidTextModule(string_view module_filename, + const std::string& header); + wabt::Result ReadInvalidModule(int line_number, + string_view module_filename, + ModuleType module_type, + const char* desc); + wabt::Result ReadUnlinkableModule(int line_number, + string_view module_filename, + ModuleType module_type, + const char* desc); + + Store store_; + Registry registry_; // Used when importing. + Registry instances_; // Used when referencing module by name in invoke. + ExportMap last_instance_; + int passed_ = 0; + int total_ = 0; + + std::string source_filename_; +}; + +CommandRunner::CommandRunner() : store_(s_features) { + auto&& spectest = registry_["spectest"]; + + // Initialize print functions for the spec test. + struct { + const char* name; + interp::FuncType type; + } const print_funcs[] = { + {"print", interp::FuncType{{}, {}}}, + {"print_i32", interp::FuncType{{ValueType::I32}, {}}}, + {"print_f32", interp::FuncType{{ValueType::F32}, {}}}, + {"print_f64", interp::FuncType{{ValueType::F64}, {}}}, + {"print_i32_f32", interp::FuncType{{ValueType::I32, ValueType::F32}, {}}}, + {"print_f64_f64", interp::FuncType{{ValueType::F64, ValueType::F64}, {}}}, + }; + + for (auto&& print : print_funcs) { + auto import_name = StringPrintf("spectest.%s", print.name); + spectest[print.name] = HostFunc::New( + store_, print.type, + [=](Thread& inst, const Values& params, Values& results, + Trap::Ptr* trap) -> wabt::Result { + printf("called host "); + WriteCall(s_stdout_stream.get(), import_name, print.type, params, + results, *trap); + return wabt::Result::Ok; + }); + } + + spectest["table"] = + interp::Table::New(store_, TableType{ValueType::FuncRef, Limits{10, 20}}); + + spectest["memory"] = interp::Memory::New(store_, MemoryType{Limits{1, 2}}); + + spectest["global_i32"] = interp::Global::New( + store_, GlobalType{ValueType::I32, Mutability::Const}, Value::Make(u32{666})); + spectest["global_i64"] = interp::Global::New( + store_, GlobalType{ValueType::I64, Mutability::Const}, Value::Make(u64{666})); + spectest["global_f32"] = interp::Global::New( + store_, GlobalType{ValueType::F32, Mutability::Const}, Value::Make(f32{666})); + spectest["global_f64"] = interp::Global::New( + store_, GlobalType{ValueType::F64, Mutability::Const}, Value::Make(f64{666})); +} + +wabt::Result CommandRunner::Run(const Script& script) { + source_filename_ = script.filename; + + for (const CommandPtr& command : script.commands) { + switch (command->type) { + case CommandType::Module: + OnModuleCommand(cast<ModuleCommand>(command.get())); + break; + + case CommandType::Action: + TallyCommand(OnActionCommand(cast<ActionCommand>(command.get()))); + break; + + case CommandType::Register: + OnRegisterCommand(cast<RegisterCommand>(command.get())); + break; + + case CommandType::AssertMalformed: + TallyCommand(OnAssertMalformedCommand( + cast<AssertMalformedCommand>(command.get()))); + break; + + case CommandType::AssertInvalid: + TallyCommand( + OnAssertInvalidCommand(cast<AssertInvalidCommand>(command.get()))); + break; + + case CommandType::AssertUnlinkable: + TallyCommand(OnAssertUnlinkableCommand( + cast<AssertUnlinkableCommand>(command.get()))); + break; + + case CommandType::AssertUninstantiable: + TallyCommand(OnAssertUninstantiableCommand( + cast<AssertUninstantiableCommand>(command.get()))); + break; + + case CommandType::AssertReturn: + TallyCommand( + OnAssertReturnCommand(cast<AssertReturnCommand>(command.get()))); + break; + + case CommandType::AssertTrap: + TallyCommand( + OnAssertTrapCommand(cast<AssertTrapCommand>(command.get()))); + break; + + case CommandType::AssertExhaustion: + TallyCommand(OnAssertExhaustionCommand( + cast<AssertExhaustionCommand>(command.get()))); + break; + } + } + + return wabt::Result::Ok; +} + +void CommandRunner::PrintError(uint32_t line_number, const char* format, ...) { + WABT_SNPRINTF_ALLOCA(buffer, length, format); + printf("%s:%u: %s\n", source_filename_.c_str(), line_number, buffer); +} + +ActionResult CommandRunner::RunAction(int line_number, + const Action* action, + RunVerbosity verbose) { + ExportMap& module = !action->module_name.empty() + ? instances_[action->module_name] + : last_instance_; + Extern::Ptr extern_ = module[action->field_name]; + if (!extern_) { + PrintError(line_number, "unknown invoke \"%s.%s\"", + action->module_name.c_str(), action->field_name.c_str()); + return {}; + } + + ActionResult result; + + switch (action->type) { + case ActionType::Invoke: { + auto* func = cast<interp::Func>(extern_.get()); + func->Call(store_, action->args, result.values, &result.trap, + s_trace_stream); + result.types = func->type().results; + if (verbose == RunVerbosity::Verbose) { + WriteCall(s_stdout_stream.get(), action->field_name, func->type(), + action->args, result.values, result.trap); + } + break; + } + + case ActionType::Get: { + auto* global = cast<interp::Global>(extern_.get()); + result.values.push_back(global->Get()); + result.types.push_back(global->type().type); + break; + } + + default: + WABT_UNREACHABLE; + } + + return result; +} + +wabt::Result CommandRunner::ReadInvalidTextModule(string_view module_filename, + const std::string& header) { + std::vector<uint8_t> file_data; + wabt::Result result = ReadFile(module_filename, &file_data); + std::unique_ptr<WastLexer> lexer = WastLexer::CreateBufferLexer( + module_filename, file_data.data(), file_data.size()); + Errors errors; + if (Succeeded(result)) { + std::unique_ptr<wabt::Module> module; + WastParseOptions options(s_features); + result = ParseWatModule(lexer.get(), &module, &errors, &options); + } + + auto line_finder = lexer->MakeLineFinder(); + FormatErrorsToFile(errors, Location::Type::Text, line_finder.get(), stdout, + header, PrintHeader::Once); + return result; +} + +interp::Module::Ptr CommandRunner::ReadModule(string_view module_filename, + Errors* errors) { + std::vector<uint8_t> file_data; + + if (Failed(ReadFile(module_filename, &file_data))) { + return {}; + } + + const bool kReadDebugNames = true; + const bool kStopOnFirstError = true; + const bool kFailOnCustomSectionError = true; + ReadBinaryOptions options(s_features, s_log_stream.get(), kReadDebugNames, + kStopOnFirstError, kFailOnCustomSectionError); + ModuleDesc module_desc; + if (Failed(ReadBinaryInterp(file_data.data(), file_data.size(), options, + errors, &module_desc))) { + return {}; + } + + if (s_verbose) { + module_desc.istream.Disassemble(s_stdout_stream.get()); + } + + return interp::Module::New(store_, module_desc); +} + +wabt::Result CommandRunner::ReadInvalidModule(int line_number, + string_view module_filename, + ModuleType module_type, + const char* desc) { + std::string header = StringPrintf( + "%s:%d: %s passed", source_filename_.c_str(), line_number, desc); + + switch (module_type) { + case ModuleType::Text: { + return ReadInvalidTextModule(module_filename, header); + } + + case ModuleType::Binary: { + Errors errors; + auto module = ReadModule(module_filename, &errors); + if (!module) { + FormatErrorsToFile(errors, Location::Type::Binary, {}, stdout, header, + PrintHeader::Once); + return wabt::Result::Error; + } else { + return wabt::Result::Ok; + } + } + } + + WABT_UNREACHABLE; +} + +Extern::Ptr CommandRunner::GetImport(const std::string& module, + const std::string& name) { + auto mod_iter = registry_.find(module); + if (mod_iter != registry_.end()) { + auto extern_iter = mod_iter->second.find(name); + if (extern_iter != mod_iter->second.end()) { + return extern_iter->second; + } + } + return {}; +} + +void CommandRunner::PopulateImports(const interp::Module::Ptr& module, + RefVec* imports) { + for (auto&& import : module->desc().imports) { + auto extern_ = GetImport(import.type.module, import.type.name); + imports->push_back(extern_ ? extern_.ref() : Ref::Null); + } +} + +void CommandRunner::PopulateExports(const Instance::Ptr& instance, + ExportMap* map) { + map->clear(); + interp::Module::Ptr module{store_, instance->module()}; + for (size_t i = 0; i < module->export_types().size(); ++i) { + const ExportType& export_type = module->export_types()[i]; + (*map)[export_type.name] = store_.UnsafeGet<Extern>(instance->exports()[i]); + } +} + +wabt::Result CommandRunner::OnModuleCommand(const ModuleCommand* command) { + Errors errors; + auto module = ReadModule(command->filename, &errors); + FormatErrorsToFile(errors, Location::Type::Binary); + + if (!module) { + PrintError(command->line, "error reading module: \"%s\"", + command->filename.c_str()); + return wabt::Result::Error; + } + + RefVec imports; + PopulateImports(module, &imports); + + Trap::Ptr trap; + auto instance = Instance::Instantiate(store_, module.ref(), imports, &trap); + if (trap) { + assert(!instance); + PrintError(command->line, "error instantiating module: \"%s\"", + trap->message().c_str()); + return wabt::Result::Error; + } + + PopulateExports(instance, &last_instance_); + if (!command->name.empty()) { + instances_[command->name] = last_instance_; + } + + return wabt::Result::Ok; +} + +wabt::Result CommandRunner::OnActionCommand(const ActionCommand* command) { + ActionResult result = + RunAction(command->line, &command->action, RunVerbosity::Verbose); + + if (result.trap) { + PrintError(command->line, "unexpected trap: %s", + result.trap->message().c_str()); + return wabt::Result::Error; + } + + return wabt::Result::Ok; +} + +wabt::Result CommandRunner::OnAssertMalformedCommand( + const AssertMalformedCommand* command) { + wabt::Result result = ReadInvalidModule(command->line, command->filename, + command->type, "assert_malformed"); + if (Succeeded(result)) { + PrintError(command->line, "expected module to be malformed: \"%s\"", + command->filename.c_str()); + return wabt::Result::Error; + } + + return wabt::Result::Ok; +} + +wabt::Result CommandRunner::OnRegisterCommand(const RegisterCommand* command) { + if (!command->name.empty()) { + auto instance_iter = instances_.find(command->name); + if (instance_iter == instances_.end()) { + PrintError(command->line, "unknown module in register"); + return wabt::Result::Error; + } + registry_[command->as] = instance_iter->second; + } else { + registry_[command->as] = last_instance_; + } + + return wabt::Result::Ok; +} + +wabt::Result CommandRunner::OnAssertUnlinkableCommand( + const AssertUnlinkableCommand* command) { + Errors errors; + auto module = ReadModule(command->filename, &errors); + + if (!module) { + PrintError(command->line, "unable to compile unlinkable module: \"%s\"", + command->filename.c_str()); + return wabt::Result::Error; + } + + RefVec imports; + PopulateImports(module, &imports); + + Trap::Ptr trap; + auto instance = Instance::Instantiate(store_, module.ref(), imports, &trap); + if (!trap) { + PrintError(command->line, "expected module to be unlinkable: \"%s\"", + command->filename.c_str()); + return wabt::Result::Error; + } + + // TODO: Change to one-line error. + PrintError(command->line, "assert_unlinkable passed:\n error: %s", + trap->message().c_str()); + return wabt::Result::Ok; +} + +wabt::Result CommandRunner::OnAssertInvalidCommand( + const AssertInvalidCommand* command) { + wabt::Result result = ReadInvalidModule(command->line, command->filename, + command->type, "assert_invalid"); + if (Succeeded(result)) { + PrintError(command->line, "expected module to be invalid: \"%s\"", + command->filename.c_str()); + return wabt::Result::Error; + } + + return wabt::Result::Ok; +} + +wabt::Result CommandRunner::OnAssertUninstantiableCommand( + const AssertUninstantiableCommand* command) { + Errors errors; + auto module = ReadModule(command->filename, &errors); + + if (!module) { + PrintError(command->line, "unable to compile uninstantiable module: \"%s\"", + command->filename.c_str()); + return wabt::Result::Error; + } + + RefVec imports; + PopulateImports(module, &imports); + + Trap::Ptr trap; + auto instance = Instance::Instantiate(store_, module.ref(), imports, &trap); + if (!trap) { + PrintError(command->line, "expected module to be uninstantiable: \"%s\"", + command->filename.c_str()); + return wabt::Result::Error; + } + + // TODO: print error when assertion passes. +#if 0 + PrintError(command->line, "assert_uninstantiable passed: %s", + trap->message().c_str()); +#endif + return wabt::Result::Ok; +} + +static bool WABT_VECTORCALL IsCanonicalNan(f32 val) { + const u32 kQuietNan = 0x7fc00000U; + const u32 kQuietNegNan = 0xffc00000U; + u32 bits = Bitcast<u32>(val); + return bits == kQuietNan || bits == kQuietNegNan; +} + +static bool WABT_VECTORCALL IsCanonicalNan(f64 val) { + const u64 kQuietNan = 0x7ff8000000000000ULL; + const u64 kQuietNegNan = 0xfff8000000000000ULL; + u64 bits = Bitcast<u64>(val); + return bits == kQuietNan || bits == kQuietNegNan; +} + +static bool WABT_VECTORCALL IsArithmeticNan(f32 val) { + const u32 kQuietNan = 0x7fc00000U; + return (Bitcast<u32>(val) & kQuietNan) == kQuietNan; +} + +static bool WABT_VECTORCALL IsArithmeticNan(f64 val) { + const u64 kQuietNan = 0x7ff8000000000000ULL; + return (Bitcast<u64>(val) & kQuietNan) == kQuietNan; +} + +static std::string ExpectedValueToString(const ExpectedValue& ev) { + // Extend TypedValueToString to print expected nan values too. + switch (ev.value.type) { + case Type::F32: + case Type::F64: + switch (ev.nan[0]) { + case ExpectedNan::None: + return TypedValueToString(ev.value); + + case ExpectedNan::Arithmetic: + return StringPrintf("%s:nan:arithmetic", ev.value.type.GetName()); + + case ExpectedNan::Canonical: + return StringPrintf("%s:nan:canonical", ev.value.type.GetName()); + } + break; + + case Type::V128: { + int lane_count = LaneCountFromType(ev.lane_type); + std::string result = "v128 "; + for (int lane = 0; lane < lane_count; ++lane) { + result += ExpectedValueToString(GetLane(ev, lane)); + } + return result; + } + + default: + break; + } + return TypedValueToString(ev.value); +} + +wabt::Result CommandRunner::CheckAssertReturnResult( + const AssertReturnCommand* command, + int index, + ExpectedValue expected, + TypedValue actual, + bool print_error) { + assert(expected.value.type == actual.type || + IsReference(expected.value.type)); + bool ok = true; + switch (expected.value.type) { + case Type::I8: + case Type::I16: + case Type::I32: + ok = expected.value.value.Get<u32>() == actual.value.Get<u32>(); + break; + + case Type::I64: + ok = expected.value.value.Get<u64>() == actual.value.Get<u64>(); + break; + + case Type::F32: + switch (expected.nan[0]) { + case ExpectedNan::Arithmetic: + ok = IsArithmeticNan(actual.value.Get<f32>()); + break; + + case ExpectedNan::Canonical: + ok = IsCanonicalNan(actual.value.Get<f32>()); + break; + + case ExpectedNan::None: + ok = Bitcast<u32>(expected.value.value.Get<f32>()) == + Bitcast<u32>(actual.value.Get<f32>()); + break; + } + break; + + case Type::F64: + switch (expected.nan[0]) { + case ExpectedNan::Arithmetic: + ok = IsArithmeticNan(actual.value.Get<f64>()); + break; + + case ExpectedNan::Canonical: + ok = IsCanonicalNan(actual.value.Get<f64>()); + break; + + case ExpectedNan::None: + ok = Bitcast<u64>(expected.value.value.Get<f64>()) == + Bitcast<u64>(actual.value.Get<f64>()); + break; + } + break; + + case Type::V128: { + // Compare each lane as if it were its own value. + for (int lane = 0; lane < LaneCountFromType(expected.lane_type); ++lane) { + ExpectedValue lane_expected = GetLane(expected, lane); + TypedValue lane_actual = GetLane(actual, expected.lane_type, lane); + + if (Failed(CheckAssertReturnResult(command, index, lane_expected, + lane_actual, false))) { + PrintError(command->line, + "mismatch in lane %u of result %u of assert_return: " + "expected %s, got %s", + lane, index, ExpectedValueToString(lane_expected).c_str(), + TypedValueToString(lane_actual).c_str()); + ok = false; + } + } + break; + } + + case Type::FuncRef: + // A funcref expectation only requires that the reference be a function, + // but it doesn't check the actual index. + ok = (actual.type == Type::FuncRef); + break; + + case Type::ExternRef: + ok = expected.value.value.Get<Ref>() == actual.value.Get<Ref>(); + break; + + default: + WABT_UNREACHABLE; + } + + if (!ok && print_error) { + PrintError(command->line, + "mismatch in result %u of assert_return: expected %s, got %s", + index, ExpectedValueToString(expected).c_str(), + TypedValueToString(actual).c_str()); + } + return ok ? wabt::Result::Ok : wabt::Result::Error; +} + +wabt::Result CommandRunner::OnAssertReturnCommand( + const AssertReturnCommand* command) { + ActionResult action_result = + RunAction(command->line, &command->action, RunVerbosity::Quiet); + + if (action_result.trap) { + PrintError(command->line, "unexpected trap: %s", + action_result.trap->message().c_str()); + return wabt::Result::Error; + } + + if (action_result.values.size() != command->expected.size()) { + PrintError(command->line, + "result length mismatch in assert_return: expected %" PRIzd + ", got %" PRIzd, + command->expected.size(), action_result.values.size()); + return wabt::Result::Error; + } + + wabt::Result result = wabt::Result::Ok; + for (size_t i = 0; i < action_result.values.size(); ++i) { + const ExpectedValue& expected = command->expected[i]; + TypedValue actual{action_result.types[i], action_result.values[i]}; + + result |= CheckAssertReturnResult(command, i, expected, actual, true); + } + + return result; +} + +wabt::Result CommandRunner::OnAssertTrapCommand( + const AssertTrapCommand* command) { + ActionResult result = + RunAction(command->line, &command->action, RunVerbosity::Quiet); + if (!result.trap) { + PrintError(command->line, "expected trap: \"%s\"", command->text.c_str()); + return wabt::Result::Error; + } + + PrintError(command->line, "assert_trap passed: %s", + result.trap->message().c_str()); + return wabt::Result::Ok; +} + +wabt::Result CommandRunner::OnAssertExhaustionCommand( + const AssertExhaustionCommand* command) { + ActionResult result = + RunAction(command->line, &command->action, RunVerbosity::Quiet); + if (!result.trap || result.trap->message() != "call stack exhausted") { + PrintError(command->line, "expected trap: \"%s\"", command->text.c_str()); + return wabt::Result::Error; + } + + // TODO: print message when assertion passes. +#if 0 + PrintError(command->line, "assert_exhaustion passed: %s", + result.trap->message().c_str()); +#endif + return wabt::Result::Ok; +} + +void CommandRunner::TallyCommand(wabt::Result result) { + if (Succeeded(result)) { + passed_++; + } + total_++; +} + +static int ReadAndRunSpecJSON(string_view spec_json_filename) { + JSONParser parser; + if (parser.ReadFile(spec_json_filename) == wabt::Result::Error) { + return 1; + } + + Script script; + if (parser.ParseScript(&script) == wabt::Result::Error) { + return 1; + } + + CommandRunner runner; + if (runner.Run(script) == wabt::Result::Error) { + return 1; + } + + printf("%d/%d tests passed.\n", runner.passed(), runner.total()); + const int failed = runner.total() - runner.passed(); + return failed; +} + +} // namespace spectest + +int ProgramMain(int argc, char** argv) { + InitStdio(); + s_stdout_stream = FileStream::CreateStdout(); + + ParseOptions(argc, argv); + return spectest::ReadAndRunSpecJSON(s_infile); +} + +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/tools/wasm-decompile.cc b/third_party/wasm2c/src/tools/wasm-decompile.cc new file mode 100644 index 0000000000..74491e55c3 --- /dev/null +++ b/third_party/wasm2c/src/tools/wasm-decompile.cc @@ -0,0 +1,119 @@ +/* + * 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 <cassert> +#include <cinttypes> +#include <cstdio> +#include <cstdlib> + +#include "src/apply-names.h" +#include "src/binary-reader.h" +#include "src/binary-reader-ir.h" +#include "src/error-formatter.h" +#include "src/feature.h" +#include "src/generate-names.h" +#include "src/ir.h" +#include "src/option-parser.h" +#include "src/stream.h" +#include "src/validator.h" +#include "src/wast-lexer.h" +#include "src/decompiler.h" + +using namespace wabt; + +int ProgramMain(int argc, char** argv) { + InitStdio(); + + std::string infile; + std::string outfile; + Features features; + DecompileOptions decompile_options; + bool fail_on_custom_section_error = true; + + { + const char s_description[] = + " Read a file in the WebAssembly binary format, and convert it to\n" + " a decompiled text file.\n" + "\n" + "examples:\n" + " # parse binary file test.wasm and write text file test.dcmp\n" + " $ wasm-decompile test.wasm -o test.dcmp\n"; + OptionParser parser("wasm-decompile", s_description); + parser.AddOption( + 'o', "output", "FILENAME", + "Output file for the decompiled file, by default use stdout", + [&](const char* argument) { + outfile = argument; + ConvertBackslashToSlash(&outfile); + }); + features.AddOptions(&parser); + parser.AddOption("ignore-custom-section-errors", + "Ignore errors in custom sections", + [&]() { fail_on_custom_section_error = false; }); + parser.AddArgument("filename", OptionParser::ArgumentCount::One, + [&](const char* argument) { + infile = argument; + ConvertBackslashToSlash(&infile); + }); + parser.Parse(argc, argv); + } + + std::vector<uint8_t> file_data; + Result result = ReadFile(infile.c_str(), &file_data); + if (Succeeded(result)) { + Errors errors; + Module module; + const bool kStopOnFirstError = true; + ReadBinaryOptions options(features, nullptr, + true, kStopOnFirstError, + fail_on_custom_section_error); + result = ReadBinaryIr(infile.c_str(), file_data.data(), file_data.size(), + options, &errors, &module); + if (Succeeded(result)) { + ValidateOptions options(features); + result = ValidateModule(&module, &errors, options); + if (Succeeded(result)) { + result = GenerateNames(&module, + static_cast<NameOpts>(NameOpts::AlphaNames)); + } + if (Succeeded(result)) { + // Must be called after ReadBinaryIr & GenerateNames, and before + // ApplyNames, see comments at definition. + RenameAll(module); + } + if (Succeeded(result)) { + /* TODO(binji): This shouldn't fail; if a name can't be applied + * (because the index is invalid, say) it should just be skipped. */ + Result dummy_result = ApplyNames(&module); + WABT_USE(dummy_result); + } + if (Succeeded(result)) { + auto s = Decompile(module, decompile_options); + FileStream stream(!outfile.empty() ? FileStream(outfile) + : FileStream(stdout)); + stream.WriteData(s.data(), s.size()); + } + } + 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/tools/wasm-interp.cc b/third_party/wasm2c/src/tools/wasm-interp.cc new file mode 100644 index 0000000000..e349a9e323 --- /dev/null +++ b/third_party/wasm2c/src/tools/wasm-interp.cc @@ -0,0 +1,337 @@ +/* + * 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 <algorithm> +#include <cassert> +#include <cstdio> +#include <cstdlib> +#include <memory> +#include <string> +#include <vector> + +#include "src/binary-reader.h" +#include "src/error-formatter.h" +#include "src/feature.h" +#include "src/interp/binary-reader-interp.h" +#include "src/interp/interp-util.h" +#include "src/interp/interp-wasi.h" +#include "src/interp/interp.h" +#include "src/option-parser.h" +#include "src/stream.h" + +#ifdef WITH_WASI +#include "uvwasi.h" +#endif + +using namespace wabt; +using namespace wabt::interp; + +static int s_verbose; +static const char* s_infile; +static Thread::Options s_thread_options; +static Stream* s_trace_stream; +static bool s_run_all_exports; +static bool s_host_print; +static bool s_dummy_import_func; +static Features s_features; +static bool s_wasi; +static std::vector<std::string> s_wasi_env; +static std::vector<std::string> s_wasi_argv; +static std::vector<std::string> s_wasi_dirs; + +static std::unique_ptr<FileStream> s_log_stream; +static std::unique_ptr<FileStream> s_stdout_stream; +static std::unique_ptr<FileStream> s_stderr_stream; + +static Store s_store; + +static const char s_description[] = + R"( read a file in the wasm binary format, and run in it a stack-based + interpreter. + +examples: + # parse binary file test.wasm, and type-check it + $ wasm-interp test.wasm + + # parse test.wasm and run all its exported functions + $ wasm-interp test.wasm --run-all-exports + + # parse test.wasm, run the exported functions and trace the output + $ wasm-interp test.wasm --run-all-exports --trace + + # parse test.wasm and run all its exported functions, setting the + # value stack size to 100 elements + $ wasm-interp test.wasm -V 100 --run-all-exports +)"; + +static void ParseOptions(int argc, char** argv) { + OptionParser parser("wasm-interp", s_description); + + parser.AddOption('v', "verbose", "Use multiple times for more info", []() { + s_verbose++; + s_log_stream = FileStream::CreateStderr(); + }); + s_features.AddOptions(&parser); + parser.AddOption('V', "value-stack-size", "SIZE", + "Size in elements of the value stack", + [](const std::string& argument) { + // TODO(binji): validate. + s_thread_options.value_stack_size = atoi(argument.c_str()); + }); + parser.AddOption('C', "call-stack-size", "SIZE", + "Size in elements of the call stack", + [](const std::string& argument) { + // TODO(binji): validate. + s_thread_options.call_stack_size = atoi(argument.c_str()); + }); + parser.AddOption('t', "trace", "Trace execution", + []() { s_trace_stream = s_stdout_stream.get(); }); + parser.AddOption("wasi", + "Assume input module is WASI compliant (Export " + " WASI API the the module and invoke _start function)", + []() { s_wasi = true; }); + parser.AddOption( + 'e', "env", "ENV", + "Pass the given environment string in the WASI runtime", + [](const std::string& argument) { s_wasi_env.push_back(argument); }); + parser.AddOption( + 'd', "dir", "DIR", "Pass the given directory the the WASI runtime", + [](const std::string& argument) { s_wasi_dirs.push_back(argument); }); + parser.AddOption( + "run-all-exports", + "Run all the exported functions, in order. Useful for testing", + []() { s_run_all_exports = true; }); + parser.AddOption("host-print", + "Include an importable function named \"host.print\" for " + "printing to stdout", + []() { s_host_print = true; }); + parser.AddOption( + "dummy-import-func", + "Provide a dummy implementation of all imported functions. The function " + "will log the call and return an appropriate zero value.", + []() { s_dummy_import_func = true; }); + + parser.AddArgument("filename", OptionParser::ArgumentCount::One, + [](const char* argument) { s_infile = argument; }); + parser.AddArgument( + "arg", OptionParser::ArgumentCount::ZeroOrMore, + [](const char* argument) { s_wasi_argv.push_back(argument); }); + parser.Parse(argc, argv); +} + +Result RunAllExports(const Instance::Ptr& instance, Errors* errors) { + Result result = Result::Ok; + + auto module = s_store.UnsafeGet<Module>(instance->module()); + auto&& module_desc = module->desc(); + + for (auto&& export_ : module_desc.exports) { + if (export_.type.type->kind != ExternalKind::Func) { + continue; + } + auto* func_type = cast<FuncType>(export_.type.type.get()); + if (func_type->params.empty()) { + if (s_trace_stream) { + s_trace_stream->Writef(">>> running export \"%s\":\n", + export_.type.name.c_str()); + } + auto func = s_store.UnsafeGet<Func>(instance->funcs()[export_.index]); + Values params; + Values results; + Trap::Ptr trap; + result |= func->Call(s_store, params, results, &trap, s_trace_stream); + WriteCall(s_stdout_stream.get(), export_.type.name, *func_type, params, + results, trap); + } + } + + return result; +} + +static void BindImports(const Module::Ptr& module, RefVec& imports) { + auto* stream = s_stdout_stream.get(); + + for (auto&& import : module->desc().imports) { + if (import.type.type->kind == ExternKind::Func && + ((s_host_print && import.type.module == "host" && + import.type.name == "print") || + s_dummy_import_func)) { + auto func_type = *cast<FuncType>(import.type.type.get()); + auto import_name = StringPrintf("%s.%s", import.type.module.c_str(), + import.type.name.c_str()); + + auto host_func = + HostFunc::New(s_store, func_type, + [=](Thread& thread, const Values& params, + Values& results, Trap::Ptr* trap) -> Result { + printf("called host "); + WriteCall(stream, import_name, func_type, params, + results, *trap); + return Result::Ok; + }); + imports.push_back(host_func.ref()); + continue; + } + + // By default, just push an null reference. This won't resolve, and + // instantiation will fail. + imports.push_back(Ref::Null); + } +} + +static Result ReadModule(const char* module_filename, + Errors* errors, + Module::Ptr* out_module) { + auto* stream = s_stdout_stream.get(); + std::vector<uint8_t> file_data; + CHECK_RESULT(ReadFile(module_filename, &file_data)); + + ModuleDesc module_desc; + const bool kReadDebugNames = true; + const bool kStopOnFirstError = true; + const bool kFailOnCustomSectionError = true; + ReadBinaryOptions options(s_features, s_log_stream.get(), kReadDebugNames, + kStopOnFirstError, kFailOnCustomSectionError); + CHECK_RESULT(ReadBinaryInterp(file_data.data(), file_data.size(), options, + errors, &module_desc)); + + if (s_verbose) { + module_desc.istream.Disassemble(stream); + } + + *out_module = Module::New(s_store, module_desc); + return Result::Ok; +} + +static Result InstantiateModule(RefVec& imports, + const Module::Ptr& module, + Instance::Ptr* out_instance) { + RefPtr<Trap> trap; + *out_instance = Instance::Instantiate(s_store, module.ref(), imports, &trap); + if (!*out_instance) { + WriteTrap(s_stderr_stream.get(), "error initializing module", trap); + return Result::Error; + } + return Result::Ok; +} + +static Result ReadAndRunModule(const char* module_filename) { + Errors errors; + Module::Ptr module; + Result result = ReadModule(module_filename, &errors, &module); + if (!Succeeded(result)) { + FormatErrorsToFile(errors, Location::Type::Binary); + return result; + } + + RefVec imports; + +#if WITH_WASI + uvwasi_t uvwasi; +#endif + + if (s_wasi) { +#if WITH_WASI + uvwasi_errno_t err; + uvwasi_options_t init_options; + + std::vector<const char*> argv; + argv.push_back(module_filename); + for (auto& s : s_wasi_argv) { + if (s_trace_stream) { + s_trace_stream->Writef("wasi: arg: \"%s\"\n", s.c_str()); + } + argv.push_back(s.c_str()); + } + argv.push_back(nullptr); + + std::vector<const char*> envp; + for (auto& s : s_wasi_env) { + if (s_trace_stream) { + s_trace_stream->Writef("wasi: env: \"%s\"\n", s.c_str()); + } + envp.push_back(s.c_str()); + } + envp.push_back(nullptr); + + std::vector<uvwasi_preopen_t> dirs; + for (auto& dir : s_wasi_dirs) { + if (s_trace_stream) { + s_trace_stream->Writef("wasi: dir: \"%s\"\n", dir.c_str()); + } + dirs.push_back({dir.c_str(), dir.c_str()}); + } + + /* Setup the initialization options. */ + init_options.in = 0; + init_options.out = 1; + init_options.err = 2; + init_options.fd_table_size = 3; + init_options.argc = argv.size() - 1; + init_options.argv = argv.data(); + init_options.envp = envp.data(); + init_options.preopenc = dirs.size(); + init_options.preopens = dirs.data(); + init_options.allocator = NULL; + + err = uvwasi_init(&uvwasi, &init_options); + if (err != UVWASI_ESUCCESS) { + s_stderr_stream.get()->Writef("error initialiazing uvwasi: %d\n", err); + return Result::Error; + } + CHECK_RESULT(WasiBindImports(module, imports, s_stderr_stream.get(), + s_trace_stream)); +#else + s_stderr_stream.get()->Writef("wasi support not compiled in\n"); + return Result::Error; +#endif + } else { + BindImports(module, imports); + } + BindImports(module, imports); + + Instance::Ptr instance; + CHECK_RESULT(InstantiateModule(imports, module, &instance)); + + if (s_run_all_exports) { + RunAllExports(instance, &errors); + } +#ifdef WITH_WASI + if (s_wasi) { + CHECK_RESULT( + WasiRunStart(instance, &uvwasi, s_stderr_stream.get(), s_trace_stream)); + } +#endif + + return Result::Ok; +} + +int ProgramMain(int argc, char** argv) { + InitStdio(); + s_stdout_stream = FileStream::CreateStdout(); + s_stderr_stream = FileStream::CreateStderr(); + + ParseOptions(argc, argv); + + wabt::Result result = ReadAndRunModule(s_infile); + return result != wabt::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/tools/wasm-objdump.cc b/third_party/wasm2c/src/tools/wasm-objdump.cc new file mode 100644 index 0000000000..fbfed12cf1 --- /dev/null +++ b/third_party/wasm2c/src/tools/wasm-objdump.cc @@ -0,0 +1,149 @@ +/* + * 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 <cstdlib> +#include <cstring> + +#include "src/common.h" +#include "src/option-parser.h" +#include "src/stream.h" +#include "src/binary-reader.h" +#include "src/binary-reader-objdump.h" + +using namespace wabt; + +static const char s_description[] = +R"( Print information about the contents of wasm binaries. + +examples: + $ wasm-objdump test.wasm +)"; + +static ObjdumpOptions s_objdump_options; + +static std::vector<const char*> s_infiles; + +static std::unique_ptr<FileStream> s_log_stream; + +static void ParseOptions(int argc, char** argv) { + OptionParser parser("wasm-objdump", s_description); + + parser.AddOption('h', "headers", "Print headers", + []() { s_objdump_options.headers = true; }); + parser.AddOption( + 'j', "section", "SECTION", "Select just one section", + [](const char* argument) { s_objdump_options.section_name = argument; }); + parser.AddOption('s', "full-contents", "Print raw section contents", + []() { s_objdump_options.raw = true; }); + parser.AddOption('d', "disassemble", "Disassemble function bodies", + []() { s_objdump_options.disassemble = true; }); + parser.AddOption("debug", "Print extra debug information", []() { + s_objdump_options.debug = true; + s_log_stream = FileStream::CreateStderr(); + s_objdump_options.log_stream = s_log_stream.get(); + }); + parser.AddOption('x', "details", "Show section details", + []() { s_objdump_options.details = true; }); + parser.AddOption('r', "reloc", "Show relocations inline with disassembly", + []() { s_objdump_options.relocs = true; }); + parser.AddOption(0, "section-offsets", + "Print section offsets instead of file offsets " + "in code disassembly", + []() { s_objdump_options.section_offsets = true; }); + parser.AddArgument( + "filename", OptionParser::ArgumentCount::OneOrMore, + [](const char* argument) { s_infiles.push_back(argument); }); + + parser.Parse(argc, argv); +} + +Result dump_file(const char* filename) { + std::vector<uint8_t> file_data; + CHECK_RESULT(ReadFile(filename, &file_data)); + + uint8_t* data = file_data.data(); + size_t size = file_data.size(); + + // Perform serveral passed over the binary in order to print out different + // types of information. + s_objdump_options.filename = filename; + printf("\n"); + + ObjdumpState state; + + Result result = Result::Ok; + + // Pass 0: Prepass + s_objdump_options.mode = ObjdumpMode::Prepass; + result |= ReadBinaryObjdump(data, size, &s_objdump_options, &state); + s_objdump_options.log_stream = nullptr; + + // Pass 1: Print the section headers + if (s_objdump_options.headers) { + s_objdump_options.mode = ObjdumpMode::Headers; + result |= ReadBinaryObjdump(data, size, &s_objdump_options, &state); + } + + // Pass 2: Print extra information based on section type + if (s_objdump_options.details) { + s_objdump_options.mode = ObjdumpMode::Details; + result |= ReadBinaryObjdump(data, size, &s_objdump_options, &state); + } + + // Pass 3: Disassemble code section + if (s_objdump_options.disassemble) { + s_objdump_options.mode = ObjdumpMode::Disassemble; + result |= ReadBinaryObjdump(data, size, &s_objdump_options, &state); + } + + // Pass 4: Dump to raw contents of the sections + if (s_objdump_options.raw) { + s_objdump_options.mode = ObjdumpMode::RawData; + result |= ReadBinaryObjdump(data, size, &s_objdump_options, &state); + } + + return result; +} + +int ProgramMain(int argc, char** argv) { + InitStdio(); + + ParseOptions(argc, argv); + if (!s_objdump_options.headers && !s_objdump_options.details && + !s_objdump_options.disassemble && !s_objdump_options.raw) { + fprintf(stderr, "At least one of the following switches must be given:\n"); + fprintf(stderr, " -d/--disassemble\n"); + fprintf(stderr, " -h/--headers\n"); + fprintf(stderr, " -x/--details\n"); + fprintf(stderr, " -s/--full-contents\n"); + return 1; + } + + for (const char* filename: s_infiles) { + if (Failed(dump_file(filename))) { + return 1; + } + } + + return 0; +} + +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/tools/wasm-opcodecnt.cc b/third_party/wasm2c/src/tools/wasm-opcodecnt.cc new file mode 100644 index 0000000000..5d6225f098 --- /dev/null +++ b/third_party/wasm2c/src/tools/wasm-opcodecnt.cc @@ -0,0 +1,188 @@ +/* + * 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 <algorithm> +#include <cassert> +#include <cerrno> +#include <cinttypes> +#include <cstdio> +#include <cstdlib> +#include <map> +#include <vector> + +#include "src/binary-reader.h" +#include "src/binary-reader-opcnt.h" +#include "src/option-parser.h" +#include "src/stream.h" + +#define ERROR(fmt, ...) \ + fprintf(stderr, "%s:%d: " fmt, __FILE__, __LINE__, __VA_ARGS__) + +using namespace wabt; + +static int s_verbose; +static const char* s_infile; +static const char* s_outfile; +static size_t s_cutoff = 0; +static const char* s_separator = ": "; + +static ReadBinaryOptions s_read_binary_options; +static std::unique_ptr<FileStream> s_log_stream; +static Features s_features; + +static const char s_description[] = +R"( Read a file in the wasm binary format, and count opcode usage for + instructions. + +examples: + # parse binary file test.wasm and write pcode dist file test.dist + $ wasm-opcodecnt test.wasm -o test.dist +)"; + +static void ParseOptions(int argc, char** argv) { + OptionParser parser("wasm-opcodecnt", s_description); + + parser.AddOption('v', "verbose", "Use multiple times for more info", []() { + s_verbose++; + s_log_stream = FileStream::CreateStderr(); + s_read_binary_options.log_stream = s_log_stream.get(); + }); + s_features.AddOptions(&parser); + parser.AddOption('o', "output", "FILENAME", + "Output file for the opcode counts, by default use stdout", + [](const char* argument) { s_outfile = argument; }); + parser.AddOption( + 'c', "cutoff", "N", "Cutoff for reporting counts less than N", + [](const std::string& argument) { s_cutoff = atol(argument.c_str()); }); + parser.AddOption( + 's', "separator", "SEPARATOR", + "Separator text between element and count when reporting counts", + [](const char* argument) { s_separator = argument; }); + parser.AddArgument("filename", OptionParser::ArgumentCount::OneOrMore, + [](const char* argument) { s_infile = argument; }); + parser.Parse(argc, argv); +} + +template <typename T> +struct SortByCountDescending { + bool operator()(const T& lhs, const T& rhs) const { + return lhs.second > rhs.second; + } +}; + +template <typename T> +struct WithinCutoff { + bool operator()(const T& pair) const { + return pair.second >= s_cutoff; + } +}; + +static size_t SumCounts(const OpcodeInfoCounts& info_counts) { + size_t sum = 0; + for (auto& pair : info_counts) { + sum += pair.second; + } + return sum; +} + +void WriteCounts(Stream& stream, const OpcodeInfoCounts& info_counts) { + typedef std::pair<Opcode, size_t> OpcodeCountPair; + + std::map<Opcode, size_t> counts; + for (auto& info_count_pair: info_counts) { + Opcode opcode = info_count_pair.first.opcode(); + size_t count = info_count_pair.second; + counts[opcode] += count; + } + + std::vector<OpcodeCountPair> sorted; + std::copy_if(counts.begin(), counts.end(), std::back_inserter(sorted), + WithinCutoff<OpcodeCountPair>()); + + // Use a stable sort to keep the elements with the same count in opcode + // order (since the Opcode map is sorted). + std::stable_sort(sorted.begin(), sorted.end(), + SortByCountDescending<OpcodeCountPair>()); + + for (auto& pair : sorted) { + Opcode opcode = pair.first; + size_t count = pair.second; + stream.Writef("%s%s%" PRIzd "\n", opcode.GetName(), s_separator, count); + } +} + +void WriteCountsWithImmediates(Stream& stream, + const OpcodeInfoCounts& counts) { + // Remove const from the key type so we can sort below. + typedef std::pair<std::remove_const<OpcodeInfoCounts::key_type>::type, + OpcodeInfoCounts::mapped_type> + OpcodeInfoCountPair; + + std::vector<OpcodeInfoCountPair> sorted; + std::copy_if(counts.begin(), counts.end(), std::back_inserter(sorted), + WithinCutoff<OpcodeInfoCountPair>()); + + // Use a stable sort to keep the elements with the same count in opcode info + // order (since the OpcodeInfoCounts map is sorted). + std::stable_sort(sorted.begin(), sorted.end(), + SortByCountDescending<OpcodeInfoCountPair>()); + + for (auto& pair : sorted) { + auto&& info = pair.first; + size_t count = pair.second; + info.Write(stream); + stream.Writef("%s%" PRIzd "\n", s_separator, count); + } +} + +int ProgramMain(int argc, char** argv) { + InitStdio(); + ParseOptions(argc, argv); + + std::vector<uint8_t> file_data; + Result result = ReadFile(s_infile, &file_data); + if (Failed(result)) { + const char* input_name = s_infile ? s_infile : "stdin"; + ERROR("Unable to parse: %s", input_name); + return 1; + } + + FileStream stream(s_outfile ? FileStream(s_outfile) : FileStream(stdout)); + + if (Succeeded(result)) { + OpcodeInfoCounts counts; + s_read_binary_options.features = s_features; + result = ReadBinaryOpcnt(file_data.data(), file_data.size(), + s_read_binary_options, &counts); + if (Succeeded(result)) { + stream.Writef("Total opcodes: %" PRIzd "\n\n", SumCounts(counts)); + + stream.Writef("Opcode counts:\n"); + WriteCounts(stream, counts); + + stream.Writef("\nOpcode counts with immediates:\n"); + WriteCountsWithImmediates(stream, counts); + } + } + + 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/tools/wasm-strip.cc b/third_party/wasm2c/src/tools/wasm-strip.cc new file mode 100644 index 0000000000..f838672064 --- /dev/null +++ b/third_party/wasm2c/src/tools/wasm-strip.cc @@ -0,0 +1,114 @@ +/* + * 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 "src/binary.h" +#include "src/binary-reader.h" +#include "src/binary-reader-nop.h" +#include "src/error-formatter.h" +#include "src/leb128.h" +#include "src/option-parser.h" +#include "src/stream.h" + +using namespace wabt; + +static std::string s_filename; + +static const char s_description[] = +R"( Remove sections of a WebAssembly binary file. + +examples: + # Remove all custom sections from test.wasm + $ wasm-strip test.wasm +)"; + +static void ParseOptions(int argc, char** argv) { + OptionParser parser("wasm-strip", s_description); + + parser.AddArgument("filename", OptionParser::ArgumentCount::One, + [](const char* argument) { + s_filename = argument; + ConvertBackslashToSlash(&s_filename); + }); + parser.Parse(argc, argv); +} + +class BinaryReaderStrip : public BinaryReaderNop { + public: + explicit BinaryReaderStrip(Errors* errors) + : errors_(errors) { + stream_.WriteU32(WABT_BINARY_MAGIC, "WASM_BINARY_MAGIC"); + stream_.WriteU32(WABT_BINARY_VERSION, "WASM_BINARY_VERSION"); + } + + bool OnError(const Error& error) override { + errors_->push_back(error); + return true; + } + + Result BeginSection(Index section_index, + BinarySection section_type, + Offset size) override { + if (section_type == BinarySection::Custom) { + return Result::Ok; + } + stream_.WriteU8Enum(section_type, "section code"); + WriteU32Leb128(&stream_, size, "section size"); + stream_.WriteData(state->data + state->offset, size, "section data"); + return Result::Ok; + } + + Result WriteToFile(string_view filename) { + return stream_.WriteToFile(filename); + } + + private: + MemoryStream stream_; + Errors* errors_; +}; + +int ProgramMain(int argc, char** argv) { + Result result; + + InitStdio(); + ParseOptions(argc, argv); + + std::vector<uint8_t> file_data; + result = ReadFile(s_filename.c_str(), &file_data); + if (Succeeded(result)) { + Errors errors; + Features features; + const bool kReadDebugNames = false; + const bool kStopOnFirstError = true; + const bool kFailOnCustomSectionError = false; + ReadBinaryOptions options(features, nullptr, kReadDebugNames, + kStopOnFirstError, kFailOnCustomSectionError); + + BinaryReaderStrip reader(&errors); + result = ReadBinary(file_data.data(), file_data.size(), &reader, options); + FormatErrorsToFile(errors, Location::Type::Binary); + + if (Succeeded(result)) { + result = reader.WriteToFile(s_filename); + } + } + 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/tools/wasm-validate.cc b/third_party/wasm2c/src/tools/wasm-validate.cc new file mode 100644 index 0000000000..a55c5ed898 --- /dev/null +++ b/third_party/wasm2c/src/tools/wasm-validate.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 <cassert> +#include <cinttypes> +#include <cstdio> +#include <cstdlib> + +#include "src/binary-reader.h" +#include "src/binary-reader-ir.h" +#include "src/error-formatter.h" +#include "src/ir.h" +#include "src/option-parser.h" +#include "src/stream.h" +#include "src/validator.h" +#include "src/wast-lexer.h" + +using namespace wabt; + +static int s_verbose; +static std::string s_infile; +static Features s_features; +static bool s_read_debug_names = true; +static bool s_fail_on_custom_section_error = true; +static std::unique_ptr<FileStream> s_log_stream; + +static const char s_description[] = +R"( Read a file in the WebAssembly binary format, and validate it. + +examples: + # validate binary file test.wasm + $ wasm-validate test.wasm +)"; + +static void ParseOptions(int argc, char** argv) { + OptionParser parser("wasm-validate", s_description); + + parser.AddOption('v', "verbose", "Use multiple times for more info", []() { + s_verbose++; + s_log_stream = FileStream::CreateStderr(); + }); + s_features.AddOptions(&parser); + parser.AddOption("no-debug-names", "Ignore debug names in the binary file", + []() { s_read_debug_names = false; }); + parser.AddOption("ignore-custom-section-errors", + "Ignore errors in custom sections", + []() { s_fail_on_custom_section_error = false; }); + parser.AddArgument("filename", OptionParser::ArgumentCount::One, + [](const char* argument) { + s_infile = argument; + ConvertBackslashToSlash(&s_infile); + }); + parser.Parse(argc, argv); +} + +int ProgramMain(int argc, char** argv) { + Result result; + + InitStdio(); + ParseOptions(argc, argv); + + std::vector<uint8_t> file_data; + result = ReadFile(s_infile.c_str(), &file_data); + if (Succeeded(result)) { + Errors errors; + Module module; + const bool kStopOnFirstError = true; + ReadBinaryOptions options(s_features, s_log_stream.get(), + s_read_debug_names, kStopOnFirstError, + s_fail_on_custom_section_error); + result = ReadBinaryIr(s_infile.c_str(), file_data.data(), file_data.size(), + options, &errors, &module); + if (Succeeded(result)) { + ValidateOptions options(s_features); + result = ValidateModule(&module, &errors, options); + } + 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/tools/wasm2c.cc b/third_party/wasm2c/src/tools/wasm2c.cc new file mode 100644 index 0000000000..680be01045 --- /dev/null +++ b/third_party/wasm2c/src/tools/wasm2c.cc @@ -0,0 +1,169 @@ +/* + * 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 "src/apply-names.h" +#include "src/binary-reader.h" +#include "src/binary-reader-ir.h" +#include "src/error-formatter.h" +#include "src/feature.h" +#include "src/generate-names.h" +#include "src/ir.h" +#include "src/option-parser.h" +#include "src/stream.h" +#include "src/validator.h" +#include "src/wast-lexer.h" + +#include "src/c-writer.h" + +using namespace wabt; + +static int s_verbose; +static std::string s_infile; +static std::string s_outfile; +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 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( + 'n', "modname", "MODNAME", + "Unique name for the module being generated. Each wasm sandboxed module in a single application should have a unique name.", + [](const char* argument) { + s_write_c_options.mod_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); + + // TODO(binji): currently wasm2c doesn't support any non-default feature + // flags. + bool any_non_default_feature = false; +#define WABT_FEATURE(variable, flag, default_, help) \ + any_non_default_feature |= (s_features.variable##_enabled() != default_); +#include "src/feature.def" +#undef WABT_FEATURE + + if (any_non_default_feature) { + fprintf(stderr, "wasm2c currently support only default feature flags.\n"); + exit(1); + } +} + +// TODO(binji): copied from binary-writer-spec.cc, probably should share. +static string_view strip_extension(string_view s) { + string_view ext = s.substr(s.find_last_of('.')); + string_view result = s; + + if (ext == ".c") + result.remove_suffix(ext.length()); + return result; +} + +int ProgramMain(int argc, char** argv) { + Result result; + + InitStdio(); + ParseOptions(argc, argv); + + std::vector<uint8_t> file_data; + result = ReadFile(s_infile.c_str(), &file_data); + if (Succeeded(result)) { + Errors errors; + Module module; + const bool kStopOnFirstError = true; + const bool kFailOnCustomSectionError = true; + ReadBinaryOptions options(s_features, s_log_stream.get(), + s_read_debug_names, kStopOnFirstError, + kFailOnCustomSectionError); + result = ReadBinaryIr(s_infile.c_str(), file_data.data(), file_data.size(), + options, &errors, &module); + if (Succeeded(result)) { + if (Succeeded(result)) { + ValidateOptions options(s_features); + result = ValidateModule(&module, &errors, options); + result |= GenerateNames(&module); + } + + if (Succeeded(result)) { + /* TODO(binji): This shouldn't fail; if a name can't be applied + * (because the index is invalid, say) it should just be skipped. */ + Result dummy_result = ApplyNames(&module); + WABT_USE(dummy_result); + } + + if (Succeeded(result)) { + if (!s_outfile.empty()) { + std::string header_name = + strip_extension(s_outfile).to_string() + ".h"; + FileStream c_stream(s_outfile.c_str()); + FileStream h_stream(header_name); + result = WriteC(&c_stream, &h_stream, header_name.c_str(), &module, + s_write_c_options); + } else { + FileStream stream(stdout); + result = + WriteC(&stream, &stream, "wasm.h", &module, s_write_c_options); + } + } + } + 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/tools/wasm2wat-fuzz.cc b/third_party/wasm2c/src/tools/wasm2wat-fuzz.cc new file mode 100644 index 0000000000..1318ef6284 --- /dev/null +++ b/third_party/wasm2c/src/tools/wasm2wat-fuzz.cc @@ -0,0 +1,30 @@ +// Copyright 2019 Google LLC +// +// 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. +// +// This file is copied from the oss-fuzz project: +// +// https://github.com/google/oss-fuzz/blob/master/projects/wabt/wasm2wat_fuzzer.cc + +#include "src/binary-reader-ir.h" +#include "src/binary-reader.h" +#include "src/common.h" +#include "src/ir.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + wabt::ReadBinaryOptions options; + wabt::Errors errors; + wabt::Module module; + wabt::ReadBinaryIr("dummy filename", data, size, options, &errors, &module); + return 0; +} diff --git a/third_party/wasm2c/src/tools/wasm2wat.cc b/third_party/wasm2c/src/tools/wasm2wat.cc new file mode 100644 index 0000000000..58e89849fe --- /dev/null +++ b/third_party/wasm2c/src/tools/wasm2wat.cc @@ -0,0 +1,149 @@ +/* + * 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 <cassert> +#include <cinttypes> +#include <cstdio> +#include <cstdlib> + +#include "src/apply-names.h" +#include "src/binary-reader.h" +#include "src/binary-reader-ir.h" +#include "src/error-formatter.h" +#include "src/feature.h" +#include "src/generate-names.h" +#include "src/ir.h" +#include "src/option-parser.h" +#include "src/stream.h" +#include "src/validator.h" +#include "src/wast-lexer.h" +#include "src/wat-writer.h" + +using namespace wabt; + +static int s_verbose; +static std::string s_infile; +static std::string s_outfile; +static Features s_features; +static WriteWatOptions s_write_wat_options; +static bool s_generate_names = false; +static bool s_read_debug_names = true; +static bool s_fail_on_custom_section_error = true; +static std::unique_ptr<FileStream> s_log_stream; +static bool s_validate = true; + +static const char s_description[] = +R"( Read a file in the WebAssembly binary format, and convert it to + the WebAssembly text format. + +examples: + # parse binary file test.wasm and write text file test.wast + $ wasm2wat test.wasm -o test.wat + + # parse test.wasm, write test.wat, but ignore the debug names, if any + $ wasm2wat test.wasm --no-debug-names -o test.wat +)"; + +static void ParseOptions(int argc, char** argv) { + OptionParser parser("wasm2wat", 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 wast file, by default use stdout", + [](const char* argument) { + s_outfile = argument; + ConvertBackslashToSlash(&s_outfile); + }); + parser.AddOption('f', "fold-exprs", "Write folded expressions where possible", + []() { s_write_wat_options.fold_exprs = true; }); + s_features.AddOptions(&parser); + parser.AddOption("inline-exports", "Write all exports inline", + []() { s_write_wat_options.inline_export = true; }); + parser.AddOption("inline-imports", "Write all imports inline", + []() { s_write_wat_options.inline_import = true; }); + parser.AddOption("no-debug-names", "Ignore debug names in the binary file", + []() { s_read_debug_names = false; }); + parser.AddOption("ignore-custom-section-errors", + "Ignore errors in custom sections", + []() { s_fail_on_custom_section_error = false; }); + parser.AddOption( + "generate-names", + "Give auto-generated names to non-named functions, types, etc.", + []() { s_generate_names = true; }); + parser.AddOption("no-check", "Don't check for invalid modules", + []() { s_validate = false; }); + parser.AddArgument("filename", OptionParser::ArgumentCount::One, + [](const char* argument) { + s_infile = argument; + ConvertBackslashToSlash(&s_infile); + }); + parser.Parse(argc, argv); +} + +int ProgramMain(int argc, char** argv) { + Result result; + + InitStdio(); + ParseOptions(argc, argv); + + std::vector<uint8_t> file_data; + result = ReadFile(s_infile.c_str(), &file_data); + if (Succeeded(result)) { + Errors errors; + Module module; + const bool kStopOnFirstError = true; + ReadBinaryOptions options(s_features, s_log_stream.get(), + s_read_debug_names, kStopOnFirstError, + s_fail_on_custom_section_error); + result = ReadBinaryIr(s_infile.c_str(), file_data.data(), file_data.size(), + options, &errors, &module); + if (Succeeded(result)) { + if (Succeeded(result) && s_validate) { + ValidateOptions options(s_features); + result = ValidateModule(&module, &errors, options); + } + + if (s_generate_names) { + result = GenerateNames(&module); + } + + if (Succeeded(result)) { + /* TODO(binji): This shouldn't fail; if a name can't be applied + * (because the index is invalid, say) it should just be skipped. */ + Result dummy_result = ApplyNames(&module); + WABT_USE(dummy_result); + } + + if (Succeeded(result)) { + FileStream stream(!s_outfile.empty() ? FileStream(s_outfile) + : FileStream(stdout)); + result = WriteWat(&stream, &module, s_write_wat_options); + } + } + 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/tools/wast2json.cc b/third_party/wasm2c/src/tools/wast2json.cc new file mode 100644 index 0000000000..a5c9a47d1d --- /dev/null +++ b/third_party/wasm2c/src/tools/wast2json.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 <cassert> +#include <cstdarg> +#include <cstdint> +#include <cstdlib> +#include <cstdio> +#include <string> + +#include "config.h" + +#include "src/binary-writer.h" +#include "src/binary-writer-spec.h" +#include "src/common.h" +#include "src/error-formatter.h" +#include "src/feature.h" +#include "src/filenames.h" +#include "src/ir.h" +#include "src/option-parser.h" +#include "src/resolve-names.h" +#include "src/stream.h" +#include "src/validator.h" +#include "src/wast-parser.h" + +using namespace wabt; + +static const char* s_infile; +static std::string s_outfile; +static int s_verbose; +static WriteBinaryOptions s_write_binary_options; +static bool s_validate = true; +static bool s_debug_parsing; +static Features s_features; + +static std::unique_ptr<FileStream> s_log_stream; + +static const char s_description[] = +R"( read a file in the wasm spec test format, check it for errors, and + convert it to a JSON file and associated wasm binary files. + +examples: + # parse spec-test.wast, and write files to spec-test.json. Modules are + # written to spec-test.0.wasm, spec-test.1.wasm, etc. + $ wast2json spec-test.wast -o spec-test.json +)"; + +static void ParseOptions(int argc, char* argv[]) { + OptionParser parser("wast2json", s_description); + + parser.AddOption('v', "verbose", "Use multiple times for more info", []() { + s_verbose++; + s_log_stream = FileStream::CreateStderr(); + }); + parser.AddOption("debug-parser", "Turn on debugging the parser of wast files", + []() { s_debug_parsing = true; }); + s_features.AddOptions(&parser); + parser.AddOption('o', "output", "FILE", "output JSON file", + [](const char* argument) { s_outfile = argument; }); + parser.AddOption( + 'r', "relocatable", + "Create a relocatable wasm binary (suitable for linking with e.g. lld)", + []() { s_write_binary_options.relocatable = true; }); + parser.AddOption( + "no-canonicalize-leb128s", + "Write all LEB128 sizes as 5-bytes instead of their minimal size", + []() { s_write_binary_options.canonicalize_lebs = false; }); + parser.AddOption("debug-names", + "Write debug names to the generated binary file", + []() { s_write_binary_options.write_debug_names = true; }); + parser.AddOption("no-check", "Don't check for invalid modules", + []() { s_validate = false; }); + parser.AddArgument("filename", OptionParser::ArgumentCount::One, + [](const char* argument) { s_infile = argument; }); + + parser.Parse(argc, argv); +} + +static std::string DefaultOuputName(string_view input_name) { + // Strip existing extension and add .json + std::string result(StripExtension(GetBasename(input_name))); + result += ".json"; + + return result; +} + +int ProgramMain(int argc, char** argv) { + InitStdio(); + + ParseOptions(argc, argv); + + std::vector<uint8_t> file_data; + Result result = ReadFile(s_infile, &file_data); + std::unique_ptr<WastLexer> lexer = WastLexer::CreateBufferLexer( + s_infile, file_data.data(), file_data.size()); + if (Failed(result)) { + WABT_FATAL("unable to read file: %s\n", s_infile); + } + + Errors errors; + std::unique_ptr<Script> script; + WastParseOptions parse_wast_options(s_features); + result = ParseWastScript(lexer.get(), &script, &errors, &parse_wast_options); + + if (Succeeded(result) && s_validate) { + ValidateOptions options(s_features); + result = ValidateScript(script.get(), &errors, options); + } + + if (Succeeded(result)) { + if (s_outfile.empty()) { + s_outfile = DefaultOuputName(s_infile); + } + + std::vector<FilenameMemoryStreamPair> module_streams; + MemoryStream json_stream; + + std::string output_basename = StripExtension(s_outfile).to_string(); + s_write_binary_options.features = s_features; + result = WriteBinarySpecScript(&json_stream, script.get(), s_infile, + output_basename, s_write_binary_options, + &module_streams, s_log_stream.get()); + + if (Succeeded(result)) { + result = json_stream.WriteToFile(s_outfile); + } + + if (Succeeded(result)) { + for (auto iter = module_streams.begin(); iter != module_streams.end(); + ++iter) { + result = iter->stream->WriteToFile(iter->filename); + if (!Succeeded(result)) { + break; + } + } + } + } + + auto line_finder = lexer->MakeLineFinder(); + FormatErrorsToFile(errors, Location::Type::Text, line_finder.get()); + + 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/tools/wat-desugar.cc b/third_party/wasm2c/src/tools/wat-desugar.cc new file mode 100644 index 0000000000..cc6a2cba8e --- /dev/null +++ b/third_party/wasm2c/src/tools/wat-desugar.cc @@ -0,0 +1,130 @@ +/* + * 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 <cassert> +#include <cstdarg> +#include <cstdint> +#include <cstdio> +#include <cstdlib> + +#include "config.h" + +#include "src/apply-names.h" +#include "src/common.h" +#include "src/error-formatter.h" +#include "src/feature.h" +#include "src/generate-names.h" +#include "src/ir.h" +#include "src/option-parser.h" +#include "src/stream.h" +#include "src/wast-parser.h" +#include "src/wat-writer.h" + +using namespace wabt; + +static const char* s_infile; +static const char* s_outfile; +static WriteWatOptions s_write_wat_options; +static bool s_generate_names; +static bool s_debug_parsing; +static Features s_features; + +static const char s_description[] = +R"( read a file in the wasm s-expression format and format it. + +examples: + # write output to stdout + $ wat-desugar test.wat + + # write output to test2.wat + $ wat-desugar test.wat -o test2.wat + + # generate names for indexed variables + $ wat-desugar --generate-names test.wat +)"; + +static void ParseOptions(int argc, char** argv) { + OptionParser parser("wat-desugar", s_description); + + parser.AddOption('o', "output", "FILE", "Output file for the formatted file", + [](const char* argument) { s_outfile = argument; }); + parser.AddOption("debug-parser", "Turn on debugging the parser of wat files", + []() { s_debug_parsing = true; }); + parser.AddOption('f', "fold-exprs", "Write folded expressions where possible", + []() { s_write_wat_options.fold_exprs = true; }); + parser.AddOption("inline-exports", "Write all exports inline", + []() { s_write_wat_options.inline_export = true; }); + parser.AddOption("inline-imports", "Write all imports inline", + []() { s_write_wat_options.inline_import = true; }); + s_features.AddOptions(&parser); + parser.AddOption( + "generate-names", + "Give auto-generated names to non-named functions, types, etc.", + []() { s_generate_names = true; }); + + parser.AddArgument("filename", OptionParser::ArgumentCount::One, + [](const char* argument) { s_infile = argument; }); + parser.Parse(argc, argv); +} + +int ProgramMain(int argc, char** argv) { + InitStdio(); + ParseOptions(argc, argv); + + std::vector<uint8_t> file_data; + Result result = ReadFile(s_infile, &file_data); + if (Failed(result)) { + WABT_FATAL("unable to read %s\n", s_infile); + } + + std::unique_ptr<WastLexer> lexer(WastLexer::CreateBufferLexer( + s_infile, file_data.data(), file_data.size())); + + Errors errors; + std::unique_ptr<Script> script; + WastParseOptions parse_wast_options(s_features); + result = ParseWastScript(lexer.get(), &script, &errors, &parse_wast_options); + auto line_finder = lexer->MakeLineFinder(); + FormatErrorsToFile(errors, Location::Type::Text); + + if (Succeeded(result)) { + Module* module = script->GetFirstModule(); + if (!module) { + WABT_FATAL("no module in file.\n"); + } + + if (s_generate_names) { + result = GenerateNames(module); + } + + if (Succeeded(result)) { + result = ApplyNames(module); + } + + if (Succeeded(result)) { + FileStream stream(s_outfile ? FileStream(s_outfile) : FileStream(stdout)); + result = WriteWat(&stream, module, s_write_wat_options); + } + } + + 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/tools/wat2wasm.cc b/third_party/wasm2c/src/tools/wat2wasm.cc new file mode 100644 index 0000000000..f77dad2778 --- /dev/null +++ b/third_party/wasm2c/src/tools/wat2wasm.cc @@ -0,0 +1,170 @@ +/* + * 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 <cassert> +#include <cstdarg> +#include <cstdint> +#include <cstdlib> +#include <cstdio> +#include <string> + +#include "config.h" + +#include "src/binary-writer.h" +#include "src/common.h" +#include "src/error-formatter.h" +#include "src/feature.h" +#include "src/filenames.h" +#include "src/ir.h" +#include "src/option-parser.h" +#include "src/resolve-names.h" +#include "src/stream.h" +#include "src/validator.h" +#include "src/wast-parser.h" + +using namespace wabt; + +static const char* s_infile; +static std::string s_outfile; +static bool s_dump_module; +static int s_verbose; +static WriteBinaryOptions s_write_binary_options; +static bool s_validate = true; +static bool s_debug_parsing; +static Features s_features; + +static std::unique_ptr<FileStream> s_log_stream; + +static const char s_description[] = +R"( read a file in the wasm text format, check it for errors, and + convert it to the wasm binary format. + +examples: + # parse and typecheck test.wat + $ wat2wasm test.wat + + # parse test.wat and write to binary file test.wasm + $ wat2wasm test.wat -o test.wasm + + # parse spec-test.wast, and write verbose output to stdout (including + # the meaning of every byte) + $ wat2wasm spec-test.wast -v +)"; + +static void ParseOptions(int argc, char* argv[]) { + OptionParser parser("wat2wasm", s_description); + + parser.AddOption('v', "verbose", "Use multiple times for more info", []() { + s_verbose++; + s_log_stream = FileStream::CreateStderr(); + }); + parser.AddOption("debug-parser", "Turn on debugging the parser of wat files", + []() { s_debug_parsing = true; }); + parser.AddOption('d', "dump-module", + "Print a hexdump of the module to stdout", + []() { s_dump_module = true; }); + s_features.AddOptions(&parser); + parser.AddOption('o', "output", "FILE", "output wasm binary file", + [](const char* argument) { s_outfile = argument; }); + parser.AddOption( + 'r', "relocatable", + "Create a relocatable wasm binary (suitable for linking with e.g. lld)", + []() { s_write_binary_options.relocatable = true; }); + parser.AddOption( + "no-canonicalize-leb128s", + "Write all LEB128 sizes as 5-bytes instead of their minimal size", + []() { s_write_binary_options.canonicalize_lebs = false; }); + parser.AddOption("debug-names", + "Write debug names to the generated binary file", + []() { s_write_binary_options.write_debug_names = true; }); + parser.AddOption("no-check", "Don't check for invalid modules", + []() { s_validate = false; }); + parser.AddArgument("filename", OptionParser::ArgumentCount::One, + [](const char* argument) { s_infile = argument; }); + + parser.Parse(argc, argv); +} + +static void WriteBufferToFile(string_view filename, + const OutputBuffer& buffer) { + if (s_dump_module) { + std::unique_ptr<FileStream> stream = FileStream::CreateStdout(); + if (s_verbose) { + stream->Writef(";; dump\n"); + } + if (!buffer.data.empty()) { + stream->WriteMemoryDump(buffer.data.data(), buffer.data.size()); + } + } + + buffer.WriteToFile(filename); +} + +static std::string DefaultOuputName(string_view input_name) { + // Strip existing extension and add .wasm + std::string result(StripExtension(GetBasename(input_name))); + result += kWasmExtension; + + return result; +} + +int ProgramMain(int argc, char** argv) { + InitStdio(); + + ParseOptions(argc, argv); + + std::vector<uint8_t> file_data; + Result result = ReadFile(s_infile, &file_data); + std::unique_ptr<WastLexer> lexer = WastLexer::CreateBufferLexer( + s_infile, file_data.data(), file_data.size()); + if (Failed(result)) { + WABT_FATAL("unable to read file: %s\n", s_infile); + } + + Errors errors; + std::unique_ptr<Module> module; + WastParseOptions parse_wast_options(s_features); + result = ParseWatModule(lexer.get(), &module, &errors, &parse_wast_options); + + if (Succeeded(result) && s_validate) { + ValidateOptions options(s_features); + result = ValidateModule(module.get(), &errors, options); + } + + if (Succeeded(result)) { + MemoryStream stream(s_log_stream.get()); + s_write_binary_options.features = s_features; + result = WriteBinaryModule(&stream, module.get(), s_write_binary_options); + + if (Succeeded(result)) { + if (s_outfile.empty()) { + s_outfile = DefaultOuputName(s_infile); + } + WriteBufferToFile(s_outfile.c_str(), stream.output_buffer()); + } + } + + auto line_finder = lexer->MakeLineFinder(); + FormatErrorsToFile(errors, Location::Type::Text, line_finder.get()); + + 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..3208acdff8 --- /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 "src/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/tracing.h b/third_party/wasm2c/src/tracing.h new file mode 100644 index 0000000000..86daf63cf1 --- /dev/null +++ b/third_party/wasm2c/src/tracing.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#ifndef WABT_TRACING_H_ +#define WABT_TRACING_H_ + +// Provides a simple tracing class that automatically generates enter/exit +// messages using the scope of the instance. +// +// It also assumes that this file is only included in .cc files. +// Immediately before the inclusion of this file, there is a define of +// for WABT_TRACING, defining whether tracing should be compiled in for +// that source file. + +#ifndef WABT_TRACING +#define WABT_TRACING 0 +#endif + +#include "src/common.h" + +namespace wabt { + +#if WABT_TRACING + +// Scoped class that automatically prints enter("->") and exit("<-") +// lines, indented by trace level. +struct TraceScope { + WABT_DISALLOW_COPY_AND_ASSIGN(TraceScope); + TraceScope() = delete; + TraceScope(const char* method); + template <typename... Args> + TraceScope(const char* method, const char* format, Args&&... args) + : method_(method) { + PrintEnter(method); + fprintf(stderr, format, std::forward<Args>(args)...); + PrintNewline(); + } + ~TraceScope(); + + private: + const char* method_; + void PrintEnter(const char* method); + void PrintNewline(); +}; + +#define WABT_TRACE(method_name) TraceScope _func_(#method_name) + +#define WABT_TRACE_ARGS(method_name, format, ...) \ + TraceScope _func_(#method_name, format, __VA_ARGS__) + +#else + +#define WABT_TRACE(method) +#define WABT_TRACE_ARGS(method_name, format, ...) + +#endif + +} // end namespace wabt + +#endif // WABT_TRACING_H_ diff --git a/third_party/wasm2c/src/type-checker.cc b/third_party/wasm2c/src/type-checker.cc new file mode 100644 index 0000000000..3e875a312f --- /dev/null +++ b/third_party/wasm2c/src/type-checker.cc @@ -0,0 +1,941 @@ +/* + * 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 "src/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::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; + PrintStackIfFailed(result, desc); + return result; +} + +Result TypeChecker::CheckType(Type actual, Type expected) { + if (expected == Type::Any || actual == Type::Any) { + return Result::Ok; + } + 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; +} + +Result TypeChecker::CheckOpcode1(Opcode opcode, + const Limits* limits, + bool has_address_operands) { + Result result = + PopAndCheck1Type(opcode.GetMemoryParam( + opcode.GetParamType1(), limits, + has_address_operands || opcode.GetMemorySize() != 0), + opcode.GetName()); + PushType(has_address_operands + ? opcode.GetMemoryParam(opcode.GetResultType(), limits, true) + : opcode.GetResultType()); + return result; +} + +Result TypeChecker::CheckOpcode2(Opcode opcode, const Limits* limits) { + Result result = + PopAndCheck2Types(opcode.GetMemoryParam(opcode.GetParamType1(), limits, + opcode.GetMemorySize() != 0), + opcode.GetParamType2(), opcode.GetName()); + PushType(opcode.GetResultType()); + return result; +} + +Result TypeChecker::CheckOpcode3(Opcode opcode, + const Limits* limits1, + const Limits* limits2, + const Limits* limits3) { + bool has_address_operands = limits1 || limits2 || limits3; + Result result = + PopAndCheck3Types(opcode.GetMemoryParam(opcode.GetParamType1(), limits1, + has_address_operands), + opcode.GetMemoryParam(opcode.GetParamType2(), limits2, + has_address_operands), + opcode.GetMemoryParam(opcode.GetParamType3(), limits3, + has_address_operands), + opcode.GetName()); + PushType(opcode.GetResultType()); + return result; +} + +void TypeChecker::PrintStackIfFailed(Result result, + const char* desc, + const TypeVector& expected) { + 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 "; + 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_ != label_sig) { + 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::OnFuncRef(Index* out_index) { + Type type; + Result result = PeekType(0, &type); + if (!type.IsIndex()) { + TypeVector actual; + if (Succeeded(result)) { + 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.GetIndex(); + } + 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)); + if (Failed(Check2LabelTypes(label, LabelType::Try, LabelType::Func))) { + PrintError("try-delegate must target a try block or function label"); + result = Result::Error; + } + + 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", "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& limits) { + return CheckOpcode3(Opcode::MemoryCopy, &limits, &limits, &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) { + return CheckOpcode1(Opcode::MemoryGrow, &limits, true); +} + +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_index) { + if (features_.function_references_enabled()) { + PushType(Type(func_index)); + } 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.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; +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/type-checker.h b/third_party/wasm2c/src/type-checker.h new file mode 100644 index 0000000000..b7cdf82dd7 --- /dev/null +++ b/third_party/wasm2c/src/type-checker.h @@ -0,0 +1,196 @@ +/* + * 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. + */ + +#ifndef WABT_TYPE_CHECKER_H_ +#define WABT_TYPE_CHECKER_H_ + +#include <functional> +#include <vector> + +#include "src/common.h" +#include "src/feature.h" +#include "src/opcode.h" + +namespace wabt { + +class TypeChecker { + public: + typedef std::function<void(const char* msg)> ErrorCallback; + + struct Label { + Label(LabelType, + const TypeVector& param_types, + const TypeVector& result_types, + size_t limit); + + TypeVector& br_types() { + return label_type == LabelType::Loop ? param_types : result_types; + } + + LabelType label_type; + TypeVector param_types; + TypeVector result_types; + size_t type_stack_limit; + bool unreachable; + }; + + explicit TypeChecker(const Features& features) : features_(features) {} + + void set_error_callback(const ErrorCallback& error_callback) { + error_callback_ = error_callback; + } + + size_t type_stack_size() const { return type_stack_.size(); } + + bool IsUnreachable(); + Result GetLabel(Index depth, Label** out_label); + Result GetRethrowLabel(Index depth, Label** out_label); + + Result BeginFunction(const TypeVector& sig); + Result OnAtomicFence(uint32_t consistency_model); + Result OnAtomicLoad(Opcode, const Limits& limits); + Result OnAtomicNotify(Opcode, const Limits& limits); + Result OnAtomicStore(Opcode, const Limits& limits); + Result OnAtomicRmw(Opcode, const Limits& limits); + Result OnAtomicRmwCmpxchg(Opcode, const Limits& limits); + Result OnAtomicWait(Opcode, const Limits& limits); + Result OnBinary(Opcode); + Result OnBlock(const TypeVector& param_types, const TypeVector& result_types); + Result OnBr(Index depth); + Result OnBrIf(Index depth); + Result BeginBrTable(); + Result OnBrTableTarget(Index depth); + Result EndBrTable(); + Result OnCall(const TypeVector& param_types, const TypeVector& result_types); + Result OnCallIndirect(const TypeVector& param_types, + const TypeVector& result_types); + Result OnFuncRef(Index* out_index); + Result OnReturnCall(const TypeVector& param_types, const TypeVector& result_types); + Result OnReturnCallIndirect(const TypeVector& param_types, const TypeVector& result_types); + Result OnCatch(const TypeVector& sig); + Result OnCompare(Opcode); + Result OnConst(Type); + Result OnConvert(Opcode); + Result OnDelegate(Index depth); + Result OnDrop(); + Result OnElse(); + Result OnEnd(); + Result OnGlobalGet(Type); + Result OnGlobalSet(Type); + Result OnIf(const TypeVector& param_types, const TypeVector& result_types); + Result OnLoad(Opcode, const Limits& limits); + Result OnLocalGet(Type); + Result OnLocalSet(Type); + Result OnLocalTee(Type); + Result OnLoop(const TypeVector& param_types, const TypeVector& result_types); + Result OnMemoryCopy(const Limits& limits); + Result OnDataDrop(Index); + Result OnMemoryFill(const Limits& limits); + Result OnMemoryGrow(const Limits& limits); + Result OnMemoryInit(Index, const Limits& limits); + Result OnMemorySize(const Limits& limits); + Result OnTableCopy(); + Result OnElemDrop(Index); + Result OnTableInit(Index, Index); + Result OnTableGet(Type elem_type); + Result OnTableSet(Type elem_type); + Result OnTableGrow(Type elem_type); + Result OnTableSize(); + Result OnTableFill(Type elem_type); + Result OnRefFuncExpr(Index func_index); + Result OnRefNullExpr(Type type); + Result OnRefIsNullExpr(); + Result OnRethrow(Index depth); + Result OnReturn(); + Result OnSelect(const TypeVector& result_types); + Result OnSimdLaneOp(Opcode, uint64_t); + Result OnSimdLoadLane(Opcode, const Limits& limits, uint64_t); + Result OnSimdStoreLane(Opcode, const Limits& limits, uint64_t); + Result OnSimdShuffleOp(Opcode, v128); + Result OnStore(Opcode, const Limits& limits); + Result OnTernary(Opcode); + Result OnThrow(const TypeVector& sig); + Result OnTry(const TypeVector& param_types, const TypeVector& result_types); + Result OnUnary(Opcode); + Result OnUnreachable(); + Result EndFunction(); + + static Result CheckType(Type actual, Type expected); + + private: + void WABT_PRINTF_FORMAT(2, 3) PrintError(const char* fmt, ...); + Result TopLabel(Label** out_label); + void ResetTypeStackToLabel(Label* label); + Result SetUnreachable(); + void PushLabel(LabelType label_type, + const TypeVector& param_types, + const TypeVector& result_types); + Result PopLabel(); + Result CheckLabelType(Label* label, LabelType label_type); + Result Check2LabelTypes(Label* label, LabelType label_type1, LabelType label_type2); + Result GetThisFunctionLabel(Label **label); + Result PeekType(Index depth, Type* out_type); + Result PeekAndCheckType(Index depth, Type expected); + Result DropTypes(size_t drop_count); + void PushType(Type type); + void PushTypes(const TypeVector& types); + Result CheckTypeStackEnd(const char* desc); + Result CheckTypes(const TypeVector &actual, const TypeVector &expected); + Result CheckSignature(const TypeVector& sig, const char* desc); + Result CheckReturnSignature(const TypeVector& sig, const TypeVector &expected,const char *desc); + Result PopAndCheckSignature(const TypeVector& sig, const char* desc); + Result PopAndCheckCall(const TypeVector& param_types, + const TypeVector& result_types, + const char* desc); + Result PopAndCheck1Type(Type expected, const char* desc); + Result PopAndCheck2Types(Type expected1, Type expected2, const char* desc); + Result PopAndCheck3Types(Type expected1, + Type expected2, + Type expected3, + const char* desc); + Result CheckOpcode1(Opcode opcode, + const Limits* limits = nullptr, + bool has_address_operands = false); + Result CheckOpcode2(Opcode opcode, const Limits* limits = nullptr); + Result CheckOpcode3(Opcode opcode, + const Limits* limits1 = nullptr, + const Limits* limits2 = nullptr, + const Limits* limits3 = nullptr); + Result OnEnd(Label* label, const char* sig_desc, const char* end_desc); + + template <typename... Args> + void PrintStackIfFailed(Result result, const char* desc, Args... args) { + // Minor optimization, check result before constructing the vector to pass + // to the other overload of PrintStackIfFailed. + if (Failed(result)) { + PrintStackIfFailed(result, desc, {args...}); + } + } + + void PrintStackIfFailed(Result, const char* desc, const TypeVector&); + + ErrorCallback error_callback_; + TypeVector type_stack_; + std::vector<Label> label_stack_; + // Cache the expected br_table signature. It will be initialized to `nullptr` + // to represent "any". + TypeVector* br_table_sig_ = nullptr; + Features features_; +}; + +} // namespace wabt + +#endif /* WABT_TYPE_CHECKER_H_ */ diff --git a/third_party/wasm2c/src/type.h b/third_party/wasm2c/src/type.h new file mode 100644 index 0000000000..cd383a16ef --- /dev/null +++ b/third_party/wasm2c/src/type.h @@ -0,0 +1,145 @@ +/* + * 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. + */ + +#ifndef WABT_TYPE_H_ +#define WABT_TYPE_H_ + +#include <cassert> +#include <cstdint> +#include <vector> + +#include "config.h" + +namespace wabt { + +class Type; + +using Index = uint32_t; +using TypeVector = std::vector<Type>; + +class Type { + public: + // Matches binary format, do not change. + enum Enum : int32_t { + I32 = -0x01, // 0x7f + I64 = -0x02, // 0x7e + F32 = -0x03, // 0x7d + F64 = -0x04, // 0x7c + V128 = -0x05, // 0x7b + I8 = -0x06, // 0x7a : packed-type only, used in gc and as v128 lane + I16 = -0x07, // 0x79 : packed-type only, used in gc and as v128 lane + FuncRef = -0x10, // 0x70 + ExternRef = -0x11, // 0x6f + Func = -0x20, // 0x60 + Struct = -0x21, // 0x5f + Array = -0x22, // 0x5e + Void = -0x40, // 0x40 + ___ = Void, // Convenient for the opcode table in opcode.h + + Any = 0, // Not actually specified, but useful for type-checking + I8U = 4, // Not actually specified, but used internally with load/store + I16U = 6, // Not actually specified, but used internally with load/store + I32U = 7, // Not actually specified, but used internally with load/store + }; + + Type() = default; // Provided so Type can be member of a union. + Type(int32_t code) : enum_(static_cast<Enum>(code)) {} + Type(Enum e) : enum_(e) {} + operator Enum() const { return enum_; } + + bool IsRef() const { + return enum_ == Type::ExternRef || enum_ == Type::FuncRef; + } + + bool IsNullableRef() const { + // Currently all reftypes are nullable + return IsRef(); + } + + const char* GetName() const { + switch (enum_) { + case Type::I32: return "i32"; + case Type::I64: return "i64"; + case Type::F32: return "f32"; + case Type::F64: return "f64"; + case Type::V128: return "v128"; + case Type::I8: return "i8"; + case Type::I16: return "i16"; + case Type::FuncRef: return "funcref"; + case Type::Func: return "func"; + case Type::Void: return "void"; + case Type::Any: return "any"; + case Type::ExternRef: return "externref"; + default: return "<type_index>"; + } + } + + const char* GetRefKindName() const { + switch (enum_) { + case Type::FuncRef: return "func"; + case Type::ExternRef: return "extern"; + case Type::Struct: return "struct"; + case Type::Array: return "array"; + default: return "<invalid>"; + } + } + + // Functions for handling types that are an index into the type section. + // These are always positive integers. They occur in the binary format in + // block signatures, e.g. + // + // (block (result i32 i64) ...) + // + // is encoded as + // + // (type $T (func (result i32 i64))) + // ... + // (block (type $T) ...) + // + bool IsIndex() const { return static_cast<int32_t>(enum_) >= 0; } + + Index GetIndex() const { + assert(IsIndex()); + return static_cast<Index>(enum_); + } + + TypeVector GetInlineVector() const { + assert(!IsIndex()); + switch (enum_) { + case Type::Void: + return TypeVector(); + + case Type::I32: + case Type::I64: + case Type::F32: + case Type::F64: + case Type::V128: + case Type::FuncRef: + case Type::ExternRef: + return TypeVector(this, this + 1); + + default: + WABT_UNREACHABLE; + } + } + + private: + Enum enum_; +}; + +} // namespace wabt + +#endif // WABT_TYPE_H_ diff --git a/third_party/wasm2c/src/utf8.cc b/third_party/wasm2c/src/utf8.cc new file mode 100644 index 0000000000..37a8df0081 --- /dev/null +++ b/third_party/wasm2c/src/utf8.cc @@ -0,0 +1,104 @@ +/* + * 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 "src/utf8.h" + +#include <cstdint> + +namespace wabt { + +namespace { + +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 +}; + +// 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/utf8.h b/third_party/wasm2c/src/utf8.h new file mode 100644 index 0000000000..c378035762 --- /dev/null +++ b/third_party/wasm2c/src/utf8.h @@ -0,0 +1,28 @@ +/* + * 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. + */ + +#ifndef WABT_UTF8_H_ +#define WABT_UTF8_H_ + +#include <stdlib.h> + +namespace wabt { + +bool IsValidUtf8(const char* s, size_t length); + +} // namespace wabt + +#endif // WABT_UTF8_H_ diff --git a/third_party/wasm2c/src/validator.cc b/third_party/wasm2c/src/validator.cc new file mode 100644 index 0000000000..d78219ee1c --- /dev/null +++ b/third_party/wasm2c/src/validator.cc @@ -0,0 +1,1072 @@ +/* + * 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 "src/validator.h" + +#include <cassert> +#include <cinttypes> +#include <cstdarg> +#include <cstdio> + +#include "config.h" + +#include "src/binary-reader.h" +#include "src/cast.h" +#include "src/expr-visitor.h" +#include "src/ir.h" +#include "src/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); + + 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 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(), expected.GetName()); + } +} + +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()); + } +} + +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}; + return Result::Ok; + } + + return Result::Error; +} + +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->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); + 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); + return Result::Ok; +} + +Result Validator::OnMemoryGrowExpr(MemoryGrowExpr* expr) { + result_ |= validator_.OnMemoryGrow(expr->loc); + return Result::Ok; +} + +Result Validator::OnMemoryInitExpr(MemoryInitExpr* expr) { + result_ |= validator_.OnMemoryInit(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnMemorySizeExpr(MemorySizeExpr* expr) { + result_ |= validator_.OnMemorySize(expr->loc); + 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->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->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->opcode.GetAlignment(expr->align)); + return Result::Ok; +} + +Result Validator::OnAtomicLoadExpr(AtomicLoadExpr* expr) { + result_ |= validator_.OnAtomicLoad(expr->loc, expr->opcode, + expr->opcode.GetAlignment(expr->align)); + return Result::Ok; +} + +Result Validator::OnAtomicStoreExpr(AtomicStoreExpr* expr) { + result_ |= validator_.OnAtomicStore(expr->loc, expr->opcode, + expr->opcode.GetAlignment(expr->align)); + return Result::Ok; +} + +Result Validator::OnAtomicRmwExpr(AtomicRmwExpr* expr) { + result_ |= validator_.OnAtomicRmw(expr->loc, expr->opcode, + expr->opcode.GetAlignment(expr->align)); + return Result::Ok; +} + +Result Validator::OnAtomicRmwCmpxchgExpr(AtomicRmwCmpxchgExpr* expr) { + result_ |= validator_.OnAtomicRmwCmpxchg( + expr->loc, expr->opcode, 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->opcode.GetAlignment(expr->align), + expr->val); + return Result::Ok; +} + +Result Validator::OnSimdStoreLaneExpr(SimdStoreLaneExpr* expr) { + result_ |= validator_.OnSimdStoreLane(expr->loc, expr->opcode, + 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->opcode.GetAlignment(expr->align)); + return Result::Ok; +} + +Result Validator::OnLoadZeroExpr(LoadZeroExpr* expr) { + result_ |= validator_.OnLoadZero(expr->loc, expr->opcode, + 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()); + 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_); + + if (f->global.init_expr.size() == 1) { + const Expr* expr = &f->global.init_expr.front(); + + switch (expr->type()) { + case ExprType::Const: + result_ |= validator_.OnGlobalInitExpr_Const( + expr->loc, cast<ConstExpr>(expr)->const_.type()); + break; + + case ExprType::GlobalGet: { + Var var = cast<GlobalGetExpr>(expr)->var; + result_ |= validator_.OnGlobalInitExpr_GlobalGet(expr->loc, var); + break; + } + + case ExprType::RefFunc: + result_ |= validator_.OnGlobalInitExpr_RefFunc( + expr->loc, cast<RefFuncExpr>(expr)->var); + break; + + case ExprType::RefNull: + result_ |= validator_.OnGlobalInitExpr_RefNull( + expr->loc, cast<RefNullExpr>(expr)->type); + break; + + default: + result_ |= validator_.OnGlobalInitExpr_Other(field.loc); + break; + } + } else { + result_ |= validator_.OnGlobalInitExpr_Other(field.loc); + } + } + } + + // 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); + + validator_.OnElemSegmentElemType(f->elem_segment.elem_type); + + // Init expr. + if (f->elem_segment.offset.size() == 1) { + const Expr* expr = &f->elem_segment.offset.front(); + + switch (expr->type()) { + case ExprType::Const: + result_ |= validator_.OnElemSegmentInitExpr_Const( + expr->loc, cast<ConstExpr>(expr)->const_.type()); + break; + + case ExprType::GlobalGet: { + Var var = cast<GlobalGetExpr>(expr)->var; + result_ |= + validator_.OnElemSegmentInitExpr_GlobalGet(expr->loc, var); + break; + } + + default: + result_ |= validator_.OnElemSegmentInitExpr_Other(field.loc); + break; + } + } else if (f->elem_segment.offset.size() > 1) { + result_ |= validator_.OnElemSegmentInitExpr_Other(field.loc); + } + + // Element expr. + for (auto&& elem_expr : f->elem_segment.elem_exprs) { + switch (elem_expr.kind) { + case ElemExprKind::RefNull: + // TODO: better location? + result_ |= validator_.OnElemSegmentElemExpr_RefNull(field.loc, + elem_expr.type); + break; + + case ElemExprKind::RefFunc: + result_ |= validator_.OnElemSegmentElemExpr_RefFunc( + elem_expr.var.loc, elem_expr.var); + break; + } + } + } + } + + // 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)) { + result_ |= validator_.BeginFunctionBody(field.loc, func_index++); + + for (auto&& decl : f->func.local_types.decls()) { + // TODO: Better location? + result_ |= validator_.OnLocalDecl(field.loc, decl.second, decl.first); + } + + ExprVisitor visitor(this); + result_ |= visitor.VisitExprList(const_cast<ExprList&>(f->func.exprs)); + result_ |= validator_.EndFunctionBody(field.loc); + } + } + + // 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.offset.size() == 1) { + const Expr* expr = &f->data_segment.offset.front(); + + switch (expr->type()) { + case ExprType::Const: + result_ |= validator_.OnDataSegmentInitExpr_Const( + expr->loc, cast<ConstExpr>(expr)->const_.type()); + break; + + case ExprType::GlobalGet: { + Var var = cast<GlobalGetExpr>(expr)->var; + result_ |= + validator_.OnDataSegmentInitExpr_GlobalGet(expr->loc, var); + break; + } + + default: + result_ |= validator_.OnDataSegmentInitExpr_Other(field.loc); + break; + } + } else if (f->data_segment.offset.size() > 1) { + result_ |= validator_.OnDataSegmentInitExpr_Other(field.loc); + } + } + } + + 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::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); + // 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 : assert_return_command->expected) { + actual_types.push_back(ex.type()); + } + switch (result.kind) { + case ActionResult::Kind::Types: + CheckResultTypes(&action->loc, actual_types, *result.types, "action"); + break; + + case ActionResult::Kind::Type: + CheckResultTypes(&action->loc, actual_types, {result.type}, "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; + } +} + +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/validator.h b/third_party/wasm2c/src/validator.h new file mode 100644 index 0000000000..b84acbc945 --- /dev/null +++ b/third_party/wasm2c/src/validator.h @@ -0,0 +1,36 @@ +/* + * 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_VALIDATOR_H_ +#define WABT_VALIDATOR_H_ + +#include "src/error.h" +#include "src/feature.h" +#include "src/shared-validator.h" + +namespace wabt { + +struct Module; +struct Script; + +// Perform all checks on the script. It is valid if and only if this function +// succeeds. +Result ValidateScript(const Script*, Errors*, const ValidateOptions&); +Result ValidateModule(const Module*, Errors*, const ValidateOptions&); + +} // namespace wabt + +#endif // WABT_VALIDATOR_H_ diff --git a/third_party/wasm2c/src/wabt.post.js b/third_party/wasm2c/src/wabt.post.js new file mode 100644 index 0000000000..c71e9d038a --- /dev/null +++ b/third_party/wasm2c/src/wabt.post.js @@ -0,0 +1,376 @@ +/* + * 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; +var FEATURES = [ + 'exceptions', + 'mutable_globals', + 'sat_float_to_int', + 'sign_extension', + 'simd', + 'threads', + 'multi_value', + 'tail_call', + 'bulk_memory', + 'reference_types', +]; + +/// 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 (var i = 0; i < FEATURES.length; ++i) { + var feature = FEATURES[i]; + this[feature] = obj[feature] | 0; + } +} +Features.prototype = Object.create(Object.prototype); + +Features.prototype.destroy = function() { + Module._wabt_destroy_features(this.addr); +}; + +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) { + 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); +} +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, lexer) { + this.kind = kind; + this.addr = Module._wabt_new_errors(); + this.lexer = lexer; +} +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 lexer = new Lexer(filename, buffer); + var errors = new Errors('text', 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; diff --git a/third_party/wasm2c/src/wasm2c.c.tmpl b/third_party/wasm2c/src/wasm2c.c.tmpl new file mode 100644 index 0000000000..6539ddc8cc --- /dev/null +++ b/third_party/wasm2c/src/wasm2c.c.tmpl @@ -0,0 +1,433 @@ +%%includes +/* Automically generated by wasm2c */ +#include <math.h> +#include <string.h> +#include <stdlib.h> +%%declarations +#if defined(_MSC_VER) +# define UNLIKELY(x) (x) +# define LIKELY(x) (x) +#else +# define UNLIKELY(x) __builtin_expect(!!(x), 0) +# define LIKELY(x) __builtin_expect(!!(x), 1) +#endif + +#define TRAP(x) (wasm_rt_trap(WASM_RT_TRAP_##x), 0) + +#ifndef FUNC_PROLOGUE +#define FUNC_PROLOGUE +#endif + +#ifndef FUNC_EPILOGUE +#define FUNC_EPILOGUE +#endif + +#ifdef EXTERNAL_CALLBACK_PROLOGUE +#define EXTERNAL_CALLBACK_PROLOGUE_EXEC(table, x) \ + if (UNLIKELY(table.data[x].func_class == WASM_RT_EXTERNAL_FUNCTION)) { \ + EXTERNAL_CALLBACK_PROLOGUE; \ + } +#else +#define EXTERNAL_CALLBACK_PROLOGUE_EXEC(table, x) +#endif + +#ifdef EXTERNAL_CALLBACK_EPILOGUE +#define EXTERNAL_CALLBACK_EPILOGUE_EXEC(table, x) \ + if (UNLIKELY(table.data[x].func_class == WASM_RT_EXTERNAL_FUNCTION)) { \ + EXTERNAL_CALLBACK_EPILOGUE; \ + } +#else +#define EXTERNAL_CALLBACK_EPILOGUE_EXEC(table, x) +#endif + +#define UNREACHABLE (void) TRAP(UNREACHABLE) + +#define CALL_INDIRECT_VOID(table, t, ft, x, func_types, ...) \ + if (LIKELY((x) < table.size && table.data[x].func && table.data[x].func_type == func_types[ft])) { \ + EXTERNAL_CALLBACK_PROLOGUE_EXEC(table, x); \ + ((t)table.data[x].func)(__VA_ARGS__); \ + EXTERNAL_CALLBACK_EPILOGUE_EXEC(table, x); \ + } else { \ + wasm_rt_callback_error_trap(&table, x, func_types[ft]); \ + } + +#define CALL_INDIRECT_RES(res, table, t, ft, x, func_types, ...) \ + if (LIKELY((x) < table.size && table.data[x].func && table.data[x].func_type == func_types[ft])) { \ + EXTERNAL_CALLBACK_PROLOGUE_EXEC(table, x); \ + res = ((t)table.data[x].func)(__VA_ARGS__); \ + EXTERNAL_CALLBACK_EPILOGUE_EXEC(table, x); \ + } else { \ + wasm_rt_callback_error_trap(&table, x, func_types[ft]); \ + } + +#if defined(WASM2C_MALLOC_FAIL_CALLBACK) +void WASM2C_MALLOC_FAIL_CALLBACK(u32 ptr_size); +# define WASM2C_MALLOC_FAIL_CHECK(ptr, ptr_size) \ + if (!ptr) { \ + WASM2C_MALLOC_FAIL_CALLBACK(ptr_size); \ + } +#else +# define WASM2C_MALLOC_FAIL_CHECK(ptr, ptr_size) +#endif + +#if defined(WASM_CHECK_SHADOW_MEMORY) +# define WASM2C_SHADOW_MEMORY_LOAD(mem, func_name, ptr, ptr_size) wasm2c_shadow_memory_load(mem, func_name, ptr, ptr_size) +# define WASM2C_SHADOW_MEMORY_STORE(mem, func_name, ptr, ptr_size) wasm2c_shadow_memory_store(mem, func_name, ptr, ptr_size) +# define WASM2C_SHADOW_MEMORY_RESERVE(mem, ptr, ptr_size) wasm2c_shadow_memory_reserve(mem, ptr, ptr_size) +# define WASM2C_SHADOW_MEMORY_DLMALLOC(mem, ptr, ptr_size) wasm2c_shadow_memory_dlmalloc(mem, ptr, ptr_size) +# define WASM2C_SHADOW_MEMORY_DLFREE(mem, ptr) wasm2c_shadow_memory_dlfree(mem, ptr) +# define WASM2C_SHADOW_MEMORY_MARK_GLOBALS_HEAP_BOUNDARY(mem, ptr) wasm2c_shadow_memory_mark_globals_heap_boundary(mem, ptr) +#else +# define WASM2C_SHADOW_MEMORY_LOAD(mem, func_name, ptr, ptr_size) +# define WASM2C_SHADOW_MEMORY_STORE(mem, func_name, ptr, ptr_size) +# define WASM2C_SHADOW_MEMORY_RESERVE(mem, ptr, ptr_size) +# define WASM2C_SHADOW_MEMORY_DLMALLOC(mem, ptr, ptr_size) +# define WASM2C_SHADOW_MEMORY_DLFREE(mem, ptr) +# define WASM2C_SHADOW_MEMORY_MARK_GLOBALS_HEAP_BOUNDARY(mem, ptr) +#endif + +#ifdef WASM_USE_GUARD_PAGES +# define MEMCHECK(mem, a, t) +#else +# define MEMCHECK(mem, a, t) if (UNLIKELY((a) + sizeof(t) > mem->size)) { (void) TRAP(OOB); } +#endif + +#if defined(WASM_USE_GUARD_PAGES) && UINTPTR_MAX == 0xffffffff +// on 32-bit platforms we have to mask memory access into range +# define MEM_ACCESS_REF(mem, addr) &mem->data[addr & mem->mem_mask] +#else +# define MEM_ACCESS_REF(mem, addr) &mem->data[addr] +#endif + +#if defined(WASM_USING_GLOBAL_HEAP) +# undef MEM_ACCESS_REF +# define MEM_ACCESS_REF(mem, addr) (char*) addr +#endif + +#ifdef __GNUC__ +#define wasm_asm __asm__ +#else +#define wasm_asm(X) +#endif + +#if WABT_BIG_ENDIAN +static inline void load_data(void *dest, const void *src, size_t n) { + size_t i = 0; + u8 *dest_chars = dest; + memcpy(dest, src, n); + for (i = 0; i < (n>>1); i++) { + u8 cursor = dest_chars[i]; + dest_chars[i] = dest_chars[n - i - 1]; + dest_chars[n - i - 1] = cursor; + } +} +#define LOAD_DATA(m, o, i, s) { load_data(&(m.data[m.size - o - s]), i, s); \ + WASM2C_SHADOW_MEMORY_RESERVE(&m, m.size - o - s, s); \ + WASM2C_SHADOW_MEMORY_STORE(&m, "GlobalDataLoad", m.size - o - s, s); \ +} + +#define DEFINE_LOAD(name, t1, t2, t3) \ + static inline t3 name(wasm_rt_memory_t* mem, u64 addr, const char* func_name) { \ + MEMCHECK(mem, addr, t1); \ + t1 result; \ + memcpy(&result, MEM_ACCESS_REF(mem, mem->size - addr - sizeof(t1)), sizeof(t1)); \ + WASM2C_SHADOW_MEMORY_LOAD(mem, func_name, mem->size - addr - sizeof(t1), sizeof(t1)); \ + wasm_asm("" ::"r"(result)); \ + return (t3)(t2)result; \ + } + +#define DEFINE_STORE(name, t1, t2) \ + static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value, const char* func_name) { \ + MEMCHECK(mem, addr, t1); \ + t1 wrapped = (t1)value; \ + memcpy(MEM_ACCESS_REF(mem, mem->size - addr - sizeof(t1), &wrapped, sizeof(t1)); \ + WASM2C_SHADOW_MEMORY_STORE(mem, func_name, mem->size - addr - sizeof(t1)), sizeof(t1)); \ + } +#else +static inline void load_data(void *dest, const void *src, size_t n) { + memcpy(dest, src, n); +} +#define LOAD_DATA(m, o, i, s) { load_data(&(m.data[o]), i, s); \ + WASM2C_SHADOW_MEMORY_RESERVE(&m, o, s); \ + WASM2C_SHADOW_MEMORY_STORE(&m, "GlobalDataLoad", o, s); \ +} + +#define DEFINE_LOAD(name, t1, t2, t3) \ + static inline t3 name(wasm_rt_memory_t* mem, u64 addr, const char* func_name) { \ + MEMCHECK(mem, addr, t1); \ + t1 result; \ + memcpy(&result, MEM_ACCESS_REF(mem, addr), sizeof(t1)); \ + WASM2C_SHADOW_MEMORY_LOAD(mem, func_name, addr, sizeof(t1)); \ + wasm_asm("" ::"r"(result)); \ + return (t3)(t2)result; \ + } + +#define DEFINE_STORE(name, t1, t2) \ + static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value, const char* func_name) { \ + MEMCHECK(mem, addr, t1); \ + t1 wrapped = (t1)value; \ + memcpy(MEM_ACCESS_REF(mem, addr), &wrapped, sizeof(t1)); \ + WASM2C_SHADOW_MEMORY_STORE(mem, func_name, addr, sizeof(t1)); \ + } +#endif + +DEFINE_LOAD(i32_load, u32, u32, u32); +DEFINE_LOAD(i64_load, u64, u64, u64); +DEFINE_LOAD(f32_load, f32, f32, f32); +DEFINE_LOAD(f64_load, f64, f64, f64); +DEFINE_LOAD(i32_load8_s, s8, s32, u32); +DEFINE_LOAD(i64_load8_s, s8, s64, u64); +DEFINE_LOAD(i32_load8_u, u8, u32, u32); +DEFINE_LOAD(i64_load8_u, u8, u64, u64); +DEFINE_LOAD(i32_load16_s, s16, s32, u32); +DEFINE_LOAD(i64_load16_s, s16, s64, u64); +DEFINE_LOAD(i32_load16_u, u16, u32, u32); +DEFINE_LOAD(i64_load16_u, u16, u64, u64); +DEFINE_LOAD(i64_load32_s, s32, s64, u64); +DEFINE_LOAD(i64_load32_u, u32, u64, u64); +DEFINE_STORE(i32_store, u32, u32); +DEFINE_STORE(i64_store, u64, u64); +DEFINE_STORE(f32_store, f32, f32); +DEFINE_STORE(f64_store, f64, f64); +DEFINE_STORE(i32_store8, u8, u32); +DEFINE_STORE(i32_store16, u16, u32); +DEFINE_STORE(i64_store8, u8, u64); +DEFINE_STORE(i64_store16, u16, u64); +DEFINE_STORE(i64_store32, u32, u64); + +#if defined(_MSC_VER) +#include <intrin.h> + +// Adapted from https://github.com/nemequ/portable-snippets/blob/master/builtin/builtin.h + +static inline int I64_CLZ(unsigned long long v) { + unsigned long r = 0; +#if defined(_M_AMD64) || defined(_M_ARM) + if (_BitScanReverse64(&r, v)) { + return 63 - r; + } +#else + if (_BitScanReverse(&r, (unsigned long) (v >> 32))) { + return 31 - r; + } else if (_BitScanReverse(&r, (unsigned long) v)) { + return 63 - r; + } +#endif + return 64; +} + +static inline int I32_CLZ(unsigned long v) { + unsigned long r = 0; + if (_BitScanReverse(&r, v)) { + return 31 - r; + } + return 32; +} + +static inline int I64_CTZ(unsigned long long v) { + if (!v) { + return 64; + } + unsigned long r = 0; +#if defined(_M_AMD64) || defined(_M_ARM) + _BitScanForward64(&r, v); + return (int) r; +#else + if (_BitScanForward(&r, (unsigned int) (v))) { + return (int) (r); + } + + _BitScanForward(&r, (unsigned int) (v >> 32)); + return (int) (r + 32); +#endif +} + +static inline int I32_CTZ(unsigned long v) { + if (!v) { + return 32; + } + unsigned long r = 0; + _BitScanForward(&r, v); + return (int) r; +} + +#define POPCOUNT_DEFINE_PORTABLE(f_n, T) \ + static inline u32 f_n(T x) { \ + x = x - ((x >> 1) & (T)~(T)0/3); \ + x = (x & (T)~(T)0/15*3) + ((x >> 2) & (T)~(T)0/15*3); \ + x = (x + (x >> 4)) & (T)~(T)0/255*15; \ + return (T)(x * ((T)~(T)0/255)) >> (sizeof(T) - 1) * 8; \ + } + +POPCOUNT_DEFINE_PORTABLE(I32_POPCNT, u32) +POPCOUNT_DEFINE_PORTABLE(I64_POPCNT, u64) + +#undef POPCOUNT_DEFINE_PORTABLE + +#else +# define I32_CLZ(x) ((x) ? __builtin_clz(x) : 32) +# define I64_CLZ(x) ((x) ? __builtin_clzll(x) : 64) +# define I32_CTZ(x) ((x) ? __builtin_ctz(x) : 32) +# define I64_CTZ(x) ((x) ? __builtin_ctzll(x) : 64) +# define I32_POPCNT(x) (__builtin_popcount(x)) +# define I64_POPCNT(x) (__builtin_popcountll(x)) +#endif + +#define DIV_S(ut, min, x, y) \ + ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \ + : (UNLIKELY((x) == min && (y) == -1)) ? TRAP(INT_OVERFLOW) \ + : (ut)((x) / (y))) + +#define REM_S(ut, min, x, y) \ + ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \ + : (UNLIKELY((x) == min && (y) == -1)) ? 0 \ + : (ut)((x) % (y))) + +#define I32_DIV_S(x, y) DIV_S(u32, INT32_MIN, (s32)x, (s32)y) +#define I64_DIV_S(x, y) DIV_S(u64, INT64_MIN, (s64)x, (s64)y) +#define I32_REM_S(x, y) REM_S(u32, INT32_MIN, (s32)x, (s32)y) +#define I64_REM_S(x, y) REM_S(u64, INT64_MIN, (s64)x, (s64)y) + +#define DIVREM_U(op, x, y) \ + ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) : ((x) op (y))) + +#define DIV_U(x, y) DIVREM_U(/, x, y) +#define REM_U(x, y) DIVREM_U(%, x, y) + +#define ROTL(x, y, mask) \ + (((x) << ((y) & (mask))) | ((x) >> (((mask) - (y) + 1) & (mask)))) +#define ROTR(x, y, mask) \ + (((x) >> ((y) & (mask))) | ((x) << (((mask) - (y) + 1) & (mask)))) + +#define I32_ROTL(x, y) ROTL(x, y, 31) +#define I64_ROTL(x, y) ROTL(x, y, 63) +#define I32_ROTR(x, y) ROTR(x, y, 31) +#define I64_ROTR(x, y) ROTR(x, y, 63) + +#define FMIN(x, y) \ + ((UNLIKELY((x) != (x))) ? NAN \ + : (UNLIKELY((y) != (y))) ? NAN \ + : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? x : y) \ + : (x < y) ? x : y) + +#define FMAX(x, y) \ + ((UNLIKELY((x) != (x))) ? NAN \ + : (UNLIKELY((y) != (y))) ? NAN \ + : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? y : x) \ + : (x > y) ? x : y) + +#define TRUNC_S(ut, st, ft, min, minop, max, x) \ + ((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \ + : (UNLIKELY(!((x)minop(min) && (x) < (max)))) ? TRAP(INT_OVERFLOW) \ + : (ut)(st)(x)) + +#define I32_TRUNC_S_F32(x) TRUNC_S(u32, s32, f32, (f32)INT32_MIN, >=, 2147483648.f, x) +#define I64_TRUNC_S_F32(x) TRUNC_S(u64, s64, f32, (f32)INT64_MIN, >=, (f32)INT64_MAX, x) +#define I32_TRUNC_S_F64(x) TRUNC_S(u32, s32, f64, -2147483649., >, 2147483648., x) +#define I64_TRUNC_S_F64(x) TRUNC_S(u64, s64, f64, (f64)INT64_MIN, >=, (f64)INT64_MAX, x) + +#define TRUNC_U(ut, ft, max, x) \ + ((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \ + : (UNLIKELY(!((x) > (ft)-1 && (x) < (max)))) ? TRAP(INT_OVERFLOW) \ + : (ut)(x)) + +#define I32_TRUNC_U_F32(x) TRUNC_U(u32, f32, 4294967296.f, x) +#define I64_TRUNC_U_F32(x) TRUNC_U(u64, f32, (f32)UINT64_MAX, x) +#define I32_TRUNC_U_F64(x) TRUNC_U(u32, f64, 4294967296., x) +#define I64_TRUNC_U_F64(x) TRUNC_U(u64, f64, (f64)UINT64_MAX, x) + +#define TRUNC_SAT_S(ut, st, ft, min, smin, minop, max, smax, x) \ + ((UNLIKELY((x) != (x))) ? 0 \ + : (UNLIKELY(!((x)minop(min)))) ? smin \ + : (UNLIKELY(!((x) < (max)))) ? smax \ + : (ut)(st)(x)) + +#define I32_TRUNC_SAT_S_F32(x) TRUNC_SAT_S(u32, s32, f32, (f32)INT32_MIN, INT32_MIN, >=, 2147483648.f, INT32_MAX, x) +#define I64_TRUNC_SAT_S_F32(x) TRUNC_SAT_S(u64, s64, f32, (f32)INT64_MIN, INT64_MIN, >=, (f32)INT64_MAX, INT64_MAX, x) +#define I32_TRUNC_SAT_S_F64(x) TRUNC_SAT_S(u32, s32, f64, -2147483649., INT32_MIN, >, 2147483648., INT32_MAX, x) +#define I64_TRUNC_SAT_S_F64(x) TRUNC_SAT_S(u64, s64, f64, (f64)INT64_MIN, INT64_MIN, >=, (f64)INT64_MAX, INT64_MAX, x) + +#define TRUNC_SAT_U(ut, ft, max, smax, x) \ + ((UNLIKELY((x) != (x))) ? 0 \ + : (UNLIKELY(!((x) > (ft)-1))) ? 0 \ + : (UNLIKELY(!((x) < (max)))) ? smax \ + : (ut)(x)) + +#define I32_TRUNC_SAT_U_F32(x) TRUNC_SAT_U(u32, f32, 4294967296.f, UINT32_MAX, x) +#define I64_TRUNC_SAT_U_F32(x) TRUNC_SAT_U(u64, f32, (f32)UINT64_MAX, UINT64_MAX, x) +#define I32_TRUNC_SAT_U_F64(x) TRUNC_SAT_U(u32, f64, 4294967296., UINT32_MAX, x) +#define I64_TRUNC_SAT_U_F64(x) TRUNC_SAT_U(u64, f64, (f64)UINT64_MAX, UINT64_MAX, x) + +#define DEFINE_REINTERPRET(name, t1, t2) \ + static inline t2 name(t1 x) { \ + t2 result; \ + memcpy(&result, &x, sizeof(result)); \ + return result; \ + } + +DEFINE_REINTERPRET(f32_reinterpret_i32, u32, f32) +DEFINE_REINTERPRET(i32_reinterpret_f32, f32, u32) +DEFINE_REINTERPRET(f64_reinterpret_i64, u64, f64) +DEFINE_REINTERPRET(i64_reinterpret_f64, f64, u64) +%%sandboxapis +//test + +static u32 add_wasm2c_callback(void* sbx_ptr, u32 func_type_idx, void* func_ptr, wasm_rt_elem_target_class_t func_class) { + wasm_rt_table_t* table = get_wasm2c_callback_table(sbx_ptr); + for (u32 i = 1; i < table->max_size; i++) { + if (i >= table->size) { + wasm_rt_expand_table(table); + } + if (table->data[i].func == 0) { + table->data[i] = (wasm_rt_elem_t){ func_class, func_type_idx, (wasm_rt_anyfunc_t) func_ptr }; + return i; + } + } + (void) TRAP(CALL_INDIRECT_TABLE_EXPANSION); +} + +static void remove_wasm2c_callback(void* sbx_ptr, u32 callback_idx) { + wasm_rt_table_t* table = get_wasm2c_callback_table(sbx_ptr); + table->data[callback_idx].func = 0; +} + +static u32 lookup_wasm2c_func_index(void* sbx_ptr, u32 param_count, u32 result_count, wasm_rt_type_t* types) { + wasm2c_sandbox_t* const sbx = (wasm2c_sandbox_t* const) sbx_ptr; + return wasm_rt_register_func_type(&sbx->func_type_structs, &sbx->func_type_count, param_count, result_count, types); +} + +static void* create_wasm2c_sandbox(uint32_t max_wasm_pages) { + wasm2c_sandbox_t* const sbx = (wasm2c_sandbox_t* const) calloc(sizeof(wasm2c_sandbox_t), 1); + if (!init_memory(sbx, max_wasm_pages)) { + free(sbx); + return 0; + } + init_func_types(sbx); + init_globals(sbx); + init_table(sbx); + wasm_rt_init_wasi(&(sbx->wasi_data)); + init_module_starts(); + // w2c___wasm_call_ctors(sbx); + return sbx; +} + +static void destroy_wasm2c_sandbox(void* aSbx) { + wasm2c_sandbox_t* const sbx = (wasm2c_sandbox_t* const) aSbx; + cleanup_memory(sbx); + cleanup_func_types(sbx); + cleanup_table(sbx); + wasm_rt_cleanup_wasi(&(sbx->wasi_data)); + free(sbx); +} + +FUNC_EXPORT wasm2c_sandbox_funcs_t WASM_CURR_ADD_PREFIX(get_wasm2c_sandbox_info)() { + wasm2c_sandbox_funcs_t ret; + ret.wasm_rt_sys_init = &wasm_rt_sys_init; + ret.create_wasm2c_sandbox = &create_wasm2c_sandbox; + ret.destroy_wasm2c_sandbox = &destroy_wasm2c_sandbox; + ret.lookup_wasm2c_nonfunc_export = &lookup_wasm2c_nonfunc_export; + ret.lookup_wasm2c_func_index = &lookup_wasm2c_func_index; + ret.add_wasm2c_callback = &add_wasm2c_callback; + ret.remove_wasm2c_callback = &remove_wasm2c_callback; + return ret; +} diff --git a/third_party/wasm2c/src/wasm2c.h.tmpl b/third_party/wasm2c/src/wasm2c.h.tmpl new file mode 100644 index 0000000000..a79f924e09 --- /dev/null +++ b/third_party/wasm2c/src/wasm2c.h.tmpl @@ -0,0 +1,51 @@ +%%top +/* Automically generated by wasm2c */ +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +#include "wasm-rt.h" + +#ifndef WASM_RT_MODULE_PREFIX +#define WASM_RT_MODULE_PREFIX +#endif + +#define WASM_RT_PASTE_(x, y) x ## y +#define WASM_RT_PASTE(x, y) WASM_RT_PASTE_(x, y) +#define WASM_RT_ADD_PREFIX(x) WASM_RT_PASTE(WASM_RT_MODULE_PREFIX, x) + +#define WASM_CURR_ADD_PREFIX(x) WASM_RT_PASTE(WASM_CURR_MODULE_PREFIX, x) + +/* TODO(binji): only use stdint.h types in header */ +typedef uint8_t u8; +typedef int8_t s8; +typedef uint16_t u16; +typedef int16_t s16; +typedef uint32_t u32; +typedef int32_t s32; +typedef uint64_t u64; +typedef int64_t s64; +typedef float f32; +typedef double f64; + +#ifndef WASM_DONT_EXPORT_FUNCS +# if defined(_WIN32) +# define FUNC_EXPORT __declspec(dllexport) +# else +# define FUNC_EXPORT +# endif +#else +# define FUNC_EXPORT +#endif + +FUNC_EXPORT wasm2c_sandbox_funcs_t WASM_CURR_ADD_PREFIX(get_wasm2c_sandbox_info)(); + +struct wasm2c_sandbox_t; +typedef struct wasm2c_sandbox_t wasm2c_sandbox_t; +%%bottom + +#ifdef __cplusplus +} +#endif diff --git a/third_party/wasm2c/src/wasm2c_tmpl.py b/third_party/wasm2c/src/wasm2c_tmpl.py new file mode 100755 index 0000000000..0da117a8ca --- /dev/null +++ b/third_party/wasm2c/src/wasm2c_tmpl.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +# +# 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. +# + +import argparse +import io +import os +import sys + + +def EscapeCString(s): + out = '' + for b in bytearray(s.encode('utf-8')): + if b in (34, 92): + # " or \ + out += '\\' + chr(b) + elif b == 10: + # newline + out += '\\n' + elif 32 <= b <= 127: + # printable char + out += chr(b) + else: + # non-printable; write as \xab + out += '\\x%02x' % b + + return out + + +def main(args): + arg_parser = argparse.ArgumentParser() + arg_parser.add_argument('-o', '--output', metavar='PATH', + help='output file.') + arg_parser.add_argument('file', help='input file.') + options = arg_parser.parse_args(args) + + section_name = None + output = io.StringIO() + + output.write('/* Generated from \'%s\' by wasm2c_tmpl.py, do not edit! */\n' % + os.path.basename(options.file)) + + with open(options.file) as f: + for line in f.readlines(): + if line.startswith('%%'): + if section_name is not None: + output.write(';\n\n') + section_name = line[2:-1] + output.write('const char SECTION_NAME(%s)[] =\n' % section_name) + else: + output.write('"%s"\n' % EscapeCString(line)) + + output.write(';\n') + if options.output: + with open(options.output, 'w') as outf: + outf.write(output.getvalue()) + else: + sys.stdout.write(output.getvalue()) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/third_party/wasm2c/src/wast-lexer.cc b/third_party/wasm2c/src/wast-lexer.cc new file mode 100644 index 0000000000..05ac736a5a --- /dev/null +++ b/third_party/wasm2c/src/wast-lexer.cc @@ -0,0 +1,557 @@ +/* + * 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 "src/wast-lexer.h" + +#include <cassert> +#include <cstdio> + +#include "config.h" + +#include "src/lexer-source.h" +#include "src/wast-parser.h" + +#define ERROR(...) parser->Error(GetLocation(), __VA_ARGS__) + +namespace wabt { + +namespace { + +#include "src/prebuilt/lexer-keywords.cc" + +} // namespace + +WastLexer::WastLexer(std::unique_ptr<LexerSource> source, string_view filename) + : 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_) {} + +// static +std::unique_ptr<WastLexer> WastLexer::CreateBufferLexer(string_view filename, + const void* data, + size_t size) { + return MakeUnique<WastLexer>(MakeUnique<LexerSource>(data, size), filename); +} + +Token WastLexer::GetToken(WastParser* parser) { + while (true) { + token_start_ = cursor_; + switch (PeekChar()) { + case kEof: + return BareToken(TokenType::Eof); + + case '(': + if (MatchString("(;")) { + if (ReadBlockComment(parser)) { + continue; + } + return BareToken(TokenType::Eof); + } else if (MatchString("(@")) { + ReadReservedChars(); + // 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(parser); + + 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 GetIdToken(); + + 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 (IsReserved(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_)); +} + +string_view WastLexer::GetText(size_t offset) { + return 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(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(WastParser* parser) { + 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(WastParser* parser) { + 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 '"': + 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; + + 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 IsReserved(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 IsReserved(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; +} + +int WastLexer::ReadReservedChars() { + int count = 0; + while (IsReserved(PeekChar())) { + ReadChar(); + ++count; + } + return count; +} + +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(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::GetIdToken() { + ReadChar(); + if (NoTrailingReservedChars()) { + return TextToken(TokenType::Reserved); + } + return TextToken(TokenType::Var); +} + +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); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/wast-lexer.h b/third_party/wasm2c/src/wast-lexer.h new file mode 100644 index 0000000000..985fb8b540 --- /dev/null +++ b/third_party/wasm2c/src/wast-lexer.h @@ -0,0 +1,108 @@ +/* + * 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_WAST_LEXER_H_ +#define WABT_WAST_LEXER_H_ + +#include <cstddef> +#include <cstdio> +#include <memory> + +#include "src/common.h" +#include "src/lexer-source-line-finder.h" +#include "src/literal.h" +#include "src/make-unique.h" +#include "src/opcode.h" +#include "src/token.h" + +namespace wabt { + +class ErrorHandler; +class LexerSource; +class WastParser; + +class WastLexer { + public: + WABT_DISALLOW_COPY_AND_ASSIGN(WastLexer); + + WastLexer(std::unique_ptr<LexerSource> source, string_view filename); + + // Convenience functions. + static std::unique_ptr<WastLexer> CreateBufferLexer(string_view filename, + const void* data, + size_t size); + + Token GetToken(WastParser* parser); + + // TODO(binji): Move this out of the lexer. + std::unique_ptr<LexerSourceLineFinder> MakeLineFinder() { + return MakeUnique<LexerSourceLineFinder>(source_->Clone()); + } + + private: + static const int kEof = -1; + enum class CharClass { Reserved = 1, Keyword = 2, HexDigit = 4, Digit = 8 }; + + Location GetLocation(); + string_view GetText(size_t offset = 0); + + Token BareToken(TokenType); + Token LiteralToken(TokenType, LiteralType); + Token TextToken(TokenType, size_t offset = 0); + + int PeekChar(); + int ReadChar(); + bool MatchChar(char); + bool MatchString(string_view); + void Newline(); + bool ReadBlockComment(WastParser*); // Returns false if EOF. + bool ReadLineComment(); // Returns false if EOF. + void ReadWhitespace(); + + static bool IsCharClass(int c, CharClass); + static bool IsDigit(int c) { return IsCharClass(c, CharClass::Digit); } + static bool IsHexDigit(int c) { return IsCharClass(c, CharClass::HexDigit); } + static bool IsKeyword(int c) { return IsCharClass(c, CharClass::Keyword); } + static bool IsReserved(int c) { return IsCharClass(c, CharClass::Reserved); } + + bool ReadNum(); + bool ReadHexNum(); + int ReadReservedChars(); + bool NoTrailingReservedChars() { return ReadReservedChars() == 0; } + void ReadSign(); + Token GetStringToken(WastParser*); + Token GetNumberToken(TokenType); + Token GetHexNumberToken(TokenType); + Token GetInfToken(); + Token GetNanToken(); + Token GetNameEqNumToken(string_view name, TokenType); + Token GetIdToken(); + Token GetKeywordToken(); + Token GetReservedToken(); + + std::unique_ptr<LexerSource> source_; + std::string filename_; + int line_; + const char* buffer_; + const char* buffer_end_; + const char* line_start_; + const char* token_start_; + const char* cursor_; +}; + +} // namespace wabt + +#endif /* WABT_WAST_LEXER_H_ */ diff --git a/third_party/wasm2c/src/wast-parser.cc b/third_party/wasm2c/src/wast-parser.cc new file mode 100644 index 0000000000..5954f6510d --- /dev/null +++ b/third_party/wasm2c/src/wast-parser.cc @@ -0,0 +1,3278 @@ +/* + * 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 "src/wast-parser.h" + +#include "src/binary-reader-ir.h" +#include "src/binary-reader.h" +#include "src/cast.h" +#include "src/expr-visitor.h" +#include "src/make-unique.h" +#include "src/resolve-names.h" +#include "src/stream.h" +#include "src/utf8.h" +#include "src/validator.h" + +#define WABT_TRACING 0 +#include "src/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(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; + 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++; + } + } +} + +typedef std::vector<string_view> TextVector; + +template <typename OutputIter> +void RemoveEscapes(const TextVector& texts, OutputIter out) { + for (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 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::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 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 = MakeUnique<TypeModuleField>(loc); + auto func_type = MakeUnique<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(), + expected.GetName())); + 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. + errors->emplace_back(ErrorLevel::Error, loc, + StringPrintf("invalid func type index %" PRIindex, + decl.type_var.index())); + 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) { + 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) { + 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& pair: func->bindings) { + pair.second.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(this)); + } + return tokens_.front(); +} + +Location WastParser::GetLocation() { + return GetToken().loc; +} + +TokenType WastParser::Peek(size_t n) { + while (tokens_.size() <= n) { + Token cur = lexer_->GetToken(this); + if (cur.token_type() != TokenType::LparAnn) { + tokens_.push_back(cur); + } else { + // Custom annotation. For now, discard until matching Rpar. + 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; + } + int indent = 1; + while (indent > 0) { + cur = lexer_->GetToken(this); + switch (cur.token_type()) { + case TokenType::Lpar: + case TokenType::LparAnn: + indent++; + break; + + case TokenType::Rpar: + indent--; + break; + + default: + break; + } + } + } + } + return tokens_.at(n).token_type(); +} + +TokenTypePair WastParser::PeekPair() { + return TokenTypePair{{Peek(), Peek(1)}}; +} + +bool WastParser::PeekMatch(TokenType type) { + return Peek() == type; +} + +bool WastParser::PeekMatchLpar(TokenType type) { + return Peek() == TokenType::Lpar && Peek(1) == type; +} + +bool WastParser::PeekMatchExpr() { + return IsExpr(PeekPair()); +} + +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 = token.text().to_string(); + return true; +} + +Result WastParser::ParseVar(Var* out_var) { + WABT_TRACE(ParseVar); + if (PeekMatch(TokenType::Nat)) { + Token token = Consume(); + string_view sv = token.literal().text; + uint64_t index = kInvalidIndex; + if (Failed(ParseUint64(sv.begin(), sv.end(), &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(ElemExpr* out_elem_expr) { + Location loc = GetLocation(); + bool item = MatchLpar(TokenType::Item); + bool lpar = Match(TokenType::Lpar); + if (Match(TokenType::RefNull)) { + if (!(options_->features.bulk_memory_enabled() || + options_->features.reference_types_enabled())) { + Error(loc, "ref.null not allowed"); + } + Type type; + CHECK_RESULT(ParseRefKind(&type)); + *out_elem_expr = ElemExpr(type); + } else if (Match(TokenType::RefFunc)) { + Var var; + CHECK_RESULT(ParseVar(&var)); + *out_elem_expr = ElemExpr(var); + } else { + return false; + } + if (lpar) { + EXPECT(Rpar); + } + if (item) { + EXPECT(Rpar); + } + return true; +} + +bool WastParser::ParseElemExprListOpt(ElemExprVector* out_list) { + ElemExpr elem_expr; + while (ParseElemExprOpt(&elem_expr)) { + out_list->push_back(elem_expr); + } + return !out_list->empty(); +} + +bool WastParser::ParseElemExprVarListOpt(ElemExprVector* out_list) { + WABT_TRACE(ParseElemExprVarListOpt); + Var var; + while (ParseVarOpt(&var)) { + out_list->emplace_back(var); + } + return !out_list->empty(); +} + +Result WastParser::ParseValueType(Type* out_type) { + WABT_TRACE(ParseValueType); + if (!PeekMatch(TokenType::ValueType)) { + return ErrorExpected({"i32", "i64", "f32", "f64", "v128", "externref"}); + } + + 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()); + return Result::Error; + } + + *out_type = type; + return Result::Ok; +} + +Result WastParser::ParseValueTypeList(TypeVector* out_type_list) { + WABT_TRACE(ParseValueTypeList); + while (PeekMatch(TokenType::ValueType)) + out_type_list->push_back(Consume().type()); + + 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()); + 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()); + 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) { + WABT_TRACE(ParseQuotedText); + if (!PeekMatch(TokenType::Text)) { + return ErrorExpected({"a quoted string"}, "\"foo\""); + } + + Token token = Consume(); + RemoveEscapes(token.text(), std::back_inserter(*text)); + if (!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; + string_view sv = token.text(); + if (Failed(ParseInt64(sv.begin(), sv.end(), &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(); + string_view sv = token.text(); + if (Failed(ParseInt64(sv.begin(), sv.end(), 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::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(); + string_view sv = token.literal().text; + if (Failed(ParseUint64(sv.begin(), sv.end(), 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 = MakeUnique<Module>(); + + if (PeekMatchLpar(TokenType::Module)) { + // Starts with "(module". Allow text and binary modules, but no quoted + // modules. + CommandPtr command; + CHECK_RESULT(ParseModuleCommand(nullptr, &command)); + auto module_command = cast<ModuleCommand>(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 = MakeUnique<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 = MakeUnique<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 = MakeUnique<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 = MakeUnique<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); + EXPECT(Lpar); + auto field = MakeUnique<TagModuleField>(GetLocation()); + EXPECT(Tag); + ParseBindVarOpt(&field->tag.name); + CHECK_RESULT(ParseTypeUseOpt(&field->tag.decl)); + CHECK_RESULT(ParseUnboundFuncSignature(&field->tag.decl.sig)); + EXPECT(Rpar); + module->AppendField(std::move(field)); + return Result::Ok; +} + +Result WastParser::ParseExportModuleField(Module* module) { + WABT_TRACE(ParseExportModuleField); + EXPECT(Lpar); + auto field = MakeUnique<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 = MakeUnique<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 = + MakeUnique<ImportModuleField>(std::move(import), GetLocation()); + module->AppendField(std::move(field)); + } else { + auto field = MakeUnique<FuncModuleField>(loc, name); + Func& func = field->func; + 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.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 = MakeUnique<TypeModuleField>(GetLocation()); + EXPECT(Type); + + std::string name; + ParseBindVarOpt(&name); + EXPECT(Lpar); + Location loc = GetLocation(); + + if (Match(TokenType::Func)) { + auto func_type = MakeUnique<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 = MakeUnique<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 = MakeUnique<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; + CHECK_RESULT(ParseValueType(&field->type)); + EXPECT(Rpar); + } else { + field->mutable_ = false; + CHECK_RESULT(ParseValueType(&field->type)); + } + 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 = MakeUnique<GlobalImport>(name); + CHECK_RESULT(ParseInlineImport(import.get())); + CHECK_RESULT(ParseGlobalType(&import->global)); + auto field = + MakeUnique<ImportModuleField>(std::move(import), GetLocation()); + module->AppendField(std::move(field)); + } else { + auto field = MakeUnique<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 = MakeUnique<FuncImport>(name); + if (PeekMatchLpar(TokenType::Type)) { + import->func.decl.has_func_type = true; + CHECK_RESULT(ParseTypeUseOpt(&import->func.decl)); + EXPECT(Rpar); + } else { + CHECK_RESULT( + ParseFuncSignature(&import->func.decl.sig, &import->func.bindings)); + CHECK_RESULT(ErrorIfLpar({"param", "result"})); + EXPECT(Rpar); + } + field = MakeUnique<ImportModuleField>(std::move(import), loc); + break; + } + + case TokenType::Table: { + Consume(); + ParseBindVarOpt(&name); + auto import = MakeUnique<TableImport>(name); + CHECK_RESULT(ParseLimits(&import->table.elem_limits)); + CHECK_RESULT(ParseRefType(&import->table.elem_type)); + EXPECT(Rpar); + field = MakeUnique<ImportModuleField>(std::move(import), loc); + break; + } + + case TokenType::Memory: { + Consume(); + ParseBindVarOpt(&name); + auto import = MakeUnique<MemoryImport>(name); + CHECK_RESULT(ParseLimitsIndex(&import->memory.page_limits)); + CHECK_RESULT(ParseLimits(&import->memory.page_limits)); + EXPECT(Rpar); + field = MakeUnique<ImportModuleField>(std::move(import), loc); + break; + } + + case TokenType::Global: { + Consume(); + ParseBindVarOpt(&name); + auto import = MakeUnique<GlobalImport>(name); + CHECK_RESULT(ParseGlobalType(&import->global)); + EXPECT(Rpar); + field = MakeUnique<ImportModuleField>(std::move(import), loc); + break; + } + + case TokenType::Tag: { + Consume(); + ParseBindVarOpt(&name); + auto import = MakeUnique<TagImport>(name); + CHECK_RESULT(ParseTypeUseOpt(&import->tag.decl)); + CHECK_RESULT(ParseUnboundFuncSignature(&import->tag.decl.sig)); + EXPECT(Rpar); + field = MakeUnique<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 = MakeUnique<MemoryImport>(name); + CHECK_RESULT(ParseInlineImport(import.get())); + CHECK_RESULT(ParseLimitsIndex(&import->memory.page_limits)); + CHECK_RESULT(ParseLimits(&import->memory.page_limits)); + auto field = + MakeUnique<ImportModuleField>(std::move(import), GetLocation()); + module->AppendField(std::move(field)); + } else { + auto field = MakeUnique<MemoryModuleField>(loc, name); + CHECK_RESULT(ParseLimitsIndex(&field->memory.page_limits)); + if (MatchLpar(TokenType::Data)) { + auto data_segment_field = MakeUnique<DataSegmentModuleField>(loc); + DataSegment& data_segment = data_segment_field->data_segment; + data_segment.memory_var = Var(module->memories.size()); + data_segment.offset.push_back(MakeUnique<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(MakeUnique<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 = MakeUnique<TableImport>(name); + CHECK_RESULT(ParseInlineImport(import.get())); + CHECK_RESULT(ParseLimits(&import->table.elem_limits)); + CHECK_RESULT(ParseRefType(&import->table.elem_type)); + auto field = + MakeUnique<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 = MakeUnique<ElemSegmentModuleField>(loc); + ElemSegment& elem_segment = elem_segment_field->elem_segment; + elem_segment.table_var = Var(module->tables.size()); + elem_segment.offset.push_back(MakeUnique<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. + ElemExpr elem_expr; + if (ParseElemExprOpt(&elem_expr)) { + elem_segment.elem_exprs.push_back(elem_expr); + // Parse the rest. + ParseElemExprListOpt(&elem_segment.elem_exprs); + } else { + ParseElemExprVarListOpt(&elem_segment.elem_exprs); + } + EXPECT(Rpar); + + auto table_field = MakeUnique<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 = MakeUnique<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 = MakeUnique<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)); + CHECK_RESULT(ParseResultList(&sig->result_types)); + return Result::Ok; +} + +Result WastParser::ParseUnboundFuncSignature(FuncSignature* sig) { + WABT_TRACE(ParseUnboundFuncSignature); + CHECK_RESULT(ParseUnboundValueTypeList(TokenType::Param, &sig->param_types)); + CHECK_RESULT(ParseResultList(&sig->result_types)); + return Result::Ok; +} + +Result WastParser::ParseBoundValueTypeList(TokenType token, + TypeVector* types, + BindingHash* bindings, + Index binding_index_offset) { + WABT_TRACE(ParseBoundValueTypeList); + while (MatchLpar(token)) { + if (PeekMatch(TokenType::Var)) { + std::string name; + Type type; + Location loc = GetLocation(); + ParseBindVarOpt(&name); + CHECK_RESULT(ParseValueType(&type)); + bindings->emplace(name, + Binding(loc, binding_index_offset + types->size())); + types->push_back(type); + } else { + CHECK_RESULT(ParseValueTypeList(types)); + } + EXPECT(Rpar); + } + return Result::Ok; +} + +Result WastParser::ParseUnboundValueTypeList(TokenType token, + TypeVector* types) { + WABT_TRACE(ParseUnboundValueTypeList); + while (MatchLpar(token)) { + CHECK_RESULT(ParseValueTypeList(types)); + EXPECT(Rpar); + } + return Result::Ok; +} + +Result WastParser::ParseResultList(TypeVector* result_types) { + WABT_TRACE(ParseResultList); + return ParseUnboundValueTypeList(TokenType::Result, result_types); +} + +Result WastParser::ParseInstrList(ExprList* exprs) { + WABT_TRACE(ParseInstrList); + ExprList new_exprs; + while (IsInstr(PeekPair())) { + if (Succeeded(ParseInstr(&new_exprs))) { + exprs->splice(exprs->end(), new_exprs); + } else { + CHECK_RESULT(Synchronize(IsInstr)); + } + } + 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; + } +} + +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::ParsePlainLoadStoreInstr(Location loc, + Token token, + std::unique_ptr<Expr>* out_expr) { + Opcode opcode = token.opcode(); + Address offset; + Address align; + ParseOffsetOpt(&offset); + ParseAlignOpt(&align); + out_expr->reset(new T(opcode, align, offset, 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.begin(), literal.text.end(), + 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() && + MatchLpar(TokenType::Result)) { + CHECK_RESULT(ParseValueTypeList(&result)); + EXPECT(Rpar); + } + 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 = MakeUnique<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 = MakeUnique<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 = MakeUnique<ReturnCallIndirectExpr>(loc); + CHECK_RESULT(ParseTypeUseOpt(&expr->decl)); + CHECK_RESULT(ParseUnboundFuncSignature(&expr->decl.sig)); + ParseVarOpt(&expr->table, Var(0, loc)); + *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( + ParsePlainLoadStoreInstr<LoadExpr>(loc, Consume(), out_expr)); + break; + + case TokenType::Store: + CHECK_RESULT( + ParsePlainLoadStoreInstr<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()); + out_expr->reset(new MemoryCopyExpr(loc)); + break; + + case TokenType::MemoryFill: + ErrorUnlessOpcodeEnabled(Consume()); + out_expr->reset(new MemoryFillExpr(loc)); + break; + + case TokenType::DataDrop: + ErrorUnlessOpcodeEnabled(Consume()); + CHECK_RESULT(ParsePlainInstrVar<DataDropExpr>(loc, out_expr)); + break; + + case TokenType::MemoryInit: + ErrorUnlessOpcodeEnabled(Consume()); + CHECK_RESULT(ParsePlainInstrVar<MemoryInitExpr>(loc, out_expr)); + break; + + case TokenType::MemorySize: + Consume(); + out_expr->reset(new MemorySizeExpr(loc)); + break; + + case TokenType::MemoryGrow: + Consume(); + out_expr->reset(new MemoryGrowExpr(loc)); + 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()); + CHECK_RESULT(ParsePlainInstrVar<TableGetExpr>(loc, out_expr)); + break; + + case TokenType::TableSet: + ErrorUnlessOpcodeEnabled(Consume()); + // TODO: Table index. + CHECK_RESULT(ParsePlainInstrVar<TableSetExpr>(loc, out_expr)); + break; + + case TokenType::TableGrow: + ErrorUnlessOpcodeEnabled(Consume()); + // TODO: Table index. + CHECK_RESULT(ParsePlainInstrVar<TableGrowExpr>(loc, out_expr)); + break; + + case TokenType::TableSize: + ErrorUnlessOpcodeEnabled(Consume()); + // TODO: Table index. + CHECK_RESULT(ParsePlainInstrVar<TableSizeExpr>(loc, out_expr)); + break; + + case TokenType::TableFill: + ErrorUnlessOpcodeEnabled(Consume()); + // TODO: Table index. + CHECK_RESULT(ParsePlainInstrVar<TableFillExpr>(loc, out_expr)); + 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( + ParsePlainLoadStoreInstr<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( + ParsePlainLoadStoreInstr<AtomicWaitExpr>(loc, token, out_expr)); + break; + } + + case TokenType::AtomicLoad: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT( + ParsePlainLoadStoreInstr<AtomicLoadExpr>(loc, token, out_expr)); + break; + } + + case TokenType::AtomicStore: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT( + ParsePlainLoadStoreInstr<AtomicStoreExpr>(loc, token, out_expr)); + break; + } + + case TokenType::AtomicRmw: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT( + ParsePlainLoadStoreInstr<AtomicRmwExpr>(loc, token, out_expr)); + break; + } + + case TokenType::AtomicRmwCmpxchg: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT( + ParsePlainLoadStoreInstr<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: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + + 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 SimdLoadLaneExpr(token.opcode(), align, offset, lane_idx, loc)); + break; + } + + case TokenType::SimdStoreLane: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + + 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 SimdStoreLaneExpr(token.opcode(), align, offset, lane_idx, loc)); + 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) { + string_view sv = Consume().literal().text; + const char* s = sv.begin(); + const char* end = sv.end(); + + switch (lane_count) { + case 16: { + uint8_t value = 0; + result = ParseInt8(s, end, &value, ParseIntType::SignedAndUnsigned); + const_->set_v128_u8(lane, value); + break; + } + case 8: { + uint16_t value = 0; + result = ParseInt16(s, end, &value, ParseIntType::SignedAndUnsigned); + const_->set_v128_u16(lane, value); + break; + } + case 4: { + uint32_t value = 0; + result = ParseInt32(s, end, &value, ParseIntType::SignedAndUnsigned); + const_->set_v128_u32(lane, value); + break; + } + case 2: { + uint64_t value = 0; + result = ParseInt64(s, end, &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 literal = Consume().literal(); + uint32_t f32_bits; + Result result = ParseFloat(literal.type, literal.text.begin(), + literal.text.end(), &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 literal = Consume().literal(); + uint64_t f64_bits; + Result result = ParseDouble(literal.type, literal.text.begin(), + literal.text.end(), &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 sv = Consume().literal().text; + uint32_t u32; + result = ParseInt32(sv.begin(), sv.end(), &u32, + ParseIntType::SignedAndUnsigned); + const_->set_u32(u32); + break; + } + + case Opcode::I64Const: { + auto sv = Consume().literal().text; + uint64_t u64; + result = ParseInt64(sv.begin(), sv.end(), &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; + string_view sv; + const char* s; + const char* end; + const_->loc = GetLocation(); + TokenType token_type = Peek(); + + switch (token_type) { + case TokenType::Nat: + case TokenType::Int: { + literal = Consume().literal(); + sv = literal.text; + s = sv.begin(); + end = sv.end(); + break; + } + default: + return ErrorExpected({"a numeric literal"}, "123"); + } + + uint64_t ref_bits; + Result result = ParseInt64(s, end, &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 = MakeUnique<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 = MakeUnique<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 = MakeUnique<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 = MakeUnique<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 = Consume().text().to_string(); + } 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 = MakeUnique<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 = MakeUnique<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 = MakeUnique<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 = MakeUnique<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; + CHECK_RESULT(ParseValueType(&global->type)); + CHECK_RESULT(ErrorIfLpar({"i32", "i64", "f32", "f64"})); + EXPECT(Rpar); + } else { + CHECK_RESULT(ParseValueType(&global->type)); + } + + 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::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::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 = MakeUnique<AssertReturnCommand>(); + CHECK_RESULT(ParseAction(&command->action)); + CHECK_RESULT(ParseConstList(&command->expected, ConstType::Expectation)); + 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 = MakeUnique<AssertUninstantiableCommand>(); + CHECK_RESULT(ParseScriptModule(&command->module)); + CHECK_RESULT(ParseQuotedText(&command->text)); + *out_command = std::move(command); + } else { + auto command = MakeUnique<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 = MakeUnique<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)); + + auto command = MakeUnique<ModuleCommand>(); + Module& module = command->module; + + switch (script_module->type()) { + case ScriptModuleType::Text: + module = std::move(cast<TextScriptModule>(script_module.get())->module); + break; + + case ScriptModuleType::Binary: { + 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()); + } + } + 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; + } + + *out_command = std::move(command); + 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 = MakeUnique<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 = MakeUnique<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::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 = MakeUnique<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 = MakeUnique<QuotedScriptModule>(); + qsm->name = name; + qsm->loc = loc; + qsm->data = std::move(data); + *out_module = std::move(qsm); + break; + } + + default: { + auto tsm = MakeUnique<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 = MakeUnique<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 = MakeUnique<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 = MakeUnique<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/wast-parser.h b/third_party/wasm2c/src/wast-parser.h new file mode 100644 index 0000000000..ea762357b7 --- /dev/null +++ b/third_party/wasm2c/src/wast-parser.h @@ -0,0 +1,250 @@ +/* + * 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_WAST_PARSER_H_ +#define WABT_WAST_PARSER_H_ + +#include <array> + +#include "src/circular-array.h" +#include "src/error.h" +#include "src/feature.h" +#include "src/intrusive-list.h" +#include "src/ir.h" +#include "src/wast-lexer.h" + +namespace wabt { + +struct WastParseOptions { + WastParseOptions(const Features& features) : features(features) {} + + Features features; + bool debug_parsing = false; +}; + +typedef std::array<TokenType, 2> TokenTypePair; + +class WastParser { + public: + WastParser(WastLexer*, Errors*, WastParseOptions*); + + void WABT_PRINTF_FORMAT(3, 4) Error(Location, const char* format, ...); + Result ParseModule(std::unique_ptr<Module>* out_module); + Result ParseScript(std::unique_ptr<Script>* out_script); + + std::unique_ptr<Script> ReleaseScript(); + + private: + enum class ConstType { + Normal, + Expectation, + }; + + void ErrorUnlessOpcodeEnabled(const Token&); + + // Print an error message listing the expected tokens, as well as an example + // of expected input. + Result ErrorExpected(const std::vector<std::string>& expected, + const char* example = nullptr); + + // Print an error message, and and return Result::Error if the next token is + // '('. This is commonly used after parsing a sequence of s-expressions -- if + // no more can be parsed, we know that a following '(' is invalid. This + // function consumes the '(' so a better error message can be provided + // (assuming the following token was unexpected). + Result ErrorIfLpar(const std::vector<std::string>& expected, + const char* example = nullptr); + + // Returns the next token without consuming it. + Token GetToken(); + + // Returns the location of the next token. + Location GetLocation(); + + // Returns the type of the next token. + TokenType Peek(size_t n = 0); + + // Returns the types of the next two tokens. + TokenTypePair PeekPair(); + + // Returns true if the next token's type is equal to the parameter. + bool PeekMatch(TokenType); + + // Returns true if the next token's type is '(' and the following token is + // equal to the parameter. + bool PeekMatchLpar(TokenType); + + // Returns true if the next two tokens can start an Expr. This allows for + // folded expressions, plain instructions and block instructions. + bool PeekMatchExpr(); + + // Returns true if the next token's type is equal to the parameter. If so, + // then the token is consumed. + bool Match(TokenType); + + // Returns true if the next token's type is equal to '(' and the following + // token is equal to the parameter. If so, then the token is consumed. + bool MatchLpar(TokenType); + + // Like Match(), but prints an error message if the token doesn't match, and + // returns Result::Error. + Result Expect(TokenType); + + // Consume one token and return it. + Token Consume(); + + // Give the Match() function a clearer name when used to optionally consume a + // token (used for printing better error messages). + void ConsumeIfLpar() { Match(TokenType::Lpar); } + + typedef bool SynchronizeFunc(TokenTypePair pair); + + // Attempt to synchronize the token stream by dropping tokens until the + // SynchronizeFunc returns true, or until a token limit is reached. This + // function returns Result::Error if the stream was not able to be + // synchronized. + Result Synchronize(SynchronizeFunc); + + bool ParseBindVarOpt(std::string* name); + Result ParseVar(Var* out_var); + bool ParseVarOpt(Var* out_var, Var default_var = Var()); + Result ParseOffsetExpr(ExprList* out_expr_list); + bool ParseOffsetExprOpt(ExprList* out_expr_list); + Result ParseTextList(std::vector<uint8_t>* out_data); + bool ParseTextListOpt(std::vector<uint8_t>* out_data); + Result ParseVarList(VarVector* out_var_list); + bool ParseElemExprOpt(ElemExpr* out_elem_expr); + bool ParseElemExprListOpt(ElemExprVector* out_list); + bool ParseElemExprVarListOpt(ElemExprVector* out_list); + Result ParseValueType(Type* out_type); + Result ParseValueTypeList(TypeVector* out_type_list); + Result ParseRefKind(Type* out_type); + Result ParseRefType(Type* out_type); + bool ParseRefTypeOpt(Type* out_type); + Result ParseQuotedText(std::string* text); + bool ParseOffsetOpt(Address* offset); + bool ParseAlignOpt(Address* align); + Result ParseLimitsIndex(Limits*); + Result ParseLimits(Limits*); + Result ParseNat(uint64_t*, bool is_64); + + Result ParseModuleFieldList(Module*); + Result ParseModuleField(Module*); + Result ParseDataModuleField(Module*); + Result ParseElemModuleField(Module*); + Result ParseTagModuleField(Module*); + Result ParseExportModuleField(Module*); + Result ParseFuncModuleField(Module*); + Result ParseTypeModuleField(Module*); + Result ParseGlobalModuleField(Module*); + Result ParseImportModuleField(Module*); + Result ParseMemoryModuleField(Module*); + Result ParseStartModuleField(Module*); + Result ParseTableModuleField(Module*); + + Result ParseExportDesc(Export*); + Result ParseInlineExports(ModuleFieldList*, ExternalKind); + Result ParseInlineImport(Import*); + Result ParseTypeUseOpt(FuncDeclaration*); + Result ParseFuncSignature(FuncSignature*, BindingHash* param_bindings); + Result ParseUnboundFuncSignature(FuncSignature*); + Result ParseBoundValueTypeList(TokenType, + TypeVector*, + BindingHash*, + Index binding_index_offset = 0); + Result ParseUnboundValueTypeList(TokenType, TypeVector*); + Result ParseResultList(TypeVector*); + Result ParseInstrList(ExprList*); + Result ParseTerminatingInstrList(ExprList*); + Result ParseInstr(ExprList*); + Result ParsePlainInstr(std::unique_ptr<Expr>*); + Result ParseF32(Const*, ConstType type); + Result ParseF64(Const*, ConstType type); + Result ParseConst(Const*, ConstType type); + Result ParseExternref(Const*); + Result ParseExpectedNan(ExpectedNan* expected); + Result ParseConstList(ConstVector*, ConstType type); + Result ParseBlockInstr(std::unique_ptr<Expr>*); + Result ParseLabelOpt(std::string*); + Result ParseEndLabelOpt(const std::string&); + Result ParseBlockDeclaration(BlockDeclaration*); + Result ParseBlock(Block*); + Result ParseExprList(ExprList*); + Result ParseExpr(ExprList*); + Result ParseCatchInstrList(CatchVector* catches); + Result ParseCatchExprList(CatchVector* catches); + Result ParseGlobalType(Global*); + Result ParseField(Field*); + Result ParseFieldList(std::vector<Field>*); + + template <typename T> + Result ParsePlainInstrVar(Location, std::unique_ptr<Expr>*); + template <typename T> + Result ParsePlainLoadStoreInstr(Location, Token, std::unique_ptr<Expr>*); + Result ParseSimdLane(Location, uint64_t*); + + Result ParseCommandList(Script*, CommandPtrVector*); + Result ParseCommand(Script*, CommandPtr*); + Result ParseAssertExhaustionCommand(CommandPtr*); + Result ParseAssertInvalidCommand(CommandPtr*); + Result ParseAssertMalformedCommand(CommandPtr*); + Result ParseAssertReturnCommand(CommandPtr*); + Result ParseAssertReturnFuncCommand(CommandPtr*); + Result ParseAssertTrapCommand(CommandPtr*); + Result ParseAssertUnlinkableCommand(CommandPtr*); + Result ParseActionCommand(CommandPtr*); + Result ParseModuleCommand(Script*, CommandPtr*); + Result ParseRegisterCommand(CommandPtr*); + Result ParseInputCommand(CommandPtr*); + Result ParseOutputCommand(CommandPtr*); + + Result ParseAction(ActionPtr*); + Result ParseScriptModule(std::unique_ptr<ScriptModule>*); + + template <typename T> + Result ParseActionCommand(TokenType, CommandPtr*); + template <typename T> + Result ParseAssertActionCommand(TokenType, CommandPtr*); + template <typename T> + Result ParseAssertActionTextCommand(TokenType, CommandPtr*); + template <typename T> + Result ParseAssertScriptModuleCommand(TokenType, CommandPtr*); + + Result ParseSimdV128Const(Const*, TokenType, ConstType); + + void CheckImportOrdering(Module*); + + WastLexer* lexer_; + Index last_module_index_ = kInvalidIndex; + Errors* errors_; + WastParseOptions* options_; + + CircularArray<Token, 2> tokens_; +}; + +Result ParseWatModule(WastLexer* lexer, + std::unique_ptr<Module>* out_module, + Errors*, + WastParseOptions* options); + +Result ParseWastScript(WastLexer* lexer, + std::unique_ptr<Script>* out_script, + Errors*, + WastParseOptions* options); + +} // namespace wabt + +#endif /* WABT_WAST_PARSER_H_ */ diff --git a/third_party/wasm2c/src/wat-writer.cc b/third_party/wasm2c/src/wat-writer.cc new file mode 100644 index 0000000000..6483224b7f --- /dev/null +++ b/third_party/wasm2c/src/wat-writer.cc @@ -0,0 +1,1723 @@ +/* + * 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 "src/wat-writer.h" + +#include <algorithm> +#include <array> +#include <cassert> +#include <cinttypes> +#include <cstdarg> +#include <cstdio> +#include <iterator> +#include <map> +#include <string> +#include <vector> + +#include "src/cast.h" +#include "src/common.h" +#include "src/expr-visitor.h" +#include "src/ir.h" +#include "src/ir-util.h" +#include "src/literal.h" +#include "src/stream.h" + +#define WABT_TRACING 0 +#include "src/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(string_view str, NextChar next_char); + void WriteNameOrIndex(string_view str, Index index, NextChar next_char); + void WriteQuotedData(const void* data, size_t length); + void WriteQuotedString(string_view str, NextChar next_char); + void WriteVar(const Var& var, NextChar next_char); + void WriteVarUnlessZero(const Var& var, 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); + 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(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(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(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::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) { + const char* type_name = type.GetName(); + assert(type_name); + WritePuts(type_name, 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); +} + +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 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::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_->WriteLoadStoreExpr<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_->WritePutsNewline(Opcode::MemoryCopy_Opcode.GetName()); + 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_->WritePutsNewline(Opcode::MemoryFill_Opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnMemoryGrowExpr(MemoryGrowExpr* expr) { + writer_->WritePutsNewline(Opcode::MemoryGrow_Opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnMemorySizeExpr(MemorySizeExpr* expr) { + writer_->WritePutsNewline(Opcode::MemorySize_Opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnMemoryInitExpr(MemoryInitExpr* expr) { + writer_->WritePutsSpace(Opcode::MemoryInit_Opcode.GetName()); + writer_->WriteVar(expr->var, NextChar::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_->WriteLoadStoreExpr<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()); + 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()); + 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_))) { + // 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"); + WriteNameOrIndex(segment.name, elem_segment_index_, NextChar::Space); + + 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 ElemExpr& expr : segment.elem_exprs) { + if (flags & SegUseElemExprs) { + if (expr.kind == ElemExprKind::RefNull) { + WriteOpenSpace("ref.null"); + WriteRefKind(expr.type, NextChar::Space); + WriteCloseSpace(); + } else { + WriteOpenSpace("ref.func"); + WriteVar(expr.var, NextChar::Space); + WriteCloseSpace(); + } + } else { + assert(expr.kind == ElemExprKind::RefFunc); + WriteVar(expr.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) { + 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 diff --git a/third_party/wasm2c/src/wat-writer.h b/third_party/wasm2c/src/wat-writer.h new file mode 100644 index 0000000000..ec8a3eeb71 --- /dev/null +++ b/third_party/wasm2c/src/wat-writer.h @@ -0,0 +1,37 @@ +/* + * 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_WAT_WRITER_H_ +#define WABT_WAT_WRITER_H_ + +#include "src/common.h" + +namespace wabt { + +struct Module; +class Stream; + +struct WriteWatOptions { + bool fold_exprs = false; // Write folded expressions. + bool inline_export = false; + bool inline_import = false; +}; + +Result WriteWat(Stream*, const Module*, const WriteWatOptions&); + +} // namespace wabt + +#endif /* WABT_WAT_WRITER_H_ */ |