diff options
Diffstat (limited to 'third_party/wasm2c')
126 files changed, 53378 insertions, 0 deletions
diff --git a/third_party/wasm2c/LICENSE b/third_party/wasm2c/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/third_party/wasm2c/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/third_party/wasm2c/include/wabt/apply-names.h b/third_party/wasm2c/include/wabt/apply-names.h new file mode 100644 index 0000000000..4d30fbd887 --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 "wabt/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/include/wabt/base-types.h b/third_party/wasm2c/include/wabt/base-types.h new file mode 100644 index 0000000000..9e33835330 --- /dev/null +++ b/third_party/wasm2c/include/wabt/base-types.h @@ -0,0 +1,35 @@ +/* + * Copyright 2021 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_BASE_TYPES_H_ +#define WABT_BASE_TYPES_H_ + +#include <cstddef> +#include <cstdint> + +namespace wabt { + +using Index = uint32_t; // An index into one of the many index spaces. +using Address = uint64_t; // An address or size in linear memory. +using Offset = size_t; // An offset into a host's file or memory buffer. + +constexpr Address kInvalidAddress = ~0; +constexpr Index kInvalidIndex = ~0; +constexpr Offset kInvalidOffset = ~0; + +} // namespace wabt + +#endif // WABT_BASE_TYPES_H_ diff --git a/third_party/wasm2c/include/wabt/binary-reader-ir.h b/third_party/wasm2c/include/wabt/binary-reader-ir.h new file mode 100644 index 0000000000..4de0ee7099 --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 "wabt/common.h" +#include "wabt/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/include/wabt/binary-reader-logging.h b/third_party/wasm2c/include/wabt/binary-reader-logging.h new file mode 100644 index 0000000000..9ce32aca5e --- /dev/null +++ b/third_party/wasm2c/include/wabt/binary-reader-logging.h @@ -0,0 +1,424 @@ +/* + * 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 "wabt/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, + std::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, + std::string_view module_name, + std::string_view field_name) override; + Result OnImportFunc(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index func_index, + Index sig_index) override; + Result OnImportTable(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) override; + Result OnImportMemory(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index memory_index, + const Limits* page_limits) override; + Result OnImportGlobal(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index global_index, + Type type, + bool mutable_) override; + Result OnImportTag(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index tag_index, + Index sig_index) override; + Result 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, + std::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 OnOpcodeUint32Uint32Uint32Uint32(uint32_t value, + uint32_t value2, + uint32_t value3, + uint32_t value4) override; + Result OnOpcodeUint64(uint64_t value) override; + Result OnOpcodeF32(uint32_t value) override; + Result OnOpcodeF64(uint64_t value) override; + Result OnOpcodeV128(v128 value) override; + Result OnOpcodeBlockSig(Type sig_type) override; + Result OnOpcodeType(Type type) override; + Result OnAtomicLoadExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnAtomicStoreExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnAtomicRmwExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnAtomicRmwCmpxchgExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result 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 OnF32ConstExpr(uint32_t value_bits) override; + Result OnF64ConstExpr(uint64_t value_bits) override; + Result OnV128ConstExpr(v128 value_bits) override; + Result OnGlobalGetExpr(Index global_index) override; + Result OnGlobalSetExpr(Index global_index) override; + Result OnI32ConstExpr(uint32_t value) override; + Result OnI64ConstExpr(uint64_t value) override; + Result OnIfExpr(Type sig_type) override; + Result OnLoadExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnLocalGetExpr(Index local_index) override; + Result OnLocalSetExpr(Index local_index) override; + Result OnLocalTeeExpr(Index local_index) override; + Result OnLoopExpr(Type sig_type) override; + Result OnMemoryCopyExpr(Index srcmemidx, Index destmemidx) override; + Result OnDataDropExpr(Index segment_index) override; + Result OnMemoryFillExpr(Index memidx) override; + Result OnMemoryGrowExpr(Index memidx) override; + Result OnMemoryInitExpr(Index segment_index, Index memidx) override; + Result OnMemorySizeExpr(Index memidx) override; + Result OnTableCopyExpr(Index dst_index, Index src_index) override; + Result OnElemDropExpr(Index segment_index) override; + Result OnTableInitExpr(Index segment_index, Index table_index) override; + Result OnTableGetExpr(Index table) 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, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnThrowExpr(Index tag_index) override; + Result OnTryExpr(Type sig_type) override; + Result OnUnaryExpr(Opcode opcode) override; + Result OnTernaryExpr(Opcode opcode) override; + Result OnUnreachableExpr() override; + Result OnAtomicWaitExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnAtomicFenceExpr(uint32_t consistency_model) override; + Result OnAtomicNotifyExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result EndFunctionBody(Index index) override; + Result EndCodeSection() override; + Result OnSimdLaneOpExpr(Opcode opcode, uint64_t value) override; + Result OnSimdLoadLaneExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset, + uint64_t value) override; + Result OnSimdStoreLaneExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset, + uint64_t value) override; + Result OnSimdShuffleOpExpr(Opcode opcode, v128 value) override; + Result OnLoadSplatExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnLoadZeroExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + + Result 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(std::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, + std::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, + std::string_view local_name) override; + Result OnNameSubsection(Index index, + NameSectionSubsection subsection_type, + Offset subsection_size) override; + Result OnNameEntry(NameSectionSubsection type, + Index index, + std::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(std::string_view needed) override; + Result OnDylinkImportCount(Index count) override; + Result OnDylinkExportCount(Index count) override; + Result OnDylinkImport(std::string_view module, + std::string_view name, + uint32_t flags) override; + Result OnDylinkExport(std::string_view name, uint32_t flags) override; + Result EndDylinkSection() override; + + Result BeginTargetFeaturesSection(Offset size) override; + Result OnFeatureCount(Index count) override; + Result OnFeature(uint8_t prefix, std::string_view name) override; + Result EndTargetFeaturesSection() override; + + Result BeginLinkingSection(Offset size) override; + Result OnSymbolCount(Index count) override; + Result OnDataSymbol(Index index, + uint32_t flags, + std::string_view name, + Index segment, + uint32_t offset, + uint32_t size) override; + Result OnFunctionSymbol(Index index, + uint32_t flags, + std::string_view name, + Index func_index) override; + Result OnGlobalSymbol(Index index, + uint32_t flags, + std::string_view name, + Index global_index) override; + Result OnSectionSymbol(Index index, + uint32_t flags, + Index section_index) override; + Result OnTagSymbol(Index index, + uint32_t flags, + std::string_view name, + Index tag_index) override; + Result OnTableSymbol(Index index, + uint32_t flags, + std::string_view name, + Index tag_index) override; + Result OnSegmentInfoCount(Index count) override; + Result OnSegmentInfo(Index index, + std::string_view name, + Address alignment, + uint32_t flags) override; + Result OnInitFunctionCount(Index count) override; + Result OnInitFunction(uint32_t priority, Index symbol_index) override; + Result OnComdatCount(Index count) override; + Result OnComdatBegin(std::string_view name, + uint32_t flags, + Index count) override; + Result OnComdatEntry(ComdatType kind, Index index) override; + Result EndLinkingSection() override; + + Result BeginTagSection(Offset size) override; + Result OnTagCount(Index count) override; + Result OnTagType(Index index, Index sig_index) override; + Result EndTagSection() override; + + /* Code Metadata sections */ + Result BeginCodeMetadataSection(std::string_view name, Offset size) override; + Result OnCodeMetadataFuncCount(Index count) override; + Result OnCodeMetadataCount(Index function_index, Index count) override; + Result OnCodeMetadata(Offset offset, const void* data, Address size) override; + Result EndCodeMetadataSection() override; + + private: + void Indent(); + void Dedent(); + 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/include/wabt/binary-reader-nop.h b/third_party/wasm2c/include/wabt/binary-reader-nop.h new file mode 100644 index 0000000000..0c9ebc6487 --- /dev/null +++ b/third_party/wasm2c/include/wabt/binary-reader-nop.h @@ -0,0 +1,589 @@ +/* + * 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 "wabt/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, + std::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, + std::string_view module_name, + std::string_view field_name) override { + return Result::Ok; + } + Result OnImportFunc(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index func_index, + Index sig_index) override { + return Result::Ok; + } + Result OnImportTable(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) override { + return Result::Ok; + } + Result OnImportMemory(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index memory_index, + const Limits* page_limits) override { + return Result::Ok; + } + Result OnImportGlobal(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index global_index, + Type type, + bool mutable_) override { + return Result::Ok; + } + Result OnImportTag(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index tag_index, + Index sig_index) override { + 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, + std::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 OnOpcodeUint32Uint32Uint32Uint32(uint32_t value, + uint32_t value2, + uint32_t value3, + uint32_t value4) 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, + Index memidx, + Address alignment_log2, + Address offset) override { + return Result::Ok; + } + Result OnAtomicStoreExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override { + return Result::Ok; + } + Result OnAtomicRmwExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override { + return Result::Ok; + } + Result OnAtomicRmwCmpxchgExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override { + return Result::Ok; + } + Result OnAtomicWaitExpr(Opcode, Index, Address, Address) override { + return Result::Ok; + } + Result OnAtomicFenceExpr(uint32_t) override { return Result::Ok; } + Result OnAtomicNotifyExpr(Opcode, Index, 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 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, + Index memidx, + 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(Index srcmemidx, Index destmemidx) override { + return Result::Ok; + } + Result OnDataDropExpr(Index segment_index) override { return Result::Ok; } + Result OnMemoryFillExpr(Index memidx) override { return Result::Ok; } + Result OnMemoryGrowExpr(Index memidx) override { return Result::Ok; } + Result OnMemoryInitExpr(Index segment_index, Index memidx) override { + return Result::Ok; + } + Result OnMemorySizeExpr(Index memidx) 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, + Index memidx, + 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, + Index memidx, + Address alignment_log2, + Address offset, + uint64_t value) override { + return Result::Ok; + } + Result OnSimdStoreLaneExpr(Opcode opcode, + Index memidx, + 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, + Index memidx, + Address alignment_log2, + Address offset) override { + return Result::Ok; + } + Result OnLoadZeroExpr(Opcode opcode, + Index memidx, + 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(std::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, + std::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, + std::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, + std::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; } + + /* Code Metadata sections */ + Result BeginCodeMetadataSection(std::string_view name, Offset size) override { + return Result::Ok; + } + Result OnCodeMetadataFuncCount(Index count) override { return Result::Ok; } + Result OnCodeMetadataCount(Index function_index, Index count) override { + return Result::Ok; + } + Result OnCodeMetadata(Offset offset, + const void* data, + Address size) override { + return Result::Ok; + } + Result EndCodeMetadataSection() override { return Result::Ok; } + + /* Dylink section */ + Result BeginDylinkSection(Offset size) override { return Result::Ok; } + Result OnDylinkInfo(uint32_t mem_size, + 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(std::string_view so_name) override { + return Result::Ok; + } + Result OnDylinkImportCount(Index count) override { return Result::Ok; } + Result OnDylinkExportCount(Index count) override { return Result::Ok; } + Result OnDylinkImport(std::string_view module, + std::string_view name, + uint32_t flags) override { + return Result::Ok; + } + Result OnDylinkExport(std::string_view name, uint32_t flags) override { + return Result::Ok; + } + Result EndDylinkSection() override { return Result::Ok; } + + /* target_features section */ + Result BeginTargetFeaturesSection(Offset size) override { return Result::Ok; } + Result OnFeatureCount(Index count) override { return Result::Ok; } + Result OnFeature(uint8_t prefix, std::string_view name) override { + return Result::Ok; + } + Result EndTargetFeaturesSection() 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, + std::string_view name, + Index segment, + uint32_t offset, + uint32_t size) override { + return Result::Ok; + } + Result OnFunctionSymbol(Index index, + uint32_t flags, + std::string_view name, + Index func_index) override { + return Result::Ok; + } + Result OnGlobalSymbol(Index index, + uint32_t flags, + std::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, + std::string_view name, + Index tag_index) override { + return Result::Ok; + } + Result OnTableSymbol(Index index, + uint32_t flags, + std::string_view name, + Index table_index) override { + return Result::Ok; + } + Result OnSegmentInfoCount(Index count) override { return Result::Ok; } + Result OnSegmentInfo(Index index, + std::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 symbol_index) override { + return Result::Ok; + } + Result OnComdatCount(Index count) override { return Result::Ok; } + Result OnComdatBegin(std::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; } +}; + +} // namespace wabt + +#endif /* WABT_BINARY_READER_H_ */ diff --git a/third_party/wasm2c/include/wabt/binary-reader-objdump.h b/third_party/wasm2c/include/wabt/binary-reader-objdump.h new file mode 100644 index 0000000000..0ca7458f8e --- /dev/null +++ b/third_party/wasm2c/include/wabt/binary-reader-objdump.h @@ -0,0 +1,98 @@ +/* + * 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 "wabt/common.h" +#include "wabt/feature.h" +#include "wabt/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 { + std::string_view Get(Index index) const; + void Set(Index index, std::string_view name); + + std::map<Index, std::string> names; +}; + +struct ObjdumpLocalNames { + std::string_view Get(Index function_index, Index local_index) const; + void Set(Index function_index, Index local_index, std::string_view name); + + std::map<std::pair<Index, 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 type_names; + ObjdumpNames function_names; + ObjdumpNames global_names; + ObjdumpNames section_names; + ObjdumpNames tag_names; + ObjdumpNames segment_names; + ObjdumpNames table_names; + ObjdumpLocalNames local_names; + std::vector<ObjdumpSymbol> symtab; + std::map<Index, Index> function_param_counts; +}; + +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/include/wabt/binary-reader-opcnt.h b/third_party/wasm2c/include/wabt/binary-reader-opcnt.h new file mode 100644 index 0000000000..8c3a9aba8d --- /dev/null +++ b/third_party/wasm2c/include/wabt/binary-reader-opcnt.h @@ -0,0 +1,96 @@ +/* + * 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 "wabt/common.h" +#include "wabt/opcode.h" + +namespace wabt { + +struct Module; +struct ReadBinaryOptions; +class Stream; + +class OpcodeInfo { + public: + enum class Kind { + Bare, + Uint32, + Uint64, + Index, + Float32, + Float64, + Uint32Uint32, + Uint32Uint32Uint32, + Uint32Uint32Uint32Uint32, + BlockSig, + BrTable, + V128, + }; + + 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&); + +using OpcodeInfoCounts = std::map<OpcodeInfo, size_t>; + +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/include/wabt/binary-reader.h b/third_party/wasm2c/include/wabt/binary-reader.h new file mode 100644 index 0000000000..825d562cb0 --- /dev/null +++ b/third_party/wasm2c/include/wabt/binary-reader.h @@ -0,0 +1,505 @@ +/* + * 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 <cstddef> +#include <cstdint> +#include <string_view> + +#include "wabt/binary.h" +#include "wabt/common.h" +#include "wabt/error.h" +#include "wabt/feature.h" +#include "wabt/opcode.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; + bool skip_function_bodies = false; +}; + +// 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, + std::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, + std::string_view module_name, + std::string_view field_name) = 0; + virtual Result OnImportFunc(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index func_index, + Index sig_index) = 0; + virtual Result OnImportTable(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) = 0; + virtual Result OnImportMemory(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index memory_index, + const Limits* page_limits) = 0; + virtual Result OnImportGlobal(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index global_index, + Type type, + bool mutable_) = 0; + virtual Result OnImportTag(Index import_index, + std::string_view module_name, + std::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, + std::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 OnOpcodeUint32Uint32Uint32Uint32(uint32_t value, + uint32_t value2, + uint32_t value3, + uint32_t value4) = 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, + Index memidx, + Address alignment_log2, + Address offset) = 0; + virtual Result OnAtomicStoreExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) = 0; + virtual Result OnAtomicRmwExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) = 0; + virtual Result OnAtomicRmwCmpxchgExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) = 0; + virtual Result OnAtomicWaitExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) = 0; + virtual Result OnAtomicFenceExpr(uint32_t consistency_model) = 0; + virtual Result OnAtomicNotifyExpr(Opcode opcode, + Index memidx, + 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 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, + Index memidx, + 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(Index srcmemidx, Index destmemidx) = 0; + virtual Result OnDataDropExpr(Index segment_index) = 0; + virtual Result OnMemoryFillExpr(Index memidx) = 0; + virtual Result OnMemoryGrowExpr(Index memidx) = 0; + virtual Result OnMemoryInitExpr(Index segment_index, Index memidx) = 0; + virtual Result OnMemorySizeExpr(Index memidx) = 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, + Index memidx, + 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, + Index memidx, + Address alignment_log2, + Address offset, + uint64_t value) = 0; + virtual Result OnSimdStoreLaneExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset, + uint64_t value) = 0; + + virtual Result OnLoadSplatExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) = 0; + virtual Result OnLoadZeroExpr(Opcode opcode, + Index memidx, + 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(std::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, + std::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, + std::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, + std::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 OnDylinkImportCount(Index count) = 0; + virtual Result OnDylinkExportCount(Index count) = 0; + virtual Result OnDylinkImport(std::string_view module, + std::string_view name, + uint32_t flags) = 0; + virtual Result OnDylinkExport(std::string_view name, uint32_t flags) = 0; + virtual Result OnDylinkNeededCount(Index count) = 0; + virtual Result OnDylinkNeeded(std::string_view so_name) = 0; + virtual Result EndDylinkSection() = 0; + + /* target_features section */ + virtual Result BeginTargetFeaturesSection(Offset size) = 0; + virtual Result OnFeatureCount(Index count) = 0; + virtual Result OnFeature(uint8_t prefix, std::string_view name) = 0; + virtual Result EndTargetFeaturesSection() = 0; + + /* Linking section */ + virtual Result BeginLinkingSection(Offset size) = 0; + virtual Result OnSymbolCount(Index count) = 0; + virtual Result OnDataSymbol(Index index, + uint32_t flags, + std::string_view name, + Index segment, + uint32_t offset, + uint32_t size) = 0; + virtual Result OnFunctionSymbol(Index index, + uint32_t flags, + std::string_view name, + Index function_index) = 0; + virtual Result OnGlobalSymbol(Index index, + uint32_t flags, + std::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, + std::string_view name, + Index tag_index) = 0; + virtual Result OnTableSymbol(Index index, + uint32_t flags, + std::string_view name, + Index table_index) = 0; + virtual Result OnSegmentInfoCount(Index count) = 0; + virtual Result OnSegmentInfo(Index index, + std::string_view name, + Address alignment_log2, + uint32_t flags) = 0; + virtual Result OnInitFunctionCount(Index count) = 0; + virtual Result OnInitFunction(uint32_t priority, Index symbol_index) = 0; + virtual Result OnComdatCount(Index count) = 0; + virtual Result OnComdatBegin(std::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; + + /* Code Metadata sections */ + virtual Result BeginCodeMetadataSection(std::string_view name, + Offset size) = 0; + virtual Result OnCodeMetadataFuncCount(Index count) = 0; + virtual Result OnCodeMetadataCount(Index function_index, Index count) = 0; + virtual Result OnCodeMetadata(Offset offset, + const void* data, + Address size) = 0; + virtual Result EndCodeMetadataSection() = 0; + + const State* state = nullptr; +}; + +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/include/wabt/binary-writer-spec.h b/third_party/wasm2c/include/wabt/binary-writer-spec.h new file mode 100644 index 0000000000..168c65efdf --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 "wabt/binary-writer.h" +#include "wabt/common.h" +#include "wabt/ir.h" + +namespace wabt { + +struct FilenameMemoryStreamPair { + FilenameMemoryStreamPair(std::string_view filename, + std::unique_ptr<MemoryStream> stream) + : filename(filename), stream(std::move(stream)) {} + std::string filename; + std::unique_ptr<MemoryStream> stream; +}; + +using WriteBinarySpecStreamFactory = + std::function<Stream*(std::string_view filename)>; + +Result WriteBinarySpecScript(Stream* json_stream, + WriteBinarySpecStreamFactory module_stream_factory, + Script*, + std::string_view source_filename, + std::string_view module_filename_noext, + const WriteBinaryOptions&); + +// Convenience function for producing MemoryStream outputs all modules. +Result WriteBinarySpecScript( + Stream* json_stream, + Script*, + std::string_view source_filename, + std::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/include/wabt/binary-writer.h b/third_party/wasm2c/include/wabt/binary-writer.h new file mode 100644 index 0000000000..dedba1bdcf --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 "wabt/common.h" +#include "wabt/feature.h" +#include "wabt/opcode.h" +#include "wabt/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, + std::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/include/wabt/binary.h b/third_party/wasm2c/include/wabt/binary.h new file mode 100644 index 0000000000..485f15cbd3 --- /dev/null +++ b/third_party/wasm2c/include/wabt/binary.h @@ -0,0 +1,106 @@ +/* + * 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 "wabt/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_TARGET_FEATURES "target_features" +#define WABT_BINARY_SECTION_DYLINK "dylink" +#define WABT_BINARY_SECTION_DYLINK0 "dylink.0" +#define WABT_BINARY_SECTION_CODE_METADATA "metadata.code." + +#define WABT_FOREACH_BINARY_SECTION(V) \ + V(Custom, custom, 0) \ + 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 */ +constexpr 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); + +// See +// https://github.com/WebAssembly/extended-name-section/blob/main/proposals/extended-name-section/Overview.md +enum class NameSectionSubsection { + Module = 0, + Function = 1, + Local = 2, + Label = 3, + Type = 4, + Table = 5, + Memory = 6, + Global = 7, + ElemSegment = 8, + DataSegment = 9, + // tag names are yet part of the extended-name-section proposal (because it + // only deals with naming things that are in the spec already). However, we + // include names for Tags in wabt using this enum value on the basis that tags + // can only exist when exceptions are enabled and that engines should ignore + // unknown name types. + Tag = 10, + + First = Module, + Last = Tag, +}; +const char* GetNameSectionSubsectionName(NameSectionSubsection subsec); + +} // namespace wabt + +#endif /* WABT_BINARY_H_ */ diff --git a/third_party/wasm2c/include/wabt/binding-hash.h b/third_party/wasm2c/include/wabt/binding-hash.h new file mode 100644 index 0000000000..5d43cd7202 --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 <string_view> +#include <unordered_map> +#include <vector> + +#include "wabt/common.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: + using DuplicateCallback = + std::function<void(const value_type&, const value_type&)>; + + 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(std::string_view name) const { + return FindIndex(std::string(name)); + } + + private: + using ValueTypeVector = std::vector<const value_type*>; + + 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/include/wabt/c-writer.h b/third_party/wasm2c/include/wabt/c-writer.h new file mode 100644 index 0000000000..7d9ff6ad74 --- /dev/null +++ b/third_party/wasm2c/include/wabt/c-writer.h @@ -0,0 +1,59 @@ +/* + * 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 <functional> +#include "wabt/common.h" +#include "wabt/feature.h" +#include "wabt/ir.h" + +namespace wabt { + +struct Module; +class Stream; + +struct WriteCOptions { + std::string_view module_name; + /* Set of wasm features enabled for wasm2c */ + Features* features; + /* + * name_to_output_file_index takes const iterators to begin and end of a list + * of all functions in the module, number of imported functions, and number of + * .c outputs as argument, returns a vector where vector[i] the index of the + * .c output that funcs_begin + i goes into. Only called when --num-outputs is + * used. + */ + std::function<std::vector<size_t>( + std::vector<Func*>::const_iterator funcs_begin, + std::vector<Func*>::const_iterator funcs_end, + size_t num_imported_functions, + size_t num_outputs)> + name_to_output_file_index; +}; + +Result WriteC(std::vector<Stream*>&& c_streams, + Stream* h_stream, + Stream* h_impl_stream, + const char* header_name, + const char* header_impl_name, + const Module*, + const WriteCOptions&); + +} // namespace wabt + +#endif /* WABT_C_WRITER_H_ */ diff --git a/third_party/wasm2c/include/wabt/cast.h b/third_party/wasm2c/include/wabt/cast.h new file mode 100644 index 0000000000..f089e2f18e --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 "wabt/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/include/wabt/circular-array.h b/third_party/wasm2c/include/wabt/circular-array.h new file mode 100644 index 0000000000..9e2d38004b --- /dev/null +++ b/third_party/wasm2c/include/wabt/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: + using value_type = T; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = size_t; + using difference_type = ptrdiff_t; + + 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 constexpr 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/include/wabt/color.h b/third_party/wasm2c/include/wabt/color.h new file mode 100644 index 0000000000..8af57f33f7 --- /dev/null +++ b/third_party/wasm2c/include/wabt/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/include/wabt/common.h b/third_party/wasm2c/include/wabt/common.h new file mode 100644 index 0000000000..2ae1fa1fe9 --- /dev/null +++ b/third_party/wasm2c/include/wabt/common.h @@ -0,0 +1,456 @@ +/* + * 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 <string_view> +#include <type_traits> +#include <vector> + +#include "wabt/config.h" + +#include "wabt/base-types.h" +#include "wabt/result.h" +#include "wabt/string-format.h" +#include "wabt/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) + +// 64k +#define WABT_PAGE_SIZE 0x10000 +// # of pages that fit in 32-bit address space +#define WABT_MAX_PAGES32 0x10000 +// # of pages that fit in 64-bit address space +#define WABT_MAX_PAGES64 0x1000000000000 +#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 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 { +inline void MemcpyEndianAware(void* dst, + const void* src, + size_t dsize, + size_t ssize, + size_t doff, + size_t soff, + size_t len) { +#if WABT_BIG_ENDIAN + memcpy(static_cast<char*>(dst) + (dsize) - (len) - (doff), + static_cast<const char*>(src) + (ssize) - (len) - (soff), (len)); +#else + 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 { + +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(); +} + +enum class LabelType { + Func, + InitExpr, + Block, + Loop, + If, + Else, + Try, + Catch, + + First = Func, + Last = Catch, +}; +constexpr int kLabelTypeCount = WABT_ENUM_COUNT(LabelType); + +struct Location { + enum class Type { + Text, + Binary, + }; + + Location() : line(0), first_column(0), last_column(0) {} + Location(std::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) {} + + std::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 global.get 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, +}; +constexpr 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, + ExportInfo = 3, + ImportInfo = 4, +}; + +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, +}; +constexpr 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(std::string_view filename, std::vector<uint8_t>* out_data); + +void InitStdio(); + +/* external kind */ + +extern const char* g_kind_name[]; + +static 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 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 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/include/wabt/decompiler-ast.h b/third_party/wasm2c/include/wabt/decompiler-ast.h new file mode 100644 index 0000000000..33cc26c113 --- /dev/null +++ b/third_party/wasm2c/include/wabt/decompiler-ast.h @@ -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_DECOMPILER_AST_H_ +#define WABT_DECOMPILER_AST_H_ + +#include "wabt/cast.h" +#include "wabt/generate-names.h" +#include "wabt/ir-util.h" +#include "wabt/ir.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; + } + case ExprType::BrTable: { + InsertNode(NodeType::Expr, ExprType::BrTable, &e, 1).u.lt = + mc.GetLabel(cast<BrTableExpr>(&e)->default_target)->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 local.get and maybe others, + // though that needs a way to ensure there's no local.set 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/include/wabt/decompiler-ls.h b/third_party/wasm2c/include/wabt/decompiler-ls.h new file mode 100644 index 0000000000..c2612fa32a --- /dev/null +++ b/third_party/wasm2c/include/wabt/decompiler-ls.h @@ -0,0 +1,267 @@ +/* + * 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 "wabt/decompiler-ast.h" +#include "wabt/string-util.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 = std::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/include/wabt/decompiler-naming.h b/third_party/wasm2c/include/wabt/decompiler-naming.h new file mode 100644 index 0000000000..12c40e04fc --- /dev/null +++ b/third_party/wasm2c/include/wabt/decompiler-naming.h @@ -0,0 +1,205 @@ +/* + * 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 "wabt/decompiler-ast.h" + +#include <set> + +namespace wabt { + +inline void RenameToIdentifier(std::string& name, + Index i, + BindingHash& bh, + const std::set<std::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 = + std::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); + } + if (s.empty()) { + s = "__empty"; + } + // 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<std::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<std::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/include/wabt/decompiler.h b/third_party/wasm2c/include/wabt/decompiler.h new file mode 100644 index 0000000000..375a4b0371 --- /dev/null +++ b/third_party/wasm2c/include/wabt/decompiler.h @@ -0,0 +1,35 @@ +/* + * 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 "wabt/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/include/wabt/error-formatter.h b/third_party/wasm2c/include/wabt/error-formatter.h new file mode 100644 index 0000000000..a66c57ab0c --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 <memory> +#include <string> + +#include "wabt/color.h" +#include "wabt/error.h" +#include "wabt/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/include/wabt/error.h b/third_party/wasm2c/include/wabt/error.h new file mode 100644 index 0000000000..d16e1e6559 --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 <string_view> +#include <vector> + +#include "wabt/common.h" + +namespace wabt { + +enum class ErrorLevel { + Warning, + Error, +}; + +static 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, std::string_view message) + : error_level(error_level), loc(loc), message(message) {} + + 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/include/wabt/expr-visitor.h b/third_party/wasm2c/include/wabt/expr-visitor.h new file mode 100644 index 0000000000..cbc317cde5 --- /dev/null +++ b/third_party/wasm2c/include/wabt/expr-visitor.h @@ -0,0 +1,222 @@ +/* + * 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 "wabt/common.h" +#include "wabt/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 OnCodeMetadataExpr(CodeMetadataExpr*) = 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 OnCodeMetadataExpr(CodeMetadataExpr*) override { return Result::Ok; } + Result OnCompareExpr(CompareExpr*) override { return Result::Ok; } + Result OnConstExpr(ConstExpr*) override { return Result::Ok; } + Result OnConvertExpr(ConvertExpr*) override { return Result::Ok; } + 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/include/wabt/feature.def b/third_party/wasm2c/include/wabt/feature.def new file mode 100644 index 0000000000..00a4e7f238 --- /dev/null +++ b/third_party/wasm2c/include/wabt/feature.def @@ -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_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", true, "Bulk-memory operations") +WABT_FEATURE(reference_types, "reference-types", true, "Reference types (externref)") +WABT_FEATURE(annotations, "annotations", false, "Custom annotation syntax") +WABT_FEATURE(code_metadata, "code-metadata", false, "Code metadata") +WABT_FEATURE(gc, "gc", false, "Garbage collection") +WABT_FEATURE(memory64, "memory64", false, "64-bit memory") +WABT_FEATURE(multi_memory, "multi-memory", false, "Multi-memory") +WABT_FEATURE(extended_const, "extended-const", false, "Extended constant expressions") +WABT_FEATURE(relaxed_simd, "relaxed-simd", false, "Relaxed SIMD") diff --git a/third_party/wasm2c/include/wabt/feature.h b/third_party/wasm2c/include/wabt/feature.h new file mode 100644 index 0000000000..5a2886586b --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 "wabt/common.h" + +namespace wabt { + +class OptionParser; + +class Features { + public: + void AddOptions(OptionParser*); + + void EnableAll() { +#define WABT_FEATURE(variable, flag, default_, help) enable_##variable(); +#include "wabt/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 "wabt/feature.def" +#undef WABT_FEATURE + + private: + void UpdateDependencies(); + +#define WABT_FEATURE(variable, flag, default_, help) \ + bool variable##_enabled_ = default_; +#include "wabt/feature.def" +#undef WABT_FEATURE +}; + +} // namespace wabt + +#endif // WABT_FEATURE_H_ diff --git a/third_party/wasm2c/include/wabt/filenames.h b/third_party/wasm2c/include/wabt/filenames.h new file mode 100644 index 0000000000..983d2fbf52 --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 "wabt/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" +std::string_view GetExtension(std::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" +std::string_view StripExtension(std::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" +std::string_view GetBasename(std::string_view filename); + +} // namespace wabt + +#endif /* WABT_FILENAMES_H_ */ diff --git a/third_party/wasm2c/include/wabt/generate-names.h b/third_party/wasm2c/include/wabt/generate-names.h new file mode 100644 index 0000000000..69dae8e20d --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 "wabt/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/include/wabt/intrusive-list.h b/third_party/wasm2c/include/wabt/intrusive-list.h new file mode 100644 index 0000000000..34332345a2 --- /dev/null +++ b/third_party/wasm2c/include/wabt/intrusive-list.h @@ -0,0 +1,631 @@ +/* + * 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> + +// 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: + using value_type = T; + using reference = value_type&; + using const_reference = const value_type&; + class iterator; + class const_iterator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reverse_iterator = std::reverse_iterator<iterator>; + using const_reverse_iterator = std::reverse_iterator<const_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: + using difference_type = std::ptrdiff_t; + using iterator_category = std::bidirectional_iterator_tag; + using value_type = T; + using pointer = T*; + using reference = T&; + + 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: + using difference_type = std::ptrdiff_t; + using iterator_category = std::bidirectional_iterator_tag; + using value_type = T; + using pointer = const T*; + using reference = const T&; + + 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(std::make_unique<T>(std::forward<Args>(args)...)); +} + +template <typename T> +template <class... Args> +inline void intrusive_list<T>::emplace_back(Args&&... args) { + push_back(std::make_unique<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(std::make_unique<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(std::make_unique<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, std::make_unique<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, std::make_unique<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/include/wabt/ir-util.h b/third_party/wasm2c/include/wabt/ir-util.h new file mode 100644 index 0000000000..7d06121902 --- /dev/null +++ b/third_party/wasm2c/include/wabt/ir-util.h @@ -0,0 +1,77 @@ +/* + * 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 "wabt/common.h" +#include "wabt/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/include/wabt/ir.h b/third_party/wasm2c/include/wabt/ir.h new file mode 100644 index 0000000000..68846bc0cc --- /dev/null +++ b/third_party/wasm2c/include/wabt/ir.h @@ -0,0 +1,1483 @@ +/* + * 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 <string_view> +#include <type_traits> +#include <vector> + +#include "wabt/binding-hash.h" +#include "wabt/common.h" +#include "wabt/intrusive-list.h" +#include "wabt/opcode.h" + +namespace wabt { + +struct Module; + +enum class VarType { + Index, + Name, +}; + +struct Var { + explicit Var(); + explicit Var(Index index, const Location& loc); + explicit Var(std::string_view name, const Location& loc); + 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(std::string_view); + + Location loc; + + private: + void Destroy(); + + VarType type_; + union { + Index index_; + std::string name_; + }; +}; +using VarVector = std::vector<Var>; + +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]; +}; +using ConstVector = std::vector<Const>; + +enum class ExpectationType { + Values, + Either, +}; + +class Expectation { + public: + Expectation() = delete; + virtual ~Expectation() = default; + ExpectationType type() const { return type_; } + + Location loc; + + ConstVector expected; + + protected: + explicit Expectation(ExpectationType type, const Location& loc = Location()) + : loc(loc), type_(type) {} + + private: + ExpectationType type_; +}; + +template <ExpectationType TypeEnum> +class ExpectationMixin : public Expectation { + public: + static bool classof(const Expectation* expectation) { + return expectation->type() == TypeEnum; + } + + explicit ExpectationMixin(const Location& loc = Location()) + : Expectation(TypeEnum, loc) {} +}; + +class ValueExpectation : public ExpectationMixin<ExpectationType::Values> { + public: + explicit ValueExpectation(const Location& loc = Location()) + : ExpectationMixin<ExpectationType::Values>(loc) {} +}; + +struct EitherExpectation : public ExpectationMixin<ExpectationType::Either> { + public: + explicit EitherExpectation(const Location& loc = Location()) + : ExpectationMixin<ExpectationType::Either>(loc) {} +}; + +typedef std::unique_ptr<Expectation> ExpectationPtr; + +struct FuncSignature { + TypeVector param_types; + TypeVector result_types; + + // Some types can have names, for example (ref $foo) has type $foo. + // So to use this type we need to translate its name into + // a proper index from the module type section. + // This is the mapping from parameter/result index to its name. + std::unordered_map<uint32_t, std::string> param_type_names; + std::unordered_map<uint32_t, std::string> result_type_names; + + 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, + std::string_view name = std::string_view(), + const Location& loc = Location()) + : loc(loc), name(name), kind_(kind) {} + + TypeEntryKind kind_; +}; + +class FuncType : public TypeEntry { + public: + static bool classof(const TypeEntry* entry) { + return entry->kind() == TypeEntryKind::Func; + } + + explicit FuncType(std::string_view name = std::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(std::string_view name = std::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(std::string_view name = std::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, + CodeMetadata, + Compare, + Const, + Convert, + Drop, + GlobalGet, + GlobalSet, + If, + Load, + LocalGet, + LocalSet, + LocalTee, + Loop, + MemoryCopy, + DataDrop, + MemoryFill, + MemoryGrow, + MemoryInit, + MemorySize, + Nop, + RefIsNull, + RefFunc, + RefNull, + Rethrow, + Return, + ReturnCall, + ReturnCallIndirect, + Select, + SimdLaneOp, + SimdLoadLane, + SimdStoreLane, + SimdShuffleOp, + LoadSplat, + LoadZero, + Store, + TableCopy, + ElemDrop, + TableInit, + TableGet, + TableGrow, + TableSize, + TableSet, + TableFill, + Ternary, + Throw, + Try, + Unary, + Unreachable, + + First = AtomicLoad, + Last = Unreachable +}; + +const char* GetExprTypeName(ExprType type); + +class Expr; +using ExprList = intrusive_list<Expr>; + +using BlockDeclaration = FuncDeclaration; + +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; + } +}; +using CatchVector = std::vector<Catch>; + +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) {} +}; + +template <ExprType TypeEnum> +class MemoryExpr : public ExprMixin<TypeEnum> { + public: + MemoryExpr(Var memidx, const Location& loc = Location()) + : ExprMixin<TypeEnum>(loc), memidx(memidx) {} + + Var memidx; +}; + +template <ExprType TypeEnum> +class MemoryBinaryExpr : public ExprMixin<TypeEnum> { + public: + MemoryBinaryExpr(Var srcmemidx, + Var destmemidx, + const Location& loc = Location()) + : ExprMixin<TypeEnum>(loc), + srcmemidx(srcmemidx), + destmemidx(destmemidx) {} + + Var srcmemidx; + Var destmemidx; +}; + +using DropExpr = ExprMixin<ExprType::Drop>; +using NopExpr = ExprMixin<ExprType::Nop>; +using ReturnExpr = ExprMixin<ExprType::Return>; +using UnreachableExpr = ExprMixin<ExprType::Unreachable>; + +using MemoryGrowExpr = MemoryExpr<ExprType::MemoryGrow>; +using MemorySizeExpr = MemoryExpr<ExprType::MemorySize>; +using MemoryFillExpr = MemoryExpr<ExprType::MemoryFill>; + +using MemoryCopyExpr = MemoryBinaryExpr<ExprType::MemoryCopy>; + +template <ExprType TypeEnum> +class RefTypeExpr : public ExprMixin<TypeEnum> { + public: + RefTypeExpr(Type type, const Location& loc = Location()) + : ExprMixin<TypeEnum>(loc), type(type) {} + + Type type; +}; + +using RefNullExpr = RefTypeExpr<ExprType::RefNull>; +using RefIsNullExpr = ExprMixin<ExprType::RefIsNull>; + +template <ExprType TypeEnum> +class OpcodeExpr : public ExprMixin<TypeEnum> { + public: + OpcodeExpr(Opcode opcode, const Location& loc = Location()) + : ExprMixin<TypeEnum>(loc), opcode(opcode) {} + + Opcode opcode; +}; + +using BinaryExpr = OpcodeExpr<ExprType::Binary>; +using CompareExpr = OpcodeExpr<ExprType::Compare>; +using ConvertExpr = OpcodeExpr<ExprType::Convert>; +using UnaryExpr = OpcodeExpr<ExprType::Unary>; +using TernaryExpr = OpcodeExpr<ExprType::Ternary>; + +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 MemoryExpr<ExprType::SimdLoadLane> { + public: + SimdLoadLaneExpr(Opcode opcode, + Var memidx, + Address align, + Address offset, + uint64_t val, + const Location& loc = Location()) + : MemoryExpr<ExprType::SimdLoadLane>(memidx, loc), + opcode(opcode), + align(align), + offset(offset), + val(val) {} + + Opcode opcode; + Address align; + Address offset; + uint64_t val; +}; + +class SimdStoreLaneExpr : public MemoryExpr<ExprType::SimdStoreLane> { + public: + SimdStoreLaneExpr(Opcode opcode, + Var memidx, + Address align, + Address offset, + uint64_t val, + const Location& loc = Location()) + : MemoryExpr<ExprType::SimdStoreLane>(memidx, loc), + opcode(opcode), + align(align), + offset(offset), + val(val) {} + + Opcode opcode; + 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; +}; + +template <ExprType TypeEnum> +class MemoryVarExpr : public MemoryExpr<TypeEnum> { + public: + MemoryVarExpr(const Var& var, Var memidx, const Location& loc = Location()) + : MemoryExpr<TypeEnum>(memidx, loc), var(var) {} + + Var var; +}; + +using BrExpr = VarExpr<ExprType::Br>; +using BrIfExpr = VarExpr<ExprType::BrIf>; +using CallExpr = VarExpr<ExprType::Call>; +using RefFuncExpr = VarExpr<ExprType::RefFunc>; +using GlobalGetExpr = VarExpr<ExprType::GlobalGet>; +using GlobalSetExpr = VarExpr<ExprType::GlobalSet>; +using LocalGetExpr = VarExpr<ExprType::LocalGet>; +using LocalSetExpr = VarExpr<ExprType::LocalSet>; +using LocalTeeExpr = VarExpr<ExprType::LocalTee>; +using ReturnCallExpr = VarExpr<ExprType::ReturnCall>; +using ThrowExpr = VarExpr<ExprType::Throw>; +using RethrowExpr = VarExpr<ExprType::Rethrow>; + +using DataDropExpr = VarExpr<ExprType::DataDrop>; +using ElemDropExpr = VarExpr<ExprType::ElemDrop>; +using TableGetExpr = VarExpr<ExprType::TableGet>; +using TableSetExpr = VarExpr<ExprType::TableSet>; +using TableGrowExpr = VarExpr<ExprType::TableGrow>; +using TableSizeExpr = VarExpr<ExprType::TableSize>; +using TableFillExpr = VarExpr<ExprType::TableFill>; + +using MemoryInitExpr = MemoryVarExpr<ExprType::MemoryInit>; + +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 CodeMetadataExpr : public ExprMixin<ExprType::CodeMetadata> { + public: + explicit CodeMetadataExpr(std::string_view name, + std::vector<uint8_t> data, + const Location& loc = Location()) + : ExprMixin<ExprType::CodeMetadata>(loc), + name(std::move(name)), + data(std::move(data)) {} + + std::string_view name; + std::vector<uint8_t> data; +}; + +class ReturnCallIndirectExpr : public ExprMixin<ExprType::ReturnCallIndirect> { + public: + explicit ReturnCallIndirectExpr(const Location& loc = Location()) + : 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; +}; + +using BlockExpr = BlockExprBase<ExprType::Block>; +using LoopExpr = BlockExprBase<ExprType::Loop>; + +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 MemoryExpr<TypeEnum> { + public: + LoadStoreExpr(Opcode opcode, + Var memidx, + Address align, + Address offset, + const Location& loc = Location()) + : MemoryExpr<TypeEnum>(memidx, loc), + opcode(opcode), + align(align), + offset(offset) {} + + Opcode opcode; + Address align; + Address offset; +}; + +using LoadExpr = LoadStoreExpr<ExprType::Load>; +using StoreExpr = LoadStoreExpr<ExprType::Store>; + +using AtomicLoadExpr = LoadStoreExpr<ExprType::AtomicLoad>; +using AtomicStoreExpr = LoadStoreExpr<ExprType::AtomicStore>; +using AtomicRmwExpr = LoadStoreExpr<ExprType::AtomicRmw>; +using AtomicRmwCmpxchgExpr = LoadStoreExpr<ExprType::AtomicRmwCmpxchg>; +using AtomicWaitExpr = LoadStoreExpr<ExprType::AtomicWait>; +using AtomicNotifyExpr = LoadStoreExpr<ExprType::AtomicNotify>; +using LoadSplatExpr = LoadStoreExpr<ExprType::LoadSplat>; +using LoadZeroExpr = LoadStoreExpr<ExprType::LoadZero>; + +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(std::string_view name) : name(name) {} + + std::string name; + FuncDeclaration decl; +}; + +class LocalTypes { + public: + using Decl = std::pair<Type, Index>; + using Decls = std::vector<Decl>; + + 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(std::string_view name) : name(name) {} + + 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; + Location loc; +}; + +struct Global { + explicit Global(std::string_view name) : name(name) {} + + std::string name; + Type type = Type::Void; + bool mutable_ = false; + ExprList init_expr; +}; + +struct Table { + explicit Table(std::string_view name) + : name(name), elem_type(Type::FuncRef) {} + + std::string name; + Limits elem_limits; + Type elem_type; +}; + +using ExprListVector = std::vector<ExprList>; + +struct ElemSegment { + explicit ElemSegment(std::string_view name) : name(name) {} + uint8_t GetFlags(const Module*) const; + + SegmentKind kind = SegmentKind::Active; + std::string name; + Var table_var; + Type elem_type; + ExprList offset; + ExprListVector elem_exprs; +}; + +struct Memory { + explicit Memory(std::string_view name) : name(name) {} + + std::string name; + Limits page_limits; +}; + +struct DataSegment { + explicit DataSegment(std::string_view name) : name(name) {} + 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(std::string_view name = std::string_view()) + : ImportMixin<ExternalKind::Func>(), func(name) {} + + Func func; +}; + +class TableImport : public ImportMixin<ExternalKind::Table> { + public: + explicit TableImport(std::string_view name = std::string_view()) + : ImportMixin<ExternalKind::Table>(), table(name) {} + + Table table; +}; + +class MemoryImport : public ImportMixin<ExternalKind::Memory> { + public: + explicit MemoryImport(std::string_view name = std::string_view()) + : ImportMixin<ExternalKind::Memory>(), memory(name) {} + + Memory memory; +}; + +class GlobalImport : public ImportMixin<ExternalKind::Global> { + public: + explicit GlobalImport(std::string_view name = std::string_view()) + : ImportMixin<ExternalKind::Global>(), global(name) {} + + Global global; +}; + +class TagImport : public ImportMixin<ExternalKind::Tag> { + public: + explicit TagImport(std::string_view name = std::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_; +}; + +using ModuleFieldList = intrusive_list<ModuleField>; + +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(), + std::string_view name = std::string_view()) + : ModuleFieldMixin<ModuleFieldType::Func>(loc), func(name) {} + + Func func; +}; + +class GlobalModuleField : public ModuleFieldMixin<ModuleFieldType::Global> { + public: + explicit GlobalModuleField(const Location& loc = Location(), + std::string_view name = std::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(), + std::string_view name = std::string_view()) + : ModuleFieldMixin<ModuleFieldType::Table>(loc), table(name) {} + + Table table; +}; + +class ElemSegmentModuleField + : public ModuleFieldMixin<ModuleFieldType::ElemSegment> { + public: + explicit ElemSegmentModuleField(const Location& loc = Location(), + std::string_view name = std::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(), + std::string_view name = std::string_view()) + : ModuleFieldMixin<ModuleFieldType::Memory>(loc), memory(name) {} + + Memory memory; +}; + +class DataSegmentModuleField + : public ModuleFieldMixin<ModuleFieldType::DataSegment> { + public: + explicit DataSegmentModuleField(const Location& loc = Location(), + std::string_view name = std::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(), + std::string_view name = std::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(std::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; +}; + +using BinaryScriptModule = DataScriptModule<ScriptModuleType::Binary>; +using QuotedScriptModule = DataScriptModule<ScriptModuleType::Quoted>; + +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_; +}; + +using ActionPtr = std::unique_ptr<Action>; + +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, + ScriptModule, + Action, + Register, + AssertMalformed, + AssertInvalid, + AssertUnlinkable, + AssertUninstantiable, + AssertReturn, + AssertTrap, + AssertExhaustion, + AssertException, + + First = Module, + Last = AssertException, +}; +constexpr 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; +}; + +class ScriptModuleCommand : public CommandMixin<CommandType::ScriptModule> { + public: + // Both the module and the script_module need to be stored since the module + // has the parsed information about the module, but the script_module has the + // original contents (binary or quoted). + Module module; + std::unique_ptr<ScriptModule> script_module; +}; + +template <CommandType TypeEnum> +class ActionCommandBase : public CommandMixin<TypeEnum> { + public: + ActionPtr action; +}; + +using ActionCommand = ActionCommandBase<CommandType::Action>; + +class RegisterCommand : public CommandMixin<CommandType::Register> { + public: + RegisterCommand(std::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; + ExpectationPtr expected; +}; + +template <CommandType TypeEnum> +class AssertTrapCommandBase : public CommandMixin<TypeEnum> { + public: + ActionPtr action; + std::string text; +}; + +using AssertTrapCommand = AssertTrapCommandBase<CommandType::AssertTrap>; +using AssertExhaustionCommand = + AssertTrapCommandBase<CommandType::AssertExhaustion>; + +template <CommandType TypeEnum> +class AssertModuleCommand : public CommandMixin<TypeEnum> { + public: + std::unique_ptr<ScriptModule> module; + std::string text; +}; + +using AssertMalformedCommand = + AssertModuleCommand<CommandType::AssertMalformed>; +using AssertInvalidCommand = AssertModuleCommand<CommandType::AssertInvalid>; +using AssertUnlinkableCommand = + AssertModuleCommand<CommandType::AssertUnlinkable>; +using AssertUninstantiableCommand = + AssertModuleCommand<CommandType::AssertUninstantiable>; + +class AssertExceptionCommand + : public CommandMixin<CommandType::AssertException> { + public: + ActionPtr action; +}; + +using CommandPtr = std::unique_ptr<Command>; +using CommandPtrVector = std::vector<CommandPtr>; + +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/include/wabt/leb128.h b/third_party/wasm2c/include/wabt/leb128.h new file mode 100644 index 0000000000..e7290475b3 --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 "wabt/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/include/wabt/lexer-source-line-finder.h b/third_party/wasm2c/include/wabt/lexer-source-line-finder.h new file mode 100644 index 0000000000..56719512d3 --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 "wabt/common.h" +#include "wabt/lexer-source.h" +#include "wabt/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/include/wabt/lexer-source.h b/third_party/wasm2c/include/wabt/lexer-source.h new file mode 100644 index 0000000000..19e7d9a4b6 --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 "wabt/common.h" +#include "wabt/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/include/wabt/literal.h b/third_party/wasm2c/include/wabt/literal.h new file mode 100644 index 0000000000..5eb8f4b0da --- /dev/null +++ b/third_party/wasm2c/include/wabt/literal.h @@ -0,0 +1,129 @@ +/* + * 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 "wabt/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); + +// Same as above but taking a string_view +inline Result ParseInt8(std::string_view v, + uint8_t* out, + ParseIntType parse_type) { + return ParseInt8(v.data(), v.data() + v.size(), out, parse_type); +} + +inline Result ParseInt16(std::string_view v, + uint16_t* out, + ParseIntType parse_type) { + return ParseInt16(v.data(), v.data() + v.size(), out, parse_type); +} + +inline Result ParseInt32(std::string_view v, + uint32_t* out, + ParseIntType parse_type) { + return ParseInt32(v.data(), v.data() + v.size(), out, parse_type); +} + +inline Result ParseInt64(std::string_view v, + uint64_t* out, + ParseIntType parse_type) { + return ParseInt64(v.data(), v.data() + v.size(), out, parse_type); +} + +inline Result ParseUint64(std::string_view v, uint64_t* out) { + return ParseUint64(v.data(), v.data() + v.size(), out); +} + +inline Result ParseUint128(std::string_view v, v128* out) { + return ParseUint128(v.data(), v.data() + v.size(), out); +} + +inline Result ParseFloat(LiteralType literal_type, + std::string_view v, + uint32_t* out_bits) { + return ParseFloat(literal_type, v.data(), v.data() + v.size(), out_bits); +} + +inline Result ParseDouble(LiteralType literal_type, + std::string_view v, + uint64_t* out_bits) { + return ParseDouble(literal_type, v.data(), v.data() + v.size(), 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/include/wabt/opcode-code-table.h b/third_party/wasm2c/include/wabt/opcode-code-table.h new file mode 100644 index 0000000000..13c5838ea6 --- /dev/null +++ b/third_party/wasm2c/include/wabt/opcode-code-table.h @@ -0,0 +1,43 @@ +/* + * 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 <stdint.h> +#include <stdlib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define WABT_OPCODE_CODE_TABLE_SIZE 131072 + +/* + * Number of bits required to store an opcode + */ +#define MAX_OPCODE_BITS 9 + +/* 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/include/wabt/opcode.def b/third_party/wasm2c/include/wabt/opcode.def new file mode 100644 index 0000000000..e10f9cba82 --- /dev/null +++ b/third_party/wasm2c/include/wabt/opcode.def @@ -0,0 +1,593 @@ +/* + * 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", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe5, InterpCatchDrop, "catch_drop", "") +WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe6, InterpAdjustFrameForReturnCall, "adjust_frame_for_return_call", "") + +/* 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 */ +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 */ +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", "") + +/* Relaxed-SIMD opcodes */ +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x100, I8X16RelaxedSwizzle, "i8x16.relaxed_swizzle", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x101, I32X4RelaxedTruncF32X4S, "i32x4.relaxed_trunc_f32x4_s", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x102, I32X4RelaxedTruncF32X4U, "i32x4.relaxed_trunc_f32x4_u", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x103, I32X4RelaxedTruncF64X2SZero, "i32x4.relaxed_trunc_f64x2_s_zero", "") +WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x104, I32X4RelaxedTruncF64X2UZero, "i32x4.relaxed_trunc_f64x2_u_zero", "") +WABT_OPCODE(V128, V128, V128, V128, 0, 0xfd, 0x105, F32X4RelaxedMadd, "f32x4.relaxed_madd", "") +WABT_OPCODE(V128, V128, V128, V128, 0, 0xfd, 0x106, F32X4RelaxedNmadd, "f32x4.relaxed_nmadd", "") +WABT_OPCODE(V128, V128, V128, V128, 0, 0xfd, 0x107, F64X2RelaxedMadd, "f64x2.relaxed_madd", "") +WABT_OPCODE(V128, V128, V128, V128, 0, 0xfd, 0x108, F64X2RelaxedNmadd, "f64x2.relaxed_nmadd", "") +WABT_OPCODE(V128, V128, V128, V128, 0, 0xfd, 0x109, I8X16RelaxedLaneSelect, "i8x16.relaxed_laneselect", "") +WABT_OPCODE(V128, V128, V128, V128, 0, 0xfd, 0x10a, I16X8RelaxedLaneSelect, "i16x8.relaxed_laneselect", "") +WABT_OPCODE(V128, V128, V128, V128, 0, 0xfd, 0x10b, I32X4RelaxedLaneSelect, "i32x4.relaxed_laneselect", "") +WABT_OPCODE(V128, V128, V128, V128, 0, 0xfd, 0x10c, I64X2RelaxedLaneSelect, "i64x2.relaxed_laneselect", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x10d, F32X4RelaxedMin, "f32x4.relaxed_min", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x10e, F32X4RelaxedMax, "f32x4.relaxed_max", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x10f, F64X2RelaxedMin, "f64x2.relaxed_min", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x110, F64X2RelaxedMax, "f64x2.relaxed_max", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x111, I16X8RelaxedQ15mulrS, "i16x8.relaxed_q15mulr_s", "") +WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x112, I16X8DotI8X16I7X16S, "i16x8.dot_i8x16_i7x16_s", "") +WABT_OPCODE(V128, V128, V128, V128, 0, 0xfd, 0x113, I32X4DotI8X16I7X16AddS, "i32x4.dot_i8x16_i7x16_add_s", "") + +/* 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/include/wabt/opcode.h b/third_party/wasm2c/include/wabt/opcode.h new file mode 100644 index 0000000000..a7c7b2495a --- /dev/null +++ b/third_party/wasm2c/include/wabt/opcode.h @@ -0,0 +1,172 @@ +/* + * 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 "wabt/common.h" +#include "wabt/leb128.h" +#include "wabt/opcode-code-table.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 "wabt/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 "wabt/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; } + + // 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 constexpr uint32_t kMathPrefix = 0xfc; + static constexpr uint32_t kThreadsPrefix = 0xfe; + static constexpr 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) { + if (code >= (1 << MAX_OPCODE_BITS)) { + // Clamp to (2^bits - 1), since we know that it is an invalid code. + code = (1 << MAX_OPCODE_BITS) - 1; + } + return (prefix << MAX_OPCODE_BITS) | 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 >> MAX_OPCODE_BITS; + *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/include/wabt/option-parser.h b/third_party/wasm2c/include/wabt/option-parser.h new file mode 100644 index 0000000000..0203ce0ccd --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 "wabt/common.h" + +namespace wabt { + +class OptionParser { + public: + enum class HasArgument { No, Yes }; + enum class ArgumentCount { One, OneOrMore, ZeroOrMore }; + + struct Option; + using Callback = std::function<void(const char*)>; + using NullCallback = std::function<void()>; + + 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/include/wabt/range.h b/third_party/wasm2c/include/wabt/range.h new file mode 100644 index 0000000000..15202e1ed3 --- /dev/null +++ b/third_party/wasm2c/include/wabt/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; } +}; + +using OffsetRange = Range<Offset>; +using ColumnRange = Range<int>; + +} // namespace wabt + +#endif // WABT_RANGE_H_ diff --git a/third_party/wasm2c/include/wabt/resolve-names.h b/third_party/wasm2c/include/wabt/resolve-names.h new file mode 100644 index 0000000000..17ba1eedc5 --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 "wabt/common.h" +#include "wabt/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/include/wabt/result.h b/third_party/wasm2c/include/wabt/result.h new file mode 100644 index 0000000000..a40faab78b --- /dev/null +++ b/third_party/wasm2c/include/wabt/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/include/wabt/sha256.h b/third_party/wasm2c/include/wabt/sha256.h new file mode 100644 index 0000000000..744e2d1c63 --- /dev/null +++ b/third_party/wasm2c/include/wabt/sha256.h @@ -0,0 +1,31 @@ +/* + * 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_SHA256_H_ +#define WABT_SHA256_H_ + +#include "wabt/config.h" + +#include <string> +#include <string_view> + +namespace wabt { + +void sha256(std::string_view input, std::string& digest); + +} // namespace wabt + +#endif // WABT_SHA256_H_ diff --git a/third_party/wasm2c/include/wabt/shared-validator.h b/third_party/wasm2c/include/wabt/shared-validator.h new file mode 100644 index 0000000000..8c1f3988b1 --- /dev/null +++ b/third_party/wasm2c/include/wabt/shared-validator.h @@ -0,0 +1,330 @@ +/* + * 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 "wabt/common.h" +#include "wabt/error.h" +#include "wabt/feature.h" +#include "wabt/ir.h" +#include "wabt/opcode.h" +#include "wabt/type-checker.h" + +#include "wabt/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 GetCatchCount(Index depth, Index* out_count) { + return typechecker_.GetCatchCount(depth, out_count); + } + + 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, + Index type_index); + 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 OnTag(const Location&, Var sig_var); + + Result OnExport(const Location&, + ExternalKind, + Var item_var, + std::string_view name); + + Result OnStart(const Location&, Var func_var); + + Result OnElemSegment(const Location&, Var table_var, SegmentKind); + Result OnElemSegmentElemType(const Location&, Type elem_type); + 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 BeginInitExpr(const Location&, Type type); + Result EndInitExpr(); + + 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, Var memidx, Address align); + Result OnAtomicNotify(const Location&, Opcode, Var memidx, Address align); + Result OnAtomicRmwCmpxchg(const Location&, Opcode, Var memidx, Address align); + Result OnAtomicRmw(const Location&, Opcode, Var memidx, Address align); + Result OnAtomicStore(const Location&, Opcode, Var memidx, Address align); + Result OnAtomicWait(const Location&, Opcode, Var memidx, 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, Var memidx, Address align); + Result OnLoadSplat(const Location&, Opcode, Var memidx, Address align); + Result OnLoadZero(const Location&, Opcode, Var memidx, 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&, Var srcmemidx, Var destmemidx); + Result OnMemoryFill(const Location&, Var memidx); + Result OnMemoryGrow(const Location&, Var memidx); + Result OnMemoryInit(const Location&, Var segment_var, Var memidx); + Result OnMemorySize(const Location&, Var memidx); + 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, + Var memidx, + Address align, + uint64_t lane_idx); + Result OnSimdStoreLane(const Location&, + Opcode, + Var memidx, + Address align, + uint64_t lane_idx); + Result OnSimdShuffleOp(const Location&, Opcode, v128 lane_idx); + Result OnStore(const Location&, Opcode, Var memidx, 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, + Index type_index) + : params(params), results(results), type_index(type_index) {} + + TypeVector params; + TypeVector results; + Index type_index; + }; + + 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, bool is_active, Type table_type) + : element(element), is_active(is_active), table_type(table_type) {} + + Type element; + bool is_active; + Type table_type; + }; + + struct LocalDecl { + Type type; + Index end; + }; + + bool ValidInitOpcode(Opcode opcode) const; + Result CheckInstr(Opcode opcode, const Location& loc); + 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); + + Index GetFunctionTypeIndex(Index func_index) const; + + TypeVector ToTypeVector(Index count, const Type* types); + + ValidateOptions options_; + Errors* errors_; + TypeChecker typechecker_; // TODO: Move into SharedValidator. + // Cached for access by OnTypecheckerError. + Location expr_loc_ = Location(kInvalidOffset); + bool in_init_expr_ = false; + + 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> check_declared_funcs_; +}; + +} // namespace wabt + +#endif // WABT_SHARED_VALIDATOR_H_ diff --git a/third_party/wasm2c/include/wabt/stream.h b/third_party/wasm2c/include/wabt/stream.h new file mode 100644 index 0000000000..67f50975eb --- /dev/null +++ b/third_party/wasm2c/include/wabt/stream.h @@ -0,0 +1,229 @@ +/* + * 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 "wabt/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(std::string_view filename) const; + Result WriteToStdout() 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); + MemoryStream(MemoryStream&&) = default; + 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(std::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(std::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/include/wabt/string-format.h b/third_party/wasm2c/include/wabt/string-format.h new file mode 100644 index 0000000000..892facf27b --- /dev/null +++ b/third_party/wasm2c/include/wabt/string-format.h @@ -0,0 +1,68 @@ +/* + * Copyright 2021 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_FORMAT_H_ +#define WABT_STRING_FORMAT_H_ + +#include <cstdarg> +#include <string> +#include <vector> + +#include "wabt/config.h" + +#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) + +namespace wabt { + +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); +} + +} // namespace wabt + +#endif // WABT_STRING_FORMAT_H_ diff --git a/third_party/wasm2c/include/wabt/string-util.h b/third_party/wasm2c/include/wabt/string-util.h new file mode 100644 index 0000000000..28734d504b --- /dev/null +++ b/third_party/wasm2c/include/wabt/string-util.h @@ -0,0 +1,82 @@ +/* + * 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_UTIL_H_ +#define WABT_STRING_UTIL_H_ + +#include <string> +#include <string_view> + +namespace wabt { + +inline std::string& operator+=(std::string& x, std::string_view y) { + x.append(y.data(), y.size()); + return x; +} + +inline std::string operator+(std::string_view x, std::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, std::string_view y) { + return std::string_view(x) + y; +} + +inline std::string operator+(std::string_view x, const std::string& y) { + return x + std::string_view(y); +} + +inline std::string operator+(const char* x, std::string_view y) { + return std::string_view(x) + y; +} + +inline std::string operator+(std::string_view x, const char* y) { + return x + std::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 std::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; +} + +} // namespace wabt + +#endif // WABT_STRING_UTIL_H_ diff --git a/third_party/wasm2c/include/wabt/token.def b/third_party/wasm2c/include/wabt/token.def new file mode 100644 index 0000000000..1e95da62d1 --- /dev/null +++ b/third_party/wasm2c/include/wabt/token.def @@ -0,0 +1,175 @@ +/* + * 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(AssertException, "assert_exception") +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(Either, "either") +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(Ref, "ref") +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/include/wabt/token.h b/third_party/wasm2c/include/wabt/token.h new file mode 100644 index 0000000000..7d17e1066c --- /dev/null +++ b/third_party/wasm2c/include/wabt/token.h @@ -0,0 +1,134 @@ +/* + * 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 <string_view> + +#include "wabt/literal.h" +#include "wabt/opcode.h" + +namespace wabt { + +struct Literal { + Literal() = default; + Literal(LiteralType type, std::string_view text) : type(type), text(text) {} + + LiteralType type; + std::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 "wabt/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, std::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_); } + + std::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 { + std::string_view text_; + Type type_; + Opcode opcode_; + Literal literal_; + }; +}; + +} // namespace wabt + +#endif // WABT_TOKEN_H_ diff --git a/third_party/wasm2c/include/wabt/tracing.h b/third_party/wasm2c/include/wabt/tracing.h new file mode 100644 index 0000000000..5be3faa3e4 --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 "wabt/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/include/wabt/type-checker.h b/third_party/wasm2c/include/wabt/type-checker.h new file mode 100644 index 0000000000..2b0ec092fe --- /dev/null +++ b/third_party/wasm2c/include/wabt/type-checker.h @@ -0,0 +1,207 @@ +/* + * 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 "wabt/common.h" +#include "wabt/feature.h" +#include "wabt/opcode.h" + +namespace wabt { + +class TypeChecker { + public: + using ErrorCallback = std::function<void(const char* msg)>; + + 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 GetCatchCount(Index depth, Index* out_depth); + + 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 OnIndexedFuncRef(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& srclimits, const Limits& dstlimits); + 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_type); + 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(); + + Result BeginInitExpr(Type type); + Result EndInitExpr(); + + 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); + 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)) { + PrintStackIfFailedV(result, desc, {args...}, /*is_end=*/false); + } + } + + void PrintStackIfFailedV(Result, + const char* desc, + const TypeVector&, + bool is_end); + + 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/include/wabt/type.h b/third_party/wasm2c/include/wabt/type.h new file mode 100644 index 0000000000..96fcf94588 --- /dev/null +++ b/third_party/wasm2c/include/wabt/type.h @@ -0,0 +1,164 @@ +/* + * 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 "wabt/config.h" +#include "wabt/base-types.h" +#include "wabt/string-format.h" + +namespace wabt { + +class Type; + +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 + Reference = -0x15, // 0x6b + 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_index_(kInvalidIndex) {} + Type(Enum e) : enum_(e), type_index_(kInvalidIndex) {} + Type(Enum e, Index type_index) : enum_(e), type_index_(type_index) { + assert(e == Enum::Reference); + } + constexpr operator Enum() const { return enum_; } + + bool IsRef() const { + return enum_ == Type::ExternRef || enum_ == Type::FuncRef || + enum_ == Type::Reference; + } + + bool IsReferenceWithIndex() const { return enum_ == Type::Reference; } + + bool IsNullableRef() const { + // Currently all reftypes are nullable + return IsRef(); + } + + std::string 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"; + case Type::Reference: + return StringPrintf("(ref %d)", type_index_); + default: + return StringPrintf("<type_index[%d]>", enum_); + } + } + + 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_); + } + + Index GetReferenceIndex() const { + assert(enum_ == Enum::Reference); + return type_index_; + } + + 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: + case Type::Reference: + return TypeVector(this, this + 1); + + default: + WABT_UNREACHABLE; + } + } + + private: + Enum enum_; + Index type_index_; // Only used for for Type::Reference +}; + +} // namespace wabt + +#endif // WABT_TYPE_H_ diff --git a/third_party/wasm2c/include/wabt/utf8.h b/third_party/wasm2c/include/wabt/utf8.h new file mode 100644 index 0000000000..9e3b9ebdaf --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 <cstddef> + +namespace wabt { + +bool IsValidUtf8(const char* s, size_t length); + +} // namespace wabt + +#endif // WABT_UTF8_H_ diff --git a/third_party/wasm2c/include/wabt/validator.h b/third_party/wasm2c/include/wabt/validator.h new file mode 100644 index 0000000000..f5b945884e --- /dev/null +++ b/third_party/wasm2c/include/wabt/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 "wabt/error.h" +#include "wabt/feature.h" +#include "wabt/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/include/wabt/wast-lexer.h b/third_party/wasm2c/include/wabt/wast-lexer.h new file mode 100644 index 0000000000..80bdfe7bb4 --- /dev/null +++ b/third_party/wasm2c/include/wabt/wast-lexer.h @@ -0,0 +1,117 @@ +/* + * 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 "wabt/common.h" +#include "wabt/error.h" +#include "wabt/lexer-source-line-finder.h" +#include "wabt/literal.h" +#include "wabt/opcode.h" +#include "wabt/token.h" + +namespace wabt { + +class ErrorHandler; +class LexerSource; + +class WastLexer { + public: + WABT_DISALLOW_COPY_AND_ASSIGN(WastLexer); + + WastLexer(std::unique_ptr<LexerSource> source, + std::string_view filename, + Errors*); + + // Convenience functions. + static std::unique_ptr<WastLexer> CreateBufferLexer(std::string_view filename, + const void* data, + size_t size, + Errors*); + + Token GetToken(); + + // TODO(binji): Move this out of the lexer. + std::unique_ptr<LexerSourceLineFinder> MakeLineFinder() { + return std::make_unique<LexerSourceLineFinder>(source_->Clone()); + } + + private: + static constexpr int kEof = -1; + enum class CharClass { IdChar = 1, Keyword = 2, HexDigit = 4, Digit = 8 }; + + Location GetLocation(); + std::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(std::string_view); + void Newline(); + bool ReadBlockComment(); // 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 IsIdChar(int c) { return IsCharClass(c, CharClass::IdChar); } + + bool ReadNum(); + bool ReadHexNum(); + + enum class ReservedChars { None, Some, Id }; + ReservedChars ReadReservedChars(); + bool NoTrailingReservedChars() { + return ReadReservedChars() == ReservedChars::None; + } + void ReadSign(); + Token GetStringToken(); + Token GetNumberToken(TokenType); + Token GetHexNumberToken(TokenType); + Token GetInfToken(); + Token GetNanToken(); + Token GetNameEqNumToken(std::string_view name, TokenType); + Token GetIdChars(); + 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_; + + Errors* errors_; + void WABT_PRINTF_FORMAT(3, 4) Error(Location, const char* format, ...); +}; + +} // namespace wabt + +#endif /* WABT_WAST_LEXER_H_ */ diff --git a/third_party/wasm2c/include/wabt/wast-parser.h b/third_party/wasm2c/include/wabt/wast-parser.h new file mode 100644 index 0000000000..09b4e5377e --- /dev/null +++ b/third_party/wasm2c/include/wabt/wast-parser.h @@ -0,0 +1,276 @@ +/* + * 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 <memory> +#include <unordered_map> + +#include "wabt/circular-array.h" +#include "wabt/error.h" +#include "wabt/feature.h" +#include "wabt/intrusive-list.h" +#include "wabt/ir.h" +#include "wabt/wast-lexer.h" + +namespace wabt { + +struct WastParseOptions { + WastParseOptions(const Features& features) : features(features) {} + + Features features; + bool debug_parsing = false; +}; + +using TokenTypePair = std::array<TokenType, 2>; + +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, size_t n = 0); + + // 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 two tokens are form reference type - (ref $t) + bool PeekMatchRefType(); + + // 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); } + + using SynchronizeFunc = bool(*)(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(ExprList* out_elem_expr); + bool ParseElemExprListOpt(ExprListVector* out_list); + bool ParseElemExprVarListOpt(ExprListVector* out_list); + Result ParseValueType(Var* out_type); + Result ParseValueTypeList( + TypeVector* out_type_list, + std::unordered_map<uint32_t, std::string>* type_names); + Result ParseRefKind(Type* out_type); + Result ParseRefType(Type* out_type); + bool ParseRefTypeOpt(Type* out_type); + Result ParseQuotedText(std::string* text, bool check_utf8 = true); + bool ParseOffsetOpt(Address* offset); + bool ParseAlignOpt(Address* align); + Result ParseMemidx(Location loc, Var* memidx); + 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*, + std::unordered_map<uint32_t, std::string>*, + Index binding_index_offset = 0); + Result ParseUnboundValueTypeList(TokenType, + TypeVector*, + std::unordered_map<uint32_t, std::string>*); + Result ParseResultList(TypeVector*, + std::unordered_map<uint32_t, std::string>*); + Result ParseInstrList(ExprList*); + Result ParseTerminatingInstrList(ExprList*); + Result ParseInstr(ExprList*); + Result ParseCodeMetadataAnnotation(ExprList*); + Result ParsePlainInstr(std::unique_ptr<Expr>*); + Result ParseF32(Const*, ConstType type); + Result ParseF64(Const*, ConstType type); + Result ParseConst(Const*, ConstType type); + Result ParseExpectedValues(ExpectationPtr*); + Result ParseEither(ConstVector*); + 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 ParseMemoryInstrVar(Location, std::unique_ptr<Expr>*); + template <typename T> + Result ParseLoadStoreInstr(Location, Token, std::unique_ptr<Expr>*); + template <typename T> + Result ParseSIMDLoadStoreInstr(Location loc, + Token token, + std::unique_ptr<Expr>* out_expr); + template <typename T> + Result ParseMemoryExpr(Location, std::unique_ptr<Expr>*); + template <typename T> + Result ParseMemoryBinaryExpr(Location, std::unique_ptr<Expr>*); + Result ParseSimdLane(Location, uint64_t*); + + Result ParseCommandList(Script*, CommandPtrVector*); + Result ParseCommand(Script*, CommandPtr*); + Result ParseAssertExceptionCommand(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/include/wabt/wat-writer.h b/third_party/wasm2c/include/wabt/wat-writer.h new file mode 100644 index 0000000000..0f19ba2465 --- /dev/null +++ b/third_party/wasm2c/include/wabt/wat-writer.h @@ -0,0 +1,41 @@ +/* + * 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 "wabt/common.h" +#include "wabt/feature.h" + +namespace wabt { + +struct Module; +class Stream; + +struct WriteWatOptions { + WriteWatOptions() = default; + WriteWatOptions(const Features& features) : features(features) {} + Features features; + 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_ */ diff --git a/third_party/wasm2c/src/apply-names.cc b/third_party/wasm2c/src/apply-names.cc new file mode 100644 index 0000000000..506ab1fe71 --- /dev/null +++ b/third_party/wasm2c/src/apply-names.cc @@ -0,0 +1,581 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/apply-names.h" + +#include <cassert> +#include <cstdio> +#include <string_view> +#include <vector> + +#include "wabt/cast.h" +#include "wabt/expr-visitor.h" +#include "wabt/ir.h" + +namespace wabt { + +namespace { + +class NameApplier : public ExprVisitor::DelegateNop { + public: + NameApplier(); + + Result VisitModule(Module* module); + + // Implementation of ExprVisitor::DelegateNop. + Result BeginBlockExpr(BlockExpr*) override; + Result EndBlockExpr(BlockExpr*) override; + Result OnBrExpr(BrExpr*) override; + Result OnBrIfExpr(BrIfExpr*) override; + Result OnBrTableExpr(BrTableExpr*) override; + Result OnCallExpr(CallExpr*) override; + Result OnRefFuncExpr(RefFuncExpr*) override; + Result OnCallIndirectExpr(CallIndirectExpr*) override; + Result OnReturnCallExpr(ReturnCallExpr*) override; + Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr*) override; + Result OnGlobalGetExpr(GlobalGetExpr*) override; + Result OnGlobalSetExpr(GlobalSetExpr*) override; + Result BeginIfExpr(IfExpr*) override; + Result EndIfExpr(IfExpr*) override; + Result OnLoadExpr(LoadExpr*) override; + Result OnLocalGetExpr(LocalGetExpr*) override; + Result OnLocalSetExpr(LocalSetExpr*) override; + Result OnLocalTeeExpr(LocalTeeExpr*) override; + Result BeginLoopExpr(LoopExpr*) override; + Result EndLoopExpr(LoopExpr*) override; + Result OnMemoryCopyExpr(MemoryCopyExpr*) override; + Result OnDataDropExpr(DataDropExpr*) override; + Result OnMemoryFillExpr(MemoryFillExpr*) override; + Result OnMemoryGrowExpr(MemoryGrowExpr*) override; + Result OnMemoryInitExpr(MemoryInitExpr*) override; + Result OnMemorySizeExpr(MemorySizeExpr*) override; + Result OnElemDropExpr(ElemDropExpr*) override; + Result OnTableCopyExpr(TableCopyExpr*) override; + Result OnTableInitExpr(TableInitExpr*) override; + Result OnTableGetExpr(TableGetExpr*) override; + Result OnTableSetExpr(TableSetExpr*) override; + Result OnTableGrowExpr(TableGrowExpr*) override; + Result OnTableSizeExpr(TableSizeExpr*) override; + Result OnTableFillExpr(TableFillExpr*) override; + Result OnStoreExpr(StoreExpr*) override; + Result BeginTryExpr(TryExpr*) override; + Result EndTryExpr(TryExpr*) override; + Result OnCatchExpr(TryExpr*, Catch*) override; + Result OnDelegateExpr(TryExpr*) override; + Result OnThrowExpr(ThrowExpr*) override; + Result OnRethrowExpr(RethrowExpr*) override; + Result OnSimdLoadLaneExpr(SimdLoadLaneExpr*) override; + Result OnSimdStoreLaneExpr(SimdStoreLaneExpr*) override; + + private: + void PushLabel(const std::string& label); + void PopLabel(); + std::string_view FindLabelByVar(Var* var); + void UseNameForVar(std::string_view name, Var* var); + Result UseNameForFuncTypeVar(Var* var); + Result UseNameForFuncVar(Var* var); + Result UseNameForGlobalVar(Var* var); + Result UseNameForTableVar(Var* var); + Result UseNameForMemoryVar(Var* var); + Result UseNameForTagVar(Var* var); + Result UseNameForDataSegmentVar(Var* var); + Result UseNameForElemSegmentVar(Var* var); + Result UseNameForParamAndLocalVar(Func* func, Var* var); + Result VisitFunc(Index func_index, Func* func); + Result VisitGlobal(Global* global); + Result VisitTag(Tag* tag); + Result VisitExport(Index export_index, Export* export_); + Result VisitElemSegment(Index elem_segment_index, ElemSegment* segment); + Result VisitDataSegment(Index data_segment_index, DataSegment* segment); + Result VisitStart(Var* start_var); + + Module* module_ = nullptr; + Func* current_func_ = nullptr; + ExprVisitor visitor_; + std::vector<std::string> param_and_local_index_to_name_; + std::vector<std::string> labels_; +}; + +NameApplier::NameApplier() : visitor_(this) {} + +void NameApplier::PushLabel(const std::string& label) { + labels_.push_back(label); +} + +void NameApplier::PopLabel() { + labels_.pop_back(); +} + +std::string_view NameApplier::FindLabelByVar(Var* var) { + if (var->is_name()) { + for (int i = labels_.size() - 1; i >= 0; --i) { + const std::string& label = labels_[i]; + if (label == var->name()) { + return label; + } + } + return std::string_view(); + } else { + if (var->index() >= labels_.size()) { + return std::string_view(); + } + return labels_[labels_.size() - 1 - var->index()]; + } +} + +void NameApplier::UseNameForVar(std::string_view name, Var* var) { + if (var->is_name()) { + assert(name == var->name()); + return; + } + + if (!name.empty()) { + var->set_name(name); + } +} + +Result NameApplier::UseNameForFuncTypeVar(Var* var) { + FuncType* func_type = module_->GetFuncType(*var); + if (!func_type) { + return Result::Error; + } + UseNameForVar(func_type->name, var); + return Result::Ok; +} + +Result NameApplier::UseNameForFuncVar(Var* var) { + Func* func = module_->GetFunc(*var); + if (!func) { + return Result::Error; + } + UseNameForVar(func->name, var); + return Result::Ok; +} + +Result NameApplier::UseNameForGlobalVar(Var* var) { + Global* global = module_->GetGlobal(*var); + if (!global) { + return Result::Error; + } + UseNameForVar(global->name, var); + return Result::Ok; +} + +Result NameApplier::UseNameForTableVar(Var* var) { + Table* table = module_->GetTable(*var); + if (!table) { + return Result::Error; + } + UseNameForVar(table->name, var); + return Result::Ok; +} + +Result NameApplier::UseNameForMemoryVar(Var* var) { + Memory* memory = module_->GetMemory(*var); + if (!memory) { + return Result::Error; + } + UseNameForVar(memory->name, var); + return Result::Ok; +} + +Result NameApplier::UseNameForTagVar(Var* var) { + Tag* tag = module_->GetTag(*var); + if (!tag) { + return Result::Error; + } + UseNameForVar(tag->name, var); + return Result::Ok; +} + +Result NameApplier::UseNameForDataSegmentVar(Var* var) { + DataSegment* data_segment = module_->GetDataSegment(*var); + if (!data_segment) { + return Result::Error; + } + UseNameForVar(data_segment->name, var); + return Result::Ok; +} + +Result NameApplier::UseNameForElemSegmentVar(Var* var) { + ElemSegment* elem_segment = module_->GetElemSegment(*var); + if (!elem_segment) { + return Result::Error; + } + UseNameForVar(elem_segment->name, var); + return Result::Ok; +} + +Result NameApplier::UseNameForParamAndLocalVar(Func* func, Var* var) { + Index local_index = func->GetLocalIndex(*var); + if (local_index >= func->GetNumParamsAndLocals()) { + return Result::Error; + } + + std::string name = param_and_local_index_to_name_[local_index]; + if (var->is_name()) { + assert(name == var->name()); + return Result::Ok; + } + + if (!name.empty()) { + var->set_name(name); + } + return Result::Ok; +} + +Result NameApplier::BeginBlockExpr(BlockExpr* expr) { + PushLabel(expr->block.label); + return Result::Ok; +} + +Result NameApplier::EndBlockExpr(BlockExpr* expr) { + PopLabel(); + return Result::Ok; +} + +Result NameApplier::BeginLoopExpr(LoopExpr* expr) { + PushLabel(expr->block.label); + return Result::Ok; +} + +Result NameApplier::EndLoopExpr(LoopExpr* expr) { + PopLabel(); + return Result::Ok; +} + +Result NameApplier::OnDataDropExpr(DataDropExpr* expr) { + CHECK_RESULT(UseNameForDataSegmentVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnMemoryCopyExpr(MemoryCopyExpr* expr) { + CHECK_RESULT(UseNameForMemoryVar(&expr->srcmemidx)); + CHECK_RESULT(UseNameForMemoryVar(&expr->destmemidx)); + return Result::Ok; +} + +Result NameApplier::OnMemoryFillExpr(MemoryFillExpr* expr) { + CHECK_RESULT(UseNameForMemoryVar(&expr->memidx)); + return Result::Ok; +} + +Result NameApplier::OnMemoryGrowExpr(MemoryGrowExpr* expr) { + CHECK_RESULT(UseNameForMemoryVar(&expr->memidx)); + return Result::Ok; +} + +Result NameApplier::OnMemoryInitExpr(MemoryInitExpr* expr) { + CHECK_RESULT(UseNameForDataSegmentVar(&expr->var)); + CHECK_RESULT(UseNameForMemoryVar(&expr->memidx)); + return Result::Ok; +} + +Result NameApplier::OnMemorySizeExpr(MemorySizeExpr* expr) { + CHECK_RESULT(UseNameForMemoryVar(&expr->memidx)); + return Result::Ok; +} + +Result NameApplier::OnElemDropExpr(ElemDropExpr* expr) { + CHECK_RESULT(UseNameForElemSegmentVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnTableCopyExpr(TableCopyExpr* expr) { + CHECK_RESULT(UseNameForTableVar(&expr->dst_table)); + CHECK_RESULT(UseNameForTableVar(&expr->src_table)); + return Result::Ok; +} + +Result NameApplier::OnTableInitExpr(TableInitExpr* expr) { + CHECK_RESULT(UseNameForElemSegmentVar(&expr->segment_index)); + CHECK_RESULT(UseNameForTableVar(&expr->table_index)); + return Result::Ok; +} + +Result NameApplier::OnTableGetExpr(TableGetExpr* expr) { + CHECK_RESULT(UseNameForTableVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnTableSetExpr(TableSetExpr* expr) { + CHECK_RESULT(UseNameForTableVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnTableGrowExpr(TableGrowExpr* expr) { + CHECK_RESULT(UseNameForTableVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnTableSizeExpr(TableSizeExpr* expr) { + CHECK_RESULT(UseNameForTableVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnTableFillExpr(TableFillExpr* expr) { + CHECK_RESULT(UseNameForTableVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnStoreExpr(StoreExpr* expr) { + CHECK_RESULT(UseNameForMemoryVar(&expr->memidx)); + return Result::Ok; +} + +Result NameApplier::OnBrExpr(BrExpr* expr) { + std::string_view label = FindLabelByVar(&expr->var); + UseNameForVar(label, &expr->var); + return Result::Ok; +} + +Result NameApplier::OnBrIfExpr(BrIfExpr* expr) { + std::string_view label = FindLabelByVar(&expr->var); + UseNameForVar(label, &expr->var); + return Result::Ok; +} + +Result NameApplier::OnBrTableExpr(BrTableExpr* expr) { + for (Var& target : expr->targets) { + std::string_view label = FindLabelByVar(&target); + UseNameForVar(label, &target); + } + + std::string_view label = FindLabelByVar(&expr->default_target); + UseNameForVar(label, &expr->default_target); + return Result::Ok; +} + +Result NameApplier::BeginTryExpr(TryExpr* expr) { + PushLabel(expr->block.label); + return Result::Ok; +} + +Result NameApplier::EndTryExpr(TryExpr*) { + PopLabel(); + return Result::Ok; +} + +Result NameApplier::OnCatchExpr(TryExpr*, Catch* expr) { + if (!expr->IsCatchAll()) { + CHECK_RESULT(UseNameForTagVar(&expr->var)); + } + return Result::Ok; +} + +Result NameApplier::OnDelegateExpr(TryExpr* expr) { + PopLabel(); + std::string_view label = FindLabelByVar(&expr->delegate_target); + UseNameForVar(label, &expr->delegate_target); + return Result::Ok; +} + +Result NameApplier::OnThrowExpr(ThrowExpr* expr) { + CHECK_RESULT(UseNameForTagVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnRethrowExpr(RethrowExpr* expr) { + std::string_view label = FindLabelByVar(&expr->var); + UseNameForVar(label, &expr->var); + return Result::Ok; +} + +Result NameApplier::OnCallExpr(CallExpr* expr) { + CHECK_RESULT(UseNameForFuncVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnRefFuncExpr(RefFuncExpr* expr) { + CHECK_RESULT(UseNameForFuncVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnCallIndirectExpr(CallIndirectExpr* expr) { + if (expr->decl.has_func_type) { + CHECK_RESULT(UseNameForFuncTypeVar(&expr->decl.type_var)); + } + CHECK_RESULT(UseNameForTableVar(&expr->table)); + return Result::Ok; +} + +Result NameApplier::OnReturnCallExpr(ReturnCallExpr* expr) { + CHECK_RESULT(UseNameForFuncVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnReturnCallIndirectExpr(ReturnCallIndirectExpr* expr) { + if (expr->decl.has_func_type) { + CHECK_RESULT(UseNameForFuncTypeVar(&expr->decl.type_var)); + } + CHECK_RESULT(UseNameForTableVar(&expr->table)); + return Result::Ok; +} + +Result NameApplier::OnGlobalGetExpr(GlobalGetExpr* expr) { + CHECK_RESULT(UseNameForGlobalVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnLocalGetExpr(LocalGetExpr* expr) { + CHECK_RESULT(UseNameForParamAndLocalVar(current_func_, &expr->var)); + return Result::Ok; +} + +Result NameApplier::BeginIfExpr(IfExpr* expr) { + PushLabel(expr->true_.label); + return Result::Ok; +} + +Result NameApplier::EndIfExpr(IfExpr* expr) { + PopLabel(); + return Result::Ok; +} + +Result NameApplier::OnLoadExpr(LoadExpr* expr) { + CHECK_RESULT(UseNameForMemoryVar(&expr->memidx)); + return Result::Ok; +} + +Result NameApplier::OnGlobalSetExpr(GlobalSetExpr* expr) { + CHECK_RESULT(UseNameForGlobalVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnLocalSetExpr(LocalSetExpr* expr) { + CHECK_RESULT(UseNameForParamAndLocalVar(current_func_, &expr->var)); + return Result::Ok; +} + +Result NameApplier::OnLocalTeeExpr(LocalTeeExpr* expr) { + CHECK_RESULT(UseNameForParamAndLocalVar(current_func_, &expr->var)); + return Result::Ok; +} + +Result NameApplier::OnSimdLoadLaneExpr(SimdLoadLaneExpr* expr) { + CHECK_RESULT(UseNameForMemoryVar(&expr->memidx)); + return Result::Ok; +} + +Result NameApplier::OnSimdStoreLaneExpr(SimdStoreLaneExpr* expr) { + CHECK_RESULT(UseNameForMemoryVar(&expr->memidx)); + return Result::Ok; +} + +Result NameApplier::VisitFunc(Index func_index, Func* func) { + current_func_ = func; + if (func->decl.has_func_type) { + CHECK_RESULT(UseNameForFuncTypeVar(&func->decl.type_var)); + } + + MakeTypeBindingReverseMapping(func->GetNumParamsAndLocals(), func->bindings, + ¶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_) { + switch (export_->kind) { + case ExternalKind::Func: + UseNameForFuncVar(&export_->var); + break; + + case ExternalKind::Table: + UseNameForTableVar(&export_->var); + break; + + case ExternalKind::Memory: + UseNameForMemoryVar(&export_->var); + break; + + case ExternalKind::Global: + UseNameForGlobalVar(&export_->var); + break; + + case ExternalKind::Tag: + UseNameForTagVar(&export_->var); + break; + } + return Result::Ok; +} + +Result NameApplier::VisitElemSegment(Index elem_segment_index, + ElemSegment* segment) { + CHECK_RESULT(UseNameForTableVar(&segment->table_var)); + CHECK_RESULT(visitor_.VisitExprList(segment->offset)); + for (ExprList& elem_expr : segment->elem_exprs) { + Expr* expr = &elem_expr.front(); + if (expr->type() == ExprType::RefFunc) { + CHECK_RESULT(UseNameForFuncVar(&cast<RefFuncExpr>(expr)->var)); + } + } + return Result::Ok; +} + +Result NameApplier::VisitDataSegment(Index data_segment_index, + DataSegment* segment) { + CHECK_RESULT(UseNameForMemoryVar(&segment->memory_var)); + CHECK_RESULT(visitor_.VisitExprList(segment->offset)); + return Result::Ok; +} + +Result NameApplier::VisitStart(Var* start_var) { + CHECK_RESULT(UseNameForFuncVar(start_var)); + return Result::Ok; +} + +Result NameApplier::VisitModule(Module* module) { + module_ = module; + for (size_t i = 0; i < module->funcs.size(); ++i) + CHECK_RESULT(VisitFunc(i, module->funcs[i])); + for (size_t i = 0; i < module->globals.size(); ++i) + CHECK_RESULT(VisitGlobal(module->globals[i])); + for (size_t i = 0; i < module->tags.size(); ++i) + CHECK_RESULT(VisitTag(module->tags[i])); + for (size_t i = 0; i < module->exports.size(); ++i) + CHECK_RESULT(VisitExport(i, module->exports[i])); + for (size_t i = 0; i < module->elem_segments.size(); ++i) + CHECK_RESULT(VisitElemSegment(i, module->elem_segments[i])); + for (size_t i = 0; i < module->data_segments.size(); ++i) + CHECK_RESULT(VisitDataSegment(i, module->data_segments[i])); + for (size_t i = 0; i < module->starts.size(); ++i) + CHECK_RESULT(VisitStart(module->starts[i])); + module_ = nullptr; + return Result::Ok; +} + +} // end anonymous namespace + +Result ApplyNames(Module* module) { + NameApplier applier; + return applier.VisitModule(module); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary-reader-ir.cc b/third_party/wasm2c/src/binary-reader-ir.cc new file mode 100644 index 0000000000..9eba058b00 --- /dev/null +++ b/third_party/wasm2c/src/binary-reader-ir.cc @@ -0,0 +1,1757 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/binary-reader-ir.h" + +#include <cassert> +#include <cinttypes> +#include <cstdarg> +#include <cstdint> +#include <cstdio> +#include <deque> +#include <vector> + +#include "wabt/binary-reader-nop.h" +#include "wabt/cast.h" +#include "wabt/common.h" +#include "wabt/ir.h" + +namespace wabt { + +namespace { + +struct LabelNode { + LabelNode(LabelType, ExprList* exprs, Expr* context = nullptr); + + LabelType label_type; + ExprList* exprs; + Expr* context; +}; + +LabelNode::LabelNode(LabelType label_type, ExprList* exprs, Expr* context) + : label_type(label_type), exprs(exprs), context(context) {} + +class CodeMetadataExprQueue { + private: + struct Entry { + Func* func; + std::deque<std::unique_ptr<CodeMetadataExpr>> func_queue; + Entry(Func* f) : func(f) {} + }; + std::deque<Entry> entries; + + public: + CodeMetadataExprQueue() {} + void push_func(Func* f) { entries.emplace_back(f); } + void push_metadata(std::unique_ptr<CodeMetadataExpr> meta) { + assert(!entries.empty()); + entries.back().func_queue.push_back(std::move(meta)); + } + + std::unique_ptr<CodeMetadataExpr> pop_match(Func* f, Offset offset) { + std::unique_ptr<CodeMetadataExpr> ret; + if (entries.empty()) { + return ret; + } + + auto& current_entry = entries.front(); + + if (current_entry.func != f) + return ret; + if (current_entry.func_queue.empty()) { + entries.pop_front(); + return ret; + } + + auto& current_metadata = current_entry.func_queue.front(); + if (current_metadata->loc.offset + current_entry.func->loc.offset != + offset) { + return ret; + } + + current_metadata->loc = Location(offset); + ret = std::move(current_metadata); + current_entry.func_queue.pop_front(); + + return ret; + } +}; + +class BinaryReaderIR : public BinaryReaderNop { + static constexpr size_t kMaxNestingDepth = 16384; // max depth of label stack + static constexpr size_t kMaxFunctionLocals = 50000; // matches V8 + static constexpr size_t kMaxFunctionParams = 1000; // matches V8 + static constexpr size_t kMaxFunctionResults = 1000; // matches V8 + + public: + BinaryReaderIR(Module* out_module, const char* filename, Errors* errors); + + bool OnError(const Error&) override; + + Result OnTypeCount(Index count) override; + Result OnFuncType(Index index, + Index param_count, + Type* param_types, + Index result_count, + Type* result_types) override; + Result OnStructType(Index index, Index field_count, TypeMut* fields) override; + Result OnArrayType(Index index, TypeMut field) override; + + Result OnImportCount(Index count) override; + Result OnImportFunc(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index func_index, + Index sig_index) override; + Result OnImportTable(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) override; + Result OnImportMemory(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index memory_index, + const Limits* page_limits) override; + Result OnImportGlobal(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index global_index, + Type type, + bool mutable_) override; + Result OnImportTag(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index tag_index, + Index sig_index) override; + + Result OnFunctionCount(Index count) override; + Result OnFunction(Index index, Index sig_index) override; + + Result OnTableCount(Index count) override; + Result OnTable(Index index, + Type elem_type, + const Limits* elem_limits) override; + + Result OnMemoryCount(Index count) override; + Result OnMemory(Index index, const Limits* limits) override; + + Result OnGlobalCount(Index count) override; + Result BeginGlobal(Index index, Type type, bool mutable_) override; + Result BeginGlobalInitExpr(Index index) override; + Result EndGlobalInitExpr(Index index) override; + + Result OnExportCount(Index count) override; + Result OnExport(Index index, + ExternalKind kind, + Index item_index, + std::string_view name) override; + + Result OnStartFunction(Index func_index) override; + + Result OnFunctionBodyCount(Index count) override; + Result BeginFunctionBody(Index index, Offset size) override; + Result OnLocalDecl(Index decl_index, Index count, Type type) override; + + Result OnOpcode(Opcode opcode) override; + Result OnAtomicLoadExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnAtomicStoreExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnAtomicRmwExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnAtomicRmwCmpxchgExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnAtomicWaitExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnAtomicFenceExpr(uint32_t consistency_model) override; + Result OnAtomicNotifyExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnBinaryExpr(Opcode opcode) override; + Result OnBlockExpr(Type sig_type) override; + Result OnBrExpr(Index depth) override; + Result OnBrIfExpr(Index depth) override; + Result OnBrTableExpr(Index num_targets, + Index* target_depths, + Index default_target_depth) override; + Result OnCallExpr(Index func_index) override; + Result OnCatchExpr(Index tag_index) override; + Result OnCatchAllExpr() override; + Result OnCallIndirectExpr(Index sig_index, Index table_index) override; + Result OnCallRefExpr() override; + Result OnReturnCallExpr(Index func_index) override; + Result OnReturnCallIndirectExpr(Index sig_index, Index table_index) override; + Result OnCompareExpr(Opcode opcode) override; + Result OnConvertExpr(Opcode opcode) override; + Result OnDelegateExpr(Index depth) override; + Result OnDropExpr() override; + Result OnElseExpr() override; + Result OnEndExpr() override; + Result OnF32ConstExpr(uint32_t value_bits) override; + Result OnF64ConstExpr(uint64_t value_bits) override; + Result OnV128ConstExpr(v128 value_bits) override; + Result OnGlobalGetExpr(Index global_index) override; + Result OnGlobalSetExpr(Index global_index) override; + Result OnI32ConstExpr(uint32_t value) override; + Result OnI64ConstExpr(uint64_t value) override; + Result OnIfExpr(Type sig_type) override; + Result OnLoadExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnLocalGetExpr(Index local_index) override; + Result OnLocalSetExpr(Index local_index) override; + Result OnLocalTeeExpr(Index local_index) override; + Result OnLoopExpr(Type sig_type) override; + Result OnMemoryCopyExpr(Index srcmemidx, Index destmemidx) override; + Result OnDataDropExpr(Index segment_index) override; + Result OnMemoryFillExpr(Index memidx) override; + Result OnMemoryGrowExpr(Index memidx) override; + Result OnMemoryInitExpr(Index segment_index, Index memidx) override; + Result OnMemorySizeExpr(Index memidx) override; + Result OnTableCopyExpr(Index dst_index, Index src_index) override; + Result OnElemDropExpr(Index segment_index) override; + Result OnTableInitExpr(Index segment_index, Index table_index) override; + Result OnTableGetExpr(Index table_index) override; + Result OnTableSetExpr(Index table_index) override; + Result OnTableGrowExpr(Index table_index) override; + Result OnTableSizeExpr(Index table_index) override; + Result OnTableFillExpr(Index table_index) override; + Result OnRefFuncExpr(Index func_index) override; + Result OnRefNullExpr(Type type) override; + Result OnRefIsNullExpr() override; + Result OnNopExpr() override; + Result OnRethrowExpr(Index depth) override; + Result OnReturnExpr() override; + Result OnSelectExpr(Index result_count, Type* result_types) override; + Result OnStoreExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnThrowExpr(Index tag_index) override; + Result OnTryExpr(Type sig_type) override; + Result OnUnaryExpr(Opcode opcode) override; + Result OnTernaryExpr(Opcode opcode) override; + Result OnUnreachableExpr() override; + Result EndFunctionBody(Index index) override; + Result OnSimdLaneOpExpr(Opcode opcode, uint64_t value) override; + Result OnSimdLoadLaneExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset, + uint64_t value) override; + Result OnSimdStoreLaneExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset, + uint64_t value) override; + Result OnSimdShuffleOpExpr(Opcode opcode, v128 value) override; + Result OnLoadSplatExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnLoadZeroExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + + Result OnElemSegmentCount(Index count) override; + Result BeginElemSegment(Index index, + Index table_index, + uint8_t flags) override; + Result BeginElemSegmentInitExpr(Index index) override; + Result EndElemSegmentInitExpr(Index index) override; + Result OnElemSegmentElemType(Index index, Type elem_type) override; + Result OnElemSegmentElemExprCount(Index index, Index count) override; + Result OnElemSegmentElemExpr_RefNull(Index segment_index, Type type) override; + Result OnElemSegmentElemExpr_RefFunc(Index segment_index, + Index func_index) override; + + Result OnDataSegmentCount(Index count) override; + Result BeginDataSegment(Index index, + Index memory_index, + uint8_t flags) override; + Result BeginDataSegmentInitExpr(Index index) override; + Result EndDataSegmentInitExpr(Index index) override; + Result OnDataSegmentData(Index index, + const void* data, + Address size) override; + + Result OnModuleName(std::string_view module_name) override; + Result OnFunctionNamesCount(Index num_functions) override; + Result OnFunctionName(Index function_index, + std::string_view function_name) override; + Result OnLocalNameLocalCount(Index function_index, Index num_locals) override; + Result OnLocalName(Index function_index, + Index local_index, + std::string_view local_name) override; + Result OnNameEntry(NameSectionSubsection type, + Index index, + std::string_view name) override; + + Result BeginTagSection(Offset size) override { return Result::Ok; } + Result OnTagCount(Index count) override { return Result::Ok; } + Result OnTagType(Index index, Index sig_index) override; + Result EndTagSection() override { return Result::Ok; } + + Result OnDataSymbol(Index index, + uint32_t flags, + std::string_view name, + Index segment, + uint32_t offset, + uint32_t size) override; + Result OnFunctionSymbol(Index index, + uint32_t flags, + std::string_view name, + Index func_index) override; + Result OnGlobalSymbol(Index index, + uint32_t flags, + std::string_view name, + Index global_index) override; + Result OnSectionSymbol(Index index, + uint32_t flags, + Index section_index) override; + /* Code Metadata sections */ + Result BeginCodeMetadataSection(std::string_view name, Offset size) override; + Result OnCodeMetadataFuncCount(Index count) override; + Result OnCodeMetadataCount(Index function_index, Index count) override; + Result OnCodeMetadata(Offset offset, const void* data, Address size) override; + + Result OnTagSymbol(Index index, + uint32_t flags, + std::string_view name, + Index tag_index) override; + Result OnTableSymbol(Index index, + uint32_t flags, + std::string_view name, + Index table_index) override; + + private: + Location GetLocation() const; + void PrintError(const char* format, ...); + Result PushLabel(LabelType label_type, + ExprList* first, + Expr* context = nullptr); + Result BeginInitExpr(ExprList* init_expr); + Result EndInitExpr(); + Result PopLabel(); + Result GetLabelAt(LabelNode** label, Index depth); + Result TopLabel(LabelNode** label); + Result TopLabelExpr(LabelNode** label, Expr** expr); + Result AppendExpr(std::unique_ptr<Expr> expr); + Result AppendCatch(Catch&& catch_); + void SetFuncDeclaration(FuncDeclaration* decl, Var var); + void SetBlockDeclaration(BlockDeclaration* decl, Type sig_type); + Result SetMemoryName(Index index, std::string_view name); + Result SetTableName(Index index, std::string_view name); + Result SetFunctionName(Index index, std::string_view name); + Result SetTypeName(Index index, std::string_view name); + Result SetGlobalName(Index index, std::string_view name); + Result SetDataSegmentName(Index index, std::string_view name); + Result SetElemSegmentName(Index index, std::string_view name); + Result SetTagName(Index index, std::string_view name); + + std::string GetUniqueName(BindingHash* bindings, + const std::string& original_name); + + Errors* errors_ = nullptr; + Module* module_ = nullptr; + + Func* current_func_ = nullptr; + std::vector<LabelNode> label_stack_; + const char* filename_; + + CodeMetadataExprQueue code_metadata_queue_; + std::string_view current_metadata_name_; +}; + +BinaryReaderIR::BinaryReaderIR(Module* out_module, + const char* filename, + Errors* errors) + : errors_(errors), module_(out_module), filename_(filename) {} + +Location BinaryReaderIR::GetLocation() const { + Location loc; + loc.filename = filename_; + loc.offset = state->offset; + return loc; +} + +void WABT_PRINTF_FORMAT(2, 3) BinaryReaderIR::PrintError(const char* format, + ...) { + WABT_SNPRINTF_ALLOCA(buffer, length, format); + errors_->emplace_back(ErrorLevel::Error, Location(kInvalidOffset), buffer); +} + +Result BinaryReaderIR::PushLabel(LabelType label_type, + ExprList* first, + Expr* context) { + if (label_stack_.size() >= kMaxNestingDepth) { + PrintError("label stack exceeds max nesting depth"); + return Result::Error; + } + label_stack_.emplace_back(label_type, first, context); + return Result::Ok; +} + +Result BinaryReaderIR::PopLabel() { + if (label_stack_.size() == 0) { + PrintError("popping empty label stack"); + return Result::Error; + } + + label_stack_.pop_back(); + return Result::Ok; +} + +Result BinaryReaderIR::GetLabelAt(LabelNode** label, Index depth) { + if (depth >= label_stack_.size()) { + PrintError("accessing stack depth: %" PRIindex " >= max: %" PRIzd, depth, + label_stack_.size()); + return Result::Error; + } + + *label = &label_stack_[label_stack_.size() - depth - 1]; + return Result::Ok; +} + +Result BinaryReaderIR::TopLabel(LabelNode** label) { + return GetLabelAt(label, 0); +} + +Result BinaryReaderIR::TopLabelExpr(LabelNode** label, Expr** expr) { + CHECK_RESULT(TopLabel(label)); + LabelNode* parent_label; + CHECK_RESULT(GetLabelAt(&parent_label, 1)); + if (parent_label->exprs->empty()) { + PrintError("TopLabelExpr: parent label has empty expr list"); + return Result::Error; + } + *expr = &parent_label->exprs->back(); + return Result::Ok; +} + +Result BinaryReaderIR::AppendExpr(std::unique_ptr<Expr> expr) { + expr->loc = GetLocation(); + LabelNode* label; + CHECK_RESULT(TopLabel(&label)); + label->exprs->push_back(std::move(expr)); + return Result::Ok; +} + +void BinaryReaderIR::SetFuncDeclaration(FuncDeclaration* decl, Var var) { + decl->has_func_type = true; + decl->type_var = var; + if (auto* func_type = module_->GetFuncType(var)) { + decl->sig = func_type->sig; + } +} + +void BinaryReaderIR::SetBlockDeclaration(BlockDeclaration* decl, + Type sig_type) { + if (sig_type.IsIndex()) { + Index type_index = sig_type.GetIndex(); + SetFuncDeclaration(decl, Var(type_index, GetLocation())); + } else { + decl->has_func_type = false; + decl->sig.param_types.clear(); + decl->sig.result_types = sig_type.GetInlineVector(); + } +} + +std::string BinaryReaderIR::GetUniqueName(BindingHash* bindings, + const std::string& orig_name) { + int counter = 1; + std::string unique_name = orig_name; + while (bindings->count(unique_name) != 0) { + unique_name = orig_name + "." + std::to_string(counter++); + } + return unique_name; +} + +bool BinaryReaderIR::OnError(const Error& error) { + errors_->push_back(error); + return true; +} + +Result BinaryReaderIR::OnTypeCount(Index count) { + WABT_TRY + module_->types.reserve(count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::OnFuncType(Index index, + Index param_count, + Type* param_types, + Index result_count, + Type* result_types) { + if (param_count > kMaxFunctionParams) { + PrintError("FuncType param count exceeds maximum value"); + return Result::Error; + } + + if (result_count > kMaxFunctionResults) { + PrintError("FuncType result count exceeds maximum value"); + return Result::Error; + } + + auto field = std::make_unique<TypeModuleField>(GetLocation()); + auto func_type = std::make_unique<FuncType>(); + func_type->sig.param_types.assign(param_types, param_types + param_count); + func_type->sig.result_types.assign(result_types, result_types + result_count); + field->type = std::move(func_type); + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::OnStructType(Index index, + Index field_count, + TypeMut* fields) { + auto field = std::make_unique<TypeModuleField>(GetLocation()); + auto struct_type = std::make_unique<StructType>(); + struct_type->fields.resize(field_count); + for (Index i = 0; i < field_count; ++i) { + struct_type->fields[i].type = fields[i].type; + struct_type->fields[i].mutable_ = fields[i].mutable_; + } + field->type = std::move(struct_type); + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::OnArrayType(Index index, TypeMut type_mut) { + auto field = std::make_unique<TypeModuleField>(GetLocation()); + auto array_type = std::make_unique<ArrayType>(); + array_type->field.type = type_mut.type; + array_type->field.mutable_ = type_mut.mutable_; + field->type = std::move(array_type); + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::OnImportCount(Index count) { + WABT_TRY + module_->imports.reserve(count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::OnImportFunc(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index func_index, + Index sig_index) { + auto import = std::make_unique<FuncImport>(); + import->module_name = module_name; + import->field_name = field_name; + SetFuncDeclaration(&import->func.decl, Var(sig_index, GetLocation())); + module_->AppendField( + std::make_unique<ImportModuleField>(std::move(import), GetLocation())); + return Result::Ok; +} + +Result BinaryReaderIR::OnImportTable(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) { + auto import = std::make_unique<TableImport>(); + import->module_name = module_name; + import->field_name = field_name; + import->table.elem_limits = *elem_limits; + import->table.elem_type = elem_type; + module_->AppendField( + std::make_unique<ImportModuleField>(std::move(import), GetLocation())); + return Result::Ok; +} + +Result BinaryReaderIR::OnImportMemory(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index memory_index, + const Limits* page_limits) { + auto import = std::make_unique<MemoryImport>(); + import->module_name = module_name; + import->field_name = field_name; + import->memory.page_limits = *page_limits; + module_->AppendField( + std::make_unique<ImportModuleField>(std::move(import), GetLocation())); + return Result::Ok; +} + +Result BinaryReaderIR::OnImportGlobal(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index global_index, + Type type, + bool mutable_) { + auto import = std::make_unique<GlobalImport>(); + import->module_name = module_name; + import->field_name = field_name; + import->global.type = type; + import->global.mutable_ = mutable_; + module_->AppendField( + std::make_unique<ImportModuleField>(std::move(import), GetLocation())); + return Result::Ok; +} + +Result BinaryReaderIR::OnImportTag(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index tag_index, + Index sig_index) { + auto import = std::make_unique<TagImport>(); + import->module_name = module_name; + import->field_name = field_name; + SetFuncDeclaration(&import->tag.decl, Var(sig_index, GetLocation())); + module_->AppendField( + std::make_unique<ImportModuleField>(std::move(import), GetLocation())); + return Result::Ok; +} + +Result BinaryReaderIR::OnFunctionCount(Index count) { + WABT_TRY + module_->funcs.reserve(module_->num_func_imports + count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::OnFunction(Index index, Index sig_index) { + auto field = std::make_unique<FuncModuleField>(GetLocation()); + Func& func = field->func; + SetFuncDeclaration(&func.decl, Var(sig_index, GetLocation())); + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::OnTableCount(Index count) { + WABT_TRY + module_->tables.reserve(module_->num_table_imports + count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::OnTable(Index index, + Type elem_type, + const Limits* elem_limits) { + auto field = std::make_unique<TableModuleField>(GetLocation()); + Table& table = field->table; + table.elem_limits = *elem_limits; + table.elem_type = elem_type; + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::OnMemoryCount(Index count) { + WABT_TRY + module_->memories.reserve(module_->num_memory_imports + count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::OnMemory(Index index, const Limits* page_limits) { + auto field = std::make_unique<MemoryModuleField>(GetLocation()); + Memory& memory = field->memory; + memory.page_limits = *page_limits; + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::OnGlobalCount(Index count) { + WABT_TRY + module_->globals.reserve(module_->num_global_imports + count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::BeginGlobal(Index index, Type type, bool mutable_) { + auto field = std::make_unique<GlobalModuleField>(GetLocation()); + Global& global = field->global; + global.type = type; + global.mutable_ = mutable_; + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::BeginGlobalInitExpr(Index index) { + assert(index == module_->globals.size() - 1); + Global* global = module_->globals[index]; + return BeginInitExpr(&global->init_expr); +} + +Result BinaryReaderIR::EndGlobalInitExpr(Index index) { + return EndInitExpr(); +} + +Result BinaryReaderIR::OnExportCount(Index count) { + WABT_TRY + module_->exports.reserve(count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::OnExport(Index index, + ExternalKind kind, + Index item_index, + std::string_view name) { + auto field = std::make_unique<ExportModuleField>(GetLocation()); + Export& export_ = field->export_; + export_.name = name; + export_.var = Var(item_index, GetLocation()); + export_.kind = kind; + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::OnStartFunction(Index func_index) { + Var start(func_index, GetLocation()); + module_->AppendField( + std::make_unique<StartModuleField>(start, GetLocation())); + return Result::Ok; +} + +Result BinaryReaderIR::OnFunctionBodyCount(Index count) { + // Can hit this case on a malformed module if we don't stop on first error. + if (module_->num_func_imports + count != module_->funcs.size()) { + PrintError( + "number of imported func + func count in code section does not match " + "actual number of funcs in module"); + return Result::Error; + } + return Result::Ok; +} + +Result BinaryReaderIR::BeginFunctionBody(Index index, Offset size) { + current_func_ = module_->funcs[index]; + current_func_->loc = GetLocation(); + return PushLabel(LabelType::Func, ¤t_func_->exprs); +} + +Result BinaryReaderIR::OnLocalDecl(Index decl_index, Index count, Type type) { + current_func_->local_types.AppendDecl(type, count); + + if (current_func_->GetNumLocals() > kMaxFunctionLocals) { + PrintError("function local count exceeds maximum value"); + return Result::Error; + } + + return Result::Ok; +} + +Result BinaryReaderIR::OnOpcode(Opcode opcode) { + std::unique_ptr<CodeMetadataExpr> metadata = + code_metadata_queue_.pop_match(current_func_, GetLocation().offset - 1); + if (metadata) { + return AppendExpr(std::move(metadata)); + } + return Result::Ok; +} + +Result BinaryReaderIR::OnAtomicLoadExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique<AtomicLoadExpr>( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnAtomicStoreExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique<AtomicStoreExpr>( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnAtomicRmwExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique<AtomicRmwExpr>( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnAtomicRmwCmpxchgExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique<AtomicRmwCmpxchgExpr>( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnAtomicWaitExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique<AtomicWaitExpr>( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnAtomicFenceExpr(uint32_t consistency_model) { + return AppendExpr(std::make_unique<AtomicFenceExpr>(consistency_model)); +} + +Result BinaryReaderIR::OnAtomicNotifyExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique<AtomicNotifyExpr>( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnBinaryExpr(Opcode opcode) { + return AppendExpr(std::make_unique<BinaryExpr>(opcode)); +} + +Result BinaryReaderIR::OnBlockExpr(Type sig_type) { + auto expr = std::make_unique<BlockExpr>(); + SetBlockDeclaration(&expr->block.decl, sig_type); + ExprList* expr_list = &expr->block.exprs; + CHECK_RESULT(AppendExpr(std::move(expr))); + return PushLabel(LabelType::Block, expr_list); +} + +Result BinaryReaderIR::OnBrExpr(Index depth) { + return AppendExpr(std::make_unique<BrExpr>(Var(depth, GetLocation()))); +} + +Result BinaryReaderIR::OnBrIfExpr(Index depth) { + return AppendExpr(std::make_unique<BrIfExpr>(Var(depth, GetLocation()))); +} + +Result BinaryReaderIR::OnBrTableExpr(Index num_targets, + Index* target_depths, + Index default_target_depth) { + auto expr = std::make_unique<BrTableExpr>(); + expr->default_target = Var(default_target_depth, GetLocation()); + expr->targets.resize(num_targets); + for (Index i = 0; i < num_targets; ++i) { + expr->targets[i] = Var(target_depths[i], GetLocation()); + } + return AppendExpr(std::move(expr)); +} + +Result BinaryReaderIR::OnCallExpr(Index func_index) { + return AppendExpr(std::make_unique<CallExpr>(Var(func_index, GetLocation()))); +} + +Result BinaryReaderIR::OnCallIndirectExpr(Index sig_index, Index table_index) { + auto expr = std::make_unique<CallIndirectExpr>(); + SetFuncDeclaration(&expr->decl, Var(sig_index, GetLocation())); + expr->table = Var(table_index, GetLocation()); + return AppendExpr(std::move(expr)); +} + +Result BinaryReaderIR::OnCallRefExpr() { + return AppendExpr(std::make_unique<CallRefExpr>()); +} + +Result BinaryReaderIR::OnReturnCallExpr(Index func_index) { + return AppendExpr( + std::make_unique<ReturnCallExpr>(Var(func_index, GetLocation()))); +} + +Result BinaryReaderIR::OnReturnCallIndirectExpr(Index sig_index, + Index table_index) { + auto expr = std::make_unique<ReturnCallIndirectExpr>(); + SetFuncDeclaration(&expr->decl, Var(sig_index, GetLocation())); + expr->table = Var(table_index, GetLocation()); + return AppendExpr(std::move(expr)); +} + +Result BinaryReaderIR::OnCompareExpr(Opcode opcode) { + return AppendExpr(std::make_unique<CompareExpr>(opcode)); +} + +Result BinaryReaderIR::OnConvertExpr(Opcode opcode) { + return AppendExpr(std::make_unique<ConvertExpr>(opcode)); +} + +Result BinaryReaderIR::OnDropExpr() { + return AppendExpr(std::make_unique<DropExpr>()); +} + +Result BinaryReaderIR::OnElseExpr() { + LabelNode* label; + Expr* expr; + CHECK_RESULT(TopLabelExpr(&label, &expr)); + + if (label->label_type == LabelType::If) { + auto* if_expr = cast<IfExpr>(expr); + if_expr->true_.end_loc = GetLocation(); + label->exprs = &if_expr->false_; + label->label_type = LabelType::Else; + } else { + PrintError("else expression without matching if"); + return Result::Error; + } + + return Result::Ok; +} + +Result BinaryReaderIR::OnEndExpr() { + if (label_stack_.size() > 1) { + LabelNode* label; + Expr* expr; + CHECK_RESULT(TopLabelExpr(&label, &expr)); + switch (label->label_type) { + case LabelType::Block: + cast<BlockExpr>(expr)->block.end_loc = GetLocation(); + break; + case LabelType::Loop: + cast<LoopExpr>(expr)->block.end_loc = GetLocation(); + break; + case LabelType::If: + cast<IfExpr>(expr)->true_.end_loc = GetLocation(); + break; + case LabelType::Else: + cast<IfExpr>(expr)->false_end_loc = GetLocation(); + break; + case LabelType::Try: + cast<TryExpr>(expr)->block.end_loc = GetLocation(); + break; + + case LabelType::InitExpr: + case LabelType::Func: + case LabelType::Catch: + break; + } + } + + return PopLabel(); +} + +Result BinaryReaderIR::OnF32ConstExpr(uint32_t value_bits) { + return AppendExpr( + std::make_unique<ConstExpr>(Const::F32(value_bits, GetLocation()))); +} + +Result BinaryReaderIR::OnF64ConstExpr(uint64_t value_bits) { + return AppendExpr( + std::make_unique<ConstExpr>(Const::F64(value_bits, GetLocation()))); +} + +Result BinaryReaderIR::OnV128ConstExpr(v128 value_bits) { + return AppendExpr( + std::make_unique<ConstExpr>(Const::V128(value_bits, GetLocation()))); +} + +Result BinaryReaderIR::OnGlobalGetExpr(Index global_index) { + return AppendExpr( + std::make_unique<GlobalGetExpr>(Var(global_index, GetLocation()))); +} + +Result BinaryReaderIR::OnLocalGetExpr(Index local_index) { + return AppendExpr( + std::make_unique<LocalGetExpr>(Var(local_index, GetLocation()))); +} + +Result BinaryReaderIR::OnI32ConstExpr(uint32_t value) { + return AppendExpr( + std::make_unique<ConstExpr>(Const::I32(value, GetLocation()))); +} + +Result BinaryReaderIR::OnI64ConstExpr(uint64_t value) { + return AppendExpr( + std::make_unique<ConstExpr>(Const::I64(value, GetLocation()))); +} + +Result BinaryReaderIR::OnIfExpr(Type sig_type) { + auto expr = std::make_unique<IfExpr>(); + SetBlockDeclaration(&expr->true_.decl, sig_type); + ExprList* expr_list = &expr->true_.exprs; + CHECK_RESULT(AppendExpr(std::move(expr))); + return PushLabel(LabelType::If, expr_list); +} + +Result BinaryReaderIR::OnLoadExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique<LoadExpr>( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnLoopExpr(Type sig_type) { + auto expr = std::make_unique<LoopExpr>(); + SetBlockDeclaration(&expr->block.decl, sig_type); + ExprList* expr_list = &expr->block.exprs; + CHECK_RESULT(AppendExpr(std::move(expr))); + return PushLabel(LabelType::Loop, expr_list); +} + +Result BinaryReaderIR::OnMemoryCopyExpr(Index srcmemidx, Index destmemidx) { + return AppendExpr(std::make_unique<MemoryCopyExpr>( + Var(srcmemidx, GetLocation()), Var(destmemidx, GetLocation()))); +} + +Result BinaryReaderIR::OnDataDropExpr(Index segment) { + return AppendExpr( + std::make_unique<DataDropExpr>(Var(segment, GetLocation()))); +} + +Result BinaryReaderIR::OnMemoryFillExpr(Index memidx) { + return AppendExpr( + std::make_unique<MemoryFillExpr>(Var(memidx, GetLocation()))); +} + +Result BinaryReaderIR::OnMemoryGrowExpr(Index memidx) { + return AppendExpr( + std::make_unique<MemoryGrowExpr>(Var(memidx, GetLocation()))); +} + +Result BinaryReaderIR::OnMemoryInitExpr(Index segment, Index memidx) { + return AppendExpr(std::make_unique<MemoryInitExpr>( + Var(segment, GetLocation()), Var(memidx, GetLocation()))); +} + +Result BinaryReaderIR::OnMemorySizeExpr(Index memidx) { + return AppendExpr( + std::make_unique<MemorySizeExpr>(Var(memidx, GetLocation()))); +} + +Result BinaryReaderIR::OnTableCopyExpr(Index dst_index, Index src_index) { + return AppendExpr(std::make_unique<TableCopyExpr>( + Var(dst_index, GetLocation()), Var(src_index, GetLocation()))); +} + +Result BinaryReaderIR::OnElemDropExpr(Index segment) { + return AppendExpr( + std::make_unique<ElemDropExpr>(Var(segment, GetLocation()))); +} + +Result BinaryReaderIR::OnTableInitExpr(Index segment, Index table_index) { + return AppendExpr(std::make_unique<TableInitExpr>( + Var(segment, GetLocation()), Var(table_index, GetLocation()))); +} + +Result BinaryReaderIR::OnTableGetExpr(Index table_index) { + return AppendExpr( + std::make_unique<TableGetExpr>(Var(table_index, GetLocation()))); +} + +Result BinaryReaderIR::OnTableSetExpr(Index table_index) { + return AppendExpr( + std::make_unique<TableSetExpr>(Var(table_index, GetLocation()))); +} + +Result BinaryReaderIR::OnTableGrowExpr(Index table_index) { + return AppendExpr( + std::make_unique<TableGrowExpr>(Var(table_index, GetLocation()))); +} + +Result BinaryReaderIR::OnTableSizeExpr(Index table_index) { + return AppendExpr( + std::make_unique<TableSizeExpr>(Var(table_index, GetLocation()))); +} + +Result BinaryReaderIR::OnTableFillExpr(Index table_index) { + return AppendExpr( + std::make_unique<TableFillExpr>(Var(table_index, GetLocation()))); +} + +Result BinaryReaderIR::OnRefFuncExpr(Index func_index) { + return AppendExpr( + std::make_unique<RefFuncExpr>(Var(func_index, GetLocation()))); +} + +Result BinaryReaderIR::OnRefNullExpr(Type type) { + return AppendExpr(std::make_unique<RefNullExpr>(type)); +} + +Result BinaryReaderIR::OnRefIsNullExpr() { + return AppendExpr(std::make_unique<RefIsNullExpr>()); +} + +Result BinaryReaderIR::OnNopExpr() { + return AppendExpr(std::make_unique<NopExpr>()); +} + +Result BinaryReaderIR::OnRethrowExpr(Index depth) { + return AppendExpr(std::make_unique<RethrowExpr>(Var(depth, GetLocation()))); +} + +Result BinaryReaderIR::OnReturnExpr() { + return AppendExpr(std::make_unique<ReturnExpr>()); +} + +Result BinaryReaderIR::OnSelectExpr(Index result_count, Type* result_types) { + TypeVector results; + results.assign(result_types, result_types + result_count); + return AppendExpr(std::make_unique<SelectExpr>(results)); +} + +Result BinaryReaderIR::OnGlobalSetExpr(Index global_index) { + return AppendExpr( + std::make_unique<GlobalSetExpr>(Var(global_index, GetLocation()))); +} + +Result BinaryReaderIR::OnLocalSetExpr(Index local_index) { + return AppendExpr( + std::make_unique<LocalSetExpr>(Var(local_index, GetLocation()))); +} + +Result BinaryReaderIR::OnStoreExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique<StoreExpr>( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnThrowExpr(Index tag_index) { + return AppendExpr(std::make_unique<ThrowExpr>(Var(tag_index, GetLocation()))); +} + +Result BinaryReaderIR::OnLocalTeeExpr(Index local_index) { + return AppendExpr( + std::make_unique<LocalTeeExpr>(Var(local_index, GetLocation()))); +} + +Result BinaryReaderIR::OnTryExpr(Type sig_type) { + auto expr_ptr = std::make_unique<TryExpr>(); + // Save expr so it can be used below, after expr_ptr has been moved. + TryExpr* expr = expr_ptr.get(); + ExprList* expr_list = &expr->block.exprs; + SetBlockDeclaration(&expr->block.decl, sig_type); + CHECK_RESULT(AppendExpr(std::move(expr_ptr))); + return PushLabel(LabelType::Try, expr_list, expr); +} + +Result BinaryReaderIR::AppendCatch(Catch&& catch_) { + LabelNode* label = nullptr; + CHECK_RESULT(TopLabel(&label)); + + if (label->label_type != LabelType::Try) { + PrintError("catch not inside try block"); + return Result::Error; + } + + auto* try_ = cast<TryExpr>(label->context); + + if (catch_.IsCatchAll() && !try_->catches.empty() && + try_->catches.back().IsCatchAll()) { + PrintError("only one catch_all allowed in try block"); + return Result::Error; + } + + if (try_->kind == TryKind::Plain) { + try_->kind = TryKind::Catch; + } else if (try_->kind != TryKind::Catch) { + PrintError("catch not allowed in try-delegate"); + return Result::Error; + } + + try_->catches.push_back(std::move(catch_)); + label->exprs = &try_->catches.back().exprs; + return Result::Ok; +} + +Result BinaryReaderIR::OnCatchExpr(Index except_index) { + return AppendCatch(Catch(Var(except_index, GetLocation()))); +} + +Result BinaryReaderIR::OnCatchAllExpr() { + return AppendCatch(Catch(GetLocation())); +} + +Result BinaryReaderIR::OnDelegateExpr(Index depth) { + LabelNode* label = nullptr; + CHECK_RESULT(TopLabel(&label)); + + if (label->label_type != LabelType::Try) { + PrintError("delegate not inside try block"); + return Result::Error; + } + + auto* try_ = cast<TryExpr>(label->context); + + if (try_->kind == TryKind::Plain) { + try_->kind = TryKind::Delegate; + } else if (try_->kind != TryKind::Delegate) { + PrintError("delegate not allowed in try-catch"); + return Result::Error; + } + + try_->delegate_target = Var(depth, GetLocation()); + + PopLabel(); + return Result::Ok; +} + +Result BinaryReaderIR::OnUnaryExpr(Opcode opcode) { + return AppendExpr(std::make_unique<UnaryExpr>(opcode)); +} + +Result BinaryReaderIR::OnTernaryExpr(Opcode opcode) { + return AppendExpr(std::make_unique<TernaryExpr>(opcode)); +} + +Result BinaryReaderIR::OnUnreachableExpr() { + return AppendExpr(std::make_unique<UnreachableExpr>()); +} + +Result BinaryReaderIR::EndFunctionBody(Index index) { + current_func_ = nullptr; + if (!label_stack_.empty()) { + PrintError("function %" PRIindex " missing end marker", index); + return Result::Error; + } + return Result::Ok; +} + +Result BinaryReaderIR::OnSimdLaneOpExpr(Opcode opcode, uint64_t value) { + return AppendExpr(std::make_unique<SimdLaneOpExpr>(opcode, value)); +} + +Result BinaryReaderIR::OnSimdLoadLaneExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset, + uint64_t value) { + return AppendExpr(std::make_unique<SimdLoadLaneExpr>( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset, value)); +} + +Result BinaryReaderIR::OnSimdStoreLaneExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset, + uint64_t value) { + return AppendExpr(std::make_unique<SimdStoreLaneExpr>( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset, value)); +} + +Result BinaryReaderIR::OnSimdShuffleOpExpr(Opcode opcode, v128 value) { + return AppendExpr(std::make_unique<SimdShuffleOpExpr>(opcode, value)); +} + +Result BinaryReaderIR::OnLoadSplatExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique<LoadSplatExpr>( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnLoadZeroExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique<LoadZeroExpr>( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnElemSegmentCount(Index count) { + WABT_TRY + module_->elem_segments.reserve(count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::BeginElemSegment(Index index, + Index table_index, + uint8_t flags) { + auto field = std::make_unique<ElemSegmentModuleField>(GetLocation()); + ElemSegment& elem_segment = field->elem_segment; + elem_segment.table_var = Var(table_index, GetLocation()); + if ((flags & SegDeclared) == SegDeclared) { + elem_segment.kind = SegmentKind::Declared; + } else if ((flags & SegPassive) == SegPassive) { + elem_segment.kind = SegmentKind::Passive; + } else { + elem_segment.kind = SegmentKind::Active; + } + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::BeginInitExpr(ExprList* expr) { + return PushLabel(LabelType::InitExpr, expr); +} + +Result BinaryReaderIR::BeginElemSegmentInitExpr(Index index) { + assert(index == module_->elem_segments.size() - 1); + ElemSegment* segment = module_->elem_segments[index]; + return BeginInitExpr(&segment->offset); +} + +Result BinaryReaderIR::EndInitExpr() { + if (!label_stack_.empty()) { + PrintError("init expression missing end marker"); + return Result::Error; + } + return Result::Ok; +} + +Result BinaryReaderIR::EndElemSegmentInitExpr(Index index) { + return EndInitExpr(); +} + +Result BinaryReaderIR::OnElemSegmentElemType(Index index, Type elem_type) { + assert(index == module_->elem_segments.size() - 1); + ElemSegment* segment = module_->elem_segments[index]; + segment->elem_type = elem_type; + return Result::Ok; +} + +Result BinaryReaderIR::OnElemSegmentElemExprCount(Index index, Index count) { + assert(index == module_->elem_segments.size() - 1); + ElemSegment* segment = module_->elem_segments[index]; + WABT_TRY + segment->elem_exprs.reserve(count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::OnElemSegmentElemExpr_RefNull(Index segment_index, + Type type) { + assert(segment_index == module_->elem_segments.size() - 1); + ElemSegment* segment = module_->elem_segments[segment_index]; + Location loc = GetLocation(); + ExprList init_expr; + init_expr.push_back(std::make_unique<RefNullExpr>(type, loc)); + segment->elem_exprs.push_back(std::move(init_expr)); + return Result::Ok; +} + +Result BinaryReaderIR::OnElemSegmentElemExpr_RefFunc(Index segment_index, + Index func_index) { + assert(segment_index == module_->elem_segments.size() - 1); + ElemSegment* segment = module_->elem_segments[segment_index]; + Location loc = GetLocation(); + ExprList init_expr; + init_expr.push_back(std::make_unique<RefFuncExpr>(Var(func_index, loc), loc)); + segment->elem_exprs.push_back(std::move(init_expr)); + return Result::Ok; +} + +Result BinaryReaderIR::OnDataSegmentCount(Index count) { + WABT_TRY + module_->data_segments.reserve(count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::BeginDataSegment(Index index, + Index memory_index, + uint8_t flags) { + auto field = std::make_unique<DataSegmentModuleField>(GetLocation()); + DataSegment& data_segment = field->data_segment; + data_segment.memory_var = Var(memory_index, GetLocation()); + if ((flags & SegPassive) == SegPassive) { + data_segment.kind = SegmentKind::Passive; + } else { + data_segment.kind = SegmentKind::Active; + } + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::BeginDataSegmentInitExpr(Index index) { + assert(index == module_->data_segments.size() - 1); + DataSegment* segment = module_->data_segments[index]; + return BeginInitExpr(&segment->offset); +} + +Result BinaryReaderIR::EndDataSegmentInitExpr(Index index) { + return EndInitExpr(); +} + +Result BinaryReaderIR::OnDataSegmentData(Index index, + const void* data, + Address size) { + assert(index == module_->data_segments.size() - 1); + DataSegment* segment = module_->data_segments[index]; + segment->data.resize(size); + if (size > 0) { + memcpy(segment->data.data(), data, size); + } + return Result::Ok; +} + +Result BinaryReaderIR::OnFunctionNamesCount(Index count) { + if (count > module_->funcs.size()) { + PrintError("expected function name count (%" PRIindex + ") <= function count (%" PRIzd ")", + count, module_->funcs.size()); + return Result::Error; + } + return Result::Ok; +} + +static std::string MakeDollarName(std::string_view name) { + return std::string("$") + std::string(name); +} + +Result BinaryReaderIR::OnModuleName(std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + + module_->name = MakeDollarName(name); + return Result::Ok; +} + +Result BinaryReaderIR::SetGlobalName(Index index, std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + if (index >= module_->globals.size()) { + PrintError("invalid global index: %" PRIindex, index); + return Result::Error; + } + Global* glob = module_->globals[index]; + std::string dollar_name = + GetUniqueName(&module_->global_bindings, MakeDollarName(name)); + glob->name = dollar_name; + module_->global_bindings.emplace(dollar_name, Binding(index)); + return Result::Ok; +} + +Result BinaryReaderIR::SetFunctionName(Index index, std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + if (index >= module_->funcs.size()) { + PrintError("invalid function index: %" PRIindex, index); + return Result::Error; + } + Func* func = module_->funcs[index]; + std::string dollar_name = + GetUniqueName(&module_->func_bindings, MakeDollarName(name)); + func->name = dollar_name; + module_->func_bindings.emplace(dollar_name, Binding(index)); + return Result::Ok; +} + +Result BinaryReaderIR::SetTypeName(Index index, std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + if (index >= module_->types.size()) { + PrintError("invalid type index: %" PRIindex, index); + return Result::Error; + } + TypeEntry* type = module_->types[index]; + std::string dollar_name = + GetUniqueName(&module_->type_bindings, MakeDollarName(name)); + type->name = dollar_name; + module_->type_bindings.emplace(dollar_name, Binding(index)); + return Result::Ok; +} + +Result BinaryReaderIR::SetTableName(Index index, std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + if (index >= module_->tables.size()) { + PrintError("invalid table index: %" PRIindex, index); + return Result::Error; + } + Table* table = module_->tables[index]; + std::string dollar_name = + GetUniqueName(&module_->table_bindings, MakeDollarName(name)); + table->name = dollar_name; + module_->table_bindings.emplace(dollar_name, Binding(index)); + return Result::Ok; +} + +Result BinaryReaderIR::SetDataSegmentName(Index index, std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + if (index >= module_->data_segments.size()) { + PrintError("invalid data segment index: %" PRIindex, index); + return Result::Error; + } + DataSegment* segment = module_->data_segments[index]; + std::string dollar_name = + GetUniqueName(&module_->data_segment_bindings, MakeDollarName(name)); + segment->name = dollar_name; + module_->data_segment_bindings.emplace(dollar_name, Binding(index)); + return Result::Ok; +} + +Result BinaryReaderIR::SetElemSegmentName(Index index, std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + if (index >= module_->elem_segments.size()) { + PrintError("invalid elem segment index: %" PRIindex, index); + return Result::Error; + } + ElemSegment* segment = module_->elem_segments[index]; + std::string dollar_name = + GetUniqueName(&module_->elem_segment_bindings, MakeDollarName(name)); + segment->name = dollar_name; + module_->elem_segment_bindings.emplace(dollar_name, Binding(index)); + return Result::Ok; +} + +Result BinaryReaderIR::SetMemoryName(Index index, std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + if (index >= module_->memories.size()) { + PrintError("invalid memory index: %" PRIindex, index); + return Result::Error; + } + Memory* memory = module_->memories[index]; + std::string dollar_name = + GetUniqueName(&module_->memory_bindings, MakeDollarName(name)); + memory->name = dollar_name; + module_->memory_bindings.emplace(dollar_name, Binding(index)); + return Result::Ok; +} + +Result BinaryReaderIR::SetTagName(Index index, std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + if (index >= module_->tags.size()) { + PrintError("invalid tag index: %" PRIindex, index); + return Result::Error; + } + Tag* tag = module_->tags[index]; + std::string dollar_name = + GetUniqueName(&module_->tag_bindings, MakeDollarName(name)); + tag->name = dollar_name; + module_->tag_bindings.emplace(dollar_name, Binding(index)); + return Result::Ok; +} + +Result BinaryReaderIR::OnFunctionName(Index index, std::string_view name) { + return SetFunctionName(index, name); +} + +Result BinaryReaderIR::OnNameEntry(NameSectionSubsection type, + Index index, + std::string_view name) { + switch (type) { + // TODO(sbc): remove OnFunctionName in favor of just using + // OnNameEntry so that this works + case NameSectionSubsection::Function: + case NameSectionSubsection::Local: + case NameSectionSubsection::Module: + case NameSectionSubsection::Label: + break; + case NameSectionSubsection::Type: + SetTypeName(index, name); + break; + case NameSectionSubsection::Tag: + SetTagName(index, name); + break; + case NameSectionSubsection::Global: + SetGlobalName(index, name); + break; + case NameSectionSubsection::Table: + SetTableName(index, name); + break; + case NameSectionSubsection::DataSegment: + SetDataSegmentName(index, name); + break; + case NameSectionSubsection::Memory: + SetMemoryName(index, name); + break; + case NameSectionSubsection::ElemSegment: + SetElemSegmentName(index, name); + break; + } + return Result::Ok; +} + +Result BinaryReaderIR::OnLocalNameLocalCount(Index index, Index count) { + assert(index < module_->funcs.size()); + Func* func = module_->funcs[index]; + Index num_params_and_locals = func->GetNumParamsAndLocals(); + if (count > num_params_and_locals) { + PrintError("expected local name count (%" PRIindex + ") <= local count (%" PRIindex ")", + count, num_params_and_locals); + return Result::Error; + } + return Result::Ok; +} + +Result BinaryReaderIR::BeginCodeMetadataSection(std::string_view name, + Offset size) { + current_metadata_name_ = name; + return Result::Ok; +} + +Result BinaryReaderIR::OnCodeMetadataFuncCount(Index count) { + return Result::Ok; +} + +Result BinaryReaderIR::OnCodeMetadataCount(Index function_index, Index count) { + code_metadata_queue_.push_func(module_->funcs[function_index]); + return Result::Ok; +} + +Result BinaryReaderIR::OnCodeMetadata(Offset offset, + const void* data, + Address size) { + std::vector<uint8_t> data_(static_cast<const uint8_t*>(data), + static_cast<const uint8_t*>(data) + size); + auto meta = std::make_unique<CodeMetadataExpr>(current_metadata_name_, + std::move(data_)); + meta->loc.offset = offset; + code_metadata_queue_.push_metadata(std::move(meta)); + return Result::Ok; +} + +Result BinaryReaderIR::OnLocalName(Index func_index, + Index local_index, + std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + + Func* func = module_->funcs[func_index]; + func->bindings.emplace(GetUniqueName(&func->bindings, MakeDollarName(name)), + Binding(local_index)); + return Result::Ok; +} + +Result BinaryReaderIR::OnTagType(Index index, Index sig_index) { + auto field = std::make_unique<TagModuleField>(GetLocation()); + Tag& tag = field->tag; + SetFuncDeclaration(&tag.decl, Var(sig_index, GetLocation())); + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::OnDataSymbol(Index index, + uint32_t flags, + std::string_view name, + Index segment, + uint32_t offset, + uint32_t size) { + if (name.empty()) { + return Result::Ok; + } + if (flags & WABT_SYMBOL_FLAG_UNDEFINED) { + // Refers to data in another file, `segment` not valid. + return Result::Ok; + } + if (offset) { + // If it is pointing into the data segment, then it's not really naming + // the whole segment. + return Result::Ok; + } + if (segment >= module_->data_segments.size()) { + PrintError("invalid data segment index: %" PRIindex, segment); + return Result::Error; + } + DataSegment* seg = module_->data_segments[segment]; + std::string dollar_name = + GetUniqueName(&module_->data_segment_bindings, MakeDollarName(name)); + seg->name = dollar_name; + module_->data_segment_bindings.emplace(dollar_name, Binding(segment)); + return Result::Ok; +} + +Result BinaryReaderIR::OnFunctionSymbol(Index index, + uint32_t flags, + std::string_view name, + Index func_index) { + if (name.empty()) { + return Result::Ok; + } + if (func_index >= module_->funcs.size()) { + PrintError("invalid function index: %" PRIindex, func_index); + return Result::Error; + } + Func* func = module_->funcs[func_index]; + if (!func->name.empty()) { + // The name section has already named this function. + return Result::Ok; + } + std::string dollar_name = + GetUniqueName(&module_->func_bindings, MakeDollarName(name)); + func->name = dollar_name; + module_->func_bindings.emplace(dollar_name, Binding(func_index)); + return Result::Ok; +} + +Result BinaryReaderIR::OnGlobalSymbol(Index index, + uint32_t flags, + std::string_view name, + Index global_index) { + return SetGlobalName(global_index, name); +} + +Result BinaryReaderIR::OnSectionSymbol(Index index, + uint32_t flags, + Index section_index) { + return Result::Ok; +} + +Result BinaryReaderIR::OnTagSymbol(Index index, + uint32_t flags, + std::string_view name, + Index tag_index) { + if (name.empty()) { + return Result::Ok; + } + if (tag_index >= module_->tags.size()) { + PrintError("invalid tag index: %" PRIindex, tag_index); + return Result::Error; + } + Tag* tag = module_->tags[tag_index]; + std::string dollar_name = + GetUniqueName(&module_->tag_bindings, MakeDollarName(name)); + tag->name = dollar_name; + module_->tag_bindings.emplace(dollar_name, Binding(tag_index)); + return Result::Ok; +} + +Result BinaryReaderIR::OnTableSymbol(Index index, + uint32_t flags, + std::string_view name, + Index table_index) { + return SetTableName(table_index, name); +} + +} // end anonymous namespace + +Result ReadBinaryIr(const char* filename, + const void* data, + size_t size, + const ReadBinaryOptions& options, + Errors* errors, + Module* out_module) { + BinaryReaderIR reader(out_module, filename, errors); + return ReadBinary(data, size, &reader, options); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary-reader-logging.cc b/third_party/wasm2c/src/binary-reader-logging.cc new file mode 100644 index 0000000000..83a14e9ff7 --- /dev/null +++ b/third_party/wasm2c/src/binary-reader-logging.cc @@ -0,0 +1,971 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/binary-reader-logging.h" + +#include <cinttypes> + +#include "wabt/stream.h" + +namespace wabt { + +#define INDENT_SIZE 2 + +#define LOGF_NOINDENT(...) stream_->Writef(__VA_ARGS__) + +#define LOGF(...) \ + do { \ + WriteIndent(); \ + LOGF_NOINDENT(__VA_ARGS__); \ + } while (0) + +namespace { + +void SPrintLimits(char* dst, size_t size, const Limits* limits) { + int result; + if (limits->has_max) { + result = wabt_snprintf(dst, size, "initial: %" PRIu64 ", max: %" PRIu64, + limits->initial, limits->max); + } else { + result = wabt_snprintf(dst, size, "initial: %" PRIu64, limits->initial); + } + WABT_USE(result); + assert(static_cast<size_t>(result) < size); +} + +} // end anonymous namespace + +BinaryReaderLogging::BinaryReaderLogging(Stream* stream, + BinaryReaderDelegate* forward) + : stream_(stream), reader_(forward), indent_(0) {} + +void BinaryReaderLogging::Indent() { + indent_ += INDENT_SIZE; +} + +void BinaryReaderLogging::Dedent() { + indent_ -= INDENT_SIZE; + assert(indent_ >= 0); +} + +void BinaryReaderLogging::WriteIndent() { + static char s_indent[] = + " " + " "; + static const size_t s_indent_len = sizeof(s_indent) - 1; + size_t i = indent_; + while (i > s_indent_len) { + stream_->WriteData(s_indent, s_indent_len); + i -= s_indent_len; + } + if (i > 0) { + stream_->WriteData(s_indent, indent_); + } +} + +void BinaryReaderLogging::LogType(Type type) { + if (type.IsIndex()) { + LOGF_NOINDENT("typeidx[%d]", type.GetIndex()); + } else { + LOGF_NOINDENT("%s", type.GetName().c_str()); + } +} + +void BinaryReaderLogging::LogTypes(Index type_count, Type* types) { + LOGF_NOINDENT("["); + for (Index i = 0; i < type_count; ++i) { + LogType(types[i]); + if (i != type_count - 1) { + LOGF_NOINDENT(", "); + } + } + LOGF_NOINDENT("]"); +} + +void BinaryReaderLogging::LogTypes(TypeVector& types) { + LogTypes(types.size(), types.data()); +} + +void BinaryReaderLogging::LogField(TypeMut field) { + if (field.mutable_) { + LOGF_NOINDENT("(mut "); + } + LogType(field.type); + if (field.mutable_) { + LOGF_NOINDENT(")"); + } +} + +bool BinaryReaderLogging::OnError(const Error& error) { + return reader_->OnError(error); +} + +void BinaryReaderLogging::OnSetState(const State* s) { + BinaryReaderDelegate::OnSetState(s); + reader_->OnSetState(s); +} + +Result BinaryReaderLogging::BeginModule(uint32_t version) { + LOGF("BeginModule(version: %u)\n", version); + Indent(); + return reader_->BeginModule(version); +} + +Result BinaryReaderLogging::BeginSection(Index section_index, + BinarySection section_type, + Offset size) { + return reader_->BeginSection(section_index, section_type, size); +} + +Result BinaryReaderLogging::BeginCustomSection(Index section_index, + Offset size, + std::string_view section_name) { + LOGF("BeginCustomSection('" PRIstringview "', size: %" PRIzd ")\n", + WABT_PRINTF_STRING_VIEW_ARG(section_name), size); + Indent(); + return reader_->BeginCustomSection(section_index, size, section_name); +} + +Result BinaryReaderLogging::OnFuncType(Index index, + Index param_count, + Type* param_types, + Index result_count, + Type* result_types) { + LOGF("OnFuncType(index: %" PRIindex ", params: ", index); + LogTypes(param_count, param_types); + LOGF_NOINDENT(", results: "); + LogTypes(result_count, result_types); + LOGF_NOINDENT(")\n"); + return reader_->OnFuncType(index, param_count, param_types, result_count, + result_types); +} + +Result BinaryReaderLogging::OnStructType(Index index, + Index field_count, + TypeMut* fields) { + LOGF("OnStructType(index: %" PRIindex ", fields: ", index); + LOGF_NOINDENT("["); + for (Index i = 0; i < field_count; ++i) { + LogField(fields[i]); + if (i != field_count - 1) { + LOGF_NOINDENT(", "); + } + } + LOGF_NOINDENT("])\n"); + return reader_->OnStructType(index, field_count, fields); +} + +Result BinaryReaderLogging::OnArrayType(Index index, TypeMut field) { + LOGF("OnArrayType(index: %" PRIindex ", field: ", index); + LogField(field); + LOGF_NOINDENT(")\n"); + return reader_->OnArrayType(index, field); +} + +Result BinaryReaderLogging::OnImport(Index index, + ExternalKind kind, + std::string_view module_name, + std::string_view field_name) { + LOGF("OnImport(index: %" PRIindex ", kind: %s, module: \"" PRIstringview + "\", field: \"" PRIstringview "\")\n", + index, GetKindName(kind), WABT_PRINTF_STRING_VIEW_ARG(module_name), + WABT_PRINTF_STRING_VIEW_ARG(field_name)); + return reader_->OnImport(index, kind, module_name, field_name); +} + +Result BinaryReaderLogging::OnImportFunc(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index func_index, + Index sig_index) { + LOGF("OnImportFunc(import_index: %" PRIindex ", func_index: %" PRIindex + ", sig_index: %" PRIindex ")\n", + import_index, func_index, sig_index); + return reader_->OnImportFunc(import_index, module_name, field_name, + func_index, sig_index); +} + +Result BinaryReaderLogging::OnImportTable(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) { + char buf[100]; + SPrintLimits(buf, sizeof(buf), elem_limits); + LOGF("OnImportTable(import_index: %" PRIindex ", table_index: %" PRIindex + ", elem_type: %s, %s)\n", + import_index, table_index, elem_type.GetName().c_str(), buf); + return reader_->OnImportTable(import_index, module_name, field_name, + table_index, elem_type, elem_limits); +} + +Result BinaryReaderLogging::OnImportMemory(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index memory_index, + const Limits* page_limits) { + char buf[100]; + SPrintLimits(buf, sizeof(buf), page_limits); + LOGF("OnImportMemory(import_index: %" PRIindex ", memory_index: %" PRIindex + ", %s)\n", + import_index, memory_index, buf); + return reader_->OnImportMemory(import_index, module_name, field_name, + memory_index, page_limits); +} + +Result BinaryReaderLogging::OnImportGlobal(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index global_index, + Type type, + bool mutable_) { + LOGF("OnImportGlobal(import_index: %" PRIindex ", global_index: %" PRIindex + ", type: %s, mutable: " + "%s)\n", + import_index, global_index, type.GetName().c_str(), + mutable_ ? "true" : "false"); + return reader_->OnImportGlobal(import_index, module_name, field_name, + global_index, type, mutable_); +} + +Result BinaryReaderLogging::OnImportTag(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index tag_index, + Index sig_index) { + LOGF("OnImportTag(import_index: %" PRIindex ", tag_index: %" PRIindex + ", sig_index: %" PRIindex ")\n", + import_index, tag_index, sig_index); + return reader_->OnImportTag(import_index, module_name, field_name, tag_index, + sig_index); +} + +Result BinaryReaderLogging::OnTable(Index index, + Type elem_type, + const Limits* elem_limits) { + char buf[100]; + SPrintLimits(buf, sizeof(buf), elem_limits); + LOGF("OnTable(index: %" PRIindex ", elem_type: %s, %s)\n", index, + elem_type.GetName().c_str(), buf); + return reader_->OnTable(index, elem_type, elem_limits); +} + +Result BinaryReaderLogging::OnMemory(Index index, const Limits* page_limits) { + char buf[100]; + SPrintLimits(buf, sizeof(buf), page_limits); + LOGF("OnMemory(index: %" PRIindex ", %s)\n", index, buf); + return reader_->OnMemory(index, page_limits); +} + +Result BinaryReaderLogging::BeginGlobal(Index index, Type type, bool mutable_) { + LOGF("BeginGlobal(index: %" PRIindex ", type: %s, mutable: %s)\n", index, + type.GetName().c_str(), mutable_ ? "true" : "false"); + return reader_->BeginGlobal(index, type, mutable_); +} + +Result BinaryReaderLogging::OnExport(Index index, + ExternalKind kind, + Index item_index, + std::string_view name) { + LOGF("OnExport(index: %" PRIindex ", kind: %s, item_index: %" PRIindex + ", name: \"" PRIstringview "\")\n", + index, GetKindName(kind), item_index, WABT_PRINTF_STRING_VIEW_ARG(name)); + return reader_->OnExport(index, kind, item_index, name); +} + +Result BinaryReaderLogging::BeginFunctionBody(Index value, Offset size) { + LOGF("BeginFunctionBody(%" PRIindex ", size:%" PRIzd ")\n", value, size); + return reader_->BeginFunctionBody(value, size); +} + +Result BinaryReaderLogging::OnLocalDecl(Index decl_index, + Index count, + Type type) { + LOGF("OnLocalDecl(index: %" PRIindex ", count: %" PRIindex ", type: %s)\n", + decl_index, count, type.GetName().c_str()); + return reader_->OnLocalDecl(decl_index, count, type); +} + +Result BinaryReaderLogging::OnBlockExpr(Type sig_type) { + LOGF("OnBlockExpr(sig: "); + LogType(sig_type); + LOGF_NOINDENT(")\n"); + return reader_->OnBlockExpr(sig_type); +} + +Result BinaryReaderLogging::OnBrExpr(Index depth) { + LOGF("OnBrExpr(depth: %" PRIindex ")\n", depth); + return reader_->OnBrExpr(depth); +} + +Result BinaryReaderLogging::OnBrIfExpr(Index depth) { + LOGF("OnBrIfExpr(depth: %" PRIindex ")\n", depth); + return reader_->OnBrIfExpr(depth); +} + +Result BinaryReaderLogging::OnBrTableExpr(Index num_targets, + Index* target_depths, + Index default_target_depth) { + LOGF("OnBrTableExpr(num_targets: %" PRIindex ", depths: [", num_targets); + for (Index i = 0; i < num_targets; ++i) { + LOGF_NOINDENT("%" PRIindex, target_depths[i]); + if (i != num_targets - 1) { + LOGF_NOINDENT(", "); + } + } + LOGF_NOINDENT("], default: %" PRIindex ")\n", default_target_depth); + return reader_->OnBrTableExpr(num_targets, target_depths, + default_target_depth); +} + +Result BinaryReaderLogging::OnF32ConstExpr(uint32_t value_bits) { + float value; + memcpy(&value, &value_bits, sizeof(value)); + LOGF("OnF32ConstExpr(%g (0x%08x))\n", value, value_bits); + return reader_->OnF32ConstExpr(value_bits); +} + +Result BinaryReaderLogging::OnF64ConstExpr(uint64_t value_bits) { + double value; + memcpy(&value, &value_bits, sizeof(value)); + LOGF("OnF64ConstExpr(%g (0x%016" PRIx64 "))\n", value, value_bits); + return reader_->OnF64ConstExpr(value_bits); +} + +Result BinaryReaderLogging::OnV128ConstExpr(v128 value_bits) { + LOGF("OnV128ConstExpr(0x%08x 0x%08x 0x%08x 0x%08x)\n", value_bits.u32(0), + value_bits.u32(1), value_bits.u32(2), value_bits.u32(3)); + return reader_->OnV128ConstExpr(value_bits); +} + +Result BinaryReaderLogging::OnI32ConstExpr(uint32_t value) { + LOGF("OnI32ConstExpr(%u (0x%x))\n", value, value); + return reader_->OnI32ConstExpr(value); +} + +Result BinaryReaderLogging::OnI64ConstExpr(uint64_t value) { + LOGF("OnI64ConstExpr(%" PRIu64 " (0x%" PRIx64 "))\n", value, value); + return reader_->OnI64ConstExpr(value); +} + +Result BinaryReaderLogging::OnIfExpr(Type sig_type) { + LOGF("OnIfExpr(sig: "); + LogType(sig_type); + LOGF_NOINDENT(")\n"); + return reader_->OnIfExpr(sig_type); +} + +Result BinaryReaderLogging::OnLoopExpr(Type sig_type) { + LOGF("OnLoopExpr(sig: "); + LogType(sig_type); + LOGF_NOINDENT(")\n"); + return reader_->OnLoopExpr(sig_type); +} + +Result BinaryReaderLogging::OnSelectExpr(Index result_count, + Type* result_types) { + LOGF("OnSelectExpr(return_type: "); + LogTypes(result_count, result_types); + LOGF_NOINDENT(")\n"); + return reader_->OnSelectExpr(result_count, result_types); +} + +Result BinaryReaderLogging::OnTryExpr(Type sig_type) { + LOGF("OnTryExpr(sig: "); + LogType(sig_type); + LOGF_NOINDENT(")\n"); + return reader_->OnTryExpr(sig_type); +} + +Result BinaryReaderLogging::OnSimdLaneOpExpr(Opcode opcode, uint64_t value) { + LOGF("OnSimdLaneOpExpr (lane: %" PRIu64 ")\n", value); + return reader_->OnSimdLaneOpExpr(opcode, value); +} + +Result BinaryReaderLogging::OnSimdShuffleOpExpr(Opcode opcode, v128 value) { + LOGF("OnSimdShuffleOpExpr (lane: 0x%08x %08x %08x %08x)\n", value.u32(0), + value.u32(1), value.u32(2), value.u32(3)); + return reader_->OnSimdShuffleOpExpr(opcode, value); +} + +Result BinaryReaderLogging::BeginElemSegment(Index index, + Index table_index, + uint8_t flags) { + LOGF("BeginElemSegment(index: %" PRIindex ", table_index: %" PRIindex + ", flags: %d)\n", + index, table_index, flags); + return reader_->BeginElemSegment(index, table_index, flags); +} + +Result BinaryReaderLogging::OnElemSegmentElemType(Index index, Type elem_type) { + LOGF("OnElemSegmentElemType(index: %" PRIindex ", type: %s)\n", index, + elem_type.GetName().c_str()); + return reader_->OnElemSegmentElemType(index, elem_type); +} + +Result BinaryReaderLogging::OnDataSegmentData(Index index, + const void* data, + Address size) { + LOGF("OnDataSegmentData(index:%" PRIindex ", size:%" PRIaddress ")\n", index, + size); + return reader_->OnDataSegmentData(index, data, size); +} + +Result BinaryReaderLogging::OnModuleNameSubsection(Index index, + uint32_t name_type, + Offset subsection_size) { + LOGF("OnModuleNameSubsection(index:%" PRIindex ", nametype:%u, size:%" PRIzd + ")\n", + index, name_type, subsection_size); + return reader_->OnModuleNameSubsection(index, name_type, subsection_size); +} + +Result BinaryReaderLogging::OnModuleName(std::string_view name) { + LOGF("OnModuleName(name: \"" PRIstringview "\")\n", + WABT_PRINTF_STRING_VIEW_ARG(name)); + return reader_->OnModuleName(name); +} + +Result BinaryReaderLogging::OnFunctionNameSubsection(Index index, + uint32_t name_type, + Offset subsection_size) { + LOGF("OnFunctionNameSubsection(index:%" PRIindex ", nametype:%u, size:%" PRIzd + ")\n", + index, name_type, subsection_size); + return reader_->OnFunctionNameSubsection(index, name_type, subsection_size); +} + +Result BinaryReaderLogging::OnFunctionName(Index index, std::string_view name) { + LOGF("OnFunctionName(index: %" PRIindex ", name: \"" PRIstringview "\")\n", + index, WABT_PRINTF_STRING_VIEW_ARG(name)); + return reader_->OnFunctionName(index, name); +} + +Result BinaryReaderLogging::OnLocalNameSubsection(Index index, + uint32_t name_type, + Offset subsection_size) { + LOGF("OnLocalNameSubsection(index:%" PRIindex ", nametype:%u, size:%" PRIzd + ")\n", + index, name_type, subsection_size); + return reader_->OnLocalNameSubsection(index, name_type, subsection_size); +} + +Result BinaryReaderLogging::OnLocalName(Index func_index, + Index local_index, + std::string_view name) { + LOGF("OnLocalName(func_index: %" PRIindex ", local_index: %" PRIindex + ", name: \"" PRIstringview "\")\n", + func_index, local_index, WABT_PRINTF_STRING_VIEW_ARG(name)); + return reader_->OnLocalName(func_index, local_index, name); +} + +Result BinaryReaderLogging::OnNameSubsection( + Index index, + NameSectionSubsection subsection_type, + Offset subsection_size) { + LOGF("OnNameSubsection(index: %" PRIindex ", type: %s, size:%" PRIzd ")\n", + index, GetNameSectionSubsectionName(subsection_type), subsection_size); + return reader_->OnNameSubsection(index, subsection_type, subsection_size); +} + +Result BinaryReaderLogging::OnNameEntry(NameSectionSubsection type, + Index index, + std::string_view name) { + LOGF("OnNameEntry(type: %s, index: %" PRIindex ", name: \"" PRIstringview + "\")\n", + GetNameSectionSubsectionName(type), index, + WABT_PRINTF_STRING_VIEW_ARG(name)); + return reader_->OnNameEntry(type, index, name); +} + +Result BinaryReaderLogging::OnDylinkInfo(uint32_t mem_size, + uint32_t mem_align, + uint32_t table_size, + uint32_t table_align) { + LOGF( + "OnDylinkInfo(mem_size: %u, mem_align: %u, table_size: %u, table_align: " + "%u)\n", + mem_size, mem_align, table_size, table_align); + return reader_->OnDylinkInfo(mem_size, mem_align, table_size, table_align); +} + +Result BinaryReaderLogging::OnDylinkNeeded(std::string_view so_name) { + LOGF("OnDylinkNeeded(name: " PRIstringview ")\n", + WABT_PRINTF_STRING_VIEW_ARG(so_name)); + return reader_->OnDylinkNeeded(so_name); +} + +Result BinaryReaderLogging::OnDylinkExport(std::string_view name, + uint32_t flags) { + LOGF("OnDylinkExport(name: " PRIstringview ", flags: 0x%x)\n", + WABT_PRINTF_STRING_VIEW_ARG(name), flags); + return reader_->OnDylinkExport(name, flags); +} + +Result BinaryReaderLogging::OnDylinkImport(std::string_view module, + std::string_view name, + uint32_t flags) { + LOGF("OnDylinkImport(module: " PRIstringview ", name: " PRIstringview + ", flags: 0x%x)\n", + WABT_PRINTF_STRING_VIEW_ARG(module), WABT_PRINTF_STRING_VIEW_ARG(name), + flags); + return reader_->OnDylinkImport(module, name, flags); +} + +Result BinaryReaderLogging::OnRelocCount(Index count, Index section_index) { + LOGF("OnRelocCount(count: %" PRIindex ", section: %" PRIindex ")\n", count, + section_index); + return reader_->OnRelocCount(count, section_index); +} + +Result BinaryReaderLogging::OnReloc(RelocType type, + Offset offset, + Index index, + uint32_t addend) { + int32_t signed_addend = static_cast<int32_t>(addend); + LOGF("OnReloc(type: %s, offset: %" PRIzd ", index: %" PRIindex + ", addend: %d)\n", + GetRelocTypeName(type), offset, index, signed_addend); + return reader_->OnReloc(type, offset, index, addend); +} + +Result BinaryReaderLogging::OnFeature(uint8_t prefix, std::string_view name) { + LOGF("OnFeature(prefix: '%c', name: '" PRIstringview "')\n", prefix, + WABT_PRINTF_STRING_VIEW_ARG(name)); + return reader_->OnFeature(prefix, name); +} + +Result BinaryReaderLogging::OnDataSymbol(Index index, + uint32_t flags, + std::string_view name, + Index segment, + uint32_t offset, + uint32_t size) { + LOGF("OnDataSymbol(name: " PRIstringview " flags: 0x%x)\n", + WABT_PRINTF_STRING_VIEW_ARG(name), flags); + return reader_->OnDataSymbol(index, flags, name, segment, offset, size); +} + +Result BinaryReaderLogging::OnFunctionSymbol(Index index, + uint32_t flags, + std::string_view name, + Index func_index) { + LOGF("OnFunctionSymbol(name: " PRIstringview " flags: 0x%x index: %" PRIindex + ")\n", + WABT_PRINTF_STRING_VIEW_ARG(name), flags, func_index); + return reader_->OnFunctionSymbol(index, flags, name, func_index); +} + +Result BinaryReaderLogging::OnGlobalSymbol(Index index, + uint32_t flags, + std::string_view name, + Index global_index) { + LOGF("OnGlobalSymbol(name: " PRIstringview " flags: 0x%x index: %" PRIindex + ")\n", + WABT_PRINTF_STRING_VIEW_ARG(name), flags, global_index); + return reader_->OnGlobalSymbol(index, flags, name, global_index); +} + +Result BinaryReaderLogging::OnSectionSymbol(Index index, + uint32_t flags, + Index section_index) { + LOGF("OnSectionSymbol(flags: 0x%x index: %" PRIindex ")\n", flags, + section_index); + return reader_->OnSectionSymbol(index, flags, section_index); +} + +Result BinaryReaderLogging::OnTagSymbol(Index index, + uint32_t flags, + std::string_view name, + Index tag_index) { + LOGF("OnTagSymbol(name: " PRIstringview " flags: 0x%x index: %" PRIindex + ")\n", + WABT_PRINTF_STRING_VIEW_ARG(name), flags, tag_index); + return reader_->OnTagSymbol(index, flags, name, tag_index); +} + +Result BinaryReaderLogging::OnTableSymbol(Index index, + uint32_t flags, + std::string_view name, + Index table_index) { + LOGF("OnTableSymbol(name: " PRIstringview " flags: 0x%x index: %" PRIindex + ")\n", + WABT_PRINTF_STRING_VIEW_ARG(name), flags, table_index); + return reader_->OnTableSymbol(index, flags, name, table_index); +} + +Result BinaryReaderLogging::OnSegmentInfo(Index index, + std::string_view name, + Address alignment, + uint32_t flags) { + LOGF("OnSegmentInfo(%d name: " PRIstringview ", alignment: %" PRIaddress + ", flags: 0x%x)\n", + index, WABT_PRINTF_STRING_VIEW_ARG(name), alignment, flags); + return reader_->OnSegmentInfo(index, name, alignment, flags); +} + +Result BinaryReaderLogging::OnInitFunction(uint32_t priority, + Index symbol_index) { + LOGF("OnInitFunction(%d priority: %d)\n", symbol_index, priority); + return reader_->OnInitFunction(priority, symbol_index); +} + +Result BinaryReaderLogging::OnComdatBegin(std::string_view name, + uint32_t flags, + Index count) { + LOGF("OnComdatBegin(" PRIstringview ", flags: %d, count: %" PRIindex ")\n", + WABT_PRINTF_STRING_VIEW_ARG(name), flags, count); + return reader_->OnComdatBegin(name, flags, count); +} + +Result BinaryReaderLogging::OnComdatEntry(ComdatType kind, Index index) { + LOGF("OnComdatEntry(kind: %d, index: %" PRIindex ")\n", + static_cast<int>(kind), index); + return reader_->OnComdatEntry(kind, index); +} + +Result BinaryReaderLogging::BeginCodeMetadataSection(std::string_view name, + Offset size) { + LOGF("BeginCodeMetadataSection('" PRIstringview "', size:%" PRIzd ")\n", + WABT_PRINTF_STRING_VIEW_ARG(name), size); + Indent(); + return reader_->BeginCodeMetadataSection(name, size); +} +Result BinaryReaderLogging::OnCodeMetadata(Offset code_offset, + const void* data, + Address size) { + std::string_view content(static_cast<const char*>(data), size); + LOGF("OnCodeMetadata(offset: %" PRIzd ", data: \"" PRIstringview "\")\n", + code_offset, WABT_PRINTF_STRING_VIEW_ARG(content)); + return reader_->OnCodeMetadata(code_offset, data, size); +} + +#define DEFINE_BEGIN(name) \ + Result BinaryReaderLogging::name(Offset size) { \ + LOGF(#name "(%" PRIzd ")\n", size); \ + Indent(); \ + return reader_->name(size); \ + } + +#define DEFINE_END(name) \ + Result BinaryReaderLogging::name() { \ + Dedent(); \ + LOGF(#name "\n"); \ + return reader_->name(); \ + } + +#define DEFINE_INDEX(name) \ + Result BinaryReaderLogging::name(Index value) { \ + LOGF(#name "(%" PRIindex ")\n", value); \ + return reader_->name(value); \ + } + +#define DEFINE_TYPE(name) \ + Result BinaryReaderLogging::name(Type type) { \ + LOGF(#name "(%s)\n", type.GetName().c_str()); \ + return reader_->name(type); \ + } + +#define DEFINE_INDEX_DESC(name, desc) \ + Result BinaryReaderLogging::name(Index value) { \ + LOGF(#name "(" desc ": %" PRIindex ")\n", value); \ + return reader_->name(value); \ + } + +#define DEFINE_INDEX_TYPE(name) \ + Result BinaryReaderLogging::name(Index value, Type type) { \ + LOGF(#name "(index: %" PRIindex ", type: %s)\n", value, \ + type.GetName().c_str()); \ + return reader_->name(value, type); \ + } + +#define DEFINE_INDEX_INDEX(name, desc0, desc1) \ + Result BinaryReaderLogging::name(Index value0, Index value1) { \ + LOGF(#name "(" desc0 ": %" PRIindex ", " desc1 ": %" PRIindex ")\n", \ + value0, value1); \ + return reader_->name(value0, value1); \ + } + +#define DEFINE_INDEX_INDEX_U8(name, desc0, desc1, desc2) \ + Result BinaryReaderLogging::name(Index value0, Index value1, \ + uint8_t value2) { \ + LOGF(#name "(" desc0 ": %" PRIindex ", " desc1 ": %" PRIindex ", " desc2 \ + ": %d)\n", \ + value0, value1, value2); \ + return reader_->name(value0, value1, value2); \ + } + +#define DEFINE_OPCODE(name) \ + Result BinaryReaderLogging::name(Opcode opcode) { \ + LOGF(#name "(\"%s\" (%u))\n", opcode.GetName(), opcode.GetCode()); \ + return reader_->name(opcode); \ + } + +#define DEFINE_LOAD_STORE_OPCODE(name) \ + Result BinaryReaderLogging::name(Opcode opcode, Index memidx, \ + Address alignment_log2, Address offset) { \ + LOGF(#name "(opcode: \"%s\" (%u), memidx: %" PRIindex \ + ", align log2: %" PRIaddress ", offset: %" PRIaddress ")\n", \ + opcode.GetName(), opcode.GetCode(), memidx, alignment_log2, offset); \ + return reader_->name(opcode, memidx, alignment_log2, offset); \ + } + +#define DEFINE_SIMD_LOAD_STORE_LANE_OPCODE(name) \ + Result BinaryReaderLogging::name(Opcode opcode, Index memidx, \ + Address alignment_log2, Address offset, \ + uint64_t value) { \ + LOGF(#name "(opcode: \"%s\" (%u), memidx: %" PRIindex \ + ", align log2: %" PRIaddress ", offset: %" PRIaddress \ + ", lane: %" PRIu64 ")\n", \ + opcode.GetName(), opcode.GetCode(), memidx, alignment_log2, offset, \ + value); \ + return reader_->name(opcode, memidx, alignment_log2, offset, value); \ + } + +#define DEFINE0(name) \ + Result BinaryReaderLogging::name() { \ + LOGF(#name "\n"); \ + return reader_->name(); \ + } + +DEFINE_END(EndModule) + +DEFINE_END(EndCustomSection) + +DEFINE_BEGIN(BeginTypeSection) +DEFINE_INDEX(OnTypeCount) +DEFINE_END(EndTypeSection) + +DEFINE_BEGIN(BeginImportSection) +DEFINE_INDEX(OnImportCount) +DEFINE_END(EndImportSection) + +DEFINE_BEGIN(BeginFunctionSection) +DEFINE_INDEX(OnFunctionCount) +DEFINE_INDEX_INDEX(OnFunction, "index", "sig_index") +DEFINE_END(EndFunctionSection) + +DEFINE_BEGIN(BeginTableSection) +DEFINE_INDEX(OnTableCount) +DEFINE_END(EndTableSection) + +DEFINE_BEGIN(BeginMemorySection) +DEFINE_INDEX(OnMemoryCount) +DEFINE_END(EndMemorySection) + +DEFINE_BEGIN(BeginGlobalSection) +DEFINE_INDEX(OnGlobalCount) +DEFINE_INDEX(BeginGlobalInitExpr) +DEFINE_INDEX(EndGlobalInitExpr) +DEFINE_INDEX(EndGlobal) +DEFINE_END(EndGlobalSection) + +DEFINE_BEGIN(BeginExportSection) +DEFINE_INDEX(OnExportCount) +DEFINE_END(EndExportSection) + +DEFINE_BEGIN(BeginStartSection) +DEFINE_INDEX(OnStartFunction) +DEFINE_END(EndStartSection) + +DEFINE_BEGIN(BeginCodeSection) +DEFINE_INDEX(OnFunctionBodyCount) +DEFINE_INDEX(EndFunctionBody) +DEFINE_INDEX(OnLocalDeclCount) +DEFINE_LOAD_STORE_OPCODE(OnAtomicLoadExpr); +DEFINE_LOAD_STORE_OPCODE(OnAtomicRmwExpr); +DEFINE_LOAD_STORE_OPCODE(OnAtomicRmwCmpxchgExpr); +DEFINE_LOAD_STORE_OPCODE(OnAtomicStoreExpr); +DEFINE_LOAD_STORE_OPCODE(OnAtomicWaitExpr); +DEFINE_INDEX_DESC(OnAtomicFenceExpr, "consistency_model"); +DEFINE_LOAD_STORE_OPCODE(OnAtomicNotifyExpr); +DEFINE_OPCODE(OnBinaryExpr) +DEFINE_INDEX_DESC(OnCallExpr, "func_index") +DEFINE_INDEX_INDEX(OnCallIndirectExpr, "sig_index", "table_index") +DEFINE0(OnCallRefExpr) +DEFINE_INDEX_DESC(OnCatchExpr, "tag_index"); +DEFINE0(OnCatchAllExpr); +DEFINE_OPCODE(OnCompareExpr) +DEFINE_OPCODE(OnConvertExpr) +DEFINE_INDEX_DESC(OnDelegateExpr, "depth"); +DEFINE0(OnDropExpr) +DEFINE0(OnElseExpr) +DEFINE0(OnEndExpr) +DEFINE_INDEX_DESC(OnGlobalGetExpr, "index") +DEFINE_INDEX_DESC(OnGlobalSetExpr, "index") +DEFINE_LOAD_STORE_OPCODE(OnLoadExpr); +DEFINE_INDEX_DESC(OnLocalGetExpr, "index") +DEFINE_INDEX_DESC(OnLocalSetExpr, "index") +DEFINE_INDEX_DESC(OnLocalTeeExpr, "index") +DEFINE_INDEX_INDEX(OnMemoryCopyExpr, "src_memory_index", "dest_memory_index") +DEFINE_INDEX(OnDataDropExpr) +DEFINE_INDEX(OnMemoryFillExpr) +DEFINE_INDEX(OnMemoryGrowExpr) +DEFINE_INDEX_INDEX(OnMemoryInitExpr, "segment_index", "memory_index") +DEFINE_INDEX(OnMemorySizeExpr) +DEFINE_INDEX_INDEX(OnTableCopyExpr, "dst_index", "src_index") +DEFINE_INDEX(OnElemDropExpr) +DEFINE_INDEX_INDEX(OnTableInitExpr, "segment_index", "table_index") +DEFINE_INDEX(OnTableSetExpr) +DEFINE_INDEX(OnTableGetExpr) +DEFINE_INDEX(OnTableGrowExpr) +DEFINE_INDEX(OnTableSizeExpr) +DEFINE_INDEX_DESC(OnTableFillExpr, "table index") +DEFINE_INDEX(OnRefFuncExpr) +DEFINE_TYPE(OnRefNullExpr) +DEFINE0(OnRefIsNullExpr) +DEFINE0(OnNopExpr) +DEFINE_INDEX_DESC(OnRethrowExpr, "depth"); +DEFINE_INDEX_DESC(OnReturnCallExpr, "func_index") + +DEFINE_INDEX_INDEX(OnReturnCallIndirectExpr, "sig_index", "table_index") +DEFINE0(OnReturnExpr) +DEFINE_LOAD_STORE_OPCODE(OnLoadSplatExpr); +DEFINE_LOAD_STORE_OPCODE(OnLoadZeroExpr); +DEFINE_LOAD_STORE_OPCODE(OnStoreExpr); +DEFINE_INDEX_DESC(OnThrowExpr, "tag_index") +DEFINE0(OnUnreachableExpr) +DEFINE_OPCODE(OnUnaryExpr) +DEFINE_OPCODE(OnTernaryExpr) +DEFINE_SIMD_LOAD_STORE_LANE_OPCODE(OnSimdLoadLaneExpr); +DEFINE_SIMD_LOAD_STORE_LANE_OPCODE(OnSimdStoreLaneExpr); +DEFINE_END(EndCodeSection) + +DEFINE_BEGIN(BeginElemSection) +DEFINE_INDEX(OnElemSegmentCount) +DEFINE_INDEX(BeginElemSegmentInitExpr) +DEFINE_INDEX(EndElemSegmentInitExpr) +DEFINE_INDEX_INDEX(OnElemSegmentElemExprCount, "index", "count") +DEFINE_INDEX_TYPE(OnElemSegmentElemExpr_RefNull) +DEFINE_INDEX_INDEX(OnElemSegmentElemExpr_RefFunc, "index", "func_index") +DEFINE_INDEX(EndElemSegment) +DEFINE_END(EndElemSection) + +DEFINE_BEGIN(BeginDataSection) +DEFINE_INDEX(OnDataSegmentCount) +DEFINE_INDEX_INDEX_U8(BeginDataSegment, "index", "memory_index", "flags") +DEFINE_INDEX(BeginDataSegmentInitExpr) +DEFINE_INDEX(EndDataSegmentInitExpr) +DEFINE_INDEX(EndDataSegment) +DEFINE_END(EndDataSection) + +DEFINE_BEGIN(BeginDataCountSection) +DEFINE_INDEX(OnDataCount) +DEFINE_END(EndDataCountSection) + +DEFINE_BEGIN(BeginNamesSection) +DEFINE_INDEX(OnFunctionNamesCount) +DEFINE_INDEX(OnLocalNameFunctionCount) +DEFINE_INDEX_INDEX(OnLocalNameLocalCount, "index", "count") +DEFINE_INDEX(OnNameCount); +DEFINE_END(EndNamesSection) + +DEFINE_BEGIN(BeginRelocSection) +DEFINE_END(EndRelocSection) + +DEFINE_BEGIN(BeginDylinkSection) +DEFINE_INDEX(OnDylinkNeededCount) +DEFINE_INDEX(OnDylinkExportCount) +DEFINE_INDEX(OnDylinkImportCount) +DEFINE_END(EndDylinkSection) + +DEFINE_BEGIN(BeginTargetFeaturesSection) +DEFINE_INDEX(OnFeatureCount) +DEFINE_END(EndTargetFeaturesSection) + +DEFINE_BEGIN(BeginLinkingSection) +DEFINE_INDEX(OnSymbolCount) +DEFINE_INDEX(OnSegmentInfoCount) +DEFINE_INDEX(OnInitFunctionCount) +DEFINE_INDEX(OnComdatCount) +DEFINE_END(EndLinkingSection) + +DEFINE_BEGIN(BeginTagSection); +DEFINE_INDEX(OnTagCount); +DEFINE_INDEX_INDEX(OnTagType, "index", "sig_index") +DEFINE_END(EndTagSection); + +DEFINE_INDEX(OnCodeMetadataFuncCount); +DEFINE_INDEX_INDEX(OnCodeMetadataCount, "func_index", "count"); +DEFINE_END(EndCodeMetadataSection); + +// We don't need to log these (the individual opcodes are logged instead), but +// we still need to forward the calls. +Result BinaryReaderLogging::OnOpcode(Opcode opcode) { + return reader_->OnOpcode(opcode); +} + +Result BinaryReaderLogging::OnOpcodeBare() { + return reader_->OnOpcodeBare(); +} + +Result BinaryReaderLogging::OnOpcodeIndex(Index value) { + return reader_->OnOpcodeIndex(value); +} + +Result BinaryReaderLogging::OnOpcodeIndexIndex(Index value, Index value2) { + return reader_->OnOpcodeIndexIndex(value, value2); +} + +Result BinaryReaderLogging::OnOpcodeUint32(uint32_t value) { + return reader_->OnOpcodeUint32(value); +} + +Result BinaryReaderLogging::OnOpcodeUint32Uint32(uint32_t value, + uint32_t value2) { + return reader_->OnOpcodeUint32Uint32(value, value2); +} + +Result BinaryReaderLogging::OnOpcodeUint32Uint32Uint32(uint32_t value, + uint32_t value2, + uint32_t value3) { + return reader_->OnOpcodeUint32Uint32Uint32(value, value2, value3); +} + +Result BinaryReaderLogging::OnOpcodeUint32Uint32Uint32Uint32(uint32_t value, + uint32_t value2, + uint32_t value3, + uint32_t value4) { + return reader_->OnOpcodeUint32Uint32Uint32Uint32(value, value2, value3, + value4); +} + +Result BinaryReaderLogging::OnOpcodeUint64(uint64_t value) { + return reader_->OnOpcodeUint64(value); +} + +Result BinaryReaderLogging::OnOpcodeF32(uint32_t value) { + return reader_->OnOpcodeF32(value); +} + +Result BinaryReaderLogging::OnOpcodeF64(uint64_t value) { + return reader_->OnOpcodeF64(value); +} + +Result BinaryReaderLogging::OnOpcodeV128(v128 value) { + return reader_->OnOpcodeV128(value); +} + +Result BinaryReaderLogging::OnOpcodeBlockSig(Type sig_type) { + return reader_->OnOpcodeBlockSig(sig_type); +} + +Result BinaryReaderLogging::OnOpcodeType(Type type) { + return reader_->OnOpcodeType(type); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary-reader-objdump.cc b/third_party/wasm2c/src/binary-reader-objdump.cc new file mode 100644 index 0000000000..96ece0689d --- /dev/null +++ b/third_party/wasm2c/src/binary-reader-objdump.cc @@ -0,0 +1,2408 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/binary-reader-objdump.h" + +#include <algorithm> +#include <cassert> +#include <cinttypes> +#include <cstdio> +#include <cstring> +#include <vector> + +#if HAVE_STRCASECMP +#include <strings.h> +#endif + +#include "wabt/binary-reader-nop.h" +#include "wabt/filenames.h" +#include "wabt/literal.h" +#include "wabt/string-util.h" + +namespace wabt { + +namespace { + +class BinaryReaderObjdumpBase : public BinaryReaderNop { + public: + BinaryReaderObjdumpBase(const uint8_t* data, + size_t size, + ObjdumpOptions* options, + ObjdumpState* state); + + bool OnError(const Error&) override; + + Result BeginModule(uint32_t version) override; + Result BeginSection(Index section_index, + BinarySection section_type, + Offset size) override; + + Result OnOpcode(Opcode Opcode) override; + Result OnRelocCount(Index count, Index section_index) override; + + protected: + std::string_view GetTypeName(Index index) const; + std::string_view GetFunctionName(Index index) const; + std::string_view GetGlobalName(Index index) const; + std::string_view GetLocalName(Index function_index, Index local_index) const; + std::string_view GetSectionName(Index index) const; + std::string_view GetTagName(Index index) const; + std::string_view GetSymbolName(Index index) const; + std::string_view GetSegmentName(Index index) const; + std::string_view GetTableName(Index index) const; + void PrintRelocation(const Reloc& reloc, Offset offset) const; + Offset GetPrintOffset(Offset offset) const; + Offset GetSectionStart(BinarySection section_code) const { + return section_starts_[static_cast<size_t>(section_code)]; + } + + ObjdumpOptions* options_; + ObjdumpState* objdump_state_; + const uint8_t* data_; + size_t size_; + bool print_details_ = false; + BinarySection reloc_section_ = BinarySection::Invalid; + Offset section_starts_[kBinarySectionCount]; + // Map of section index to section type + std::vector<BinarySection> section_types_; + bool section_found_ = false; + std::string module_name_; + Opcode current_opcode = Opcode::Unreachable; + + std::unique_ptr<FileStream> err_stream_; +}; + +BinaryReaderObjdumpBase::BinaryReaderObjdumpBase(const uint8_t* data, + size_t size, + ObjdumpOptions* options, + ObjdumpState* objdump_state) + : options_(options), + objdump_state_(objdump_state), + data_(data), + size_(size), + err_stream_(FileStream::CreateStderr()) { + ZeroMemory(section_starts_); +} + +Result BinaryReaderObjdumpBase::BeginSection(Index section_index, + BinarySection section_code, + Offset size) { + section_starts_[static_cast<size_t>(section_code)] = state->offset; + section_types_.push_back(section_code); + return Result::Ok; +} + +bool BinaryReaderObjdumpBase::OnError(const Error&) { + // Tell the BinaryReader that this error is "handled" for all passes other + // than the prepass. When the error is handled the default message will be + // suppressed. + return options_->mode != ObjdumpMode::Prepass; +} + +Result BinaryReaderObjdumpBase::BeginModule(uint32_t version) { + switch (options_->mode) { + case ObjdumpMode::Headers: + printf("\n"); + printf("Sections:\n\n"); + break; + case ObjdumpMode::Details: + printf("\n"); + printf("Section Details:\n\n"); + break; + case ObjdumpMode::Disassemble: + printf("\n"); + printf("Code Disassembly:\n\n"); + break; + case ObjdumpMode::Prepass: { + std::string_view basename = GetBasename(options_->filename); + if (basename == "-") { + basename = "<stdin>"; + } + printf("%s:\tfile format wasm %#x\n", std::string(basename).c_str(), + version); + break; + } + case ObjdumpMode::RawData: + break; + } + + return Result::Ok; +} + +std::string_view BinaryReaderObjdumpBase::GetTypeName(Index index) const { + return objdump_state_->type_names.Get(index); +} + +std::string_view BinaryReaderObjdumpBase::GetFunctionName(Index index) const { + return objdump_state_->function_names.Get(index); +} + +std::string_view BinaryReaderObjdumpBase::GetGlobalName(Index index) const { + return objdump_state_->global_names.Get(index); +} + +std::string_view BinaryReaderObjdumpBase::GetLocalName( + Index function_index, + Index local_index) const { + return objdump_state_->local_names.Get(function_index, local_index); +} + +std::string_view BinaryReaderObjdumpBase::GetSectionName(Index index) const { + return objdump_state_->section_names.Get(index); +} + +std::string_view BinaryReaderObjdumpBase::GetTagName(Index index) const { + return objdump_state_->tag_names.Get(index); +} + +std::string_view BinaryReaderObjdumpBase::GetSegmentName(Index index) const { + return objdump_state_->segment_names.Get(index); +} + +std::string_view BinaryReaderObjdumpBase::GetTableName(Index index) const { + return objdump_state_->table_names.Get(index); +} + +std::string_view BinaryReaderObjdumpBase::GetSymbolName( + Index symbol_index) const { + if (symbol_index >= objdump_state_->symtab.size()) + return "<illegal_symbol_index>"; + ObjdumpSymbol& sym = objdump_state_->symtab[symbol_index]; + switch (sym.kind) { + case SymbolType::Function: + return GetFunctionName(sym.index); + case SymbolType::Data: + return sym.name; + case SymbolType::Global: + return GetGlobalName(sym.index); + case SymbolType::Section: + return GetSectionName(sym.index); + case SymbolType::Tag: + return GetTagName(sym.index); + case SymbolType::Table: + return GetTableName(sym.index); + } + WABT_UNREACHABLE; +} + +void BinaryReaderObjdumpBase::PrintRelocation(const Reloc& reloc, + Offset offset) const { + printf(" %06" PRIzx ": %-18s %" PRIindex, offset, + GetRelocTypeName(reloc.type), reloc.index); + if (reloc.addend) { + printf(" + %d", reloc.addend); + } + if (reloc.type != RelocType::TypeIndexLEB) { + printf(" <" PRIstringview ">", + WABT_PRINTF_STRING_VIEW_ARG(GetSymbolName(reloc.index))); + } + printf("\n"); +} + +Offset BinaryReaderObjdumpBase::GetPrintOffset(Offset offset) const { + return options_->section_offsets + ? offset - GetSectionStart(BinarySection::Code) + : offset; +} + +Result BinaryReaderObjdumpBase::OnOpcode(Opcode opcode) { + current_opcode = opcode; + return Result::Ok; +} + +Result BinaryReaderObjdumpBase::OnRelocCount(Index count, Index section_index) { + if (section_index >= section_types_.size()) { + err_stream_->Writef("invalid relocation section index: %" PRIindex "\n", + section_index); + reloc_section_ = BinarySection::Invalid; + return Result::Error; + } + reloc_section_ = section_types_[section_index]; + return Result::Ok; +} + +class BinaryReaderObjdumpPrepass : public BinaryReaderObjdumpBase { + public: + using BinaryReaderObjdumpBase::BinaryReaderObjdumpBase; + + Result BeginSection(Index section_index, + BinarySection section_code, + Offset size) override { + BinaryReaderObjdumpBase::BeginSection(section_index, section_code, size); + if (section_code != BinarySection::Custom) { + objdump_state_->section_names.Set(section_index, + wabt::GetSectionName(section_code)); + } + return Result::Ok; + } + + Result BeginCustomSection(Index section_index, + Offset size, + std::string_view section_name) override { + objdump_state_->section_names.Set(section_index, section_name); + return Result::Ok; + } + + Result OnFunctionName(Index index, std::string_view name) override { + SetFunctionName(index, name); + return Result::Ok; + } + + Result OnFuncType(Index index, + Index param_count, + Type* param_types, + Index result_count, + Type* result_types) override { + objdump_state_->function_param_counts[index] = param_count; + return Result::Ok; + } + + Result OnNameEntry(NameSectionSubsection type, + Index index, + std::string_view name) override { + switch (type) { + // TODO(sbc): remove OnFunctionName in favor of just using + // OnNameEntry so that this works + /* + case NameSectionSubsection::Function: + SetFunctionName(index, name); + break; + */ + case NameSectionSubsection::Type: + SetTypeName(index, name); + break; + case NameSectionSubsection::Global: + SetGlobalName(index, name); + break; + case NameSectionSubsection::Table: + SetTableName(index, name); + break; + case NameSectionSubsection::DataSegment: + SetSegmentName(index, name); + break; + case NameSectionSubsection::Tag: + SetTagName(index, name); + break; + default: + break; + } + return Result::Ok; + } + + Result OnLocalName(Index function_index, + Index local_index, + std::string_view local_name) override { + SetLocalName(function_index, local_index, local_name); + return Result::Ok; + } + + Result OnSymbolCount(Index count) override { + objdump_state_->symtab.resize(count); + return Result::Ok; + } + + Result OnDataSymbol(Index index, + uint32_t flags, + std::string_view name, + Index segment, + uint32_t offset, + uint32_t size) override { + objdump_state_->symtab[index] = {SymbolType::Data, std::string(name), 0}; + return Result::Ok; + } + + Result OnFunctionSymbol(Index index, + uint32_t flags, + std::string_view name, + Index func_index) override { + if (!name.empty()) { + SetFunctionName(func_index, name); + } + objdump_state_->symtab[index] = {SymbolType::Function, std::string(name), + func_index}; + return Result::Ok; + } + + Result OnGlobalSymbol(Index index, + uint32_t flags, + std::string_view name, + Index global_index) override { + if (!name.empty()) { + SetGlobalName(global_index, name); + } + objdump_state_->symtab[index] = {SymbolType::Global, std::string(name), + global_index}; + return Result::Ok; + } + + Result OnSectionSymbol(Index index, + uint32_t flags, + Index section_index) override { + objdump_state_->symtab[index] = {SymbolType::Section, + std::string(GetSectionName(section_index)), + section_index}; + return Result::Ok; + } + + Result OnTagSymbol(Index index, + uint32_t flags, + std::string_view name, + Index tag_index) override { + if (!name.empty()) { + SetTagName(tag_index, name); + } + objdump_state_->symtab[index] = {SymbolType::Tag, std::string(name), + tag_index}; + return Result::Ok; + } + + Result OnTableSymbol(Index index, + uint32_t flags, + std::string_view name, + Index table_index) override { + if (!name.empty()) { + SetTableName(table_index, name); + } + objdump_state_->symtab[index] = {SymbolType::Table, std::string(name), + table_index}; + return Result::Ok; + } + + Result OnImportFunc(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index func_index, + Index sig_index) override { + SetFunctionName(func_index, module_name + "." + field_name); + return Result::Ok; + } + + Result OnImportTag(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index tag_index, + Index sig_index) override { + SetTagName(tag_index, module_name + "." + field_name); + return Result::Ok; + } + + Result OnImportGlobal(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index global_index, + Type type, + bool mutable_) override { + SetGlobalName(global_index, module_name + "." + field_name); + return Result::Ok; + } + + Result OnImportTable(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) override { + SetTableName(table_index, module_name + "." + field_name); + return Result::Ok; + } + + Result OnExport(Index index, + ExternalKind kind, + Index item_index, + std::string_view name) override { + if (kind == ExternalKind::Func) { + SetFunctionName(item_index, name); + } else if (kind == ExternalKind::Global) { + SetGlobalName(item_index, name); + } + return Result::Ok; + } + + Result OnReloc(RelocType type, + Offset offset, + Index index, + uint32_t addend) override; + + Result OnModuleName(std::string_view name) override { + if (options_->mode == ObjdumpMode::Prepass) { + printf("module name: <" PRIstringview ">\n", + WABT_PRINTF_STRING_VIEW_ARG(name)); + } + return Result::Ok; + } + + Result OnSegmentInfo(Index index, + std::string_view name, + Address alignment_log2, + uint32_t flags) override { + SetSegmentName(index, name); + return Result::Ok; + } + + protected: + void SetTypeName(Index index, std::string_view name); + void SetFunctionName(Index index, std::string_view name); + void SetGlobalName(Index index, std::string_view name); + void SetLocalName(Index function_index, + Index local_index, + std::string_view name); + void SetTagName(Index index, std::string_view name); + void SetTableName(Index index, std::string_view name); + void SetSegmentName(Index index, std::string_view name); +}; + +void BinaryReaderObjdumpPrepass::SetTypeName(Index index, + std::string_view name) { + objdump_state_->type_names.Set(index, name); +} + +void BinaryReaderObjdumpPrepass::SetFunctionName(Index index, + std::string_view name) { + objdump_state_->function_names.Set(index, name); +} + +void BinaryReaderObjdumpPrepass::SetGlobalName(Index index, + std::string_view name) { + objdump_state_->global_names.Set(index, name); +} + +void BinaryReaderObjdumpPrepass::SetLocalName(Index function_index, + Index local_index, + std::string_view name) { + objdump_state_->local_names.Set(function_index, local_index, name); +} + +void BinaryReaderObjdumpPrepass::SetTagName(Index index, + std::string_view name) { + objdump_state_->tag_names.Set(index, name); +} + +void BinaryReaderObjdumpPrepass::SetTableName(Index index, + std::string_view name) { + objdump_state_->table_names.Set(index, name); +} + +void BinaryReaderObjdumpPrepass::SetSegmentName(Index index, + std::string_view name) { + objdump_state_->segment_names.Set(index, name); +} + +Result BinaryReaderObjdumpPrepass::OnReloc(RelocType type, + Offset offset, + Index index, + uint32_t addend) { + BinaryReaderObjdumpBase::OnReloc(type, offset, index, addend); + if (reloc_section_ == BinarySection::Code) { + objdump_state_->code_relocations.emplace_back(type, offset, index, addend); + } else if (reloc_section_ == BinarySection::Data) { + objdump_state_->data_relocations.emplace_back(type, offset, index, addend); + } + return Result::Ok; +} + +class BinaryReaderObjdumpDisassemble : public BinaryReaderObjdumpBase { + public: + using BinaryReaderObjdumpBase::BinaryReaderObjdumpBase; + + std::string BlockSigToString(Type type) const; + + Result BeginFunctionBody(Index index, Offset size) override; + Result EndFunctionBody(Index index) override; + + Result OnLocalDeclCount(Index count) override; + Result OnLocalDecl(Index decl_index, Index count, Type type) override; + + Result OnOpcode(Opcode Opcode) override; + Result OnOpcodeBare() override; + Result OnOpcodeIndex(Index value) override; + Result OnOpcodeIndexIndex(Index value, Index value2) override; + Result OnOpcodeUint32(uint32_t value) override; + Result OnOpcodeUint32Uint32(uint32_t value, uint32_t value2) override; + Result OnCallIndirectExpr(uint32_t sig_indix, uint32_t table_index) override; + Result OnOpcodeUint32Uint32Uint32(uint32_t value, + uint32_t value2, + uint32_t value3) override; + Result OnOpcodeUint32Uint32Uint32Uint32(uint32_t value, + uint32_t value2, + uint32_t value3, + uint32_t value4) override; + Result OnOpcodeUint64(uint64_t value) override; + Result OnOpcodeF32(uint32_t value) override; + Result OnOpcodeF64(uint64_t value) override; + Result OnOpcodeV128(v128 value) override; + Result OnOpcodeBlockSig(Type sig_type) override; + Result OnOpcodeType(Type type) override; + + Result OnBrTableExpr(Index num_targets, + Index* target_depths, + Index default_target_depth) override; + Result OnDelegateExpr(Index) override; + Result OnEndExpr() override; + + private: + void LogOpcode(const char* fmt, ...); + + Offset current_opcode_offset = 0; + Offset last_opcode_end = 0; + int indent_level = 0; + Index next_reloc = 0; + Index current_function_index = 0; + Index local_index_ = 0; + bool in_function_body = false; + bool skip_next_opcode_ = false; +}; + +std::string BinaryReaderObjdumpDisassemble::BlockSigToString(Type type) const { + if (type.IsIndex()) { + return StringPrintf("type[%d]", type.GetIndex()); + } else if (type == Type::Void) { + return ""; + } else { + return type.GetName(); + } +} + +Result BinaryReaderObjdumpDisassemble::OnOpcode(Opcode opcode) { + BinaryReaderObjdumpBase::OnOpcode(opcode); + if (!in_function_body) { + return Result::Ok; + } + if (options_->debug) { + const char* opcode_name = opcode.GetName(); + err_stream_->Writef("on_opcode: %#" PRIzx ": %s\n", state->offset, + opcode_name); + } + + if (last_opcode_end) { + // Takes care of cases where opcode's bytes was a non-canonical leb128 + // encoding. In this case, opcode.GetLength() under-reports the length, + // since it canonicalizes the opcode. + if (state->offset < last_opcode_end + opcode.GetLength()) { + Opcode missing_opcode = Opcode::FromCode(data_[last_opcode_end]); + const char* opcode_name = missing_opcode.GetName(); + fprintf(stderr, + "error: %#" PRIzx " missing opcode callback at %#" PRIzx + " (%#02x=%s)\n", + state->offset, last_opcode_end + 1, data_[last_opcode_end], + opcode_name); + return Result::Error; + } + } + + current_opcode_offset = state->offset; + return Result::Ok; +} + +#define IMMEDIATE_OCTET_COUNT 9 + +Result BinaryReaderObjdumpDisassemble::OnLocalDeclCount(Index count) { + if (!in_function_body) { + return Result::Ok; + } + current_opcode_offset = state->offset; + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnLocalDecl(Index decl_index, + Index count, + Type type) { + if (!in_function_body) { + return Result::Ok; + } + Offset offset = current_opcode_offset; + size_t data_size = state->offset - offset; + + printf(" %06" PRIzx ":", GetPrintOffset(offset)); + for (size_t i = 0; i < data_size && i < IMMEDIATE_OCTET_COUNT; + i++, offset++) { + printf(" %02x", data_[offset]); + } + for (size_t i = data_size; i < IMMEDIATE_OCTET_COUNT; i++) { + printf(" "); + } + printf(" | local[%" PRIindex, local_index_); + + if (count != 1) { + printf("..%" PRIindex "", local_index_ + count - 1); + } + local_index_ += count; + + printf("] type=%s\n", type.GetName().c_str()); + + last_opcode_end = current_opcode_offset + data_size; + current_opcode_offset = last_opcode_end; + + return Result::Ok; +} + +void BinaryReaderObjdumpDisassemble::LogOpcode(const char* fmt, ...) { + // BinaryReaderObjdumpDisassemble is only used to disassembly function bodies + // so this should never be called for instructions outside of function bodies + // (i.e. init expresions). + assert(in_function_body); + if (skip_next_opcode_) { + skip_next_opcode_ = false; + return; + } + const Offset immediate_len = state->offset - current_opcode_offset; + const Offset opcode_size = current_opcode.GetLength(); + const Offset total_size = opcode_size + immediate_len; + // current_opcode_offset has already read past this opcode; rewind it by the + // size of this opcode, which may be more than one byte. + Offset offset = current_opcode_offset - opcode_size; + const Offset offset_end = offset + total_size; + + bool first_line = true; + while (offset < offset_end) { + // Print bytes, but only display a maximum of IMMEDIATE_OCTET_COUNT on each + // line. + printf(" %06" PRIzx ":", GetPrintOffset(offset)); + size_t i; + for (i = 0; offset < offset_end && i < IMMEDIATE_OCTET_COUNT; + ++i, ++offset) { + printf(" %02x", data_[offset]); + } + // Fill the rest of the remaining space with spaces. + for (; i < IMMEDIATE_OCTET_COUNT; ++i) { + printf(" "); + } + printf(" | "); + + if (first_line) { + first_line = false; + + // Print disassembly. + int indent_level = this->indent_level; + switch (current_opcode) { + case Opcode::Else: + case Opcode::Catch: + case Opcode::CatchAll: + indent_level--; + break; + default: + break; + } + for (int j = 0; j < indent_level; j++) { + printf(" "); + } + + const char* opcode_name = current_opcode.GetName(); + printf("%s", opcode_name); + if (fmt) { + printf(" "); + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + } + } + + printf("\n"); + } + + last_opcode_end = state->offset; + + // Print relocation after then full (potentially multi-line) instruction. + if (options_->relocs && + next_reloc < objdump_state_->code_relocations.size()) { + const Reloc& reloc = objdump_state_->code_relocations[next_reloc]; + Offset code_start = GetSectionStart(BinarySection::Code); + Offset abs_offset = code_start + reloc.offset; + if (last_opcode_end > abs_offset) { + PrintRelocation(reloc, abs_offset); + next_reloc++; + } + } +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeBare() { + if (!in_function_body) { + return Result::Ok; + } + LogOpcode(0, nullptr); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeIndex(Index value) { + if (!in_function_body) { + return Result::Ok; + } + std::string_view name; + if (current_opcode == Opcode::Call && + !(name = GetFunctionName(value)).empty()) { + LogOpcode("%d <" PRIstringview ">", value, + WABT_PRINTF_STRING_VIEW_ARG(name)); + } else if (current_opcode == Opcode::Throw && + !(name = GetTagName(value)).empty()) { + LogOpcode("%d <" PRIstringview ">", value, + WABT_PRINTF_STRING_VIEW_ARG(name)); + } else if ((current_opcode == Opcode::GlobalGet || + current_opcode == Opcode::GlobalSet) && + !(name = GetGlobalName(value)).empty()) { + LogOpcode("%d <" PRIstringview ">", value, + WABT_PRINTF_STRING_VIEW_ARG(name)); + } else if ((current_opcode == Opcode::LocalGet || + current_opcode == Opcode::LocalSet) && + !(name = GetLocalName(current_function_index, value)).empty()) { + LogOpcode("%d <" PRIstringview ">", value, + WABT_PRINTF_STRING_VIEW_ARG(name)); + } else { + LogOpcode("%d", value); + } + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeIndexIndex(Index value, + Index value2) { + if (!in_function_body) { + return Result::Ok; + } + LogOpcode("%" PRIindex " %" PRIindex, value, value2); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32(uint32_t value) { + if (!in_function_body) { + return Result::Ok; + } + std::string_view name; + if (current_opcode == Opcode::DataDrop && + !(name = GetSegmentName(value)).empty()) { + LogOpcode("%d <" PRIstringview ">", value, + WABT_PRINTF_STRING_VIEW_ARG(name)); + } else { + LogOpcode("%u", value); + } + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32Uint32(uint32_t value, + uint32_t value2) { + if (!in_function_body) + return Result::Ok; + std::string_view name; + if (current_opcode == Opcode::MemoryInit && + !(name = GetSegmentName(value)).empty()) { + LogOpcode("%u %u <" PRIstringview ">", value, value2, + WABT_PRINTF_STRING_VIEW_ARG(name)); + } else { + LogOpcode("%u %u", value, value2); + } + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnCallIndirectExpr( + uint32_t sig_index, + uint32_t table_index) { + std::string_view table_name = GetTableName(table_index); + std::string_view type_name = GetTypeName(sig_index); + if (!type_name.empty() && !table_name.empty()) { + LogOpcode("%u <" PRIstringview "> (type %u <" PRIstringview ">)", + table_index, WABT_PRINTF_STRING_VIEW_ARG(table_name), sig_index, + WABT_PRINTF_STRING_VIEW_ARG(type_name)); + } else if (!table_name.empty()) { + LogOpcode("%u <" PRIstringview "> (type %u)", table_index, + WABT_PRINTF_STRING_VIEW_ARG(table_name), sig_index); + } else if (!type_name.empty()) { + LogOpcode("%u (type %u <" PRIstringview ">)", table_index, sig_index, + WABT_PRINTF_STRING_VIEW_ARG(type_name)); + } else { + LogOpcode("%u (type %u)", table_index, sig_index); + } + skip_next_opcode_ = true; + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32Uint32Uint32( + uint32_t value, + uint32_t value2, + uint32_t value3) { + if (!in_function_body) { + return Result::Ok; + } + LogOpcode("%u %u %u", value, value2, value3); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32Uint32Uint32Uint32( + uint32_t value, + uint32_t value2, + uint32_t value3, + uint32_t value4) { + if (!in_function_body) { + return Result::Ok; + } + LogOpcode("%u %u %u %u", value, value2, value3, value4); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeUint64(uint64_t value) { + if (!in_function_body) { + return Result::Ok; + } + LogOpcode("%" PRId64, value); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeF32(uint32_t value) { + if (!in_function_body) { + return Result::Ok; + } + char buffer[WABT_MAX_FLOAT_HEX]; + WriteFloatHex(buffer, sizeof(buffer), value); + LogOpcode(buffer); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeF64(uint64_t value) { + if (!in_function_body) { + return Result::Ok; + } + char buffer[WABT_MAX_DOUBLE_HEX]; + WriteDoubleHex(buffer, sizeof(buffer), value); + LogOpcode(buffer); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeV128(v128 value) { + if (!in_function_body) { + return Result::Ok; + } + // v128 is always dumped as i32x4: + LogOpcode("0x%08x 0x%08x 0x%08x 0x%08x", value.u32(0), value.u32(1), + value.u32(2), value.u32(3)); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeType(Type type) { + if (!in_function_body) { + return Result::Ok; + } + if (current_opcode == Opcode::SelectT) { + LogOpcode(type.GetName().c_str()); + } else { + LogOpcode(type.GetRefKindName()); + } + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnBrTableExpr( + Index num_targets, + Index* target_depths, + Index default_target_depth) { + if (!in_function_body) { + return Result::Ok; + } + + std::string buffer = std::string(); + for (Index i = 0; i < num_targets; i++) { + buffer.append(std::to_string(target_depths[i])).append(" "); + } + buffer.append(std::to_string(default_target_depth)); + + LogOpcode("%s", buffer.c_str()); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnDelegateExpr(Index depth) { + if (!in_function_body) { + return Result::Ok; + } + // Because `delegate` ends the block we need to dedent here, and + // we don't need to dedent it in LogOpcode. + if (indent_level > 0) { + indent_level--; + } + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnEndExpr() { + if (!in_function_body) { + return Result::Ok; + } + if (indent_level > 0) { + indent_level--; + } + LogOpcode(0, nullptr); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::BeginFunctionBody(Index index, + Offset size) { + printf("%06" PRIzx " func[%" PRIindex "]", GetPrintOffset(state->offset), + index); + auto name = GetFunctionName(index); + if (!name.empty()) { + printf(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + printf(":\n"); + + last_opcode_end = 0; + in_function_body = true; + current_function_index = index; + local_index_ = objdump_state_->function_param_counts[index]; + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::EndFunctionBody(Index index) { + assert(in_function_body); + in_function_body = false; + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeBlockSig(Type sig_type) { + if (!in_function_body) { + return Result::Ok; + } + if (sig_type != Type::Void) { + LogOpcode("%s", BlockSigToString(sig_type).c_str()); + } else { + LogOpcode(nullptr); + } + indent_level++; + return Result::Ok; +} + +enum class InitExprType { + I32, + F32, + I64, + F64, + V128, + Global, + FuncRef, + // TODO: There isn't a nullref anymore, this just represents ref.null of some + // type T. + NullRef, +}; + +struct InitInst { + Opcode opcode; + union { + Index index; + uint32_t i32; + uint32_t f32; + uint64_t i64; + uint64_t f64; + v128 v128_v; + Type type; + } imm; +}; + +struct InitExpr { + InitExprType type; + std::vector<InitInst> insts; +}; + +class BinaryReaderObjdump : public BinaryReaderObjdumpBase { + public: + BinaryReaderObjdump(const uint8_t* data, + size_t size, + ObjdumpOptions* options, + ObjdumpState* state); + + Result EndModule() override; + Result BeginSection(Index section_index, + BinarySection section_type, + Offset size) override; + Result BeginCustomSection(Index section_index, + Offset size, + std::string_view section_name) override; + + Result OnTypeCount(Index count) override; + Result OnFuncType(Index index, + Index param_count, + Type* param_types, + Index result_count, + Type* result_types) override; + Result OnStructType(Index index, Index field_count, TypeMut* fields) override; + Result OnArrayType(Index index, TypeMut field) override; + + Result OnImportCount(Index count) override; + Result OnImportFunc(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index func_index, + Index sig_index) override; + Result OnImportTable(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) override; + Result OnImportMemory(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index memory_index, + const Limits* page_limits) override; + Result OnImportGlobal(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index global_index, + Type type, + bool mutable_) override; + Result OnImportTag(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index tag_index, + Index sig_index) override; + + Result OnFunctionCount(Index count) override; + Result OnFunction(Index index, Index sig_index) override; + + Result OnTableCount(Index count) override; + Result OnTable(Index index, + Type elem_type, + const Limits* elem_limits) override; + + Result OnMemoryCount(Index count) override; + Result OnMemory(Index index, const Limits* limits) override; + + Result OnGlobalCount(Index count) override; + Result BeginGlobal(Index index, Type type, bool mutable_) override; + + Result OnExportCount(Index count) override; + Result OnExport(Index index, + ExternalKind kind, + Index item_index, + std::string_view name) override; + + Result OnStartFunction(Index func_index) override; + Result OnDataCount(Index count) override; + + Result OnFunctionBodyCount(Index count) override; + Result BeginFunctionBody(Index index, Offset size) override; + + Result OnElemSegmentCount(Index count) override; + Result BeginElemSegment(Index index, + Index table_index, + uint8_t flags) override; + Result OnElemSegmentElemType(Index index, Type elem_type) override; + Result OnElemSegmentElemExprCount(Index index, Index count) override; + Result OnElemSegmentElemExpr_RefNull(Index segment_index, Type type) override; + Result OnElemSegmentElemExpr_RefFunc(Index segment_index, + Index func_index) override; + + void BeginInitExpr() { current_init_expr_.insts.clear(); } + + Result BeginElemSegmentInitExpr(Index index) override { + reading_elem_init_expr_ = true; + BeginInitExpr(); + return Result::Ok; + } + + Result EndElemSegmentInitExpr(Index index) override { return EndInitExpr(); } + + Result BeginDataSegmentInitExpr(Index index) override { + reading_data_init_expr_ = true; + BeginInitExpr(); + return Result::Ok; + } + + Result EndDataSegmentInitExpr(Index index) override { return EndInitExpr(); } + + Result BeginGlobalInitExpr(Index index) override { + reading_global_init_expr_ = true; + BeginInitExpr(); + return Result::Ok; + } + + Result EndGlobalInitExpr(Index index) override { return EndInitExpr(); } + + Result OnDataSegmentCount(Index count) override; + Result BeginDataSegment(Index index, + Index memory_index, + uint8_t flags) override; + Result OnDataSegmentData(Index index, + const void* data, + Address size) override; + + Result OnModuleName(std::string_view name) override; + Result OnFunctionName(Index function_index, + std::string_view function_name) override; + Result OnLocalName(Index function_index, + Index local_index, + std::string_view local_name) override; + Result OnNameEntry(NameSectionSubsection type, + Index index, + std::string_view name) override; + + Result OnDylinkInfo(uint32_t mem_size, + uint32_t mem_align_log2, + uint32_t table_size, + uint32_t table_align_log2) override; + Result OnDylinkNeededCount(Index count) override; + Result OnDylinkNeeded(std::string_view so_name) override; + Result OnDylinkImportCount(Index count) override; + Result OnDylinkExportCount(Index count) override; + Result OnDylinkImport(std::string_view module, + std::string_view name, + uint32_t flags) override; + Result OnDylinkExport(std::string_view name, uint32_t flags) override; + + Result OnRelocCount(Index count, Index section_index) override; + Result OnReloc(RelocType type, + Offset offset, + Index index, + uint32_t addend) override; + + Result OnFeature(uint8_t prefix, std::string_view name) override; + + Result OnSymbolCount(Index count) override; + Result OnDataSymbol(Index index, + uint32_t flags, + std::string_view name, + Index segment, + uint32_t offset, + uint32_t size) override; + Result OnFunctionSymbol(Index index, + uint32_t flags, + std::string_view name, + Index func_index) override; + Result OnGlobalSymbol(Index index, + uint32_t flags, + std::string_view name, + Index global_index) override; + Result OnSectionSymbol(Index index, + uint32_t flags, + Index section_index) override; + Result OnTagSymbol(Index index, + uint32_t flags, + std::string_view name, + Index tag_index) override; + Result OnTableSymbol(Index index, + uint32_t flags, + std::string_view name, + Index table_index) override; + Result OnSegmentInfoCount(Index count) override; + Result OnSegmentInfo(Index index, + std::string_view name, + Address alignment_log2, + uint32_t flags) override; + Result OnInitFunctionCount(Index count) override; + Result OnInitFunction(uint32_t priority, Index symbol_index) override; + Result OnComdatCount(Index count) override; + Result OnComdatBegin(std::string_view name, + uint32_t flags, + Index count) override; + Result OnComdatEntry(ComdatType kind, Index index) override; + + Result OnTagCount(Index count) override; + Result OnTagType(Index index, Index sig_index) override; + + Result OnOpcode(Opcode Opcode) override; + Result OnI32ConstExpr(uint32_t value) override; + Result OnI64ConstExpr(uint64_t value) override; + Result OnF32ConstExpr(uint32_t value) override; + Result OnF64ConstExpr(uint64_t value) override; + Result OnGlobalGetExpr(Index global_index) override; + Result OnCodeMetadataCount(Index function_index, Index count) override; + Result OnCodeMetadata(Offset code_offset, + const void* data, + Address size) override; + + private: + Result EndInitExpr(); + bool ShouldPrintDetails(); + void PrintDetails(const char* fmt, ...); + Result PrintSymbolFlags(uint32_t flags); + Result PrintSegmentFlags(uint32_t flags); + void PrintInitExpr(const InitExpr& expr, bool as_unsigned = false); + Result OnCount(Index count); + + std::unique_ptr<FileStream> out_stream_; + Index elem_index_ = 0; + Index table_index_ = 0; + Index next_data_reloc_ = 0; + bool reading_elem_init_expr_ = false; + bool reading_data_init_expr_ = false; + bool reading_global_init_expr_ = false; + InitExpr current_init_expr_; + uint8_t data_flags_ = 0; + uint8_t elem_flags_ = 0; + Index data_mem_index_ = 0; + uint64_t data_offset_ = 0; + uint64_t elem_offset_ = 0; + + bool ReadingInitExpr() { + return reading_elem_init_expr_ || reading_data_init_expr_ || + reading_global_init_expr_; + } +}; + +BinaryReaderObjdump::BinaryReaderObjdump(const uint8_t* data, + size_t size, + ObjdumpOptions* options, + ObjdumpState* objdump_state) + : BinaryReaderObjdumpBase(data, size, options, objdump_state), + out_stream_(FileStream::CreateStdout()) {} + +Result BinaryReaderObjdump::BeginCustomSection(Index section_index, + Offset size, + std::string_view section_name) { + PrintDetails(" - name: \"" PRIstringview "\"\n", + WABT_PRINTF_STRING_VIEW_ARG(section_name)); + if (options_->mode == ObjdumpMode::Headers) { + printf("\"" PRIstringview "\"\n", + WABT_PRINTF_STRING_VIEW_ARG(section_name)); + } + return Result::Ok; +} + +Result BinaryReaderObjdump::BeginSection(Index section_index, + BinarySection section_code, + Offset size) { + BinaryReaderObjdumpBase::BeginSection(section_index, section_code, size); + + // |section_name| and |match_name| are identical for known sections. For + // custom sections, |section_name| is "Custom", but |match_name| is the name + // of the custom section. + const char* section_name = wabt::GetSectionName(section_code); + std::string match_name(GetSectionName(section_index)); + + bool section_match = !options_->section_name || + !strcasecmp(options_->section_name, match_name.c_str()); + if (section_match) { + section_found_ = true; + } + + switch (options_->mode) { + case ObjdumpMode::Headers: + printf("%9s start=%#010" PRIzx " end=%#010" PRIzx " (size=%#010" PRIoffset + ") ", + section_name, state->offset, state->offset + size, size); + break; + case ObjdumpMode::Details: + if (section_match) { + printf("%s", section_name); + // All known section types except the Start and DataCount sections have + // a count in which case this line gets completed in OnCount(). + if (section_code == BinarySection::Start || + section_code == BinarySection::DataCount || + section_code == BinarySection::Custom) { + printf(":\n"); + } + print_details_ = true; + } else { + print_details_ = false; + } + break; + case ObjdumpMode::RawData: + if (section_match) { + printf("\nContents of section %s:\n", section_name); + out_stream_->WriteMemoryDump(data_ + state->offset, size, state->offset, + PrintChars::Yes); + } + break; + case ObjdumpMode::Prepass: + case ObjdumpMode::Disassemble: + break; + } + return Result::Ok; +} + +bool BinaryReaderObjdump::ShouldPrintDetails() { + if (options_->mode != ObjdumpMode::Details) { + return false; + } + return print_details_; +} + +void WABT_PRINTF_FORMAT(2, 3) BinaryReaderObjdump::PrintDetails(const char* fmt, + ...) { + if (!ShouldPrintDetails()) { + return; + } + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +Result BinaryReaderObjdump::OnCount(Index count) { + if (options_->mode == ObjdumpMode::Headers) { + printf("count: %" PRIindex "\n", count); + } else if (options_->mode == ObjdumpMode::Details && print_details_) { + printf("[%" PRIindex "]:\n", count); + } + return Result::Ok; +} + +Result BinaryReaderObjdump::EndModule() { + if (options_->section_name && !section_found_) { + err_stream_->Writef("Section not found: %s\n", options_->section_name); + return Result::Error; + } + + if (options_->relocs && ShouldPrintDetails()) { + if (next_data_reloc_ != objdump_state_->data_relocations.size()) { + err_stream_->Writef("Data reloctions outside of segments!:\n"); + for (size_t i = next_data_reloc_; + i < objdump_state_->data_relocations.size(); i++) { + const Reloc& reloc = objdump_state_->data_relocations[i]; + PrintRelocation(reloc, reloc.offset); + } + + return Result::Error; + } + } + + return Result::Ok; +} + +Result BinaryReaderObjdump::OnTypeCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::OnFuncType(Index index, + Index param_count, + Type* param_types, + Index result_count, + Type* result_types) { + if (!ShouldPrintDetails()) { + return Result::Ok; + } + printf(" - type[%" PRIindex "] (", index); + for (Index i = 0; i < param_count; i++) { + if (i != 0) { + printf(", "); + } + printf("%s", param_types[i].GetName().c_str()); + } + printf(") -> "); + switch (result_count) { + case 0: + printf("nil"); + break; + case 1: + printf("%s", result_types[0].GetName().c_str()); + break; + default: + printf("("); + for (Index i = 0; i < result_count; i++) { + if (i != 0) { + printf(", "); + } + printf("%s", result_types[i].GetName().c_str()); + } + printf(")"); + break; + } + printf("\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnStructType(Index index, + Index field_count, + TypeMut* fields) { + if (!ShouldPrintDetails()) { + return Result::Ok; + } + printf(" - type[%" PRIindex "] (struct", index); + for (Index i = 0; i < field_count; i++) { + if (fields[i].mutable_) { + printf(" (mut"); + } + printf(" %s", fields[i].type.GetName().c_str()); + if (fields[i].mutable_) { + printf(")"); + } + } + printf(")\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnArrayType(Index index, TypeMut field) { + if (!ShouldPrintDetails()) { + return Result::Ok; + } + printf(" - type[%" PRIindex "] (array", index); + if (field.mutable_) { + printf(" (mut"); + } + printf(" %s", field.type.GetName().c_str()); + if (field.mutable_) { + printf(")"); + } + printf(")\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnFunctionCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::OnFunction(Index index, Index sig_index) { + PrintDetails(" - func[%" PRIindex "] sig=%" PRIindex, index, sig_index); + auto name = GetFunctionName(index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails("\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnFunctionBodyCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::BeginFunctionBody(Index index, Offset size) { + PrintDetails(" - func[%" PRIindex "] size=%" PRIzd, index, size); + auto name = GetFunctionName(index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails("\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnStartFunction(Index func_index) { + if (options_->mode == ObjdumpMode::Headers) { + printf("start: %" PRIindex "\n", func_index); + } else { + PrintDetails(" - start function: %" PRIindex, func_index); + auto name = GetFunctionName(func_index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails("\n"); + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnDataCount(Index count) { + if (options_->mode == ObjdumpMode::Headers) { + printf("count: %" PRIindex "\n", count); + } else { + PrintDetails(" - data count: %" PRIindex "\n", count); + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnImportCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::OnImportFunc(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index func_index, + Index sig_index) { + PrintDetails(" - func[%" PRIindex "] sig=%" PRIindex, func_index, sig_index); + auto name = GetFunctionName(func_index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails(" <- " PRIstringview "." PRIstringview "\n", + WABT_PRINTF_STRING_VIEW_ARG(module_name), + WABT_PRINTF_STRING_VIEW_ARG(field_name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnImportTable(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) { + PrintDetails(" - table[%" PRIindex "] type=%s initial=%" PRId64, table_index, + elem_type.GetName().c_str(), elem_limits->initial); + if (elem_limits->has_max) { + PrintDetails(" max=%" PRId64, elem_limits->max); + } + PrintDetails(" <- " PRIstringview "." PRIstringview "\n", + WABT_PRINTF_STRING_VIEW_ARG(module_name), + WABT_PRINTF_STRING_VIEW_ARG(field_name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnImportMemory(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index memory_index, + const Limits* page_limits) { + PrintDetails(" - memory[%" PRIindex "] pages: initial=%" PRId64, memory_index, + page_limits->initial); + if (page_limits->has_max) { + PrintDetails(" max=%" PRId64, page_limits->max); + } + if (page_limits->is_shared) { + PrintDetails(" shared"); + } + if (page_limits->is_64) { + PrintDetails(" i64"); + } + PrintDetails(" <- " PRIstringview "." PRIstringview "\n", + WABT_PRINTF_STRING_VIEW_ARG(module_name), + WABT_PRINTF_STRING_VIEW_ARG(field_name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnImportGlobal(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index global_index, + Type type, + bool mutable_) { + PrintDetails(" - global[%" PRIindex "] %s mutable=%d", global_index, + type.GetName().c_str(), mutable_); + PrintDetails(" <- " PRIstringview "." PRIstringview "\n", + WABT_PRINTF_STRING_VIEW_ARG(module_name), + WABT_PRINTF_STRING_VIEW_ARG(field_name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnImportTag(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index tag_index, + Index sig_index) { + PrintDetails(" - tag[%" PRIindex "] sig=%" PRIindex, tag_index, sig_index); + auto name = GetTagName(tag_index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails(" <- " PRIstringview "." PRIstringview "\n", + WABT_PRINTF_STRING_VIEW_ARG(module_name), + WABT_PRINTF_STRING_VIEW_ARG(field_name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnMemoryCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::OnMemory(Index index, const Limits* page_limits) { + PrintDetails(" - memory[%" PRIindex "] pages: initial=%" PRId64, index, + page_limits->initial); + if (page_limits->has_max) { + PrintDetails(" max=%" PRId64, page_limits->max); + } + if (page_limits->is_shared) { + PrintDetails(" shared"); + } + if (page_limits->is_64) { + PrintDetails(" i64"); + } + PrintDetails("\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnTableCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::OnTable(Index index, + Type elem_type, + const Limits* elem_limits) { + PrintDetails(" - table[%" PRIindex "] type=%s initial=%" PRId64, index, + elem_type.GetName().c_str(), elem_limits->initial); + if (elem_limits->has_max) { + PrintDetails(" max=%" PRId64, elem_limits->max); + } + auto name = GetTableName(index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails("\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnExportCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::OnExport(Index index, + ExternalKind kind, + Index item_index, + std::string_view name) { + PrintDetails(" - %s[%" PRIindex "]", GetKindName(kind), item_index); + if (kind == ExternalKind::Func) { + auto name = GetFunctionName(item_index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + } + + PrintDetails(" -> \"" PRIstringview "\"\n", + WABT_PRINTF_STRING_VIEW_ARG(name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnElemSegmentElemExpr_RefNull(Index segment_index, + Type type) { + PrintDetails(" - elem[%" PRIu64 "] = ref.null %s\n", + elem_offset_ + elem_index_, type.GetName().c_str()); + elem_index_++; + return Result::Ok; +} + +Result BinaryReaderObjdump::OnElemSegmentElemExpr_RefFunc(Index segment_index, + Index func_index) { + PrintDetails(" - elem[%" PRIu64 "] = func[%" PRIindex "]", + elem_offset_ + elem_index_, func_index); + auto name = GetFunctionName(func_index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails("\n"); + elem_index_++; + return Result::Ok; +} + +Result BinaryReaderObjdump::OnElemSegmentCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::BeginElemSegment(Index index, + Index table_index, + uint8_t flags) { + table_index_ = table_index; + elem_index_ = 0; + elem_flags_ = flags; + return Result::Ok; +} + +Result BinaryReaderObjdump::OnElemSegmentElemType(Index index, Type elem_type) { + // TODO: Add support for this. + return Result::Ok; +} + +Result BinaryReaderObjdump::OnElemSegmentElemExprCount(Index index, + Index count) { + PrintDetails(" - segment[%" PRIindex "] flags=%d table=%" PRIindex + " count=%" PRIindex, + index, elem_flags_, table_index_, count); + if (elem_flags_ & SegPassive) { + PrintDetails("\n"); + } else { + PrintInitExpr(current_init_expr_, /*as_unsigned=*/true); + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnGlobalCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::BeginGlobal(Index index, Type type, bool mutable_) { + PrintDetails(" - global[%" PRIindex "] %s mutable=%d", index, + type.GetName().c_str(), mutable_); + std::string_view name = GetGlobalName(index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + return Result::Ok; +} + +void BinaryReaderObjdump::PrintInitExpr(const InitExpr& expr, + bool as_unsigned) { + assert(expr.insts.size() > 0); + + // We have two different way to print init expressions. One for + // extended expressions involving more than one instruction, and + // a short form for the more traditional single instruction form. + if (expr.insts.size() > 1) { + PrintDetails(" - init ("); + bool first = true; + for (auto& inst : expr.insts) { + if (!first) { + PrintDetails(", "); + } + first = false; + PrintDetails("%s", inst.opcode.GetName()); + switch (inst.opcode) { + case Opcode::I32Const: + PrintDetails(" %d", inst.imm.i32); + break; + case Opcode::I64Const: + PrintDetails(" %" PRId64, inst.imm.i64); + break; + case Opcode::F32Const: { + char buffer[WABT_MAX_FLOAT_HEX]; + WriteFloatHex(buffer, sizeof(buffer), inst.imm.f32); + PrintDetails(" %s\n", buffer); + break; + } + case Opcode::F64Const: { + char buffer[WABT_MAX_DOUBLE_HEX]; + WriteDoubleHex(buffer, sizeof(buffer), inst.imm.f64); + PrintDetails(" %s\n", buffer); + break; + } + case Opcode::GlobalGet: { + PrintDetails(" %" PRIindex, inst.imm.index); + std::string_view name = GetGlobalName(inst.imm.index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", + WABT_PRINTF_STRING_VIEW_ARG(name)); + } + break; + } + default: + break; + } + } + PrintDetails(")\n"); + return; + } + + switch (expr.type) { + case InitExprType::I32: + if (as_unsigned) { + PrintDetails(" - init i32=%u\n", expr.insts[0].imm.i32); + } else { + PrintDetails(" - init i32=%d\n", expr.insts[0].imm.i32); + } + break; + case InitExprType::I64: + if (as_unsigned) { + PrintDetails(" - init i64=%" PRIu64 "\n", expr.insts[0].imm.i64); + } else { + PrintDetails(" - init i64=%" PRId64 "\n", expr.insts[0].imm.i64); + } + break; + case InitExprType::F64: { + char buffer[WABT_MAX_DOUBLE_HEX]; + WriteDoubleHex(buffer, sizeof(buffer), expr.insts[0].imm.f64); + PrintDetails(" - init f64=%s\n", buffer); + break; + } + case InitExprType::F32: { + char buffer[WABT_MAX_FLOAT_HEX]; + WriteFloatHex(buffer, sizeof(buffer), expr.insts[0].imm.f32); + PrintDetails(" - init f32=%s\n", buffer); + break; + } + case InitExprType::V128: { + PrintDetails( + " - init v128=0x%08x 0x%08x 0x%08x 0x%08x \n", + expr.insts[0].imm.v128_v.u32(0), expr.insts[0].imm.v128_v.u32(1), + expr.insts[0].imm.v128_v.u32(2), expr.insts[0].imm.v128_v.u32(3)); + break; + } + case InitExprType::Global: { + PrintDetails(" - init global=%" PRIindex, expr.insts[0].imm.index); + std::string_view name = GetGlobalName(expr.insts[0].imm.index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails("\n"); + break; + } + case InitExprType::FuncRef: { + PrintDetails(" - init ref.func:%" PRIindex, expr.insts[0].imm.index); + std::string_view name = GetFunctionName(expr.insts[0].imm.index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails("\n"); + break; + } + case InitExprType::NullRef: + PrintDetails(" - init null\n"); + break; + break; + } +} + +static void InitExprToConstOffset(const InitExpr& expr, uint64_t* out_offset) { + if (expr.insts.size() == 1) { + switch (expr.type) { + case InitExprType::I32: + *out_offset = expr.insts[0].imm.i32; + break; + case InitExprType::I64: + *out_offset = expr.insts[0].imm.i64; + break; + default: + break; + } + } +} + +Result BinaryReaderObjdump::EndInitExpr() { + if (reading_data_init_expr_) { + reading_data_init_expr_ = false; + InitExprToConstOffset(current_init_expr_, &data_offset_); + } else if (reading_elem_init_expr_) { + reading_elem_init_expr_ = false; + InitExprToConstOffset(current_init_expr_, &elem_offset_); + } else if (reading_global_init_expr_) { + reading_global_init_expr_ = false; + PrintInitExpr(current_init_expr_); + } else { + WABT_UNREACHABLE; + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnI32ConstExpr(uint32_t value) { + if (ReadingInitExpr()) { + current_init_expr_.type = InitExprType::I32; + current_init_expr_.insts.back().imm.i32 = value; + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnI64ConstExpr(uint64_t value) { + if (ReadingInitExpr()) { + current_init_expr_.type = InitExprType::I64; + current_init_expr_.insts.back().imm.i64 = value; + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnF32ConstExpr(uint32_t value) { + if (ReadingInitExpr()) { + current_init_expr_.type = InitExprType::F32; + current_init_expr_.insts.back().imm.f32 = value; + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnF64ConstExpr(uint64_t value) { + if (ReadingInitExpr()) { + current_init_expr_.type = InitExprType::F64; + current_init_expr_.insts.back().imm.f64 = value; + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnOpcode(Opcode opcode) { + BinaryReaderObjdumpBase::OnOpcode(opcode); + if (ReadingInitExpr() && opcode != Opcode::End) { + InitInst i; + i.opcode = current_opcode; + current_init_expr_.insts.push_back(i); + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnGlobalGetExpr(Index global_index) { + if (ReadingInitExpr()) { + current_init_expr_.type = InitExprType::Global; + current_init_expr_.insts.back().imm.index = global_index; + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnModuleName(std::string_view name) { + PrintDetails(" - module <" PRIstringview ">\n", + WABT_PRINTF_STRING_VIEW_ARG(name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnFunctionName(Index index, std::string_view name) { + PrintDetails(" - func[%" PRIindex "] <" PRIstringview ">\n", index, + WABT_PRINTF_STRING_VIEW_ARG(name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnNameEntry(NameSectionSubsection type, + Index index, + std::string_view name) { + PrintDetails(" - %s[%" PRIindex "] <" PRIstringview ">\n", + GetNameSectionSubsectionName(type), index, + WABT_PRINTF_STRING_VIEW_ARG(name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnLocalName(Index func_index, + Index local_index, + std::string_view name) { + if (!name.empty()) { + PrintDetails(" - func[%" PRIindex "] local[%" PRIindex "] <" PRIstringview + ">\n", + func_index, local_index, WABT_PRINTF_STRING_VIEW_ARG(name)); + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnDataSegmentCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::BeginDataSegment(Index index, + Index memory_index, + uint8_t flags) { + data_mem_index_ = memory_index; + data_flags_ = flags; + return Result::Ok; +} + +Result BinaryReaderObjdump::OnDataSegmentData(Index index, + const void* src_data, + Address size) { + if (!ShouldPrintDetails()) { + return Result::Ok; + } + + PrintDetails(" - segment[%" PRIindex "]", index); + auto name = GetSegmentName(index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + if (data_flags_ & SegPassive) { + PrintDetails(" passive"); + } else { + PrintDetails(" memory=%" PRIindex, data_mem_index_); + } + PrintDetails(" size=%" PRIaddress, size); + if (data_flags_ & SegPassive) { + PrintDetails("\n"); + } else { + PrintInitExpr(current_init_expr_, /*as_unsigned=*/true); + } + + out_stream_->WriteMemoryDump(src_data, size, data_offset_, PrintChars::Yes, + " - "); + + // Print relocations from this segment. + if (!options_->relocs) { + return Result::Ok; + } + + Offset data_start = GetSectionStart(BinarySection::Data); + Offset segment_start = state->offset - size; + Offset segment_offset = segment_start - data_start; + while (next_data_reloc_ < objdump_state_->data_relocations.size()) { + const Reloc& reloc = objdump_state_->data_relocations[next_data_reloc_]; + Offset abs_offset = data_start + reloc.offset; + if (abs_offset > state->offset) { + break; + } + PrintRelocation(reloc, reloc.offset - segment_offset + data_offset_); + next_data_reloc_++; + } + + return Result::Ok; +} + +Result BinaryReaderObjdump::OnDylinkInfo(uint32_t mem_size, + uint32_t mem_align_log2, + uint32_t table_size, + uint32_t table_align_log2) { + PrintDetails(" - mem_size : %u\n", mem_size); + PrintDetails(" - mem_p2align : %u\n", mem_align_log2); + PrintDetails(" - table_size : %u\n", table_size); + PrintDetails(" - table_p2align: %u\n", table_align_log2); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnDylinkNeededCount(Index count) { + if (count) { + PrintDetails(" - needed_dynlibs[%u]:\n", count); + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnDylinkImportCount(Index count) { + PrintDetails(" - imports[%u]:\n", count); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnDylinkExportCount(Index count) { + PrintDetails(" - exports[%u]:\n", count); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnDylinkExport(std::string_view name, + uint32_t flags) { + PrintDetails(" - " PRIstringview, WABT_PRINTF_STRING_VIEW_ARG(name)); + return PrintSymbolFlags(flags); +} + +Result BinaryReaderObjdump::OnDylinkImport(std::string_view module, + std::string_view name, + uint32_t flags) { + PrintDetails(" - " PRIstringview "." PRIstringview, + WABT_PRINTF_STRING_VIEW_ARG(module), + WABT_PRINTF_STRING_VIEW_ARG(name)); + return PrintSymbolFlags(flags); +} + +Result BinaryReaderObjdump::OnDylinkNeeded(std::string_view so_name) { + PrintDetails(" - " PRIstringview "\n", WABT_PRINTF_STRING_VIEW_ARG(so_name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnRelocCount(Index count, Index section_index) { + BinaryReaderObjdumpBase::OnRelocCount(count, section_index); + PrintDetails(" - relocations for section: %d (" PRIstringview ") [%d]\n", + section_index, + WABT_PRINTF_STRING_VIEW_ARG(GetSectionName(section_index)), + count); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnReloc(RelocType type, + Offset offset, + Index index, + uint32_t addend) { + Offset total_offset = GetSectionStart(reloc_section_) + offset; + PrintDetails(" - %-18s offset=%#08" PRIoffset "(file=%#08" PRIoffset ") ", + GetRelocTypeName(type), offset, total_offset); + if (type == RelocType::TypeIndexLEB) { + PrintDetails("type=%" PRIindex, index); + } else { + PrintDetails("symbol=%" PRIindex " <" PRIstringview ">", index, + WABT_PRINTF_STRING_VIEW_ARG(GetSymbolName(index))); + } + + if (addend) { + int32_t signed_addend = static_cast<int32_t>(addend); + if (signed_addend < 0) { + PrintDetails("-"); + signed_addend = -signed_addend; + } else { + PrintDetails("+"); + } + PrintDetails("%#x", signed_addend); + } + PrintDetails("\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnFeature(uint8_t prefix, std::string_view name) { + PrintDetails(" - [%c] " PRIstringview "\n", prefix, + WABT_PRINTF_STRING_VIEW_ARG(name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnSymbolCount(Index count) { + PrintDetails(" - symbol table [count=%d]\n", count); + return Result::Ok; +} + +Result BinaryReaderObjdump::PrintSymbolFlags(uint32_t flags) { + if (flags > WABT_SYMBOL_FLAG_MAX) { + err_stream_->Writef("Unknown symbols flags: %x\n", flags); + return Result::Error; + } + + const char* binding_name = nullptr; + SymbolBinding binding = + static_cast<SymbolBinding>(flags & WABT_SYMBOL_MASK_BINDING); + switch (binding) { + case SymbolBinding::Global: + binding_name = "global"; + break; + case SymbolBinding::Local: + binding_name = "local"; + break; + case SymbolBinding::Weak: + binding_name = "weak"; + break; + } + flags &= ~WABT_SYMBOL_MASK_BINDING; + + const char* vis_name = nullptr; + SymbolVisibility vis = + static_cast<SymbolVisibility>(flags & WABT_SYMBOL_MASK_VISIBILITY); + switch (vis) { + case SymbolVisibility::Hidden: + vis_name = "hidden"; + break; + case SymbolVisibility::Default: + vis_name = "default"; + break; + } + flags &= ~WABT_SYMBOL_MASK_VISIBILITY; + + PrintDetails(" ["); + if (flags & WABT_SYMBOL_FLAG_UNDEFINED) { + PrintDetails(" undefined"); + flags &= ~WABT_SYMBOL_FLAG_UNDEFINED; + } + if (flags & WABT_SYMBOL_FLAG_EXPORTED) { + PrintDetails(" exported"); + flags &= ~WABT_SYMBOL_FLAG_EXPORTED; + } + if (flags & WABT_SYMBOL_FLAG_EXPLICIT_NAME) { + PrintDetails(" explicit_name"); + flags &= ~WABT_SYMBOL_FLAG_EXPLICIT_NAME; + } + if (flags & WABT_SYMBOL_FLAG_NO_STRIP) { + PrintDetails(" no_strip"); + flags &= ~WABT_SYMBOL_FLAG_NO_STRIP; + } + if (flags & WABT_SYMBOL_FLAG_TLS) { + PrintDetails(" tls"); + flags &= ~WABT_SYMBOL_FLAG_TLS; + } + if (flags != 0) { + PrintDetails(" unknown_flags=%#x", flags); + } + PrintDetails(" binding=%s vis=%s ]\n", binding_name, vis_name); + return Result::Ok; +} + +Result BinaryReaderObjdump::PrintSegmentFlags(uint32_t flags) { + if (flags > WABT_SYMBOL_FLAG_MAX) { + err_stream_->Writef("Unknown symbols flags: %x\n", flags); + return Result::Error; + } + PrintDetails(" ["); + if (flags & WABT_SEGMENT_FLAG_STRINGS) { + PrintDetails(" STRINGS"); + flags &= ~WABT_SEGMENT_FLAG_STRINGS; + } + if (flags & WABT_SEGMENT_FLAG_TLS) { + PrintDetails(" TLS"); + flags &= ~WABT_SEGMENT_FLAG_TLS; + } + if (flags != 0) { + PrintDetails(" unknown_flags=%#x", flags); + } + PrintDetails(" ]\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnDataSymbol(Index index, + uint32_t flags, + std::string_view name, + Index segment, + uint32_t offset, + uint32_t size) { + PrintDetails(" - %d: D <" PRIstringview ">", index, + WABT_PRINTF_STRING_VIEW_ARG(name)); + if (!(flags & WABT_SYMBOL_FLAG_UNDEFINED)) + PrintDetails(" segment=%" PRIindex " offset=%d size=%d", segment, offset, + size); + return PrintSymbolFlags(flags); +} + +Result BinaryReaderObjdump::OnFunctionSymbol(Index index, + uint32_t flags, + std::string_view name, + Index func_index) { + if (name.empty()) { + name = GetFunctionName(func_index); + } + PrintDetails(" - %d: F <" PRIstringview "> func=%" PRIindex, index, + WABT_PRINTF_STRING_VIEW_ARG(name), func_index); + return PrintSymbolFlags(flags); +} + +Result BinaryReaderObjdump::OnGlobalSymbol(Index index, + uint32_t flags, + std::string_view name, + Index global_index) { + if (name.empty()) { + name = GetGlobalName(global_index); + } + PrintDetails(" - %d: G <" PRIstringview "> global=%" PRIindex, index, + WABT_PRINTF_STRING_VIEW_ARG(name), global_index); + return PrintSymbolFlags(flags); +} + +Result BinaryReaderObjdump::OnSectionSymbol(Index index, + uint32_t flags, + Index section_index) { + auto sym_name = GetSectionName(section_index); + assert(!sym_name.empty()); + PrintDetails(" - %d: S <" PRIstringview "> section=%" PRIindex, index, + WABT_PRINTF_STRING_VIEW_ARG(sym_name), section_index); + return PrintSymbolFlags(flags); +} + +Result BinaryReaderObjdump::OnTagSymbol(Index index, + uint32_t flags, + std::string_view name, + Index tag_index) { + if (name.empty()) { + name = GetTagName(tag_index); + } + PrintDetails(" - %d: E <" PRIstringview "> tag=%" PRIindex, index, + WABT_PRINTF_STRING_VIEW_ARG(name), tag_index); + return PrintSymbolFlags(flags); +} + +Result BinaryReaderObjdump::OnTableSymbol(Index index, + uint32_t flags, + std::string_view name, + Index table_index) { + if (name.empty()) { + name = GetTableName(table_index); + } + PrintDetails(" - %d: T <" PRIstringview "> table=%" PRIindex, index, + WABT_PRINTF_STRING_VIEW_ARG(name), table_index); + return PrintSymbolFlags(flags); +} + +Result BinaryReaderObjdump::OnSegmentInfoCount(Index count) { + PrintDetails(" - segment info [count=%d]\n", count); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnSegmentInfo(Index index, + std::string_view name, + Address alignment_log2, + uint32_t flags) { + PrintDetails(" - %d: " PRIstringview " p2align=%" PRIaddress, index, + WABT_PRINTF_STRING_VIEW_ARG(name), alignment_log2); + return PrintSegmentFlags(flags); +} + +Result BinaryReaderObjdump::OnInitFunctionCount(Index count) { + PrintDetails(" - init functions [count=%d]\n", count); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnInitFunction(uint32_t priority, + Index symbol_index) { + PrintDetails(" - %d: priority=%d", symbol_index, priority); + auto name = GetSymbolName(symbol_index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails("\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnComdatCount(Index count) { + PrintDetails(" - comdat groups [count=%d]\n", count); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnComdatBegin(std::string_view name, + uint32_t flags, + Index count) { + PrintDetails(" - " PRIstringview ": [count=%d]\n", + WABT_PRINTF_STRING_VIEW_ARG(name), count); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnComdatEntry(ComdatType kind, Index index) { + switch (kind) { + case ComdatType::Data: { + PrintDetails(" - segment[%" PRIindex "]", index); + auto name = GetSegmentName(index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + break; + } + case ComdatType::Function: { + PrintDetails(" - func[%" PRIindex "]", index); + auto name = GetFunctionName(index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + break; + } + } + PrintDetails("\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnTagCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::OnTagType(Index index, Index sig_index) { + if (!ShouldPrintDetails()) { + return Result::Ok; + } + printf(" - tag[%" PRIindex "] sig=%" PRIindex "\n", index, sig_index); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnCodeMetadataCount(Index function_index, + Index count) { + if (!ShouldPrintDetails()) { + return Result::Ok; + } + printf(" - func[%" PRIindex "]", function_index); + auto name = GetFunctionName(function_index); + if (!name.empty()) { + printf(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + printf(":\n"); + return Result::Ok; +} +Result BinaryReaderObjdump::OnCodeMetadata(Offset code_offset, + const void* data, + Address size) { + if (!ShouldPrintDetails()) { + return Result::Ok; + } + printf(" - meta[%" PRIzx "]:\n", code_offset); + + out_stream_->WriteMemoryDump(data, size, 0, PrintChars::Yes, " - "); + return Result::Ok; +} + +} // end anonymous namespace + +std::string_view ObjdumpNames::Get(Index index) const { + auto iter = names.find(index); + if (iter == names.end()) + return std::string_view(); + return iter->second; +} + +void ObjdumpNames::Set(Index index, std::string_view name) { + names[index] = std::string(name); +} + +std::string_view ObjdumpLocalNames::Get(Index function_index, + Index local_index) const { + auto iter = names.find(std::pair<Index, Index>(function_index, local_index)); + if (iter == names.end()) + return std::string_view(); + return iter->second; +} + +void ObjdumpLocalNames::Set(Index function_index, + Index local_index, + std::string_view name) { + names[std::pair<Index, Index>(function_index, local_index)] = + std::string(name); +} + +Result ReadBinaryObjdump(const uint8_t* data, + size_t size, + ObjdumpOptions* options, + ObjdumpState* state) { + Features features; + features.EnableAll(); + const bool kReadDebugNames = true; + const bool kStopOnFirstError = false; + const bool kFailOnCustomSectionError = false; + ReadBinaryOptions read_options(features, options->log_stream, kReadDebugNames, + kStopOnFirstError, kFailOnCustomSectionError); + + switch (options->mode) { + case ObjdumpMode::Prepass: { + read_options.skip_function_bodies = true; + BinaryReaderObjdumpPrepass reader(data, size, options, state); + return ReadBinary(data, size, &reader, read_options); + } + case ObjdumpMode::Disassemble: { + BinaryReaderObjdumpDisassemble reader(data, size, options, state); + return ReadBinary(data, size, &reader, read_options); + } + default: { + read_options.skip_function_bodies = true; + BinaryReaderObjdump reader(data, size, options, state); + return ReadBinary(data, size, &reader, read_options); + } + } +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary-reader-opcnt.cc b/third_party/wasm2c/src/binary-reader-opcnt.cc new file mode 100644 index 0000000000..3177dbb7eb --- /dev/null +++ b/third_party/wasm2c/src/binary-reader-opcnt.cc @@ -0,0 +1,314 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/binary-reader-opcnt.h" + +#include <cassert> +#include <cinttypes> +#include <cstdarg> +#include <cstdint> +#include <cstdio> + +#include "wabt/binary-reader-nop.h" +#include "wabt/common.h" +#include "wabt/literal.h" +#include "wabt/stream.h" + +namespace wabt { + +OpcodeInfo::OpcodeInfo(Opcode opcode, Kind kind) + : opcode_(opcode), kind_(kind) {} + +template <typename T> +OpcodeInfo::OpcodeInfo(Opcode opcode, Kind kind, T* data, size_t count) + : OpcodeInfo(opcode, kind) { + if (count > 0) { + data_.resize(sizeof(T) * count); + memcpy(data_.data(), data, data_.size()); + } +} + +template <typename T> +OpcodeInfo::OpcodeInfo(Opcode opcode, Kind kind, T* data, size_t count, T extra) + : OpcodeInfo(opcode, kind, data, count) { + data_.resize(data_.size() + sizeof(T)); + memcpy(data_.data() + data_.size() - sizeof(T), &extra, sizeof(T)); +} + +template <typename T> +std::pair<const T*, size_t> OpcodeInfo::GetDataArray() const { + if (data_.empty()) { + return std::pair<const T*, size_t>(nullptr, 0); + } + + assert(data_.size() % sizeof(T) == 0); + return std::make_pair(reinterpret_cast<const T*>(data_.data()), + data_.size() / sizeof(T)); +} + +template <typename T> +const T* OpcodeInfo::GetData(size_t expected_size) const { + auto [data, size] = GetDataArray<T>(); + assert(size == expected_size); + return data; +} + +template <typename T, typename F> +void OpcodeInfo::WriteArray(Stream& stream, F&& write_func) { + auto [data, size] = GetDataArray<T>(); + for (size_t i = 0; i < size; ++i) { + // Write an initial space (to separate from the opcode name) first, then + // comma-separate. + stream.Writef("%s", i == 0 ? " " : ", "); + write_func(data[i]); + } +} + +void OpcodeInfo::Write(Stream& stream) { + stream.Writef("%s", opcode_.GetName()); + + switch (kind_) { + case Kind::Bare: + break; + + case Kind::Uint32: + stream.Writef(" %u (0x%x)", *GetData<uint32_t>(), *GetData<uint32_t>()); + break; + + case Kind::Uint64: + stream.Writef(" %" PRIu64 " (0x%" PRIx64 ")", *GetData<uint64_t>(), + *GetData<uint64_t>()); + break; + + case Kind::Index: + stream.Writef(" %" PRIindex, *GetData<Index>()); + break; + + case Kind::Float32: { + stream.Writef(" %g", *GetData<float>()); + char buffer[WABT_MAX_FLOAT_HEX + 1]; + WriteFloatHex(buffer, sizeof(buffer), *GetData<uint32_t>()); + stream.Writef(" (%s)", buffer); + break; + } + + case Kind::Float64: { + stream.Writef(" %g", *GetData<double>()); + char buffer[WABT_MAX_DOUBLE_HEX + 1]; + WriteDoubleHex(buffer, sizeof(buffer), *GetData<uint64_t>()); + stream.Writef(" (%s)", buffer); + break; + } + + case Kind::V128: { + auto data = *GetData<v128>(); + auto l0 = data.u32(0); + auto l1 = data.u32(1); + auto l2 = data.u32(2); + auto l3 = data.u32(3); + stream.Writef(" %u %u %u %u (0x%x 0x%x 0x%x 0x%x)", l0, l1, l2, l3, l0, + l1, l2, l3); + break; + } + + case Kind::Uint32Uint32: + case Kind::Uint32Uint32Uint32: + case Kind::Uint32Uint32Uint32Uint32: + WriteArray<uint32_t>( + stream, [&stream](uint32_t value) { stream.Writef("%u", value); }); + break; + + case Kind::BlockSig: { + auto type = *GetData<Type>(); + if (type.IsIndex()) { + stream.Writef(" type:%d", type.GetIndex()); + } else if (type != Type::Void) { + stream.Writef(" %s", type.GetName().c_str()); + } + break; + } + + case Kind::BrTable: { + WriteArray<Index>(stream, [&stream](Index index) { + stream.Writef("%" PRIindex, index); + }); + break; + } + } +} + +bool operator==(const OpcodeInfo& lhs, const OpcodeInfo& rhs) { + return lhs.opcode_ == rhs.opcode_ && lhs.kind_ == rhs.kind_ && + lhs.data_ == rhs.data_; +} + +bool operator!=(const OpcodeInfo& lhs, const OpcodeInfo& rhs) { + return !(lhs == rhs); +} + +bool operator<(const OpcodeInfo& lhs, const OpcodeInfo& rhs) { + if (lhs.opcode_ < rhs.opcode_) { + return true; + } + if (lhs.opcode_ > rhs.opcode_) { + return false; + } + if (lhs.kind_ < rhs.kind_) { + return true; + } + if (lhs.kind_ > rhs.kind_) { + return false; + } + if (lhs.data_ < rhs.data_) { + return true; + } + if (lhs.data_ > rhs.data_) { + return false; + } + return false; +} + +bool operator<=(const OpcodeInfo& lhs, const OpcodeInfo& rhs) { + return lhs < rhs || lhs == rhs; +} + +bool operator>(const OpcodeInfo& lhs, const OpcodeInfo& rhs) { + return !(lhs <= rhs); +} + +bool operator>=(const OpcodeInfo& lhs, const OpcodeInfo& rhs) { + return !(lhs < rhs); +} + +namespace { + +class BinaryReaderOpcnt : public BinaryReaderNop { + public: + explicit BinaryReaderOpcnt(OpcodeInfoCounts* counts); + + Result OnOpcode(Opcode opcode) override; + Result OnOpcodeBare() override; + Result OnOpcodeUint32(uint32_t value) override; + Result OnOpcodeIndex(Index value) override; + Result OnOpcodeUint32Uint32(uint32_t value, uint32_t value2) override; + Result OnOpcodeUint32Uint32Uint32(uint32_t value, + uint32_t value2, + uint32_t value3) override; + Result OnOpcodeUint64(uint64_t value) override; + Result OnOpcodeF32(uint32_t value) override; + Result OnOpcodeF64(uint64_t value) override; + Result OnOpcodeV128(v128 value) override; + Result OnOpcodeBlockSig(Type sig_type) override; + Result OnBrTableExpr(Index num_targets, + Index* target_depths, + Index default_target_depth) override; + Result OnEndExpr() override; + + private: + template <typename... Args> + Result Emplace(Args&&... args); + + OpcodeInfoCounts* opcode_counts_; + Opcode current_opcode_; +}; + +template <typename... Args> +Result BinaryReaderOpcnt::Emplace(Args&&... args) { + auto pair = opcode_counts_->emplace( + std::piecewise_construct, std::make_tuple(std::forward<Args>(args)...), + std::make_tuple(0)); + + auto& count = pair.first->second; + count++; + return Result::Ok; +} + +BinaryReaderOpcnt::BinaryReaderOpcnt(OpcodeInfoCounts* counts) + : opcode_counts_(counts) {} + +Result BinaryReaderOpcnt::OnOpcode(Opcode opcode) { + current_opcode_ = opcode; + return Result::Ok; +} + +Result BinaryReaderOpcnt::OnOpcodeBare() { + return Emplace(current_opcode_, OpcodeInfo::Kind::Bare); +} + +Result BinaryReaderOpcnt::OnOpcodeUint32(uint32_t value) { + return Emplace(current_opcode_, OpcodeInfo::Kind::Uint32, &value); +} + +Result BinaryReaderOpcnt::OnOpcodeIndex(Index value) { + return Emplace(current_opcode_, OpcodeInfo::Kind::Index, &value); +} + +Result BinaryReaderOpcnt::OnOpcodeUint32Uint32(uint32_t value0, + uint32_t value1) { + uint32_t array[2] = {value0, value1}; + return Emplace(current_opcode_, OpcodeInfo::Kind::Uint32Uint32, array, 2); +} + +Result BinaryReaderOpcnt::OnOpcodeUint32Uint32Uint32(uint32_t value0, + uint32_t value1, + uint32_t value2) { + uint32_t array[3] = {value0, value1, value2}; + return Emplace(current_opcode_, OpcodeInfo::Kind::Uint32Uint32Uint32, array, + 3); +} + +Result BinaryReaderOpcnt::OnOpcodeUint64(uint64_t value) { + return Emplace(current_opcode_, OpcodeInfo::Kind::Uint64, &value); +} + +Result BinaryReaderOpcnt::OnOpcodeF32(uint32_t value) { + return Emplace(current_opcode_, OpcodeInfo::Kind::Float32, &value); +} + +Result BinaryReaderOpcnt::OnOpcodeF64(uint64_t value) { + return Emplace(current_opcode_, OpcodeInfo::Kind::Float64, &value); +} + +Result BinaryReaderOpcnt::OnOpcodeV128(v128 value) { + return Emplace(current_opcode_, OpcodeInfo::Kind::V128, &value); +} + +Result BinaryReaderOpcnt::OnOpcodeBlockSig(Type sig_type) { + return Emplace(current_opcode_, OpcodeInfo::Kind::BlockSig, &sig_type); +} + +Result BinaryReaderOpcnt::OnBrTableExpr(Index num_targets, + Index* target_depths, + Index default_target_depth) { + return Emplace(current_opcode_, OpcodeInfo::Kind::BrTable, target_depths, + num_targets, default_target_depth); +} + +Result BinaryReaderOpcnt::OnEndExpr() { + return Emplace(Opcode::End, OpcodeInfo::Kind::Bare); +} + +} // end anonymous namespace + +Result ReadBinaryOpcnt(const void* data, + size_t size, + const ReadBinaryOptions& options, + OpcodeInfoCounts* counts) { + BinaryReaderOpcnt reader(counts); + return ReadBinary(data, size, &reader, options); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary-reader.cc b/third_party/wasm2c/src/binary-reader.cc new file mode 100644 index 0000000000..8e86383db4 --- /dev/null +++ b/third_party/wasm2c/src/binary-reader.cc @@ -0,0 +1,3027 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/binary-reader.h" + +#include <cassert> +#include <cinttypes> +#include <cstdarg> +#include <cstdint> +#include <cstdio> +#include <cstring> +#include <vector> + +#include "wabt/config.h" + +#include "wabt/binary-reader-logging.h" +#include "wabt/binary.h" +#include "wabt/leb128.h" +#include "wabt/stream.h" +#include "wabt/utf8.h" + +#if HAVE_ALLOCA +#include <alloca.h> +#endif + +#define ERROR_IF(expr, ...) \ + do { \ + if (expr) { \ + PrintError(__VA_ARGS__); \ + return Result::Error; \ + } \ + } while (0) + +#define ERROR_UNLESS(expr, ...) ERROR_IF(!(expr), __VA_ARGS__) + +#define ERROR_UNLESS_OPCODE_ENABLED(opcode) \ + do { \ + if (!opcode.IsEnabled(options_.features)) { \ + return ReportUnexpectedOpcode(opcode); \ + } \ + } while (0) + +#define CALLBACK0(member) \ + ERROR_UNLESS(Succeeded(delegate_->member()), #member " callback failed") + +#define CALLBACK(member, ...) \ + ERROR_UNLESS(Succeeded(delegate_->member(__VA_ARGS__)), \ + #member " callback failed") + +namespace wabt { + +namespace { + +class BinaryReader { + public: + struct ReadModuleOptions { + bool stop_on_first_error; + }; + + BinaryReader(const void* data, + size_t size, + BinaryReaderDelegate* delegate, + const ReadBinaryOptions& options); + + Result ReadModule(const ReadModuleOptions& options); + + private: + template <typename T, T BinaryReader::*member> + struct ValueRestoreGuard { + explicit ValueRestoreGuard(BinaryReader* this_) + : this_(this_), previous_value_(this_->*member) {} + ~ValueRestoreGuard() { this_->*member = previous_value_; } + + BinaryReader* this_; + T previous_value_; + }; + + struct ReadSectionsOptions { + bool stop_on_first_error; + }; + + void WABT_PRINTF_FORMAT(2, 3) PrintError(const char* format, ...); + [[nodiscard]] Result ReadOpcode(Opcode* out_value, const char* desc); + template <typename T> + [[nodiscard]] Result ReadT(T* out_value, + const char* type_name, + const char* desc); + [[nodiscard]] Result ReadU8(uint8_t* out_value, const char* desc); + [[nodiscard]] Result ReadU32(uint32_t* out_value, const char* desc); + [[nodiscard]] Result ReadF32(uint32_t* out_value, const char* desc); + [[nodiscard]] Result ReadF64(uint64_t* out_value, const char* desc); + [[nodiscard]] Result ReadV128(v128* out_value, const char* desc); + [[nodiscard]] Result ReadU32Leb128(uint32_t* out_value, const char* desc); + [[nodiscard]] Result ReadU64Leb128(uint64_t* out_value, const char* desc); + [[nodiscard]] Result ReadS32Leb128(uint32_t* out_value, const char* desc); + [[nodiscard]] Result ReadS64Leb128(uint64_t* out_value, const char* desc); + [[nodiscard]] Result ReadType(Type* out_value, const char* desc); + [[nodiscard]] Result ReadRefType(Type* out_value, const char* desc); + [[nodiscard]] Result ReadExternalKind(ExternalKind* out_value, + const char* desc); + [[nodiscard]] Result ReadStr(std::string_view* out_str, const char* desc); + [[nodiscard]] Result ReadBytes(const void** out_data, + Address* out_data_size, + const char* desc); + [[nodiscard]] Result ReadIndex(Index* index, const char* desc); + [[nodiscard]] Result ReadOffset(Offset* offset, const char* desc); + [[nodiscard]] Result ReadAlignment(Address* align_log2, const char* desc); + [[nodiscard]] Result ReadMemidx(Index* memidx, const char* desc); + [[nodiscard]] Result ReadMemLocation(Address* alignment_log2, + Index* memidx, + Address* offset, + const char* desc_align, + const char* desc_memidx, + const char* desc_offset, + uint8_t* lane_val = nullptr); + [[nodiscard]] Result CallbackMemLocation(const Address* alignment_log2, + const Index* memidx, + const Address* offset, + const uint8_t* lane_val = nullptr); + [[nodiscard]] Result ReadCount(Index* index, const char* desc); + [[nodiscard]] Result ReadField(TypeMut* out_value); + + bool IsConcreteType(Type); + bool IsBlockType(Type); + + Index NumTotalFuncs(); + + [[nodiscard]] Result ReadInitExpr(Index index); + [[nodiscard]] Result ReadTable(Type* out_elem_type, Limits* out_elem_limits); + [[nodiscard]] Result ReadMemory(Limits* out_page_limits); + [[nodiscard]] Result ReadGlobalHeader(Type* out_type, bool* out_mutable); + [[nodiscard]] Result ReadTagType(Index* out_sig_index); + [[nodiscard]] Result ReadAddress(Address* out_value, + Index memory, + const char* desc); + [[nodiscard]] Result ReadFunctionBody(Offset end_offset); + // ReadInstructions either until and END instruction, or until + // the given end_offset. + [[nodiscard]] Result ReadInstructions(bool stop_on_end, + Offset end_offset, + Opcode* final_opcode); + [[nodiscard]] Result ReadNameSection(Offset section_size); + [[nodiscard]] Result ReadRelocSection(Offset section_size); + [[nodiscard]] Result ReadDylinkSection(Offset section_size); + [[nodiscard]] Result ReadDylink0Section(Offset section_size); + [[nodiscard]] Result ReadTargetFeaturesSections(Offset section_size); + [[nodiscard]] Result ReadLinkingSection(Offset section_size); + [[nodiscard]] Result ReadCodeMetadataSection(std::string_view name, + Offset section_size); + [[nodiscard]] Result ReadCustomSection(Index section_index, + Offset section_size); + [[nodiscard]] Result ReadTypeSection(Offset section_size); + [[nodiscard]] Result ReadImportSection(Offset section_size); + [[nodiscard]] Result ReadFunctionSection(Offset section_size); + [[nodiscard]] Result ReadTableSection(Offset section_size); + [[nodiscard]] Result ReadMemorySection(Offset section_size); + [[nodiscard]] Result ReadGlobalSection(Offset section_size); + [[nodiscard]] Result ReadExportSection(Offset section_size); + [[nodiscard]] Result ReadStartSection(Offset section_size); + [[nodiscard]] Result ReadElemSection(Offset section_size); + [[nodiscard]] Result ReadCodeSection(Offset section_size); + [[nodiscard]] Result ReadDataSection(Offset section_size); + [[nodiscard]] Result ReadDataCountSection(Offset section_size); + [[nodiscard]] Result ReadTagSection(Offset section_size); + [[nodiscard]] Result ReadSections(const ReadSectionsOptions& options); + Result ReportUnexpectedOpcode(Opcode opcode, const char* message = nullptr); + + size_t read_end_ = 0; // Either the section end or data_size. + BinaryReaderDelegate::State state_; + BinaryReaderLogging logging_delegate_; + BinaryReaderDelegate* delegate_ = nullptr; + TypeVector param_types_; + TypeVector result_types_; + TypeMutVector fields_; + std::vector<Index> target_depths_; + const ReadBinaryOptions& options_; + BinarySection last_known_section_ = BinarySection::Invalid; + bool did_read_names_section_ = false; + bool reading_custom_section_ = false; + Index num_func_imports_ = 0; + Index num_table_imports_ = 0; + Index num_memory_imports_ = 0; + Index num_global_imports_ = 0; + Index num_tag_imports_ = 0; + Index num_function_signatures_ = 0; + Index num_function_bodies_ = 0; + Index data_count_ = kInvalidIndex; + std::vector<Limits> memories; + + using ReadEndRestoreGuard = + ValueRestoreGuard<size_t, &BinaryReader::read_end_>; +}; + +BinaryReader::BinaryReader(const void* data, + size_t size, + BinaryReaderDelegate* delegate, + const ReadBinaryOptions& options) + : read_end_(size), + state_(static_cast<const uint8_t*>(data), size), + logging_delegate_(options.log_stream, delegate), + delegate_(options.log_stream ? &logging_delegate_ : delegate), + options_(options), + last_known_section_(BinarySection::Invalid) { + delegate->OnSetState(&state_); +} + +void WABT_PRINTF_FORMAT(2, 3) BinaryReader::PrintError(const char* format, + ...) { + ErrorLevel error_level = + reading_custom_section_ && !options_.fail_on_custom_section_error + ? ErrorLevel::Warning + : ErrorLevel::Error; + + WABT_SNPRINTF_ALLOCA(buffer, length, format); + Error error(error_level, Location(state_.offset), buffer); + bool handled = delegate_->OnError(error); + + if (!handled) { + // Not great to just print, but we don't want to eat the error either. + fprintf(stderr, "%07" PRIzx ": %s: %s\n", state_.offset, + GetErrorLevelName(error_level), buffer); + } +} + +Result BinaryReader::ReportUnexpectedOpcode(Opcode opcode, const char* where) { + std::string message = "unexpected opcode"; + if (where) { + message += ' '; + message += where; + } + + message += ":"; + + std::vector<uint8_t> bytes = opcode.GetBytes(); + assert(bytes.size() > 0); + + for (uint8_t byte : bytes) { + message += StringPrintf(" 0x%x", byte); + } + + PrintError("%s", message.c_str()); + return Result::Error; +} + +Result BinaryReader::ReadOpcode(Opcode* out_value, const char* desc) { + uint8_t value = 0; + CHECK_RESULT(ReadU8(&value, desc)); + + if (Opcode::IsPrefixByte(value)) { + uint32_t code; + CHECK_RESULT(ReadU32Leb128(&code, desc)); + *out_value = Opcode::FromCode(value, code); + } else { + *out_value = Opcode::FromCode(value); + } + return Result::Ok; +} + +template <typename T> +Result BinaryReader::ReadT(T* out_value, + const char* type_name, + const char* desc) { + if (state_.offset + sizeof(T) > read_end_) { + PrintError("unable to read %s: %s", type_name, desc); + return Result::Error; + } +#if WABT_BIG_ENDIAN + uint8_t tmp[sizeof(T)]; + memcpy(tmp, state_.data + state_.offset, sizeof(tmp)); + SwapBytesSized(tmp, sizeof(tmp)); + memcpy(out_value, tmp, sizeof(T)); +#else + memcpy(out_value, state_.data + state_.offset, sizeof(T)); +#endif + state_.offset += sizeof(T); + return Result::Ok; +} + +Result BinaryReader::ReadU8(uint8_t* out_value, const char* desc) { + return ReadT(out_value, "uint8_t", desc); +} + +Result BinaryReader::ReadU32(uint32_t* out_value, const char* desc) { + return ReadT(out_value, "uint32_t", desc); +} + +Result BinaryReader::ReadF32(uint32_t* out_value, const char* desc) { + return ReadT(out_value, "float", desc); +} + +Result BinaryReader::ReadF64(uint64_t* out_value, const char* desc) { + return ReadT(out_value, "double", desc); +} + +Result BinaryReader::ReadV128(v128* out_value, const char* desc) { + return ReadT(out_value, "v128", desc); +} + +Result BinaryReader::ReadU32Leb128(uint32_t* out_value, const char* desc) { + const uint8_t* p = state_.data + state_.offset; + const uint8_t* end = state_.data + read_end_; + size_t bytes_read = wabt::ReadU32Leb128(p, end, out_value); + ERROR_UNLESS(bytes_read > 0, "unable to read u32 leb128: %s", desc); + state_.offset += bytes_read; + return Result::Ok; +} + +Result BinaryReader::ReadU64Leb128(uint64_t* out_value, const char* desc) { + const uint8_t* p = state_.data + state_.offset; + const uint8_t* end = state_.data + read_end_; + size_t bytes_read = wabt::ReadU64Leb128(p, end, out_value); + ERROR_UNLESS(bytes_read > 0, "unable to read u64 leb128: %s", desc); + state_.offset += bytes_read; + return Result::Ok; +} + +Result BinaryReader::ReadS32Leb128(uint32_t* out_value, const char* desc) { + const uint8_t* p = state_.data + state_.offset; + const uint8_t* end = state_.data + read_end_; + size_t bytes_read = wabt::ReadS32Leb128(p, end, out_value); + ERROR_UNLESS(bytes_read > 0, "unable to read i32 leb128: %s", desc); + state_.offset += bytes_read; + return Result::Ok; +} + +Result BinaryReader::ReadS64Leb128(uint64_t* out_value, const char* desc) { + const uint8_t* p = state_.data + state_.offset; + const uint8_t* end = state_.data + read_end_; + size_t bytes_read = wabt::ReadS64Leb128(p, end, out_value); + ERROR_UNLESS(bytes_read > 0, "unable to read i64 leb128: %s", desc); + state_.offset += bytes_read; + return Result::Ok; +} + +Result BinaryReader::ReadType(Type* out_value, const char* desc) { + uint32_t type = 0; + CHECK_RESULT(ReadS32Leb128(&type, desc)); + if (static_cast<Type::Enum>(type) == Type::Reference) { + uint32_t heap_type = 0; + CHECK_RESULT(ReadS32Leb128(&heap_type, desc)); + *out_value = Type(Type::Reference, heap_type); + } else { + *out_value = static_cast<Type>(type); + } + return Result::Ok; +} + +Result BinaryReader::ReadRefType(Type* out_value, const char* desc) { + uint32_t type = 0; + CHECK_RESULT(ReadS32Leb128(&type, desc)); + *out_value = static_cast<Type>(type); + ERROR_UNLESS(out_value->IsRef(), "%s must be a reference type", desc); + return Result::Ok; +} + +Result BinaryReader::ReadExternalKind(ExternalKind* out_value, + const char* desc) { + uint8_t value = 0; + CHECK_RESULT(ReadU8(&value, desc)); + ERROR_UNLESS(value < kExternalKindCount, "invalid export external kind: %d", + value); + *out_value = static_cast<ExternalKind>(value); + return Result::Ok; +} + +Result BinaryReader::ReadStr(std::string_view* out_str, const char* desc) { + uint32_t str_len = 0; + CHECK_RESULT(ReadU32Leb128(&str_len, "string length")); + + ERROR_UNLESS(state_.offset + str_len <= read_end_, + "unable to read string: %s", desc); + + *out_str = std::string_view( + reinterpret_cast<const char*>(state_.data) + state_.offset, str_len); + state_.offset += str_len; + + ERROR_UNLESS(IsValidUtf8(out_str->data(), out_str->length()), + "invalid utf-8 encoding: %s", desc); + return Result::Ok; +} + +Result BinaryReader::ReadBytes(const void** out_data, + Address* out_data_size, + const char* desc) { + uint32_t data_size = 0; + CHECK_RESULT(ReadU32Leb128(&data_size, "data size")); + + ERROR_UNLESS(state_.offset + data_size <= read_end_, + "unable to read data: %s", desc); + + *out_data = static_cast<const uint8_t*>(state_.data) + state_.offset; + *out_data_size = data_size; + state_.offset += data_size; + return Result::Ok; +} + +Result BinaryReader::ReadIndex(Index* index, const char* desc) { + uint32_t value; + CHECK_RESULT(ReadU32Leb128(&value, desc)); + *index = value; + return Result::Ok; +} + +Result BinaryReader::ReadOffset(Offset* offset, const char* desc) { + uint32_t value; + CHECK_RESULT(ReadU32Leb128(&value, desc)); + *offset = value; + return Result::Ok; +} + +Result BinaryReader::ReadAlignment(Address* alignment_log2, const char* desc) { + uint32_t value; + CHECK_RESULT(ReadU32Leb128(&value, desc)); + if (value >= 128 || + (value >= 32 && !options_.features.multi_memory_enabled())) { + PrintError("invalid %s: %u", desc, value); + return Result::Error; + } + *alignment_log2 = value; + return Result::Ok; +} + +Result BinaryReader::ReadMemidx(Index* memidx, const char* desc) { + CHECK_RESULT(ReadIndex(memidx, desc)); + ERROR_UNLESS(*memidx < memories.size(), "memory index %u out of range", + *memidx); + return Result::Ok; +} + +Result BinaryReader::ReadMemLocation(Address* alignment_log2, + Index* memidx, + Address* offset, + const char* desc_align, + const char* desc_memidx, + const char* desc_offset, + uint8_t* lane_val) { + CHECK_RESULT(ReadAlignment(alignment_log2, desc_align)); + *memidx = 0; + if (*alignment_log2 >> 6) { + ERROR_IF(!options_.features.multi_memory_enabled(), + "multi_memory not allowed"); + *alignment_log2 = *alignment_log2 & ((1 << 6) - 1); + CHECK_RESULT(ReadMemidx(memidx, desc_memidx)); + } + CHECK_RESULT(ReadAddress(offset, 0, desc_offset)); + + if (lane_val) { + CHECK_RESULT(ReadU8(lane_val, "Lane idx")); + } + + return Result::Ok; +} + +Result BinaryReader::CallbackMemLocation(const Address* alignment_log2, + const Index* memidx, + const Address* offset, + const uint8_t* lane_val) { + if (lane_val) { + if (*memidx) { + CALLBACK(OnOpcodeUint32Uint32Uint32Uint32, *alignment_log2, *memidx, + *offset, *lane_val); + } else { + CALLBACK(OnOpcodeUint32Uint32Uint32, *alignment_log2, *offset, *lane_val); + } + } else { + if (*memidx) { + CALLBACK(OnOpcodeUint32Uint32Uint32, *alignment_log2, *memidx, *offset); + } else { + CALLBACK(OnOpcodeUint32Uint32, *alignment_log2, *offset); + } + } + + return Result::Ok; +} + +Result BinaryReader::ReadCount(Index* count, const char* desc) { + CHECK_RESULT(ReadIndex(count, desc)); + + // This check assumes that each item follows in this section, and takes at + // least 1 byte. It's possible that this check passes but reading fails + // later. It is still useful to check here, though, because it early-outs + // when an erroneous large count is used, before allocating memory for it. + size_t section_remaining = read_end_ - state_.offset; + if (*count > section_remaining) { + PrintError("invalid %s %" PRIindex ", only %" PRIzd + " bytes left in section", + desc, *count, section_remaining); + return Result::Error; + } + return Result::Ok; +} + +Result BinaryReader::ReadField(TypeMut* out_value) { + // TODO: Reuse for global header too? + Type field_type; + CHECK_RESULT(ReadType(&field_type, "field type")); + ERROR_UNLESS(IsConcreteType(field_type), + "expected valid field type (got " PRItypecode ")", + WABT_PRINTF_TYPE_CODE(field_type)); + + uint8_t mutable_ = 0; + CHECK_RESULT(ReadU8(&mutable_, "field mutability")); + ERROR_UNLESS(mutable_ <= 1, "field mutability must be 0 or 1"); + out_value->type = field_type; + out_value->mutable_ = mutable_; + return Result::Ok; +} + +bool BinaryReader::IsConcreteType(Type type) { + switch (type) { + case Type::I32: + case Type::I64: + case Type::F32: + case Type::F64: + return true; + + case Type::V128: + return options_.features.simd_enabled(); + + case Type::FuncRef: + case Type::ExternRef: + return options_.features.reference_types_enabled(); + + case Type::Reference: + return options_.features.function_references_enabled(); + + default: + return false; + } +} + +bool BinaryReader::IsBlockType(Type type) { + if (IsConcreteType(type) || type == Type::Void) { + return true; + } + + if (!(options_.features.multi_value_enabled() && type.IsIndex())) { + return false; + } + + return true; +} + +Index BinaryReader::NumTotalFuncs() { + return num_func_imports_ + num_function_signatures_; +} + +Result BinaryReader::ReadInitExpr(Index index) { + // Read instructions until END opcode is reached. + Opcode final_opcode(Opcode::Invalid); + CHECK_RESULT( + ReadInstructions(/*stop_on_end=*/true, read_end_, &final_opcode)); + ERROR_UNLESS(state_.offset <= read_end_, + "init expression longer than given size"); + ERROR_UNLESS(final_opcode == Opcode::End, + "init expression must end with END opcode"); + return Result::Ok; +} + +Result BinaryReader::ReadTable(Type* out_elem_type, Limits* out_elem_limits) { + CHECK_RESULT(ReadRefType(out_elem_type, "table elem type")); + + uint8_t flags; + uint32_t initial; + uint32_t max = 0; + CHECK_RESULT(ReadU8(&flags, "table flags")); + bool has_max = flags & WABT_BINARY_LIMITS_HAS_MAX_FLAG; + bool is_shared = flags & WABT_BINARY_LIMITS_IS_SHARED_FLAG; + bool is_64 = flags & WABT_BINARY_LIMITS_IS_64_FLAG; + const uint8_t unknown_flags = flags & ~WABT_BINARY_LIMITS_ALL_FLAGS; + ERROR_IF(is_shared, "tables may not be shared"); + ERROR_IF(is_64, "tables may not be 64-bit"); + ERROR_UNLESS(unknown_flags == 0, "malformed table limits flag: %d", flags); + CHECK_RESULT(ReadU32Leb128(&initial, "table initial elem count")); + if (has_max) { + CHECK_RESULT(ReadU32Leb128(&max, "table max elem count")); + } + + out_elem_limits->has_max = has_max; + out_elem_limits->initial = initial; + out_elem_limits->max = max; + return Result::Ok; +} + +Result BinaryReader::ReadMemory(Limits* out_page_limits) { + uint8_t flags; + uint64_t initial; + uint64_t max = 0; + CHECK_RESULT(ReadU8(&flags, "memory flags")); + bool has_max = flags & WABT_BINARY_LIMITS_HAS_MAX_FLAG; + bool is_shared = flags & WABT_BINARY_LIMITS_IS_SHARED_FLAG; + bool is_64 = flags & WABT_BINARY_LIMITS_IS_64_FLAG; + const uint8_t unknown_flags = flags & ~WABT_BINARY_LIMITS_ALL_FLAGS; + ERROR_UNLESS(unknown_flags == 0, "malformed memory limits flag: %d", flags); + ERROR_IF(is_shared && !options_.features.threads_enabled(), + "memory may not be shared: threads not allowed"); + ERROR_IF(is_64 && !options_.features.memory64_enabled(), + "memory64 not allowed"); + if (is_64) { + CHECK_RESULT(ReadU64Leb128(&initial, "memory initial page count")); + if (has_max) { + CHECK_RESULT(ReadU64Leb128(&max, "memory max page count")); + } + } else { + uint32_t initial32; + CHECK_RESULT(ReadU32Leb128(&initial32, "memory initial page count")); + initial = initial32; + if (has_max) { + uint32_t max32; + CHECK_RESULT(ReadU32Leb128(&max32, "memory max page count")); + max = max32; + } + } + + out_page_limits->has_max = has_max; + out_page_limits->is_shared = is_shared; + out_page_limits->is_64 = is_64; + out_page_limits->initial = initial; + out_page_limits->max = max; + + // Have to keep a copy of these, to know how to interpret load/stores. + memories.push_back(*out_page_limits); + return Result::Ok; +} + +Result BinaryReader::ReadGlobalHeader(Type* out_type, bool* out_mutable) { + Type global_type = Type::Void; + uint8_t mutable_ = 0; + CHECK_RESULT(ReadType(&global_type, "global type")); + ERROR_UNLESS(IsConcreteType(global_type), "invalid global type: %#x", + static_cast<int>(global_type)); + + CHECK_RESULT(ReadU8(&mutable_, "global mutability")); + ERROR_UNLESS(mutable_ <= 1, "global mutability must be 0 or 1"); + + *out_type = global_type; + *out_mutable = mutable_; + return Result::Ok; +} + +Result BinaryReader::ReadAddress(Address* out_value, + Index memory, + const char* desc) { + ERROR_UNLESS(memory < memories.size(), + "load/store memory %u out of range %zu", memory, + memories.size()); + if (memories[memory].is_64) { + return ReadU64Leb128(out_value, desc); + } else { + uint32_t val; + Result res = ReadU32Leb128(&val, desc); + *out_value = val; + return res; + } +} + +Result BinaryReader::ReadFunctionBody(Offset end_offset) { + Opcode final_opcode(Opcode::Invalid); + CHECK_RESULT( + ReadInstructions(/*stop_on_end=*/false, end_offset, &final_opcode)); + ERROR_UNLESS(state_.offset == end_offset, + "function body longer than given size"); + ERROR_UNLESS(final_opcode == Opcode::End, + "function body must end with END opcode"); + return Result::Ok; +} + +Result BinaryReader::ReadInstructions(bool stop_on_end, + Offset end_offset, + Opcode* final_opcode) { + while (state_.offset < end_offset) { + Opcode opcode; + CHECK_RESULT(ReadOpcode(&opcode, "opcode")); + CALLBACK(OnOpcode, opcode); + ERROR_UNLESS_OPCODE_ENABLED(opcode); + if (final_opcode) { + *final_opcode = opcode; + } + + switch (opcode) { + case Opcode::Unreachable: + CALLBACK0(OnUnreachableExpr); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::Block: { + Type sig_type; + CHECK_RESULT(ReadType(&sig_type, "block signature type")); + ERROR_UNLESS(IsBlockType(sig_type), + "expected valid block signature type"); + CALLBACK(OnBlockExpr, sig_type); + CALLBACK(OnOpcodeBlockSig, sig_type); + break; + } + + case Opcode::Loop: { + Type sig_type; + CHECK_RESULT(ReadType(&sig_type, "loop signature type")); + ERROR_UNLESS(IsBlockType(sig_type), + "expected valid block signature type"); + CALLBACK(OnLoopExpr, sig_type); + CALLBACK(OnOpcodeBlockSig, sig_type); + break; + } + + case Opcode::If: { + Type sig_type; + CHECK_RESULT(ReadType(&sig_type, "if signature type")); + ERROR_UNLESS(IsBlockType(sig_type), + "expected valid block signature type"); + CALLBACK(OnIfExpr, sig_type); + CALLBACK(OnOpcodeBlockSig, sig_type); + break; + } + + case Opcode::Else: + CALLBACK0(OnElseExpr); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::SelectT: { + Index num_results; + CHECK_RESULT(ReadCount(&num_results, "num result types")); + + result_types_.resize(num_results); + for (Index i = 0; i < num_results; ++i) { + Type result_type; + CHECK_RESULT(ReadType(&result_type, "select result type")); + ERROR_UNLESS(IsConcreteType(result_type), + "expected valid select result type (got " PRItypecode + ")", + WABT_PRINTF_TYPE_CODE(result_type)); + result_types_[i] = result_type; + } + + if (num_results) { + CALLBACK(OnSelectExpr, num_results, result_types_.data()); + CALLBACK(OnOpcodeType, result_types_[0]); + } else { + CALLBACK(OnSelectExpr, 0, NULL); + CALLBACK0(OnOpcodeBare); + } + break; + } + + case Opcode::Select: + CALLBACK(OnSelectExpr, 0, nullptr); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::Br: { + Index depth; + CHECK_RESULT(ReadIndex(&depth, "br depth")); + CALLBACK(OnBrExpr, depth); + CALLBACK(OnOpcodeIndex, depth); + break; + } + + case Opcode::BrIf: { + Index depth; + CHECK_RESULT(ReadIndex(&depth, "br_if depth")); + CALLBACK(OnBrIfExpr, depth); + CALLBACK(OnOpcodeIndex, depth); + break; + } + + case Opcode::BrTable: { + Index num_targets; + CHECK_RESULT(ReadCount(&num_targets, "br_table target count")); + target_depths_.resize(num_targets); + + for (Index i = 0; i < num_targets; ++i) { + Index target_depth; + CHECK_RESULT(ReadIndex(&target_depth, "br_table target depth")); + target_depths_[i] = target_depth; + } + + Index default_target_depth; + CHECK_RESULT( + ReadIndex(&default_target_depth, "br_table default target depth")); + + Index* target_depths = num_targets ? target_depths_.data() : nullptr; + + CALLBACK(OnBrTableExpr, num_targets, target_depths, + default_target_depth); + break; + } + + case Opcode::Return: + CALLBACK0(OnReturnExpr); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::Nop: + CALLBACK0(OnNopExpr); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::Drop: + CALLBACK0(OnDropExpr); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::End: + CALLBACK0(OnEndExpr); + if (stop_on_end) { + return Result::Ok; + } + break; + + case Opcode::I32Const: { + uint32_t value; + CHECK_RESULT(ReadS32Leb128(&value, "i32.const value")); + CALLBACK(OnI32ConstExpr, value); + CALLBACK(OnOpcodeUint32, value); + break; + } + + case Opcode::I64Const: { + uint64_t value; + CHECK_RESULT(ReadS64Leb128(&value, "i64.const value")); + CALLBACK(OnI64ConstExpr, value); + CALLBACK(OnOpcodeUint64, value); + break; + } + + case Opcode::F32Const: { + uint32_t value_bits = 0; + CHECK_RESULT(ReadF32(&value_bits, "f32.const value")); + CALLBACK(OnF32ConstExpr, value_bits); + CALLBACK(OnOpcodeF32, value_bits); + break; + } + + case Opcode::F64Const: { + uint64_t value_bits = 0; + CHECK_RESULT(ReadF64(&value_bits, "f64.const value")); + CALLBACK(OnF64ConstExpr, value_bits); + CALLBACK(OnOpcodeF64, value_bits); + break; + } + + case Opcode::V128Const: { + v128 value_bits; + ZeroMemory(value_bits); + CHECK_RESULT(ReadV128(&value_bits, "v128.const value")); + CALLBACK(OnV128ConstExpr, value_bits); + CALLBACK(OnOpcodeV128, value_bits); + break; + } + + case Opcode::GlobalGet: { + Index global_index; + CHECK_RESULT(ReadIndex(&global_index, "global.get global index")); + CALLBACK(OnGlobalGetExpr, global_index); + CALLBACK(OnOpcodeIndex, global_index); + break; + } + + case Opcode::LocalGet: { + Index local_index; + CHECK_RESULT(ReadIndex(&local_index, "local.get local index")); + CALLBACK(OnLocalGetExpr, local_index); + CALLBACK(OnOpcodeIndex, local_index); + break; + } + + case Opcode::GlobalSet: { + Index global_index; + CHECK_RESULT(ReadIndex(&global_index, "global.set global index")); + CALLBACK(OnGlobalSetExpr, global_index); + CALLBACK(OnOpcodeIndex, global_index); + break; + } + + case Opcode::LocalSet: { + Index local_index; + CHECK_RESULT(ReadIndex(&local_index, "local.set local index")); + CALLBACK(OnLocalSetExpr, local_index); + CALLBACK(OnOpcodeIndex, local_index); + break; + } + + case Opcode::Call: { + Index func_index; + CHECK_RESULT(ReadIndex(&func_index, "call function index")); + CALLBACK(OnCallExpr, func_index); + CALLBACK(OnOpcodeIndex, func_index); + break; + } + + case Opcode::CallIndirect: { + Index sig_index; + CHECK_RESULT(ReadIndex(&sig_index, "call_indirect signature index")); + Index table_index = 0; + if (options_.features.reference_types_enabled()) { + CHECK_RESULT(ReadIndex(&table_index, "call_indirect table index")); + } else { + uint8_t reserved; + CHECK_RESULT(ReadU8(&reserved, "call_indirect reserved")); + ERROR_UNLESS(reserved == 0, "call_indirect reserved value must be 0"); + } + CALLBACK(OnCallIndirectExpr, sig_index, table_index); + CALLBACK(OnOpcodeUint32Uint32, sig_index, table_index); + break; + } + + case Opcode::ReturnCall: { + Index func_index; + CHECK_RESULT(ReadIndex(&func_index, "return_call")); + CALLBACK(OnReturnCallExpr, func_index); + CALLBACK(OnOpcodeIndex, func_index); + break; + } + + case Opcode::ReturnCallIndirect: { + Index sig_index; + CHECK_RESULT(ReadIndex(&sig_index, "return_call_indirect")); + Index table_index = 0; + if (options_.features.reference_types_enabled()) { + CHECK_RESULT( + ReadIndex(&table_index, "return_call_indirect table index")); + } else { + uint8_t reserved; + CHECK_RESULT(ReadU8(&reserved, "return_call_indirect reserved")); + ERROR_UNLESS(reserved == 0, + "return_call_indirect reserved value must be 0"); + } + CALLBACK(OnReturnCallIndirectExpr, sig_index, table_index); + CALLBACK(OnOpcodeUint32Uint32, sig_index, table_index); + break; + } + + case Opcode::LocalTee: { + Index local_index; + CHECK_RESULT(ReadIndex(&local_index, "local.tee local index")); + CALLBACK(OnLocalTeeExpr, local_index); + CALLBACK(OnOpcodeIndex, local_index); + break; + } + + case Opcode::I32Load8S: + case Opcode::I32Load8U: + case Opcode::I32Load16S: + case Opcode::I32Load16U: + case Opcode::I64Load8S: + case Opcode::I64Load8U: + case Opcode::I64Load16S: + case Opcode::I64Load16U: + case Opcode::I64Load32S: + case Opcode::I64Load32U: + case Opcode::I32Load: + case Opcode::I64Load: + case Opcode::F32Load: + case Opcode::F64Load: + case Opcode::V128Load: + case Opcode::V128Load8X8S: + case Opcode::V128Load8X8U: + case Opcode::V128Load16X4S: + case Opcode::V128Load16X4U: + case Opcode::V128Load32X2S: + case Opcode::V128Load32X2U: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "load alignment", "load memidx", + "load offset")); + CALLBACK(OnLoadExpr, opcode, memidx, alignment_log2, offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + + case Opcode::I32Store8: + case Opcode::I32Store16: + case Opcode::I64Store8: + case Opcode::I64Store16: + case Opcode::I64Store32: + case Opcode::I32Store: + case Opcode::I64Store: + case Opcode::F32Store: + case Opcode::F64Store: + case Opcode::V128Store: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "store alignment", "store memidx", + "store offset")); + CALLBACK(OnStoreExpr, opcode, memidx, alignment_log2, offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + + case Opcode::MemorySize: { + Index memidx = 0; + if (!options_.features.multi_memory_enabled()) { + uint8_t reserved; + CHECK_RESULT(ReadU8(&reserved, "memory.size reserved")); + ERROR_UNLESS(reserved == 0, "memory.size reserved value must be 0"); + } else { + CHECK_RESULT(ReadMemidx(&memidx, "memory.size memidx")); + } + CALLBACK(OnMemorySizeExpr, memidx); + CALLBACK(OnOpcodeUint32, memidx); + break; + } + + case Opcode::MemoryGrow: { + Index memidx = 0; + if (!options_.features.multi_memory_enabled()) { + uint8_t reserved; + CHECK_RESULT(ReadU8(&reserved, "memory.grow reserved")); + ERROR_UNLESS(reserved == 0, "memory.grow reserved value must be 0"); + } else { + CHECK_RESULT(ReadMemidx(&memidx, "memory.grow memidx")); + } + CALLBACK(OnMemoryGrowExpr, memidx); + CALLBACK(OnOpcodeUint32, memidx); + break; + } + + case Opcode::I32Add: + case Opcode::I32Sub: + case Opcode::I32Mul: + case Opcode::I32DivS: + case Opcode::I32DivU: + case Opcode::I32RemS: + case Opcode::I32RemU: + case Opcode::I32And: + case Opcode::I32Or: + case Opcode::I32Xor: + case Opcode::I32Shl: + case Opcode::I32ShrU: + case Opcode::I32ShrS: + case Opcode::I32Rotr: + case Opcode::I32Rotl: + case Opcode::I64Add: + case Opcode::I64Sub: + case Opcode::I64Mul: + case Opcode::I64DivS: + case Opcode::I64DivU: + case Opcode::I64RemS: + case Opcode::I64RemU: + case Opcode::I64And: + case Opcode::I64Or: + case Opcode::I64Xor: + case Opcode::I64Shl: + case Opcode::I64ShrU: + case Opcode::I64ShrS: + case Opcode::I64Rotr: + case Opcode::I64Rotl: + case Opcode::F32Add: + case Opcode::F32Sub: + case Opcode::F32Mul: + case Opcode::F32Div: + case Opcode::F32Min: + case Opcode::F32Max: + case Opcode::F32Copysign: + case Opcode::F64Add: + case Opcode::F64Sub: + case Opcode::F64Mul: + case Opcode::F64Div: + case Opcode::F64Min: + case Opcode::F64Max: + case Opcode::F64Copysign: + case Opcode::I8X16Add: + case Opcode::I16X8Add: + case Opcode::I32X4Add: + case Opcode::I64X2Add: + case Opcode::I8X16Sub: + case Opcode::I16X8Sub: + case Opcode::I32X4Sub: + case Opcode::I64X2Sub: + case Opcode::I16X8Mul: + case Opcode::I32X4Mul: + case Opcode::I64X2Mul: + case Opcode::I8X16AddSatS: + case Opcode::I8X16AddSatU: + case Opcode::I16X8AddSatS: + case Opcode::I16X8AddSatU: + case Opcode::I8X16SubSatS: + case Opcode::I8X16SubSatU: + case Opcode::I16X8SubSatS: + case Opcode::I16X8SubSatU: + case Opcode::I8X16MinS: + case Opcode::I16X8MinS: + case Opcode::I32X4MinS: + case Opcode::I8X16MinU: + case Opcode::I16X8MinU: + case Opcode::I32X4MinU: + case Opcode::I8X16MaxS: + case Opcode::I16X8MaxS: + case Opcode::I32X4MaxS: + case Opcode::I8X16MaxU: + case Opcode::I16X8MaxU: + case Opcode::I32X4MaxU: + case Opcode::I8X16Shl: + case Opcode::I16X8Shl: + case Opcode::I32X4Shl: + case Opcode::I64X2Shl: + case Opcode::I8X16ShrS: + case Opcode::I8X16ShrU: + case Opcode::I16X8ShrS: + case Opcode::I16X8ShrU: + case Opcode::I32X4ShrS: + case Opcode::I32X4ShrU: + case Opcode::I64X2ShrS: + case Opcode::I64X2ShrU: + case Opcode::V128And: + case Opcode::V128Or: + case Opcode::V128Xor: + case Opcode::F32X4Min: + case Opcode::F32X4PMin: + case Opcode::F64X2Min: + case Opcode::F64X2PMin: + case Opcode::F32X4Max: + case Opcode::F32X4PMax: + case Opcode::F64X2Max: + case Opcode::F64X2PMax: + case Opcode::F32X4Add: + case Opcode::F64X2Add: + case Opcode::F32X4Sub: + case Opcode::F64X2Sub: + case Opcode::F32X4Div: + case Opcode::F64X2Div: + case Opcode::F32X4Mul: + case Opcode::F64X2Mul: + case Opcode::I8X16Swizzle: + case Opcode::I8X16NarrowI16X8S: + case Opcode::I8X16NarrowI16X8U: + case Opcode::I16X8NarrowI32X4S: + case Opcode::I16X8NarrowI32X4U: + case Opcode::V128Andnot: + case Opcode::I8X16AvgrU: + case Opcode::I16X8AvgrU: + case Opcode::I16X8ExtmulLowI8X16S: + case Opcode::I16X8ExtmulHighI8X16S: + case Opcode::I16X8ExtmulLowI8X16U: + case Opcode::I16X8ExtmulHighI8X16U: + case Opcode::I32X4ExtmulLowI16X8S: + case Opcode::I32X4ExtmulHighI16X8S: + case Opcode::I32X4ExtmulLowI16X8U: + case Opcode::I32X4ExtmulHighI16X8U: + case Opcode::I64X2ExtmulLowI32X4S: + case Opcode::I64X2ExtmulHighI32X4S: + case Opcode::I64X2ExtmulLowI32X4U: + case Opcode::I64X2ExtmulHighI32X4U: + case Opcode::I16X8Q15mulrSatS: + case Opcode::I32X4DotI16X8S: + case Opcode::I8X16RelaxedSwizzle: + case Opcode::F32X4RelaxedMin: + case Opcode::F32X4RelaxedMax: + case Opcode::F64X2RelaxedMin: + case Opcode::F64X2RelaxedMax: + case Opcode::I16X8RelaxedQ15mulrS: + case Opcode::I16X8DotI8X16I7X16S: + CALLBACK(OnBinaryExpr, opcode); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::I32Eq: + case Opcode::I32Ne: + case Opcode::I32LtS: + case Opcode::I32LeS: + case Opcode::I32LtU: + case Opcode::I32LeU: + case Opcode::I32GtS: + case Opcode::I32GeS: + case Opcode::I32GtU: + case Opcode::I32GeU: + case Opcode::I64Eq: + case Opcode::I64Ne: + case Opcode::I64LtS: + case Opcode::I64LeS: + case Opcode::I64LtU: + case Opcode::I64LeU: + case Opcode::I64GtS: + case Opcode::I64GeS: + case Opcode::I64GtU: + case Opcode::I64GeU: + case Opcode::F32Eq: + case Opcode::F32Ne: + case Opcode::F32Lt: + case Opcode::F32Le: + case Opcode::F32Gt: + case Opcode::F32Ge: + case Opcode::F64Eq: + case Opcode::F64Ne: + case Opcode::F64Lt: + case Opcode::F64Le: + case Opcode::F64Gt: + case Opcode::F64Ge: + case Opcode::I8X16Eq: + case Opcode::I16X8Eq: + case Opcode::I32X4Eq: + case Opcode::I64X2Eq: + case Opcode::F32X4Eq: + case Opcode::F64X2Eq: + case Opcode::I8X16Ne: + case Opcode::I16X8Ne: + case Opcode::I32X4Ne: + case Opcode::I64X2Ne: + case Opcode::F32X4Ne: + case Opcode::F64X2Ne: + case Opcode::I8X16LtS: + case Opcode::I8X16LtU: + case Opcode::I16X8LtS: + case Opcode::I16X8LtU: + case Opcode::I32X4LtS: + case Opcode::I32X4LtU: + case Opcode::I64X2LtS: + case Opcode::F32X4Lt: + case Opcode::F64X2Lt: + case Opcode::I8X16LeS: + case Opcode::I8X16LeU: + case Opcode::I16X8LeS: + case Opcode::I16X8LeU: + case Opcode::I32X4LeS: + case Opcode::I32X4LeU: + case Opcode::I64X2LeS: + case Opcode::F32X4Le: + case Opcode::F64X2Le: + case Opcode::I8X16GtS: + case Opcode::I8X16GtU: + case Opcode::I16X8GtS: + case Opcode::I16X8GtU: + case Opcode::I32X4GtS: + case Opcode::I32X4GtU: + case Opcode::I64X2GtS: + case Opcode::F32X4Gt: + case Opcode::F64X2Gt: + case Opcode::I8X16GeS: + case Opcode::I8X16GeU: + case Opcode::I16X8GeS: + case Opcode::I16X8GeU: + case Opcode::I32X4GeS: + case Opcode::I32X4GeU: + case Opcode::I64X2GeS: + case Opcode::F32X4Ge: + case Opcode::F64X2Ge: + CALLBACK(OnCompareExpr, opcode); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::I32Clz: + case Opcode::I32Ctz: + case Opcode::I32Popcnt: + case Opcode::I64Clz: + case Opcode::I64Ctz: + case Opcode::I64Popcnt: + case Opcode::F32Abs: + case Opcode::F32Neg: + case Opcode::F32Ceil: + case Opcode::F32Floor: + case Opcode::F32Trunc: + case Opcode::F32Nearest: + case Opcode::F32Sqrt: + case Opcode::F64Abs: + case Opcode::F64Neg: + case Opcode::F64Ceil: + case Opcode::F64Floor: + case Opcode::F64Trunc: + case Opcode::F64Nearest: + case Opcode::F64Sqrt: + case Opcode::I8X16Splat: + case Opcode::I16X8Splat: + case Opcode::I32X4Splat: + case Opcode::I64X2Splat: + case Opcode::F32X4Splat: + case Opcode::F64X2Splat: + case Opcode::I8X16Neg: + case Opcode::I16X8Neg: + case Opcode::I32X4Neg: + case Opcode::I64X2Neg: + case Opcode::V128Not: + case Opcode::V128AnyTrue: + case Opcode::I8X16Bitmask: + case Opcode::I16X8Bitmask: + case Opcode::I32X4Bitmask: + case Opcode::I64X2Bitmask: + case Opcode::I8X16AllTrue: + case Opcode::I16X8AllTrue: + case Opcode::I32X4AllTrue: + case Opcode::I64X2AllTrue: + case Opcode::F32X4Ceil: + case Opcode::F64X2Ceil: + case Opcode::F32X4Floor: + case Opcode::F64X2Floor: + case Opcode::F32X4Trunc: + case Opcode::F64X2Trunc: + case Opcode::F32X4Nearest: + case Opcode::F64X2Nearest: + case Opcode::F32X4Neg: + case Opcode::F64X2Neg: + case Opcode::F32X4Abs: + case Opcode::F64X2Abs: + case Opcode::F32X4Sqrt: + case Opcode::F64X2Sqrt: + case Opcode::I16X8ExtendLowI8X16S: + case Opcode::I16X8ExtendHighI8X16S: + case Opcode::I16X8ExtendLowI8X16U: + case Opcode::I16X8ExtendHighI8X16U: + case Opcode::I32X4ExtendLowI16X8S: + case Opcode::I32X4ExtendHighI16X8S: + case Opcode::I32X4ExtendLowI16X8U: + case Opcode::I32X4ExtendHighI16X8U: + case Opcode::I64X2ExtendLowI32X4S: + case Opcode::I64X2ExtendHighI32X4S: + case Opcode::I64X2ExtendLowI32X4U: + case Opcode::I64X2ExtendHighI32X4U: + case Opcode::I8X16Abs: + case Opcode::I16X8Abs: + case Opcode::I32X4Abs: + case Opcode::I64X2Abs: + case Opcode::I8X16Popcnt: + case Opcode::I16X8ExtaddPairwiseI8X16S: + case Opcode::I16X8ExtaddPairwiseI8X16U: + case Opcode::I32X4ExtaddPairwiseI16X8S: + case Opcode::I32X4ExtaddPairwiseI16X8U: + case Opcode::I32X4RelaxedTruncF32X4S: + case Opcode::I32X4RelaxedTruncF32X4U: + case Opcode::I32X4RelaxedTruncF64X2SZero: + case Opcode::I32X4RelaxedTruncF64X2UZero: + CALLBACK(OnUnaryExpr, opcode); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::V128BitSelect: + case Opcode::F32X4RelaxedMadd: + case Opcode::F32X4RelaxedNmadd: + case Opcode::F64X2RelaxedMadd: + case Opcode::F64X2RelaxedNmadd: + case Opcode::I8X16RelaxedLaneSelect: + case Opcode::I16X8RelaxedLaneSelect: + case Opcode::I32X4RelaxedLaneSelect: + case Opcode::I64X2RelaxedLaneSelect: + case Opcode::I32X4DotI8X16I7X16AddS: + CALLBACK(OnTernaryExpr, opcode); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::I8X16ExtractLaneS: + case Opcode::I8X16ExtractLaneU: + case Opcode::I16X8ExtractLaneS: + case Opcode::I16X8ExtractLaneU: + case Opcode::I32X4ExtractLane: + case Opcode::I64X2ExtractLane: + case Opcode::F32X4ExtractLane: + case Opcode::F64X2ExtractLane: + case Opcode::I8X16ReplaceLane: + case Opcode::I16X8ReplaceLane: + case Opcode::I32X4ReplaceLane: + case Opcode::I64X2ReplaceLane: + case Opcode::F32X4ReplaceLane: + case Opcode::F64X2ReplaceLane: { + uint8_t lane_val; + CHECK_RESULT(ReadU8(&lane_val, "Lane idx")); + CALLBACK(OnSimdLaneOpExpr, opcode, lane_val); + CALLBACK(OnOpcodeUint64, lane_val); + break; + } + + case Opcode::I8X16Shuffle: { + v128 value; + CHECK_RESULT(ReadV128(&value, "Lane idx [16]")); + CALLBACK(OnSimdShuffleOpExpr, opcode, value); + CALLBACK(OnOpcodeV128, value); + break; + } + + case Opcode::V128Load8Splat: + case Opcode::V128Load16Splat: + case Opcode::V128Load32Splat: + case Opcode::V128Load64Splat: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "load alignment", "load memidx", + "load offset")); + CALLBACK(OnLoadSplatExpr, opcode, memidx, alignment_log2, offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + case Opcode::V128Load8Lane: + case Opcode::V128Load16Lane: + case Opcode::V128Load32Lane: + case Opcode::V128Load64Lane: { + Address alignment_log2; + Index memidx; + Address offset; + uint8_t lane_val; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "load alignment", "load memidx", + "load offset", &lane_val)); + CALLBACK(OnSimdLoadLaneExpr, opcode, memidx, alignment_log2, offset, + lane_val); + CHECK_RESULT( + CallbackMemLocation(&alignment_log2, &memidx, &offset, &lane_val)); + break; + } + case Opcode::V128Store8Lane: + case Opcode::V128Store16Lane: + case Opcode::V128Store32Lane: + case Opcode::V128Store64Lane: { + Address alignment_log2; + Index memidx; + Address offset; + uint8_t lane_val; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "store alignment", "store memidx", + "store offset", &lane_val)); + CALLBACK(OnSimdStoreLaneExpr, opcode, memidx, alignment_log2, offset, + lane_val); + CHECK_RESULT( + CallbackMemLocation(&alignment_log2, &memidx, &offset, &lane_val)); + break; + } + case Opcode::V128Load32Zero: + case Opcode::V128Load64Zero: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "load alignment", "load memidx", + "load offset")); + CALLBACK(OnLoadZeroExpr, opcode, memidx, alignment_log2, offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + case Opcode::I32TruncF32S: + case Opcode::I32TruncF64S: + case Opcode::I32TruncF32U: + case Opcode::I32TruncF64U: + case Opcode::I32WrapI64: + case Opcode::I64TruncF32S: + case Opcode::I64TruncF64S: + case Opcode::I64TruncF32U: + case Opcode::I64TruncF64U: + case Opcode::I64ExtendI32S: + case Opcode::I64ExtendI32U: + case Opcode::F32ConvertI32S: + case Opcode::F32ConvertI32U: + case Opcode::F32ConvertI64S: + case Opcode::F32ConvertI64U: + case Opcode::F32DemoteF64: + case Opcode::F32ReinterpretI32: + case Opcode::F64ConvertI32S: + case Opcode::F64ConvertI32U: + case Opcode::F64ConvertI64S: + case Opcode::F64ConvertI64U: + case Opcode::F64PromoteF32: + case Opcode::F64ReinterpretI64: + case Opcode::I32ReinterpretF32: + case Opcode::I64ReinterpretF64: + case Opcode::I32Eqz: + case Opcode::I64Eqz: + case Opcode::F32X4ConvertI32X4S: + case Opcode::F32X4ConvertI32X4U: + case Opcode::I32X4TruncSatF32X4S: + case Opcode::I32X4TruncSatF32X4U: + case Opcode::F32X4DemoteF64X2Zero: + case Opcode::F64X2PromoteLowF32X4: + case Opcode::I32X4TruncSatF64X2SZero: + case Opcode::I32X4TruncSatF64X2UZero: + case Opcode::F64X2ConvertLowI32X4S: + case Opcode::F64X2ConvertLowI32X4U: + CALLBACK(OnConvertExpr, opcode); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::Try: { + Type sig_type; + CHECK_RESULT(ReadType(&sig_type, "try signature type")); + ERROR_UNLESS(IsBlockType(sig_type), + "expected valid block signature type"); + CALLBACK(OnTryExpr, sig_type); + CALLBACK(OnOpcodeBlockSig, sig_type); + break; + } + + case Opcode::Catch: { + Index index; + CHECK_RESULT(ReadIndex(&index, "tag index")); + CALLBACK(OnCatchExpr, index); + CALLBACK(OnOpcodeIndex, index); + break; + } + + case Opcode::CatchAll: { + CALLBACK(OnCatchAllExpr); + CALLBACK(OnOpcodeBare); + break; + } + + case Opcode::Delegate: { + Index index; + CHECK_RESULT(ReadIndex(&index, "depth")); + CALLBACK(OnDelegateExpr, index); + CALLBACK(OnOpcodeIndex, index); + break; + } + + case Opcode::Rethrow: { + Index depth; + CHECK_RESULT(ReadIndex(&depth, "catch depth")); + CALLBACK(OnRethrowExpr, depth); + CALLBACK(OnOpcodeIndex, depth); + break; + } + + case Opcode::Throw: { + Index index; + CHECK_RESULT(ReadIndex(&index, "tag index")); + CALLBACK(OnThrowExpr, index); + CALLBACK(OnOpcodeIndex, index); + break; + } + + case Opcode::I32Extend8S: + case Opcode::I32Extend16S: + case Opcode::I64Extend8S: + case Opcode::I64Extend16S: + case Opcode::I64Extend32S: + CALLBACK(OnUnaryExpr, opcode); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::I32TruncSatF32S: + case Opcode::I32TruncSatF32U: + case Opcode::I32TruncSatF64S: + case Opcode::I32TruncSatF64U: + case Opcode::I64TruncSatF32S: + case Opcode::I64TruncSatF32U: + case Opcode::I64TruncSatF64S: + case Opcode::I64TruncSatF64U: + CALLBACK(OnConvertExpr, opcode); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::MemoryAtomicNotify: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "notify alignment", "notify memidx", + "notify offset")); + CALLBACK(OnAtomicNotifyExpr, opcode, memidx, alignment_log2, offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + + case Opcode::MemoryAtomicWait32: + case Opcode::MemoryAtomicWait64: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "wait alignment", "wait memidx", + "wait offset")); + CALLBACK(OnAtomicWaitExpr, opcode, memidx, alignment_log2, offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + + case Opcode::AtomicFence: { + uint8_t consistency_model; + CHECK_RESULT(ReadU8(&consistency_model, "consistency model")); + ERROR_UNLESS(consistency_model == 0, + "atomic.fence consistency model must be 0"); + CALLBACK(OnAtomicFenceExpr, consistency_model); + CALLBACK(OnOpcodeUint32, consistency_model); + break; + } + + case Opcode::I32AtomicLoad8U: + case Opcode::I32AtomicLoad16U: + case Opcode::I64AtomicLoad8U: + case Opcode::I64AtomicLoad16U: + case Opcode::I64AtomicLoad32U: + case Opcode::I32AtomicLoad: + case Opcode::I64AtomicLoad: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "load alignment", "load memidx", + "load offset")); + CALLBACK(OnAtomicLoadExpr, opcode, memidx, alignment_log2, offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + + case Opcode::I32AtomicStore8: + case Opcode::I32AtomicStore16: + case Opcode::I64AtomicStore8: + case Opcode::I64AtomicStore16: + case Opcode::I64AtomicStore32: + case Opcode::I32AtomicStore: + case Opcode::I64AtomicStore: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "store alignment", "store memidx", + "store offset")); + CALLBACK(OnAtomicStoreExpr, opcode, memidx, alignment_log2, offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + + case Opcode::I32AtomicRmwAdd: + case Opcode::I64AtomicRmwAdd: + case Opcode::I32AtomicRmw8AddU: + case Opcode::I32AtomicRmw16AddU: + case Opcode::I64AtomicRmw8AddU: + case Opcode::I64AtomicRmw16AddU: + case Opcode::I64AtomicRmw32AddU: + case Opcode::I32AtomicRmwSub: + case Opcode::I64AtomicRmwSub: + case Opcode::I32AtomicRmw8SubU: + case Opcode::I32AtomicRmw16SubU: + case Opcode::I64AtomicRmw8SubU: + case Opcode::I64AtomicRmw16SubU: + case Opcode::I64AtomicRmw32SubU: + case Opcode::I32AtomicRmwAnd: + case Opcode::I64AtomicRmwAnd: + case Opcode::I32AtomicRmw8AndU: + case Opcode::I32AtomicRmw16AndU: + case Opcode::I64AtomicRmw8AndU: + case Opcode::I64AtomicRmw16AndU: + case Opcode::I64AtomicRmw32AndU: + case Opcode::I32AtomicRmwOr: + case Opcode::I64AtomicRmwOr: + case Opcode::I32AtomicRmw8OrU: + case Opcode::I32AtomicRmw16OrU: + case Opcode::I64AtomicRmw8OrU: + case Opcode::I64AtomicRmw16OrU: + case Opcode::I64AtomicRmw32OrU: + case Opcode::I32AtomicRmwXor: + case Opcode::I64AtomicRmwXor: + case Opcode::I32AtomicRmw8XorU: + case Opcode::I32AtomicRmw16XorU: + case Opcode::I64AtomicRmw8XorU: + case Opcode::I64AtomicRmw16XorU: + case Opcode::I64AtomicRmw32XorU: + case Opcode::I32AtomicRmwXchg: + case Opcode::I64AtomicRmwXchg: + case Opcode::I32AtomicRmw8XchgU: + case Opcode::I32AtomicRmw16XchgU: + case Opcode::I64AtomicRmw8XchgU: + case Opcode::I64AtomicRmw16XchgU: + case Opcode::I64AtomicRmw32XchgU: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "memory alignment", "memory memidx", + "memory offset")); + CALLBACK(OnAtomicRmwExpr, opcode, memidx, alignment_log2, offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + + case Opcode::I32AtomicRmwCmpxchg: + case Opcode::I64AtomicRmwCmpxchg: + case Opcode::I32AtomicRmw8CmpxchgU: + case Opcode::I32AtomicRmw16CmpxchgU: + case Opcode::I64AtomicRmw8CmpxchgU: + case Opcode::I64AtomicRmw16CmpxchgU: + case Opcode::I64AtomicRmw32CmpxchgU: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "memory alignment", "memory memidx", + "memory offset")); + CALLBACK(OnAtomicRmwCmpxchgExpr, opcode, memidx, alignment_log2, + offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + + case Opcode::TableInit: { + Index segment; + CHECK_RESULT(ReadIndex(&segment, "elem segment index")); + Index table_index; + CHECK_RESULT(ReadIndex(&table_index, "reserved table index")); + CALLBACK(OnTableInitExpr, segment, table_index); + CALLBACK(OnOpcodeUint32Uint32, segment, table_index); + break; + } + + case Opcode::MemoryInit: { + Index segment; + ERROR_IF(data_count_ == kInvalidIndex, + "memory.init requires data count section"); + CHECK_RESULT(ReadIndex(&segment, "elem segment index")); + Index memidx = 0; + if (!options_.features.multi_memory_enabled()) { + uint8_t reserved; + CHECK_RESULT(ReadU8(&reserved, "reserved memory index")); + ERROR_UNLESS(reserved == 0, "reserved value must be 0"); + } else { + CHECK_RESULT(ReadMemidx(&memidx, "memory.init memidx")); + } + CALLBACK(OnMemoryInitExpr, segment, memidx); + CALLBACK(OnOpcodeUint32Uint32, segment, memidx); + break; + } + + case Opcode::DataDrop: + ERROR_IF(data_count_ == kInvalidIndex, + "data.drop requires data count section"); + [[fallthrough]]; + case Opcode::ElemDrop: { + Index segment; + CHECK_RESULT(ReadIndex(&segment, "segment index")); + if (opcode == Opcode::DataDrop) { + CALLBACK(OnDataDropExpr, segment); + } else { + CALLBACK(OnElemDropExpr, segment); + } + CALLBACK(OnOpcodeUint32, segment); + break; + } + + case Opcode::MemoryFill: { + Index memidx = 0; + if (!options_.features.multi_memory_enabled()) { + uint8_t reserved; + CHECK_RESULT(ReadU8(&reserved, "memory.fill reserved")); + ERROR_UNLESS(reserved == 0, "memory.fill reserved value must be 0"); + } else { + CHECK_RESULT(ReadMemidx(&memidx, "memory.fill memidx")); + } + CALLBACK(OnMemoryFillExpr, memidx); + CALLBACK(OnOpcodeUint32, memidx); + break; + } + + case Opcode::MemoryCopy: { + Index srcmemidx = 0; + Index destmemidx = 0; + if (!options_.features.multi_memory_enabled()) { + uint8_t reserved; + CHECK_RESULT(ReadU8(&reserved, "reserved memory index")); + ERROR_UNLESS(reserved == 0, "reserved value must be 0"); + CHECK_RESULT(ReadU8(&reserved, "reserved memory index")); + ERROR_UNLESS(reserved == 0, "reserved value must be 0"); + } else { + CHECK_RESULT(ReadMemidx(&srcmemidx, "memory.copy srcmemidx")); + CHECK_RESULT(ReadMemidx(&destmemidx, "memory.copy destmemindex")); + } + CALLBACK(OnMemoryCopyExpr, srcmemidx, destmemidx); + CALLBACK(OnOpcodeUint32Uint32, srcmemidx, destmemidx); + break; + } + + case Opcode::TableCopy: { + Index table_dst; + Index table_src; + CHECK_RESULT(ReadIndex(&table_dst, "reserved table index")); + CHECK_RESULT(ReadIndex(&table_src, "table src")); + CALLBACK(OnTableCopyExpr, table_dst, table_src); + CALLBACK(OnOpcodeUint32Uint32, table_dst, table_src); + break; + } + + case Opcode::TableGet: { + Index table; + CHECK_RESULT(ReadIndex(&table, "table index")); + CALLBACK(OnTableGetExpr, table); + CALLBACK(OnOpcodeUint32, table); + break; + } + + case Opcode::TableSet: { + Index table; + CHECK_RESULT(ReadIndex(&table, "table index")); + CALLBACK(OnTableSetExpr, table); + CALLBACK(OnOpcodeUint32, table); + break; + } + + case Opcode::TableGrow: { + Index table; + CHECK_RESULT(ReadIndex(&table, "table index")); + CALLBACK(OnTableGrowExpr, table); + CALLBACK(OnOpcodeUint32, table); + break; + } + + case Opcode::TableSize: { + Index table; + CHECK_RESULT(ReadIndex(&table, "table index")); + CALLBACK(OnTableSizeExpr, table); + CALLBACK(OnOpcodeUint32, table); + break; + } + + case Opcode::TableFill: { + Index table; + CHECK_RESULT(ReadIndex(&table, "table index")); + CALLBACK(OnTableFillExpr, table); + CALLBACK(OnOpcodeUint32, table); + break; + } + + case Opcode::RefFunc: { + Index func; + CHECK_RESULT(ReadIndex(&func, "func index")); + CALLBACK(OnRefFuncExpr, func); + CALLBACK(OnOpcodeUint32, func); + break; + } + + case Opcode::RefNull: { + Type type; + CHECK_RESULT(ReadRefType(&type, "ref.null type")); + CALLBACK(OnRefNullExpr, type); + CALLBACK(OnOpcodeType, type); + break; + } + + case Opcode::RefIsNull: + CALLBACK(OnRefIsNullExpr); + CALLBACK(OnOpcodeBare); + break; + + case Opcode::CallRef: + CALLBACK(OnCallRefExpr); + CALLBACK(OnOpcodeBare); + break; + + default: + return ReportUnexpectedOpcode(opcode); + } + } + return Result::Ok; +} + +Result BinaryReader::ReadNameSection(Offset section_size) { + CALLBACK(BeginNamesSection, section_size); + Index i = 0; + uint32_t previous_subsection_type = 0; + while (state_.offset < read_end_) { + uint32_t name_type; + Offset subsection_size; + CHECK_RESULT(ReadU32Leb128(&name_type, "name type")); + if (i != 0) { + ERROR_UNLESS(name_type != previous_subsection_type, + "duplicate sub-section"); + ERROR_UNLESS(name_type >= previous_subsection_type, + "out-of-order sub-section"); + } + previous_subsection_type = name_type; + CHECK_RESULT(ReadOffset(&subsection_size, "subsection size")); + size_t subsection_end = state_.offset + subsection_size; + ERROR_UNLESS(subsection_end <= read_end_, + "invalid sub-section size: extends past end"); + ReadEndRestoreGuard guard(this); + read_end_ = subsection_end; + + NameSectionSubsection type = static_cast<NameSectionSubsection>(name_type); + if (type <= NameSectionSubsection::Last) { + CALLBACK(OnNameSubsection, i, type, subsection_size); + } + + switch (type) { + case NameSectionSubsection::Module: + CALLBACK(OnModuleNameSubsection, i, name_type, subsection_size); + if (subsection_size) { + std::string_view name; + CHECK_RESULT(ReadStr(&name, "module name")); + CALLBACK(OnModuleName, name); + } + break; + case NameSectionSubsection::Function: + CALLBACK(OnFunctionNameSubsection, i, name_type, subsection_size); + if (subsection_size) { + Index num_names; + CHECK_RESULT(ReadCount(&num_names, "name count")); + CALLBACK(OnFunctionNamesCount, num_names); + Index last_function_index = kInvalidIndex; + + for (Index j = 0; j < num_names; ++j) { + Index function_index; + std::string_view function_name; + + CHECK_RESULT(ReadIndex(&function_index, "function index")); + ERROR_UNLESS(function_index != last_function_index, + "duplicate function name: %u", function_index); + ERROR_UNLESS(last_function_index == kInvalidIndex || + function_index > last_function_index, + "function index out of order: %u", function_index); + last_function_index = function_index; + ERROR_UNLESS(function_index < NumTotalFuncs(), + "invalid function index: %" PRIindex, function_index); + CHECK_RESULT(ReadStr(&function_name, "function name")); + CALLBACK(OnFunctionName, function_index, function_name); + } + } + break; + case NameSectionSubsection::Local: + CALLBACK(OnLocalNameSubsection, i, name_type, subsection_size); + if (subsection_size) { + Index num_funcs; + CHECK_RESULT(ReadCount(&num_funcs, "function count")); + CALLBACK(OnLocalNameFunctionCount, num_funcs); + Index last_function_index = kInvalidIndex; + for (Index j = 0; j < num_funcs; ++j) { + Index function_index; + CHECK_RESULT(ReadIndex(&function_index, "function index")); + ERROR_UNLESS(function_index < NumTotalFuncs(), + "invalid function index: %u", function_index); + ERROR_UNLESS(last_function_index == kInvalidIndex || + function_index > last_function_index, + "locals function index out of order: %u", + function_index); + last_function_index = function_index; + Index num_locals; + CHECK_RESULT(ReadCount(&num_locals, "local count")); + CALLBACK(OnLocalNameLocalCount, function_index, num_locals); + Index last_local_index = kInvalidIndex; + for (Index k = 0; k < num_locals; ++k) { + Index local_index; + std::string_view local_name; + + CHECK_RESULT(ReadIndex(&local_index, "named index")); + ERROR_UNLESS(local_index != last_local_index, + "duplicate local index: %u", local_index); + ERROR_UNLESS(last_local_index == kInvalidIndex || + local_index > last_local_index, + "local index out of order: %u", local_index); + last_local_index = local_index; + CHECK_RESULT(ReadStr(&local_name, "name")); + CALLBACK(OnLocalName, function_index, local_index, local_name); + } + } + } + break; + case NameSectionSubsection::Label: + // TODO(sbc): Implement label names. These are slightly more complicated + // since they refer to offsets in the code section / instruction stream. + state_.offset = subsection_end; + break; + case NameSectionSubsection::Type: + case NameSectionSubsection::Table: + case NameSectionSubsection::Memory: + case NameSectionSubsection::Global: + case NameSectionSubsection::ElemSegment: + case NameSectionSubsection::DataSegment: + case NameSectionSubsection::Tag: + if (subsection_size) { + Index num_names; + CHECK_RESULT(ReadCount(&num_names, "name count")); + CALLBACK(OnNameCount, num_names); + for (Index j = 0; j < num_names; ++j) { + Index index; + std::string_view name; + + CHECK_RESULT(ReadIndex(&index, "index")); + CHECK_RESULT(ReadStr(&name, "name")); + CALLBACK(OnNameEntry, type, index, name); + } + } + state_.offset = subsection_end; + break; + default: + // Unknown subsection, skip it. + state_.offset = subsection_end; + break; + } + ++i; + ERROR_UNLESS(state_.offset == subsection_end, + "unfinished sub-section (expected end: 0x%" PRIzx ")", + subsection_end); + } + CALLBACK0(EndNamesSection); + return Result::Ok; +} + +Result BinaryReader::ReadRelocSection(Offset section_size) { + CALLBACK(BeginRelocSection, section_size); + uint32_t section_index; + CHECK_RESULT(ReadU32Leb128(§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; + + uint32_t count; + switch (static_cast<DylinkEntryType>(dylink_type)) { + case DylinkEntryType::MemInfo: { + uint32_t mem_size; + uint32_t mem_align; + uint32_t table_size; + uint32_t table_align; + + CHECK_RESULT(ReadU32Leb128(&mem_size, "mem_size")); + CHECK_RESULT(ReadU32Leb128(&mem_align, "mem_align")); + CHECK_RESULT(ReadU32Leb128(&table_size, "table_size")); + CHECK_RESULT(ReadU32Leb128(&table_align, "table_align")); + CALLBACK(OnDylinkInfo, mem_size, mem_align, table_size, table_align); + break; + } + case DylinkEntryType::Needed: + CHECK_RESULT(ReadU32Leb128(&count, "needed_dynlibs")); + CALLBACK(OnDylinkNeededCount, count); + while (count--) { + std::string_view so_name; + CHECK_RESULT(ReadStr(&so_name, "dylib so_name")); + CALLBACK(OnDylinkNeeded, so_name); + } + break; + case DylinkEntryType::ImportInfo: + CHECK_RESULT(ReadU32Leb128(&count, "count")); + CALLBACK(OnDylinkImportCount, count); + for (Index i = 0; i < count; ++i) { + uint32_t flags = 0; + std::string_view module; + std::string_view field; + CHECK_RESULT(ReadStr(&module, "module")); + CHECK_RESULT(ReadStr(&field, "field")); + CHECK_RESULT(ReadU32Leb128(&flags, "flags")); + CALLBACK(OnDylinkImport, module, field, flags); + } + break; + case DylinkEntryType::ExportInfo: + CHECK_RESULT(ReadU32Leb128(&count, "count")); + CALLBACK(OnDylinkExportCount, count); + for (Index i = 0; i < count; ++i) { + uint32_t flags = 0; + std::string_view name; + CHECK_RESULT(ReadStr(&name, "name")); + CHECK_RESULT(ReadU32Leb128(&flags, "flags")); + CALLBACK(OnDylinkExport, name, flags); + } + break; + default: + // Unknown subsection, skip it. + state_.offset = subsection_end; + break; + } + ERROR_UNLESS(state_.offset == subsection_end, + "unfinished sub-section (expected end: 0x%" PRIzx ")", + subsection_end); + } + + CALLBACK0(EndDylinkSection); + return Result::Ok; +} + +Result BinaryReader::ReadDylinkSection(Offset section_size) { + CALLBACK(BeginDylinkSection, section_size); + uint32_t mem_size; + uint32_t mem_align; + uint32_t table_size; + uint32_t table_align; + + CHECK_RESULT(ReadU32Leb128(&mem_size, "mem_size")); + CHECK_RESULT(ReadU32Leb128(&mem_align, "mem_align")); + CHECK_RESULT(ReadU32Leb128(&table_size, "table_size")); + CHECK_RESULT(ReadU32Leb128(&table_align, "table_align")); + CALLBACK(OnDylinkInfo, mem_size, mem_align, table_size, table_align); + + uint32_t count; + CHECK_RESULT(ReadU32Leb128(&count, "needed_dynlibs")); + CALLBACK(OnDylinkNeededCount, count); + while (count--) { + std::string_view so_name; + CHECK_RESULT(ReadStr(&so_name, "dylib so_name")); + CALLBACK(OnDylinkNeeded, so_name); + } + + CALLBACK0(EndDylinkSection); + return Result::Ok; +} + +Result BinaryReader::ReadTargetFeaturesSections(Offset section_size) { + CALLBACK(BeginTargetFeaturesSection, section_size); + uint32_t count; + CHECK_RESULT(ReadU32Leb128(&count, "sym count")); + CALLBACK(OnFeatureCount, count); + while (count--) { + uint8_t prefix; + std::string_view name; + CHECK_RESULT(ReadU8(&prefix, "prefix")); + CHECK_RESULT(ReadStr(&name, "feature name")); + CALLBACK(OnFeature, prefix, name); + } + CALLBACK0(EndTargetFeaturesSection); + return Result::Ok; +} + +Result BinaryReader::ReadLinkingSection(Offset section_size) { + CALLBACK(BeginLinkingSection, section_size); + uint32_t version; + CHECK_RESULT(ReadU32Leb128(&version, "version")); + ERROR_UNLESS(version == 2, "invalid linking metadata version: %u", version); + while (state_.offset < read_end_) { + uint32_t linking_type; + Offset subsection_size; + CHECK_RESULT(ReadU32Leb128(&linking_type, "type")); + CHECK_RESULT(ReadOffset(&subsection_size, "subsection size")); + size_t subsection_end = state_.offset + subsection_size; + ERROR_UNLESS(subsection_end <= read_end_, + "invalid sub-section size: extends past end"); + ReadEndRestoreGuard guard(this); + read_end_ = subsection_end; + + uint32_t count; + switch (static_cast<LinkingEntryType>(linking_type)) { + case LinkingEntryType::SymbolTable: + CHECK_RESULT(ReadU32Leb128(&count, "sym count")); + CALLBACK(OnSymbolCount, count); + for (Index i = 0; i < count; ++i) { + std::string_view name; + uint32_t flags = 0; + uint32_t kind = 0; + CHECK_RESULT(ReadU32Leb128(&kind, "sym type")); + CHECK_RESULT(ReadU32Leb128(&flags, "sym flags")); + SymbolType sym_type = static_cast<SymbolType>(kind); + switch (sym_type) { + case SymbolType::Function: + case SymbolType::Global: + case SymbolType::Tag: + case SymbolType::Table: { + uint32_t index = 0; + CHECK_RESULT(ReadU32Leb128(&index, "index")); + if ((flags & WABT_SYMBOL_FLAG_UNDEFINED) == 0 || + (flags & WABT_SYMBOL_FLAG_EXPLICIT_NAME) != 0) + CHECK_RESULT(ReadStr(&name, "symbol name")); + switch (sym_type) { + case SymbolType::Function: + CALLBACK(OnFunctionSymbol, i, flags, name, index); + break; + case SymbolType::Global: + CALLBACK(OnGlobalSymbol, i, flags, name, index); + break; + case SymbolType::Tag: + CALLBACK(OnTagSymbol, i, flags, name, index); + break; + case SymbolType::Table: + CALLBACK(OnTableSymbol, i, flags, name, index); + break; + default: + WABT_UNREACHABLE; + } + break; + } + case SymbolType::Data: { + uint32_t segment = 0; + uint32_t offset = 0; + uint32_t size = 0; + CHECK_RESULT(ReadStr(&name, "symbol name")); + if ((flags & WABT_SYMBOL_FLAG_UNDEFINED) == 0) { + CHECK_RESULT(ReadU32Leb128(&segment, "segment")); + CHECK_RESULT(ReadU32Leb128(&offset, "offset")); + CHECK_RESULT(ReadU32Leb128(&size, "size")); + } + CALLBACK(OnDataSymbol, i, flags, name, segment, offset, size); + break; + } + case SymbolType::Section: { + uint32_t index = 0; + CHECK_RESULT(ReadU32Leb128(&index, "index")); + CALLBACK(OnSectionSymbol, i, flags, index); + break; + } + } + } + break; + case LinkingEntryType::SegmentInfo: + CHECK_RESULT(ReadU32Leb128(&count, "info count")); + CALLBACK(OnSegmentInfoCount, count); + for (Index i = 0; i < count; i++) { + std::string_view name; + Address alignment_log2; + uint32_t flags; + CHECK_RESULT(ReadStr(&name, "segment name")); + CHECK_RESULT(ReadAlignment(&alignment_log2, "segment alignment")); + CHECK_RESULT(ReadU32Leb128(&flags, "segment flags")); + CALLBACK(OnSegmentInfo, i, name, alignment_log2, flags); + } + break; + case LinkingEntryType::InitFunctions: + CHECK_RESULT(ReadU32Leb128(&count, "info count")); + CALLBACK(OnInitFunctionCount, count); + while (count--) { + uint32_t priority; + uint32_t symbol; + CHECK_RESULT(ReadU32Leb128(&priority, "priority")); + CHECK_RESULT(ReadU32Leb128(&symbol, "symbol index")); + CALLBACK(OnInitFunction, priority, symbol); + } + break; + case LinkingEntryType::ComdatInfo: + CHECK_RESULT(ReadU32Leb128(&count, "count")); + CALLBACK(OnComdatCount, count); + while (count--) { + uint32_t flags; + uint32_t entry_count; + std::string_view name; + CHECK_RESULT(ReadStr(&name, "comdat name")); + CHECK_RESULT(ReadU32Leb128(&flags, "flags")); + CHECK_RESULT(ReadU32Leb128(&entry_count, "entry count")); + CALLBACK(OnComdatBegin, name, flags, entry_count); + while (entry_count--) { + uint32_t kind; + uint32_t index; + CHECK_RESULT(ReadU32Leb128(&kind, "kind")); + CHECK_RESULT(ReadU32Leb128(&index, "index")); + ComdatType comdat_type = static_cast<ComdatType>(kind); + CALLBACK(OnComdatEntry, comdat_type, index); + } + } + break; + default: + // Unknown subsection, skip it. + state_.offset = subsection_end; + break; + } + ERROR_UNLESS(state_.offset == subsection_end, + "unfinished sub-section (expected end: 0x%" PRIzx ")", + subsection_end); + } + CALLBACK0(EndLinkingSection); + return Result::Ok; +} + +Result BinaryReader::ReadTagType(Index* out_sig_index) { + uint8_t attribute; + CHECK_RESULT(ReadU8(&attribute, "tag attribute")); + ERROR_UNLESS(attribute == 0, "tag attribute must be 0"); + CHECK_RESULT(ReadIndex(out_sig_index, "tag signature index")); + return Result::Ok; +} + +Result BinaryReader::ReadTagSection(Offset section_size) { + CALLBACK(BeginTagSection, section_size); + Index num_tags; + CHECK_RESULT(ReadCount(&num_tags, "tag count")); + CALLBACK(OnTagCount, num_tags); + + for (Index i = 0; i < num_tags; ++i) { + Index tag_index = num_tag_imports_ + i; + Index sig_index; + CHECK_RESULT(ReadTagType(&sig_index)); + CALLBACK(OnTagType, tag_index, sig_index); + } + + CALLBACK(EndTagSection); + return Result::Ok; +} + +Result BinaryReader::ReadCodeMetadataSection(std::string_view name, + Offset section_size) { + CALLBACK(BeginCodeMetadataSection, name, section_size); + + Index num_functions; + CHECK_RESULT(ReadCount(&num_functions, "function count")); + CALLBACK(OnCodeMetadataFuncCount, num_functions); + + Index last_function_index = kInvalidIndex; + for (Index i = 0; i < num_functions; ++i) { + Index function_index; + CHECK_RESULT(ReadCount(&function_index, "function index")); + ERROR_UNLESS(function_index >= num_func_imports_, + "function import can't have metadata (got %" PRIindex ")", + function_index); + ERROR_UNLESS(function_index < NumTotalFuncs(), + "invalid function index: %" PRIindex, function_index); + ERROR_UNLESS(function_index != last_function_index, + "duplicate function index: %" PRIindex, function_index); + ERROR_UNLESS(last_function_index == kInvalidIndex || + function_index > last_function_index, + "function index out of order: %" PRIindex, function_index); + last_function_index = function_index; + + Index num_metadata; + CHECK_RESULT(ReadCount(&num_metadata, "metadata instances count")); + + CALLBACK(OnCodeMetadataCount, function_index, num_metadata); + + Offset last_code_offset = kInvalidOffset; + for (Index j = 0; j < num_metadata; ++j) { + Offset code_offset; + CHECK_RESULT(ReadOffset(&code_offset, "code offset")); + ERROR_UNLESS(code_offset != last_code_offset, + "duplicate code offset: %" PRIzx, code_offset); + ERROR_UNLESS( + last_code_offset == kInvalidOffset || code_offset > last_code_offset, + "code offset out of order: %" PRIzx, code_offset); + last_code_offset = code_offset; + + Address data_size; + const void* data; + CHECK_RESULT(ReadBytes(&data, &data_size, "instance data")); + CALLBACK(OnCodeMetadata, code_offset, data, data_size); + } + } + + CALLBACK(EndCodeMetadataSection); + return Result::Ok; +} + +Result BinaryReader::ReadCustomSection(Index section_index, + Offset section_size) { + std::string_view section_name; + CHECK_RESULT(ReadStr(§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_TARGET_FEATURES) { + CHECK_RESULT(ReadTargetFeaturesSections(section_size)); + } else if (section_name == WABT_BINARY_SECTION_LINKING) { + CHECK_RESULT(ReadLinkingSection(section_size)); + } else if (options_.features.code_metadata_enabled() && + section_name.find(WABT_BINARY_SECTION_CODE_METADATA) == 0) { + std::string_view metadata_name = section_name; + metadata_name.remove_prefix(sizeof(WABT_BINARY_SECTION_CODE_METADATA) - 1); + CHECK_RESULT(ReadCodeMetadataSection(metadata_name, section_size)); + } else { + // This is an unknown custom section, skip it. + state_.offset = read_end_; + } + CALLBACK0(EndCustomSection); + return Result::Ok; +} + +Result BinaryReader::ReadTypeSection(Offset section_size) { + CALLBACK(BeginTypeSection, section_size); + Index num_signatures; + CHECK_RESULT(ReadCount(&num_signatures, "type count")); + CALLBACK(OnTypeCount, num_signatures); + + for (Index i = 0; i < num_signatures; ++i) { + Type form; + if (options_.features.gc_enabled()) { + CHECK_RESULT(ReadType(&form, "type form")); + } else { + uint8_t type; + CHECK_RESULT(ReadU8(&type, "type form")); + ERROR_UNLESS(type == 0x60, "unexpected type form (got %#x)", type); + form = Type::Func; + } + + switch (form) { + case Type::Func: { + Index num_params; + CHECK_RESULT(ReadCount(&num_params, "function param count")); + + param_types_.resize(num_params); + + for (Index j = 0; j < num_params; ++j) { + Type param_type; + CHECK_RESULT(ReadType(¶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) { + std::string_view module_name; + CHECK_RESULT(ReadStr(&module_name, "import module name")); + std::string_view field_name; + CHECK_RESULT(ReadStr(&field_name, "import field name")); + + uint8_t kind; + CHECK_RESULT(ReadU8(&kind, "import kind")); + CALLBACK(OnImport, i, static_cast<ExternalKind>(kind), module_name, + field_name); + switch (static_cast<ExternalKind>(kind)) { + case ExternalKind::Func: { + Index sig_index; + CHECK_RESULT(ReadIndex(&sig_index, "import signature index")); + CALLBACK(OnImportFunc, i, module_name, field_name, num_func_imports_, + sig_index); + num_func_imports_++; + break; + } + + case ExternalKind::Table: { + Type elem_type; + Limits elem_limits; + CHECK_RESULT(ReadTable(&elem_type, &elem_limits)); + CALLBACK(OnImportTable, i, module_name, field_name, num_table_imports_, + elem_type, &elem_limits); + num_table_imports_++; + break; + } + + case ExternalKind::Memory: { + Limits page_limits; + CHECK_RESULT(ReadMemory(&page_limits)); + CALLBACK(OnImportMemory, i, module_name, field_name, + num_memory_imports_, &page_limits); + num_memory_imports_++; + break; + } + + case ExternalKind::Global: { + Type type; + bool mutable_; + CHECK_RESULT(ReadGlobalHeader(&type, &mutable_)); + CALLBACK(OnImportGlobal, i, module_name, field_name, + num_global_imports_, type, mutable_); + num_global_imports_++; + break; + } + + case ExternalKind::Tag: { + ERROR_UNLESS(options_.features.exceptions_enabled(), + "invalid import tag kind: exceptions not allowed"); + Index sig_index; + CHECK_RESULT(ReadTagType(&sig_index)); + CALLBACK(OnImportTag, i, module_name, field_name, num_tag_imports_, + sig_index); + num_tag_imports_++; + break; + } + + default: + PrintError("malformed import kind: %d", kind); + return Result::Error; + } + } + + CALLBACK0(EndImportSection); + return Result::Ok; +} + +Result BinaryReader::ReadFunctionSection(Offset section_size) { + CALLBACK(BeginFunctionSection, section_size); + CHECK_RESULT( + ReadCount(&num_function_signatures_, "function signature count")); + CALLBACK(OnFunctionCount, num_function_signatures_); + for (Index i = 0; i < num_function_signatures_; ++i) { + Index func_index = num_func_imports_ + i; + Index sig_index; + CHECK_RESULT(ReadIndex(&sig_index, "function signature index")); + CALLBACK(OnFunction, func_index, sig_index); + } + CALLBACK0(EndFunctionSection); + return Result::Ok; +} + +Result BinaryReader::ReadTableSection(Offset section_size) { + CALLBACK(BeginTableSection, section_size); + Index num_tables; + CHECK_RESULT(ReadCount(&num_tables, "table count")); + CALLBACK(OnTableCount, num_tables); + for (Index i = 0; i < num_tables; ++i) { + Index table_index = num_table_imports_ + i; + Type elem_type; + Limits elem_limits; + CHECK_RESULT(ReadTable(&elem_type, &elem_limits)); + CALLBACK(OnTable, table_index, elem_type, &elem_limits); + } + CALLBACK0(EndTableSection); + return Result::Ok; +} + +Result BinaryReader::ReadMemorySection(Offset section_size) { + CALLBACK(BeginMemorySection, section_size); + Index num_memories; + CHECK_RESULT(ReadCount(&num_memories, "memory count")); + CALLBACK(OnMemoryCount, num_memories); + for (Index i = 0; i < num_memories; ++i) { + Index memory_index = num_memory_imports_ + i; + Limits page_limits; + CHECK_RESULT(ReadMemory(&page_limits)); + CALLBACK(OnMemory, memory_index, &page_limits); + } + CALLBACK0(EndMemorySection); + return Result::Ok; +} + +Result BinaryReader::ReadGlobalSection(Offset section_size) { + CALLBACK(BeginGlobalSection, section_size); + Index num_globals; + CHECK_RESULT(ReadCount(&num_globals, "global count")); + CALLBACK(OnGlobalCount, num_globals); + for (Index i = 0; i < num_globals; ++i) { + Index global_index = num_global_imports_ + i; + Type global_type; + bool mutable_; + CHECK_RESULT(ReadGlobalHeader(&global_type, &mutable_)); + CALLBACK(BeginGlobal, global_index, global_type, mutable_); + CALLBACK(BeginGlobalInitExpr, global_index); + CHECK_RESULT(ReadInitExpr(global_index)); + CALLBACK(EndGlobalInitExpr, global_index); + CALLBACK(EndGlobal, global_index); + } + CALLBACK0(EndGlobalSection); + return Result::Ok; +} + +Result BinaryReader::ReadExportSection(Offset section_size) { + CALLBACK(BeginExportSection, section_size); + Index num_exports; + CHECK_RESULT(ReadCount(&num_exports, "export count")); + CALLBACK(OnExportCount, num_exports); + for (Index i = 0; i < num_exports; ++i) { + std::string_view name; + CHECK_RESULT(ReadStr(&name, "export item name")); + + ExternalKind kind; + CHECK_RESULT(ReadExternalKind(&kind, "export kind")); + + Index item_index; + CHECK_RESULT(ReadIndex(&item_index, "export item index")); + if (kind == ExternalKind::Tag) { + ERROR_UNLESS(options_.features.exceptions_enabled(), + "invalid export tag kind: exceptions not allowed"); + } + + CALLBACK(OnExport, i, static_cast<ExternalKind>(kind), item_index, name); + } + CALLBACK0(EndExportSection); + return Result::Ok; +} + +Result BinaryReader::ReadStartSection(Offset section_size) { + CALLBACK(BeginStartSection, section_size); + Index func_index; + CHECK_RESULT(ReadIndex(&func_index, "start function index")); + CALLBACK(OnStartFunction, func_index); + CALLBACK0(EndStartSection); + return Result::Ok; +} + +Result BinaryReader::ReadElemSection(Offset section_size) { + CALLBACK(BeginElemSection, section_size); + Index num_elem_segments; + CHECK_RESULT(ReadCount(&num_elem_segments, "elem segment count")); + CALLBACK(OnElemSegmentCount, num_elem_segments); + for (Index i = 0; i < num_elem_segments; ++i) { + uint32_t flags; + CHECK_RESULT(ReadU32Leb128(&flags, "elem segment flags")); + ERROR_IF(flags > SegFlagMax, "invalid elem segment flags: %#x", flags); + Index table_index(0); + if ((flags & (SegPassive | SegExplicitIndex)) == SegExplicitIndex) { + CHECK_RESULT(ReadIndex(&table_index, "elem segment table index")); + } + Type elem_type = Type::FuncRef; + + CALLBACK(BeginElemSegment, i, table_index, flags); + + if (!(flags & SegPassive)) { + CALLBACK(BeginElemSegmentInitExpr, i); + CHECK_RESULT(ReadInitExpr(i)); + CALLBACK(EndElemSegmentInitExpr, i); + } + + // For backwards compat we support not declaring the element kind. + if (flags & (SegPassive | SegExplicitIndex)) { + if (flags & SegUseElemExprs) { + CHECK_RESULT(ReadRefType(&elem_type, "table elem type")); + } else { + ExternalKind kind; + CHECK_RESULT(ReadExternalKind(&kind, "export kind")); + ERROR_UNLESS(kind == ExternalKind::Func, + "segment elem type must be func (%s)", + elem_type.GetName().c_str()); + elem_type = Type::FuncRef; + } + } + + CALLBACK(OnElemSegmentElemType, i, elem_type); + + Index num_elem_exprs; + CHECK_RESULT(ReadCount(&num_elem_exprs, "elem count")); + + CALLBACK(OnElemSegmentElemExprCount, i, num_elem_exprs); + for (Index j = 0; j < num_elem_exprs; ++j) { + if (flags & SegUseElemExprs) { + Opcode opcode; + CHECK_RESULT(ReadOpcode(&opcode, "elem expr opcode")); + if (opcode == Opcode::RefNull) { + Type type; + CHECK_RESULT(ReadRefType(&type, "elem expr ref.null type")); + CALLBACK(OnElemSegmentElemExpr_RefNull, i, type); + } else if (opcode == Opcode::RefFunc) { + Index func_index; + CHECK_RESULT(ReadIndex(&func_index, "elem expr func index")); + CALLBACK(OnElemSegmentElemExpr_RefFunc, i, func_index); + } else { + PrintError( + "expected ref.null or ref.func in passive element segment"); + } + CHECK_RESULT(ReadOpcode(&opcode, "opcode")); + ERROR_UNLESS(opcode == Opcode::End, + "expected END opcode after element expression"); + } else { + Index func_index; + CHECK_RESULT(ReadIndex(&func_index, "elem expr func index")); + CALLBACK(OnElemSegmentElemExpr_RefFunc, i, func_index); + } + } + CALLBACK(EndElemSegment, i); + } + CALLBACK0(EndElemSection); + return Result::Ok; +} + +Result BinaryReader::ReadCodeSection(Offset section_size) { + CALLBACK(BeginCodeSection, section_size); + CHECK_RESULT(ReadCount(&num_function_bodies_, "function body count")); + ERROR_UNLESS(num_function_signatures_ == num_function_bodies_, + "function signature count != function body count"); + CALLBACK(OnFunctionBodyCount, num_function_bodies_); + for (Index i = 0; i < num_function_bodies_; ++i) { + Index func_index = num_func_imports_ + i; + Offset func_offset = state_.offset; + state_.offset = func_offset; + uint32_t body_size; + CHECK_RESULT(ReadU32Leb128(&body_size, "function body size")); + Offset body_start_offset = state_.offset; + Offset end_offset = body_start_offset + body_size; + CALLBACK(BeginFunctionBody, func_index, body_size); + + uint64_t total_locals = 0; + Index num_local_decls; + CHECK_RESULT(ReadCount(&num_local_decls, "local declaration count")); + CALLBACK(OnLocalDeclCount, num_local_decls); + for (Index k = 0; k < num_local_decls; ++k) { + Index num_local_types; + CHECK_RESULT(ReadIndex(&num_local_types, "local type count")); + total_locals += num_local_types; + ERROR_UNLESS(total_locals <= UINT32_MAX, "local count must be <= 0x%x", + UINT32_MAX); + Type local_type; + CHECK_RESULT(ReadType(&local_type, "local type")); + ERROR_UNLESS(IsConcreteType(local_type), "expected valid local type"); + CALLBACK(OnLocalDecl, k, num_local_types, local_type); + } + + if (options_.skip_function_bodies) { + state_.offset = end_offset; + } else { + CHECK_RESULT(ReadFunctionBody(end_offset)); + } + + CALLBACK(EndFunctionBody, func_index); + } + CALLBACK0(EndCodeSection); + return Result::Ok; +} + +Result BinaryReader::ReadDataSection(Offset section_size) { + CALLBACK(BeginDataSection, section_size); + Index num_data_segments; + CHECK_RESULT(ReadCount(&num_data_segments, "data segment count")); + CALLBACK(OnDataSegmentCount, num_data_segments); + // If the DataCount section is not present, then data_count_ will be invalid. + ERROR_UNLESS(data_count_ == kInvalidIndex || data_count_ == num_data_segments, + "data segment count does not equal count in DataCount section"); + for (Index i = 0; i < num_data_segments; ++i) { + uint32_t flags; + CHECK_RESULT(ReadU32Leb128(&flags, "data segment flags")); + ERROR_IF(flags != 0 && !options_.features.bulk_memory_enabled(), + "invalid memory index %d: bulk memory not allowed", flags); + ERROR_IF(flags > SegFlagMax, "invalid data segment flags: %#x", flags); + Index memory_index(0); + if (flags & SegExplicitIndex) { + CHECK_RESULT(ReadIndex(&memory_index, "data segment memory index")); + } + CALLBACK(BeginDataSegment, i, memory_index, flags); + if (!(flags & SegPassive)) { + ERROR_UNLESS(memories.size() > 0, "no memory to copy data to"); + CALLBACK(BeginDataSegmentInitExpr, i); + CHECK_RESULT(ReadInitExpr(i)); + CALLBACK(EndDataSegmentInitExpr, i); + } + + Address data_size; + const void* data; + CHECK_RESULT(ReadBytes(&data, &data_size, "data segment data")); + CALLBACK(OnDataSegmentData, i, data, data_size); + CALLBACK(EndDataSegment, i); + } + CALLBACK0(EndDataSection); + return Result::Ok; +} + +Result BinaryReader::ReadDataCountSection(Offset section_size) { + CALLBACK(BeginDataCountSection, section_size); + Index data_count; + CHECK_RESULT(ReadIndex(&data_count, "data count")); + CALLBACK(OnDataCount, data_count); + CALLBACK0(EndDataCountSection); + data_count_ = data_count; + return Result::Ok; +} + +Result BinaryReader::ReadSections(const ReadSectionsOptions& options) { + Result result = Result::Ok; + Index section_index = 0; + bool seen_section_code[static_cast<int>(BinarySection::Last) + 1] = {false}; + + for (; state_.offset < state_.size; ++section_index) { + uint8_t section_code; + Offset section_size; + CHECK_RESULT(ReadU8(§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); + if (options.stop_on_first_error) { + return Result::Error; + } + // If we don't have to stop on first error, continue reading + // sections, because although we could not understand the + // current section, we can continue and correctly parse + // subsequent sections, so we can give back as much information + // as we can understand. + result = Result::Error; + state_.offset = read_end_; + continue; + } + + BinarySection section = static_cast<BinarySection>(section_code); + if (section != BinarySection::Custom) { + if (seen_section_code[section_code]) { + PrintError("multiple %s sections", GetSectionName(section)); + return Result::Error; + } + seen_section_code[section_code] = true; + } + + ERROR_UNLESS(read_end_ <= state_.size, + "invalid section size: extends past end"); + + ERROR_UNLESS( + last_known_section_ == BinarySection::Invalid || + section == BinarySection::Custom || + GetSectionOrder(section) > GetSectionOrder(last_known_section_), + "section %s out of order", GetSectionName(section)); + + ERROR_UNLESS(!did_read_names_section_ || section == BinarySection::Custom, + "%s section can not occur after Name section", + GetSectionName(section)); + + CALLBACK(BeginSection, section_index, section, section_size); + + bool stop_on_first_error = options_.stop_on_first_error; + Result section_result = Result::Error; + switch (section) { + case BinarySection::Custom: + section_result = ReadCustomSection(section_index, section_size); + if (options_.fail_on_custom_section_error) { + result |= section_result; + } else { + stop_on_first_error = false; + } + break; + case BinarySection::Type: + section_result = ReadTypeSection(section_size); + result |= section_result; + break; + case BinarySection::Import: + section_result = ReadImportSection(section_size); + result |= section_result; + break; + case BinarySection::Function: + section_result = ReadFunctionSection(section_size); + result |= section_result; + break; + case BinarySection::Table: + section_result = ReadTableSection(section_size); + result |= section_result; + break; + case BinarySection::Memory: + section_result = ReadMemorySection(section_size); + result |= section_result; + break; + case BinarySection::Global: + section_result = ReadGlobalSection(section_size); + result |= section_result; + break; + case BinarySection::Export: + section_result = ReadExportSection(section_size); + result |= section_result; + break; + case BinarySection::Start: + section_result = ReadStartSection(section_size); + result |= section_result; + break; + case BinarySection::Elem: + section_result = ReadElemSection(section_size); + result |= section_result; + break; + case BinarySection::Code: + section_result = ReadCodeSection(section_size); + result |= section_result; + break; + case BinarySection::Data: + section_result = ReadDataSection(section_size); + result |= section_result; + break; + case BinarySection::Tag: + ERROR_UNLESS(options_.features.exceptions_enabled(), + "invalid section code: %u", + static_cast<unsigned int>(section)); + section_result = ReadTagSection(section_size); + result |= section_result; + break; + case BinarySection::DataCount: + ERROR_UNLESS(options_.features.bulk_memory_enabled(), + "invalid section code: %u", + static_cast<unsigned int>(section)); + section_result = ReadDataCountSection(section_size); + result |= section_result; + break; + case BinarySection::Invalid: + WABT_UNREACHABLE; + } + + if (Succeeded(section_result) && state_.offset != read_end_) { + PrintError("unfinished section (expected end: 0x%" PRIzx ")", read_end_); + section_result = Result::Error; + result |= section_result; + } + + if (Failed(section_result)) { + if (stop_on_first_error) { + return Result::Error; + } + + // If we're continuing after failing to read this section, move the + // offset to the expected section end. This way we may be able to read + // further sections. + state_.offset = read_end_; + } + + if (section != BinarySection::Custom) { + last_known_section_ = section; + } + } + + return result; +} + +Result BinaryReader::ReadModule(const ReadModuleOptions& options) { + uint32_t magic = 0; + CHECK_RESULT(ReadU32(&magic, "magic")); + ERROR_UNLESS(magic == WABT_BINARY_MAGIC, "bad magic value"); + uint32_t version = 0; + CHECK_RESULT(ReadU32(&version, "version")); + ERROR_UNLESS(version == WABT_BINARY_VERSION, + "bad wasm file version: %#x (expected %#x)", version, + WABT_BINARY_VERSION); + + CALLBACK(BeginModule, version); + CHECK_RESULT(ReadSections(ReadSectionsOptions{options.stop_on_first_error})); + // This is checked in ReadCodeSection, but it must be checked at the end too, + // in case the code section was omitted. + ERROR_UNLESS(num_function_signatures_ == num_function_bodies_, + "function signature count != function body count"); + CALLBACK0(EndModule); + + return Result::Ok; +} + +} // end anonymous namespace + +Result ReadBinary(const void* data, + size_t size, + BinaryReaderDelegate* delegate, + const ReadBinaryOptions& options) { + BinaryReader reader(data, size, delegate, options); + return reader.ReadModule( + BinaryReader::ReadModuleOptions{options.stop_on_first_error}); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary-writer-spec.cc b/third_party/wasm2c/src/binary-writer-spec.cc new file mode 100644 index 0000000000..3cb6674de3 --- /dev/null +++ b/third_party/wasm2c/src/binary-writer-spec.cc @@ -0,0 +1,672 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/binary-writer-spec.h" + +#include <cassert> +#include <cinttypes> +#include <string_view> + +#include "wabt/config.h" + +#include "wabt/binary-writer.h" +#include "wabt/binary.h" +#include "wabt/cast.h" +#include "wabt/filenames.h" +#include "wabt/ir.h" +#include "wabt/literal.h" +#include "wabt/stream.h" + +namespace wabt { + +namespace { + +class BinaryWriterSpec { + public: + BinaryWriterSpec(Stream* json_stream, + WriteBinarySpecStreamFactory module_stream_factory, + std::string_view source_filename, + std::string_view module_filename_noext, + const WriteBinaryOptions& options); + + Result WriteScript(const Script& script); + + private: + std::string GetModuleFilename(const char* extension); + void WriteString(const char* s); + void WriteKey(const char* key); + void WriteSeparator(); + void WriteEscapedString(std::string_view); + void WriteCommandType(const Command& command); + void WriteLocation(const Location& loc); + void WriteVar(const Var& var); + void WriteTypeObject(Type type); + void WriteF32(uint32_t, ExpectedNan); + void WriteF64(uint64_t, ExpectedNan); + void WriteRefBits(uintptr_t ref_bits); + void WriteConst(const Const& const_); + void WriteConstVector(const ConstVector& consts); + void WriteAction(const Action& action); + void WriteActionResultType(const Action& action); + void WriteModule(std::string_view filename, const Module& module); + void WriteScriptModule(std::string_view filename, + const ScriptModule& script_module); + void WriteInvalidModule(const ScriptModule& module, std::string_view text); + void WriteCommands(); + + const Script* script_ = nullptr; + Stream* json_stream_ = nullptr; + WriteBinarySpecStreamFactory module_stream_factory_; + std::string source_filename_; + std::string module_filename_noext_; + const WriteBinaryOptions& options_; + Result result_ = Result::Ok; + size_t num_modules_ = 0; +}; + +BinaryWriterSpec::BinaryWriterSpec( + Stream* json_stream, + WriteBinarySpecStreamFactory module_stream_factory, + std::string_view source_filename, + std::string_view module_filename_noext, + const WriteBinaryOptions& options) + : json_stream_(json_stream), + module_stream_factory_(module_stream_factory), + source_filename_(source_filename), + module_filename_noext_(module_filename_noext), + options_(options) {} + +std::string BinaryWriterSpec::GetModuleFilename(const char* extension) { + std::string result = module_filename_noext_; + result += '.'; + result += std::to_string(num_modules_); + result += extension; + ConvertBackslashToSlash(&result); + return result; +} + +void BinaryWriterSpec::WriteString(const char* s) { + json_stream_->Writef("\"%s\"", s); +} + +void BinaryWriterSpec::WriteKey(const char* key) { + json_stream_->Writef("\"%s\": ", key); +} + +void BinaryWriterSpec::WriteSeparator() { + json_stream_->Writef(", "); +} + +void BinaryWriterSpec::WriteEscapedString(std::string_view s) { + json_stream_->WriteChar('"'); + for (size_t i = 0; i < s.length(); ++i) { + uint8_t c = s[i]; + if (c < 0x20 || c == '\\' || c == '"') { + json_stream_->Writef("\\u%04x", c); + } else { + json_stream_->WriteChar(c); + } + } + json_stream_->WriteChar('"'); +} + +void BinaryWriterSpec::WriteCommandType(const Command& command) { + static const char* s_command_names[] = { + "module", + "module", + "action", + "register", + "assert_malformed", + "assert_invalid", + "assert_unlinkable", + "assert_uninstantiable", + "assert_return", + "assert_trap", + "assert_exhaustion", + "assert_exception", + }; + WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(s_command_names) == kCommandTypeCount); + + WriteKey("type"); + assert(s_command_names[static_cast<size_t>(command.type)]); + WriteString(s_command_names[static_cast<size_t>(command.type)]); +} + +void BinaryWriterSpec::WriteLocation(const Location& loc) { + WriteKey("line"); + json_stream_->Writef("%d", loc.line); +} + +void BinaryWriterSpec::WriteVar(const Var& var) { + if (var.is_index()) { + json_stream_->Writef("\"%" PRIindex "\"", var.index()); + } else { + WriteEscapedString(var.name()); + } +} + +void BinaryWriterSpec::WriteTypeObject(Type type) { + json_stream_->Writef("{"); + WriteKey("type"); + WriteString(type.GetName().c_str()); + json_stream_->Writef("}"); +} + +void BinaryWriterSpec::WriteF32(uint32_t f32_bits, ExpectedNan expected) { + switch (expected) { + case ExpectedNan::None: + json_stream_->Writef("\"%u\"", f32_bits); + break; + + case ExpectedNan::Arithmetic: + WriteString("nan:arithmetic"); + break; + + case ExpectedNan::Canonical: + WriteString("nan:canonical"); + break; + } +} + +void BinaryWriterSpec::WriteF64(uint64_t f64_bits, ExpectedNan expected) { + switch (expected) { + case ExpectedNan::None: + json_stream_->Writef("\"%" PRIu64 "\"", f64_bits); + break; + + case ExpectedNan::Arithmetic: + WriteString("nan:arithmetic"); + break; + + case ExpectedNan::Canonical: + WriteString("nan:canonical"); + break; + } +} + +void BinaryWriterSpec::WriteRefBits(uintptr_t ref_bits) { + if (ref_bits == Const::kRefNullBits) { + json_stream_->Writef("\"null\""); + } else { + json_stream_->Writef("\"%" PRIuPTR "\"", ref_bits); + } +} + +void BinaryWriterSpec::WriteConst(const Const& const_) { + json_stream_->Writef("{"); + WriteKey("type"); + + /* Always write the values as strings, even though they may be representable + * as JSON numbers. This way the formatting is consistent. */ + switch (const_.type()) { + case Type::I32: + WriteString("i32"); + WriteSeparator(); + WriteKey("value"); + json_stream_->Writef("\"%u\"", const_.u32()); + break; + + case Type::I64: + WriteString("i64"); + WriteSeparator(); + WriteKey("value"); + json_stream_->Writef("\"%" PRIu64 "\"", const_.u64()); + break; + + case Type::F32: + WriteString("f32"); + WriteSeparator(); + WriteKey("value"); + WriteF32(const_.f32_bits(), const_.expected_nan()); + break; + + case Type::F64: + WriteString("f64"); + WriteSeparator(); + WriteKey("value"); + WriteF64(const_.f64_bits(), const_.expected_nan()); + break; + + case Type::FuncRef: { + WriteString("funcref"); + WriteSeparator(); + WriteKey("value"); + WriteRefBits(const_.ref_bits()); + break; + } + + case Type::ExternRef: { + WriteString("externref"); + WriteSeparator(); + WriteKey("value"); + WriteRefBits(const_.ref_bits()); + break; + } + + case Type::V128: { + WriteString("v128"); + WriteSeparator(); + WriteKey("lane_type"); + WriteString(const_.lane_type().GetName().c_str()); + WriteSeparator(); + WriteKey("value"); + json_stream_->Writef("["); + + for (int lane = 0; lane < const_.lane_count(); ++lane) { + switch (const_.lane_type()) { + case Type::I8: + json_stream_->Writef("\"%u\"", const_.v128_lane<uint8_t>(lane)); + break; + + case Type::I16: + json_stream_->Writef("\"%u\"", const_.v128_lane<uint16_t>(lane)); + break; + + case Type::I32: + json_stream_->Writef("\"%u\"", const_.v128_lane<uint32_t>(lane)); + break; + + case Type::I64: + json_stream_->Writef("\"%" PRIu64 "\"", + const_.v128_lane<uint64_t>(lane)); + break; + + case Type::F32: + WriteF32(const_.v128_lane<uint32_t>(lane), + const_.expected_nan(lane)); + break; + + case Type::F64: + WriteF64(const_.v128_lane<uint64_t>(lane), + const_.expected_nan(lane)); + break; + + default: + WABT_UNREACHABLE; + } + + if (lane != const_.lane_count() - 1) { + WriteSeparator(); + } + } + + json_stream_->Writef("]"); + + break; + } + + default: + WABT_UNREACHABLE; + } + + json_stream_->Writef("}"); +} + +void BinaryWriterSpec::WriteConstVector(const ConstVector& consts) { + json_stream_->Writef("["); + for (size_t i = 0; i < consts.size(); ++i) { + const Const& const_ = consts[i]; + WriteConst(const_); + if (i != consts.size() - 1) { + WriteSeparator(); + } + } + json_stream_->Writef("]"); +} + +void BinaryWriterSpec::WriteAction(const Action& action) { + WriteKey("action"); + json_stream_->Writef("{"); + WriteKey("type"); + if (action.type() == ActionType::Invoke) { + WriteString("invoke"); + } else { + assert(action.type() == ActionType::Get); + WriteString("get"); + } + WriteSeparator(); + if (action.module_var.is_name()) { + WriteKey("module"); + WriteVar(action.module_var); + WriteSeparator(); + } + if (action.type() == ActionType::Invoke) { + WriteKey("field"); + WriteEscapedString(action.name); + WriteSeparator(); + WriteKey("args"); + WriteConstVector(cast<InvokeAction>(&action)->args); + } else { + WriteKey("field"); + WriteEscapedString(action.name); + } + json_stream_->Writef("}"); +} + +void BinaryWriterSpec::WriteActionResultType(const Action& action) { + const Module* module = script_->GetModule(action.module_var); + const Export* export_; + json_stream_->Writef("["); + switch (action.type()) { + case ActionType::Invoke: { + export_ = module->GetExport(action.name); + assert(export_->kind == ExternalKind::Func); + const Func* func = module->GetFunc(export_->var); + Index num_results = func->GetNumResults(); + for (Index i = 0; i < num_results; ++i) + WriteTypeObject(func->GetResultType(i)); + break; + } + + case ActionType::Get: { + export_ = module->GetExport(action.name); + assert(export_->kind == ExternalKind::Global); + const Global* global = module->GetGlobal(export_->var); + WriteTypeObject(global->type); + break; + } + } + json_stream_->Writef("]"); +} + +void BinaryWriterSpec::WriteModule(std::string_view filename, + const Module& module) { + result_ |= + WriteBinaryModule(module_stream_factory_(filename), &module, options_); +} + +void BinaryWriterSpec::WriteScriptModule(std::string_view filename, + const ScriptModule& script_module) { + switch (script_module.type()) { + case ScriptModuleType::Text: + WriteModule(filename, cast<TextScriptModule>(&script_module)->module); + break; + + case ScriptModuleType::Binary: + module_stream_factory_(filename)->WriteData( + cast<BinaryScriptModule>(&script_module)->data, ""); + break; + + case ScriptModuleType::Quoted: + module_stream_factory_(filename)->WriteData( + cast<QuotedScriptModule>(&script_module)->data, ""); + break; + } +} + +void BinaryWriterSpec::WriteInvalidModule(const ScriptModule& module, + std::string_view text) { + const char* extension = ""; + const char* module_type = ""; + switch (module.type()) { + case ScriptModuleType::Text: + extension = kWasmExtension; + module_type = "binary"; + break; + + case ScriptModuleType::Binary: + extension = kWasmExtension; + module_type = "binary"; + break; + + case ScriptModuleType::Quoted: + extension = kWatExtension; + module_type = "text"; + break; + } + + WriteLocation(module.location()); + WriteSeparator(); + std::string filename = GetModuleFilename(extension); + WriteKey("filename"); + WriteEscapedString(GetBasename(filename)); + WriteSeparator(); + WriteKey("text"); + WriteEscapedString(text); + WriteSeparator(); + WriteKey("module_type"); + WriteString(module_type); + WriteScriptModule(filename, module); +} + +void BinaryWriterSpec::WriteCommands() { + json_stream_->Writef("{\"source_filename\": "); + WriteEscapedString(source_filename_); + json_stream_->Writef(",\n \"commands\": [\n"); + Index last_module_index = kInvalidIndex; + for (size_t i = 0; i < script_->commands.size(); ++i) { + const Command* command = script_->commands[i].get(); + + if (i != 0) { + WriteSeparator(); + json_stream_->Writef("\n"); + } + + json_stream_->Writef(" {"); + WriteCommandType(*command); + WriteSeparator(); + + switch (command->type) { + case CommandType::Module: { + const Module& module = cast<ModuleCommand>(command)->module; + std::string filename = GetModuleFilename(kWasmExtension); + WriteLocation(module.loc); + WriteSeparator(); + if (!module.name.empty()) { + WriteKey("name"); + WriteEscapedString(module.name); + WriteSeparator(); + } + WriteKey("filename"); + WriteEscapedString(GetBasename(filename)); + WriteModule(filename, module); + num_modules_++; + last_module_index = i; + break; + } + + case CommandType::ScriptModule: { + auto* script_module_command = cast<ScriptModuleCommand>(command); + const auto& module = script_module_command->module; + std::string filename = GetModuleFilename(kWasmExtension); + WriteLocation(module.loc); + WriteSeparator(); + if (!module.name.empty()) { + WriteKey("name"); + WriteEscapedString(module.name); + WriteSeparator(); + } + WriteKey("filename"); + WriteEscapedString(GetBasename(filename)); + WriteScriptModule(filename, *script_module_command->script_module); + num_modules_++; + last_module_index = i; + break; + } + + case CommandType::Action: { + const Action& action = *cast<ActionCommand>(command)->action; + WriteLocation(action.loc); + WriteSeparator(); + WriteAction(action); + WriteSeparator(); + WriteKey("expected"); + WriteActionResultType(action); + break; + } + + case CommandType::Register: { + auto* register_command = cast<RegisterCommand>(command); + const Var& var = register_command->var; + WriteLocation(var.loc); + WriteSeparator(); + if (var.is_name()) { + WriteKey("name"); + WriteVar(var); + WriteSeparator(); + } else { + /* If we're not registering by name, then we should only be + * registering the last module. */ + WABT_USE(last_module_index); + assert(var.index() == last_module_index); + } + WriteKey("as"); + WriteEscapedString(register_command->module_name); + break; + } + + case CommandType::AssertMalformed: { + auto* assert_malformed_command = cast<AssertMalformedCommand>(command); + WriteInvalidModule(*assert_malformed_command->module, + assert_malformed_command->text); + num_modules_++; + break; + } + + case CommandType::AssertInvalid: { + auto* assert_invalid_command = cast<AssertInvalidCommand>(command); + WriteInvalidModule(*assert_invalid_command->module, + assert_invalid_command->text); + num_modules_++; + break; + } + + case CommandType::AssertUnlinkable: { + auto* assert_unlinkable_command = + cast<AssertUnlinkableCommand>(command); + WriteInvalidModule(*assert_unlinkable_command->module, + assert_unlinkable_command->text); + num_modules_++; + break; + } + + case CommandType::AssertUninstantiable: { + auto* assert_uninstantiable_command = + cast<AssertUninstantiableCommand>(command); + WriteInvalidModule(*assert_uninstantiable_command->module, + assert_uninstantiable_command->text); + num_modules_++; + break; + } + + case CommandType::AssertReturn: { + auto* assert_return_command = cast<AssertReturnCommand>(command); + WriteLocation(assert_return_command->action->loc); + WriteSeparator(); + WriteAction(*assert_return_command->action); + WriteSeparator(); + const Expectation* expectation = assert_return_command->expected.get(); + switch (expectation->type()) { + case ExpectationType::Values: + WriteKey("expected"); + break; + + case ExpectationType::Either: + WriteKey("either"); + break; + } + WriteConstVector(expectation->expected); + break; + } + + case CommandType::AssertTrap: { + auto* assert_trap_command = cast<AssertTrapCommand>(command); + WriteLocation(assert_trap_command->action->loc); + WriteSeparator(); + WriteAction(*assert_trap_command->action); + WriteSeparator(); + WriteKey("text"); + WriteEscapedString(assert_trap_command->text); + WriteSeparator(); + WriteKey("expected"); + WriteActionResultType(*assert_trap_command->action); + break; + } + + case CommandType::AssertExhaustion: { + auto* assert_exhaustion_command = + cast<AssertExhaustionCommand>(command); + WriteLocation(assert_exhaustion_command->action->loc); + WriteSeparator(); + WriteAction(*assert_exhaustion_command->action); + WriteSeparator(); + WriteKey("text"); + WriteEscapedString(assert_exhaustion_command->text); + WriteSeparator(); + WriteKey("expected"); + WriteActionResultType(*assert_exhaustion_command->action); + break; + } + + case CommandType::AssertException: { + auto* assert_exception_command = cast<AssertExceptionCommand>(command); + WriteLocation(assert_exception_command->action->loc); + WriteSeparator(); + WriteAction(*assert_exception_command->action); + WriteSeparator(); + WriteKey("expected"); + WriteActionResultType(*assert_exception_command->action); + break; + } + } + + json_stream_->Writef("}"); + } + json_stream_->Writef("]}\n"); +} + +Result BinaryWriterSpec::WriteScript(const Script& script) { + script_ = &script; + WriteCommands(); + return result_; +} + +} // end anonymous namespace + +Result WriteBinarySpecScript(Stream* json_stream, + WriteBinarySpecStreamFactory module_stream_factory, + Script* script, + std::string_view source_filename, + std::string_view module_filename_noext, + const WriteBinaryOptions& options) { + BinaryWriterSpec binary_writer_spec(json_stream, module_stream_factory, + source_filename, module_filename_noext, + options); + return binary_writer_spec.WriteScript(*script); +} + +Result WriteBinarySpecScript( + Stream* json_stream, + Script* script, + std::string_view source_filename, + std::string_view module_filename_noext, + const WriteBinaryOptions& options, + std::vector<FilenameMemoryStreamPair>* out_module_streams, + Stream* log_stream) { + WriteBinarySpecStreamFactory module_stream_factory = + [&](std::string_view filename) { + out_module_streams->emplace_back( + filename, std::make_unique<MemoryStream>(log_stream)); + return out_module_streams->back().stream.get(); + }; + + BinaryWriterSpec binary_writer_spec(json_stream, module_stream_factory, + source_filename, module_filename_noext, + options); + return binary_writer_spec.WriteScript(*script); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary-writer.cc b/third_party/wasm2c/src/binary-writer.cc new file mode 100644 index 0000000000..2eb2884684 --- /dev/null +++ b/third_party/wasm2c/src/binary-writer.cc @@ -0,0 +1,1798 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/binary-writer.h" + +#include <cassert> +#include <cmath> +#include <cstdarg> +#include <cstdint> +#include <cstdio> +#include <set> +#include <string_view> +#include <vector> + +#include "wabt/config.h" + +#include "wabt/binary.h" +#include "wabt/cast.h" +#include "wabt/expr-visitor.h" +#include "wabt/ir.h" +#include "wabt/leb128.h" +#include "wabt/stream.h" + +#define PRINT_HEADER_NO_INDEX -1 +#define MAX_U32_LEB128_BYTES 5 + +namespace wabt { + +void WriteStr(Stream* stream, + std::string_view s, + const char* desc, + PrintChars print_chars) { + WriteU32Leb128(stream, s.length(), "string length"); + stream->WriteData(s.data(), s.length(), desc, print_chars); +} + +void WriteOpcode(Stream* stream, Opcode opcode) { + if (opcode.HasPrefix()) { + stream->WriteU8(opcode.GetPrefix(), "prefix"); + WriteU32Leb128(stream, opcode.GetCode(), opcode.GetName()); + } else { + stream->WriteU8(opcode.GetCode(), opcode.GetName()); + } +} + +void WriteType(Stream* stream, Type type, const char* desc) { + WriteS32Leb128(stream, type, desc ? desc : type.GetName().c_str()); + if (type.IsReferenceWithIndex()) { + WriteS32Leb128(stream, type.GetReferenceIndex(), + desc ? desc : type.GetName().c_str()); + } +} + +void WriteLimits(Stream* stream, const Limits* limits) { + uint32_t flags = limits->has_max ? WABT_BINARY_LIMITS_HAS_MAX_FLAG : 0; + flags |= limits->is_shared ? WABT_BINARY_LIMITS_IS_SHARED_FLAG : 0; + flags |= limits->is_64 ? WABT_BINARY_LIMITS_IS_64_FLAG : 0; + WriteU32Leb128(stream, flags, "limits: flags"); + if (limits->is_64) { + WriteU64Leb128(stream, limits->initial, "limits: initial"); + if (limits->has_max) { + WriteU64Leb128(stream, limits->max, "limits: max"); + } + } else { + WriteU32Leb128(stream, limits->initial, "limits: initial"); + if (limits->has_max) { + WriteU32Leb128(stream, limits->max, "limits: max"); + } + } +} + +void WriteDebugName(Stream* stream, std::string_view name, const char* desc) { + std::string_view stripped_name = name; + if (!stripped_name.empty()) { + // Strip leading $ from name + assert(stripped_name.front() == '$'); + stripped_name.remove_prefix(1); + } + WriteStr(stream, stripped_name, desc, PrintChars::Yes); +} + +namespace { + +/* TODO(binji): better leb size guess. Some sections we know will only be 1 + byte, but others we can be fairly certain will be larger. */ +constexpr size_t LEB_SECTION_SIZE_GUESS = 1; + +#define ALLOC_FAILURE \ + fprintf(stderr, "%s:%d: allocation failed\n", __FILE__, __LINE__) + +struct RelocSection { + RelocSection(const char* name, Index index) + : name(name), section_index(index) {} + + const char* name; + Index section_index; + std::vector<Reloc> relocations; +}; + +class Symbol { + public: + struct Function { + static const SymbolType type = SymbolType::Function; + Index index; + }; + struct Data { + static const SymbolType type = SymbolType::Data; + Index index; + Offset offset; + Address size; + }; + struct Global { + static const SymbolType type = SymbolType::Global; + Index index; + }; + struct Section { + static const SymbolType type = SymbolType::Section; + Index section; + }; + struct Tag { + static const SymbolType type = SymbolType::Tag; + Index index; + }; + struct Table { + static const SymbolType type = SymbolType::Table; + Index index; + }; + + private: + SymbolType type_; + std::string_view name_; + uint8_t flags_; + union { + Function function_; + Data data_; + Global global_; + Section section_; + Tag tag_; + Table table_; + }; + + public: + Symbol(const std::string_view& name, uint8_t flags, const Function& f) + : type_(Function::type), name_(name), flags_(flags), function_(f) {} + Symbol(const std::string_view& name, uint8_t flags, const Data& d) + : type_(Data::type), name_(name), flags_(flags), data_(d) {} + Symbol(const std::string_view& name, uint8_t flags, const Global& g) + : type_(Global::type), name_(name), flags_(flags), global_(g) {} + Symbol(const std::string_view& name, uint8_t flags, const Section& s) + : type_(Section::type), name_(name), flags_(flags), section_(s) {} + Symbol(const std::string_view& name, uint8_t flags, const Tag& e) + : type_(Tag::type), name_(name), flags_(flags), tag_(e) {} + Symbol(const std::string_view& name, uint8_t flags, const Table& t) + : type_(Table::type), name_(name), flags_(flags), table_(t) {} + + SymbolType type() const { return type_; } + const std::string_view& name() const { return name_; } + uint8_t flags() const { return flags_; } + + SymbolVisibility visibility() const { + return static_cast<SymbolVisibility>(flags() & WABT_SYMBOL_MASK_VISIBILITY); + } + SymbolBinding binding() const { + return static_cast<SymbolBinding>(flags() & WABT_SYMBOL_MASK_BINDING); + } + bool undefined() const { return flags() & WABT_SYMBOL_FLAG_UNDEFINED; } + bool defined() const { return !undefined(); } + bool exported() const { return flags() & WABT_SYMBOL_FLAG_EXPORTED; } + bool explicit_name() const { + return flags() & WABT_SYMBOL_FLAG_EXPLICIT_NAME; + } + bool no_strip() const { return flags() & WABT_SYMBOL_FLAG_NO_STRIP; } + + bool IsFunction() const { return type() == Function::type; } + bool IsData() const { return type() == Data::type; } + bool IsGlobal() const { return type() == Global::type; } + bool IsSection() const { return type() == Section::type; } + bool IsTag() const { return type() == Tag::type; } + bool IsTable() const { return type() == Table::type; } + + const Function& AsFunction() const { + assert(IsFunction()); + return function_; + } + const Data& AsData() const { + assert(IsData()); + return data_; + } + const Global& AsGlobal() const { + assert(IsGlobal()); + return global_; + } + const Section& AsSection() const { + assert(IsSection()); + return section_; + } + const Tag& AsTag() const { + assert(IsTag()); + return tag_; + } + const Table& AsTable() const { + assert(IsTable()); + return table_; + } +}; + +class SymbolTable { + WABT_DISALLOW_COPY_AND_ASSIGN(SymbolTable); + + std::vector<Symbol> symbols_; + + std::vector<Index> functions_; + std::vector<Index> tables_; + std::vector<Index> globals_; + + std::set<std::string_view> seen_names_; + + Result EnsureUnique(const std::string_view& name) { + if (seen_names_.count(name)) { + fprintf(stderr, + "error: duplicate symbol when writing relocatable " + "binary: %s\n", + &name[0]); + return Result::Error; + } + seen_names_.insert(name); + return Result::Ok; + }; + + template <typename T> + Result AddSymbol(std::vector<Index>* map, + std::string_view name, + bool imported, + bool exported, + T&& sym) { + uint8_t flags = 0; + if (imported) { + flags |= WABT_SYMBOL_FLAG_UNDEFINED; + // Wabt currently has no way for a user to explicitly specify the name of + // an import, so never set the EXPLICIT_NAME flag, and ignore any display + // name fabricated by wabt. + name = std::string_view(); + } else { + if (name.empty()) { + // Definitions without a name are local. + flags |= uint8_t(SymbolBinding::Local); + flags |= uint8_t(SymbolVisibility::Hidden); + } else { + // Otherwise, strip the dollar off the name; a definition $foo is + // available for linking as "foo". + assert(name[0] == '$'); + name.remove_prefix(1); + } + + if (exported) { + CHECK_RESULT(EnsureUnique(name)); + flags |= uint8_t(SymbolVisibility::Hidden); + flags |= WABT_SYMBOL_FLAG_NO_STRIP; + } + } + if (exported) { + flags |= WABT_SYMBOL_FLAG_EXPORTED; + } + + map->push_back(symbols_.size()); + symbols_.emplace_back(name, flags, sym); + return Result::Ok; + }; + + Index SymbolIndex(const std::vector<Index>& table, Index index) const { + // For well-formed modules, an index into (e.g.) functions_ will always be + // within bounds; the out-of-bounds case here is just to allow --relocatable + // to write known-invalid modules. + return index < table.size() ? table[index] : kInvalidIndex; + } + + public: + SymbolTable() {} + + Result Populate(const Module* module) { + std::set<Index> exported_funcs; + std::set<Index> exported_globals; + std::set<Index> exported_tags; + std::set<Index> exported_tables; + + for (const Export* export_ : module->exports) { + switch (export_->kind) { + case ExternalKind::Func: + exported_funcs.insert(module->GetFuncIndex(export_->var)); + break; + case ExternalKind::Table: + exported_tables.insert(module->GetTableIndex(export_->var)); + break; + case ExternalKind::Memory: + break; + case ExternalKind::Global: + exported_globals.insert(module->GetGlobalIndex(export_->var)); + break; + case ExternalKind::Tag: + exported_tags.insert(module->GetTagIndex(export_->var)); + break; + } + } + + // We currently only create symbol table entries for function, table, and + // global symbols. + for (size_t i = 0; i < module->funcs.size(); ++i) { + const Func* func = module->funcs[i]; + bool imported = i < module->num_func_imports; + bool exported = exported_funcs.count(i); + CHECK_RESULT(AddSymbol(&functions_, func->name, imported, exported, + Symbol::Function{Index(i)})); + } + + for (size_t i = 0; i < module->tables.size(); ++i) { + const Table* table = module->tables[i]; + bool imported = i < module->num_table_imports; + bool exported = exported_tables.count(i); + CHECK_RESULT(AddSymbol(&tables_, table->name, imported, exported, + Symbol::Table{Index(i)})); + } + + for (size_t i = 0; i < module->globals.size(); ++i) { + const Global* global = module->globals[i]; + bool imported = i < module->num_global_imports; + bool exported = exported_globals.count(i); + CHECK_RESULT(AddSymbol(&globals_, global->name, imported, exported, + Symbol::Global{Index(i)})); + } + + return Result::Ok; + } + + const std::vector<Symbol>& symbols() const { return symbols_; } + Index FunctionSymbolIndex(Index index) const { + return SymbolIndex(functions_, index); + } + Index TableSymbolIndex(Index index) const { + return SymbolIndex(tables_, index); + } + Index GlobalSymbolIndex(Index index) const { + return SymbolIndex(globals_, index); + } +}; + +struct CodeMetadata { + Offset offset; + std::vector<uint8_t> data; + CodeMetadata(Offset offset, std::vector<uint8_t> data) + : offset(offset), data(std::move(data)) {} +}; +struct FuncCodeMetadata { + Index func_idx; + std::vector<CodeMetadata> entries; + FuncCodeMetadata(Index func_idx) : func_idx(func_idx) {} +}; +struct CodeMetadataSection { + std::vector<FuncCodeMetadata> entries; +}; +using CodeMetadataSections = + std::unordered_map<std::string_view, CodeMetadataSection>; + +class BinaryWriter { + WABT_DISALLOW_COPY_AND_ASSIGN(BinaryWriter); + + public: + BinaryWriter(Stream*, + const WriteBinaryOptions& options, + const Module* module); + + Result WriteModule(); + + private: + void WriteHeader(const char* name, int index); + Offset WriteU32Leb128Space(Offset leb_size_guess, const char* desc); + Offset WriteFixupU32Leb128Size(Offset offset, + Offset leb_size_guess, + const char* desc); + void BeginKnownSection(BinarySection section_code); + void BeginCustomSection(const char* name); + void WriteSectionHeader(const char* desc, BinarySection section_code); + void EndSection(); + void BeginSubsection(const char* name); + void EndSubsection(); + Index GetLabelVarDepth(const Var* var); + Index GetTagVarDepth(const Var* var); + Index GetLocalIndex(const Func* func, const Var& var); + Index GetSymbolIndex(RelocType reloc_type, Index index); + void AddReloc(RelocType reloc_type, Index index); + void WriteBlockDecl(const BlockDeclaration& decl); + void WriteU32Leb128WithReloc(Index index, + const char* desc, + RelocType reloc_type); + void WriteS32Leb128WithReloc(int32_t value, + const char* desc, + RelocType reloc_type); + void WriteTableNumberWithReloc(Index table_number, const char* desc); + template <typename T> + void WriteLoadStoreExpr(const Func* func, const Expr* expr, const char* desc); + template <typename T> + void WriteSimdLoadStoreLaneExpr(const Func* func, + const Expr* expr, + const char* desc); + void WriteExpr(const Func* func, const Expr* expr); + void WriteExprList(const Func* func, const ExprList& exprs); + void WriteInitExpr(const ExprList& expr); + void WriteFuncLocals(const Func* func, const LocalTypes& local_types); + void WriteFunc(const Func* func); + void WriteTable(const Table* table); + void WriteMemory(const Memory* memory); + void WriteGlobalHeader(const Global* global); + void WriteTagType(const Tag* tag); + void WriteRelocSection(const RelocSection* reloc_section); + void WriteLinkingSection(); + template <typename T> + void WriteNames(const std::vector<T*>& elems, NameSectionSubsection type); + void WriteCodeMetadataSections(); + + Stream* stream_; + const WriteBinaryOptions& options_; + const Module* module_; + + SymbolTable symtab_; + std::vector<RelocSection> reloc_sections_; + RelocSection* current_reloc_section_ = nullptr; + + Index section_count_ = 0; + size_t last_section_offset_ = 0; + size_t last_section_leb_size_guess_ = 0; + BinarySection last_section_type_ = BinarySection::Invalid; + size_t last_section_payload_offset_ = 0; + + size_t last_subsection_offset_ = 0; + size_t last_subsection_leb_size_guess_ = 0; + size_t last_subsection_payload_offset_ = 0; + + // Information about the data count section, so it can be removed if it is + // not needed, and relocs relative to the code section patched up. + size_t code_start_ = 0; + size_t data_count_start_ = 0; + size_t data_count_end_ = 0; + bool has_data_segment_instruction_ = false; + + CodeMetadataSections code_metadata_sections_; + Offset cur_func_start_offset_; + Index cur_func_index_; +}; + +static uint8_t log2_u32(uint32_t x) { + uint8_t result = 0; + while (x > 1) { + x >>= 1; + result++; + } + return result; +} + +BinaryWriter::BinaryWriter(Stream* stream, + const WriteBinaryOptions& options, + const Module* module) + : stream_(stream), options_(options), module_(module) {} + +void BinaryWriter::WriteHeader(const char* name, int index) { + if (stream_->has_log_stream()) { + if (index == PRINT_HEADER_NO_INDEX) { + stream_->log_stream().Writef("; %s\n", name); + } else { + stream_->log_stream().Writef("; %s %d\n", name, index); + } + } +} + +/* returns offset of leb128 */ +Offset BinaryWriter::WriteU32Leb128Space(Offset leb_size_guess, + const char* desc) { + assert(leb_size_guess <= MAX_U32_LEB128_BYTES); + uint8_t data[MAX_U32_LEB128_BYTES] = {0}; + Offset result = stream_->offset(); + Offset bytes_to_write = + options_.canonicalize_lebs ? leb_size_guess : MAX_U32_LEB128_BYTES; + stream_->WriteData(data, bytes_to_write, desc); + return result; +} + +Offset BinaryWriter::WriteFixupU32Leb128Size(Offset offset, + Offset leb_size_guess, + const char* desc) { + if (options_.canonicalize_lebs) { + Offset size = stream_->offset() - offset - leb_size_guess; + Offset leb_size = U32Leb128Length(size); + Offset delta = leb_size - leb_size_guess; + if (delta != 0) { + Offset src_offset = offset + leb_size_guess; + Offset dst_offset = offset + leb_size; + stream_->MoveData(dst_offset, src_offset, size); + } + WriteU32Leb128At(stream_, offset, size, desc); + stream_->AddOffset(delta); + return delta; + } else { + Offset size = stream_->offset() - offset - MAX_U32_LEB128_BYTES; + WriteFixedU32Leb128At(stream_, offset, size, desc); + return 0; + } +} + +void BinaryWriter::WriteBlockDecl(const BlockDeclaration& decl) { + if (decl.sig.GetNumParams() == 0 && decl.sig.GetNumResults() <= 1) { + if (decl.sig.GetNumResults() == 0) { + WriteType(stream_, Type::Void); + } else if (decl.sig.GetNumResults() == 1) { + WriteType(stream_, decl.sig.GetResultType(0)); + } + return; + } + + Index index = decl.has_func_type ? module_->GetFuncTypeIndex(decl.type_var) + : module_->GetFuncTypeIndex(decl.sig); + assert(index != kInvalidIndex); + WriteS32Leb128WithReloc(index, "block type function index", + RelocType::TypeIndexLEB); +} + +void BinaryWriter::WriteSectionHeader(const char* desc, + BinarySection section_code) { + assert(last_section_leb_size_guess_ == 0); + WriteHeader(desc, PRINT_HEADER_NO_INDEX); + stream_->WriteU8Enum(section_code, "section code"); + last_section_type_ = section_code; + last_section_leb_size_guess_ = LEB_SECTION_SIZE_GUESS; + last_section_offset_ = + WriteU32Leb128Space(LEB_SECTION_SIZE_GUESS, "section size (guess)"); + last_section_payload_offset_ = stream_->offset(); +} + +void BinaryWriter::BeginKnownSection(BinarySection section_code) { + char desc[100]; + wabt_snprintf(desc, sizeof(desc), "section \"%s\" (%u)", + GetSectionName(section_code), + static_cast<unsigned>(section_code)); + WriteSectionHeader(desc, section_code); +} + +void BinaryWriter::BeginCustomSection(const char* name) { + char desc[100]; + wabt_snprintf(desc, sizeof(desc), "section \"%s\"", name); + WriteSectionHeader(desc, BinarySection::Custom); + WriteStr(stream_, name, "custom section name", PrintChars::Yes); +} + +void BinaryWriter::EndSection() { + assert(last_section_leb_size_guess_ != 0); + WriteFixupU32Leb128Size(last_section_offset_, last_section_leb_size_guess_, + "FIXUP section size"); + last_section_leb_size_guess_ = 0; + section_count_++; +} + +void BinaryWriter::BeginSubsection(const char* name) { + assert(last_subsection_leb_size_guess_ == 0); + last_subsection_leb_size_guess_ = LEB_SECTION_SIZE_GUESS; + last_subsection_offset_ = + WriteU32Leb128Space(LEB_SECTION_SIZE_GUESS, "subsection size (guess)"); + last_subsection_payload_offset_ = stream_->offset(); +} + +void BinaryWriter::EndSubsection() { + assert(last_subsection_leb_size_guess_ != 0); + WriteFixupU32Leb128Size(last_subsection_offset_, + last_subsection_leb_size_guess_, + "FIXUP subsection size"); + last_subsection_leb_size_guess_ = 0; +} + +Index BinaryWriter::GetLabelVarDepth(const Var* var) { + return var->index(); +} + +Index BinaryWriter::GetTagVarDepth(const Var* var) { + return var->index(); +} + +Index BinaryWriter::GetSymbolIndex(RelocType reloc_type, Index index) { + switch (reloc_type) { + case RelocType::FuncIndexLEB: + return symtab_.FunctionSymbolIndex(index); + case RelocType::TableNumberLEB: + return symtab_.TableSymbolIndex(index); + case RelocType::GlobalIndexLEB: + return symtab_.GlobalSymbolIndex(index); + case RelocType::TypeIndexLEB: + // Type indexes don't create entries in the symbol table; instead their + // index is used directly. + return index; + default: + fprintf(stderr, "warning: unsupported relocation type: %s\n", + GetRelocTypeName(reloc_type)); + return kInvalidIndex; + } +} + +void BinaryWriter::AddReloc(RelocType reloc_type, Index index) { + // Add a new reloc section if needed + if (!current_reloc_section_ || + current_reloc_section_->section_index != section_count_) { + reloc_sections_.emplace_back(GetSectionName(last_section_type_), + section_count_); + current_reloc_section_ = &reloc_sections_.back(); + } + + // Add a new relocation to the curent reloc section + size_t offset = stream_->offset() - last_section_payload_offset_; + Index symbol_index = GetSymbolIndex(reloc_type, index); + if (symbol_index == kInvalidIndex) { + // The file is invalid, for example a reference to function 42 where only 10 + // functions are defined. The user must have already passed --no-check, so + // no extra warning here is needed. + return; + } + current_reloc_section_->relocations.emplace_back(reloc_type, offset, + symbol_index); +} + +void BinaryWriter::WriteU32Leb128WithReloc(Index index, + const char* desc, + RelocType reloc_type) { + if (options_.relocatable) { + AddReloc(reloc_type, index); + WriteFixedU32Leb128(stream_, index, desc); + } else { + WriteU32Leb128(stream_, index, desc); + } +} + +void BinaryWriter::WriteS32Leb128WithReloc(int32_t value, + const char* desc, + RelocType reloc_type) { + if (options_.relocatable) { + AddReloc(reloc_type, value); + WriteFixedS32Leb128(stream_, value, desc); + } else { + WriteS32Leb128(stream_, value, desc); + } +} + +void BinaryWriter::WriteTableNumberWithReloc(Index value, const char* desc) { + // Unless reference types are enabled, all references to tables refer to table + // 0, so no relocs need be emitted when making relocatable binaries. + if (options_.relocatable && options_.features.reference_types_enabled()) { + AddReloc(RelocType::TableNumberLEB, value); + WriteFixedS32Leb128(stream_, value, desc); + } else { + WriteS32Leb128(stream_, value, desc); + } +} + +Index BinaryWriter::GetLocalIndex(const Func* func, const Var& var) { + // func can be nullptr when using local.get/local.set/local.tee in an + // init_expr. + if (func) { + return func->GetLocalIndex(var); + } else if (var.is_index()) { + return var.index(); + } else { + return kInvalidIndex; + } +} + +// TODO(binji): Rename this, it is used for more than loads/stores now. +template <typename T> +void BinaryWriter::WriteLoadStoreExpr(const Func* func, + const Expr* expr, + const char* desc) { + auto* typed_expr = cast<T>(expr); + WriteOpcode(stream_, typed_expr->opcode); + Address align = typed_expr->opcode.GetAlignment(typed_expr->align); + Index memidx = module_->GetMemoryIndex(typed_expr->memidx); + if (memidx != 0) { + stream_->WriteU8(log2_u32(align) | (1 << 6), "alignment"); + WriteU32Leb128(stream_, memidx, "memidx"); + } else { + stream_->WriteU8(log2_u32(align), "alignment"); + } + WriteU32Leb128(stream_, typed_expr->offset, desc); +} + +template <typename T> +void BinaryWriter::WriteSimdLoadStoreLaneExpr(const Func* func, + const Expr* expr, + const char* desc) { + WriteLoadStoreExpr<T>(func, expr, desc); + auto* typed_expr = cast<T>(expr); + stream_->WriteU8(static_cast<uint8_t>(typed_expr->val), "Simd Lane literal"); +} + +void BinaryWriter::WriteExpr(const Func* func, const Expr* expr) { + switch (expr->type()) { + case ExprType::AtomicLoad: + WriteLoadStoreExpr<AtomicLoadExpr>(func, expr, "memory offset"); + break; + case ExprType::AtomicRmw: + WriteLoadStoreExpr<AtomicRmwExpr>(func, expr, "memory offset"); + break; + case ExprType::AtomicRmwCmpxchg: + WriteLoadStoreExpr<AtomicRmwCmpxchgExpr>(func, expr, "memory offset"); + break; + case ExprType::AtomicStore: + WriteLoadStoreExpr<AtomicStoreExpr>(func, expr, "memory offset"); + break; + case ExprType::AtomicWait: + WriteLoadStoreExpr<AtomicWaitExpr>(func, expr, "memory offset"); + break; + case ExprType::AtomicFence: { + auto* fence_expr = cast<AtomicFenceExpr>(expr); + WriteOpcode(stream_, Opcode::AtomicFence); + WriteU32Leb128(stream_, fence_expr->consistency_model, + "consistency model"); + break; + } + case ExprType::AtomicNotify: + WriteLoadStoreExpr<AtomicNotifyExpr>(func, expr, "memory offset"); + break; + case ExprType::Binary: + WriteOpcode(stream_, cast<BinaryExpr>(expr)->opcode); + break; + case ExprType::Block: + WriteOpcode(stream_, Opcode::Block); + WriteBlockDecl(cast<BlockExpr>(expr)->block.decl); + WriteExprList(func, cast<BlockExpr>(expr)->block.exprs); + WriteOpcode(stream_, Opcode::End); + break; + case ExprType::Br: + WriteOpcode(stream_, Opcode::Br); + WriteU32Leb128(stream_, GetLabelVarDepth(&cast<BrExpr>(expr)->var), + "break depth"); + break; + case ExprType::BrIf: + WriteOpcode(stream_, Opcode::BrIf); + WriteU32Leb128(stream_, GetLabelVarDepth(&cast<BrIfExpr>(expr)->var), + "break depth"); + break; + case ExprType::BrTable: { + auto* br_table_expr = cast<BrTableExpr>(expr); + WriteOpcode(stream_, Opcode::BrTable); + WriteU32Leb128(stream_, br_table_expr->targets.size(), "num targets"); + Index depth; + for (const Var& var : br_table_expr->targets) { + depth = GetLabelVarDepth(&var); + WriteU32Leb128(stream_, depth, "break depth"); + } + depth = GetLabelVarDepth(&br_table_expr->default_target); + WriteU32Leb128(stream_, depth, "break depth for default"); + break; + } + case ExprType::Call: { + Index index = module_->GetFuncIndex(cast<CallExpr>(expr)->var); + WriteOpcode(stream_, Opcode::Call); + WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB); + break; + } + case ExprType::ReturnCall: { + Index index = module_->GetFuncIndex(cast<ReturnCallExpr>(expr)->var); + WriteOpcode(stream_, Opcode::ReturnCall); + WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB); + break; + } + case ExprType::CallIndirect: { + Index sig_index = + module_->GetFuncTypeIndex(cast<CallIndirectExpr>(expr)->decl); + Index table_index = + module_->GetTableIndex(cast<CallIndirectExpr>(expr)->table); + WriteOpcode(stream_, Opcode::CallIndirect); + WriteU32Leb128WithReloc(sig_index, "signature index", + RelocType::TypeIndexLEB); + WriteTableNumberWithReloc(table_index, "table index"); + break; + } + case ExprType::CallRef: { + WriteOpcode(stream_, Opcode::CallRef); + break; + } + case ExprType::ReturnCallIndirect: { + Index sig_index = + module_->GetFuncTypeIndex(cast<ReturnCallIndirectExpr>(expr)->decl); + Index table_index = + module_->GetTableIndex(cast<ReturnCallIndirectExpr>(expr)->table); + WriteOpcode(stream_, Opcode::ReturnCallIndirect); + WriteU32Leb128WithReloc(sig_index, "signature index", + RelocType::TypeIndexLEB); + WriteTableNumberWithReloc(table_index, "table index"); + break; + } + case ExprType::Compare: + WriteOpcode(stream_, cast<CompareExpr>(expr)->opcode); + break; + case ExprType::Const: { + const Const& const_ = cast<ConstExpr>(expr)->const_; + switch (const_.type()) { + case Type::I32: { + WriteOpcode(stream_, Opcode::I32Const); + WriteS32Leb128(stream_, const_.u32(), "i32 literal"); + break; + } + case Type::I64: + WriteOpcode(stream_, Opcode::I64Const); + WriteS64Leb128(stream_, const_.u64(), "i64 literal"); + break; + case Type::F32: + WriteOpcode(stream_, Opcode::F32Const); + stream_->WriteU32(const_.f32_bits(), "f32 literal"); + break; + case Type::F64: + WriteOpcode(stream_, Opcode::F64Const); + stream_->WriteU64(const_.f64_bits(), "f64 literal"); + break; + case Type::V128: + WriteOpcode(stream_, Opcode::V128Const); + stream_->WriteU128(const_.vec128(), "v128 literal"); + break; + default: + assert(0); + } + break; + } + case ExprType::Convert: + WriteOpcode(stream_, cast<ConvertExpr>(expr)->opcode); + break; + case ExprType::Drop: + WriteOpcode(stream_, Opcode::Drop); + break; + case ExprType::GlobalGet: { + Index index = module_->GetGlobalIndex(cast<GlobalGetExpr>(expr)->var); + WriteOpcode(stream_, Opcode::GlobalGet); + WriteU32Leb128WithReloc(index, "global index", RelocType::GlobalIndexLEB); + break; + } + case ExprType::GlobalSet: { + Index index = module_->GetGlobalIndex(cast<GlobalSetExpr>(expr)->var); + WriteOpcode(stream_, Opcode::GlobalSet); + WriteU32Leb128WithReloc(index, "global index", RelocType::GlobalIndexLEB); + break; + } + case ExprType::If: { + auto* if_expr = cast<IfExpr>(expr); + WriteOpcode(stream_, Opcode::If); + WriteBlockDecl(if_expr->true_.decl); + WriteExprList(func, if_expr->true_.exprs); + if (!if_expr->false_.empty()) { + WriteOpcode(stream_, Opcode::Else); + WriteExprList(func, if_expr->false_); + } + WriteOpcode(stream_, Opcode::End); + break; + } + case ExprType::Load: + WriteLoadStoreExpr<LoadExpr>(func, expr, "load offset"); + break; + case ExprType::LocalGet: { + Index index = GetLocalIndex(func, cast<LocalGetExpr>(expr)->var); + WriteOpcode(stream_, Opcode::LocalGet); + WriteU32Leb128(stream_, index, "local index"); + break; + } + case ExprType::LocalSet: { + Index index = GetLocalIndex(func, cast<LocalSetExpr>(expr)->var); + WriteOpcode(stream_, Opcode::LocalSet); + WriteU32Leb128(stream_, index, "local index"); + break; + } + case ExprType::LocalTee: { + Index index = GetLocalIndex(func, cast<LocalTeeExpr>(expr)->var); + WriteOpcode(stream_, Opcode::LocalTee); + WriteU32Leb128(stream_, index, "local index"); + break; + } + case ExprType::Loop: + WriteOpcode(stream_, Opcode::Loop); + WriteBlockDecl(cast<LoopExpr>(expr)->block.decl); + WriteExprList(func, cast<LoopExpr>(expr)->block.exprs); + WriteOpcode(stream_, Opcode::End); + break; + case ExprType::MemoryCopy: { + Index srcmemidx = + module_->GetMemoryIndex(cast<MemoryCopyExpr>(expr)->srcmemidx); + Index destmemidx = + module_->GetMemoryIndex(cast<MemoryCopyExpr>(expr)->destmemidx); + WriteOpcode(stream_, Opcode::MemoryCopy); + WriteU32Leb128(stream_, srcmemidx, "memory.copy srcmemidx"); + WriteU32Leb128(stream_, destmemidx, "memory.copy destmemidx"); + break; + } + case ExprType::DataDrop: { + Index index = module_->GetDataSegmentIndex(cast<DataDropExpr>(expr)->var); + WriteOpcode(stream_, Opcode::DataDrop); + WriteU32Leb128(stream_, index, "data.drop segment"); + has_data_segment_instruction_ = true; + break; + } + case ExprType::MemoryFill: { + Index memidx = + module_->GetMemoryIndex(cast<MemoryFillExpr>(expr)->memidx); + WriteOpcode(stream_, Opcode::MemoryFill); + WriteU32Leb128(stream_, memidx, "memory.fill memidx"); + break; + } + case ExprType::MemoryGrow: { + Index memidx = + module_->GetMemoryIndex(cast<MemoryGrowExpr>(expr)->memidx); + WriteOpcode(stream_, Opcode::MemoryGrow); + WriteU32Leb128(stream_, memidx, "memory.grow memidx"); + break; + } + case ExprType::MemoryInit: { + Index index = + module_->GetDataSegmentIndex(cast<MemoryInitExpr>(expr)->var); + Index memidx = + module_->GetMemoryIndex(cast<MemoryInitExpr>(expr)->memidx); + WriteOpcode(stream_, Opcode::MemoryInit); + WriteU32Leb128(stream_, index, "memory.init segment"); + WriteU32Leb128(stream_, memidx, "memory.init memidx"); + has_data_segment_instruction_ = true; + break; + } + case ExprType::MemorySize: { + Index memidx = + module_->GetMemoryIndex(cast<MemorySizeExpr>(expr)->memidx); + WriteOpcode(stream_, Opcode::MemorySize); + WriteU32Leb128(stream_, memidx, "memory.size memidx"); + break; + } + case ExprType::TableCopy: { + auto* copy_expr = cast<TableCopyExpr>(expr); + Index dst = module_->GetTableIndex(copy_expr->dst_table); + Index src = module_->GetTableIndex(copy_expr->src_table); + WriteOpcode(stream_, Opcode::TableCopy); + WriteTableNumberWithReloc(dst, "table.copy dst_table"); + WriteTableNumberWithReloc(src, "table.copy src_table"); + break; + } + case ExprType::ElemDrop: { + Index index = module_->GetElemSegmentIndex(cast<ElemDropExpr>(expr)->var); + WriteOpcode(stream_, Opcode::ElemDrop); + WriteU32Leb128(stream_, index, "elem.drop segment"); + break; + } + case ExprType::TableInit: { + auto* init_expr = cast<TableInitExpr>(expr); + Index table_index = module_->GetTableIndex(init_expr->table_index); + Index segment_index = + module_->GetElemSegmentIndex(init_expr->segment_index); + WriteOpcode(stream_, Opcode::TableInit); + WriteU32Leb128(stream_, segment_index, "table.init segment"); + WriteTableNumberWithReloc(table_index, "table.init table"); + break; + } + case ExprType::TableGet: { + Index index = module_->GetTableIndex(cast<TableGetExpr>(expr)->var); + WriteOpcode(stream_, Opcode::TableGet); + WriteTableNumberWithReloc(index, "table.get table index"); + break; + } + case ExprType::TableSet: { + Index index = module_->GetTableIndex(cast<TableSetExpr>(expr)->var); + WriteOpcode(stream_, Opcode::TableSet); + WriteTableNumberWithReloc(index, "table.set table index"); + break; + } + case ExprType::TableGrow: { + Index index = module_->GetTableIndex(cast<TableGrowExpr>(expr)->var); + WriteOpcode(stream_, Opcode::TableGrow); + WriteTableNumberWithReloc(index, "table.grow table index"); + break; + } + case ExprType::TableSize: { + Index index = module_->GetTableIndex(cast<TableSizeExpr>(expr)->var); + WriteOpcode(stream_, Opcode::TableSize); + WriteTableNumberWithReloc(index, "table.size table index"); + break; + } + case ExprType::TableFill: { + Index index = module_->GetTableIndex(cast<TableFillExpr>(expr)->var); + WriteOpcode(stream_, Opcode::TableFill); + WriteTableNumberWithReloc(index, "table.fill table index"); + break; + } + case ExprType::RefFunc: { + WriteOpcode(stream_, Opcode::RefFunc); + Index index = module_->GetFuncIndex(cast<RefFuncExpr>(expr)->var); + WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB); + break; + } + case ExprType::RefNull: { + WriteOpcode(stream_, Opcode::RefNull); + WriteType(stream_, cast<RefNullExpr>(expr)->type, "ref.null type"); + break; + } + case ExprType::RefIsNull: + WriteOpcode(stream_, Opcode::RefIsNull); + break; + case ExprType::Nop: + WriteOpcode(stream_, Opcode::Nop); + break; + case ExprType::Rethrow: + WriteOpcode(stream_, Opcode::Rethrow); + WriteU32Leb128(stream_, GetLabelVarDepth(&cast<RethrowExpr>(expr)->var), + "rethrow depth"); + break; + case ExprType::Return: + WriteOpcode(stream_, Opcode::Return); + break; + case ExprType::Select: { + auto* select_expr = cast<SelectExpr>(expr); + if (select_expr->result_type.empty()) { + WriteOpcode(stream_, Opcode::Select); + } else { + WriteOpcode(stream_, Opcode::SelectT); + WriteU32Leb128(stream_, select_expr->result_type.size(), + "num result types"); + for (Type t : select_expr->result_type) { + WriteType(stream_, t, "result type"); + } + } + break; + } + case ExprType::Store: + WriteLoadStoreExpr<StoreExpr>(func, expr, "store offset"); + break; + case ExprType::Throw: + WriteOpcode(stream_, Opcode::Throw); + WriteU32Leb128(stream_, GetTagVarDepth(&cast<ThrowExpr>(expr)->var), + "throw tag"); + break; + case ExprType::Try: { + auto* try_expr = cast<TryExpr>(expr); + WriteOpcode(stream_, Opcode::Try); + WriteBlockDecl(try_expr->block.decl); + WriteExprList(func, try_expr->block.exprs); + switch (try_expr->kind) { + case TryKind::Catch: + for (const Catch& catch_ : try_expr->catches) { + if (catch_.IsCatchAll()) { + WriteOpcode(stream_, Opcode::CatchAll); + } else { + WriteOpcode(stream_, Opcode::Catch); + WriteU32Leb128(stream_, GetTagVarDepth(&catch_.var), "catch tag"); + } + WriteExprList(func, catch_.exprs); + } + WriteOpcode(stream_, Opcode::End); + break; + case TryKind::Delegate: + WriteOpcode(stream_, Opcode::Delegate); + WriteU32Leb128(stream_, GetLabelVarDepth(&try_expr->delegate_target), + "delegate depth"); + break; + case TryKind::Plain: + WriteOpcode(stream_, Opcode::End); + break; + } + break; + } + case ExprType::Unary: + WriteOpcode(stream_, cast<UnaryExpr>(expr)->opcode); + break; + case ExprType::Ternary: + WriteOpcode(stream_, cast<TernaryExpr>(expr)->opcode); + break; + case ExprType::SimdLaneOp: { + const Opcode opcode = cast<SimdLaneOpExpr>(expr)->opcode; + WriteOpcode(stream_, opcode); + stream_->WriteU8(static_cast<uint8_t>(cast<SimdLaneOpExpr>(expr)->val), + "Simd Lane literal"); + break; + } + case ExprType::SimdLoadLane: { + WriteSimdLoadStoreLaneExpr<SimdLoadLaneExpr>(func, expr, "load offset"); + break; + } + case ExprType::SimdStoreLane: { + WriteSimdLoadStoreLaneExpr<SimdStoreLaneExpr>(func, expr, "store offset"); + break; + } + case ExprType::SimdShuffleOp: { + const Opcode opcode = cast<SimdShuffleOpExpr>(expr)->opcode; + WriteOpcode(stream_, opcode); + stream_->WriteU128(cast<SimdShuffleOpExpr>(expr)->val, + "Simd Lane[16] literal"); + break; + } + case ExprType::LoadSplat: + WriteLoadStoreExpr<LoadSplatExpr>(func, expr, "load offset"); + break; + case ExprType::LoadZero: + WriteLoadStoreExpr<LoadZeroExpr>(func, expr, "load offset"); + break; + case ExprType::Unreachable: + WriteOpcode(stream_, Opcode::Unreachable); + break; + case ExprType::CodeMetadata: { + auto* meta_expr = cast<CodeMetadataExpr>(expr); + auto& s = code_metadata_sections_[meta_expr->name]; + if (s.entries.empty() || s.entries.back().func_idx != cur_func_index_) { + s.entries.emplace_back(cur_func_index_); + } + auto& a = s.entries.back(); + Offset code_offset = stream_->offset() - cur_func_start_offset_; + a.entries.emplace_back(code_offset, meta_expr->data); + break; + } + } +} + +void BinaryWriter::WriteExprList(const Func* func, const ExprList& exprs) { + for (const Expr& expr : exprs) { + WriteExpr(func, &expr); + } +} + +void BinaryWriter::WriteInitExpr(const ExprList& expr) { + WriteExprList(nullptr, expr); + WriteOpcode(stream_, Opcode::End); +} + +void BinaryWriter::WriteFuncLocals(const Func* func, + const LocalTypes& local_types) { + if (local_types.size() == 0) { + WriteU32Leb128(stream_, 0, "local decl count"); + return; + } + + Index local_decl_count = local_types.decls().size(); + WriteU32Leb128(stream_, local_decl_count, "local decl count"); + for (auto decl : local_types.decls()) { + WriteU32Leb128(stream_, decl.second, "local type count"); + WriteType(stream_, decl.first); + } +} + +void BinaryWriter::WriteFunc(const Func* func) { + WriteFuncLocals(func, func->local_types); + WriteExprList(func, func->exprs); + WriteOpcode(stream_, Opcode::End); +} + +void BinaryWriter::WriteTable(const Table* table) { + WriteType(stream_, table->elem_type); + WriteLimits(stream_, &table->elem_limits); +} + +void BinaryWriter::WriteMemory(const Memory* memory) { + WriteLimits(stream_, &memory->page_limits); +} + +void BinaryWriter::WriteGlobalHeader(const Global* global) { + WriteType(stream_, global->type); + stream_->WriteU8(global->mutable_, "global mutability"); +} + +void BinaryWriter::WriteTagType(const Tag* tag) { + stream_->WriteU8(0, "tag attribute"); + WriteU32Leb128(stream_, module_->GetFuncTypeIndex(tag->decl), + "tag signature index"); +} + +void BinaryWriter::WriteRelocSection(const RelocSection* reloc_section) { + char section_name[128]; + wabt_snprintf(section_name, sizeof(section_name), "%s.%s", + WABT_BINARY_SECTION_RELOC, reloc_section->name); + BeginCustomSection(section_name); + WriteU32Leb128(stream_, reloc_section->section_index, "reloc section index"); + const std::vector<Reloc>& relocs = reloc_section->relocations; + WriteU32Leb128(stream_, relocs.size(), "num relocs"); + + for (const Reloc& reloc : relocs) { + WriteU32Leb128(stream_, reloc.type, "reloc type"); + WriteU32Leb128(stream_, reloc.offset, "reloc offset"); + WriteU32Leb128(stream_, reloc.index, "reloc index"); + switch (reloc.type) { + case RelocType::MemoryAddressLEB: + case RelocType::MemoryAddressLEB64: + case RelocType::MemoryAddressSLEB: + case RelocType::MemoryAddressSLEB64: + case RelocType::MemoryAddressRelSLEB: + case RelocType::MemoryAddressRelSLEB64: + case RelocType::MemoryAddressI32: + case RelocType::MemoryAddressI64: + case RelocType::FunctionOffsetI32: + case RelocType::SectionOffsetI32: + case RelocType::MemoryAddressTLSSLEB: + case RelocType::MemoryAddressTLSI32: + WriteU32Leb128(stream_, reloc.addend, "reloc addend"); + break; + case RelocType::FuncIndexLEB: + case RelocType::TableIndexSLEB: + case RelocType::TableIndexSLEB64: + case RelocType::TableIndexI32: + case RelocType::TableIndexI64: + case RelocType::TypeIndexLEB: + case RelocType::GlobalIndexLEB: + case RelocType::TagIndexLEB: + case RelocType::TableIndexRelSLEB: + case RelocType::TableNumberLEB: + break; + default: + fprintf(stderr, "warning: unsupported relocation type: %s\n", + GetRelocTypeName(reloc.type)); + } + } + + EndSection(); +} + +void BinaryWriter::WriteLinkingSection() { + BeginCustomSection(WABT_BINARY_SECTION_LINKING); + WriteU32Leb128(stream_, 2, "metadata version"); + const std::vector<Symbol>& symbols = symtab_.symbols(); + if (symbols.size()) { + stream_->WriteU8Enum(LinkingEntryType::SymbolTable, "symbol table"); + BeginSubsection("symbol table"); + WriteU32Leb128(stream_, symbols.size(), "num symbols"); + + for (const Symbol& sym : symbols) { + stream_->WriteU8Enum(sym.type(), "symbol type"); + WriteU32Leb128(stream_, sym.flags(), "symbol flags"); + switch (sym.type()) { + case SymbolType::Function: + WriteU32Leb128(stream_, sym.AsFunction().index, "function index"); + if (sym.defined() || sym.explicit_name()) { + WriteStr(stream_, sym.name(), "function name", PrintChars::Yes); + } + break; + case SymbolType::Data: + WriteStr(stream_, sym.name(), "data name", PrintChars::Yes); + if (sym.defined()) { + WriteU32Leb128(stream_, sym.AsData().index, "data index"); + WriteU32Leb128(stream_, sym.AsData().offset, "data offset"); + WriteU32Leb128(stream_, sym.AsData().size, "data size"); + } + break; + case SymbolType::Global: + WriteU32Leb128(stream_, sym.AsGlobal().index, "global index"); + if (sym.defined() || sym.explicit_name()) { + WriteStr(stream_, sym.name(), "global name", PrintChars::Yes); + } + break; + case SymbolType::Section: + WriteU32Leb128(stream_, sym.AsSection().section, "section index"); + break; + case SymbolType::Tag: + WriteU32Leb128(stream_, sym.AsTag().index, "tag index"); + if (sym.defined() || sym.explicit_name()) { + WriteStr(stream_, sym.name(), "tag name", PrintChars::Yes); + } + break; + case SymbolType::Table: + WriteU32Leb128(stream_, sym.AsTable().index, "table index"); + if (sym.defined() || sym.explicit_name()) { + WriteStr(stream_, sym.name(), "table name", PrintChars::Yes); + } + break; + } + } + EndSubsection(); + } + EndSection(); +} + +template <typename T> +void BinaryWriter::WriteNames(const std::vector<T*>& elems, + NameSectionSubsection type) { + size_t num_named_elems = 0; + for (const T* elem : elems) { + if (!elem->name.empty()) { + num_named_elems++; + } + } + + if (!num_named_elems) { + return; + } + + WriteU32Leb128(stream_, type, "name subsection type"); + BeginSubsection("name subsection"); + + char desc[100]; + WriteU32Leb128(stream_, num_named_elems, "num names"); + for (size_t i = 0; i < elems.size(); ++i) { + const T* elem = elems[i]; + if (elem->name.empty()) { + continue; + } + WriteU32Leb128(stream_, i, "elem index"); + wabt_snprintf(desc, sizeof(desc), "elem name %" PRIzd, i); + WriteDebugName(stream_, elem->name, desc); + } + EndSubsection(); +} + +Result BinaryWriter::WriteModule() { + stream_->WriteU32(WABT_BINARY_MAGIC, "WASM_BINARY_MAGIC"); + stream_->WriteU32(WABT_BINARY_VERSION, "WASM_BINARY_VERSION"); + + if (options_.relocatable) { + CHECK_RESULT(symtab_.Populate(module_)); + } + + if (module_->types.size()) { + BeginKnownSection(BinarySection::Type); + WriteU32Leb128(stream_, module_->types.size(), "num types"); + for (size_t i = 0; i < module_->types.size(); ++i) { + const TypeEntry* type = module_->types[i]; + switch (type->kind()) { + case TypeEntryKind::Func: { + const FuncType* func_type = cast<FuncType>(type); + const FuncSignature* sig = &func_type->sig; + WriteHeader("func type", i); + WriteType(stream_, Type::Func); + + Index num_params = sig->param_types.size(); + Index num_results = sig->result_types.size(); + WriteU32Leb128(stream_, num_params, "num params"); + for (size_t j = 0; j < num_params; ++j) { + WriteType(stream_, sig->param_types[j]); + } + + WriteU32Leb128(stream_, num_results, "num results"); + for (size_t j = 0; j < num_results; ++j) { + WriteType(stream_, sig->result_types[j]); + } + break; + } + + case TypeEntryKind::Struct: { + const StructType* struct_type = cast<StructType>(type); + WriteHeader("struct type", i); + WriteType(stream_, Type::Struct); + Index num_fields = struct_type->fields.size(); + WriteU32Leb128(stream_, num_fields, "num fields"); + for (size_t j = 0; j < num_fields; ++j) { + const Field& field = struct_type->fields[j]; + WriteType(stream_, field.type); + stream_->WriteU8(field.mutable_, "field mutability"); + } + break; + } + + case TypeEntryKind::Array: { + const ArrayType* array_type = cast<ArrayType>(type); + WriteHeader("array type", i); + WriteType(stream_, Type::Array); + WriteType(stream_, array_type->field.type); + stream_->WriteU8(array_type->field.mutable_, "field mutability"); + break; + } + } + } + EndSection(); + } + + if (module_->imports.size()) { + BeginKnownSection(BinarySection::Import); + WriteU32Leb128(stream_, module_->imports.size(), "num imports"); + + for (size_t i = 0; i < module_->imports.size(); ++i) { + const Import* import = module_->imports[i]; + WriteHeader("import header", i); + WriteStr(stream_, import->module_name, "import module name", + PrintChars::Yes); + WriteStr(stream_, import->field_name, "import field name", + PrintChars::Yes); + stream_->WriteU8Enum(import->kind(), "import kind"); + switch (import->kind()) { + case ExternalKind::Func: + WriteU32Leb128( + stream_, + module_->GetFuncTypeIndex(cast<FuncImport>(import)->func.decl), + "import signature index"); + break; + + case ExternalKind::Table: + WriteTable(&cast<TableImport>(import)->table); + break; + + case ExternalKind::Memory: + WriteMemory(&cast<MemoryImport>(import)->memory); + break; + + case ExternalKind::Global: + WriteGlobalHeader(&cast<GlobalImport>(import)->global); + break; + + case ExternalKind::Tag: + WriteTagType(&cast<TagImport>(import)->tag); + break; + } + } + EndSection(); + } + + assert(module_->funcs.size() >= module_->num_func_imports); + Index num_funcs = module_->funcs.size() - module_->num_func_imports; + if (num_funcs) { + BeginKnownSection(BinarySection::Function); + WriteU32Leb128(stream_, num_funcs, "num functions"); + + for (size_t i = 0; i < num_funcs; ++i) { + const Func* func = module_->funcs[i + module_->num_func_imports]; + char desc[100]; + wabt_snprintf(desc, sizeof(desc), "function %" PRIzd " signature index", + i); + WriteU32Leb128(stream_, module_->GetFuncTypeIndex(func->decl), desc); + } + EndSection(); + } + + assert(module_->tables.size() >= module_->num_table_imports); + Index num_tables = module_->tables.size() - module_->num_table_imports; + if (num_tables) { + BeginKnownSection(BinarySection::Table); + WriteU32Leb128(stream_, num_tables, "num tables"); + for (size_t i = 0; i < num_tables; ++i) { + const Table* table = module_->tables[i + module_->num_table_imports]; + WriteHeader("table", i); + WriteTable(table); + } + EndSection(); + } + + assert(module_->memories.size() >= module_->num_memory_imports); + Index num_memories = module_->memories.size() - module_->num_memory_imports; + if (num_memories) { + BeginKnownSection(BinarySection::Memory); + WriteU32Leb128(stream_, num_memories, "num memories"); + for (size_t i = 0; i < num_memories; ++i) { + const Memory* memory = module_->memories[i + module_->num_memory_imports]; + WriteHeader("memory", i); + WriteMemory(memory); + } + EndSection(); + } + + assert(module_->tags.size() >= module_->num_tag_imports); + Index num_tags = module_->tags.size() - module_->num_tag_imports; + if (num_tags) { + BeginKnownSection(BinarySection::Tag); + WriteU32Leb128(stream_, num_tags, "tag count"); + for (size_t i = 0; i < num_tags; ++i) { + WriteHeader("tag", i); + const Tag* tag = module_->tags[i + module_->num_tag_imports]; + WriteTagType(tag); + } + EndSection(); + } + + assert(module_->globals.size() >= module_->num_global_imports); + Index num_globals = module_->globals.size() - module_->num_global_imports; + if (num_globals) { + BeginKnownSection(BinarySection::Global); + WriteU32Leb128(stream_, num_globals, "num globals"); + + for (size_t i = 0; i < num_globals; ++i) { + const Global* global = module_->globals[i + module_->num_global_imports]; + WriteGlobalHeader(global); + WriteInitExpr(global->init_expr); + } + EndSection(); + } + + if (module_->exports.size()) { + BeginKnownSection(BinarySection::Export); + WriteU32Leb128(stream_, module_->exports.size(), "num exports"); + + for (const Export* export_ : module_->exports) { + WriteStr(stream_, export_->name, "export name", PrintChars::Yes); + stream_->WriteU8Enum(export_->kind, "export kind"); + switch (export_->kind) { + case ExternalKind::Func: { + Index index = module_->GetFuncIndex(export_->var); + WriteU32Leb128(stream_, index, "export func index"); + break; + } + case ExternalKind::Table: { + Index index = module_->GetTableIndex(export_->var); + WriteU32Leb128(stream_, index, "export table index"); + break; + } + case ExternalKind::Memory: { + Index index = module_->GetMemoryIndex(export_->var); + WriteU32Leb128(stream_, index, "export memory index"); + break; + } + case ExternalKind::Global: { + Index index = module_->GetGlobalIndex(export_->var); + WriteU32Leb128(stream_, index, "export global index"); + break; + } + case ExternalKind::Tag: { + Index index = module_->GetTagIndex(export_->var); + WriteU32Leb128(stream_, index, "export tag index"); + break; + } + } + } + EndSection(); + } + + if (module_->starts.size()) { + Index start_func_index = module_->GetFuncIndex(*module_->starts[0]); + if (start_func_index != kInvalidIndex) { + BeginKnownSection(BinarySection::Start); + WriteU32Leb128(stream_, start_func_index, "start func index"); + EndSection(); + } + } + + if (module_->elem_segments.size()) { + BeginKnownSection(BinarySection::Elem); + WriteU32Leb128(stream_, module_->elem_segments.size(), "num elem segments"); + for (size_t i = 0; i < module_->elem_segments.size(); ++i) { + ElemSegment* segment = module_->elem_segments[i]; + WriteHeader("elem segment header", i); + // 1. flags + uint8_t flags = segment->GetFlags(module_); + stream_->WriteU8(flags, "segment flags"); + // 2. optional target table + if (flags & SegExplicitIndex && segment->kind != SegmentKind::Declared) { + WriteU32Leb128(stream_, module_->GetTableIndex(segment->table_var), + "table index"); + } + // 3. optional target location within the table (active segments only) + if (!(flags & SegPassive)) { + WriteInitExpr(segment->offset); + } + // 4. type of item in the following list (omitted for "legacy" segments) + if (flags & (SegPassive | SegExplicitIndex)) { + if (flags & SegUseElemExprs) { + WriteType(stream_, segment->elem_type, "elem expr list type"); + } else { + stream_->WriteU8Enum(ExternalKind::Func, "elem list type"); + } + } + // 5. actual list of elements (with extern indexes or elem expr's) + // preceeded by length + WriteU32Leb128(stream_, segment->elem_exprs.size(), "num elems"); + if (flags & SegUseElemExprs) { + for (const ExprList& elem_expr : segment->elem_exprs) { + WriteInitExpr(elem_expr); + } + } else { + for (const ExprList& elem_expr : segment->elem_exprs) { + assert(elem_expr.size() == 1); + const Expr* expr = &elem_expr.front(); + assert(expr->type() == ExprType::RefFunc); + WriteU32Leb128(stream_, + module_->GetFuncIndex(cast<RefFuncExpr>(expr)->var), + "elem function index"); + } + } + } + EndSection(); + } + + if (options_.features.bulk_memory_enabled() && + module_->data_segments.size()) { + // Keep track of the data count section offset so it can be removed if + // it isn't needed. + data_count_start_ = stream_->offset(); + BeginKnownSection(BinarySection::DataCount); + WriteU32Leb128(stream_, module_->data_segments.size(), "data count"); + EndSection(); + data_count_end_ = stream_->offset(); + } + + if (num_funcs) { + code_start_ = stream_->offset(); + BeginKnownSection(BinarySection::Code); + WriteU32Leb128(stream_, num_funcs, "num functions"); + + for (size_t i = 0; i < num_funcs; ++i) { + cur_func_index_ = i + module_->num_func_imports; + WriteHeader("function body", i); + const Func* func = module_->funcs[cur_func_index_]; + + /* TODO(binji): better guess of the size of the function body section */ + const Offset leb_size_guess = 1; + Offset body_size_offset = + WriteU32Leb128Space(leb_size_guess, "func body size (guess)"); + cur_func_start_offset_ = stream_->offset(); + WriteFunc(func); + auto func_start_offset = body_size_offset - last_section_payload_offset_; + auto func_end_offset = stream_->offset() - last_section_payload_offset_; + auto delta = WriteFixupU32Leb128Size(body_size_offset, leb_size_guess, + "FIXUP func body size"); + if (current_reloc_section_ && delta != 0) { + for (Reloc& reloc : current_reloc_section_->relocations) { + if (reloc.offset >= func_start_offset && + reloc.offset <= func_end_offset) { + reloc.offset += delta; + } + } + } + } + EndSection(); + } + + // Remove the DataCount section if there are no instructions that require it. + if (options_.features.bulk_memory_enabled() && + module_->data_segments.size() && !has_data_segment_instruction_) { + Offset size = stream_->offset() - data_count_end_; + if (size) { + // If the DataCount section was followed by anything, assert that it's + // only the Code section. This limits the amount of fixing-up that we + // need to do. + assert(data_count_end_ == code_start_); + assert(last_section_type_ == BinarySection::Code); + stream_->MoveData(data_count_start_, data_count_end_, size); + code_start_ = data_count_start_; + } + stream_->Truncate(data_count_start_ + size); + + --section_count_; + + // We just effectively decremented the code section's index; adjust anything + // that might have captured it. + for (RelocSection& section : reloc_sections_) { + if (section.section_index == section_count_) { + assert(last_section_type_ == BinarySection::Code); + --section.section_index; + } + } + } + + WriteCodeMetadataSections(); + + if (module_->data_segments.size()) { + BeginKnownSection(BinarySection::Data); + WriteU32Leb128(stream_, module_->data_segments.size(), "num data segments"); + for (size_t i = 0; i < module_->data_segments.size(); ++i) { + const DataSegment* segment = module_->data_segments[i]; + WriteHeader("data segment header", i); + uint8_t flags = segment->GetFlags(module_); + stream_->WriteU8(flags, "segment flags"); + if (!(flags & SegPassive)) { + if (options_.features.multi_memory_enabled() && + (flags & SegExplicitIndex)) { + WriteU32Leb128(stream_, module_->GetMemoryIndex(segment->memory_var), + "memidx"); + } else { + assert(module_->GetMemoryIndex(segment->memory_var) == 0); + } + WriteInitExpr(segment->offset); + } + WriteU32Leb128(stream_, segment->data.size(), "data segment size"); + WriteHeader("data segment data", i); + stream_->WriteData(segment->data, "data segment data"); + } + EndSection(); + } + + if (options_.write_debug_names) { + std::vector<std::string> index_to_name; + + char desc[100]; + BeginCustomSection(WABT_BINARY_SECTION_NAME); + + if (!module_->name.empty()) { + WriteU32Leb128(stream_, NameSectionSubsection::Module, + "module name type"); + BeginSubsection("module name subsection"); + WriteDebugName(stream_, module_->name, "module name"); + EndSubsection(); + } + + WriteNames<Func>(module_->funcs, NameSectionSubsection::Function); + + WriteU32Leb128(stream_, 2, "local name type"); + + BeginSubsection("local name subsection"); + WriteU32Leb128(stream_, module_->funcs.size(), "num functions"); + for (size_t i = 0; i < module_->funcs.size(); ++i) { + const Func* func = module_->funcs[i]; + Index num_params_and_locals = func->GetNumParamsAndLocals(); + MakeTypeBindingReverseMapping(num_params_and_locals, func->bindings, + &index_to_name); + Index num_named = 0; + for (auto s : index_to_name) { + if (!s.empty()) { + num_named++; + } + } + + WriteU32Leb128(stream_, i, "function index"); + WriteU32Leb128(stream_, num_named, "num locals"); + + for (size_t j = 0; j < num_params_and_locals; ++j) { + const std::string& name = index_to_name[j]; + if (!name.empty()) { + wabt_snprintf(desc, sizeof(desc), "local name %" PRIzd, j); + WriteU32Leb128(stream_, j, "local index"); + WriteDebugName(stream_, name, desc); + } + } + } + EndSubsection(); + + WriteNames<TypeEntry>(module_->types, NameSectionSubsection::Type); + WriteNames<Table>(module_->tables, NameSectionSubsection::Table); + WriteNames<Memory>(module_->memories, NameSectionSubsection::Memory); + WriteNames<Global>(module_->globals, NameSectionSubsection::Global); + WriteNames<ElemSegment>(module_->elem_segments, + NameSectionSubsection::ElemSegment); + WriteNames<DataSegment>(module_->data_segments, + NameSectionSubsection::DataSegment); + WriteNames<Tag>(module_->tags, NameSectionSubsection::Tag); + + EndSection(); + } + + if (options_.relocatable) { + WriteLinkingSection(); + for (RelocSection& section : reloc_sections_) { + WriteRelocSection(§ion); + } + } + + return stream_->result(); +} + +void BinaryWriter::WriteCodeMetadataSections() { + if (code_metadata_sections_.empty()) + return; + + section_count_ -= 1; + // We have to increment the code section's index; adjust anything + // that might have captured it. + for (RelocSection& section : reloc_sections_) { + if (section.section_index == section_count_) { + assert(last_section_type_ == BinarySection::Code); + section.section_index += code_metadata_sections_.size(); + } + } + + MemoryStream tmp_stream; + Stream* main_stream = stream_; + stream_ = &tmp_stream; + for (auto& s : code_metadata_sections_) { + std::string name = "metadata.code."; + name.append(s.first); + auto& section = s.second; + BeginCustomSection(name.c_str()); + WriteU32Leb128(stream_, section.entries.size(), "function count"); + for (auto& f : section.entries) { + WriteU32Leb128WithReloc(f.func_idx, "function index", + RelocType::FuncIndexLEB); + WriteU32Leb128(stream_, f.entries.size(), "instances count"); + for (auto& a : f.entries) { + WriteU32Leb128(stream_, a.offset, "code offset"); + WriteU32Leb128(stream_, a.data.size(), "data length"); + stream_->WriteData(a.data.data(), a.data.size(), "data", + PrintChars::Yes); + } + } + EndSection(); + } + stream_ = main_stream; + auto buf = tmp_stream.ReleaseOutputBuffer(); + stream_->MoveData(code_start_ + buf->data.size(), code_start_, + stream_->offset() - code_start_); + stream_->WriteDataAt(code_start_, buf->data.data(), buf->data.size()); + stream_->AddOffset(buf->data.size()); + code_start_ += buf->data.size(); + section_count_ += 1; + last_section_type_ = BinarySection::Code; +} + +} // end anonymous namespace + +Result WriteBinaryModule(Stream* stream, + const Module* module, + const WriteBinaryOptions& options) { + BinaryWriter binary_writer(stream, options, module); + return binary_writer.WriteModule(); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary.cc b/third_party/wasm2c/src/binary.cc new file mode 100644 index 0000000000..d4938a1939 --- /dev/null +++ b/third_party/wasm2c/src/binary.cc @@ -0,0 +1,68 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/binary.h" + +namespace wabt { + +BinarySectionOrder GetSectionOrder(BinarySection sec) { + switch (sec) { +#define V(Name, name, code) \ + case BinarySection::Name: \ + return BinarySectionOrder::Name; + WABT_FOREACH_BINARY_SECTION(V) +#undef V + default: + WABT_UNREACHABLE; + } +} + +const char* GetSectionName(BinarySection sec) { + switch (sec) { +#define V(Name, name, code) \ + case BinarySection::Name: \ + return #Name; + WABT_FOREACH_BINARY_SECTION(V) +#undef V + default: + WABT_UNREACHABLE; + } +} + +// clang-format off +const char* NameSubsectionName[] = { + "module", + "function", + "local", + "label", + "type", + "table", + "memory", + "global", + "elemseg", + "dataseg", + "tag", +}; +// clang-format on + +const char* GetNameSectionSubsectionName(NameSectionSubsection subsec) { + static_assert(WABT_ENUM_COUNT(NameSectionSubsection) == + WABT_ARRAY_SIZE(NameSubsectionName), + "Malformed ExprTypeName array"); + return NameSubsectionName[size_t(subsec)]; +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binding-hash.cc b/third_party/wasm2c/src/binding-hash.cc new file mode 100644 index 0000000000..cd1b5f5a3e --- /dev/null +++ b/third_party/wasm2c/src/binding-hash.cc @@ -0,0 +1,91 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/binding-hash.h" + +#include <algorithm> +#include <vector> + +#include "wabt/ir.h" + +namespace wabt { + +void BindingHash::FindDuplicates(DuplicateCallback callback) const { + if (size() > 0) { + ValueTypeVector duplicates; + CreateDuplicatesVector(&duplicates); + SortDuplicatesVectorByLocation(&duplicates); + CallCallbacks(duplicates, callback); + } +} + +Index BindingHash::FindIndex(const Var& var) const { + if (var.is_name()) { + return FindIndex(var.name()); + } + return var.index(); +} + +void BindingHash::CreateDuplicatesVector( + ValueTypeVector* out_duplicates) const { + // This relies on the fact that in an unordered_multimap, all values with the + // same key are adjacent in iteration order. + auto first = begin(); + bool is_first = true; + for (auto iter = std::next(first); iter != end(); ++iter) { + if (first->first == iter->first) { + if (is_first) { + out_duplicates->push_back(&*first); + } + out_duplicates->push_back(&*iter); + is_first = false; + } else { + is_first = true; + first = iter; + } + } +} + +void BindingHash::SortDuplicatesVectorByLocation( + ValueTypeVector* duplicates) const { + std::sort( + duplicates->begin(), duplicates->end(), + [](const value_type* lhs, const value_type* rhs) -> bool { + return lhs->second.loc.line < rhs->second.loc.line || + (lhs->second.loc.line == rhs->second.loc.line && + lhs->second.loc.first_column < rhs->second.loc.first_column); + }); +} + +void BindingHash::CallCallbacks(const ValueTypeVector& duplicates, + DuplicateCallback callback) const { + // Loop through all duplicates in order, and call callback with first + // occurrence. + for (auto iter = duplicates.begin(), end = duplicates.end(); iter != end; + ++iter) { + auto first = std::find_if(duplicates.begin(), duplicates.end(), + [iter](const value_type* x) -> bool { + return x->first == (*iter)->first; + }); + if (first == iter) { + continue; + } + assert(first != duplicates.end()); + callback(**first, **iter); + } +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/c-writer.cc b/third_party/wasm2c/src/c-writer.cc new file mode 100644 index 0000000000..798f85f9bc --- /dev/null +++ b/third_party/wasm2c/src/c-writer.cc @@ -0,0 +1,5241 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/c-writer.h" + +#include <cctype> +#include <cinttypes> +#include <iterator> +#include <limits> +#include <map> +#include <set> +#include <string_view> +#include <vector> + +#include "wabt/cast.h" +#include "wabt/common.h" +#include "wabt/ir.h" +#include "wabt/literal.h" +#include "wabt/sha256.h" +#include "wabt/stream.h" +#include "wabt/string-util.h" + +#define INDENT_SIZE 2 + +#define UNIMPLEMENTED(x) printf("unimplemented: %s\n", (x)), abort() + +// code to be inserted into the generated output +extern const char* s_header_top; +extern const char* s_header_bottom; +extern const char* s_source_includes; +extern const char* s_source_declarations; + +namespace wabt { + +namespace { + +struct Label { + Label(LabelType label_type, + const std::string& name, + const TypeVector& sig, + size_t type_stack_size, + size_t try_catch_stack_size, + bool used = false) + : label_type(label_type), + name(name), + sig(sig), + type_stack_size(type_stack_size), + try_catch_stack_size(try_catch_stack_size), + used(used) {} + + bool HasValue() const { return !sig.empty(); } + + LabelType label_type; + const std::string& name; + const TypeVector& sig; + size_t type_stack_size; + size_t try_catch_stack_size; + bool used = false; +}; + +struct LocalName { + explicit LocalName(const std::string& name) : name(name) {} + const std::string& name; +}; + +struct ParamName : LocalName { + using LocalName::LocalName; + ParamName(const Var& var) : LocalName(var.name()) {} +}; + +struct LabelName : LocalName { + using LocalName::LocalName; +}; + +struct GlobalName { + GlobalName(ModuleFieldType type, const std::string& name) + : type(type), name(name) {} + ModuleFieldType type; + const std::string& name; +}; + +struct ExternalPtr : GlobalName { + using GlobalName::GlobalName; +}; + +struct ExternalRef : GlobalName { + using GlobalName::GlobalName; +}; + +struct ExternalInstancePtr : GlobalName { + using GlobalName::GlobalName; +}; + +struct ExternalInstanceRef : GlobalName { + using GlobalName::GlobalName; +}; + +struct GotoLabel { + explicit GotoLabel(const Var& var) : var(var) {} + const Var& var; +}; + +struct LabelDecl { + explicit LabelDecl(const std::string& name) : name(name) {} + std::string name; +}; + +struct GlobalInstanceVar { + explicit GlobalInstanceVar(const Var& var) : var(var) {} + const Var& var; +}; + +struct StackVar { + explicit StackVar(Index index, Type type = Type::Any) + : index(index), type(type) {} + Index index; + Type type; +}; + +struct TypeEnum { + explicit TypeEnum(Type type) : type(type) {} + Type type; +}; + +struct SignedType { + explicit SignedType(Type type) : type(type) {} + Type type; +}; + +struct ResultType { + explicit ResultType(const TypeVector& types) : types(types) {} + const TypeVector& types; +}; + +struct TryCatchLabel { + TryCatchLabel(const std::string& name, size_t try_catch_stack_size) + : name(name), try_catch_stack_size(try_catch_stack_size), used(false) {} + std::string name; + size_t try_catch_stack_size; + bool used; +}; + +struct FuncTypeExpr { + const FuncType* func_type; + FuncTypeExpr(const FuncType* f) : func_type(f) {} +}; + +struct Newline {}; +struct OpenBrace {}; +struct CloseBrace {}; + +int GetShiftMask(Type type) { + // clang-format off + switch (type) { + case Type::I32: return 31; + case Type::I64: return 63; + default: WABT_UNREACHABLE; return 0; + } + // clang-format on +} + +/* + * This function is the default behavior for name_to_output_file_index_. For + * single .c output, this function returns a vector filled with 0. For multiple + * .c outputs, this function sorts all non-imported functions in the module by + * their names, and then divides all non-imported functions into equal-sized + * buckets (# of non-imported functions / # of .c outputs) based on the sorting. + */ +static std::vector<size_t> default_name_to_output_file_index( + std::vector<Func*>::const_iterator func_begin, + std::vector<Func*>::const_iterator func_end, + size_t num_imports, + size_t num_streams) { + std::vector<size_t> result; + result.resize(std::distance(func_begin, func_end)); + if (num_streams == 1) { + return result; + } + + std::map<std::string, Index> sorted_functions; + size_t non_imported_funcs = result.size() - num_imports; + size_t bucket_size = non_imported_funcs / num_streams + + (non_imported_funcs % num_streams ? 1 : 0); + Index func_index = 0; + for (auto func = func_begin; func != func_end; func++) { + sorted_functions.insert({(*func)->name, func_index}); + ++func_index; + } + Index sorted_func_index = 0; + for (const auto& [func_name, index] : sorted_functions) { + bool is_import = index < num_imports; + if (!is_import) { + result.at(index) = sorted_func_index / bucket_size; + ++sorted_func_index; + } + } + return result; +} + +class CWriter { + public: + CWriter(std::vector<Stream*>&& c_streams, + Stream* h_stream, + Stream* h_impl_stream, + const char* header_name, + const char* header_impl_name, + const WriteCOptions& options) + : options_(options), + c_streams_(std::move(c_streams)), + h_stream_(h_stream), + h_impl_stream_(h_impl_stream), + header_name_(header_name), + header_impl_name_(header_impl_name) { + module_prefix_ = MangleModuleName(options_.module_name); + if (c_streams_.size() != 1 && options.name_to_output_file_index) { + name_to_output_file_index_ = options.name_to_output_file_index; + } else { + name_to_output_file_index_ = default_name_to_output_file_index; + } + } + + Result WriteModule(const Module&); + + private: + using SymbolSet = std::set<std::string>; + using SymbolMap = std::map<std::string, std::string>; + using StackTypePair = std::pair<Index, Type>; + using StackVarSymbolMap = std::map<StackTypePair, std::string>; + + void WriteCHeader(); + void WriteCSource(); + + size_t MarkTypeStack() const; + void ResetTypeStack(size_t mark); + Type StackType(Index) const; + void PushType(Type); + void PushTypes(const TypeVector&); + void DropTypes(size_t count); + + void PushLabel(LabelType, + const std::string& name, + const FuncSignature&, + bool used = false); + const Label* FindLabel(const Var& var, bool mark_used = true); + bool IsTopLabelUsed() const; + void PopLabel(); + + static constexpr char MangleType(Type); + static constexpr char MangleField(ModuleFieldType); + static std::string MangleMultivalueTypes(const TypeVector&); + static std::string MangleTagTypes(const TypeVector&); + static std::string Mangle(std::string_view name, bool double_underscores); + static std::string MangleName(std::string_view); + static std::string MangleModuleName(std::string_view); + std::string ExportName(std::string_view module_name, + std::string_view export_name); + std::string ExportName(std::string_view export_name); + std::string ModuleInstanceTypeName() const; + static std::string ModuleInstanceTypeName(std::string_view module_name); + void ClaimName(SymbolSet& set, + SymbolMap& map, + char type_suffix, + std::string_view wasm_name, + const std::string& c_name); + std::string FindUniqueName(SymbolSet& set, std::string_view proposed_name); + std::string ClaimUniqueName(SymbolSet& set, + SymbolMap& map, + char type_suffix, + std::string_view wasm_name, + const std::string& proposed_c_name); + void DefineImportName(const Import* import, + std::string_view module_name, + std::string_view field_name); + void ReserveExportNames(); + void ReserveExportName(std::string_view); + std::string DefineImportedModuleInstanceName(std::string_view name); + std::string DefineInstanceMemberName(ModuleFieldType, std::string_view); + std::string DefineGlobalScopeName(ModuleFieldType, std::string_view); + std::string DefineLocalScopeName(std::string_view name, bool is_label); + std::string DefineParamName(std::string_view); + std::string DefineLabelName(std::string_view); + std::string DefineStackVarName(Index, Type, std::string_view); + + static void SerializeFuncType(const FuncType&, std::string&); + + std::string GetGlobalName(ModuleFieldType, const std::string&) const; + std::string GetLocalName(const std::string&, bool is_label) const; + + void Indent(int size = INDENT_SIZE); + void Dedent(int size = INDENT_SIZE); + void WriteIndent(); + void WriteData(const char* src, size_t size); + void Writef(const char* format, ...); + + template <typename T, typename U, typename... Args> + void Write(T&& t, U&& u, Args&&... args) { + Write(std::forward<T>(t)); + Write(std::forward<U>(u)); + Write(std::forward<Args>(args)...); + } + + static const char* GetReferenceTypeName(const Type& type); + static const char* GetReferenceNullValue(const Type& type); + static const char* GetCTypeName(const Type& type); + + const char* InternalSymbolScope() const; + + enum class CWriterPhase { + Declarations, + Definitions, + }; + + void Write() {} + void Write(Newline); + void Write(OpenBrace); + void Write(CloseBrace); + void Write(uint64_t); + void Write(std::string_view); + void Write(const ParamName&); + void Write(const LabelName&); + void Write(const GlobalName&); + void Write(const ExternalPtr&); + void Write(const ExternalRef&); + void Write(const ExternalInstancePtr&); + void Write(const ExternalInstanceRef&); + void Write(Type); + void Write(SignedType); + void Write(TypeEnum); + void Write(const GotoLabel&); + void Write(const LabelDecl&); + void Write(const GlobalInstanceVar&); + void Write(const StackVar&); + void Write(const ResultType&); + void Write(const Const&); + void WriteInitExpr(const ExprList&); + void WriteInitExprTerminal(const Expr*); + std::string GenerateHeaderGuard() const; + void WriteSourceTop(); + void WriteMultiCTop(); + void WriteMultiCTopEmpty(); + void WriteMultivalueTypes(); + void WriteTagTypes(); + void WriteFuncTypeDecls(); + void WriteFuncTypes(); + void Write(const FuncTypeExpr&); + void WriteTagDecls(); + void WriteTags(); + void ComputeUniqueImports(); + void BeginInstance(); + void WriteImports(); + void WriteFuncDeclarations(); + void WriteFuncDeclaration(const FuncDeclaration&, const std::string&); + void WriteImportFuncDeclaration(const FuncDeclaration&, + const std::string& module_name, + const std::string&); + void WriteCallIndirectFuncDeclaration(const FuncDeclaration&, + const std::string&); + void WriteFeatureMacros(); + void WriteModuleInstance(); + void WriteGlobals(); + void WriteGlobal(const Global&, const std::string&); + void WriteGlobalPtr(const Global&, const std::string&); + void WriteMemories(); + void WriteMemory(const std::string&); + void WriteMemoryPtr(const std::string&); + void WriteTables(); + void WriteTable(const std::string&, const wabt::Type&); + void WriteTablePtr(const std::string&, const Table&); + void WriteTableType(const wabt::Type&); + void WriteDataInstances(); + void WriteElemInstances(); + void WriteGlobalInitializers(); + void WriteDataInitializerDecls(); + void WriteDataInitializers(); + void WriteElemInitializerDecls(); + void WriteElemInitializers(); + void WriteElemTableInit(bool, const ElemSegment*, const Table*); + void WriteExports(CWriterPhase); + void WriteInitDecl(); + void WriteFreeDecl(); + void WriteGetFuncTypeDecl(); + void WriteInit(); + void WriteFree(); + void WriteGetFuncType(); + void WriteInitInstanceImport(); + void WriteImportProperties(CWriterPhase); + void WriteFuncs(); + void Write(const Func&); + void WriteParamsAndLocals(); + void WriteParams(const std::vector<std::string>& index_to_name); + void WriteParamSymbols(const std::vector<std::string>& index_to_name); + void WriteParamTypes(const FuncDeclaration& decl); + void WriteLocals(const std::vector<std::string>& index_to_name); + void WriteStackVarDeclarations(); + void Write(const ExprList&); + + enum class AssignOp { + Disallowed, + Allowed, + }; + + void WriteSimpleUnaryExpr(Opcode, const char* op); + void WriteInfixBinaryExpr(Opcode, + const char* op, + AssignOp = AssignOp::Allowed); + void WritePrefixBinaryExpr(Opcode, const char* op); + void WriteSignedBinaryExpr(Opcode, const char* op); + void Write(const BinaryExpr&); + void Write(const CompareExpr&); + void Write(const ConvertExpr&); + void Write(const LoadExpr&); + void Write(const StoreExpr&); + void Write(const UnaryExpr&); + void Write(const TernaryExpr&); + void Write(const SimdLaneOpExpr&); + void Write(const SimdLoadLaneExpr&); + void Write(const SimdStoreLaneExpr&); + void Write(const SimdShuffleOpExpr&); + void Write(const LoadSplatExpr&); + void Write(const LoadZeroExpr&); + void Write(const Block&); + + size_t BeginTry(const TryExpr& tryexpr); + void WriteTryCatch(const TryExpr& tryexpr); + void WriteTryDelegate(const TryExpr& tryexpr); + void Write(const Catch& c); + void WriteThrow(); + + void PushTryCatch(const std::string& name); + void PopTryCatch(); + + void PushFuncSection(std::string_view include_condition = ""); + + const WriteCOptions& options_; + const Module* module_ = nullptr; + const Func* func_ = nullptr; + Stream* stream_ = nullptr; + std::vector<Stream*> c_streams_; + Stream* h_stream_ = nullptr; + Stream* h_impl_stream_ = nullptr; + std::string header_name_; + std::string header_impl_name_; + Result result_ = Result::Ok; + int indent_ = 0; + bool should_write_indent_next_ = false; + int consecutive_newline_count_ = 0; + + SymbolMap global_sym_map_; + SymbolMap local_sym_map_; + SymbolMap import_module_sym_map_; + StackVarSymbolMap stack_var_sym_map_; + SymbolSet global_syms_; + SymbolSet local_syms_; + SymbolSet import_syms_; + TypeVector type_stack_; + std::vector<Label> label_stack_; + std::vector<TryCatchLabel> try_catch_stack_; + std::string module_prefix_; + + std::vector<const Import*> unique_imports_; + SymbolSet import_module_set_; // modules that are imported from + SymbolSet import_func_module_set_; // modules that funcs are imported from + + std::vector<std::pair<std::string, MemoryStream>> func_sections_; + SymbolSet func_includes_; + + std::vector<std::string> unique_func_type_names_; + + std::function<std::vector<size_t>(std::vector<Func*>::const_iterator, + std::vector<Func*>::const_iterator, + size_t, + size_t)> + name_to_output_file_index_; +}; + +// TODO: if WABT begins supporting debug names for labels, +// will need to avoid conflict between a label named "$Bfunc" and +// the implicit func label +static constexpr char kImplicitFuncLabel[] = "$Bfunc"; + +// These should be greater than any ModuleFieldType (used for MangleField). +static constexpr char kParamSuffix = + 'a' + static_cast<char>(ModuleFieldType::Tag) + 1; +static constexpr char kLabelSuffix = kParamSuffix + 1; + +static constexpr char kGlobalSymbolPrefix[] = "w2c_"; +static constexpr char kLocalSymbolPrefix[] = "var_"; +static constexpr char kAdminSymbolPrefix[] = "wasm2c_"; + +size_t CWriter::MarkTypeStack() const { + return type_stack_.size(); +} + +void CWriter::ResetTypeStack(size_t mark) { + assert(mark <= type_stack_.size()); + type_stack_.erase(type_stack_.begin() + mark, type_stack_.end()); +} + +Type CWriter::StackType(Index index) const { + assert(index < type_stack_.size()); + return *(type_stack_.rbegin() + index); +} + +void CWriter::PushType(Type type) { + type_stack_.push_back(type); +} + +void CWriter::PushTypes(const TypeVector& types) { + type_stack_.insert(type_stack_.end(), types.begin(), types.end()); +} + +void CWriter::DropTypes(size_t count) { + assert(count <= type_stack_.size()); + type_stack_.erase(type_stack_.end() - count, type_stack_.end()); +} + +void CWriter::PushLabel(LabelType label_type, + const std::string& name, + const FuncSignature& sig, + bool used) { + if (label_type == LabelType::Loop) + label_stack_.emplace_back(label_type, name, sig.param_types, + type_stack_.size(), try_catch_stack_.size(), + used); + else + label_stack_.emplace_back(label_type, name, sig.result_types, + type_stack_.size(), try_catch_stack_.size(), + used); +} + +const Label* CWriter::FindLabel(const Var& var, bool mark_used) { + Label* label = nullptr; + + if (var.is_index()) { + // We've generated names for all labels, so we should only be using an + // index when branching to the implicit function label, which can't be + // named. + assert(var.index() + 1 == label_stack_.size()); + label = &label_stack_[0]; + } else { + assert(var.is_name()); + for (Index i = label_stack_.size(); i > 0; --i) { + label = &label_stack_[i - 1]; + if (label->name == var.name()) + break; + } + } + + assert(label); + if (mark_used) { + label->used = true; + } + return label; +} + +bool CWriter::IsTopLabelUsed() const { + assert(!label_stack_.empty()); + return label_stack_.back().used; +} + +void CWriter::PopLabel() { + label_stack_.pop_back(); +} + +// static +constexpr char CWriter::MangleType(Type type) { + // clang-format off + switch (type) { + case Type::I32: return 'i'; + case Type::I64: return 'j'; + case Type::F32: return 'f'; + case Type::F64: return 'd'; + case Type::V128: return 'o'; + case Type::FuncRef: return 'r'; + case Type::ExternRef: return 'e'; + default: + WABT_UNREACHABLE; + } + // clang-format on +} + +// static +constexpr char CWriter::MangleField(ModuleFieldType type) { + assert(static_cast<std::underlying_type<ModuleFieldType>::type>(type) < + std::numeric_limits<char>::max()); + return 'a' + static_cast<char>(type); +} + +// remove risky characters for pasting into a C-style comment +static std::string SanitizeForComment(std::string_view str) { + std::string result; + + for (const uint8_t ch : str) { + // escape control chars, DEL, >7-bit chars, trigraphs, and end of comment + if (ch < ' ' || ch > '~' || ch == '?' || ch == '/') { + result += "\\" + StringPrintf("%02X", ch); + } else { + result += ch; + } + } + + return result; +} + +// static +std::string CWriter::MangleMultivalueTypes(const TypeVector& types) { + assert(types.size() >= 2); + std::string result = "wasm_multi_"; + for (auto type : types) { + result += MangleType(type); + } + return result; +} + +// static +std::string CWriter::MangleTagTypes(const TypeVector& types) { + assert(types.size() >= 2); + std::string result = "wasm_tag_"; + for (auto type : types) { + result += MangleType(type); + } + return result; +} + +/* The C symbol for an export from this module. */ +std::string CWriter::ExportName(std::string_view export_name) { + return kGlobalSymbolPrefix + module_prefix_ + '_' + MangleName(export_name); +} + +/* The C symbol for an export from an arbitrary module. */ +// static +std::string CWriter::ExportName(std::string_view module_name, + std::string_view export_name) { + return kGlobalSymbolPrefix + MangleModuleName(module_name) + '_' + + MangleName(export_name); +} + +/* The type name of an instance of this module. */ +std::string CWriter::ModuleInstanceTypeName() const { + return kGlobalSymbolPrefix + module_prefix_; +} + +/* The type name of an instance of an arbitrary module. */ +// static +std::string CWriter::ModuleInstanceTypeName(std::string_view module_name) { + return kGlobalSymbolPrefix + MangleModuleName(module_name); +} + +/* + * Hardcoded "C"-locale versions of isalpha/isdigit/isalnum/isxdigit for use + * in CWriter::Mangle(). We don't use the standard isalpha/isdigit/isalnum + * because the caller might have changed the current locale. + */ +static bool internal_isalpha(uint8_t ch) { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); +} + +static bool internal_isdigit(uint8_t ch) { + return (ch >= '0' && ch <= '9'); +} + +static bool internal_isalnum(uint8_t ch) { + return internal_isalpha(ch) || internal_isdigit(ch); +} + +static bool internal_ishexdigit(uint8_t ch) { + return internal_isdigit(ch) || (ch >= 'A' && ch <= 'F'); // capitals only +} + +// static +std::string CWriter::Mangle(std::string_view name, bool double_underscores) { + /* + * Name mangling transforms arbitrary Wasm names into "safe" C names + * in a deterministic way. To avoid collisions, distinct Wasm names must be + * transformed into distinct C names. + * + * The rules implemented here are: + * 1) any hex digit ('A' through 'F') that follows the sequence "0x" + * is escaped + * 2) any underscore at the beginning, at the end, or following another + * underscore, is escaped + * 3) if double_underscores is set, underscores are replaced with + * two underscores. + * 4) otherwise, any alphanumeric character is kept as-is, + * and any other character is escaped + * + * "Escaped" means the character is represented with the sequence "0xAB", + * where A B are hex digits ('0'-'9' or 'A'-'F') representing the character's + * numeric value. + * + * Module names are mangled with double_underscores=true to prevent + * collisions between, e.g., a module "alfa" with export + * "bravo_charlie" vs. a module "alfa_bravo" with export "charlie". + */ + + enum State { Any, Zero, ZeroX, ZeroXHexDigit } state{Any}; + bool last_was_underscore = false; + + std::string result; + auto append_escaped = [&](const uint8_t ch) { + result += "0x" + StringPrintf("%02X", ch); + last_was_underscore = false; + state = Any; + }; + + auto append_verbatim = [&](const uint8_t ch) { + result += ch; + last_was_underscore = (ch == '_'); + }; + + for (auto it = name.begin(); it != name.end(); ++it) { + const uint8_t ch = *it; + switch (state) { + case Any: + state = (ch == '0') ? Zero : Any; + break; + case Zero: + state = (ch == 'x') ? ZeroX : Any; + break; + case ZeroX: + state = internal_ishexdigit(ch) ? ZeroXHexDigit : Any; + break; + case ZeroXHexDigit: + WABT_UNREACHABLE; + break; + } + + /* rule 1 */ + if (state == ZeroXHexDigit) { + append_escaped(ch); + continue; + } + + /* rule 2 */ + if ((ch == '_') && ((it == name.begin()) || (std::next(it) == name.end()) || + last_was_underscore)) { + append_escaped(ch); + continue; + } + + /* rule 3 */ + if (double_underscores && ch == '_') { + append_verbatim(ch); + append_verbatim(ch); + continue; + } + + /* rule 4 */ + if (internal_isalnum(ch) || (ch == '_')) { + append_verbatim(ch); + } else { + append_escaped(ch); + } + } + + return result; +} + +// static +std::string CWriter::MangleName(std::string_view name) { + return Mangle(name, false); +} + +// static +std::string CWriter::MangleModuleName(std::string_view name) { + return Mangle(name, true); +} + +/* + * Allocate a C symbol (must be unused) in the SymbolSet, + * and a mapping from the Wasm name (tagged with + * the index space of the name) to that C symbol. + */ +void CWriter::ClaimName(SymbolSet& set, + SymbolMap& map, + char type_suffix, + std::string_view wasm_name, + const std::string& c_name) { + const std::string type_tagged_wasm_name = + std::string(wasm_name) + type_suffix; + + [[maybe_unused]] bool success; + success = set.insert(c_name).second; + assert(success); + + success = map.emplace(type_tagged_wasm_name, c_name).second; + assert(success); +} + +/* + * Make a proposed C symbol unique in a given symbol set by appending + * an integer to the symbol if necessary. + */ +std::string CWriter::FindUniqueName(SymbolSet& set, + std::string_view proposed_name) { + std::string unique{proposed_name}; + if (set.find(unique) != set.end()) { + std::string base = unique + "_"; + size_t count = 0; + do { + unique = base + std::to_string(count++); + } while (set.find(unique) != set.end()); + } + return unique; +} + +/* + * Find a unique C symbol in the symbol set and claim it (mapping the + * type-tagged Wasm name to it). + */ +std::string CWriter::ClaimUniqueName(SymbolSet& set, + SymbolMap& map, + char type_suffix, + std::string_view wasm_name, + const std::string& proposed_c_name) { + const std::string unique = FindUniqueName(set, proposed_c_name); + ClaimName(set, map, type_suffix, wasm_name, unique); + return unique; +} + +std::string_view StripLeadingDollar(std::string_view name) { + assert(!name.empty()); + assert(name.front() == '$'); + name.remove_prefix(1); + return name; +} + +void CWriter::DefineImportName(const Import* import, + std::string_view module, + std::string_view field_name) { + std::string name; + ModuleFieldType type{}; + + switch (import->kind()) { + case ExternalKind::Func: + type = ModuleFieldType::Func; + name = cast<FuncImport>(import)->func.name; + break; + case ExternalKind::Tag: + type = ModuleFieldType::Tag; + name = cast<TagImport>(import)->tag.name; + break; + case ExternalKind::Global: + type = ModuleFieldType::Global; + name = cast<GlobalImport>(import)->global.name; + break; + case ExternalKind::Memory: + type = ModuleFieldType::Memory; + name = cast<MemoryImport>(import)->memory.name; + break; + case ExternalKind::Table: + type = ModuleFieldType::Table; + name = cast<TableImport>(import)->table.name; + break; + } + + import_syms_.insert(name); + import_module_sym_map_.emplace(name, import->module_name); + + const std::string mangled = ExportName(module, field_name); + global_syms_.erase(mangled); // duplicate imports are allowed + ClaimName(global_syms_, global_sym_map_, MangleField(type), name, mangled); +} + +/* + * Reserve a C symbol for the public name of a module's export. The + * format of these is "w2c_" + the module prefix + "_" + the mangled + * export name. Reserving the symbol prevents internal functions and + * other names from shadowing/overlapping the exports. + */ +void CWriter::ReserveExportName(std::string_view name) { + ClaimName(global_syms_, global_sym_map_, MangleField(ModuleFieldType::Export), + name, ExportName(name)); +} + +/* + * Names for functions, function types, tags, and segments are globally unique + * across modules (formatted the same as an export, as "w2c_" + module prefix + + * "_" + the name, made unique if necessary). + */ +std::string CWriter::DefineGlobalScopeName(ModuleFieldType type, + std::string_view name) { + return ClaimUniqueName(global_syms_, global_sym_map_, MangleField(type), name, + ExportName(StripLeadingDollar(name))); +} + +std::string CWriter::GetGlobalName(ModuleFieldType type, + const std::string& name) const { + std::string mangled = name + MangleField(type); + assert(global_sym_map_.count(mangled) == 1); + return global_sym_map_.at(mangled); +} + +/* Names for params, locals, and stack vars are formatted as "var_" + name. */ +std::string CWriter::DefineLocalScopeName(std::string_view name, + bool is_label) { + return ClaimUniqueName( + local_syms_, local_sym_map_, is_label ? kLabelSuffix : kParamSuffix, name, + kLocalSymbolPrefix + MangleName(StripLeadingDollar(name))); +} + +std::string CWriter::GetLocalName(const std::string& name, + bool is_label) const { + std::string mangled = name + (is_label ? kLabelSuffix : kParamSuffix); + assert(local_sym_map_.count(mangled) == 1); + return local_sym_map_.at(mangled); +} + +std::string CWriter::DefineParamName(std::string_view name) { + return DefineLocalScopeName(name, false); +} + +std::string CWriter::DefineLabelName(std::string_view name) { + return DefineLocalScopeName(name, true); +} + +std::string CWriter::DefineStackVarName(Index index, + Type type, + std::string_view name) { + std::string unique = + FindUniqueName(local_syms_, kLocalSymbolPrefix + MangleName(name)); + StackTypePair stp = {index, type}; + [[maybe_unused]] bool success = + stack_var_sym_map_.emplace(stp, unique).second; + assert(success); + return unique; +} + +/* + * Members of the module instance (globals, tables, and memories) are formatted + * as "w2c_" + the mangled name of the element (made unique if necessary). + */ +std::string CWriter::DefineInstanceMemberName(ModuleFieldType type, + std::string_view name) { + return ClaimUniqueName( + global_syms_, global_sym_map_, MangleField(type), name, + kGlobalSymbolPrefix + MangleName(StripLeadingDollar(name))); +} + +/* + * The name of a module-instance member that points to the originating + * instance of an imported function is formatted as "w2c_" + originating + * module prefix + "_instance". + */ +std::string CWriter::DefineImportedModuleInstanceName(std::string_view name) { + return ClaimUniqueName(global_syms_, global_sym_map_, + MangleField(ModuleFieldType::Import), name, + ExportName(name, "instance")); +} + +void CWriter::Indent(int size) { + indent_ += size; +} + +void CWriter::Dedent(int size) { + indent_ -= size; + assert(indent_ >= 0); +} + +void CWriter::WriteIndent() { + static char s_indent[] = + " " + " "; + static size_t s_indent_len = sizeof(s_indent) - 1; + size_t to_write = indent_; + while (to_write >= s_indent_len) { + stream_->WriteData(s_indent, s_indent_len); + to_write -= s_indent_len; + } + if (to_write > 0) { + stream_->WriteData(s_indent, to_write); + } +} + +void CWriter::WriteData(const char* src, size_t size) { + if (should_write_indent_next_) { + WriteIndent(); + should_write_indent_next_ = false; + } + if (size > 0 && src[0] != '\n') { + consecutive_newline_count_ = 0; + } + stream_->WriteData(src, size); +} + +void WABT_PRINTF_FORMAT(2, 3) CWriter::Writef(const char* format, ...) { + WABT_SNPRINTF_ALLOCA(buffer, length, format); + WriteData(buffer, length); +} + +void CWriter::Write(Newline) { + // Allow maximum one blank line between sections + if (consecutive_newline_count_ < 2) { + Write("\n"); + consecutive_newline_count_++; + } + should_write_indent_next_ = true; +} + +void CWriter::Write(OpenBrace) { + Write("{"); + Indent(); + Write(Newline()); +} + +void CWriter::Write(CloseBrace) { + Dedent(); + Write("}"); +} + +void CWriter::Write(uint64_t val) { + Writef("%" PRIu64, val); +} + +void CWriter::Write(std::string_view s) { + WriteData(s.data(), s.size()); +} + +void CWriter::Write(const ParamName& name) { + Write(GetLocalName(name.name, false)); +} + +void CWriter::Write(const LabelName& name) { + Write(GetLocalName(name.name, true)); +} + +void CWriter::Write(const GlobalName& name) { + Write(GetGlobalName(name.type, name.name)); +} + +void CWriter::Write(const ExternalPtr& name) { + bool is_import = import_syms_.count(name.name) != 0; + if (!is_import) { + Write("&"); + } + Write(GlobalName(name)); +} + +void CWriter::Write(const ExternalInstancePtr& name) { + bool is_import = import_syms_.count(name.name) != 0; + if (!is_import) { + Write("&"); + } + Write("instance->", GlobalName(name)); +} + +void CWriter::Write(const ExternalRef& name) { + bool is_import = import_syms_.count(name.name) != 0; + if (is_import) { + Write("(*", GlobalName(name), ")"); + } else { + Write(GlobalName(name)); + } +} + +void CWriter::Write(const ExternalInstanceRef& name) { + bool is_import = import_syms_.count(name.name) != 0; + if (is_import) { + Write("(*instance->", GlobalName(name), ")"); + } else { + Write("instance->", GlobalName(name)); + } +} + +void CWriter::Write(const GotoLabel& goto_label) { + const Label* label = FindLabel(goto_label.var); + if (label->HasValue()) { + size_t amount = label->sig.size(); + assert(type_stack_.size() >= label->type_stack_size); + assert(type_stack_.size() >= amount); + assert(type_stack_.size() - amount >= label->type_stack_size); + Index offset = type_stack_.size() - label->type_stack_size - amount; + if (offset != 0) { + for (Index i = 0; i < amount; ++i) { + Write(StackVar(amount - i - 1 + offset, label->sig[i]), " = ", + StackVar(amount - i - 1), "; "); + } + } + } + + assert(try_catch_stack_.size() >= label->try_catch_stack_size); + + if (try_catch_stack_.size() != label->try_catch_stack_size) { + const std::string& name = + try_catch_stack_.at(label->try_catch_stack_size).name; + + Write("wasm_rt_set_unwind_target(", name, "_outer_target);", Newline()); + } + + if (goto_label.var.is_name()) { + Write("goto ", LabelName(goto_label.var.name()), ";"); + } else { + // We've generated names for all labels, so we should only be using an + // index when branching to the implicit function label, which can't be + // named. + Write("goto ", LabelName(kImplicitFuncLabel), ";"); + } +} + +void CWriter::Write(const LabelDecl& label) { + if (IsTopLabelUsed()) + Write(label.name, ":;", Newline()); +} + +void CWriter::Write(const GlobalInstanceVar& var) { + assert(var.var.is_name()); + Write(ExternalInstanceRef(ModuleFieldType::Global, var.var.name())); +} + +void CWriter::Write(const StackVar& sv) { + Index index = type_stack_.size() - 1 - sv.index; + Type type = sv.type; + if (type == Type::Any) { + assert(index < type_stack_.size()); + type = type_stack_[index]; + } + + StackTypePair stp = {index, type}; + auto iter = stack_var_sym_map_.find(stp); + if (iter == stack_var_sym_map_.end()) { + std::string name = MangleType(type) + std::to_string(index); + Write(DefineStackVarName(index, type, name)); + } else { + Write(iter->second); + } +} + +// static +const char* CWriter::GetCTypeName(const Type& type) { + // clang-format off + switch (type) { + case Type::I32: return "u32"; + case Type::I64: return "u64"; + case Type::F32: return "f32"; + case Type::F64: return "f64"; + case Type::V128: return "v128"; + case Type::FuncRef: return "wasm_rt_funcref_t"; + case Type::ExternRef: return "wasm_rt_externref_t"; + default: + WABT_UNREACHABLE; + } + // clang-format on +} + +void CWriter::Write(Type type) { + Write(GetCTypeName(type)); +} + +void CWriter::Write(TypeEnum type) { + // clang-format off + switch (type.type) { + case Type::I32: Write("WASM_RT_I32"); break; + case Type::I64: Write("WASM_RT_I64"); break; + case Type::F32: Write("WASM_RT_F32"); break; + case Type::F64: Write("WASM_RT_F64"); break; + case Type::V128: Write("WASM_RT_V128"); break; + case Type::FuncRef: Write("WASM_RT_FUNCREF"); break; + case Type::ExternRef: Write("WASM_RT_EXTERNREF"); break; + default: + WABT_UNREACHABLE; + } + // clang-format on +} + +void CWriter::Write(SignedType type) { + // clang-format off + switch (type.type) { + case Type::I32: Write("s32"); break; + case Type::I64: Write("s64"); break; + default: + WABT_UNREACHABLE; + } + // clang-format on +} + +void CWriter::Write(const ResultType& rt) { + if (rt.types.empty()) { + Write("void"); + } else if (rt.types.size() == 1) { + Write(rt.types[0]); + } else { + Write("struct ", MangleMultivalueTypes(rt.types)); + } +} + +void CWriter::Write(const Const& const_) { + switch (const_.type()) { + case Type::I32: + Writef("%uu", static_cast<int32_t>(const_.u32())); + break; + + case Type::I64: + Writef("%" PRIu64 "ull", static_cast<int64_t>(const_.u64())); + break; + + case Type::F32: { + uint32_t f32_bits = const_.f32_bits(); + // TODO(binji): Share with similar float info in interp.cc and literal.cc + if ((f32_bits & 0x7f800000u) == 0x7f800000u) { + const char* sign = (f32_bits & 0x80000000) ? "-" : ""; + uint32_t significand = f32_bits & 0x7fffffu; + if (significand == 0) { + // Infinity. + Writef("%sINFINITY", sign); + } else { + // Nan. + Writef("f32_reinterpret_i32(0x%08x) /* %snan:0x%06x */", f32_bits, + sign, significand); + } + } else if (f32_bits == 0x80000000) { + // Negative zero. Special-cased so it isn't written as -0 below. + Writef("-0.f"); + } else { + Writef("%.9g", Bitcast<float>(f32_bits)); + } + break; + } + + case Type::F64: { + uint64_t f64_bits = const_.f64_bits(); + // TODO(binji): Share with similar float info in interp.cc and literal.cc + if ((f64_bits & 0x7ff0000000000000ull) == 0x7ff0000000000000ull) { + const char* sign = (f64_bits & 0x8000000000000000ull) ? "-" : ""; + uint64_t significand = f64_bits & 0xfffffffffffffull; + if (significand == 0) { + // Infinity. + Writef("%sINFINITY", sign); + } else { + // Nan. + Writef("f64_reinterpret_i64(0x%016" PRIx64 ") /* %snan:0x%013" PRIx64 + " */", + f64_bits, sign, significand); + } + } else if (f64_bits == 0x8000000000000000ull) { + // Negative zero. Special-cased so it isn't written as -0 below. + Writef("-0.0"); + } else { + Writef("%.17g", Bitcast<double>(f64_bits)); + } + break; + } + case Type::V128: { + Writef("simde_wasm_i32x4_const(0x%08x, 0x%08x, 0x%08x, 0x%08x)", + const_.vec128().u32(0), const_.vec128().u32(1), + const_.vec128().u32(2), const_.vec128().u32(3)); + break; + } + + default: + WABT_UNREACHABLE; + } +} + +void CWriter::WriteInitDecl() { + Write("void ", kAdminSymbolPrefix, module_prefix_, "_instantiate(", + ModuleInstanceTypeName(), "*"); + for (const auto& import_module_name : import_module_set_) { + Write(", struct ", ModuleInstanceTypeName(import_module_name), "*"); + } + Write(");", Newline()); +} + +void CWriter::WriteFreeDecl() { + Write("void ", kAdminSymbolPrefix, module_prefix_, "_free(", + ModuleInstanceTypeName(), "*);", Newline()); +} + +void CWriter::WriteGetFuncTypeDecl() { + Write("wasm_rt_func_type_t ", kAdminSymbolPrefix, module_prefix_, + "_get_func_type(uint32_t param_count, uint32_t result_count, ...);", + Newline()); +} + +void CWriter::WriteInitExpr(const ExprList& expr_list) { + if (expr_list.empty()) { + WABT_UNREACHABLE; + } + + std::vector<std::string> mini_stack; + + for (const auto& expr : expr_list) { + if (expr.type() == ExprType::Binary) { + // Extended const expressions include at least one binary op. + // This builds a C expression from the operands. + if (mini_stack.size() < 2) { + WABT_UNREACHABLE; + } + + const auto binexpr = cast<BinaryExpr>(&expr); + char op; + switch (binexpr->opcode) { + case Opcode::I32Add: + case Opcode::I64Add: + case Opcode::F32Add: + case Opcode::F64Add: + op = '+'; + break; + + case Opcode::I32Sub: + case Opcode::I64Sub: + case Opcode::F32Sub: + case Opcode::F64Sub: + op = '-'; + break; + + case Opcode::I32Mul: + case Opcode::I64Mul: + case Opcode::F32Mul: + case Opcode::F64Mul: + op = '*'; + break; + + default: + WABT_UNREACHABLE; + } + + std::string combination = + "((" + std::string(GetCTypeName(binexpr->opcode.GetParamType1())) + + ")" + mini_stack.at(mini_stack.size() - 2) + ")" + op + "((" + + std::string(GetCTypeName(binexpr->opcode.GetParamType2())) + ")" + + mini_stack.at(mini_stack.size() - 1) + ")"; + mini_stack.resize(mini_stack.size() - 2); + mini_stack.push_back(std::move(combination)); + } else { + // Leaf node (nullary const expression) + Stream* existing_stream = stream_; + MemoryStream terminal_stream; + stream_ = &terminal_stream; + WriteInitExprTerminal(&expr); + const auto& buf = terminal_stream.output_buffer(); + mini_stack.emplace_back(reinterpret_cast<const char*>(buf.data.data()), + buf.data.size()); + stream_ = existing_stream; + } + } + + if (mini_stack.size() != 1) { + WABT_UNREACHABLE; + } + + Write(mini_stack.front()); +} + +void CWriter::WriteInitExprTerminal(const Expr* expr) { + switch (expr->type()) { + case ExprType::Const: + Write(cast<ConstExpr>(expr)->const_); + break; + + case ExprType::GlobalGet: + Write(GlobalInstanceVar(cast<GlobalGetExpr>(expr)->var)); + break; + + case ExprType::RefFunc: { + const Func* func = module_->GetFunc(cast<RefFuncExpr>(expr)->var); + const FuncDeclaration& decl = func->decl; + + assert(decl.has_func_type); + const FuncType* func_type = module_->GetFuncType(decl.type_var); + + Write("(wasm_rt_funcref_t){", FuncTypeExpr(func_type), ", ", + "(wasm_rt_function_ptr_t)", + ExternalPtr(ModuleFieldType::Func, func->name), ", "); + + bool is_import = import_module_sym_map_.count(func->name) != 0; + if (is_import) { + Write("instance->", GlobalName(ModuleFieldType::Import, + import_module_sym_map_[func->name])); + } else { + Write("instance"); + } + + Write("};", Newline()); + } break; + + case ExprType::RefNull: + Write(GetReferenceNullValue(cast<RefNullExpr>(expr)->type)); + break; + + default: + WABT_UNREACHABLE; + } +} + +std::string CWriter::GenerateHeaderGuard() const { + std::string result; + for (char c : header_name_) { + if (isalnum(c) || c == '_') { + result += toupper(c); + } else { + result += '_'; + } + } + result += "_GENERATED_"; + return result; +} + +void CWriter::WriteSourceTop() { + Write(s_source_includes); + Write(Newline(), "#include \"", header_name_, "\"", Newline()); + Write(s_source_declarations); +} + +void CWriter::WriteMultiCTop() { + if (c_streams_.size() > 1) { + assert(header_impl_name_.size() > 0); + Write("/* Automatically generated by wasm2c */", Newline()); + Write("#include \"", header_impl_name_, "\"", Newline()); + } +} + +void CWriter::WriteMultiCTopEmpty() { + for (auto& stream : c_streams_) { + if (stream->offset() == 0) { + stream_ = stream; + Write("/* Empty wasm2c generated file */\n"); + Write("typedef int dummy_def;"); + } + } +} + +void CWriter::WriteMultivalueTypes() { + for (TypeEntry* type : module_->types) { + FuncType* func_type = cast<FuncType>(type); + Index num_results = func_type->GetNumResults(); + if (num_results <= 1) { + continue; + } + std::string name = MangleMultivalueTypes(func_type->sig.result_types); + // these ifndefs are actually to support importing multiple modules + // incidentally they also mean we don't have to bother with deduplication + Write("#ifndef ", name, Newline()); + Write("#define ", name, " ", name, Newline()); + Write("struct ", name, " ", OpenBrace()); + for (Index i = 0; i < num_results; ++i) { + Type type = func_type->GetResultType(i); + Write(type); + Writef(" %c%d;", MangleType(type), i); + Write(Newline()); + } + Write(CloseBrace(), ";", Newline(), "#endif /* ", name, " */", Newline()); + } +} + +void CWriter::WriteTagTypes() { + for (const Tag* tag : module_->tags) { + const FuncDeclaration& tag_type = tag->decl; + Index num_params = tag_type.GetNumParams(); + if (num_params <= 1) { + continue; + } + const std::string name = MangleTagTypes(tag_type.sig.param_types); + // use same method as WriteMultivalueTypes + Write("#ifndef ", name, Newline()); + Write("#define ", name, " ", name, Newline()); + Write("struct ", name, " ", OpenBrace()); + for (Index i = 0; i < num_params; ++i) { + Type type = tag_type.GetParamType(i); + Write(type); + Writef(" %c%d;", MangleType(type), i); + Write(Newline()); + } + Write(CloseBrace(), ";", Newline(), "#endif /* ", name, " */", Newline()); + } +} + +void CWriter::WriteFuncTypeDecls() { + if (module_->types.empty()) { + return; + } + + Write(Newline()); + + std::string serialized_type; + for (const TypeEntry* type : module_->types) { + const std::string name = + DefineGlobalScopeName(ModuleFieldType::Type, type->name); + + if (c_streams_.size() > 1) { + Write("FUNC_TYPE_DECL_EXTERN_T(", name, ");", Newline()); + } + } +} + +void CWriter::WriteFuncTypes() { + if (module_->types.empty()) { + return; + } + + Write(Newline()); + + std::unordered_map<std::string, std::string> type_hash; + + std::string serialized_type; + for (const TypeEntry* type : module_->types) { + const std::string name = GetGlobalName(ModuleFieldType::Type, type->name); + SerializeFuncType(*cast<FuncType>(type), serialized_type); + + auto prior_type = type_hash.find(serialized_type); + if (prior_type != type_hash.end()) { + /* duplicate function type */ + unique_func_type_names_.push_back(prior_type->second); + } else { + unique_func_type_names_.push_back(name); + type_hash.emplace(serialized_type, name); + if (c_streams_.size() > 1) { + Write("FUNC_TYPE_EXTERN_T("); + } else { + Write("FUNC_TYPE_T("); + } + Write(name, ") = \""); + for (uint8_t x : serialized_type) { + Writef("\\x%02x", x); + } + Write("\";", Newline()); + } + } +} + +void CWriter::Write(const FuncTypeExpr& expr) { + Index func_type_index = module_->GetFuncTypeIndex(expr.func_type->sig); + Write(unique_func_type_names_.at(func_type_index)); +} + +// static +void CWriter::SerializeFuncType(const FuncType& func_type, + std::string& serialized_type) { + unsigned int len = func_type.GetNumParams() + func_type.GetNumResults() + 1; + + char* const mangled_signature = static_cast<char*>(alloca(len)); + char* next_byte = mangled_signature; + + // step 1: serialize each param type + for (Index i = 0; i < func_type.GetNumParams(); ++i) { + *next_byte++ = MangleType(func_type.GetParamType(i)); + } + + // step 2: separate params and results with a space + *next_byte++ = ' '; + + // step 3: serialize each result type + for (Index i = 0; i < func_type.GetNumResults(); ++i) { + *next_byte++ = MangleType(func_type.GetResultType(i)); + } + + assert(next_byte - mangled_signature == len); + + // step 4: SHA-256 the whole string + sha256({mangled_signature, len}, serialized_type); +} + +void CWriter::WriteTagDecls() { + Index tag_index = 0; + for (const Tag* tag : module_->tags) { + bool is_import = tag_index < module_->num_tag_imports; + if (!is_import) { + // Tags are identified and compared solely by their (unique) address. + // The data stored in this variable is never read. + if (tag_index == module_->num_tag_imports) { + Write(Newline()); + Write("typedef char wasm_tag_placeholder_t;", Newline()); + } + DefineGlobalScopeName(ModuleFieldType::Tag, tag->name); + if (c_streams_.size() > 1) { + Write("extern const wasm_tag_placeholder_t ", + GlobalName(ModuleFieldType::Tag, tag->name), ";", Newline()); + } + } + tag_index++; + } +} + +void CWriter::WriteTags() { + Write(Newline()); + Index tag_index = 0; + for (const Tag* tag : module_->tags) { + bool is_import = tag_index < module_->num_tag_imports; + if (!is_import) { + Write(InternalSymbolScope(), "const wasm_tag_placeholder_t ", + GlobalName(ModuleFieldType::Tag, tag->name), ";", Newline()); + } + tag_index++; + } +} + +void CWriter::ComputeUniqueImports() { + using modname_name_pair = std::pair<std::string, std::string>; + std::map<modname_name_pair, const Import*> import_map; + for (const Import* import : module_->imports) { + // After emplacing, the returned bool says whether the insert happened; + // i.e., was there already an import with the same modname and name? + // If there was, make sure it was at least the same kind of import. + const auto iterator_and_insertion_bool = import_map.emplace( + modname_name_pair(import->module_name, import->field_name), import); + if (!iterator_and_insertion_bool.second) { + if (iterator_and_insertion_bool.first->second->kind() != import->kind()) { + UNIMPLEMENTED("contradictory import declaration"); + } else { + fprintf(stderr, "warning: duplicate import declaration \"%s\" \"%s\"\n", + import->module_name.c_str(), import->field_name.c_str()); + } + } + import_module_set_.insert(import->module_name); + if (import->kind() == ExternalKind::Func) { + import_func_module_set_.insert(import->module_name); + } + } + + for (const auto& node : import_map) { + unique_imports_.push_back(node.second); + } +} + +void CWriter::BeginInstance() { + if (module_->imports.empty()) { + Write("typedef struct ", ModuleInstanceTypeName(), " ", OpenBrace()); + return; + } + + ComputeUniqueImports(); + + // define names of per-instance imports + for (const Import* import : module_->imports) { + DefineImportName(import, import->module_name, import->field_name); + } + + // Forward declaring module instance types + for (const auto& import_module : import_module_set_) { + DefineImportedModuleInstanceName(import_module); + Write("struct ", ModuleInstanceTypeName(import_module), ";", Newline()); + } + + // Forward declaring module imports + for (const Import* import : unique_imports_) { + if ((import->kind() == ExternalKind::Func) || + (import->kind() == ExternalKind::Tag)) { + continue; + } + + Write("extern "); + switch (import->kind()) { + case ExternalKind::Global: { + const Global& global = cast<GlobalImport>(import)->global; + Write(global.type); + break; + } + + case ExternalKind::Memory: { + Write("wasm_rt_memory_t"); + break; + } + + case ExternalKind::Table: + WriteTableType(cast<TableImport>(import)->table.elem_type); + break; + + default: + WABT_UNREACHABLE; + } + Write("* ", ExportName(import->module_name, import->field_name), "(struct ", + ModuleInstanceTypeName(import->module_name), "*);", Newline()); + } + Write(Newline()); + + // Add pointers to module instances that any func is imported from, + // so that imported functions can be given their own module instances + // when invoked + Write("typedef struct ", ModuleInstanceTypeName(), " ", OpenBrace()); + for (const auto& import_module : import_func_module_set_) { + Write("struct ", ModuleInstanceTypeName(import_module), "* ", + GlobalName(ModuleFieldType::Import, import_module), ";", Newline()); + } + + for (const Import* import : unique_imports_) { + if ((import->kind() == ExternalKind::Func) || + (import->kind() == ExternalKind::Tag)) { + continue; + } + + Write("/* import: '", SanitizeForComment(import->module_name), "' '", + SanitizeForComment(import->field_name), "' */", Newline()); + + switch (import->kind()) { + case ExternalKind::Global: + WriteGlobal(cast<GlobalImport>(import)->global, + std::string("*") + + ExportName(import->module_name, import->field_name)); + break; + + case ExternalKind::Memory: + WriteMemory(std::string("*") + + ExportName(import->module_name, import->field_name)); + break; + + case ExternalKind::Table: { + const Table& table = cast<TableImport>(import)->table; + WriteTable(std::string("*") + + ExportName(import->module_name, import->field_name), + table.elem_type); + } break; + + default: + WABT_UNREACHABLE; + } + + Write(Newline()); + } +} + +// Write module-wide imports (funcs & tags), which aren't tied to an instance. +void CWriter::WriteImports() { + if (unique_imports_.empty()) + return; + + Write(Newline()); + + for (const Import* import : unique_imports_) { + if (import->kind() == ExternalKind::Func) { + Write(Newline(), "/* import: '", SanitizeForComment(import->module_name), + "' '", SanitizeForComment(import->field_name), "' */", Newline()); + const Func& func = cast<FuncImport>(import)->func; + WriteImportFuncDeclaration( + func.decl, import->module_name, + ExportName(import->module_name, import->field_name)); + Write(";"); + Write(Newline()); + } else if (import->kind() == ExternalKind::Tag) { + Write(Newline(), "/* import: '", SanitizeForComment(import->module_name), + "' '", SanitizeForComment(import->field_name), "' */", Newline()); + Write("extern const wasm_rt_tag_t ", + ExportName(import->module_name, import->field_name), ";", + Newline()); + } + } +} + +void CWriter::WriteFuncDeclarations() { + if (module_->funcs.size() == module_->num_func_imports) + return; + + Write(Newline()); + + Index func_index = 0; + for (const Func* func : module_->funcs) { + bool is_import = func_index < module_->num_func_imports; + if (!is_import) { + Write(InternalSymbolScope()); + WriteFuncDeclaration( + func->decl, DefineGlobalScopeName(ModuleFieldType::Func, func->name)); + Write(";", Newline()); + } + ++func_index; + } +} + +void CWriter::WriteFuncDeclaration(const FuncDeclaration& decl, + const std::string& name) { + Write(ResultType(decl.sig.result_types), " ", name, "("); + Write(ModuleInstanceTypeName(), "*"); + WriteParamTypes(decl); + Write(")"); +} + +void CWriter::WriteImportFuncDeclaration(const FuncDeclaration& decl, + const std::string& module_name, + const std::string& name) { + Write(ResultType(decl.sig.result_types), " ", name, "("); + Write("struct ", ModuleInstanceTypeName(module_name), "*"); + WriteParamTypes(decl); + Write(")"); +} + +void CWriter::WriteCallIndirectFuncDeclaration(const FuncDeclaration& decl, + const std::string& name) { + Write(ResultType(decl.sig.result_types), " ", name, "(void*"); + WriteParamTypes(decl); + Write(")"); +} + +void CWriter::WriteFeatureMacros() { + if (options_.features->exceptions_enabled()) { + Write("#define WASM_RT_ENABLE_EXCEPTION_HANDLING", Newline(), Newline()); + } + if (options_.features->simd_enabled()) { + Write("#define WASM_RT_ENABLE_SIMD", Newline(), Newline()); + } +} + +void CWriter::WriteModuleInstance() { + BeginInstance(); + WriteGlobals(); + WriteMemories(); + WriteTables(); + WriteDataInstances(); + WriteElemInstances(); + + // C forbids an empty struct + if (module_->globals.empty() && module_->memories.empty() && + module_->tables.empty() && import_func_module_set_.empty()) { + Write("char dummy_member;", Newline()); + } + + Write(CloseBrace(), " ", ModuleInstanceTypeName(), ";", Newline()); + Write(Newline()); +} + +void CWriter::WriteGlobals() { + Index global_index = 0; + if (module_->globals.size() != module_->num_global_imports) { + for (const Global* global : module_->globals) { + bool is_import = global_index < module_->num_global_imports; + if (!is_import) { + WriteGlobal(*global, DefineInstanceMemberName(ModuleFieldType::Global, + global->name)); + Write(Newline()); + } + ++global_index; + } + } +} + +void CWriter::WriteGlobal(const Global& global, const std::string& name) { + Write(global.type, " ", name, ";"); +} + +void CWriter::WriteGlobalPtr(const Global& global, const std::string& name) { + Write(global.type, "* ", name, "(", ModuleInstanceTypeName(), "* instance)"); +} + +void CWriter::WriteMemories() { + if (module_->memories.size() == module_->num_memory_imports) + return; + + Index memory_index = 0; + for (const Memory* memory : module_->memories) { + bool is_import = memory_index < module_->num_memory_imports; + if (!is_import) { + WriteMemory( + DefineInstanceMemberName(ModuleFieldType::Memory, memory->name)); + Write(Newline()); + } + ++memory_index; + } +} + +void CWriter::WriteMemory(const std::string& name) { + Write("wasm_rt_memory_t ", name, ";"); +} + +void CWriter::WriteMemoryPtr(const std::string& name) { + Write("wasm_rt_memory_t* ", name, "(", ModuleInstanceTypeName(), + "* instance)"); +} + +void CWriter::WriteTables() { + if (module_->tables.size() == module_->num_table_imports) { + return; + } + + Index table_index = 0; + for (const Table* table : module_->tables) { + bool is_import = table_index < module_->num_table_imports; + if (!is_import) { + WriteTable(DefineInstanceMemberName(ModuleFieldType::Table, table->name), + table->elem_type); + Write(Newline()); + } + ++table_index; + } +} + +void CWriter::WriteTable(const std::string& name, const wabt::Type& type) { + WriteTableType(type); + Write(" ", name, ";"); +} + +void CWriter::WriteTablePtr(const std::string& name, const Table& table) { + WriteTableType(table.elem_type); + Write("* ", name, "(", ModuleInstanceTypeName(), "* instance)"); +} + +void CWriter::WriteTableType(const wabt::Type& type) { + Write("wasm_rt_", GetReferenceTypeName(type), "_table_t"); +} + +void CWriter::WriteGlobalInitializers() { + if (module_->globals.empty()) + return; + + Write(Newline(), "static void init_globals(", ModuleInstanceTypeName(), + "* instance) ", OpenBrace()); + Index global_index = 0; + for (const Global* global : module_->globals) { + bool is_import = global_index < module_->num_global_imports; + if (!is_import) { + assert(!global->init_expr.empty()); + Write(ExternalInstanceRef(ModuleFieldType::Global, global->name), " = "); + WriteInitExpr(global->init_expr); + Write(";", Newline()); + } + ++global_index; + } + Write(CloseBrace(), Newline()); +} + +static inline bool is_droppable(const DataSegment* data_segment) { + return (data_segment->kind == SegmentKind::Passive) && + (!data_segment->data.empty()); +} + +static inline bool is_droppable(const ElemSegment* elem_segment) { + return (elem_segment->kind == SegmentKind::Passive) && + (!elem_segment->elem_exprs.empty()); +} + +void CWriter::WriteDataInstances() { + for (const DataSegment* data_segment : module_->data_segments) { + std::string name = + DefineGlobalScopeName(ModuleFieldType::DataSegment, data_segment->name); + if (is_droppable(data_segment)) { + Write("bool ", "data_segment_dropped_", name, " : 1;", Newline()); + } + } +} + +void CWriter::WriteDataInitializerDecls() { + if (module_->memories.empty()) { + return; + } + + for (const DataSegment* data_segment : module_->data_segments) { + if (data_segment->data.empty()) { + continue; + } + + if (c_streams_.size() > 1) { + Write(Newline(), "extern const u8 data_segment_data_", + GlobalName(ModuleFieldType::DataSegment, data_segment->name), "[];", + Newline()); + } + } +} + +void CWriter::WriteDataInitializers() { + if (module_->memories.empty()) { + return; + } + + for (const DataSegment* data_segment : module_->data_segments) { + if (data_segment->data.empty()) { + continue; + } + + Write(Newline(), InternalSymbolScope(), "const u8 data_segment_data_", + GlobalName(ModuleFieldType::DataSegment, data_segment->name), + "[] = ", OpenBrace()); + size_t i = 0; + for (uint8_t x : data_segment->data) { + Writef("0x%02x, ", x); + if ((++i % 12) == 0) + Write(Newline()); + } + if (i > 0) + Write(Newline()); + Write(CloseBrace(), ";", Newline()); + } + + Write(Newline(), "static void init_memories(", ModuleInstanceTypeName(), + "* instance) ", OpenBrace()); + if (module_->memories.size() > module_->num_memory_imports) { + Index memory_idx = module_->num_memory_imports; + for (Index i = memory_idx; i < module_->memories.size(); i++) { + const Memory* memory = module_->memories[i]; + uint64_t max; + if (memory->page_limits.has_max) { + max = memory->page_limits.max; + } else { + max = memory->page_limits.is_64 ? (static_cast<uint64_t>(1) << 48) + : 65536; + } + Write("wasm_rt_allocate_memory(", + ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", ", + memory->page_limits.initial, ", ", max, ", ", + memory->page_limits.is_64, ");", Newline()); + } + } + + for (const DataSegment* data_segment : module_->data_segments) { + if (data_segment->kind != SegmentKind::Active) { + continue; + } + const Memory* memory = + module_->memories[module_->GetMemoryIndex(data_segment->memory_var)]; + Write("LOAD_DATA(", + ExternalInstanceRef(ModuleFieldType::Memory, memory->name), ", "); + WriteInitExpr(data_segment->offset); + if (data_segment->data.empty()) { + Write(", NULL, 0"); + } else { + Write(", data_segment_data_", + GlobalName(ModuleFieldType::DataSegment, data_segment->name), ", ", + data_segment->data.size()); + } + Write(");", Newline()); + } + + Write(CloseBrace(), Newline()); + + if (!module_->data_segments.empty()) { + Write(Newline(), "static void init_data_instances(", + ModuleInstanceTypeName(), " *instance) ", OpenBrace()); + + for (const DataSegment* data_segment : module_->data_segments) { + if (is_droppable(data_segment)) { + Write("instance->data_segment_dropped_", + GlobalName(ModuleFieldType::DataSegment, data_segment->name), + " = false;", Newline()); + } + } + + Write(CloseBrace(), Newline()); + } +} + +void CWriter::WriteElemInstances() { + for (const ElemSegment* elem_segment : module_->elem_segments) { + std::string name = + DefineGlobalScopeName(ModuleFieldType::ElemSegment, elem_segment->name); + if (is_droppable(elem_segment)) { + Write("bool ", "elem_segment_dropped_", name, " : 1;", Newline()); + } + } +} + +void CWriter::WriteElemInitializerDecls() { + if (module_->tables.empty()) { + return; + } + + for (const ElemSegment* elem_segment : module_->elem_segments) { + if (elem_segment->elem_exprs.empty()) { + continue; + } + + if (elem_segment->elem_type == Type::ExternRef) { + // no need to store externref elem initializers because only + // ref.null is possible + continue; + } + + if (c_streams_.size() > 1) { + Write(Newline(), + "extern const wasm_elem_segment_expr_t elem_segment_exprs_", + GlobalName(ModuleFieldType::ElemSegment, elem_segment->name), "[];", + Newline()); + } + } +} + +void CWriter::WriteElemInitializers() { + if (module_->tables.empty()) { + return; + } + + for (const ElemSegment* elem_segment : module_->elem_segments) { + if (elem_segment->elem_exprs.empty()) { + continue; + } + + if (elem_segment->elem_type == Type::ExternRef) { + // no need to store externref elem initializers because only + // ref.null is possible + continue; + } + + Write(Newline(), InternalSymbolScope(), + "const wasm_elem_segment_expr_t elem_segment_exprs_", + GlobalName(ModuleFieldType::ElemSegment, elem_segment->name), + "[] = ", OpenBrace()); + + for (const ExprList& elem_expr : elem_segment->elem_exprs) { + assert(elem_expr.size() == 1); + const Expr& expr = elem_expr.front(); + switch (expr.type()) { + case ExprType::RefFunc: { + const Func* func = module_->GetFunc(cast<RefFuncExpr>(&expr)->var); + const FuncType* func_type = module_->GetFuncType(func->decl.type_var); + Write("{", FuncTypeExpr(func_type), ", (wasm_rt_function_ptr_t)", + ExternalPtr(ModuleFieldType::Func, func->name), ", "); + const bool is_import = import_module_sym_map_.count(func->name) != 0; + if (is_import) { + Write("offsetof(", ModuleInstanceTypeName(), ", ", + GlobalName(ModuleFieldType::Import, + import_module_sym_map_[func->name]), + ")"); + } else { + Write("0"); + } + Write("},", Newline()); + } break; + case ExprType::RefNull: + Write("{NULL, NULL, 0},", Newline()); + break; + default: + WABT_UNREACHABLE; + } + } + Write(CloseBrace(), ";", Newline()); + } + + Write(Newline(), "static void init_tables(", ModuleInstanceTypeName(), + "* instance) ", OpenBrace()); + + if (module_->tables.size() > module_->num_table_imports) { + Index table_idx = module_->num_table_imports; + for (Index i = table_idx; i < module_->tables.size(); i++) { + const Table* table = module_->tables[i]; + uint32_t max = + table->elem_limits.has_max ? table->elem_limits.max : UINT32_MAX; + Write("wasm_rt_allocate_", GetReferenceTypeName(table->elem_type), + "_table(", ExternalInstancePtr(ModuleFieldType::Table, table->name), + ", ", table->elem_limits.initial, ", ", max, ");", Newline()); + } + } + + for (const ElemSegment* elem_segment : module_->elem_segments) { + if (elem_segment->kind != SegmentKind::Active) { + continue; + } + + const Table* table = module_->GetTable(elem_segment->table_var); + + WriteElemTableInit(true, elem_segment, table); + } + + Write(CloseBrace(), Newline()); + + if (!module_->elem_segments.empty()) { + Write(Newline(), "static void init_elem_instances(", + ModuleInstanceTypeName(), " *instance) ", OpenBrace()); + + for (const ElemSegment* elem_segment : module_->elem_segments) { + if (is_droppable(elem_segment)) { + Write("instance->elem_segment_dropped_", + GlobalName(ModuleFieldType::ElemSegment, elem_segment->name), + " = false;", Newline()); + } + } + + Write(CloseBrace(), Newline()); + } +} + +void CWriter::WriteElemTableInit(bool active_initialization, + const ElemSegment* src_segment, + const Table* dst_table) { + assert(dst_table->elem_type == Type::FuncRef || + dst_table->elem_type == Type::ExternRef); + assert(dst_table->elem_type == src_segment->elem_type); + + Write(GetReferenceTypeName(dst_table->elem_type), "_table_init(", + ExternalInstancePtr(ModuleFieldType::Table, dst_table->name), ", "); + + // elem segment exprs needed only for funcref tables + // because externref tables can only be initialized with ref.null + if (dst_table->elem_type == Type::FuncRef) { + if (src_segment->elem_exprs.empty()) { + Write("NULL, "); + } else { + Write("elem_segment_exprs_", + GlobalName(ModuleFieldType::ElemSegment, src_segment->name), ", "); + } + } + + // src_size, dest_addr, src_addr, N + if (active_initialization) { + Write(src_segment->elem_exprs.size(), ", "); + WriteInitExpr(src_segment->offset); + Write(", 0, ", src_segment->elem_exprs.size()); + } else { + if (is_droppable(src_segment)) { + Write("(instance->elem_segment_dropped_", + GlobalName(ModuleFieldType::ElemSegment, src_segment->name), + " ? 0 : ", src_segment->elem_exprs.size(), "), "); + } else { + Write("0, "); + } + Write(StackVar(2), ", ", StackVar(1), ", ", StackVar(0)); + } + + if (dst_table->elem_type == Type::FuncRef) { + Write(", instance"); + } + + Write(");", Newline()); +} + +void CWriter::WriteExports(CWriterPhase kind) { + if (module_->exports.empty()) + return; + + for (const Export* export_ : module_->exports) { + Write(Newline(), "/* export: '", SanitizeForComment(export_->name), "' */", + Newline()); + + const std::string mangled_name = ExportName(export_->name); + std::string internal_name; + std::vector<std::string> index_to_name; + + switch (export_->kind) { + case ExternalKind::Func: { + const Func* func = module_->GetFunc(export_->var); + internal_name = func->name; + if (kind == CWriterPhase::Declarations) { + WriteFuncDeclaration(func->decl, mangled_name); + } else { + func_ = func; + local_syms_ = global_syms_; + local_sym_map_.clear(); + stack_var_sym_map_.clear(); + Write(ResultType(func_->decl.sig.result_types), " ", mangled_name, + "("); + MakeTypeBindingReverseMapping(func_->GetNumParamsAndLocals(), + func_->bindings, &index_to_name); + WriteParams(index_to_name); + } + break; + } + + case ExternalKind::Global: { + const Global* global = module_->GetGlobal(export_->var); + internal_name = global->name; + WriteGlobalPtr(*global, mangled_name); + break; + } + + case ExternalKind::Memory: { + const Memory* memory = module_->GetMemory(export_->var); + internal_name = memory->name; + WriteMemoryPtr(mangled_name); + break; + } + + case ExternalKind::Table: { + const Table* table = module_->GetTable(export_->var); + internal_name = table->name; + WriteTablePtr(mangled_name, *table); + break; + } + + case ExternalKind::Tag: { + const Tag* tag = module_->GetTag(export_->var); + internal_name = tag->name; + if (kind == CWriterPhase::Declarations) { + Write("extern "); + } + Write("const wasm_rt_tag_t ", mangled_name); + break; + } + + default: + WABT_UNREACHABLE; + } + + if (kind == CWriterPhase::Declarations) { + Write(";", Newline()); + continue; + } + + Write(" "); + switch (export_->kind) { + case ExternalKind::Func: { + Write(OpenBrace()); + Write("return ", ExternalRef(ModuleFieldType::Func, internal_name), + "("); + + bool is_import = import_module_sym_map_.count(internal_name) != 0; + if (is_import) { + Write("instance->", + GlobalName(ModuleFieldType::Import, + import_module_sym_map_[internal_name])); + } else { + Write("instance"); + } + WriteParamSymbols(index_to_name); + Write(CloseBrace(), Newline()); + + local_sym_map_.clear(); + stack_var_sym_map_.clear(); + func_ = nullptr; + break; + } + + case ExternalKind::Global: + Write(OpenBrace()); + Write("return ", + ExternalInstancePtr(ModuleFieldType::Global, internal_name), ";", + Newline()); + Write(CloseBrace(), Newline()); + break; + + case ExternalKind::Memory: + Write(OpenBrace()); + Write("return ", + ExternalInstancePtr(ModuleFieldType::Memory, internal_name), ";", + Newline()); + Write(CloseBrace(), Newline()); + break; + + case ExternalKind::Table: + Write(OpenBrace()); + Write("return ", + ExternalInstancePtr(ModuleFieldType::Table, internal_name), ";", + Newline()); + Write(CloseBrace(), Newline()); + break; + + case ExternalKind::Tag: + Write("= ", ExternalPtr(ModuleFieldType::Tag, internal_name), ";", + Newline()); + break; + + default: + WABT_UNREACHABLE; + } + } +} + +void CWriter::WriteInit() { + Write(Newline(), "void ", kAdminSymbolPrefix, module_prefix_, "_instantiate(", + ModuleInstanceTypeName(), "* instance"); + for (const auto& import_module_name : import_module_set_) { + Write(", struct ", ModuleInstanceTypeName(import_module_name), "* ", + GlobalName(ModuleFieldType::Import, import_module_name)); + } + Write(") ", OpenBrace()); + + Write("assert(wasm_rt_is_initialized());", Newline()); + + if (!import_module_set_.empty()) { + Write("init_instance_import(instance"); + for (const auto& import_module_name : import_module_set_) { + Write(", ", GlobalName(ModuleFieldType::Import, import_module_name)); + } + Write(");", Newline()); + } + + if (!module_->globals.empty()) { + Write("init_globals(instance);", Newline()); + } + if (!module_->tables.empty()) { + Write("init_tables(instance);", Newline()); + } + if (!module_->memories.empty()) { + Write("init_memories(instance);", Newline()); + } + if (!module_->tables.empty() && !module_->elem_segments.empty()) { + Write("init_elem_instances(instance);", Newline()); + } + if (!module_->memories.empty() && !module_->data_segments.empty()) { + Write("init_data_instances(instance);", Newline()); + } + + for (Var* var : module_->starts) { + Write(ExternalRef(ModuleFieldType::Func, module_->GetFunc(*var)->name)); + bool is_import = + import_module_sym_map_.count(module_->GetFunc(*var)->name) != 0; + if (is_import) { + Write("(instance->", + GlobalName(ModuleFieldType::Import, + import_module_sym_map_[module_->GetFunc(*var)->name]), + ");"); + } else { + Write("(instance);"); + } + Write(Newline()); + } + Write(CloseBrace(), Newline()); +} + +void CWriter::WriteGetFuncType() { + Write(Newline(), "wasm_rt_func_type_t ", kAdminSymbolPrefix, module_prefix_, + "_get_func_type(uint32_t param_count, uint32_t result_count, " + "...) ", + OpenBrace()); + + Write("va_list args;", Newline()); + + for (const TypeEntry* type : module_->types) { + const FuncType* func_type = cast<FuncType>(type); + const FuncSignature& signature = func_type->sig; + + Write(Newline(), "if (param_count == ", signature.GetNumParams(), + " && result_count == ", signature.GetNumResults(), ") ", OpenBrace()); + Write("va_start(args, result_count);", Newline()); + Write("if (true"); + for (const auto& t : signature.param_types) { + Write(" && va_arg(args, wasm_rt_type_t) == ", TypeEnum(t)); + } + for (const auto& t : signature.result_types) { + Write(" && va_arg(args, wasm_rt_type_t) == ", TypeEnum(t)); + } + Write(") ", OpenBrace()); + Write("va_end(args);", Newline()); + Write("return ", FuncTypeExpr(func_type), ";", Newline()); + Write(CloseBrace(), Newline()); + Write("va_end(args);", Newline()); + Write(CloseBrace(), Newline()); + } + + Write(Newline(), "return NULL;", Newline()); + Write(CloseBrace(), Newline()); +} + +void CWriter::WriteInitInstanceImport() { + if (import_module_set_.empty()) + return; + + Write(Newline(), "static void init_instance_import(", + ModuleInstanceTypeName(), "* instance"); + for (const auto& import_module_name : import_module_set_) { + Write(", struct ", ModuleInstanceTypeName(import_module_name), "* ", + GlobalName(ModuleFieldType::Import, import_module_name)); + } + Write(")", OpenBrace()); + + for (const auto& import_module : import_func_module_set_) { + Write("instance->", GlobalName(ModuleFieldType::Import, import_module), + " = ", GlobalName(ModuleFieldType::Import, import_module), ";", + Newline()); + } + + for (const Import* import : unique_imports_) { + switch (import->kind()) { + case ExternalKind::Func: + case ExternalKind::Tag: + break; + + case ExternalKind::Global: + case ExternalKind::Memory: + case ExternalKind::Table: { + Write("instance->", ExportName(import->module_name, import->field_name), + " = ", ExportName(import->module_name, import->field_name), "(", + GlobalName(ModuleFieldType::Import, import->module_name), ");", + Newline()); + break; + } + + default: + WABT_UNREACHABLE; + } + } + Write(CloseBrace(), Newline()); +} + +void CWriter::WriteImportProperties(CWriterPhase kind) { + if (import_module_set_.empty()) + return; + + Write(Newline()); + + auto write_import_prop = [&](const Import* import, std::string prop, + std::string type, uint64_t value) { + if (kind == CWriterPhase::Declarations) { + Write("extern "); + } + Write("const ", type, " ", kAdminSymbolPrefix, module_prefix_, "_", prop, + "_", MangleModuleName(import->module_name), "_", + MangleName(import->field_name)); + if (kind == CWriterPhase::Definitions) { + Write(" = ", value); + } + + Write(";", Newline()); + }; + + for (const Import* import : unique_imports_) { + if (import->kind() == ExternalKind::Memory) { + const Limits* limits = &(cast<MemoryImport>(import)->memory.page_limits); + // We use u64 so we can handle both 32-bit and 64-bit memories + const uint64_t default_max = limits->is_64 + ? (static_cast<uint64_t>(1) << 48) + : (static_cast<uint64_t>(1) << 16); + write_import_prop(import, "min", "u64", limits->initial); + write_import_prop(import, "max", "u64", + limits->has_max ? limits->max : default_max); + write_import_prop(import, "is64", "u8", limits->is_64); + } else if (import->kind() == ExternalKind::Table) { + const Limits* limits = &(cast<TableImport>(import)->table.elem_limits); + const uint64_t default_max = std::numeric_limits<uint32_t>::max(); + write_import_prop(import, "min", "u32", limits->initial); + write_import_prop(import, "max", "u32", + limits->has_max ? limits->max : default_max); + } else { + continue; + } + } +} + +void CWriter::WriteFree() { + Write(Newline(), "void ", kAdminSymbolPrefix, module_prefix_, "_free(", + ModuleInstanceTypeName(), "* instance) ", OpenBrace()); + + { + Index table_index = 0; + for (const Table* table : module_->tables) { + bool is_import = table_index < module_->num_table_imports; + if (!is_import) { + Write("wasm_rt_free_", GetReferenceTypeName(table->elem_type), + "_table(", + ExternalInstancePtr(ModuleFieldType::Table, table->name), ");", + Newline()); + } + ++table_index; + } + } + + { + Index memory_index = 0; + for (const Memory* memory : module_->memories) { + bool is_import = memory_index < module_->num_memory_imports; + if (!is_import) { + Write("wasm_rt_free_memory(", + ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ");", + Newline()); + } + ++memory_index; + } + } + + Write(CloseBrace(), Newline()); +} + +void CWriter::WriteFuncs() { + std::vector<size_t> c_stream_assignment = + name_to_output_file_index_(module_->funcs.begin(), module_->funcs.end(), + module_->num_func_imports, c_streams_.size()); + Index func_index = 0; + for (const Func* func : module_->funcs) { + bool is_import = func_index < module_->num_func_imports; + if (!is_import) { + stream_ = c_streams_.at(c_stream_assignment.at(func_index)); + Write(*func); + } + ++func_index; + } +} + +void CWriter::PushFuncSection(std::string_view include_condition) { + func_sections_.emplace_back(include_condition, MemoryStream{}); + stream_ = &func_sections_.back().second; +} + +void CWriter::Write(const Func& func) { + func_ = &func; + local_syms_.clear(); + local_sym_map_.clear(); + stack_var_sym_map_.clear(); + func_sections_.clear(); + func_includes_.clear(); + + Stream* prev_stream = stream_; + + /* + * If offset of stream_ is 0, this is the first time some function is written + * to this stream, then write multi c top. + */ + if (stream_->offset() == 0) { + WriteMultiCTop(); + } + Write(Newline()); + + PushFuncSection(); + Write(ResultType(func.decl.sig.result_types), " ", + GlobalName(ModuleFieldType::Func, func.name), "("); + WriteParamsAndLocals(); + Write("FUNC_PROLOGUE;", Newline()); + + PushFuncSection(); + + std::string label = DefineLabelName(kImplicitFuncLabel); + ResetTypeStack(0); + std::string empty; // Must not be temporary, since address is taken by Label. + PushLabel(LabelType::Func, empty, func.decl.sig); + Write(func.exprs, LabelDecl(label)); + PopLabel(); + ResetTypeStack(0); + PushTypes(func.decl.sig.result_types); + Write("FUNC_EPILOGUE;", Newline()); + + // Return the top of the stack implicitly. + Index num_results = func.GetNumResults(); + if (num_results == 1) { + Write("return ", StackVar(0), ";", Newline()); + } else if (num_results >= 2) { + Write(OpenBrace()); + Write(ResultType(func.decl.sig.result_types), " tmp;", Newline()); + for (Index i = 0; i < num_results; ++i) { + Type type = func.GetResultType(i); + Writef("tmp.%c%d = ", MangleType(type), i); + Write(StackVar(num_results - i - 1), ";", Newline()); + } + Write("return tmp;", Newline()); + Write(CloseBrace(), Newline()); + } + + stream_ = prev_stream; + + for (size_t i = 0; i < func_sections_.size(); ++i) { + auto& [condition, stream] = func_sections_.at(i); + std::unique_ptr<OutputBuffer> buf = stream.ReleaseOutputBuffer(); + if (condition.empty() || func_includes_.count(condition)) { + stream_->WriteData(buf->data.data(), buf->data.size()); + } + + if (i == 0) { + WriteStackVarDeclarations(); // these come immediately after section #0 + // (return type/name/params/locals) + } + } + + Write(CloseBrace(), Newline()); + + func_ = nullptr; +} + +void CWriter::WriteParamsAndLocals() { + std::vector<std::string> index_to_name; + MakeTypeBindingReverseMapping(func_->GetNumParamsAndLocals(), func_->bindings, + &index_to_name); + WriteParams(index_to_name); + Write(" ", OpenBrace()); + WriteLocals(index_to_name); +} + +void CWriter::WriteParams(const std::vector<std::string>& index_to_name) { + Write(ModuleInstanceTypeName(), "* instance"); + if (func_->GetNumParams() != 0) { + Indent(4); + for (Index i = 0; i < func_->GetNumParams(); ++i) { + Write(", "); + if (i != 0 && (i % 8) == 0) { + Write(Newline()); + } + Write(func_->GetParamType(i), " ", DefineParamName(index_to_name[i])); + } + Dedent(4); + } + Write(")"); +} + +void CWriter::WriteParamSymbols(const std::vector<std::string>& index_to_name) { + if (func_->GetNumParams() != 0) { + Indent(4); + for (Index i = 0; i < func_->GetNumParams(); ++i) { + Write(", "); + if (i != 0 && (i % 8) == 0) { + Write(Newline()); + } + Write(ParamName(index_to_name[i])); + } + Dedent(4); + } + Write(");", Newline()); +} + +void CWriter::WriteParamTypes(const FuncDeclaration& decl) { + if (decl.GetNumParams() != 0) { + for (Index i = 0; i < decl.GetNumParams(); ++i) { + Write(", "); + Write(decl.GetParamType(i)); + } + } +} + +void CWriter::WriteLocals(const std::vector<std::string>& index_to_name) { + Index num_params = func_->GetNumParams(); + for (Type type : {Type::I32, Type::I64, Type::F32, Type::F64, Type::V128, + Type::FuncRef, Type::ExternRef}) { + Index local_index = 0; + size_t count = 0; + for (Type local_type : func_->local_types) { + if (local_type == type) { + if (count == 0) { + Write(type, " "); + Indent(4); + } else { + Write(", "); + if ((count % 8) == 0) + Write(Newline()); + } + + Write(DefineParamName(index_to_name[num_params + local_index]), " = "); + if (local_type == Type::FuncRef || local_type == Type::ExternRef) { + Write(GetReferenceNullValue(local_type)); + } else if (local_type == Type::V128) { + Write("simde_wasm_i64x2_make(0, 0)"); + } else { + Write("0"); + } + ++count; + } + ++local_index; + } + if (count != 0) { + Dedent(4); + Write(";", Newline()); + } + } +} + +void CWriter::WriteStackVarDeclarations() { + for (Type type : {Type::I32, Type::I64, Type::F32, Type::F64, Type::V128, + Type::FuncRef, Type::ExternRef}) { + size_t count = 0; + for (const auto& [pair, name] : stack_var_sym_map_) { + Type stp_type = pair.second; + + if (stp_type == type) { + if (count == 0) { + Write(type, " "); + Indent(4); + } else { + Write(", "); + if ((count % 8) == 0) + Write(Newline()); + } + + Write(name); + ++count; + } + } + if (count != 0) { + Dedent(4); + Write(";", Newline()); + } + } +} + +void CWriter::Write(const Block& block) { + std::string label = DefineLabelName(block.label); + DropTypes(block.decl.GetNumParams()); + size_t mark = MarkTypeStack(); + PushLabel(LabelType::Block, block.label, block.decl.sig); + PushTypes(block.decl.sig.param_types); + Write(block.exprs, LabelDecl(label)); + ResetTypeStack(mark); + PopLabel(); + PushTypes(block.decl.sig.result_types); +} + +size_t CWriter::BeginTry(const TryExpr& tryexpr) { + Write(OpenBrace()); /* beginning of try-catch */ + const std::string tlabel = DefineLabelName(tryexpr.block.label); + Write("WASM_RT_UNWIND_TARGET *", tlabel, + "_outer_target = wasm_rt_get_unwind_target();", Newline()); + Write("WASM_RT_UNWIND_TARGET ", tlabel, "_unwind_target;", Newline()); + Write("if (!wasm_rt_try(", tlabel, "_unwind_target)) "); + Write(OpenBrace()); /* beginning of try block */ + DropTypes(tryexpr.block.decl.GetNumParams()); + const size_t mark = MarkTypeStack(); + PushLabel(LabelType::Try, tryexpr.block.label, tryexpr.block.decl.sig); + PushTypes(tryexpr.block.decl.sig.param_types); + Write("wasm_rt_set_unwind_target(&", tlabel, "_unwind_target);", Newline()); + PushTryCatch(tlabel); + Write(tryexpr.block.exprs); + ResetTypeStack(mark); + Write("wasm_rt_set_unwind_target(", tlabel, "_outer_target);", Newline()); + Write(CloseBrace()); /* end of try block */ + Write(" else ", OpenBrace()); /* beginning of catch blocks or delegate */ + assert(label_stack_.back().name == tryexpr.block.label); + assert(label_stack_.back().label_type == LabelType::Try); + label_stack_.back().label_type = LabelType::Catch; + if (try_catch_stack_.back().used) { + Write(tlabel, "_catch:;", Newline()); + } + + return mark; +} + +void CWriter::WriteTryCatch(const TryExpr& tryexpr) { + const size_t mark = BeginTry(tryexpr); + + /* exception has been thrown -- do we catch it? */ + + const LabelName tlabel = LabelName(tryexpr.block.label); + + Write("wasm_rt_set_unwind_target(", tlabel, "_outer_target);", Newline()); + PopTryCatch(); + + /* save the thrown exception to the stack if it might be rethrown later */ + PushFuncSection(tryexpr.block.label); + Write("/* save exception ", tlabel, " for rethrow */", Newline()); + Write("const wasm_rt_tag_t ", tlabel, "_tag = wasm_rt_exception_tag();", + Newline()); + Write("uint32_t ", tlabel, "_size = wasm_rt_exception_size();", Newline()); + Write("void *", tlabel, " = alloca(", tlabel, "_size);", Newline()); + Write("wasm_rt_memcpy(", tlabel, ", wasm_rt_exception(), ", tlabel, "_size);", + Newline()); + PushFuncSection(); + + assert(!tryexpr.catches.empty()); + bool has_catch_all{}; + for (auto it = tryexpr.catches.cbegin(); it != tryexpr.catches.cend(); ++it) { + if (it == tryexpr.catches.cbegin()) { + Write(Newline()); + } else { + Write(" else "); + } + ResetTypeStack(mark); + Write(*it); + if (it->IsCatchAll()) { + has_catch_all = true; + break; + } + } + if (!has_catch_all) { + /* if not caught, rethrow */ + Write(" else ", OpenBrace()); + WriteThrow(); + Write(CloseBrace(), Newline()); + } + Write(CloseBrace(), Newline()); /* end of catch blocks */ + Write(CloseBrace(), Newline()); /* end of try-catch */ + + ResetTypeStack(mark); + Write(LabelDecl(label_stack_.back().name)); + PopLabel(); + PushTypes(tryexpr.block.decl.sig.result_types); +} + +void CWriter::Write(const Catch& c) { + if (c.IsCatchAll()) { + Write(c.exprs); + return; + } + + Write("if (wasm_rt_exception_tag() == ", + ExternalPtr(ModuleFieldType::Tag, module_->GetTag(c.var)->name), ") ", + OpenBrace()); + + const Tag* tag = module_->GetTag(c.var); + const FuncDeclaration& tag_type = tag->decl; + const Index num_params = tag_type.GetNumParams(); + if (num_params == 1) { + PushType(tag_type.GetParamType(0)); + Write("wasm_rt_memcpy(&", StackVar(0), ", wasm_rt_exception(), sizeof(", + tag_type.GetParamType(0), "));", Newline()); + } else if (num_params > 1) { + for (const auto& type : tag_type.sig.param_types) { + PushType(type); + } + Write(OpenBrace()); + Write("struct ", MangleTagTypes(tag_type.sig.param_types), " tmp;", + Newline()); + Write("wasm_rt_memcpy(&tmp, wasm_rt_exception(), sizeof(tmp));", Newline()); + for (unsigned int i = 0; i < tag_type.sig.param_types.size(); ++i) { + Write(StackVar(i)); + Writef(" = tmp.%c%d;", MangleType(tag_type.sig.param_types.at(i)), i); + Write(Newline()); + } + + Write(CloseBrace(), Newline()); + } + + Write(c.exprs); + Write(CloseBrace()); +} + +void CWriter::WriteThrow() { + if (try_catch_stack_.empty()) { + Write("wasm_rt_throw();", Newline()); + } else { + Write("goto ", try_catch_stack_.back().name, "_catch;", Newline()); + try_catch_stack_.back().used = true; + } +} + +void CWriter::PushTryCatch(const std::string& name) { + try_catch_stack_.emplace_back(name, try_catch_stack_.size()); +} + +void CWriter::PopTryCatch() { + assert(!try_catch_stack_.empty()); + try_catch_stack_.pop_back(); +} + +void CWriter::WriteTryDelegate(const TryExpr& tryexpr) { + const size_t mark = BeginTry(tryexpr); + + /* exception has been thrown -- where do we delegate it? */ + + if (tryexpr.delegate_target.is_index()) { + /* must be the implicit function label */ + assert(!try_catch_stack_.empty()); + const std::string& unwind_name = try_catch_stack_.at(0).name; + Write("wasm_rt_set_unwind_target(", unwind_name, "_outer_target);", + Newline()); + + Write("wasm_rt_throw();", Newline()); + } else { + const Label* label = FindLabel(tryexpr.delegate_target, false); + + assert(try_catch_stack_.size() >= label->try_catch_stack_size); + + if (label->label_type == LabelType::Try) { + Write("goto ", LabelName(label->name), "_catch;", Newline()); + try_catch_stack_.at(label->try_catch_stack_size).used = true; + } else if (label->try_catch_stack_size == 0) { + assert(!try_catch_stack_.empty()); + const std::string& unwind_name = try_catch_stack_.at(0).name; + Write("wasm_rt_set_unwind_target(", unwind_name, "_outer_target);", + Newline()); + + Write("wasm_rt_throw();", Newline()); + } else { + const std::string label_target = + try_catch_stack_.at(label->try_catch_stack_size - 1).name + "_catch"; + Write("goto ", label_target, ";", Newline()); + try_catch_stack_.at(label->try_catch_stack_size - 1).used = true; + } + } + + Write(CloseBrace(), Newline()); + Write(CloseBrace(), Newline()); + + PopTryCatch(); + ResetTypeStack(mark); + Write(LabelDecl(label_stack_.back().name)); + PopLabel(); + PushTypes(tryexpr.block.decl.sig.result_types); +} + +void CWriter::Write(const ExprList& exprs) { + for (const Expr& expr : exprs) { + switch (expr.type()) { + case ExprType::Binary: + Write(*cast<BinaryExpr>(&expr)); + break; + + case ExprType::Block: + Write(cast<BlockExpr>(&expr)->block); + break; + + case ExprType::Br: + Write(GotoLabel(cast<BrExpr>(&expr)->var), Newline()); + // Stop processing this ExprList, since the following are unreachable. + return; + + case ExprType::BrIf: + Write("if (", StackVar(0), ") {"); + DropTypes(1); + Write(GotoLabel(cast<BrIfExpr>(&expr)->var), "}", Newline()); + break; + + case ExprType::BrTable: { + const auto* bt_expr = cast<BrTableExpr>(&expr); + Write("switch (", StackVar(0), ") ", OpenBrace()); + DropTypes(1); + Index i = 0; + for (const Var& var : bt_expr->targets) { + Write("case ", i++, ": ", GotoLabel(var), Newline()); + } + Write("default: "); + Write(GotoLabel(bt_expr->default_target), Newline(), CloseBrace(), + Newline()); + // Stop processing this ExprList, since the following are unreachable. + return; + } + + case ExprType::Call: { + const Var& var = cast<CallExpr>(&expr)->var; + const Func& func = *module_->GetFunc(var); + Index num_params = func.GetNumParams(); + Index num_results = func.GetNumResults(); + assert(type_stack_.size() >= num_params); + if (num_results > 1) { + Write(OpenBrace()); + Write("struct ", MangleMultivalueTypes(func.decl.sig.result_types)); + Write(" tmp = "); + } else if (num_results == 1) { + Write(StackVar(num_params - 1, func.GetResultType(0)), " = "); + } + + assert(var.is_name()); + Write(ExternalRef(ModuleFieldType::Func, var.name()), "("); + bool is_import = import_module_sym_map_.count(func.name) != 0; + if (is_import) { + Write("instance->", GlobalName(ModuleFieldType::Import, + import_module_sym_map_[func.name])); + } else { + Write("instance"); + } + for (Index i = 0; i < num_params; ++i) { + Write(", "); + Write(StackVar(num_params - i - 1)); + } + Write(");", Newline()); + DropTypes(num_params); + if (num_results > 1) { + for (Index i = 0; i < num_results; ++i) { + Type type = func.GetResultType(i); + PushType(type); + Write(StackVar(0)); + Writef(" = tmp.%c%d;", MangleType(type), i); + Write(Newline()); + } + Write(CloseBrace(), Newline()); + } else { + PushTypes(func.decl.sig.result_types); + } + break; + } + + case ExprType::CallIndirect: { + const FuncDeclaration& decl = cast<CallIndirectExpr>(&expr)->decl; + Index num_params = decl.GetNumParams(); + Index num_results = decl.GetNumResults(); + assert(type_stack_.size() > num_params); + if (num_results > 1) { + Write(OpenBrace()); + Write("struct ", MangleMultivalueTypes(decl.sig.result_types)); + Write(" tmp = "); + } else if (num_results == 1) { + Write(StackVar(num_params, decl.GetResultType(0)), " = "); + } + + const Table* table = + module_->GetTable(cast<CallIndirectExpr>(&expr)->table); + + assert(decl.has_func_type); + const FuncType* func_type = module_->GetFuncType(decl.type_var); + + Write("CALL_INDIRECT(", + ExternalInstanceRef(ModuleFieldType::Table, table->name), ", "); + WriteCallIndirectFuncDeclaration(decl, "(*)"); + Write(", ", FuncTypeExpr(func_type), ", ", StackVar(0)); + Write(", ", ExternalInstanceRef(ModuleFieldType::Table, table->name), + ".data[", StackVar(0), "].module_instance"); + for (Index i = 0; i < num_params; ++i) { + Write(", ", StackVar(num_params - i)); + } + Write(");", Newline()); + DropTypes(num_params + 1); + if (num_results > 1) { + for (Index i = 0; i < num_results; ++i) { + Type type = decl.GetResultType(i); + PushType(type); + Write(StackVar(0)); + Writef(" = tmp.%c%d;", MangleType(type), i); + Write(Newline()); + } + Write(CloseBrace(), Newline()); + } else { + PushTypes(decl.sig.result_types); + } + break; + } + + case ExprType::CodeMetadata: + break; + + case ExprType::Compare: + Write(*cast<CompareExpr>(&expr)); + break; + + case ExprType::Const: { + const Const& const_ = cast<ConstExpr>(&expr)->const_; + PushType(const_.type()); + Write(StackVar(0), " = ", const_, ";", Newline()); + break; + } + + case ExprType::Convert: + Write(*cast<ConvertExpr>(&expr)); + break; + + case ExprType::Drop: + DropTypes(1); + break; + + case ExprType::GlobalGet: { + const Var& var = cast<GlobalGetExpr>(&expr)->var; + PushType(module_->GetGlobal(var)->type); + Write(StackVar(0), " = ", GlobalInstanceVar(var), ";", Newline()); + break; + } + + case ExprType::GlobalSet: { + const Var& var = cast<GlobalSetExpr>(&expr)->var; + Write(GlobalInstanceVar(var), " = ", StackVar(0), ";", Newline()); + DropTypes(1); + break; + } + + case ExprType::If: { + const IfExpr& if_ = *cast<IfExpr>(&expr); + Write("if (", StackVar(0), ") ", OpenBrace()); + DropTypes(1); + std::string label = DefineLabelName(if_.true_.label); + DropTypes(if_.true_.decl.GetNumParams()); + size_t mark = MarkTypeStack(); + PushLabel(LabelType::If, if_.true_.label, if_.true_.decl.sig); + PushTypes(if_.true_.decl.sig.param_types); + Write(if_.true_.exprs, CloseBrace()); + if (!if_.false_.empty()) { + ResetTypeStack(mark); + PushTypes(if_.true_.decl.sig.param_types); + Write(" else ", OpenBrace(), if_.false_, CloseBrace()); + } + ResetTypeStack(mark); + Write(Newline(), LabelDecl(label)); + PopLabel(); + PushTypes(if_.true_.decl.sig.result_types); + break; + } + + case ExprType::Load: + Write(*cast<LoadExpr>(&expr)); + break; + + case ExprType::LocalGet: { + const Var& var = cast<LocalGetExpr>(&expr)->var; + PushType(func_->GetLocalType(var)); + Write(StackVar(0), " = ", var, ";", Newline()); + break; + } + + case ExprType::LocalSet: { + const Var& var = cast<LocalSetExpr>(&expr)->var; + Write(var, " = ", StackVar(0), ";", Newline()); + DropTypes(1); + break; + } + + case ExprType::LocalTee: { + const Var& var = cast<LocalTeeExpr>(&expr)->var; + Write(var, " = ", StackVar(0), ";", Newline()); + break; + } + + case ExprType::Loop: { + const Block& block = cast<LoopExpr>(&expr)->block; + if (!block.exprs.empty()) { + Write(DefineLabelName(block.label), ": "); + Indent(); + DropTypes(block.decl.GetNumParams()); + size_t mark = MarkTypeStack(); + PushLabel(LabelType::Loop, block.label, block.decl.sig); + PushTypes(block.decl.sig.param_types); + Write(Newline(), block.exprs); + ResetTypeStack(mark); + PopLabel(); + PushTypes(block.decl.sig.result_types); + Dedent(); + } + break; + } + + case ExprType::MemoryFill: { + const auto inst = cast<MemoryFillExpr>(&expr); + Memory* memory = + module_->memories[module_->GetMemoryIndex(inst->memidx)]; + Write("memory_fill(", + ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", ", + StackVar(2), ", ", StackVar(1), ", ", StackVar(0), ");", + Newline()); + DropTypes(3); + } break; + + case ExprType::MemoryCopy: { + const auto inst = cast<MemoryCopyExpr>(&expr); + Memory* dest_memory = + module_->memories[module_->GetMemoryIndex(inst->destmemidx)]; + const Memory* src_memory = module_->GetMemory(inst->srcmemidx); + Write("memory_copy(", + ExternalInstancePtr(ModuleFieldType::Memory, dest_memory->name), + ", ", + ExternalInstancePtr(ModuleFieldType::Memory, src_memory->name), + ", ", StackVar(2), ", ", StackVar(1), ", ", StackVar(0), ");", + Newline()); + DropTypes(3); + } break; + + case ExprType::MemoryInit: { + const auto inst = cast<MemoryInitExpr>(&expr); + Memory* dest_memory = + module_->memories[module_->GetMemoryIndex(inst->memidx)]; + const DataSegment* src_data = module_->GetDataSegment(inst->var); + Write("memory_init(", + ExternalInstancePtr(ModuleFieldType::Memory, dest_memory->name), + ", "); + if (src_data->data.empty()) { + Write("NULL, 0"); + } else { + Write("data_segment_data_", + GlobalName(ModuleFieldType::DataSegment, src_data->name), ", "); + if (is_droppable(src_data)) { + Write("(", "instance->data_segment_dropped_", + GlobalName(ModuleFieldType::DataSegment, src_data->name), + " ? 0 : ", src_data->data.size(), ")"); + } else { + Write("0"); + } + } + + Write(", ", StackVar(2), ", ", StackVar(1), ", ", StackVar(0), ");", + Newline()); + DropTypes(3); + } break; + + case ExprType::TableInit: { + const auto inst = cast<TableInitExpr>(&expr); + Table* dest_table = + module_->tables[module_->GetTableIndex(inst->table_index)]; + const ElemSegment* src_segment = + module_->GetElemSegment(inst->segment_index); + + WriteElemTableInit(false, src_segment, dest_table); + DropTypes(3); + } break; + + case ExprType::DataDrop: { + const auto inst = cast<DataDropExpr>(&expr); + const DataSegment* data = module_->GetDataSegment(inst->var); + if (is_droppable(data)) { + Write("instance->data_segment_dropped_", + GlobalName(ModuleFieldType::DataSegment, data->name), + " = true;", Newline()); + } + } break; + + case ExprType::ElemDrop: { + const auto inst = cast<ElemDropExpr>(&expr); + const ElemSegment* seg = module_->GetElemSegment(inst->var); + if (is_droppable(seg)) { + Write("instance->elem_segment_dropped_", + GlobalName(ModuleFieldType::ElemSegment, seg->name), " = true;", + Newline()); + } + } break; + + case ExprType::TableCopy: { + const auto inst = cast<TableCopyExpr>(&expr); + Table* dest_table = + module_->tables[module_->GetTableIndex(inst->dst_table)]; + const Table* src_table = module_->GetTable(inst->src_table); + if (dest_table->elem_type != src_table->elem_type) { + WABT_UNREACHABLE; + } + + Write( + GetReferenceTypeName(dest_table->elem_type), "_table_copy(", + ExternalInstancePtr(ModuleFieldType::Table, dest_table->name), ", ", + ExternalInstancePtr(ModuleFieldType::Table, src_table->name), ", ", + StackVar(2), ", ", StackVar(1), ", ", StackVar(0), ");", Newline()); + DropTypes(3); + } break; + + case ExprType::TableGet: { + const Table* table = module_->GetTable(cast<TableGetExpr>(&expr)->var); + Write(StackVar(0, table->elem_type), " = ", + GetReferenceTypeName(table->elem_type), "_table_get(", + ExternalInstancePtr(ModuleFieldType::Table, table->name), ", ", + StackVar(0), ");", Newline()); + DropTypes(1); + PushType(table->elem_type); + } break; + + case ExprType::TableSet: { + const Table* table = module_->GetTable(cast<TableSetExpr>(&expr)->var); + Write(GetReferenceTypeName(table->elem_type), "_table_set(", + ExternalInstancePtr(ModuleFieldType::Table, table->name), ", ", + StackVar(1), ", ", StackVar(0), ");", Newline()); + DropTypes(2); + } break; + + case ExprType::TableGrow: { + const Table* table = module_->GetTable(cast<TableGrowExpr>(&expr)->var); + Write(StackVar(1, Type::I32), " = wasm_rt_grow_", + GetReferenceTypeName(table->elem_type), "_table(", + ExternalInstancePtr(ModuleFieldType::Table, table->name), ", ", + StackVar(0), ", ", StackVar(1), ");", Newline()); + DropTypes(2); + PushType(Type::I32); + } break; + + case ExprType::TableSize: { + const Table* table = module_->GetTable(cast<TableSizeExpr>(&expr)->var); + + PushType(Type::I32); + Write(StackVar(0), " = ", + ExternalInstanceRef(ModuleFieldType::Table, table->name), + ".size;", Newline()); + } break; + + case ExprType::TableFill: { + const Table* table = module_->GetTable(cast<TableFillExpr>(&expr)->var); + Write(GetReferenceTypeName(table->elem_type), "_table_fill(", + ExternalInstancePtr(ModuleFieldType::Table, table->name), ", ", + StackVar(2), ", ", StackVar(1), ", ", StackVar(0), ");", + Newline()); + DropTypes(3); + } break; + + case ExprType::RefFunc: { + const Func* func = module_->GetFunc(cast<RefFuncExpr>(&expr)->var); + PushType(Type::FuncRef); + const FuncDeclaration& decl = func->decl; + + assert(decl.has_func_type); + const FuncType* func_type = module_->GetFuncType(decl.type_var); + + Write(StackVar(0), " = (wasm_rt_funcref_t){", FuncTypeExpr(func_type), + ", (wasm_rt_function_ptr_t)", + ExternalPtr(ModuleFieldType::Func, func->name), ", "); + + bool is_import = import_module_sym_map_.count(func->name) != 0; + if (is_import) { + Write("instance->", GlobalName(ModuleFieldType::Import, + import_module_sym_map_[func->name])); + } else { + Write("instance"); + } + + Write("};", Newline()); + } break; + + case ExprType::RefNull: + PushType(cast<RefNullExpr>(&expr)->type); + Write(StackVar(0), " = ", + GetReferenceNullValue(cast<RefNullExpr>(&expr)->type), ";", + Newline()); + break; + + case ExprType::RefIsNull: + switch (StackType(0)) { + case Type::FuncRef: + Write(StackVar(0, Type::I32), " = (", StackVar(0), ".func == NULL", + ");", Newline()); + break; + case Type::ExternRef: + Write(StackVar(0, Type::I32), " = (", StackVar(0), + " == ", GetReferenceNullValue(Type::ExternRef), ");", + Newline()); + break; + default: + WABT_UNREACHABLE; + } + + DropTypes(1); + PushType(Type::I32); + break; + + case ExprType::MemoryGrow: { + Memory* memory = module_->memories[module_->GetMemoryIndex( + cast<MemoryGrowExpr>(&expr)->memidx)]; + + Write(StackVar(0), " = wasm_rt_grow_memory(", + ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", ", + StackVar(0), ");", Newline()); + break; + } + + case ExprType::MemorySize: { + Memory* memory = module_->memories[module_->GetMemoryIndex( + cast<MemorySizeExpr>(&expr)->memidx)]; + + PushType(memory->page_limits.IndexType()); + Write(StackVar(0), " = ", + ExternalInstanceRef(ModuleFieldType::Memory, memory->name), + ".pages;", Newline()); + break; + } + + case ExprType::Nop: + break; + + case ExprType::Return: + // Goto the function label instead; this way we can do shared function + // cleanup code in one place. + Write(GotoLabel(Var(label_stack_.size() - 1, {})), Newline()); + // Stop processing this ExprList, since the following are unreachable. + return; + + case ExprType::Select: { + Type type = StackType(1); + Write(StackVar(2), " = ", StackVar(0), " ? ", StackVar(2), " : ", + StackVar(1), ";", Newline()); + DropTypes(3); + PushType(type); + break; + } + + case ExprType::Store: + Write(*cast<StoreExpr>(&expr)); + break; + + case ExprType::Unary: + Write(*cast<UnaryExpr>(&expr)); + break; + + case ExprType::Ternary: + Write(*cast<TernaryExpr>(&expr)); + break; + + case ExprType::SimdLaneOp: { + Write(*cast<SimdLaneOpExpr>(&expr)); + break; + } + + case ExprType::SimdLoadLane: { + Write(*cast<SimdLoadLaneExpr>(&expr)); + break; + } + + case ExprType::SimdStoreLane: { + Write(*cast<SimdStoreLaneExpr>(&expr)); + break; + } + + case ExprType::SimdShuffleOp: { + Write(*cast<SimdShuffleOpExpr>(&expr)); + break; + } + + case ExprType::LoadSplat: + Write(*cast<LoadSplatExpr>(&expr)); + break; + + case ExprType::LoadZero: + Write(*cast<LoadZeroExpr>(&expr)); + break; + + case ExprType::Unreachable: + Write("UNREACHABLE;", Newline()); + return; + + case ExprType::Throw: { + const Var& var = cast<ThrowExpr>(&expr)->var; + const Tag* tag = module_->GetTag(var); + + Index num_params = tag->decl.GetNumParams(); + if (num_params == 0) { + Write("wasm_rt_load_exception(", + ExternalPtr(ModuleFieldType::Tag, tag->name), ", 0, NULL);", + Newline()); + } else if (num_params == 1) { + Write("wasm_rt_load_exception(", + ExternalPtr(ModuleFieldType::Tag, tag->name), ", sizeof(", + tag->decl.GetParamType(0), "), &", StackVar(0), ");", + Newline()); + } else { + Write(OpenBrace()); + Write("struct ", MangleTagTypes(tag->decl.sig.param_types)); + Write(" tmp = {"); + for (Index i = 0; i < num_params; ++i) { + Write(StackVar(i), ", "); + } + Write("};", Newline()); + Write("wasm_rt_load_exception(", + ExternalPtr(ModuleFieldType::Tag, tag->name), + ", sizeof(tmp), &tmp);", Newline()); + Write(CloseBrace(), Newline()); + } + + WriteThrow(); + } break; + + case ExprType::Rethrow: { + const RethrowExpr* rethrow = cast<RethrowExpr>(&expr); + assert(rethrow->var.is_name()); + const LabelName ex{rethrow->var.name()}; + func_includes_.insert(rethrow->var.name()); + Write("wasm_rt_load_exception(", ex, "_tag, ", ex, "_size, ", ex, ");", + Newline()); + WriteThrow(); + } break; + + case ExprType::Try: { + const TryExpr& tryexpr = *cast<TryExpr>(&expr); + switch (tryexpr.kind) { + case TryKind::Plain: + Write(tryexpr.block); + break; + case TryKind::Catch: + WriteTryCatch(tryexpr); + break; + case TryKind::Delegate: + WriteTryDelegate(tryexpr); + break; + } + } break; + + case ExprType::AtomicLoad: + case ExprType::AtomicRmw: + case ExprType::AtomicRmwCmpxchg: + case ExprType::AtomicStore: + case ExprType::AtomicWait: + case ExprType::AtomicFence: + case ExprType::AtomicNotify: + case ExprType::ReturnCall: + case ExprType::ReturnCallIndirect: + case ExprType::CallRef: + UNIMPLEMENTED("..."); + break; + } + } +} + +void CWriter::WriteSimpleUnaryExpr(Opcode opcode, const char* op) { + Type result_type = opcode.GetResultType(); + Write(StackVar(0, result_type), " = ", op, "(", StackVar(0), ");", Newline()); + DropTypes(1); + PushType(opcode.GetResultType()); +} + +void CWriter::WriteInfixBinaryExpr(Opcode opcode, + const char* op, + AssignOp assign_op) { + Type result_type = opcode.GetResultType(); + Write(StackVar(1, result_type)); + if (assign_op == AssignOp::Allowed) { + Write(" ", op, "= ", StackVar(0)); + } else { + Write(" = ", StackVar(1), " ", op, " ", StackVar(0)); + } + Write(";", Newline()); + DropTypes(2); + PushType(result_type); +} + +void CWriter::WritePrefixBinaryExpr(Opcode opcode, const char* op) { + Type result_type = opcode.GetResultType(); + Write(StackVar(1, result_type), " = ", op, "(", StackVar(1), ", ", + StackVar(0), ");", Newline()); + DropTypes(2); + PushType(result_type); +} + +void CWriter::WriteSignedBinaryExpr(Opcode opcode, const char* op) { + Type result_type = opcode.GetResultType(); + Type type = opcode.GetParamType1(); + assert(opcode.GetParamType2() == type); + Write(StackVar(1, result_type), " = (", type, ")((", SignedType(type), ")", + StackVar(1), " ", op, " (", SignedType(type), ")", StackVar(0), ");", + Newline()); + DropTypes(2); + PushType(result_type); +} + +void CWriter::Write(const BinaryExpr& expr) { + switch (expr.opcode) { + case Opcode::I32Add: + case Opcode::I64Add: + case Opcode::F32Add: + case Opcode::F64Add: + WriteInfixBinaryExpr(expr.opcode, "+"); + break; + + case Opcode::I32Sub: + case Opcode::I64Sub: + case Opcode::F32Sub: + case Opcode::F64Sub: + WriteInfixBinaryExpr(expr.opcode, "-"); + break; + + case Opcode::I32Mul: + case Opcode::I64Mul: + case Opcode::F32Mul: + case Opcode::F64Mul: + WriteInfixBinaryExpr(expr.opcode, "*"); + break; + + case Opcode::I32DivS: + WritePrefixBinaryExpr(expr.opcode, "I32_DIV_S"); + break; + + case Opcode::I64DivS: + WritePrefixBinaryExpr(expr.opcode, "I64_DIV_S"); + break; + + case Opcode::I32DivU: + case Opcode::I64DivU: + WritePrefixBinaryExpr(expr.opcode, "DIV_U"); + break; + + case Opcode::F32Div: + case Opcode::F64Div: + WriteInfixBinaryExpr(expr.opcode, "/"); + break; + + case Opcode::I32RemS: + WritePrefixBinaryExpr(expr.opcode, "I32_REM_S"); + break; + + case Opcode::I64RemS: + WritePrefixBinaryExpr(expr.opcode, "I64_REM_S"); + break; + + case Opcode::I32RemU: + case Opcode::I64RemU: + WritePrefixBinaryExpr(expr.opcode, "REM_U"); + break; + + case Opcode::I32And: + case Opcode::I64And: + WriteInfixBinaryExpr(expr.opcode, "&"); + break; + + case Opcode::I32Or: + case Opcode::I64Or: + WriteInfixBinaryExpr(expr.opcode, "|"); + break; + + case Opcode::I32Xor: + case Opcode::I64Xor: + WriteInfixBinaryExpr(expr.opcode, "^"); + break; + + case Opcode::I32Shl: + case Opcode::I64Shl: + Write(StackVar(1), " <<= (", StackVar(0), " & ", + GetShiftMask(expr.opcode.GetResultType()), ");", Newline()); + DropTypes(1); + break; + + case Opcode::I32ShrS: + case Opcode::I64ShrS: { + Type type = expr.opcode.GetResultType(); + Write(StackVar(1), " = (", type, ")((", SignedType(type), ")", + StackVar(1), " >> (", StackVar(0), " & ", GetShiftMask(type), "));", + Newline()); + DropTypes(1); + break; + } + + case Opcode::I32ShrU: + case Opcode::I64ShrU: + Write(StackVar(1), " >>= (", StackVar(0), " & ", + GetShiftMask(expr.opcode.GetResultType()), ");", Newline()); + DropTypes(1); + break; + + case Opcode::I32Rotl: + WritePrefixBinaryExpr(expr.opcode, "I32_ROTL"); + break; + + case Opcode::I64Rotl: + WritePrefixBinaryExpr(expr.opcode, "I64_ROTL"); + break; + + case Opcode::I32Rotr: + WritePrefixBinaryExpr(expr.opcode, "I32_ROTR"); + break; + + case Opcode::I64Rotr: + WritePrefixBinaryExpr(expr.opcode, "I64_ROTR"); + break; + + case Opcode::F32Min: + case Opcode::F64Min: + WritePrefixBinaryExpr(expr.opcode, "FMIN"); + break; + + case Opcode::F32Max: + case Opcode::F64Max: + WritePrefixBinaryExpr(expr.opcode, "FMAX"); + break; + + case Opcode::F32Copysign: + WritePrefixBinaryExpr(expr.opcode, "copysignf"); + break; + + case Opcode::F64Copysign: + WritePrefixBinaryExpr(expr.opcode, "copysign"); + break; + + case Opcode::I8X16Add: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_add"); + break; + + case Opcode::I8X16AddSatS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_add_sat"); + break; + + case Opcode::I8X16AddSatU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_add_sat"); + break; + + case Opcode::I8X16AvgrU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_avgr"); + break; + + case Opcode::I8X16MaxS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_max"); + break; + + case Opcode::I8X16MaxU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_max"); + break; + + case Opcode::I8X16MinS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_min"); + break; + + case Opcode::I8X16MinU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_min"); + break; + + case Opcode::I8X16NarrowI16X8S: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_narrow_i16x8"); + break; + + case Opcode::I8X16NarrowI16X8U: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_narrow_i16x8"); + break; + + case Opcode::I8X16Shl: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_shl"); + break; + + case Opcode::I8X16ShrS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_shr"); + break; + + case Opcode::I8X16ShrU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_shr"); + break; + + case Opcode::I8X16Sub: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_sub"); + break; + + case Opcode::I8X16SubSatS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_sub_sat"); + break; + + case Opcode::I8X16SubSatU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_sub_sat"); + break; + + case Opcode::I8X16Swizzle: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_swizzle"); + break; + + case Opcode::I16X8Add: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_add"); + break; + + case Opcode::I16X8AvgrU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_avgr"); + break; + + case Opcode::I16X8AddSatS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_add_sat"); + break; + + case Opcode::I16X8AddSatU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_add_sat"); + break; + + case Opcode::I16X8ExtmulHighI8X16S: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_extmul_high_i8x16"); + break; + + case Opcode::I16X8ExtmulHighI8X16U: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_extmul_high_u8x16"); + break; + + case Opcode::I16X8ExtmulLowI8X16S: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_extmul_low_i8x16"); + break; + + case Opcode::I16X8ExtmulLowI8X16U: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_extmul_low_u8x16"); + break; + + case Opcode::I16X8MaxS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_max"); + break; + + case Opcode::I16X8MaxU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_max"); + break; + + case Opcode::I16X8MinS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_min"); + break; + + case Opcode::I16X8MinU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_min"); + break; + + case Opcode::I16X8Mul: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_mul"); + break; + + case Opcode::I16X8NarrowI32X4S: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_narrow_i32x4"); + break; + + case Opcode::I16X8NarrowI32X4U: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_narrow_i32x4"); + break; + + case Opcode::I16X8Q15mulrSatS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_q15mulr_sat"); + break; + + case Opcode::I16X8Shl: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_shl"); + break; + + case Opcode::I16X8ShrS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_shr"); + break; + + case Opcode::I16X8ShrU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_shr"); + break; + + case Opcode::I16X8Sub: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_sub"); + break; + + case Opcode::I16X8SubSatS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_sub_sat"); + break; + + case Opcode::I16X8SubSatU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_sub_sat"); + break; + + case Opcode::I32X4Add: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_add"); + break; + + case Opcode::I32X4DotI16X8S: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_dot_i16x8"); + break; + + case Opcode::I32X4ExtmulHighI16X8S: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_extmul_high_i16x8"); + break; + + case Opcode::I32X4ExtmulHighI16X8U: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u32x4_extmul_high_u16x8"); + break; + + case Opcode::I32X4ExtmulLowI16X8S: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_extmul_low_i16x8"); + break; + + case Opcode::I32X4ExtmulLowI16X8U: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u32x4_extmul_low_u16x8"); + break; + + case Opcode::I32X4MaxS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_max"); + break; + + case Opcode::I32X4MaxU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u32x4_max"); + break; + + case Opcode::I32X4MinS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_min"); + break; + + case Opcode::I32X4MinU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u32x4_min"); + break; + + case Opcode::I32X4Mul: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_mul"); + break; + + case Opcode::I32X4Shl: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_shl"); + break; + + case Opcode::I32X4ShrS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_shr"); + break; + + case Opcode::I32X4ShrU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u32x4_shr"); + break; + + case Opcode::I32X4Sub: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_sub"); + break; + + case Opcode::I64X2Add: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_add"); + break; + + case Opcode::I64X2ExtmulHighI32X4S: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_extmul_high_i32x4"); + break; + + case Opcode::I64X2ExtmulHighI32X4U: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u64x2_extmul_high_u32x4"); + break; + + case Opcode::I64X2ExtmulLowI32X4S: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_extmul_low_i32x4"); + break; + + case Opcode::I64X2ExtmulLowI32X4U: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u64x2_extmul_low_u32x4"); + break; + + case Opcode::I64X2Mul: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_mul"); + break; + + case Opcode::I64X2Shl: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_shl"); + break; + + case Opcode::I64X2ShrS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_shr"); + break; + + case Opcode::I64X2ShrU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u64x2_shr"); + break; + + case Opcode::I64X2Sub: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_sub"); + break; + + case Opcode::F32X4Add: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_add"); + break; + + case Opcode::F32X4Div: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_div"); + break; + + case Opcode::F32X4Max: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_max"); + break; + + case Opcode::F32X4Mul: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_mul"); + break; + + case Opcode::F32X4Min: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_min"); + break; + + case Opcode::F32X4PMax: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_pmax"); + break; + + case Opcode::F32X4PMin: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_pmin"); + break; + + case Opcode::F32X4Sub: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_sub"); + break; + + case Opcode::F64X2Add: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_add"); + break; + + case Opcode::F64X2Div: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_div"); + break; + + case Opcode::F64X2Max: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_max"); + break; + + case Opcode::F64X2Mul: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_mul"); + break; + + case Opcode::F64X2Min: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_min"); + break; + + case Opcode::F64X2PMax: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_pmax"); + break; + + case Opcode::F64X2PMin: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_pmin"); + break; + + case Opcode::F64X2Sub: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_sub"); + break; + + case Opcode::V128And: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_v128_and"); + break; + + case Opcode::V128Andnot: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_v128_andnot"); + break; + + case Opcode::V128Or: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_v128_or"); + break; + + case Opcode::V128Xor: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_v128_xor"); + break; + + default: + WABT_UNREACHABLE; + } +} + +void CWriter::Write(const CompareExpr& expr) { + switch (expr.opcode) { + case Opcode::I32Eq: + case Opcode::I64Eq: + case Opcode::F32Eq: + case Opcode::F64Eq: + WriteInfixBinaryExpr(expr.opcode, "==", AssignOp::Disallowed); + break; + + case Opcode::I32Ne: + case Opcode::I64Ne: + case Opcode::F32Ne: + case Opcode::F64Ne: + WriteInfixBinaryExpr(expr.opcode, "!=", AssignOp::Disallowed); + break; + + case Opcode::I32LtS: + case Opcode::I64LtS: + WriteSignedBinaryExpr(expr.opcode, "<"); + break; + + case Opcode::I32LtU: + case Opcode::I64LtU: + case Opcode::F32Lt: + case Opcode::F64Lt: + WriteInfixBinaryExpr(expr.opcode, "<", AssignOp::Disallowed); + break; + + case Opcode::I32LeS: + case Opcode::I64LeS: + WriteSignedBinaryExpr(expr.opcode, "<="); + break; + + case Opcode::I32LeU: + case Opcode::I64LeU: + case Opcode::F32Le: + case Opcode::F64Le: + WriteInfixBinaryExpr(expr.opcode, "<=", AssignOp::Disallowed); + break; + + case Opcode::I32GtS: + case Opcode::I64GtS: + WriteSignedBinaryExpr(expr.opcode, ">"); + break; + + case Opcode::I32GtU: + case Opcode::I64GtU: + case Opcode::F32Gt: + case Opcode::F64Gt: + WriteInfixBinaryExpr(expr.opcode, ">", AssignOp::Disallowed); + break; + + case Opcode::I32GeS: + case Opcode::I64GeS: + WriteSignedBinaryExpr(expr.opcode, ">="); + break; + + case Opcode::I32GeU: + case Opcode::I64GeU: + case Opcode::F32Ge: + case Opcode::F64Ge: + WriteInfixBinaryExpr(expr.opcode, ">=", AssignOp::Disallowed); + break; + + case Opcode::I8X16Eq: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_eq"); + break; + + case Opcode::I8X16GeS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_ge"); + break; + + case Opcode::I8X16GeU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_ge"); + break; + + case Opcode::I8X16GtS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_gt"); + break; + + case Opcode::I8X16GtU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_gt"); + break; + + case Opcode::I8X16LeS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_le"); + break; + + case Opcode::I8X16LeU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_le"); + break; + + case Opcode::I8X16LtS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_lt"); + break; + + case Opcode::I8X16LtU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u8x16_lt"); + break; + + case Opcode::I8X16Ne: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i8x16_ne"); + break; + + case Opcode::I16X8Eq: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_eq"); + break; + + case Opcode::I16X8GeS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_ge"); + break; + + case Opcode::I16X8GeU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_ge"); + break; + + case Opcode::I16X8GtS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_gt"); + break; + case Opcode::I16X8GtU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_gt"); + break; + + case Opcode::I16X8LeS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_le"); + break; + + case Opcode::I16X8LeU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_le"); + break; + + case Opcode::I16X8LtS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_lt"); + break; + + case Opcode::I16X8LtU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u16x8_lt"); + break; + + case Opcode::I16X8Ne: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i16x8_ne"); + break; + + case Opcode::I32X4Eq: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_eq"); + break; + + case Opcode::I32X4GeS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_ge"); + break; + + case Opcode::I32X4GeU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u32x4_ge"); + break; + + case Opcode::I32X4GtS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_gt"); + break; + + case Opcode::I32X4GtU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u32x4_gt"); + break; + + case Opcode::I32X4LeS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_le"); + break; + + case Opcode::I32X4LeU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u32x4_le"); + break; + + case Opcode::I32X4LtS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_lt"); + break; + + case Opcode::I32X4LtU: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_u32x4_lt"); + break; + + case Opcode::I32X4Ne: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i32x4_ne"); + break; + + case Opcode::I64X2Eq: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_eq"); + break; + + case Opcode::I64X2GeS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_ge"); + break; + + case Opcode::I64X2GtS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_gt"); + break; + + case Opcode::I64X2LeS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_le"); + break; + + case Opcode::I64X2LtS: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_lt"); + break; + + case Opcode::I64X2Ne: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_i64x2_ne"); + break; + + case Opcode::F32X4Eq: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_eq"); + break; + + case Opcode::F32X4Ge: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_ge"); + break; + + case Opcode::F32X4Gt: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_gt"); + break; + + case Opcode::F32X4Le: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_le"); + break; + + case Opcode::F32X4Lt: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_lt"); + break; + + case Opcode::F32X4Ne: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f32x4_ne"); + break; + + case Opcode::F64X2Eq: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_eq"); + break; + + case Opcode::F64X2Ge: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_ge"); + break; + + case Opcode::F64X2Gt: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_gt"); + break; + + case Opcode::F64X2Le: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_le"); + break; + + case Opcode::F64X2Lt: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_lt"); + break; + + case Opcode::F64X2Ne: + WritePrefixBinaryExpr(expr.opcode, "simde_wasm_f64x2_ne"); + break; + + default: + WABT_UNREACHABLE; + } +} + +void CWriter::Write(const ConvertExpr& expr) { + switch (expr.opcode) { + case Opcode::I32Eqz: + case Opcode::I64Eqz: + WriteSimpleUnaryExpr(expr.opcode, "!"); + break; + + case Opcode::I64ExtendI32S: + WriteSimpleUnaryExpr(expr.opcode, "(u64)(s64)(s32)"); + break; + + case Opcode::I64ExtendI32U: + WriteSimpleUnaryExpr(expr.opcode, "(u64)"); + break; + + case Opcode::I32WrapI64: + WriteSimpleUnaryExpr(expr.opcode, "(u32)"); + break; + + case Opcode::I32TruncF32S: + WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_S_F32"); + break; + + case Opcode::I64TruncF32S: + WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_S_F32"); + break; + + case Opcode::I32TruncF64S: + WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_S_F64"); + break; + + case Opcode::I64TruncF64S: + WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_S_F64"); + break; + + case Opcode::I32TruncF32U: + WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_U_F32"); + break; + + case Opcode::I64TruncF32U: + WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_U_F32"); + break; + + case Opcode::I32TruncF64U: + WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_U_F64"); + break; + + case Opcode::I64TruncF64U: + WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_U_F64"); + break; + + case Opcode::I32TruncSatF32S: + WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_SAT_S_F32"); + break; + + case Opcode::I64TruncSatF32S: + WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_SAT_S_F32"); + break; + + case Opcode::I32TruncSatF64S: + WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_SAT_S_F64"); + break; + + case Opcode::I64TruncSatF64S: + WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_SAT_S_F64"); + break; + + case Opcode::I32TruncSatF32U: + WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_SAT_U_F32"); + break; + + case Opcode::I64TruncSatF32U: + WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_SAT_U_F32"); + break; + + case Opcode::I32TruncSatF64U: + WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_SAT_U_F64"); + break; + + case Opcode::I64TruncSatF64U: + WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_SAT_U_F64"); + break; + + case Opcode::F32ConvertI32S: + WriteSimpleUnaryExpr(expr.opcode, "(f32)(s32)"); + break; + + case Opcode::F32ConvertI64S: + WriteSimpleUnaryExpr(expr.opcode, "(f32)(s64)"); + break; + + case Opcode::F32ConvertI32U: + WriteSimpleUnaryExpr(expr.opcode, "(f32)"); + break; + + case Opcode::F32DemoteF64: + WriteSimpleUnaryExpr(expr.opcode, "(f32)wasm_quiet"); + break; + + case Opcode::F32ConvertI64U: + // TODO(binji): This needs to be handled specially (see + // wabt_convert_uint64_to_float). + WriteSimpleUnaryExpr(expr.opcode, "(f32)"); + break; + + case Opcode::F64ConvertI32S: + WriteSimpleUnaryExpr(expr.opcode, "(f64)(s32)"); + break; + + case Opcode::F64ConvertI64S: + WriteSimpleUnaryExpr(expr.opcode, "(f64)(s64)"); + break; + + case Opcode::F64ConvertI32U: + WriteSimpleUnaryExpr(expr.opcode, "(f64)"); + break; + + case Opcode::F64PromoteF32: + WriteSimpleUnaryExpr(expr.opcode, "(f64)wasm_quietf"); + break; + + case Opcode::F64ConvertI64U: + // TODO(binji): This needs to be handled specially (see + // wabt_convert_uint64_to_double). + WriteSimpleUnaryExpr(expr.opcode, "(f64)"); + break; + + case Opcode::F32ReinterpretI32: + WriteSimpleUnaryExpr(expr.opcode, "f32_reinterpret_i32"); + break; + + case Opcode::I32ReinterpretF32: + WriteSimpleUnaryExpr(expr.opcode, "i32_reinterpret_f32"); + break; + + case Opcode::F64ReinterpretI64: + WriteSimpleUnaryExpr(expr.opcode, "f64_reinterpret_i64"); + break; + + case Opcode::I64ReinterpretF64: + WriteSimpleUnaryExpr(expr.opcode, "i64_reinterpret_f64"); + break; + + case Opcode::I32X4TruncSatF32X4S: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i32x4_trunc_sat_f32x4"); + break; + + case Opcode::I32X4TruncSatF32X4U: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_u32x4_trunc_sat_f32x4"); + break; + + case Opcode::I32X4TruncSatF64X2SZero: + WriteSimpleUnaryExpr(expr.opcode, + "simde_wasm_i32x4_trunc_sat_f64x2_zero"); + break; + + case Opcode::I32X4TruncSatF64X2UZero: + WriteSimpleUnaryExpr(expr.opcode, + "simde_wasm_u32x4_trunc_sat_f64x2_zero"); + break; + + case Opcode::F32X4ConvertI32X4S: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_convert_i32x4"); + break; + + case Opcode::F32X4ConvertI32X4U: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_convert_u32x4"); + break; + + case Opcode::F32X4DemoteF64X2Zero: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_demote_f64x2_zero"); + break; + + case Opcode::F64X2ConvertLowI32X4S: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_convert_low_i32x4"); + break; + + case Opcode::F64X2ConvertLowI32X4U: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_convert_low_u32x4"); + break; + + case Opcode::F64X2PromoteLowF32X4: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_promote_low_f32x4"); + break; + + default: + WABT_UNREACHABLE; + } +} + +void CWriter::Write(const LoadExpr& expr) { + const char* func = nullptr; + // clang-format off + switch (expr.opcode) { + case Opcode::I32Load: func = "i32_load"; break; + case Opcode::I64Load: func = "i64_load"; break; + case Opcode::F32Load: func = "f32_load"; break; + case Opcode::F64Load: func = "f64_load"; break; + case Opcode::I32Load8S: func = "i32_load8_s"; break; + case Opcode::I64Load8S: func = "i64_load8_s"; break; + case Opcode::I32Load8U: func = "i32_load8_u"; break; + case Opcode::I64Load8U: func = "i64_load8_u"; break; + case Opcode::I32Load16S: func = "i32_load16_s"; break; + case Opcode::I64Load16S: func = "i64_load16_s"; break; + case Opcode::I32Load16U: func = "i32_load16_u"; break; + case Opcode::I64Load16U: func = "i64_load16_u"; break; + case Opcode::I64Load32S: func = "i64_load32_s"; break; + case Opcode::I64Load32U: func = "i64_load32_u"; break; + case Opcode::V128Load: func = "v128_load"; break; + case Opcode::V128Load8X8S: func = "i16x8_load8x8"; break; + case Opcode::V128Load8X8U: func = "u16x8_load8x8"; break; + case Opcode::V128Load16X4S: func = "i32x4_load16x4"; break; + case Opcode::V128Load16X4U: func = "u32x4_load16x4"; break; + case Opcode::V128Load32X2S: func = "i64x2_load32x2"; break; + case Opcode::V128Load32X2U: func = "u64x2_load32x2"; break; + + default: + WABT_UNREACHABLE; + } + // clang-format on + + Memory* memory = module_->memories[module_->GetMemoryIndex(expr.memidx)]; + + Type result_type = expr.opcode.GetResultType(); + Write(StackVar(0, result_type), " = ", func, "(", + ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", (u64)(", + StackVar(0), ")"); + if (expr.offset != 0) + Write(" + ", expr.offset, "u"); + Write(");", Newline()); + DropTypes(1); + PushType(result_type); +} + +void CWriter::Write(const StoreExpr& expr) { + const char* func = nullptr; + // clang-format off + switch (expr.opcode) { + case Opcode::I32Store: func = "i32_store"; break; + case Opcode::I64Store: func = "i64_store"; break; + case Opcode::F32Store: func = "f32_store"; break; + case Opcode::F64Store: func = "f64_store"; break; + case Opcode::I32Store8: func = "i32_store8"; break; + case Opcode::I64Store8: func = "i64_store8"; break; + case Opcode::I32Store16: func = "i32_store16"; break; + case Opcode::I64Store16: func = "i64_store16"; break; + case Opcode::I64Store32: func = "i64_store32"; break; + case Opcode::V128Store: func = "v128_store"; break; + + default: + WABT_UNREACHABLE; + } + // clang-format on + + Memory* memory = module_->memories[module_->GetMemoryIndex(expr.memidx)]; + + Write(func, "(", ExternalInstancePtr(ModuleFieldType::Memory, memory->name), + ", (u64)(", StackVar(1), ")"); + if (expr.offset != 0) + Write(" + ", expr.offset); + Write(", ", StackVar(0), ");", Newline()); + DropTypes(2); +} + +void CWriter::Write(const UnaryExpr& expr) { + switch (expr.opcode) { + case Opcode::I32Clz: + WriteSimpleUnaryExpr(expr.opcode, "I32_CLZ"); + break; + + case Opcode::I64Clz: + WriteSimpleUnaryExpr(expr.opcode, "I64_CLZ"); + break; + + case Opcode::I32Ctz: + WriteSimpleUnaryExpr(expr.opcode, "I32_CTZ"); + break; + + case Opcode::I64Ctz: + WriteSimpleUnaryExpr(expr.opcode, "I64_CTZ"); + break; + + case Opcode::I32Popcnt: + WriteSimpleUnaryExpr(expr.opcode, "I32_POPCNT"); + break; + + case Opcode::I64Popcnt: + WriteSimpleUnaryExpr(expr.opcode, "I64_POPCNT"); + break; + + case Opcode::F32Neg: + case Opcode::F64Neg: + WriteSimpleUnaryExpr(expr.opcode, "-"); + break; + + case Opcode::F32Abs: + WriteSimpleUnaryExpr(expr.opcode, "wasm_fabsf"); + break; + + case Opcode::F64Abs: + WriteSimpleUnaryExpr(expr.opcode, "wasm_fabs"); + break; + + case Opcode::F32Sqrt: + WriteSimpleUnaryExpr(expr.opcode, "wasm_sqrtf"); + break; + + case Opcode::F64Sqrt: + WriteSimpleUnaryExpr(expr.opcode, "wasm_sqrt"); + break; + + case Opcode::F32Ceil: + WriteSimpleUnaryExpr(expr.opcode, "wasm_ceilf"); + break; + + case Opcode::F64Ceil: + WriteSimpleUnaryExpr(expr.opcode, "wasm_ceil"); + break; + + case Opcode::F32Floor: + WriteSimpleUnaryExpr(expr.opcode, "wasm_floorf"); + break; + + case Opcode::F64Floor: + WriteSimpleUnaryExpr(expr.opcode, "wasm_floor"); + break; + + case Opcode::F32Trunc: + WriteSimpleUnaryExpr(expr.opcode, "wasm_truncf"); + break; + + case Opcode::F64Trunc: + WriteSimpleUnaryExpr(expr.opcode, "wasm_trunc"); + break; + + case Opcode::F32Nearest: + WriteSimpleUnaryExpr(expr.opcode, "wasm_nearbyintf"); + break; + + case Opcode::F64Nearest: + WriteSimpleUnaryExpr(expr.opcode, "wasm_nearbyint"); + break; + + case Opcode::I32Extend8S: + WriteSimpleUnaryExpr(expr.opcode, "(u32)(s32)(s8)(u8)"); + break; + + case Opcode::I32Extend16S: + WriteSimpleUnaryExpr(expr.opcode, "(u32)(s32)(s16)(u16)"); + break; + + case Opcode::I64Extend8S: + WriteSimpleUnaryExpr(expr.opcode, "(u64)(s64)(s8)(u8)"); + break; + + case Opcode::I64Extend16S: + WriteSimpleUnaryExpr(expr.opcode, "(u64)(s64)(s16)(u16)"); + break; + + case Opcode::I64Extend32S: + WriteSimpleUnaryExpr(expr.opcode, "(u64)(s64)(s32)(u32)"); + break; + + case Opcode::I8X16Abs: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i8x16_abs"); + break; + + case Opcode::I8X16AllTrue: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i8x16_all_true"); + break; + + case Opcode::I8X16Bitmask: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i8x16_bitmask"); + break; + + case Opcode::I8X16Neg: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i8x16_neg"); + break; + + case Opcode::I8X16Popcnt: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i8x16_popcnt"); + break; + + case Opcode::I8X16Splat: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i8x16_splat"); + break; + + case Opcode::I16X8Abs: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i16x8_abs"); + break; + + case Opcode::I16X8AllTrue: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i16x8_all_true"); + break; + + case Opcode::I16X8Bitmask: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i16x8_bitmask"); + break; + + case Opcode::I16X8ExtaddPairwiseI8X16S: + WriteSimpleUnaryExpr(expr.opcode, + "simde_wasm_i16x8_extadd_pairwise_i8x16"); + break; + + case Opcode::I16X8ExtaddPairwiseI8X16U: + WriteSimpleUnaryExpr(expr.opcode, + "simde_wasm_u16x8_extadd_pairwise_u8x16"); + break; + + case Opcode::I16X8ExtendHighI8X16S: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i16x8_extend_high_i8x16"); + break; + + case Opcode::I16X8ExtendHighI8X16U: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_u16x8_extend_high_u8x16"); + break; + + case Opcode::I16X8ExtendLowI8X16S: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i16x8_extend_low_i8x16"); + break; + + case Opcode::I16X8ExtendLowI8X16U: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_u16x8_extend_low_u8x16"); + break; + + case Opcode::I16X8Neg: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i16x8_neg"); + break; + + case Opcode::I16X8Splat: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i16x8_splat"); + break; + + case Opcode::I32X4Abs: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i32x4_abs"); + break; + + case Opcode::I32X4AllTrue: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i32x4_all_true"); + break; + + case Opcode::I32X4Bitmask: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i32x4_bitmask"); + break; + + case Opcode::I32X4ExtaddPairwiseI16X8S: + WriteSimpleUnaryExpr(expr.opcode, + "simde_wasm_i32x4_extadd_pairwise_i16x8"); + break; + + case Opcode::I32X4ExtaddPairwiseI16X8U: + WriteSimpleUnaryExpr(expr.opcode, + "simde_wasm_u32x4_extadd_pairwise_u16x8"); + break; + + case Opcode::I32X4ExtendHighI16X8S: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i32x4_extend_high_i16x8"); + break; + + case Opcode::I32X4ExtendHighI16X8U: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_u32x4_extend_high_u16x8"); + break; + + case Opcode::I32X4ExtendLowI16X8S: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i32x4_extend_low_i16x8"); + break; + + case Opcode::I32X4ExtendLowI16X8U: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_u32x4_extend_low_u16x8"); + break; + + case Opcode::I32X4Neg: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i32x4_neg"); + break; + + case Opcode::I32X4Splat: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i32x4_splat"); + break; + + case Opcode::I64X2Abs: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i64x2_abs"); + break; + + case Opcode::I64X2AllTrue: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i64x2_all_true"); + break; + + case Opcode::I64X2Bitmask: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i64x2_bitmask"); + break; + + case Opcode::I64X2ExtendHighI32X4S: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i64x2_extend_high_i32x4"); + break; + + case Opcode::I64X2ExtendHighI32X4U: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_u64x2_extend_high_u32x4"); + break; + + case Opcode::I64X2ExtendLowI32X4S: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i64x2_extend_low_i32x4"); + break; + + case Opcode::I64X2ExtendLowI32X4U: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_u64x2_extend_low_u32x4"); + break; + + case Opcode::I64X2Neg: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i64x2_neg"); + break; + + case Opcode::I64X2Splat: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_i64x2_splat"); + break; + + case Opcode::F32X4Abs: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_abs"); + break; + + case Opcode::F32X4Ceil: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_ceil"); + break; + + case Opcode::F32X4Floor: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_floor"); + break; + + case Opcode::F32X4Nearest: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_nearest"); + break; + + case Opcode::F32X4Neg: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_neg"); + break; + + case Opcode::F32X4Splat: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_splat"); + break; + + case Opcode::F32X4Sqrt: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_sqrt"); + break; + + case Opcode::F32X4Trunc: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f32x4_trunc"); + break; + + case Opcode::F64X2Abs: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_abs"); + break; + + case Opcode::F64X2Ceil: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_ceil"); + break; + + case Opcode::F64X2Floor: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_floor"); + break; + + case Opcode::F64X2Nearest: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_nearest"); + break; + + case Opcode::F64X2Neg: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_neg"); + break; + + case Opcode::F64X2Splat: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_splat"); + break; + + case Opcode::F64X2Sqrt: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_sqrt"); + break; + + case Opcode::F64X2Trunc: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_f64x2_trunc"); + break; + + case Opcode::V128AnyTrue: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_v128_any_true"); + break; + + case Opcode::V128Not: + WriteSimpleUnaryExpr(expr.opcode, "simde_wasm_v128_not"); + break; + + default: + WABT_UNREACHABLE; + } +} + +void CWriter::Write(const TernaryExpr& expr) { + switch (expr.opcode) { + case Opcode::V128BitSelect: { + Type result_type = expr.opcode.GetResultType(); + Write(StackVar(2, result_type), " = ", "simde_wasm_v128_bitselect", "(", + StackVar(2), ", ", StackVar(1), ", ", StackVar(0), ");", Newline()); + DropTypes(3); + PushType(result_type); + break; + } + default: + WABT_UNREACHABLE; + } +} + +void CWriter::Write(const SimdLaneOpExpr& expr) { + Type result_type = expr.opcode.GetResultType(); + + switch (expr.opcode) { + case Opcode::I8X16ExtractLaneS: { + Write(StackVar(0, result_type), " = simde_wasm_i8x16_extract_lane(", + StackVar(0), ", ", expr.val, ");", Newline()); + DropTypes(1); + break; + } + case Opcode::I8X16ExtractLaneU: { + Write(StackVar(0, result_type), " = simde_wasm_u8x16_extract_lane(", + StackVar(0), ", ", expr.val, ");", Newline()); + DropTypes(1); + break; + } + case Opcode::I16X8ExtractLaneS: { + Write(StackVar(0, result_type), " = simde_wasm_i16x8_extract_lane(", + StackVar(0), ", ", expr.val, ");", Newline()); + DropTypes(1); + break; + } + case Opcode::I16X8ExtractLaneU: { + Write(StackVar(0, result_type), " = simde_wasm_u16x8_extract_lane(", + StackVar(0), ", ", expr.val, ");", Newline()); + DropTypes(1); + break; + } + case Opcode::I32X4ExtractLane: { + Write(StackVar(0, result_type), " = simde_wasm_i32x4_extract_lane(", + StackVar(0), ", ", expr.val, ");", Newline()); + DropTypes(1); + break; + } + case Opcode::I64X2ExtractLane: { + Write(StackVar(0, result_type), " = simde_wasm_i64x2_extract_lane(", + StackVar(0), ", ", expr.val, ");", Newline()); + DropTypes(1); + break; + } + case Opcode::F32X4ExtractLane: { + Write(StackVar(0, result_type), " = simde_wasm_f32x4_extract_lane(", + StackVar(0), ", ", expr.val, ");", Newline()); + DropTypes(1); + break; + } + case Opcode::F64X2ExtractLane: { + Write(StackVar(0, result_type), " = simde_wasm_f64x2_extract_lane(", + StackVar(0), ", ", expr.val, ");", Newline()); + DropTypes(1); + break; + } + case Opcode::I8X16ReplaceLane: { + Write(StackVar(1, result_type), " = simde_wasm_i8x16_replace_lane(", + StackVar(1), ", ", expr.val, ", ", StackVar(0), ");", Newline()); + DropTypes(2); + break; + } + case Opcode::I16X8ReplaceLane: { + Write(StackVar(1, result_type), " = simde_wasm_i16x8_replace_lane(", + StackVar(1), ", ", expr.val, ", ", StackVar(0), ");", Newline()); + DropTypes(2); + break; + } + case Opcode::I32X4ReplaceLane: { + Write(StackVar(1, result_type), " = simde_wasm_i32x4_replace_lane(", + StackVar(1), ", ", expr.val, ", ", StackVar(0), ");", Newline()); + DropTypes(2); + break; + } + case Opcode::I64X2ReplaceLane: { + Write(StackVar(1, result_type), " = simde_wasm_i64x2_replace_lane(", + StackVar(1), ", ", expr.val, ", ", StackVar(0), ");", Newline()); + DropTypes(2); + break; + } + case Opcode::F32X4ReplaceLane: { + Write(StackVar(1, result_type), " = simde_wasm_f32x4_replace_lane(", + StackVar(1), ", ", expr.val, ", ", StackVar(0), ");", Newline()); + DropTypes(2); + break; + } + case Opcode::F64X2ReplaceLane: { + Write(StackVar(1, result_type), " = simde_wasm_f64x2_replace_lane(", + StackVar(1), ", ", expr.val, ", ", StackVar(0), ");", Newline()); + DropTypes(2); + break; + } + default: + WABT_UNREACHABLE; + } + + PushType(result_type); +} + +void CWriter::Write(const SimdLoadLaneExpr& expr) { + const char* func = nullptr; + // clang-format off + switch (expr.opcode) { + case Opcode::V128Load8Lane: func = "v128_load8_lane"; break; + case Opcode::V128Load16Lane: func = "v128_load16_lane"; break; + case Opcode::V128Load32Lane: func = "v128_load32_lane"; break; + case Opcode::V128Load64Lane: func = "v128_load64_lane"; break; + default: + WABT_UNREACHABLE; + } + // clang-format on + Memory* memory = module_->memories[module_->GetMemoryIndex(expr.memidx)]; + Type result_type = expr.opcode.GetResultType(); + Write(StackVar(1, result_type), " = ", func, expr.val, "(", + ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", (u64)(", + StackVar(1), ")"); + + if (expr.offset != 0) + Write(" + ", expr.offset, "u"); + Write(", ", StackVar(0)); + Write(");", Newline()); + + DropTypes(2); + PushType(result_type); +} + +void CWriter::Write(const SimdStoreLaneExpr& expr) { + const char* func = nullptr; + // clang-format off + switch (expr.opcode) { + case Opcode::V128Store8Lane: func = "v128_store8_lane"; break; + case Opcode::V128Store16Lane: func = "v128_store16_lane"; break; + case Opcode::V128Store32Lane: func = "v128_store32_lane"; break; + case Opcode::V128Store64Lane: func = "v128_store64_lane"; break; + default: + WABT_UNREACHABLE; + } + // clang-format on + Memory* memory = module_->memories[module_->GetMemoryIndex(expr.memidx)]; + + Write(func, expr.val, "(", + ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", (u64)(", + StackVar(1), ")"); + + if (expr.offset != 0) + Write(" + ", expr.offset, "u"); + Write(", ", StackVar(0)); + Write(");", Newline()); + + DropTypes(2); +} + +void CWriter::Write(const SimdShuffleOpExpr& expr) { + Type result_type = expr.opcode.GetResultType(); + switch (expr.opcode) { + case Opcode::I8X16Shuffle: { + Write(StackVar(1, result_type), " = simde_wasm_i8x16_shuffle(", + StackVar(1), ", ", StackVar(0), ", ", expr.val.u8(0), ", ", + expr.val.u8(1), ", ", expr.val.u8(2), ", ", expr.val.u8(3), ", ", + expr.val.u8(4), ", ", expr.val.u8(5), ", ", expr.val.u8(6), ", ", + expr.val.u8(7), ", ", expr.val.u8(8), ", ", expr.val.u8(9), ", ", + expr.val.u8(10), ", ", expr.val.u8(11), ", ", expr.val.u8(12), ", ", + expr.val.u8(13), ", ", expr.val.u8(14), ", ", expr.val.u8(15), ");", + Newline()); + DropTypes(2); + break; + } + default: + WABT_UNREACHABLE; + } + PushType(result_type); +} + +void CWriter::Write(const LoadSplatExpr& expr) { + Memory* memory = module_->memories[module_->GetMemoryIndex(expr.memidx)]; + + const char* func = nullptr; + // clang-format off + switch (expr.opcode) { + case Opcode::V128Load8Splat: func = "v128_load8_splat"; break; + case Opcode::V128Load16Splat: func = "v128_load16_splat"; break; + case Opcode::V128Load32Splat: func = "v128_load32_splat"; break; + case Opcode::V128Load64Splat: func = "v128_load64_splat"; break; + default: + WABT_UNREACHABLE; + } + // clang-format on + Type result_type = expr.opcode.GetResultType(); + Write(StackVar(0, result_type), " = ", func, "(", + ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", (u64)(", + StackVar(0), ")"); + if (expr.offset != 0) + Write(" + ", expr.offset); + Write(");", Newline()); + + DropTypes(1); + PushType(result_type); +} + +void CWriter::Write(const LoadZeroExpr& expr) { + Memory* memory = module_->memories[module_->GetMemoryIndex(expr.memidx)]; + + const char* func = nullptr; + // clang-format off + switch (expr.opcode) { + case Opcode::V128Load32Zero: func = "v128_load32_zero"; break; + case Opcode::V128Load64Zero: func = "v128_load64_zero"; break; + default: + WABT_UNREACHABLE; + } + // clang-format on + + Type result_type = expr.opcode.GetResultType(); + Write(StackVar(0, result_type), " = ", func, "(", + ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", (u64)(", + StackVar(0), ")"); + if (expr.offset != 0) + Write(" + ", expr.offset); + Write(");", Newline()); + + DropTypes(1); + PushType(result_type); +} + +void CWriter::ReserveExportNames() { + for (const Export* export_ : module_->exports) { + ReserveExportName(export_->name); + } +} + +void CWriter::WriteCHeader() { + ReserveExportNames(); + + stream_ = h_stream_; + std::string guard = GenerateHeaderGuard(); + Write("/* Automatically generated by wasm2c */", Newline()); + Write("#ifndef ", guard, Newline()); + Write("#define ", guard, Newline()); + Write(Newline()); + WriteFeatureMacros(); + Write(s_header_top); + Write(Newline()); + WriteModuleInstance(); + WriteInitDecl(); + WriteFreeDecl(); + WriteGetFuncTypeDecl(); + WriteMultivalueTypes(); + WriteImports(); + WriteImportProperties(CWriterPhase::Declarations); + WriteExports(CWriterPhase::Declarations); + Write(Newline()); + Write(s_header_bottom); + Write(Newline(), "#endif /* ", guard, " */", Newline()); +} + +void CWriter::WriteCSource() { + /* Write the "top" to h_impl stream */ + stream_ = h_impl_stream_; + Write("/* Automatically generated by wasm2c */", Newline()); + WriteSourceTop(); + + /* Write module-wide declarations to impl header */ + WriteFuncTypeDecls(); + WriteTagTypes(); + WriteTagDecls(); + WriteFuncDeclarations(); + WriteDataInitializerDecls(); + WriteElemInitializerDecls(); + + /* Write the module-wide material to the first output stream */ + stream_ = c_streams_.front(); + WriteMultiCTop(); + WriteFuncTypes(); + WriteTags(); + WriteGlobalInitializers(); + WriteDataInitializers(); + WriteElemInitializers(); + WriteExports(CWriterPhase::Definitions); + WriteInitInstanceImport(); + WriteImportProperties(CWriterPhase::Definitions); + WriteInit(); + WriteFree(); + WriteGetFuncType(); + + /* Write function bodies across the different output streams */ + WriteFuncs(); + + /* For any empty .c output, write a dummy typedef to avoid gcc warning */ + WriteMultiCTopEmpty(); +} + +Result CWriter::WriteModule(const Module& module) { + WABT_USE(options_); + module_ = &module; + WriteCHeader(); + WriteCSource(); + return result_; +} + +// static +const char* CWriter::GetReferenceTypeName(const Type& type) { + switch (type) { + case Type::FuncRef: + return "funcref"; + case Type::ExternRef: + return "externref"; + default: + WABT_UNREACHABLE; + } +} + +// static +const char* CWriter::GetReferenceNullValue(const Type& type) { + switch (type) { + case Type::FuncRef: + return "wasm_rt_funcref_null_value"; + case Type::ExternRef: + return "wasm_rt_externref_null_value"; + default: + WABT_UNREACHABLE; + } +} + +const char* CWriter::InternalSymbolScope() const { + if (c_streams_.size() == 1) { + return "static "; + } else { + return ""; + } +} + +} // end anonymous namespace + +Result WriteC(std::vector<Stream*>&& c_streams, + Stream* h_stream, + Stream* h_impl_stream, + const char* header_name, + const char* header_impl_name, + const Module* module, + const WriteCOptions& options) { + CWriter c_writer(std::move(c_streams), h_stream, h_impl_stream, header_name, + header_impl_name, options); + return c_writer.WriteModule(*module); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/color.cc b/third_party/wasm2c/src/color.cc new file mode 100644 index 0000000000..15e9eb90bd --- /dev/null +++ b/third_party/wasm2c/src/color.cc @@ -0,0 +1,86 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/color.h" + +#include <cstdlib> + +#include "wabt/common.h" + +#if _WIN32 +#if HAVE_WIN32_VT100 +#include <io.h> +#include <windows.h> +#endif +#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/common.cc b/third_party/wasm2c/src/common.cc new file mode 100644 index 0000000000..037b8304bd --- /dev/null +++ b/third_party/wasm2c/src/common.cc @@ -0,0 +1,148 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/common.h" + +#include <cassert> +#include <cerrno> +#include <climits> +#include <cstdint> +#include <cstdio> +#include <cstring> + +#include <sys/stat.h> +#include <sys/types.h> + +#if COMPILER_IS_MSVC +#include <fcntl.h> +#include <io.h> +#include <stdlib.h> +#define PATH_MAX _MAX_PATH +#define stat _stat +#define S_IFREG _S_IFREG +#endif + +namespace wabt { + +Reloc::Reloc(RelocType type, Offset offset, Index index, int32_t addend) + : type(type), offset(offset), index(index), addend(addend) {} + +const char* g_kind_name[] = {"func", "table", "memory", "global", "tag"}; +WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(g_kind_name) == kExternalKindCount); + +const char* g_reloc_type_name[] = { + "R_WASM_FUNCTION_INDEX_LEB", "R_WASM_TABLE_INDEX_SLEB", + "R_WASM_TABLE_INDEX_I32", "R_WASM_MEMORY_ADDR_LEB", + "R_WASM_MEMORY_ADDR_SLEB", "R_WASM_MEMORY_ADDR_I32", + "R_WASM_TYPE_INDEX_LEB", "R_WASM_GLOBAL_INDEX_LEB", + "R_WASM_FUNCTION_OFFSET_I32", "R_WASM_SECTION_OFFSET_I32", + "R_WASM_TAG_INDEX_LEB", "R_WASM_MEMORY_ADDR_REL_SLEB", + "R_WASM_TABLE_INDEX_REL_SLEB", "R_WASM_GLOBAL_INDEX_I32", + "R_WASM_MEMORY_ADDR_LEB64", "R_WASM_MEMORY_ADDR_SLEB64", + "R_WASM_MEMORY_ADDR_I64", "R_WASM_MEMORY_ADDR_REL_SLEB64", + "R_WASM_TABLE_INDEX_SLEB64", "R_WASM_TABLE_INDEX_I64", + "R_WASM_TABLE_NUMBER_LEB", "R_WASM_MEMORY_ADDR_TLS_SLEB", + "R_WASM_MEMORY_ADDR_TLS_I32", +}; +WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(g_reloc_type_name) == kRelocTypeCount); + +static Result ReadStdin(std::vector<uint8_t>* out_data) { + out_data->resize(0); + uint8_t buffer[4096]; + while (true) { + size_t bytes_read = fread(buffer, 1, sizeof(buffer), stdin); + if (bytes_read == 0) { + if (ferror(stdin)) { + fprintf(stderr, "error reading from stdin: %s\n", strerror(errno)); + return Result::Error; + } + return Result::Ok; + } + size_t old_size = out_data->size(); + out_data->resize(old_size + bytes_read); + memcpy(out_data->data() + old_size, buffer, bytes_read); + } +} + +Result ReadFile(std::string_view filename, std::vector<uint8_t>* out_data) { + std::string filename_str(filename); + const char* filename_cstr = filename_str.c_str(); + + if (filename == "-") { + return ReadStdin(out_data); + } + + struct stat statbuf; + if (stat(filename_cstr, &statbuf) < 0) { + fprintf(stderr, "%s: %s\n", filename_cstr, strerror(errno)); + return Result::Error; + } + + if (!(statbuf.st_mode & S_IFREG)) { + fprintf(stderr, "%s: not a regular file\n", filename_cstr); + return Result::Error; + } + + FILE* infile = fopen(filename_cstr, "rb"); + if (!infile) { + fprintf(stderr, "%s: %s\n", filename_cstr, strerror(errno)); + return Result::Error; + } + + if (fseek(infile, 0, SEEK_END) < 0) { + perror("fseek to end failed"); + fclose(infile); + return Result::Error; + } + + long size = ftell(infile); + if (size < 0) { + perror("ftell failed"); + fclose(infile); + return Result::Error; + } + + if (fseek(infile, 0, SEEK_SET) < 0) { + perror("fseek to beginning failed"); + fclose(infile); + return Result::Error; + } + + out_data->resize(size); + if (size != 0 && fread(out_data->data(), size, 1, infile) != 1) { + fprintf(stderr, "%s: fread failed: %s\n", filename_cstr, strerror(errno)); + fclose(infile); + return Result::Error; + } + + fclose(infile); + return Result::Ok; +} + +void InitStdio() { +#if COMPILER_IS_MSVC + int result = _setmode(_fileno(stdout), _O_BINARY); + if (result == -1) { + perror("Cannot set mode binary to stdout"); + } + result = _setmode(_fileno(stderr), _O_BINARY); + if (result == -1) { + perror("Cannot set mode binary to stderr"); + } +#endif +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/config.cc b/third_party/wasm2c/src/config.cc new file mode 100644 index 0000000000..ccf80e68ea --- /dev/null +++ b/third_party/wasm2c/src/config.cc @@ -0,0 +1,162 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/config.h" + +#include <cstdarg> +#include <cstdio> + +#if COMPILER_IS_MSVC && _M_X64 +#include <emmintrin.h> +#elif COMPILER_IS_MSVC && _M_IX86 +#include <float.h> +#endif + +/* c99-style vsnprintf for MSVC < 2015. See http://stackoverflow.com/a/8712996 + using _snprintf or vsnprintf will not-properly null-terminate, and will return + -1 instead of the number of characters needed on overflow. */ +#if COMPILER_IS_MSVC +int wabt_vsnprintf(char* str, size_t size, const char* format, va_list ap) { + int result = -1; + if (size != 0) { + result = _vsnprintf_s(str, size, _TRUNCATE, format, ap); + } + if (result == -1) { + result = _vscprintf(format, ap); + } + return result; +} + +#if !HAVE_SNPRINTF +int wabt_snprintf(char* str, size_t size, const char* format, ...) { + va_list args; + va_start(args, format); + int result = wabt_vsnprintf(str, size, format, args); + va_end(args); + return result; +} +#endif +#endif + +#if COMPILER_IS_MSVC && _M_IX86 +// Allow the following functions to change the floating-point environment (e.g. +// update to 64-bit precision in the mantissa). This is only needed for x87 +// floats, which are only used on MSVC 32-bit. +#pragma fenv_access(on) +namespace { + +typedef unsigned int FPControl; + +FPControl Set64BitPrecisionControl() { + FPControl old_ctrl = _control87(0, 0); + _control87(_PC_64, _MCW_PC); + return old_ctrl; +} + +void ResetPrecisionControl(FPControl old_ctrl) { + _control87(old_ctrl, _MCW_PC); +} + +} // end of anonymous namespace +#endif + +double wabt_convert_uint64_to_double(uint64_t x) { +#if COMPILER_IS_MSVC && _M_X64 + // MSVC on x64 generates uint64 -> float conversions but doesn't do + // round-to-nearest-ties-to-even, which is required by WebAssembly. + __m128d result = _mm_setzero_pd(); + if (x & 0x8000000000000000ULL) { + result = _mm_cvtsi64_sd(result, (x >> 1) | (x & 1)); + result = _mm_add_sd(result, result); + } else { + result = _mm_cvtsi64_sd(result, x); + } + return _mm_cvtsd_f64(result); +#elif COMPILER_IS_MSVC && _M_IX86 + // MSVC on x86 converts from i64 -> double -> float, which causes incorrect + // rounding. Using the x87 float stack instead preserves the correct + // rounding. + FPControl old_ctrl = Set64BitPrecisionControl(); + static const double c = 18446744073709551616.0; + double result; + __asm fild x; + if (x & 0x8000000000000000ULL) { + __asm fadd c; + } + __asm fstp result; + ResetPrecisionControl(old_ctrl); + return result; +#else + return static_cast<double>(x); +#endif +} + +float wabt_convert_uint64_to_float(uint64_t x) { +#if COMPILER_IS_MSVC && _M_X64 + // MSVC on x64 generates uint64 -> float conversions but doesn't do + // round-to-nearest-ties-to-even, which is required by WebAssembly. + __m128 result = _mm_setzero_ps(); + if (x & 0x8000000000000000ULL) { + result = _mm_cvtsi64_ss(result, (x >> 1) | (x & 1)); + result = _mm_add_ss(result, result); + } else { + result = _mm_cvtsi64_ss(result, x); + } + return _mm_cvtss_f32(result); +#elif COMPILER_IS_MSVC && _M_IX86 + // MSVC on x86 converts from i64 -> double -> float, which causes incorrect + // rounding. Using the x87 float stack instead preserves the correct + // rounding. + FPControl old_ctrl = Set64BitPrecisionControl(); + static const float c = 18446744073709551616.0f; + float result; + __asm fild x; + if (x & 0x8000000000000000ULL) { + __asm fadd c; + } + __asm fstp result; + ResetPrecisionControl(old_ctrl); + return result; +#else + return static_cast<float>(x); +#endif +} + +double wabt_convert_int64_to_double(int64_t x) { +#if COMPILER_IS_MSVC && _M_IX86 + double result; + __asm fild x; + __asm fstp result; + return result; +#else + return static_cast<double>(x); +#endif +} + +float wabt_convert_int64_to_float(int64_t x) { +#if COMPILER_IS_MSVC && _M_IX86 + float result; + __asm fild x; + __asm fstp result; + return result; +#else + return static_cast<float>(x); +#endif +} + +#if COMPILER_IS_MSVC && _M_IX86 +#pragma fenv_access(off) +#endif diff --git a/third_party/wasm2c/src/config.h.in b/third_party/wasm2c/src/config.h.in new file mode 100644 index 0000000000..f7187c3228 --- /dev/null +++ b/third_party/wasm2c/src/config.h.in @@ -0,0 +1,332 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WABT_CONFIG_H_ +#define WABT_CONFIG_H_ + +#include <stdint.h> +#include <stdlib.h> + +#cmakedefine WABT_VERSION_STRING "@WABT_VERSION_STRING@" + +#cmakedefine WABT_DEBUG @WABT_DEBUG@ + +/* TODO(binji): nice way to define these with WABT_ prefix? */ + +/* Whether <alloca.h> is available */ +#cmakedefine01 HAVE_ALLOCA_H + +/* Whether <unistd.h> is available */ +#cmakedefine01 HAVE_UNISTD_H + +/* Whether snprintf is defined by stdio.h */ +#cmakedefine01 HAVE_SNPRINTF + +/* Whether ssize_t is defined by stddef.h */ +#cmakedefine01 HAVE_SSIZE_T + +/* Whether strcasecmp is defined by strings.h */ +#cmakedefine01 HAVE_STRCASECMP + +/* Whether ENABLE_VIRTUAL_TERMINAL_PROCESSING is defined by windows.h */ +#cmakedefine01 HAVE_WIN32_VT100 + +/* Whether the target architecture is big endian */ +#cmakedefine01 WABT_BIG_ENDIAN + +/* Whether <openssl/sha.h> is available */ +#cmakedefine01 HAVE_OPENSSL_SHA_H + +#cmakedefine01 COMPILER_IS_CLANG +#cmakedefine01 COMPILER_IS_GNU +#cmakedefine01 COMPILER_IS_MSVC + +#cmakedefine01 WITH_EXCEPTIONS + +#define SIZEOF_SIZE_T @SIZEOF_SIZE_T@ + +#if HAVE_ALLOCA_H +#include <alloca.h> +#elif COMPILER_IS_MSVC +#include <malloc.h> +#define alloca _alloca +#elif defined(__MINGW32__) +#include <malloc.h> +#endif + +#if COMPILER_IS_CLANG || COMPILER_IS_GNU + +#define WABT_UNLIKELY(x) __builtin_expect(!!(x), 0) +#define WABT_LIKELY(x) __builtin_expect(!!(x), 1) + +#define WABT_VECTORCALL + +#if __MINGW32__ +// mingw defaults to printf format specifier being ms_printf (which doesn't +// understand 'llu', etc.) We always want gnu_printf, and force mingw to always +// use mingw_printf, mingw_vprintf, etc. +#define WABT_PRINTF_FORMAT(format_arg, first_arg) \ + __attribute__((format(gnu_printf, (format_arg), (first_arg)))) +#else +#define WABT_PRINTF_FORMAT(format_arg, first_arg) \ + __attribute__((format(printf, (format_arg), (first_arg)))) +#endif + +#ifdef __cplusplus +#define WABT_STATIC_ASSERT(x) static_assert((x), #x) +#else +#define WABT_STATIC_ASSERT(x) _Static_assert((x), #x) +#endif + +#elif COMPILER_IS_MSVC + +#include <intrin.h> +#include <string.h> + +#define WABT_STATIC_ASSERT(x) _STATIC_ASSERT(x) +#define WABT_UNLIKELY(x) (x) +#define WABT_LIKELY(x) (x) +#define WABT_PRINTF_FORMAT(format_arg, first_arg) + +#define WABT_VECTORCALL __vectorcall + +#else + +#error unknown compiler + +#endif + +#define WABT_UNREACHABLE abort() + +#ifdef __cplusplus + +namespace wabt { + +#if COMPILER_IS_CLANG || COMPILER_IS_GNU + +inline int Clz(unsigned x) { return x ? __builtin_clz(x) : sizeof(x) * 8; } +inline int Clz(unsigned long x) { return x ? __builtin_clzl(x) : sizeof(x) * 8; } +inline int Clz(unsigned long long x) { return x ? __builtin_clzll(x) : sizeof(x) * 8; } + +inline int Ctz(unsigned x) { return x ? __builtin_ctz(x) : sizeof(x) * 8; } +inline int Ctz(unsigned long x) { return x ? __builtin_ctzl(x) : sizeof(x) * 8; } +inline int Ctz(unsigned long long x) { return x ? __builtin_ctzll(x) : sizeof(x) * 8; } + +inline int Popcount(uint8_t x) { return __builtin_popcount(x); } +inline int Popcount(unsigned x) { return __builtin_popcount(x); } +inline int Popcount(unsigned long x) { return __builtin_popcountl(x); } +inline int Popcount(unsigned long long x) { return __builtin_popcountll(x); } + +#elif COMPILER_IS_MSVC + +#if _M_IX86 +inline unsigned long LowDword(unsigned __int64 value) { + return (unsigned long)value; +} + +inline unsigned long HighDword(unsigned __int64 value) { + unsigned long high; + memcpy(&high, (unsigned char*)&value + sizeof(high), sizeof(high)); + return high; +} +#endif + +inline int Clz(unsigned long mask) { + if (mask == 0) + return 32; + + unsigned long index; + _BitScanReverse(&index, mask); + return sizeof(unsigned long) * 8 - (index + 1); +} + +inline int Clz(unsigned int mask) { + return Clz((unsigned long)mask); +} + +inline int Clz(unsigned __int64 mask) { +#if _M_X64 || _M_ARM64 + if (mask == 0) + return 64; + + unsigned long index; + _BitScanReverse64(&index, mask); + return sizeof(unsigned __int64) * 8 - (index + 1); +#elif _M_IX86 + int result = Clz(HighDword(mask)); + if (result == 32) + result += Clz(LowDword(mask)); + + return result; +#else +#error unexpected architecture +#endif +} + +inline int Ctz(unsigned long mask) { + if (mask == 0) + return 32; + + unsigned long index; + _BitScanForward(&index, mask); + return index; +} + +inline int Ctz(unsigned int mask) { + return Ctz((unsigned long)mask); +} + +inline int Ctz(unsigned __int64 mask) { +#if _M_X64 || _M_ARM64 + if (mask == 0) + return 64; + + unsigned long index; + _BitScanForward64(&index, mask); + return index; +#elif _M_IX86 + int result = Ctz(LowDword(mask)); + if (result == 32) + result += Ctz(HighDword(mask)); + + return result; +#else +#error unexpected architecture +#endif +} + +#if _M_ARM64 +//https://stackoverflow.com/a/70012905 +template <typename T> +int BrianKernighanPopcount(T value) { + int count; + for(count = 0; value; count++) + { + value &= value - 1; + } + return count; +} +#endif + +inline int Popcount(unsigned long value) { + #if _M_X64 || _M_IX86 + return __popcnt(value); +#elif _M_ARM64 + return BrianKernighanPopcount(value); +#else +#error unexpected architecture +#endif +} + +inline int Popcount(uint8_t value) { + return Popcount((unsigned long)value); +} + +inline int Popcount(unsigned int value) { + return Popcount((unsigned long)value); +} + +inline int Popcount(unsigned __int64 value) { +#if _M_X64 + return __popcnt64(value); +#elif _M_IX86 + return Popcount(HighDword(value)) + Popcount(LowDword(value)); +#elif _M_ARM64 + return BrianKernighanPopcount(value); +#else +#error unexpected architecture +#endif +} + +#else + +#error unknown compiler + +#endif + +} // namespace wabt + +#if COMPILER_IS_MSVC + +/* print format specifier for size_t */ +#if SIZEOF_SIZE_T == 4 +#define PRIzd "d" +#define PRIzx "x" +#elif SIZEOF_SIZE_T == 8 +#define PRIzd "I64d" +#define PRIzx "I64x" +#else +#error "weird sizeof size_t" +#endif + +#elif COMPILER_IS_CLANG || COMPILER_IS_GNU + +/* print format specifier for size_t */ +#define PRIzd "zd" +#define PRIzx "zx" + +#else + +#error unknown compiler + +#endif + +#if HAVE_SNPRINTF +#define wabt_snprintf snprintf +#elif COMPILER_IS_MSVC +/* can't just use _snprintf because it doesn't always null terminate */ +#include <cstdarg> +int wabt_snprintf(char* str, size_t size, const char* format, ...); +#else +#error no snprintf +#endif + +#if COMPILER_IS_MSVC +/* can't just use vsnprintf because it doesn't always null terminate */ +int wabt_vsnprintf(char* str, size_t size, const char* format, va_list ap); +#else +#define wabt_vsnprintf vsnprintf +#endif + +#if !HAVE_SSIZE_T +#if COMPILER_IS_MSVC +/* define ssize_t identically to how LLVM does, to avoid conflicts if including both */ +#if defined(_WIN64) +typedef signed __int64 ssize_t; +#else +typedef signed int ssize_t; +#endif /* _WIN64 */ +#else +typedef long ssize_t; +#endif +#endif + +#if !HAVE_STRCASECMP +#if COMPILER_IS_MSVC +#define strcasecmp _stricmp +#else +#error no strcasecmp +#endif +#endif + +double wabt_convert_uint64_to_double(uint64_t x); +float wabt_convert_uint64_to_float(uint64_t x); +double wabt_convert_int64_to_double(int64_t x); +float wabt_convert_int64_to_float(int64_t x); + +#endif // __cplusplus + +#endif /* WABT_CONFIG_H_ */ diff --git a/third_party/wasm2c/src/decompiler.cc b/third_party/wasm2c/src/decompiler.cc new file mode 100644 index 0000000000..dd8eb76701 --- /dev/null +++ b/third_party/wasm2c/src/decompiler.cc @@ -0,0 +1,857 @@ +/* + * Copyright 2019 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/decompiler.h" + +#include "wabt/decompiler-ast.h" +#include "wabt/decompiler-ls.h" +#include "wabt/decompiler-naming.h" + +#include "wabt/stream.h" + +#define WABT_TRACING 0 +#include "wabt/tracing.h" + +#include <inttypes.h> + +namespace wabt { + +struct Decompiler { + Decompiler(const Module& module, const DecompileOptions& options) + : mc(module), options(options) {} + + // Sorted such that "greater precedence" is also the bigger enum val. + enum Precedence { + // Low precedence. + None, // precedence doesn't matter, since never nested. + Assign, // = + OtherBin, // min + Bit, // & | + Equal, // == != < > >= <= + Shift, // << >> + Add, // + - + Multiply, // * / % + If, // if{} + Indexing, // [] + Atomic, // (), a, 1, a() + // High precedence. + }; + + // Anything besides these will get parentheses if used with equal precedence, + // for clarity. + bool Associative(Precedence p) { + return p == Precedence::Add || p == Precedence::Multiply; + } + + struct Value { + std::vector<std::string> v; + // Lazily add bracketing only if the parent requires it. + // This is the precedence level of this value, for example, if this + // precedence is Add, and the parent is Multiply, bracketing is needed, + // but not if it is the reverse. + Precedence precedence; + + Value(std::vector<std::string>&& v, Precedence p) : v(v), precedence(p) {} + + size_t width() { + size_t w = 0; + for (auto& s : v) { + w = std::max(w, s.size()); + } + return w; + } + + // This value should really never be copied, only moved. + Value(Value&& rhs) = default; + Value(const Value& rhs) = delete; + Value& operator=(Value&& rhs) = default; + Value& operator=(const Value& rhs) = delete; + }; + + std::string to_string(double d) { + auto s = std::to_string(d); + // Remove redundant trailing '0's that to_string produces. + while (s.size() > 2 && s.back() == '0' && s[s.size() - 2] != '.') + s.pop_back(); + return s; + } + + std::string Indent(size_t amount) { return std::string(amount, ' '); } + + std::string OpcodeToToken(Opcode opcode) { + std::string s = opcode.GetDecomp(); + std::replace(s.begin(), s.end(), '.', '_'); + return s; + } + + void IndentValue(Value& val, size_t amount, std::string_view first_indent) { + auto indent = Indent(amount); + for (auto& stat : val.v) { + auto is = (&stat != &val.v[0] || first_indent.empty()) + ? std::string_view(indent) + : first_indent; + stat.insert(0, is.data(), is.size()); + } + } + + Value WrapChild(Value& child, + std::string_view prefix, + std::string_view postfix, + Precedence precedence) { + auto width = prefix.size() + postfix.size() + child.width(); + auto& v = child.v; + if (width < target_exp_width || + (prefix.size() <= indent_amount && postfix.size() <= indent_amount)) { + if (v.size() == 1) { + // Fits in a single line. + v[0].insert(0, prefix.data(), prefix.size()); + v[0].append(postfix.data(), postfix.size()); + } else { + // Multiline, but with prefix on same line. + IndentValue(child, prefix.size(), prefix); + v.back().append(postfix.data(), postfix.size()); + } + } else { + // Multiline with prefix on its own line. + IndentValue(child, indent_amount, {}); + v.insert(v.begin(), std::string(prefix)); + v.back().append(postfix.data(), postfix.size()); + } + child.precedence = precedence; + return std::move(child); + } + + void BracketIfNeeded(Value& val, Precedence parent_precedence) { + if (parent_precedence < val.precedence || + (parent_precedence == val.precedence && + Associative(parent_precedence))) { + return; + } + val = WrapChild(val, "(", ")", Precedence::Atomic); + } + + Value WrapBinary(std::vector<Value>& args, + std::string_view infix, + bool indent_right, + Precedence precedence) { + assert(args.size() == 2); + auto& left = args[0]; + auto& right = args[1]; + BracketIfNeeded(left, precedence); + BracketIfNeeded(right, precedence); + auto width = infix.size() + left.width() + right.width() + 2; + if (width < target_exp_width && left.v.size() == 1 && right.v.size() == 1) { + return Value{{cat(left.v[0], " ", infix, " ", right.v[0])}, precedence}; + } else { + Value bin{{}, precedence}; + std::move(left.v.begin(), left.v.end(), std::back_inserter(bin.v)); + bin.v.back().append(" ", 1); + bin.v.back().append(infix.data(), infix.size()); + if (indent_right) { + IndentValue(right, indent_amount, {}); + } + std::move(right.v.begin(), right.v.end(), std::back_inserter(bin.v)); + return bin; + } + } + + Value WrapNAry(std::vector<Value>& args, + std::string_view prefix, + std::string_view postfix, + Precedence precedence) { + size_t total_width = 0; + size_t max_width = 0; + bool multiline = false; + for (auto& child : args) { + auto w = child.width(); + max_width = std::max(max_width, w); + total_width += w; + multiline = multiline || child.v.size() > 1; + } + if (!multiline && + (total_width + prefix.size() + postfix.size() < target_exp_width || + args.empty())) { + // Single line. + auto s = std::string(prefix); + for (auto& child : args) { + if (&child != &args[0]) + s += ", "; + s += child.v[0]; + } + s += postfix; + return Value{{std::move(s)}, precedence}; + } else { + // Multi-line. + Value ml{{}, precedence}; + auto ident_with_name = max_width + prefix.size() < target_exp_width; + size_t i = 0; + for (auto& child : args) { + IndentValue(child, ident_with_name ? prefix.size() : indent_amount, + !i && ident_with_name ? prefix : std::string_view{}); + if (i < args.size() - 1) { + child.v.back() += ","; + } + std::move(child.v.begin(), child.v.end(), std::back_inserter(ml.v)); + i++; + } + if (!ident_with_name) { + ml.v.insert(ml.v.begin(), std::string(prefix)); + } + ml.v.back() += postfix; + return ml; + } + } + + std::string_view VarName(std::string_view name) { + assert(!name.empty()); + return name[0] == '$' ? name.substr(1) : name; + } + + template <ExprType T> + Value Get(const VarExpr<T>& ve) { + return Value{{std::string(VarName(ve.var.name()))}, Precedence::Atomic}; + } + + template <ExprType T> + Value Set(Value& child, const VarExpr<T>& ve) { + return WrapChild(child, VarName(ve.var.name()) + " = ", "", + Precedence::Assign); + } + + std::string TempVarName(Index n) { + // FIXME: this needs much better variable naming. Problem is, the code + // in generate-names.cc has allready run, its dictionaries deleted, so it + // is not easy to integrate with it. + return "t" + std::to_string(n); + } + + std::string LocalDecl(const std::string& name, Type t) { + auto struc = lst.GenTypeDecl(name); + return cat(VarName(name), ":", + struc.empty() ? GetDecompTypeName(t) : struc); + } + + bool ConstIntVal(const Expr* e, uint64_t& dest) { + dest = 0; + if (!e || e->type() != ExprType::Const) { + return false; + } + auto& c = cast<ConstExpr>(e)->const_; + if (c.type() != Type::I32 && c.type() != Type::I64) { + return false; + } + dest = c.type() == Type::I32 ? c.u32() : c.u64(); + return true; + } + + void LoadStore(Value& val, + const Node& addr_exp, + uint64_t offset, + Opcode opc, + Address align, + Type op_type) { + bool append_type = true; + auto access = lst.GenAccess(offset, addr_exp); + if (!access.empty()) { + if (access == "*") { + // The variable was declared as a typed pointer, so this access + // doesn't need a type. + append_type = false; + } else { + // We can do this load/store as a struct access. + BracketIfNeeded(val, Precedence::Indexing); + val.v.back() += "." + access; + return; + } + } + // Detect absolute addressing, which we try to turn into references to the + // data section when possible. + uint64_t abs_base; + if (ConstIntVal(addr_exp.e, abs_base)) { + // We don't care what part of the absolute address was stored where, + // 1[0] and 0[1] are the same. + abs_base += offset; + // FIXME: make this less expensive with a binary search or whatever. + for (auto dat : mc.module.data_segments) { + uint64_t dat_base; + if (dat->offset.size() == 1 && + ConstIntVal(&dat->offset.front(), dat_base) && + abs_base >= dat_base && abs_base < dat_base + dat->data.size()) { + // We are inside the range of this data segment! + // Turn expression into data_name[index] + val = Value{{dat->name}, Precedence::Atomic}; + // The new offset is from the start of the data segment, instead of + // whatever it was.. this may be a different value from both the + // original const and offset! + offset = abs_base - dat_base; + } + } + } + // Do the load/store as a generalized indexing operation. + // The offset is divisible by the alignment in 99.99% of + // cases, but the spec doesn't guarantee it, so we must + // have a backup syntax. + auto index = offset % align == 0 + ? std::to_string(offset / align) + : cat(std::to_string(offset), "@", std::to_string(align)); + // Detect the very common case of (base + (index << 2))[0]:int etc. + // so we can instead do base[index]:int + // TODO: (index << 2) on the left of + occurs also. + // TODO: sadly this does not address cases where the shift amount > align. + // (which happens for arrays of structs or arrays of long (with align=4)). + // TODO: also very common is (v = base + (index << 2))[0]:int + if (addr_exp.etype == ExprType::Binary) { + auto& pe = *cast<BinaryExpr>(addr_exp.e); + auto& shift_exp = addr_exp.children[1]; + if (pe.opcode == Opcode::I32Add && shift_exp.etype == ExprType::Binary) { + auto& se = *cast<BinaryExpr>(shift_exp.e); + auto& const_exp = shift_exp.children[1]; + if (se.opcode == Opcode::I32Shl && const_exp.etype == ExprType::Const) { + auto& ce = *cast<ConstExpr>(const_exp.e); + if (ce.const_.type() == Type::I32 && + (1ULL << ce.const_.u32()) == align) { + // Pfew, case detected :( Lets re-write this in Haskell. + // TODO: we're decompiling these twice. + // The thing to the left of << is going to be part of the index. + auto ival = DecompileExpr(shift_exp.children[0], &shift_exp); + if (ival.v.size() == 1) { // Don't bother if huge. + if (offset == 0) { + index = ival.v[0]; + } else { + BracketIfNeeded(ival, Precedence::Add); + index = cat(ival.v[0], " + ", index); + } + // We're going to use the thing to the left of + as the new + // base address: + val = DecompileExpr(addr_exp.children[0], &addr_exp); + } + } + } + } + } + BracketIfNeeded(val, Precedence::Indexing); + val.v.back() += cat("[", index, "]"); + if (append_type) { + val.v.back() += cat(":", GetDecompTypeName(GetMemoryType(op_type, opc)), + lst.GenAlign(align, opc)); + } + val.precedence = Precedence::Indexing; + } + + Value DecompileExpr(const Node& n, const Node* parent) { + std::vector<Value> args; + for (auto& c : n.children) { + args.push_back(DecompileExpr(c, &n)); + } + // First deal with the specialized node types. + switch (n.ntype) { + case NodeType::FlushToVars: { + std::string decls = "let "; + for (Index i = 0; i < n.u.var_count; i++) { + if (i) { + decls += ", "; + } + decls += TempVarName(n.u.var_start + i); + } + decls += " = "; + return WrapNAry(args, decls, "", Precedence::Assign); + } + case NodeType::FlushedVar: { + return Value{{TempVarName(n.u.var_start)}, Precedence::Atomic}; + } + case NodeType::Statements: { + Value stats{{}, Precedence::None}; + for (size_t i = 0; i < n.children.size(); i++) { + auto& s = args[i].v.back(); + if (s.back() != '}' && s.back() != ':') { + s += ';'; + } + std::move(args[i].v.begin(), args[i].v.end(), + std::back_inserter(stats.v)); + } + return stats; + } + case NodeType::EndReturn: { + return WrapNAry(args, "return ", "", Precedence::None); + } + case NodeType::Decl: { + cur_ast->vars_defined[n.u.var->name()].defined = true; + return Value{{"var " + LocalDecl(std::string(n.u.var->name()), + cur_func->GetLocalType(*n.u.var))}, + Precedence::None}; + } + case NodeType::DeclInit: { + if (cur_ast->vars_defined[n.u.var->name()].defined) { + // This has already been pre-declared, output as assign. + return WrapChild(args[0], cat(VarName(n.u.var->name()), " = "), "", + Precedence::None); + } else { + return WrapChild(args[0], + cat("var ", + LocalDecl(std::string(n.u.var->name()), + cur_func->GetLocalType(*n.u.var)), + " = "), + "", Precedence::None); + } + } + case NodeType::Expr: + // We're going to fall thru to the second switch to deal with ExprType. + break; + case NodeType::Uninitialized: + assert(false); + break; + } + // Existing ExprTypes. + switch (n.etype) { + case ExprType::Const: { + auto& c = cast<ConstExpr>(n.e)->const_; + switch (c.type()) { + case Type::I32: + return Value{{std::to_string(static_cast<int32_t>(c.u32()))}, + Precedence::Atomic}; + case Type::I64: + return Value{{std::to_string(static_cast<int64_t>(c.u64())) + "L"}, + Precedence::Atomic}; + case Type::F32: { + float f = Bitcast<float>(c.f32_bits()); + return Value{{to_string(f) + "f"}, Precedence::Atomic}; + } + case Type::F64: { + double d = Bitcast<double>(c.f64_bits()); + return Value{{to_string(d)}, Precedence::Atomic}; + } + case Type::V128: + return Value{{"V128"}, Precedence::Atomic}; // FIXME + default: + WABT_UNREACHABLE; + } + } + case ExprType::LocalGet: { + return Get(*cast<LocalGetExpr>(n.e)); + } + case ExprType::GlobalGet: { + return Get(*cast<GlobalGetExpr>(n.e)); + } + case ExprType::LocalSet: { + return Set(args[0], *cast<LocalSetExpr>(n.e)); + } + case ExprType::GlobalSet: { + return Set(args[0], *cast<GlobalSetExpr>(n.e)); + } + case ExprType::LocalTee: { + auto& te = *cast<LocalTeeExpr>(n.e); + return args.empty() ? Get(te) : Set(args[0], te); + } + case ExprType::Binary: { + auto& be = *cast<BinaryExpr>(n.e); + auto opcs = OpcodeToToken(be.opcode); + // TODO: Is this selection better done on Opcode values directly? + // What if new values get added and OtherBin doesn't make sense? + auto prec = Precedence::OtherBin; + if (opcs == "*" || opcs == "/" || opcs == "%") { + prec = Precedence::Multiply; + } else if (opcs == "+" || opcs == "-") { + prec = Precedence::Add; + } else if (opcs == "&" || opcs == "|" || opcs == "^") { + prec = Precedence::Bit; + } else if (opcs == "<<" || opcs == ">>") { + prec = Precedence::Shift; + } + return WrapBinary(args, opcs, false, prec); + } + case ExprType::Compare: { + auto& ce = *cast<CompareExpr>(n.e); + return WrapBinary(args, OpcodeToToken(ce.opcode), false, + Precedence::Equal); + } + case ExprType::Unary: { + auto& ue = *cast<UnaryExpr>(n.e); + // BracketIfNeeded(stack.back()); + // TODO: also version without () depending on precedence. + return WrapChild(args[0], OpcodeToToken(ue.opcode) + "(", ")", + Precedence::Atomic); + } + case ExprType::Load: { + auto& le = *cast<LoadExpr>(n.e); + LoadStore(args[0], n.children[0], le.offset, le.opcode, le.align, + le.opcode.GetResultType()); + return std::move(args[0]); + } + case ExprType::Store: { + auto& se = *cast<StoreExpr>(n.e); + LoadStore(args[0], n.children[0], se.offset, se.opcode, se.align, + se.opcode.GetParamType2()); + return WrapBinary(args, "=", true, Precedence::Assign); + } + case ExprType::If: { + auto ife = cast<IfExpr>(n.e); + Value* elsep = nullptr; + if (!ife->false_.empty()) { + elsep = &args[2]; + } + auto& thenp = args[1]; + auto& ifs = args[0]; + bool multiline = ifs.v.size() > 1 || thenp.v.size() > 1; + size_t width = ifs.width() + thenp.width(); + if (elsep) { + width += elsep->width(); + multiline = multiline || elsep->v.size() > 1; + } + multiline = multiline || width > target_exp_width; + if (multiline) { + auto if_start = std::string_view("if ("); + IndentValue(ifs, if_start.size(), if_start); + ifs.v.back() += ") {"; + IndentValue(thenp, indent_amount, {}); + std::move(thenp.v.begin(), thenp.v.end(), std::back_inserter(ifs.v)); + if (elsep) { + ifs.v.push_back("} else {"); + IndentValue(*elsep, indent_amount, {}); + std::move(elsep->v.begin(), elsep->v.end(), + std::back_inserter(ifs.v)); + } + ifs.v.push_back("}"); + ifs.precedence = Precedence::If; + return std::move(ifs); + } else { + auto s = cat("if (", ifs.v[0], ") { ", thenp.v[0], " }"); + if (elsep) + s += cat(" else { ", elsep->v[0], " }"); + return Value{{std::move(s)}, Precedence::If}; + } + } + case ExprType::Block: { + auto& val = args[0]; + val.v.push_back( + cat("label ", VarName(cast<BlockExpr>(n.e)->block.label), ":")); + // If this block is part of a larger statement scope, it doesn't + // need its own indenting, but if its part of an exp we wrap it in {}. + if (parent && parent->ntype != NodeType::Statements && + parent->etype != ExprType::Block && + parent->etype != ExprType::Loop && + (parent->etype != ExprType::If || &parent->children[0] == &n)) { + IndentValue(val, indent_amount, {}); + val.v.insert(val.v.begin(), "{"); + val.v.push_back("}"); + } + val.precedence = Precedence::Atomic; + return std::move(val); + } + case ExprType::Loop: { + auto& val = args[0]; + auto& block = cast<LoopExpr>(n.e)->block; + IndentValue(val, indent_amount, {}); + val.v.insert(val.v.begin(), cat("loop ", VarName(block.label), " {")); + val.v.push_back("}"); + val.precedence = Precedence::Atomic; + return std::move(val); + } + case ExprType::Br: { + auto be = cast<BrExpr>(n.e); + return Value{{(n.u.lt == LabelType::Loop ? "continue " : "goto ") + + VarName(be->var.name())}, + Precedence::None}; + } + case ExprType::BrIf: { + auto bie = cast<BrIfExpr>(n.e); + auto jmp = n.u.lt == LabelType::Loop ? "continue" : "goto"; + return WrapChild(args[0], "if (", + cat(") ", jmp, " ", VarName(bie->var.name())), + Precedence::None); + } + case ExprType::Return: { + return WrapNAry(args, "return ", "", Precedence::None); + } + case ExprType::Rethrow: { + return WrapNAry(args, "rethrow ", "", Precedence::None); + } + case ExprType::Drop: { + // Silent dropping of return values is very common, so currently + // don't output this. + return std::move(args[0]); + } + case ExprType::Nop: { + return Value{{"nop"}, Precedence::None}; + } + case ExprType::Unreachable: { + return Value{{"unreachable"}, Precedence::None}; + } + case ExprType::RefNull: { + return Value{{"null"}, Precedence::Atomic}; + } + case ExprType::BrTable: { + auto bte = cast<BrTableExpr>(n.e); + std::string ts = "br_table["; + for (auto& v : bte->targets) { + ts += VarName(v.name()); + ts += ", "; + } + ts += ".."; + ts += VarName(bte->default_target.name()); + ts += "]("; + return WrapChild(args[0], ts, ")", Precedence::Atomic); + } + case ExprType::CodeMetadata: { + auto cme = cast<CodeMetadataExpr>(n.e); + std::string c = "// @metadata.code." + cme->name + " "; + c += BinaryToString(cme->data); + return Value{{std::move(c)}, Precedence::None}; + } + default: { + // Everything that looks like a function call. + std::string name; + auto precedence = Precedence::Atomic; + switch (n.etype) { + case ExprType::Call: + name = cast<CallExpr>(n.e)->var.name(); + break; + case ExprType::ReturnCall: + name = "return_call " + cast<ReturnCallExpr>(n.e)->var.name(); + precedence = Precedence::None; + break; + case ExprType::Convert: + name = std::string(OpcodeToToken(cast<ConvertExpr>(n.e)->opcode)); + break; + case ExprType::Ternary: + name = std::string(OpcodeToToken(cast<TernaryExpr>(n.e)->opcode)); + break; + case ExprType::Select: + // This one looks like it could be translated to "?:" style ternary, + // but the arguments are NOT lazy, and side effects definitely do + // occur in the branches. So it has no clear equivalent in C-syntax. + // To emphasize that all args are being evaluated in order, we + // leave it as a function call. + name = "select_if"; + break; + case ExprType::MemoryGrow: + name = "memory_grow"; + break; + case ExprType::MemorySize: + name = "memory_size"; + break; + case ExprType::MemoryCopy: + name = "memory_copy"; + break; + case ExprType::MemoryFill: + name = "memory_fill"; + break; + case ExprType::RefIsNull: + name = "is_null"; + break; + case ExprType::CallIndirect: + name = "call_indirect"; + break; + case ExprType::ReturnCallIndirect: + name = "return_call call_indirect"; + break; + default: + name = GetExprTypeName(n.etype); + break; + } + return WrapNAry(args, name + "(", ")", precedence); + } + } + } + + bool CheckImportExport(std::string& s, + ExternalKind kind, + Index index, + std::string_view name) { + // Figure out if this thing is imported, exported, or neither. + auto is_import = mc.module.IsImport(kind, Var(index, Location())); + // TODO: is this the best way to check for export? + // FIXME: this doesn't work for functions that get renamed in some way, + // as the export has the original name.. + auto xport = mc.module.GetExport(name); + auto is_export = xport && xport->kind == kind; + if (is_export) + s += "export "; + if (is_import) + s += "import "; + return is_import; + } + + std::string InitExp(const ExprList& el) { + assert(!el.empty()); + AST ast(mc, nullptr); + ast.Construct(el, 1, 0, false); + auto val = DecompileExpr(ast.exp_stack[0], nullptr); + assert(ast.exp_stack.size() == 1 && val.v.size() == 1); + return std::move(val.v[0]); + } + + // FIXME: Merge with WatWriter::WriteQuotedData somehow. + std::string BinaryToString(const std::vector<uint8_t>& in) { + std::string s = "\""; + size_t line_start = 0; + static const char s_hexdigits[] = "0123456789abcdef"; + for (auto c : in) { + if (c >= ' ' && c <= '~') { + s += c; + } else { + s += '\\'; + s += s_hexdigits[c >> 4]; + s += s_hexdigits[c & 0xf]; + } + if (s.size() - line_start > target_exp_width) { + if (line_start == 0) { + s = " " + s; + } + s += "\"\n "; + line_start = s.size(); + s += "\""; + } + } + s += '\"'; + return s; + } + + std::string Decompile() { + std::string s; + // Memories. + Index memory_index = 0; + for (auto m : mc.module.memories) { + auto is_import = + CheckImportExport(s, ExternalKind::Memory, memory_index, m->name); + s += cat("memory ", m->name); + if (!is_import) { + s += cat("(initial: ", std::to_string(m->page_limits.initial), + ", max: ", std::to_string(m->page_limits.max), ")"); + } + s += ";\n"; + memory_index++; + } + if (!mc.module.memories.empty()) + s += "\n"; + + // Globals. + Index global_index = 0; + for (auto g : mc.module.globals) { + auto is_import = + CheckImportExport(s, ExternalKind::Global, global_index, g->name); + s += cat("global ", g->name, ":", GetDecompTypeName(g->type)); + if (!is_import) { + s += cat(" = ", InitExp(g->init_expr)); + } + s += ";\n"; + global_index++; + } + if (!mc.module.globals.empty()) + s += "\n"; + + // Tables. + Index table_index = 0; + for (auto tab : mc.module.tables) { + auto is_import = + CheckImportExport(s, ExternalKind::Table, table_index, tab->name); + s += cat("table ", tab->name, ":", GetDecompTypeName(tab->elem_type)); + if (!is_import) { + s += cat("(min: ", std::to_string(tab->elem_limits.initial), + ", max: ", std::to_string(tab->elem_limits.max), ")"); + } + s += ";\n"; + table_index++; + } + if (!mc.module.tables.empty()) + s += "\n"; + + // Data. + for (auto dat : mc.module.data_segments) { + s += cat("data ", dat->name, "(offset: ", InitExp(dat->offset), ") ="); + auto ds = BinaryToString(dat->data); + if (ds.size() > target_exp_width / 2) { + s += "\n"; + } else { + s += " "; + } + s += ds; + s += ";\n"; + } + if (!mc.module.data_segments.empty()) + s += "\n"; + + // Code. + Index func_index = 0; + for (auto f : mc.module.funcs) { + cur_func = f; + auto is_import = + CheckImportExport(s, ExternalKind::Func, func_index, f->name); + AST ast(mc, f); + cur_ast = * + if (!is_import) { + ast.Construct(f->exprs, f->GetNumResults(), 0, true); + lst.Track(ast.exp_stack[0]); + lst.CheckLayouts(); + } + s += cat("function ", f->name, "("); + for (Index i = 0; i < f->GetNumParams(); i++) { + if (i) + s += ", "; + auto t = f->GetParamType(i); + auto name = "$" + IndexToAlphaName(i); + s += LocalDecl(name, t); + } + s += ")"; + if (f->GetNumResults()) { + if (f->GetNumResults() == 1) { + s += cat(":", GetDecompTypeName(f->GetResultType(0))); + } else { + s += ":("; + for (Index i = 0; i < f->GetNumResults(); i++) { + if (i) + s += ", "; + s += GetDecompTypeName(f->GetResultType(i)); + } + s += ")"; + } + } + if (is_import) { + s += ";"; + } else { + s += " {\n"; + auto val = DecompileExpr(ast.exp_stack[0], nullptr); + IndentValue(val, indent_amount, {}); + for (auto& stat : val.v) { + s += stat; + s += "\n"; + } + s += "}"; + } + s += "\n\n"; + mc.EndFunc(); + lst.Clear(); + func_index++; + cur_ast = nullptr; + cur_func = nullptr; + } + return s; + } + + ModuleContext mc; + const DecompileOptions& options; + size_t indent_amount = 2; + size_t target_exp_width = 70; + const Func* cur_func = nullptr; + AST* cur_ast = nullptr; + LoadStoreTracking lst; +}; + +std::string Decompile(const Module& module, const DecompileOptions& options) { + Decompiler decompiler(module, options); + return decompiler.Decompile(); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/emscripten-exports.txt b/third_party/wasm2c/src/emscripten-exports.txt new file mode 100644 index 0000000000..638673120a --- /dev/null +++ b/third_party/wasm2c/src/emscripten-exports.txt @@ -0,0 +1,72 @@ +_free +_malloc +_wabt_annotations_enabled +_wabt_apply_names_module +_wabt_bulk_memory_enabled +_wabt_code_metadata_enabled +_wabt_destroy_errors +_wabt_destroy_features +_wabt_destroy_module +_wabt_destroy_output_buffer +_wabt_destroy_parse_wat_result +_wabt_destroy_read_binary_result +_wabt_destroy_wast_lexer +_wabt_destroy_write_module_result +_wabt_exceptions_enabled +_wabt_extended_const_enabled +_wabt_format_binary_errors +_wabt_format_text_errors +_wabt_function_references_enabled +_wabt_gc_enabled +_wabt_generate_names_module +_wabt_memory64_enabled +_wabt_multi_memory_enabled +_wabt_multi_value_enabled +_wabt_mutable_globals_enabled +_wabt_new_errors +_wabt_new_features +_wabt_new_wast_buffer_lexer +_wabt_output_buffer_get_data +_wabt_output_buffer_get_size +_wabt_parse_wast +_wabt_parse_wast_result_get_result +_wabt_parse_wast_result_release_module +_wabt_parse_wat +_wabt_parse_wat_result_get_result +_wabt_parse_wat_result_release_module +_wabt_read_binary +_wabt_read_binary_result_get_result +_wabt_read_binary_result_release_module +_wabt_reference_types_enabled +_wabt_relaxed_simd_enabled +_wabt_sat_float_to_int_enabled +_wabt_set_annotations_enabled +_wabt_set_bulk_memory_enabled +_wabt_set_code_metadata_enabled +_wabt_set_exceptions_enabled +_wabt_set_extended_const_enabled +_wabt_set_function_references_enabled +_wabt_set_gc_enabled +_wabt_set_memory64_enabled +_wabt_set_multi_memory_enabled +_wabt_set_multi_value_enabled +_wabt_set_mutable_globals_enabled +_wabt_set_reference_types_enabled +_wabt_set_relaxed_simd_enabled +_wabt_set_sat_float_to_int_enabled +_wabt_set_sign_extension_enabled +_wabt_set_simd_enabled +_wabt_set_tail_call_enabled +_wabt_set_threads_enabled +_wabt_sign_extension_enabled +_wabt_simd_enabled +_wabt_tail_call_enabled +_wabt_threads_enabled +_wabt_validate_module +_wabt_validate_script +_wabt_write_binary_module +_wabt_write_binary_spec_script +_wabt_write_module_result_get_result +_wabt_write_module_result_release_log_output_buffer +_wabt_write_module_result_release_output_buffer +_wabt_write_text_module diff --git a/third_party/wasm2c/src/emscripten-helpers.cc b/third_party/wasm2c/src/emscripten-helpers.cc new file mode 100644 index 0000000000..127a8c458d --- /dev/null +++ b/third_party/wasm2c/src/emscripten-helpers.cc @@ -0,0 +1,403 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WABT_EMSCRIPTEN_HELPERS_H_ +#define WABT_EMSCRIPTEN_HELPERS_H_ + +#include <cstddef> + +#include <algorithm> +#include <iterator> +#include <memory> +#include <utility> +#include <vector> + +#include "wabt/apply-names.h" +#include "wabt/binary-reader-ir.h" +#include "wabt/binary-reader.h" +#include "wabt/binary-writer-spec.h" +#include "wabt/binary-writer.h" +#include "wabt/common.h" +#include "wabt/error-formatter.h" +#include "wabt/feature.h" +#include "wabt/filenames.h" +#include "wabt/generate-names.h" +#include "wabt/ir.h" +#include "wabt/stream.h" +#include "wabt/validator.h" +#include "wabt/wast-lexer.h" +#include "wabt/wast-parser.h" +#include "wabt/wat-writer.h" + +using WabtOutputBufferPtr = std::unique_ptr<wabt::OutputBuffer>; +using WabtFilenameOutputBufferPair = + std::pair<std::string, WabtOutputBufferPtr>; + +struct WabtParseWatResult { + wabt::Result result; + std::unique_ptr<wabt::Module> module; +}; + +struct WabtReadBinaryResult { + wabt::Result result; + std::unique_ptr<wabt::Module> module; +}; + +struct WabtWriteModuleResult { + wabt::Result result; + WabtOutputBufferPtr buffer; + WabtOutputBufferPtr log_buffer; +}; + +struct WabtWriteScriptResult { + wabt::Result result; + WabtOutputBufferPtr json_buffer; + WabtOutputBufferPtr log_buffer; + std::vector<WabtFilenameOutputBufferPair> module_buffers; +}; + +struct WabtParseWastResult { + wabt::Result result; + std::unique_ptr<wabt::Script> script; +}; + +extern "C" { + +wabt::Features* wabt_new_features(void) { + return new wabt::Features(); +} + +void wabt_destroy_features(wabt::Features* f) { + delete f; +} + +#define WABT_FEATURE(variable, flag, default_, help) \ + bool wabt_##variable##_enabled(wabt::Features* f) { \ + return f->variable##_enabled(); \ + } \ + void wabt_set_##variable##_enabled(wabt::Features* f, int enabled) { \ + f->set_##variable##_enabled(enabled); \ + } +#include "wabt/feature.def" +#undef WABT_FEATURE + +wabt::WastLexer* wabt_new_wast_buffer_lexer(const char* filename, + const void* data, + size_t size, + wabt::Errors* errors) { + std::unique_ptr<wabt::WastLexer> lexer = + wabt::WastLexer::CreateBufferLexer(filename, data, size, errors); + return lexer.release(); +} + +WabtParseWatResult* wabt_parse_wat(wabt::WastLexer* lexer, + wabt::Features* features, + wabt::Errors* errors) { + wabt::WastParseOptions options(*features); + WabtParseWatResult* result = new WabtParseWatResult(); + std::unique_ptr<wabt::Module> module; + result->result = wabt::ParseWatModule(lexer, &module, errors, &options); + result->module = std::move(module); + return result; +} + +WabtParseWastResult* wabt_parse_wast(wabt::WastLexer* lexer, + wabt::Features* features, + wabt::Errors* errors) { + wabt::WastParseOptions options(*features); + WabtParseWastResult* result = new WabtParseWastResult(); + std::unique_ptr<wabt::Script> script; + result->result = wabt::ParseWastScript(lexer, &script, errors, &options); + result->script = std::move(script); + return result; +} + +WabtReadBinaryResult* wabt_read_binary(const void* data, + size_t size, + int read_debug_names, + wabt::Features* features, + wabt::Errors* errors) { + wabt::ReadBinaryOptions options; + options.features = *features; + options.read_debug_names = read_debug_names; + + WabtReadBinaryResult* result = new WabtReadBinaryResult(); + wabt::Module* module = new wabt::Module(); + // TODO(binji): Pass through from wabt_read_binary parameter. + const char* filename = "<binary>"; + result->result = + wabt::ReadBinaryIr(filename, data, size, options, errors, module); + result->module.reset(module); + return result; +} + +wabt::Result::Enum wabt_validate_module(wabt::Module* module, + wabt::Features* features, + wabt::Errors* errors) { + wabt::ValidateOptions options; + options.features = *features; + return ValidateModule(module, errors, options); +} + +wabt::Result::Enum wabt_validate_script(wabt::Script* script, + wabt::Features* features, + wabt::Errors* errors) { + wabt::ValidateOptions options; + options.features = *features; + return ValidateScript(script, errors, options); +} + +WabtWriteScriptResult* wabt_write_binary_spec_script( + wabt::Script* script, + const char* source_filename, + const char* out_filename, + int log, + int canonicalize_lebs, + int relocatable, + int write_debug_names) { + wabt::MemoryStream log_stream; + wabt::MemoryStream* log_stream_p = log ? &log_stream : nullptr; + + wabt::WriteBinaryOptions options; + options.canonicalize_lebs = canonicalize_lebs; + options.relocatable = relocatable; + options.write_debug_names = write_debug_names; + + std::vector<wabt::FilenameMemoryStreamPair> module_streams; + wabt::MemoryStream json_stream(log_stream_p); + + std::string module_filename_noext( + wabt::StripExtension(out_filename ? out_filename : source_filename)); + + WabtWriteScriptResult* result = new WabtWriteScriptResult(); + result->result = WriteBinarySpecScript(&json_stream, script, source_filename, + module_filename_noext, options, + &module_streams, log_stream_p); + + if (result->result == wabt::Result::Ok) { + result->json_buffer = json_stream.ReleaseOutputBuffer(); + result->log_buffer = log ? log_stream.ReleaseOutputBuffer() : nullptr; + std::transform(module_streams.begin(), module_streams.end(), + std::back_inserter(result->module_buffers), + [](wabt::FilenameMemoryStreamPair& pair) { + return WabtFilenameOutputBufferPair( + pair.filename, pair.stream->ReleaseOutputBuffer()); + }); + } + return result; +} + +wabt::Result::Enum wabt_apply_names_module(wabt::Module* module) { + return ApplyNames(module); +} + +wabt::Result::Enum wabt_generate_names_module(wabt::Module* module) { + return GenerateNames(module); +} + +WabtWriteModuleResult* wabt_write_binary_module(wabt::Module* module, + int log, + int canonicalize_lebs, + int relocatable, + int write_debug_names) { + wabt::MemoryStream log_stream; + wabt::WriteBinaryOptions options; + options.canonicalize_lebs = canonicalize_lebs; + options.relocatable = relocatable; + options.write_debug_names = write_debug_names; + + wabt::MemoryStream stream(log ? &log_stream : nullptr); + WabtWriteModuleResult* result = new WabtWriteModuleResult(); + result->result = WriteBinaryModule(&stream, module, options); + if (result->result == wabt::Result::Ok) { + result->buffer = stream.ReleaseOutputBuffer(); + result->log_buffer = log ? log_stream.ReleaseOutputBuffer() : nullptr; + } + return result; +} + +WabtWriteModuleResult* wabt_write_text_module(wabt::Module* module, + int fold_exprs, + int inline_export) { + wabt::WriteWatOptions options; + options.fold_exprs = fold_exprs; + options.inline_export = inline_export; + + wabt::MemoryStream stream; + WabtWriteModuleResult* result = new WabtWriteModuleResult(); + result->result = WriteWat(&stream, module, options); + if (result->result == wabt::Result::Ok) { + result->buffer = stream.ReleaseOutputBuffer(); + } + return result; +} + +void wabt_destroy_module(wabt::Module* module) { + delete module; +} + +void wabt_destroy_wast_lexer(wabt::WastLexer* lexer) { + delete lexer; +} + +// Errors +wabt::Errors* wabt_new_errors(void) { + return new wabt::Errors(); +} + +wabt::OutputBuffer* wabt_format_text_errors(wabt::Errors* errors, + wabt::WastLexer* lexer) { + auto line_finder = lexer->MakeLineFinder(); + std::string string_result = FormatErrorsToString( + *errors, wabt::Location::Type::Text, line_finder.get()); + + wabt::OutputBuffer* result = new wabt::OutputBuffer(); + std::copy(string_result.begin(), string_result.end(), + std::back_inserter(result->data)); + return result; +} + +wabt::OutputBuffer* wabt_format_binary_errors(wabt::Errors* errors) { + std::string string_result = + FormatErrorsToString(*errors, wabt::Location::Type::Binary); + + wabt::OutputBuffer* result = new wabt::OutputBuffer(); + std::copy(string_result.begin(), string_result.end(), + std::back_inserter(result->data)); + return result; +} + +void wabt_destroy_errors(wabt::Errors* errors) { + delete errors; +} + +// WabtParseWatResult +wabt::Result::Enum wabt_parse_wat_result_get_result( + WabtParseWatResult* result) { + return result->result; +} + +wabt::Module* wabt_parse_wat_result_release_module(WabtParseWatResult* result) { + return result->module.release(); +} + +void wabt_destroy_parse_wat_result(WabtParseWatResult* result) { + delete result; +} + +// WabtParseWastResult +wabt::Result::Enum wabt_parse_wast_result_get_result( + WabtParseWastResult* result) { + return result->result; +} + +wabt::Script* wabt_parse_wast_result_release_module( + WabtParseWastResult* result) { + return result->script.release(); +} + +void wabt_destroy_parse_wast_result(WabtParseWastResult* result) { + delete result; +} + +// WabtReadBinaryResult +wabt::Result::Enum wabt_read_binary_result_get_result( + WabtReadBinaryResult* result) { + return result->result; +} + +wabt::Module* wabt_read_binary_result_release_module( + WabtReadBinaryResult* result) { + return result->module.release(); +} + +void wabt_destroy_read_binary_result(WabtReadBinaryResult* result) { + delete result; +} + +// WabtWriteModuleResult +wabt::Result::Enum wabt_write_module_result_get_result( + WabtWriteModuleResult* result) { + return result->result; +} + +wabt::OutputBuffer* wabt_write_module_result_release_output_buffer( + WabtWriteModuleResult* result) { + return result->buffer.release(); +} + +wabt::OutputBuffer* wabt_write_module_result_release_log_output_buffer( + WabtWriteModuleResult* result) { + return result->log_buffer.release(); +} + +void wabt_destroy_write_module_result(WabtWriteModuleResult* result) { + delete result; +} + +// WabtWriteScriptResult +wabt::Result::Enum wabt_write_script_result_get_result( + WabtWriteScriptResult* result) { + return result->result; +} + +wabt::OutputBuffer* wabt_write_script_result_release_json_output_buffer( + WabtWriteScriptResult* result) { + return result->json_buffer.release(); +} + +wabt::OutputBuffer* wabt_write_script_result_release_log_output_buffer( + WabtWriteScriptResult* result) { + return result->log_buffer.release(); +} + +size_t wabt_write_script_result_get_module_count( + WabtWriteScriptResult* result) { + return result->module_buffers.size(); +} + +const char* wabt_write_script_result_get_module_filename( + WabtWriteScriptResult* result, + size_t index) { + return result->module_buffers[index].first.c_str(); +} + +wabt::OutputBuffer* wabt_write_script_result_release_module_output_buffer( + WabtWriteScriptResult* result, + size_t index) { + return result->module_buffers[index].second.release(); +} + +void wabt_destroy_write_script_result(WabtWriteScriptResult* result) { + delete result; +} + +// wabt::OutputBuffer* +const void* wabt_output_buffer_get_data(wabt::OutputBuffer* output_buffer) { + return output_buffer->data.data(); +} + +size_t wabt_output_buffer_get_size(wabt::OutputBuffer* output_buffer) { + return output_buffer->data.size(); +} + +void wabt_destroy_output_buffer(wabt::OutputBuffer* output_buffer) { + delete output_buffer; +} + +} // extern "C" + +#endif /* WABT_EMSCRIPTEN_HELPERS_H_ */ diff --git a/third_party/wasm2c/src/error-formatter.cc b/third_party/wasm2c/src/error-formatter.cc new file mode 100644 index 0000000000..9a14cfac25 --- /dev/null +++ b/third_party/wasm2c/src/error-formatter.cc @@ -0,0 +1,127 @@ +/* + * Copyright 2018 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/error-formatter.h" + +namespace wabt { + +namespace { + +std::string FormatError(const Error& error, + Location::Type location_type, + const Color& color, + LexerSourceLineFinder* line_finder, + int source_line_max_length, + int indent) { + std::string indent_str(indent, ' '); + std::string result = indent_str; + + result += color.MaybeBoldCode(); + + const Location& loc = error.loc; + if (!loc.filename.empty()) { + result += loc.filename; + result += ":"; + } + + if (location_type == Location::Type::Text) { + result += StringPrintf("%d:%d: ", loc.line, loc.first_column); + } else if (loc.offset != kInvalidOffset) { + result += StringPrintf("%07" PRIzx ": ", loc.offset); + } + + result += color.MaybeRedCode(); + result += GetErrorLevelName(error.error_level); + result += ": "; + result += color.MaybeDefaultCode(); + + result += error.message; + result += '\n'; + + LexerSourceLineFinder::SourceLine source_line; + if (line_finder) { + line_finder->GetSourceLine(loc, source_line_max_length, &source_line); + } + + if (!source_line.line.empty()) { + result += indent_str; + result += source_line.line; + result += '\n'; + result += indent_str; + + size_t num_spaces = (loc.first_column - 1) - source_line.column_offset; + size_t num_carets = loc.last_column - loc.first_column; + num_carets = std::min(num_carets, source_line.line.size() - num_spaces); + num_carets = std::max<size_t>(num_carets, 1); + result.append(num_spaces, ' '); + result += color.MaybeBoldCode(); + result += color.MaybeGreenCode(); + result.append(num_carets, '^'); + result += color.MaybeDefaultCode(); + result += '\n'; + } + + return result; +} + +} // End of anonymous namespace + +std::string FormatErrorsToString(const Errors& errors, + Location::Type location_type, + LexerSourceLineFinder* line_finder, + const Color& color, + const std::string& header, + PrintHeader print_header, + int source_line_max_length) { + std::string result; + for (const auto& error : errors) { + if (!header.empty()) { + switch (print_header) { + case PrintHeader::Never: + break; + case PrintHeader::Once: + print_header = PrintHeader::Never; + [[fallthrough]]; + case PrintHeader::Always: + result += header; + result += ":\n"; + break; + } + } + + int indent = header.empty() ? 0 : 2; + + result += FormatError(error, location_type, color, line_finder, + source_line_max_length, indent); + } + return result; +} + +void FormatErrorsToFile(const Errors& errors, + Location::Type location_type, + LexerSourceLineFinder* line_finder, + FILE* file, + const std::string& header, + PrintHeader print_header, + int source_line_max_length) { + Color color(file); + std::string s = + FormatErrorsToString(errors, location_type, line_finder, color, header, + print_header, source_line_max_length); + fwrite(s.data(), 1, s.size(), file); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/expr-visitor.cc b/third_party/wasm2c/src/expr-visitor.cc new file mode 100644 index 0000000000..05cd2798ca --- /dev/null +++ b/third_party/wasm2c/src/expr-visitor.cc @@ -0,0 +1,472 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/expr-visitor.h" + +#include "wabt/cast.h" +#include "wabt/ir.h" + +namespace wabt { + +ExprVisitor::ExprVisitor(Delegate* delegate) : delegate_(delegate) {} + +Result ExprVisitor::VisitExpr(Expr* root_expr) { + state_stack_.clear(); + expr_stack_.clear(); + expr_iter_stack_.clear(); + catch_index_stack_.clear(); + + PushDefault(root_expr); + + while (!state_stack_.empty()) { + State state = state_stack_.back(); + auto* expr = expr_stack_.back(); + + switch (state) { + case State::Default: + PopDefault(); + CHECK_RESULT(HandleDefaultState(expr)); + break; + + case State::Block: { + auto block_expr = cast<BlockExpr>(expr); + auto& iter = expr_iter_stack_.back(); + if (iter != block_expr->block.exprs.end()) { + PushDefault(&*iter++); + } else { + CHECK_RESULT(delegate_->EndBlockExpr(block_expr)); + PopExprlist(); + } + break; + } + + case State::IfTrue: { + auto if_expr = cast<IfExpr>(expr); + auto& iter = expr_iter_stack_.back(); + if (iter != if_expr->true_.exprs.end()) { + PushDefault(&*iter++); + } else { + CHECK_RESULT(delegate_->AfterIfTrueExpr(if_expr)); + PopExprlist(); + PushExprlist(State::IfFalse, expr, if_expr->false_); + } + break; + } + + case State::IfFalse: { + auto if_expr = cast<IfExpr>(expr); + auto& iter = expr_iter_stack_.back(); + if (iter != if_expr->false_.end()) { + PushDefault(&*iter++); + } else { + CHECK_RESULT(delegate_->EndIfExpr(if_expr)); + PopExprlist(); + } + break; + } + + case State::Loop: { + auto loop_expr = cast<LoopExpr>(expr); + auto& iter = expr_iter_stack_.back(); + if (iter != loop_expr->block.exprs.end()) { + PushDefault(&*iter++); + } else { + CHECK_RESULT(delegate_->EndLoopExpr(loop_expr)); + PopExprlist(); + } + break; + } + + case State::Try: { + auto try_expr = cast<TryExpr>(expr); + auto& iter = expr_iter_stack_.back(); + if (iter != try_expr->block.exprs.end()) { + PushDefault(&*iter++); + } else { + PopExprlist(); + switch (try_expr->kind) { + case TryKind::Catch: + if (!try_expr->catches.empty()) { + Catch& catch_ = try_expr->catches[0]; + CHECK_RESULT(delegate_->OnCatchExpr(try_expr, &catch_)); + PushCatch(expr, 0, catch_.exprs); + } else { + CHECK_RESULT(delegate_->EndTryExpr(try_expr)); + } + break; + case TryKind::Delegate: + CHECK_RESULT(delegate_->OnDelegateExpr(try_expr)); + break; + case TryKind::Plain: + CHECK_RESULT(delegate_->EndTryExpr(try_expr)); + break; + } + } + break; + } + + case State::Catch: { + auto try_expr = cast<TryExpr>(expr); + Index catch_index = catch_index_stack_.back(); + auto& iter = expr_iter_stack_.back(); + if (iter != try_expr->catches[catch_index].exprs.end()) { + PushDefault(&*iter++); + } else { + PopCatch(); + catch_index++; + if (catch_index < try_expr->catches.size()) { + Catch& catch_ = try_expr->catches[catch_index]; + CHECK_RESULT(delegate_->OnCatchExpr(try_expr, &catch_)); + PushCatch(expr, catch_index, catch_.exprs); + } else { + CHECK_RESULT(delegate_->EndTryExpr(try_expr)); + } + } + break; + } + } + } + + return Result::Ok; +} + +Result ExprVisitor::VisitExprList(ExprList& exprs) { + for (Expr& expr : exprs) + CHECK_RESULT(VisitExpr(&expr)); + return Result::Ok; +} + +Result ExprVisitor::VisitFunc(Func* func) { + return VisitExprList(func->exprs); +} + +Result ExprVisitor::HandleDefaultState(Expr* expr) { + switch (expr->type()) { + case ExprType::AtomicLoad: + CHECK_RESULT(delegate_->OnAtomicLoadExpr(cast<AtomicLoadExpr>(expr))); + break; + + case ExprType::AtomicStore: + CHECK_RESULT(delegate_->OnAtomicStoreExpr(cast<AtomicStoreExpr>(expr))); + break; + + case ExprType::AtomicRmw: + CHECK_RESULT(delegate_->OnAtomicRmwExpr(cast<AtomicRmwExpr>(expr))); + break; + + case ExprType::AtomicRmwCmpxchg: + CHECK_RESULT( + delegate_->OnAtomicRmwCmpxchgExpr(cast<AtomicRmwCmpxchgExpr>(expr))); + break; + + case ExprType::AtomicWait: + CHECK_RESULT(delegate_->OnAtomicWaitExpr(cast<AtomicWaitExpr>(expr))); + break; + + case ExprType::AtomicFence: + CHECK_RESULT(delegate_->OnAtomicFenceExpr(cast<AtomicFenceExpr>(expr))); + break; + + case ExprType::AtomicNotify: + CHECK_RESULT(delegate_->OnAtomicNotifyExpr(cast<AtomicNotifyExpr>(expr))); + break; + + case ExprType::Binary: + CHECK_RESULT(delegate_->OnBinaryExpr(cast<BinaryExpr>(expr))); + break; + + case ExprType::Block: { + auto block_expr = cast<BlockExpr>(expr); + CHECK_RESULT(delegate_->BeginBlockExpr(block_expr)); + PushExprlist(State::Block, expr, block_expr->block.exprs); + break; + } + + case ExprType::Br: + CHECK_RESULT(delegate_->OnBrExpr(cast<BrExpr>(expr))); + break; + + case ExprType::BrIf: + CHECK_RESULT(delegate_->OnBrIfExpr(cast<BrIfExpr>(expr))); + break; + + case ExprType::BrTable: + CHECK_RESULT(delegate_->OnBrTableExpr(cast<BrTableExpr>(expr))); + break; + + case ExprType::Call: + CHECK_RESULT(delegate_->OnCallExpr(cast<CallExpr>(expr))); + break; + + case ExprType::CallIndirect: + CHECK_RESULT(delegate_->OnCallIndirectExpr(cast<CallIndirectExpr>(expr))); + break; + + case ExprType::CallRef: + CHECK_RESULT(delegate_->OnCallRefExpr(cast<CallRefExpr>(expr))); + break; + + case ExprType::CodeMetadata: + CHECK_RESULT(delegate_->OnCodeMetadataExpr(cast<CodeMetadataExpr>(expr))); + break; + + case ExprType::Compare: + CHECK_RESULT(delegate_->OnCompareExpr(cast<CompareExpr>(expr))); + break; + + case ExprType::Const: + CHECK_RESULT(delegate_->OnConstExpr(cast<ConstExpr>(expr))); + break; + + case ExprType::Convert: + CHECK_RESULT(delegate_->OnConvertExpr(cast<ConvertExpr>(expr))); + break; + + case ExprType::Drop: + CHECK_RESULT(delegate_->OnDropExpr(cast<DropExpr>(expr))); + break; + + case ExprType::GlobalGet: + CHECK_RESULT(delegate_->OnGlobalGetExpr(cast<GlobalGetExpr>(expr))); + break; + + case ExprType::GlobalSet: + CHECK_RESULT(delegate_->OnGlobalSetExpr(cast<GlobalSetExpr>(expr))); + break; + + case ExprType::If: { + auto if_expr = cast<IfExpr>(expr); + CHECK_RESULT(delegate_->BeginIfExpr(if_expr)); + PushExprlist(State::IfTrue, expr, if_expr->true_.exprs); + break; + } + + case ExprType::Load: + CHECK_RESULT(delegate_->OnLoadExpr(cast<LoadExpr>(expr))); + break; + + case ExprType::LoadSplat: + CHECK_RESULT(delegate_->OnLoadSplatExpr(cast<LoadSplatExpr>(expr))); + break; + + case ExprType::LoadZero: + CHECK_RESULT(delegate_->OnLoadZeroExpr(cast<LoadZeroExpr>(expr))); + break; + + case ExprType::LocalGet: + CHECK_RESULT(delegate_->OnLocalGetExpr(cast<LocalGetExpr>(expr))); + break; + + case ExprType::LocalSet: + CHECK_RESULT(delegate_->OnLocalSetExpr(cast<LocalSetExpr>(expr))); + break; + + case ExprType::LocalTee: + CHECK_RESULT(delegate_->OnLocalTeeExpr(cast<LocalTeeExpr>(expr))); + break; + + case ExprType::Loop: { + auto loop_expr = cast<LoopExpr>(expr); + CHECK_RESULT(delegate_->BeginLoopExpr(loop_expr)); + PushExprlist(State::Loop, expr, loop_expr->block.exprs); + break; + } + + case ExprType::MemoryCopy: + CHECK_RESULT(delegate_->OnMemoryCopyExpr(cast<MemoryCopyExpr>(expr))); + break; + + case ExprType::DataDrop: + CHECK_RESULT(delegate_->OnDataDropExpr(cast<DataDropExpr>(expr))); + break; + + case ExprType::MemoryFill: + CHECK_RESULT(delegate_->OnMemoryFillExpr(cast<MemoryFillExpr>(expr))); + break; + + case ExprType::MemoryGrow: + CHECK_RESULT(delegate_->OnMemoryGrowExpr(cast<MemoryGrowExpr>(expr))); + break; + + case ExprType::MemoryInit: + CHECK_RESULT(delegate_->OnMemoryInitExpr(cast<MemoryInitExpr>(expr))); + break; + + case ExprType::MemorySize: + CHECK_RESULT(delegate_->OnMemorySizeExpr(cast<MemorySizeExpr>(expr))); + break; + + case ExprType::TableCopy: + CHECK_RESULT(delegate_->OnTableCopyExpr(cast<TableCopyExpr>(expr))); + break; + + case ExprType::ElemDrop: + CHECK_RESULT(delegate_->OnElemDropExpr(cast<ElemDropExpr>(expr))); + break; + + case ExprType::TableInit: + CHECK_RESULT(delegate_->OnTableInitExpr(cast<TableInitExpr>(expr))); + break; + + case ExprType::TableGet: + CHECK_RESULT(delegate_->OnTableGetExpr(cast<TableGetExpr>(expr))); + break; + + case ExprType::TableSet: + CHECK_RESULT(delegate_->OnTableSetExpr(cast<TableSetExpr>(expr))); + break; + + case ExprType::TableGrow: + CHECK_RESULT(delegate_->OnTableGrowExpr(cast<TableGrowExpr>(expr))); + break; + + case ExprType::TableSize: + CHECK_RESULT(delegate_->OnTableSizeExpr(cast<TableSizeExpr>(expr))); + break; + + case ExprType::TableFill: + CHECK_RESULT(delegate_->OnTableFillExpr(cast<TableFillExpr>(expr))); + break; + + case ExprType::RefFunc: + CHECK_RESULT(delegate_->OnRefFuncExpr(cast<RefFuncExpr>(expr))); + break; + + case ExprType::RefNull: + CHECK_RESULT(delegate_->OnRefNullExpr(cast<RefNullExpr>(expr))); + break; + + case ExprType::RefIsNull: + CHECK_RESULT(delegate_->OnRefIsNullExpr(cast<RefIsNullExpr>(expr))); + break; + + case ExprType::Nop: + CHECK_RESULT(delegate_->OnNopExpr(cast<NopExpr>(expr))); + break; + + case ExprType::Rethrow: + CHECK_RESULT(delegate_->OnRethrowExpr(cast<RethrowExpr>(expr))); + break; + + case ExprType::Return: + CHECK_RESULT(delegate_->OnReturnExpr(cast<ReturnExpr>(expr))); + break; + + case ExprType::ReturnCall: + CHECK_RESULT(delegate_->OnReturnCallExpr(cast<ReturnCallExpr>(expr))); + break; + + case ExprType::ReturnCallIndirect: + CHECK_RESULT(delegate_->OnReturnCallIndirectExpr( + cast<ReturnCallIndirectExpr>(expr))); + break; + + case ExprType::Select: + CHECK_RESULT(delegate_->OnSelectExpr(cast<SelectExpr>(expr))); + break; + + case ExprType::Store: + CHECK_RESULT(delegate_->OnStoreExpr(cast<StoreExpr>(expr))); + break; + + case ExprType::Throw: + CHECK_RESULT(delegate_->OnThrowExpr(cast<ThrowExpr>(expr))); + break; + + case ExprType::Try: { + auto try_expr = cast<TryExpr>(expr); + CHECK_RESULT(delegate_->BeginTryExpr(try_expr)); + PushExprlist(State::Try, expr, try_expr->block.exprs); + break; + } + + case ExprType::Unary: + CHECK_RESULT(delegate_->OnUnaryExpr(cast<UnaryExpr>(expr))); + break; + + case ExprType::Ternary: + CHECK_RESULT(delegate_->OnTernaryExpr(cast<TernaryExpr>(expr))); + break; + + case ExprType::SimdLaneOp: { + CHECK_RESULT(delegate_->OnSimdLaneOpExpr(cast<SimdLaneOpExpr>(expr))); + break; + } + + case ExprType::SimdLoadLane: { + CHECK_RESULT(delegate_->OnSimdLoadLaneExpr(cast<SimdLoadLaneExpr>(expr))); + break; + } + + case ExprType::SimdStoreLane: { + CHECK_RESULT( + delegate_->OnSimdStoreLaneExpr(cast<SimdStoreLaneExpr>(expr))); + break; + } + + case ExprType::SimdShuffleOp: { + CHECK_RESULT( + delegate_->OnSimdShuffleOpExpr(cast<SimdShuffleOpExpr>(expr))); + break; + } + + case ExprType::Unreachable: + CHECK_RESULT(delegate_->OnUnreachableExpr(cast<UnreachableExpr>(expr))); + break; + } + + return Result::Ok; +} + +void ExprVisitor::PushDefault(Expr* expr) { + state_stack_.emplace_back(State::Default); + expr_stack_.emplace_back(expr); +} + +void ExprVisitor::PopDefault() { + state_stack_.pop_back(); + expr_stack_.pop_back(); +} + +void ExprVisitor::PushExprlist(State state, Expr* expr, ExprList& expr_list) { + state_stack_.emplace_back(state); + expr_stack_.emplace_back(expr); + expr_iter_stack_.emplace_back(expr_list.begin()); +} + +void ExprVisitor::PopExprlist() { + state_stack_.pop_back(); + expr_stack_.pop_back(); + expr_iter_stack_.pop_back(); +} + +void ExprVisitor::PushCatch(Expr* expr, + Index catch_index, + ExprList& expr_list) { + state_stack_.emplace_back(State::Catch); + expr_stack_.emplace_back(expr); + expr_iter_stack_.emplace_back(expr_list.begin()); + catch_index_stack_.emplace_back(catch_index); +} + +void ExprVisitor::PopCatch() { + state_stack_.pop_back(); + expr_stack_.pop_back(); + expr_iter_stack_.pop_back(); + catch_index_stack_.pop_back(); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/feature.cc b/third_party/wasm2c/src/feature.cc new file mode 100644 index 0000000000..88b474c290 --- /dev/null +++ b/third_party/wasm2c/src/feature.cc @@ -0,0 +1,56 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/feature.h" + +#include "wabt/option-parser.h" + +namespace wabt { + +void Features::AddOptions(OptionParser* parser) { +#define WABT_FEATURE(variable, flag, default_, help) \ + if (default_ == true) { \ + parser->AddOption("disable-" flag, "Disable " help, \ + [this]() { disable_##variable(); }); \ + } else { \ + parser->AddOption("enable-" flag, "Enable " help, \ + [this]() { enable_##variable(); }); \ + } + +#include "wabt/feature.def" +#undef WABT_FEATURE + parser->AddOption("enable-all", "Enable all features", + [this]() { EnableAll(); }); +} + +void Features::UpdateDependencies() { + // Exception handling requires reference types. + if (exceptions_enabled_) { + reference_types_enabled_ = true; + } + + // Function references require reference types. + if (function_references_enabled_) { + reference_types_enabled_ = true; + } + + // Reference types requires bulk memory. + if (!bulk_memory_enabled_) { + reference_types_enabled_ = false; + } +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/filenames.cc b/third_party/wasm2c/src/filenames.cc new file mode 100644 index 0000000000..9f020f46dd --- /dev/null +++ b/third_party/wasm2c/src/filenames.cc @@ -0,0 +1,57 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/filenames.h" + +namespace wabt { + +const char* kWasmExtension = ".wasm"; + +const char* kWatExtension = ".wat"; + +std::string_view StripExtension(std::string_view filename) { + return filename.substr(0, filename.find_last_of('.')); +} + +std::string_view GetBasename(std::string_view filename) { + size_t last_slash = filename.find_last_of('/'); + size_t last_backslash = filename.find_last_of('\\'); + if (last_slash == std::string_view::npos && + last_backslash == std::string_view::npos) { + return filename; + } + + if (last_slash == std::string_view::npos) { + if (last_backslash == std::string_view::npos) { + return filename; + } + last_slash = last_backslash; + } else if (last_backslash != std::string_view::npos) { + last_slash = std::max(last_slash, last_backslash); + } + + return filename.substr(last_slash + 1); +} + +std::string_view GetExtension(std::string_view filename) { + size_t pos = filename.find_last_of('.'); + if (pos == std::string_view::npos) { + return ""; + } + return filename.substr(pos); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/generate-names.cc b/third_party/wasm2c/src/generate-names.cc new file mode 100644 index 0000000000..ec3612d704 --- /dev/null +++ b/third_party/wasm2c/src/generate-names.cc @@ -0,0 +1,433 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/generate-names.h" + +#include <cassert> +#include <cstdio> +#include <string> +#include <vector> + +#include "wabt/cast.h" +#include "wabt/expr-visitor.h" +#include "wabt/ir.h" + +namespace wabt { + +namespace { + +class NameGenerator : public ExprVisitor::DelegateNop { + public: + NameGenerator(NameOpts opts); + + Result VisitModule(Module* module); + + // Implementation of ExprVisitor::DelegateNop. + Result BeginBlockExpr(BlockExpr* expr) override; + Result BeginTryExpr(TryExpr* expr) override; + Result BeginLoopExpr(LoopExpr* expr) override; + Result BeginIfExpr(IfExpr* expr) override; + + private: + static bool HasName(const std::string& str); + + // Generate a name with the given prefix, followed by the index and + // optionally a disambiguating number. If index == kInvalidIndex, the index + // is not appended. + void GenerateName(const char* prefix, + Index index, + unsigned disambiguator, + std::string* out_str); + + // Like GenerateName, but only generates a name if |out_str| is empty. + void MaybeGenerateName(const char* prefix, Index index, std::string* out_str); + + // Generate a name via GenerateName and bind it to the given binding hash. If + // the name already exists, the name will be disambiguated until it can be + // added. + void GenerateAndBindName(BindingHash* bindings, + const char* prefix, + Index index, + std::string* out_str); + + // Like GenerateAndBindName, but only generates a name if |out_str| is empty. + void MaybeGenerateAndBindName(BindingHash* bindings, + const char* prefix, + Index index, + std::string* out_str); + + // Like MaybeGenerateAndBindName but uses the name directly, without + // appending the index. If the name already exists, a disambiguating suffix + // is added. + void MaybeUseAndBindName(BindingHash* bindings, + const char* name, + Index index, + std::string* out_str); + + void GenerateAndBindLocalNames(Func* func); + + template <typename T> + Result VisitAll(const std::vector<T*>& items, + Result (NameGenerator::*func)(Index, T*)); + + Result VisitFunc(Index func_index, Func* func); + Result VisitGlobal(Index global_index, Global* global); + Result VisitType(Index func_type_index, TypeEntry* type); + Result VisitTable(Index table_index, Table* table); + Result VisitMemory(Index memory_index, Memory* memory); + Result VisitTag(Index tag_index, Tag* tag); + Result VisitDataSegment(Index data_segment_index, DataSegment* data_segment); + Result VisitElemSegment(Index elem_segment_index, ElemSegment* elem_segment); + Result VisitImport(Import* import); + Result VisitExport(Export* export_); + + Module* module_ = nullptr; + ExprVisitor visitor_; + Index label_count_ = 0; + + Index num_func_imports_ = 0; + Index num_table_imports_ = 0; + Index num_memory_imports_ = 0; + Index num_global_imports_ = 0; + Index num_tag_imports_ = 0; + + NameOpts opts_; +}; + +NameGenerator::NameGenerator(NameOpts opts) : visitor_(this), opts_(opts) {} + +// static +bool NameGenerator::HasName(const std::string& str) { + return !str.empty(); +} + +void NameGenerator::GenerateName(const char* prefix, + Index index, + unsigned disambiguator, + std::string* str) { + *str = "$"; + *str += prefix; + if (index != kInvalidIndex) { + if (opts_ & NameOpts::AlphaNames) { + // For params and locals, do not use a prefix char. + if (!strcmp(prefix, "p") || !strcmp(prefix, "l")) { + str->pop_back(); + } else { + *str += '_'; + } + *str += IndexToAlphaName(index); + } else { + *str += std::to_string(index); + } + } + if (disambiguator != 0) { + *str += '_' + std::to_string(disambiguator); + } +} + +void NameGenerator::MaybeGenerateName(const char* prefix, + Index index, + std::string* str) { + if (!HasName(*str)) { + // There's no bindings hash, so the name can't be a duplicate. Therefore it + // doesn't need a disambiguating number. + GenerateName(prefix, index, 0, str); + } +} + +void NameGenerator::GenerateAndBindName(BindingHash* bindings, + const char* prefix, + Index index, + std::string* str) { + unsigned disambiguator = 0; + while (true) { + GenerateName(prefix, index, disambiguator, str); + if (bindings->find(*str) == bindings->end()) { + bindings->emplace(*str, Binding(index)); + break; + } + + disambiguator++; + } +} + +void NameGenerator::MaybeGenerateAndBindName(BindingHash* bindings, + const char* prefix, + Index index, + std::string* str) { + if (!HasName(*str)) { + GenerateAndBindName(bindings, prefix, index, str); + } +} + +void NameGenerator::MaybeUseAndBindName(BindingHash* bindings, + const char* name, + Index index, + std::string* str) { + if (!HasName(*str)) { + unsigned disambiguator = 0; + while (true) { + GenerateName(name, kInvalidIndex, disambiguator, str); + if (bindings->find(*str) == bindings->end()) { + bindings->emplace(*str, Binding(index)); + break; + } + + disambiguator++; + } + } +} + +void NameGenerator::GenerateAndBindLocalNames(Func* func) { + std::vector<std::string> index_to_name; + MakeTypeBindingReverseMapping(func->GetNumParamsAndLocals(), func->bindings, + &index_to_name); + for (size_t i = 0; i < index_to_name.size(); ++i) { + const std::string& old_name = index_to_name[i]; + if (!old_name.empty()) { + continue; + } + + const char* prefix = i < func->GetNumParams() ? "p" : "l"; + std::string new_name; + GenerateAndBindName(&func->bindings, prefix, i, &new_name); + index_to_name[i] = new_name; + } +} + +Result NameGenerator::BeginBlockExpr(BlockExpr* expr) { + MaybeGenerateName("B", label_count_++, &expr->block.label); + return Result::Ok; +} + +Result NameGenerator::BeginTryExpr(TryExpr* expr) { + MaybeGenerateName("T", label_count_++, &expr->block.label); + return Result::Ok; +} + +Result NameGenerator::BeginLoopExpr(LoopExpr* expr) { + MaybeGenerateName("L", label_count_++, &expr->block.label); + return Result::Ok; +} + +Result NameGenerator::BeginIfExpr(IfExpr* expr) { + MaybeGenerateName("I", label_count_++, &expr->true_.label); + return Result::Ok; +} + +Result NameGenerator::VisitFunc(Index func_index, Func* func) { + MaybeGenerateAndBindName(&module_->func_bindings, "f", func_index, + &func->name); + GenerateAndBindLocalNames(func); + + label_count_ = 0; + CHECK_RESULT(visitor_.VisitFunc(func)); + return Result::Ok; +} + +Result NameGenerator::VisitGlobal(Index global_index, Global* global) { + MaybeGenerateAndBindName(&module_->global_bindings, "g", global_index, + &global->name); + return Result::Ok; +} + +Result NameGenerator::VisitType(Index type_index, TypeEntry* type) { + MaybeGenerateAndBindName(&module_->type_bindings, "t", type_index, + &type->name); + return Result::Ok; +} + +Result NameGenerator::VisitTable(Index table_index, Table* table) { + MaybeGenerateAndBindName(&module_->table_bindings, "T", table_index, + &table->name); + return Result::Ok; +} + +Result NameGenerator::VisitMemory(Index memory_index, Memory* memory) { + MaybeGenerateAndBindName(&module_->memory_bindings, "M", memory_index, + &memory->name); + return Result::Ok; +} + +Result NameGenerator::VisitTag(Index tag_index, Tag* tag) { + MaybeGenerateAndBindName(&module_->tag_bindings, "e", tag_index, &tag->name); + return Result::Ok; +} + +Result NameGenerator::VisitDataSegment(Index data_segment_index, + DataSegment* data_segment) { + MaybeGenerateAndBindName(&module_->data_segment_bindings, "d", + data_segment_index, &data_segment->name); + return Result::Ok; +} + +Result NameGenerator::VisitElemSegment(Index elem_segment_index, + ElemSegment* elem_segment) { + MaybeGenerateAndBindName(&module_->elem_segment_bindings, "e", + elem_segment_index, &elem_segment->name); + return Result::Ok; +} + +Result NameGenerator::VisitImport(Import* import) { + BindingHash* bindings = nullptr; + std::string* name = nullptr; + Index index = kInvalidIndex; + + switch (import->kind()) { + case ExternalKind::Func: + if (auto* func_import = cast<FuncImport>(import)) { + bindings = &module_->func_bindings; + name = &func_import->func.name; + index = num_func_imports_++; + } + break; + + case ExternalKind::Table: + if (auto* table_import = cast<TableImport>(import)) { + bindings = &module_->table_bindings; + name = &table_import->table.name; + index = num_table_imports_++; + } + break; + + case ExternalKind::Memory: + if (auto* memory_import = cast<MemoryImport>(import)) { + bindings = &module_->memory_bindings; + name = &memory_import->memory.name; + index = num_memory_imports_++; + } + break; + + case ExternalKind::Global: + if (auto* global_import = cast<GlobalImport>(import)) { + bindings = &module_->global_bindings; + name = &global_import->global.name; + index = num_global_imports_++; + } + break; + + case ExternalKind::Tag: + if (auto* tag_import = cast<TagImport>(import)) { + bindings = &module_->tag_bindings; + name = &tag_import->tag.name; + index = num_tag_imports_++; + } + break; + } + + if (bindings && name) { + assert(index != kInvalidIndex); + std::string new_name = import->module_name + '.' + import->field_name; + MaybeUseAndBindName(bindings, new_name.c_str(), index, name); + } + + return Result::Ok; +} + +Result NameGenerator::VisitExport(Export* export_) { + BindingHash* bindings = nullptr; + std::string* name = nullptr; + Index index = kInvalidIndex; + + switch (export_->kind) { + case ExternalKind::Func: + if (Func* func = module_->GetFunc(export_->var)) { + index = module_->GetFuncIndex(export_->var); + bindings = &module_->func_bindings; + name = &func->name; + } + break; + + case ExternalKind::Table: + if (Table* table = module_->GetTable(export_->var)) { + index = module_->GetTableIndex(export_->var); + bindings = &module_->table_bindings; + name = &table->name; + } + break; + + case ExternalKind::Memory: + if (Memory* memory = module_->GetMemory(export_->var)) { + index = module_->GetMemoryIndex(export_->var); + bindings = &module_->memory_bindings; + name = &memory->name; + } + break; + + case ExternalKind::Global: + if (Global* global = module_->GetGlobal(export_->var)) { + index = module_->GetGlobalIndex(export_->var); + bindings = &module_->global_bindings; + name = &global->name; + } + break; + + case ExternalKind::Tag: + if (Tag* tag = module_->GetTag(export_->var)) { + index = module_->GetTagIndex(export_->var); + bindings = &module_->tag_bindings; + name = &tag->name; + } + break; + } + + if (bindings && name) { + MaybeUseAndBindName(bindings, export_->name.c_str(), index, name); + } + + return Result::Ok; +} + +template <typename T> +Result NameGenerator::VisitAll(const std::vector<T*>& items, + Result (NameGenerator::*func)(Index, T*)) { + for (Index i = 0; i < items.size(); ++i) { + CHECK_RESULT((this->*func)(i, items[i])); + } + return Result::Ok; +} + +Result NameGenerator::VisitModule(Module* module) { + module_ = module; + // Visit imports and exports first to give better names, derived from the + // import/export name. + for (auto* import : module->imports) { + CHECK_RESULT(VisitImport(import)); + } + for (auto* export_ : module->exports) { + CHECK_RESULT(VisitExport(export_)); + } + + VisitAll(module->globals, &NameGenerator::VisitGlobal); + VisitAll(module->types, &NameGenerator::VisitType); + VisitAll(module->funcs, &NameGenerator::VisitFunc); + VisitAll(module->tables, &NameGenerator::VisitTable); + VisitAll(module->memories, &NameGenerator::VisitMemory); + VisitAll(module->tags, &NameGenerator::VisitTag); + VisitAll(module->data_segments, &NameGenerator::VisitDataSegment); + VisitAll(module->elem_segments, &NameGenerator::VisitElemSegment); + module_ = nullptr; + return Result::Ok; +} + +} // end anonymous namespace + +Result GenerateNames(Module* module, NameOpts opts) { + NameGenerator generator(opts); + return generator.VisitModule(module); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/ir-util.cc b/third_party/wasm2c/src/ir-util.cc new file mode 100644 index 0000000000..ee9262cc0c --- /dev/null +++ b/third_party/wasm2c/src/ir-util.cc @@ -0,0 +1,271 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/ir-util.h" + +#include <algorithm> +#include <array> +#include <cassert> +#include <cinttypes> +#include <cstdarg> +#include <cstdio> +#include <iterator> +#include <map> +#include <string> +#include <vector> + +#include "wabt/cast.h" +#include "wabt/common.h" +#include "wabt/expr-visitor.h" +#include "wabt/ir-util.h" +#include "wabt/ir.h" +#include "wabt/literal.h" +#include "wabt/stream.h" + +#define WABT_TRACING 0 +#include "wabt/tracing.h" + +using namespace wabt; + +const Label* ModuleContext::GetLabel(const Var& var) const { + if (var.is_name()) { + for (Index i = GetLabelStackSize(); i > 0; --i) { + auto label = &label_stack_[i - 1]; + if (label->name == var.name()) { + return label; + } + } + } else if (var.index() < GetLabelStackSize()) { + auto label = &label_stack_[GetLabelStackSize() - var.index() - 1]; + return label; + } + return nullptr; +} + +Index ModuleContext::GetLabelArity(const Var& var) const { + auto label = GetLabel(var); + if (!label) { + return 0; + } + + return label->label_type == LabelType::Loop ? label->param_types.size() + : label->result_types.size(); +} + +Index ModuleContext::GetFuncParamCount(const Var& var) const { + const Func* func = module.GetFunc(var); + return func ? func->GetNumParams() : 0; +} + +Index ModuleContext::GetFuncResultCount(const Var& var) const { + const Func* func = module.GetFunc(var); + return func ? func->GetNumResults() : 0; +} + +void ModuleContext::BeginBlock(LabelType label_type, const Block& block) { + label_stack_.emplace_back(label_type, block.label, block.decl.sig.param_types, + block.decl.sig.result_types); +} + +void ModuleContext::EndBlock() { + label_stack_.pop_back(); +} + +void ModuleContext::BeginFunc(const Func& func) { + label_stack_.clear(); + label_stack_.emplace_back(LabelType::Func, std::string(), TypeVector(), + func.decl.sig.result_types); + current_func_ = &func; +} + +void ModuleContext::EndFunc() { + current_func_ = nullptr; +} + +ModuleContext::Arities ModuleContext::GetExprArity(const Expr& expr) const { + switch (expr.type()) { + case ExprType::AtomicNotify: + case ExprType::AtomicRmw: + case ExprType::Binary: + case ExprType::Compare: + case ExprType::TableGrow: + return {2, 1}; + + case ExprType::AtomicStore: + case ExprType::Store: + case ExprType::TableSet: + return {2, 0}; + + case ExprType::Block: + return {0, cast<BlockExpr>(&expr)->block.decl.sig.GetNumResults()}; + + case ExprType::Br: + return {GetLabelArity(cast<BrExpr>(&expr)->var), 1, true}; + + case ExprType::BrIf: { + Index arity = GetLabelArity(cast<BrIfExpr>(&expr)->var); + return {arity + 1, arity}; + } + + case ExprType::BrTable: + return {GetLabelArity(cast<BrTableExpr>(&expr)->default_target) + 1, 1, + true}; + + case ExprType::Call: { + const Var& var = cast<CallExpr>(&expr)->var; + return {GetFuncParamCount(var), GetFuncResultCount(var)}; + } + + case ExprType::ReturnCall: { + const Var& var = cast<ReturnCallExpr>(&expr)->var; + return {GetFuncParamCount(var), GetFuncResultCount(var), true}; + } + + case ExprType::CallIndirect: { + const auto* ci_expr = cast<CallIndirectExpr>(&expr); + return {ci_expr->decl.GetNumParams() + 1, ci_expr->decl.GetNumResults()}; + } + + case ExprType::CallRef: { + const Var& var = cast<CallRefExpr>(&expr)->function_type_index; + return {GetFuncParamCount(var) + 1, GetFuncResultCount(var)}; + } + + case ExprType::ReturnCallIndirect: { + const auto* rci_expr = cast<ReturnCallIndirectExpr>(&expr); + return {rci_expr->decl.GetNumParams() + 1, rci_expr->decl.GetNumResults(), + true}; + } + + case ExprType::Const: + case ExprType::GlobalGet: + case ExprType::LocalGet: + case ExprType::MemorySize: + case ExprType::TableSize: + case ExprType::RefNull: + case ExprType::RefFunc: + return {0, 1}; + + case ExprType::Unreachable: + return {0, 1, true}; + + case ExprType::DataDrop: + case ExprType::ElemDrop: + case ExprType::AtomicFence: + case ExprType::CodeMetadata: + return {0, 0}; + + case ExprType::MemoryInit: + case ExprType::TableInit: + case ExprType::MemoryFill: + case ExprType::MemoryCopy: + case ExprType::TableCopy: + case ExprType::TableFill: + return {3, 0}; + + case ExprType::AtomicLoad: + case ExprType::Convert: + case ExprType::Load: + case ExprType::LocalTee: + case ExprType::MemoryGrow: + case ExprType::Unary: + case ExprType::TableGet: + case ExprType::RefIsNull: + case ExprType::LoadSplat: + case ExprType::LoadZero: + return {1, 1}; + + case ExprType::Drop: + case ExprType::GlobalSet: + case ExprType::LocalSet: + return {1, 0}; + + case ExprType::If: + return {1, cast<IfExpr>(&expr)->true_.decl.sig.GetNumResults()}; + + case ExprType::Loop: + return {0, cast<LoopExpr>(&expr)->block.decl.sig.GetNumResults()}; + + case ExprType::Nop: + return {0, 0}; + + case ExprType::Return: + return {static_cast<Index>(current_func_->decl.sig.result_types.size()), + 1, true}; + + case ExprType::Rethrow: + return {0, 0, true}; + + case ExprType::AtomicRmwCmpxchg: + case ExprType::AtomicWait: + case ExprType::Select: + return {3, 1}; + + case ExprType::Throw: { + auto throw_ = cast<ThrowExpr>(&expr); + Index operand_count = 0; + if (Tag* tag = module.GetTag(throw_->var)) { + operand_count = tag->decl.sig.param_types.size(); + } + return {operand_count, 0, true}; + } + + case ExprType::Try: + return {0, cast<TryExpr>(&expr)->block.decl.sig.GetNumResults()}; + + case ExprType::Ternary: + return {3, 1}; + + case ExprType::SimdLaneOp: { + const Opcode opcode = cast<SimdLaneOpExpr>(&expr)->opcode; + switch (opcode) { + case Opcode::I8X16ExtractLaneS: + case Opcode::I8X16ExtractLaneU: + case Opcode::I16X8ExtractLaneS: + case Opcode::I16X8ExtractLaneU: + case Opcode::I32X4ExtractLane: + case Opcode::I64X2ExtractLane: + case Opcode::F32X4ExtractLane: + case Opcode::F64X2ExtractLane: + return {1, 1}; + + case Opcode::I8X16ReplaceLane: + case Opcode::I16X8ReplaceLane: + case Opcode::I32X4ReplaceLane: + case Opcode::I64X2ReplaceLane: + case Opcode::F32X4ReplaceLane: + case Opcode::F64X2ReplaceLane: + return {2, 1}; + + default: + fprintf(stderr, "Invalid Opcode for expr type: %s\n", + GetExprTypeName(expr)); + assert(0); + return {0, 0}; + } + } + + case ExprType::SimdLoadLane: + case ExprType::SimdStoreLane: { + return {2, 1}; + } + + case ExprType::SimdShuffleOp: + return {2, 1}; + } + + WABT_UNREACHABLE; +} diff --git a/third_party/wasm2c/src/ir.cc b/third_party/wasm2c/src/ir.cc new file mode 100644 index 0000000000..88db7d5f46 --- /dev/null +++ b/third_party/wasm2c/src/ir.cc @@ -0,0 +1,701 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/ir.h" + +#include <cassert> +#include <cstddef> +#include <numeric> + +#include "wabt/cast.h" + +namespace { + +const char* ExprTypeName[] = { + "AtomicFence", + "AtomicLoad", + "AtomicRmw", + "AtomicRmwCmpxchg", + "AtomicStore", + "AtomicNotify", + "AtomicWait", + "Binary", + "Block", + "Br", + "BrIf", + "BrTable", + "Call", + "CallIndirect", + "CallRef", + "CodeMetadata", + "Compare", + "Const", + "Convert", + "Drop", + "GlobalGet", + "GlobalSet", + "If", + "Load", + "LocalGet", + "LocalSet", + "LocalTee", + "Loop", + "MemoryCopy", + "DataDrop", + "MemoryFill", + "MemoryGrow", + "MemoryInit", + "MemorySize", + "Nop", + "RefIsNull", + "RefFunc", + "RefNull", + "Rethrow", + "Return", + "ReturnCall", + "ReturnCallIndirect", + "Select", + "SimdLaneOp", + "SimdLoadLane", + "SimdStoreLane", + "SimdShuffleOp", + "LoadSplat", + "LoadZero", + "Store", + "TableCopy", + "ElemDrop", + "TableInit", + "TableGet", + "TableGrow", + "TableSize", + "TableSet", + "TableFill", + "Ternary", + "Throw", + "Try", + "Unary", + "Unreachable", +}; + +} // end of anonymous namespace + +namespace wabt { + +const char* GetExprTypeName(ExprType type) { + static_assert(WABT_ENUM_COUNT(ExprType) == WABT_ARRAY_SIZE(ExprTypeName), + "Malformed ExprTypeName array"); + return ExprTypeName[size_t(type)]; +} + +const char* GetExprTypeName(const Expr& expr) { + return GetExprTypeName(expr.type()); +} + +bool FuncSignature::operator==(const FuncSignature& rhs) const { + return param_types == rhs.param_types && result_types == rhs.result_types; +} + +const Export* Module::GetExport(std::string_view name) const { + Index index = export_bindings.FindIndex(name); + if (index >= exports.size()) { + return nullptr; + } + return exports[index]; +} + +Index Module::GetFuncIndex(const Var& var) const { + return func_bindings.FindIndex(var); +} + +Index Module::GetGlobalIndex(const Var& var) const { + return global_bindings.FindIndex(var); +} + +Index Module::GetTableIndex(const Var& var) const { + return table_bindings.FindIndex(var); +} + +Index Module::GetMemoryIndex(const Var& var) const { + return memory_bindings.FindIndex(var); +} + +Index Module::GetFuncTypeIndex(const Var& var) const { + return type_bindings.FindIndex(var); +} + +Index Module::GetTagIndex(const Var& var) const { + return tag_bindings.FindIndex(var); +} + +Index Module::GetDataSegmentIndex(const Var& var) const { + return data_segment_bindings.FindIndex(var); +} + +Index Module::GetElemSegmentIndex(const Var& var) const { + return elem_segment_bindings.FindIndex(var); +} + +bool Module::IsImport(ExternalKind kind, const Var& var) const { + switch (kind) { + case ExternalKind::Func: + return GetFuncIndex(var) < num_func_imports; + + case ExternalKind::Global: + return GetGlobalIndex(var) < num_global_imports; + + case ExternalKind::Memory: + return GetMemoryIndex(var) < num_memory_imports; + + case ExternalKind::Table: + return GetTableIndex(var) < num_table_imports; + + case ExternalKind::Tag: + return GetTagIndex(var) < num_tag_imports; + + default: + return false; + } +} + +void LocalTypes::Set(const TypeVector& types) { + decls_.clear(); + if (types.empty()) { + return; + } + + Type type = types[0]; + Index count = 1; + for (Index i = 1; i < types.size(); ++i) { + if (types[i] != type) { + decls_.emplace_back(type, count); + type = types[i]; + count = 1; + } else { + ++count; + } + } + decls_.emplace_back(type, count); +} + +Index LocalTypes::size() const { + return std::accumulate( + decls_.begin(), decls_.end(), 0, + [](Index sum, const Decl& decl) { return sum + decl.second; }); +} + +Type LocalTypes::operator[](Index i) const { + Index count = 0; + for (auto decl : decls_) { + if (i < count + decl.second) { + return decl.first; + } + count += decl.second; + } + assert(i < count); + return Type::Any; +} + +Type Func::GetLocalType(Index index) const { + Index num_params = decl.GetNumParams(); + if (index < num_params) { + return GetParamType(index); + } else { + index -= num_params; + assert(index < local_types.size()); + return local_types[index]; + } +} + +Type Func::GetLocalType(const Var& var) const { + return GetLocalType(GetLocalIndex(var)); +} + +Index Func::GetLocalIndex(const Var& var) const { + if (var.is_index()) { + return var.index(); + } + return bindings.FindIndex(var); +} + +const Func* Module::GetFunc(const Var& var) const { + return const_cast<Module*>(this)->GetFunc(var); +} + +Func* Module::GetFunc(const Var& var) { + Index index = func_bindings.FindIndex(var); + if (index >= funcs.size()) { + return nullptr; + } + return funcs[index]; +} + +const Global* Module::GetGlobal(const Var& var) const { + return const_cast<Module*>(this)->GetGlobal(var); +} + +Global* Module::GetGlobal(const Var& var) { + Index index = global_bindings.FindIndex(var); + if (index >= globals.size()) { + return nullptr; + } + return globals[index]; +} + +const Table* Module::GetTable(const Var& var) const { + return const_cast<Module*>(this)->GetTable(var); +} + +Table* Module::GetTable(const Var& var) { + Index index = table_bindings.FindIndex(var); + if (index >= tables.size()) { + return nullptr; + } + return tables[index]; +} + +const Memory* Module::GetMemory(const Var& var) const { + return const_cast<Module*>(this)->GetMemory(var); +} + +Memory* Module::GetMemory(const Var& var) { + Index index = memory_bindings.FindIndex(var); + if (index >= memories.size()) { + return nullptr; + } + return memories[index]; +} + +Tag* Module::GetTag(const Var& var) const { + Index index = GetTagIndex(var); + if (index >= tags.size()) { + return nullptr; + } + return tags[index]; +} + +const DataSegment* Module::GetDataSegment(const Var& var) const { + return const_cast<Module*>(this)->GetDataSegment(var); +} + +DataSegment* Module::GetDataSegment(const Var& var) { + Index index = data_segment_bindings.FindIndex(var); + if (index >= data_segments.size()) { + return nullptr; + } + return data_segments[index]; +} + +const ElemSegment* Module::GetElemSegment(const Var& var) const { + return const_cast<Module*>(this)->GetElemSegment(var); +} + +ElemSegment* Module::GetElemSegment(const Var& var) { + Index index = elem_segment_bindings.FindIndex(var); + if (index >= elem_segments.size()) { + return nullptr; + } + return elem_segments[index]; +} + +const FuncType* Module::GetFuncType(const Var& var) const { + return const_cast<Module*>(this)->GetFuncType(var); +} + +FuncType* Module::GetFuncType(const Var& var) { + Index index = type_bindings.FindIndex(var); + if (index >= types.size()) { + return nullptr; + } + return dyn_cast<FuncType>(types[index]); +} + +Index Module::GetFuncTypeIndex(const FuncSignature& sig) const { + for (size_t i = 0; i < types.size(); ++i) { + if (auto* func_type = dyn_cast<FuncType>(types[i])) { + if (func_type->sig == sig) { + return i; + } + } + } + return kInvalidIndex; +} + +Index Module::GetFuncTypeIndex(const FuncDeclaration& decl) const { + if (decl.has_func_type) { + return GetFuncTypeIndex(decl.type_var); + } else { + return GetFuncTypeIndex(decl.sig); + } +} + +void Module::AppendField(std::unique_ptr<DataSegmentModuleField> field) { + DataSegment& data_segment = field->data_segment; + if (!data_segment.name.empty()) { + data_segment_bindings.emplace(data_segment.name, + Binding(field->loc, data_segments.size())); + } + data_segments.push_back(&data_segment); + fields.push_back(std::move(field)); +} + +void Module::AppendField(std::unique_ptr<ElemSegmentModuleField> field) { + ElemSegment& elem_segment = field->elem_segment; + if (!elem_segment.name.empty()) { + elem_segment_bindings.emplace(elem_segment.name, + Binding(field->loc, elem_segments.size())); + } + elem_segments.push_back(&elem_segment); + fields.push_back(std::move(field)); +} + +void Module::AppendField(std::unique_ptr<TagModuleField> field) { + Tag& tag = field->tag; + if (!tag.name.empty()) { + tag_bindings.emplace(tag.name, Binding(field->loc, tags.size())); + } + tags.push_back(&tag); + fields.push_back(std::move(field)); +} + +void Module::AppendField(std::unique_ptr<ExportModuleField> field) { + // Exported names are allowed to be empty. + Export& export_ = field->export_; + export_bindings.emplace(export_.name, Binding(field->loc, exports.size())); + exports.push_back(&export_); + fields.push_back(std::move(field)); +} + +void Module::AppendField(std::unique_ptr<FuncModuleField> field) { + Func& func = field->func; + if (!func.name.empty()) { + func_bindings.emplace(func.name, Binding(field->loc, funcs.size())); + } + funcs.push_back(&func); + fields.push_back(std::move(field)); +} + +void Module::AppendField(std::unique_ptr<TypeModuleField> field) { + TypeEntry& type = *field->type; + if (!type.name.empty()) { + type_bindings.emplace(type.name, Binding(field->loc, types.size())); + } + types.push_back(&type); + fields.push_back(std::move(field)); +} + +void Module::AppendField(std::unique_ptr<GlobalModuleField> field) { + Global& global = field->global; + if (!global.name.empty()) { + global_bindings.emplace(global.name, Binding(field->loc, globals.size())); + } + globals.push_back(&global); + fields.push_back(std::move(field)); +} + +void Module::AppendField(std::unique_ptr<ImportModuleField> field) { + Import* import = field->import.get(); + const std::string* name = nullptr; + BindingHash* bindings = nullptr; + Index index = kInvalidIndex; + + switch (import->kind()) { + case ExternalKind::Func: { + Func& func = cast<FuncImport>(import)->func; + name = &func.name; + bindings = &func_bindings; + index = funcs.size(); + funcs.push_back(&func); + ++num_func_imports; + break; + } + + case ExternalKind::Table: { + Table& table = cast<TableImport>(import)->table; + name = &table.name; + bindings = &table_bindings; + index = tables.size(); + tables.push_back(&table); + ++num_table_imports; + break; + } + + case ExternalKind::Memory: { + Memory& memory = cast<MemoryImport>(import)->memory; + name = &memory.name; + bindings = &memory_bindings; + index = memories.size(); + memories.push_back(&memory); + ++num_memory_imports; + break; + } + + case ExternalKind::Global: { + Global& global = cast<GlobalImport>(import)->global; + name = &global.name; + bindings = &global_bindings; + index = globals.size(); + globals.push_back(&global); + ++num_global_imports; + break; + } + + case ExternalKind::Tag: { + Tag& tag = cast<TagImport>(import)->tag; + name = &tag.name; + bindings = &tag_bindings; + index = tags.size(); + tags.push_back(&tag); + ++num_tag_imports; + break; + } + } + + assert(name && bindings && index != kInvalidIndex); + if (!name->empty()) { + bindings->emplace(*name, Binding(field->loc, index)); + } + imports.push_back(import); + fields.push_back(std::move(field)); +} + +void Module::AppendField(std::unique_ptr<MemoryModuleField> field) { + Memory& memory = field->memory; + if (!memory.name.empty()) { + memory_bindings.emplace(memory.name, Binding(field->loc, memories.size())); + } + memories.push_back(&memory); + fields.push_back(std::move(field)); +} + +void Module::AppendField(std::unique_ptr<StartModuleField> field) { + starts.push_back(&field->start); + fields.push_back(std::move(field)); +} + +void Module::AppendField(std::unique_ptr<TableModuleField> field) { + Table& table = field->table; + if (!table.name.empty()) { + table_bindings.emplace(table.name, Binding(field->loc, tables.size())); + } + tables.push_back(&table); + fields.push_back(std::move(field)); +} + +void Module::AppendField(std::unique_ptr<ModuleField> field) { + switch (field->type()) { + case ModuleFieldType::Func: + AppendField(cast<FuncModuleField>(std::move(field))); + break; + + case ModuleFieldType::Global: + AppendField(cast<GlobalModuleField>(std::move(field))); + break; + + case ModuleFieldType::Import: + AppendField(cast<ImportModuleField>(std::move(field))); + break; + + case ModuleFieldType::Export: + AppendField(cast<ExportModuleField>(std::move(field))); + break; + + case ModuleFieldType::Type: + AppendField(cast<TypeModuleField>(std::move(field))); + break; + + case ModuleFieldType::Table: + AppendField(cast<TableModuleField>(std::move(field))); + break; + + case ModuleFieldType::ElemSegment: + AppendField(cast<ElemSegmentModuleField>(std::move(field))); + break; + + case ModuleFieldType::Memory: + AppendField(cast<MemoryModuleField>(std::move(field))); + break; + + case ModuleFieldType::DataSegment: + AppendField(cast<DataSegmentModuleField>(std::move(field))); + break; + + case ModuleFieldType::Start: + AppendField(cast<StartModuleField>(std::move(field))); + break; + + case ModuleFieldType::Tag: + AppendField(cast<TagModuleField>(std::move(field))); + break; + } +} + +void Module::AppendFields(ModuleFieldList* fields) { + while (!fields->empty()) + AppendField(std::unique_ptr<ModuleField>(fields->extract_front())); +} + +const Module* Script::GetFirstModule() const { + return const_cast<Script*>(this)->GetFirstModule(); +} + +Module* Script::GetFirstModule() { + for (const std::unique_ptr<Command>& command : commands) { + if (auto* module_command = dyn_cast<ModuleCommand>(command.get())) { + return &module_command->module; + } + } + return nullptr; +} + +const Module* Script::GetModule(const Var& var) const { + Index index = module_bindings.FindIndex(var); + if (index >= commands.size()) { + return nullptr; + } + auto* command = commands[index].get(); + if (isa<ModuleCommand>(command)) { + return &cast<ModuleCommand>(command)->module; + } else if (isa<ScriptModuleCommand>(command)) { + return &cast<ScriptModuleCommand>(command)->module; + } + return nullptr; +} + +void MakeTypeBindingReverseMapping( + size_t num_types, + const BindingHash& bindings, + std::vector<std::string>* out_reverse_mapping) { + out_reverse_mapping->clear(); + out_reverse_mapping->resize(num_types); + for (const auto& [name, binding] : bindings) { + assert(static_cast<size_t>(binding.index) < out_reverse_mapping->size()); + (*out_reverse_mapping)[binding.index] = name; + } +} + +Var::Var() : Var(kInvalidIndex, Location()) {} + +Var::Var(Index index, const Location& loc) + : loc(loc), type_(VarType::Index), index_(index) {} + +Var::Var(std::string_view name, const Location& loc) + : loc(loc), type_(VarType::Name), name_(name) {} + +Var::Var(Var&& rhs) : Var() { + *this = std::move(rhs); +} + +Var::Var(const Var& rhs) : Var() { + *this = rhs; +} + +Var& Var::operator=(Var&& rhs) { + loc = rhs.loc; + if (rhs.is_index()) { + set_index(rhs.index_); + } else { + set_name(rhs.name_); + } + return *this; +} + +Var& Var::operator=(const Var& rhs) { + loc = rhs.loc; + if (rhs.is_index()) { + set_index(rhs.index_); + } else { + set_name(rhs.name_); + } + return *this; +} + +Var::~Var() { + Destroy(); +} + +void Var::set_index(Index index) { + Destroy(); + type_ = VarType::Index; + index_ = index; +} + +void Var::set_name(std::string&& name) { + Destroy(); + type_ = VarType::Name; + Construct(name_, std::move(name)); +} + +void Var::set_name(std::string_view name) { + set_name(std::string(name)); +} + +void Var::Destroy() { + if (is_name()) { + Destruct(name_); + } +} + +uint8_t ElemSegment::GetFlags(const Module* module) const { + uint8_t flags = 0; + + switch (kind) { + case SegmentKind::Active: { + Index table_index = module->GetTableIndex(table_var); + if (elem_type != Type::FuncRef || table_index != 0) { + flags |= SegExplicitIndex; + } + break; + } + + case SegmentKind::Passive: + flags |= SegPassive; + break; + + case SegmentKind::Declared: + flags |= SegDeclared; + break; + } + + bool all_ref_func = + elem_type == Type::FuncRef && + std::all_of(elem_exprs.begin(), elem_exprs.end(), + [](const ExprList& elem_expr) { + return elem_expr.front().type() == ExprType::RefFunc; + }); + + if (!all_ref_func) { + flags |= SegUseElemExprs; + } + + return flags; +} + +uint8_t DataSegment::GetFlags(const Module* module) const { + uint8_t flags = 0; + + if (kind == SegmentKind::Passive) { + flags |= SegPassive; + } + + Index memory_index = module->GetMemoryIndex(memory_var); + if (memory_index != 0) { + flags |= SegExplicitIndex; + } + + return flags; +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/leb128.cc b/third_party/wasm2c/src/leb128.cc new file mode 100644 index 0000000000..29b20f61bc --- /dev/null +++ b/third_party/wasm2c/src/leb128.cc @@ -0,0 +1,361 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/leb128.h" + +#include <type_traits> + +#include "wabt/stream.h" + +#define MAX_U32_LEB128_BYTES 5 +#define MAX_U64_LEB128_BYTES 10 + +namespace wabt { + +Offset U32Leb128Length(uint32_t value) { + uint32_t size = 0; + do { + value >>= 7; + size++; + } while (value != 0); + return size; +} + +#define LEB128_LOOP_UNTIL(end_cond) \ + do { \ + uint8_t byte = value & 0x7f; \ + value >>= 7; \ + if (end_cond) { \ + data[length++] = byte; \ + break; \ + } else { \ + data[length++] = byte | 0x80; \ + } \ + } while (1) + +Offset WriteFixedU32Leb128At(Stream* stream, + Offset offset, + uint32_t value, + const char* desc) { + uint8_t data[MAX_U32_LEB128_BYTES]; + Offset length = + WriteFixedU32Leb128Raw(data, data + MAX_U32_LEB128_BYTES, value); + stream->WriteDataAt(offset, data, length, desc); + return length; +} + +void WriteU32Leb128(Stream* stream, uint32_t value, const char* desc) { + uint8_t data[MAX_U32_LEB128_BYTES]; + Offset length = 0; + LEB128_LOOP_UNTIL(value == 0); + stream->WriteData(data, length, desc); +} + +void WriteFixedU32Leb128(Stream* stream, uint32_t value, const char* desc) { + uint8_t data[MAX_U32_LEB128_BYTES]; + Offset length = + WriteFixedU32Leb128Raw(data, data + MAX_U32_LEB128_BYTES, value); + stream->WriteData(data, length, desc); +} + +// returns the length of the leb128. +Offset WriteU32Leb128At(Stream* stream, + Offset offset, + uint32_t value, + const char* desc) { + uint8_t data[MAX_U32_LEB128_BYTES]; + Offset length = 0; + LEB128_LOOP_UNTIL(value == 0); + stream->WriteDataAt(offset, data, length, desc); + return length; +} + +Offset WriteU32Leb128Raw(uint8_t* dest, uint8_t* dest_end, uint32_t value) { + uint8_t data[MAX_U32_LEB128_BYTES]; + Offset length = 0; + LEB128_LOOP_UNTIL(value == 0); + if (static_cast<Offset>(dest_end - dest) < length) { + return 0; + } + memcpy(dest, data, length); + return length; +} + +Offset WriteFixedU32Leb128Raw(uint8_t* data, uint8_t* end, uint32_t value) { + if (end - data < MAX_U32_LEB128_BYTES) { + return 0; + } + data[0] = (value & 0x7f) | 0x80; + data[1] = ((value >> 7) & 0x7f) | 0x80; + data[2] = ((value >> 14) & 0x7f) | 0x80; + data[3] = ((value >> 21) & 0x7f) | 0x80; + data[4] = ((value >> 28) & 0x0f); + return MAX_U32_LEB128_BYTES; +} + +static void WriteS32Leb128(Stream* stream, int32_t value, const char* desc) { + uint8_t data[MAX_U32_LEB128_BYTES]; + Offset length = 0; + if (value < 0) { + LEB128_LOOP_UNTIL(value == -1 && (byte & 0x40)); + } else { + LEB128_LOOP_UNTIL(value == 0 && !(byte & 0x40)); + } + + stream->WriteData(data, length, desc); +} + +static void WriteS64Leb128(Stream* stream, int64_t value, const char* desc) { + uint8_t data[MAX_U64_LEB128_BYTES]; + Offset length = 0; + if (value < 0) { + LEB128_LOOP_UNTIL(value == -1 && (byte & 0x40)); + } else { + LEB128_LOOP_UNTIL(value == 0 && !(byte & 0x40)); + } + + stream->WriteData(data, length, desc); +} + +void WriteS32Leb128(Stream* stream, uint32_t value, const char* desc) { + WriteS32Leb128(stream, Bitcast<int32_t>(value), desc); +} + +void WriteU64Leb128(Stream* stream, uint64_t value, const char* desc) { + uint8_t data[MAX_U64_LEB128_BYTES]; + Offset length = 0; + LEB128_LOOP_UNTIL(value == 0); + stream->WriteData(data, length, desc); +} + +void WriteS64Leb128(Stream* stream, uint64_t value, const char* desc) { + WriteS64Leb128(stream, Bitcast<int64_t>(value), desc); +} + +void WriteFixedS32Leb128(Stream* stream, uint32_t value, const char* desc) { + uint8_t data[MAX_U32_LEB128_BYTES]; + data[0] = (value & 0x7f) | 0x80; + data[1] = ((value >> 7) & 0x7f) | 0x80; + data[2] = ((value >> 14) & 0x7f) | 0x80; + data[3] = ((value >> 21) & 0x7f) | 0x80; + // The last byte needs to be sign-extended. + data[4] = ((value >> 28) & 0x0f); + if (static_cast<int32_t>(value) < 0) { + data[4] |= 0x70; + } + stream->WriteData(data, MAX_U32_LEB128_BYTES, desc); +} + +#undef LEB128_LOOP_UNTIL + +#define BYTE_AT(type, i, shift) ((static_cast<type>(p[i]) & 0x7f) << (shift)) + +#define LEB128_1(type) (BYTE_AT(type, 0, 0)) +#define LEB128_2(type) (BYTE_AT(type, 1, 7) | LEB128_1(type)) +#define LEB128_3(type) (BYTE_AT(type, 2, 14) | LEB128_2(type)) +#define LEB128_4(type) (BYTE_AT(type, 3, 21) | LEB128_3(type)) +#define LEB128_5(type) (BYTE_AT(type, 4, 28) | LEB128_4(type)) +#define LEB128_6(type) (BYTE_AT(type, 5, 35) | LEB128_5(type)) +#define LEB128_7(type) (BYTE_AT(type, 6, 42) | LEB128_6(type)) +#define LEB128_8(type) (BYTE_AT(type, 7, 49) | LEB128_7(type)) +#define LEB128_9(type) (BYTE_AT(type, 8, 56) | LEB128_8(type)) +#define LEB128_10(type) (BYTE_AT(type, 9, 63) | LEB128_9(type)) + +#define SHIFT_AMOUNT(type, sign_bit) (sizeof(type) * 8 - 1 - (sign_bit)) +#define SIGN_EXTEND(type, value, sign_bit) \ + (static_cast<type>((value) << SHIFT_AMOUNT(type, sign_bit)) >> \ + SHIFT_AMOUNT(type, sign_bit)) + +size_t ReadU32Leb128(const uint8_t* p, + const uint8_t* end, + uint32_t* out_value) { + if (p < end && (p[0] & 0x80) == 0) { + *out_value = LEB128_1(uint32_t); + return 1; + } else if (p + 1 < end && (p[1] & 0x80) == 0) { + *out_value = LEB128_2(uint32_t); + return 2; + } else if (p + 2 < end && (p[2] & 0x80) == 0) { + *out_value = LEB128_3(uint32_t); + return 3; + } else if (p + 3 < end && (p[3] & 0x80) == 0) { + *out_value = LEB128_4(uint32_t); + return 4; + } else if (p + 4 < end && (p[4] & 0x80) == 0) { + // The top bits set represent values > 32 bits. + if (p[4] & 0xf0) { + return 0; + } + *out_value = LEB128_5(uint32_t); + return 5; + } else { + // past the end. + *out_value = 0; + return 0; + } +} + +size_t ReadU64Leb128(const uint8_t* p, + const uint8_t* end, + uint64_t* out_value) { + if (p < end && (p[0] & 0x80) == 0) { + *out_value = LEB128_1(uint64_t); + return 1; + } else if (p + 1 < end && (p[1] & 0x80) == 0) { + *out_value = LEB128_2(uint64_t); + return 2; + } else if (p + 2 < end && (p[2] & 0x80) == 0) { + *out_value = LEB128_3(uint64_t); + return 3; + } else if (p + 3 < end && (p[3] & 0x80) == 0) { + *out_value = LEB128_4(uint64_t); + return 4; + } else if (p + 4 < end && (p[4] & 0x80) == 0) { + *out_value = LEB128_5(uint64_t); + return 5; + } else if (p + 5 < end && (p[5] & 0x80) == 0) { + *out_value = LEB128_6(uint64_t); + return 6; + } else if (p + 6 < end && (p[6] & 0x80) == 0) { + *out_value = LEB128_7(uint64_t); + return 7; + } else if (p + 7 < end && (p[7] & 0x80) == 0) { + *out_value = LEB128_8(uint64_t); + return 8; + } else if (p + 8 < end && (p[8] & 0x80) == 0) { + *out_value = LEB128_9(uint64_t); + return 9; + } else if (p + 9 < end && (p[9] & 0x80) == 0) { + // The top bits set represent values > 32 bits. + if (p[9] & 0xf0) { + return 0; + } + *out_value = LEB128_10(uint64_t); + return 10; + } else { + // past the end. + *out_value = 0; + return 0; + } +} + +size_t ReadS32Leb128(const uint8_t* p, + const uint8_t* end, + uint32_t* out_value) { + if (p < end && (p[0] & 0x80) == 0) { + uint32_t result = LEB128_1(uint32_t); + *out_value = SIGN_EXTEND(int32_t, result, 6); + return 1; + } else if (p + 1 < end && (p[1] & 0x80) == 0) { + uint32_t result = LEB128_2(uint32_t); + *out_value = SIGN_EXTEND(int32_t, result, 13); + return 2; + } else if (p + 2 < end && (p[2] & 0x80) == 0) { + uint32_t result = LEB128_3(uint32_t); + *out_value = SIGN_EXTEND(int32_t, result, 20); + return 3; + } else if (p + 3 < end && (p[3] & 0x80) == 0) { + uint32_t result = LEB128_4(uint32_t); + *out_value = SIGN_EXTEND(int32_t, result, 27); + return 4; + } else if (p + 4 < end && (p[4] & 0x80) == 0) { + // The top bits should be a sign-extension of the sign bit. + bool sign_bit_set = (p[4] & 0x8); + int top_bits = p[4] & 0xf0; + if ((sign_bit_set && top_bits != 0x70) || + (!sign_bit_set && top_bits != 0)) { + return 0; + } + uint32_t result = LEB128_5(uint32_t); + *out_value = result; + return 5; + } else { + // Past the end. + return 0; + } +} + +size_t ReadS64Leb128(const uint8_t* p, + const uint8_t* end, + uint64_t* out_value) { + if (p < end && (p[0] & 0x80) == 0) { + uint64_t result = LEB128_1(uint64_t); + *out_value = SIGN_EXTEND(int64_t, result, 6); + return 1; + } else if (p + 1 < end && (p[1] & 0x80) == 0) { + uint64_t result = LEB128_2(uint64_t); + *out_value = SIGN_EXTEND(int64_t, result, 13); + return 2; + } else if (p + 2 < end && (p[2] & 0x80) == 0) { + uint64_t result = LEB128_3(uint64_t); + *out_value = SIGN_EXTEND(int64_t, result, 20); + return 3; + } else if (p + 3 < end && (p[3] & 0x80) == 0) { + uint64_t result = LEB128_4(uint64_t); + *out_value = SIGN_EXTEND(int64_t, result, 27); + return 4; + } else if (p + 4 < end && (p[4] & 0x80) == 0) { + uint64_t result = LEB128_5(uint64_t); + *out_value = SIGN_EXTEND(int64_t, result, 34); + return 5; + } else if (p + 5 < end && (p[5] & 0x80) == 0) { + uint64_t result = LEB128_6(uint64_t); + *out_value = SIGN_EXTEND(int64_t, result, 41); + return 6; + } else if (p + 6 < end && (p[6] & 0x80) == 0) { + uint64_t result = LEB128_7(uint64_t); + *out_value = SIGN_EXTEND(int64_t, result, 48); + return 7; + } else if (p + 7 < end && (p[7] & 0x80) == 0) { + uint64_t result = LEB128_8(uint64_t); + *out_value = SIGN_EXTEND(int64_t, result, 55); + return 8; + } else if (p + 8 < end && (p[8] & 0x80) == 0) { + uint64_t result = LEB128_9(uint64_t); + *out_value = SIGN_EXTEND(int64_t, result, 62); + return 9; + } else if (p + 9 < end && (p[9] & 0x80) == 0) { + // The top bits should be a sign-extension of the sign bit. + bool sign_bit_set = (p[9] & 0x1); + int top_bits = p[9] & 0xfe; + if ((sign_bit_set && top_bits != 0x7e) || + (!sign_bit_set && top_bits != 0)) { + return 0; + } + uint64_t result = LEB128_10(uint64_t); + *out_value = result; + return 10; + } else { + // Past the end. + return 0; + } +} + +#undef BYTE_AT +#undef LEB128_1 +#undef LEB128_2 +#undef LEB128_3 +#undef LEB128_4 +#undef LEB128_5 +#undef LEB128_6 +#undef LEB128_7 +#undef LEB128_8 +#undef LEB128_9 +#undef LEB128_10 +#undef SHIFT_AMOUNT +#undef SIGN_EXTEND + +} // namespace wabt diff --git a/third_party/wasm2c/src/lexer-keywords.txt b/third_party/wasm2c/src/lexer-keywords.txt new file mode 100644 index 0000000000..9690279297 --- /dev/null +++ b/third_party/wasm2c/src/lexer-keywords.txt @@ -0,0 +1,609 @@ +struct TokenInfo { + TokenInfo(const char* name) : name(name) {} + TokenInfo(const char* name, TokenType token_type) + : name(name), token_type(token_type) {} + TokenInfo(const char* name, Type value_type) + : name(name), token_type(TokenType::ValueType), value_type(value_type) {} + TokenInfo(const char* name, Type value_type, TokenType token_type) + : name(name), token_type(token_type), value_type(value_type) {} + TokenInfo(const char* name, TokenType token_type, Opcode opcode) + : name(name), token_type(token_type), opcode(opcode) {} + + const char* name; + TokenType token_type; + union { + Type value_type; + Opcode opcode; + }; +}; +%% +array, Type::Array, TokenType::Array +assert_exception, TokenType::AssertException +assert_exhaustion, TokenType::AssertExhaustion +assert_invalid, TokenType::AssertInvalid +assert_malformed, TokenType::AssertMalformed +assert_return, TokenType::AssertReturn +assert_trap, TokenType::AssertTrap +assert_unlinkable, TokenType::AssertUnlinkable +atomic.fence, TokenType::AtomicFence, Opcode::AtomicFence +binary, TokenType::Bin +block, TokenType::Block, Opcode::Block +br_if, TokenType::BrIf, Opcode::BrIf +br_table, TokenType::BrTable, Opcode::BrTable +br, TokenType::Br, Opcode::Br +call_indirect, TokenType::CallIndirect, Opcode::CallIndirect +call_ref, TokenType::CallRef, Opcode::CallRef +call, TokenType::Call, Opcode::Call +catch, TokenType::Catch, Opcode::Catch +catch_all, TokenType::CatchAll, Opcode::CatchAll +data.drop, TokenType::DataDrop, Opcode::DataDrop +data, TokenType::Data +declare, TokenType::Declare +delegate, TokenType::Delegate +do, TokenType::Do +drop, TokenType::Drop, Opcode::Drop +either, TokenType::Either +elem.drop, TokenType::ElemDrop, Opcode::ElemDrop +elem, TokenType::Elem +else, TokenType::Else, Opcode::Else +end, TokenType::End, Opcode::End +tag, TokenType::Tag +extern, Type::ExternRef, TokenType::Extern +externref, Type::ExternRef +export, TokenType::Export +f32.abs, TokenType::Unary, Opcode::F32Abs +f32.add, TokenType::Binary, Opcode::F32Add +f32.ceil, TokenType::Unary, Opcode::F32Ceil +f32.const, TokenType::Const, Opcode::F32Const +f32.convert_i32_s, TokenType::Convert, Opcode::F32ConvertI32S +f32.convert_i32_u, TokenType::Convert, Opcode::F32ConvertI32U +f32.convert_i64_s, TokenType::Convert, Opcode::F32ConvertI64S +f32.convert_i64_u, TokenType::Convert, Opcode::F32ConvertI64U +f32.copysign, TokenType::Binary, Opcode::F32Copysign +f32.demote_f64, TokenType::Convert, Opcode::F32DemoteF64 +f32.div, TokenType::Binary, Opcode::F32Div +f32.eq, TokenType::Compare, Opcode::F32Eq +f32.floor, TokenType::Unary, Opcode::F32Floor +f32.ge, TokenType::Compare, Opcode::F32Ge +f32.gt, TokenType::Compare, Opcode::F32Gt +f32.le, TokenType::Compare, Opcode::F32Le +f32.load, TokenType::Load, Opcode::F32Load +f32.lt, TokenType::Compare, Opcode::F32Lt +f32.max, TokenType::Binary, Opcode::F32Max +f32.min, TokenType::Binary, Opcode::F32Min +f32.mul, TokenType::Binary, Opcode::F32Mul +f32.nearest, TokenType::Unary, Opcode::F32Nearest +f32.neg, TokenType::Unary, Opcode::F32Neg +f32.ne, TokenType::Compare, Opcode::F32Ne +f32.reinterpret_i32, TokenType::Convert, Opcode::F32ReinterpretI32 +f32.sqrt, TokenType::Unary, Opcode::F32Sqrt +f32.store, TokenType::Store, Opcode::F32Store +f32.sub, TokenType::Binary, Opcode::F32Sub +f32.trunc, TokenType::Unary, Opcode::F32Trunc +f32, Type::F32 +f32x4.abs, TokenType::Unary, Opcode::F32X4Abs +f32x4.add, TokenType::Binary, Opcode::F32X4Add +f32x4.ceil, TokenType::Unary, Opcode::F32X4Ceil +f32x4.convert_i32x4_s, TokenType::Unary, Opcode::F32X4ConvertI32X4S +f32x4.convert_i32x4_u, TokenType::Unary, Opcode::F32X4ConvertI32X4U +f32x4.div, TokenType::Binary, Opcode::F32X4Div +f32x4.eq, TokenType::Compare, Opcode::F32X4Eq +f32x4.extract_lane, TokenType::SimdLaneOp, Opcode::F32X4ExtractLane +f32x4.floor, TokenType::Unary, Opcode::F32X4Floor +f32x4.ge, TokenType::Compare, Opcode::F32X4Ge +f32x4.gt, TokenType::Compare, Opcode::F32X4Gt +f32x4.le, TokenType::Compare, Opcode::F32X4Le +f32x4.lt, TokenType::Compare, Opcode::F32X4Lt +f32x4.max, TokenType::Binary, Opcode::F32X4Max +f32x4.min, TokenType::Binary, Opcode::F32X4Min +f32x4.mul, TokenType::Binary, Opcode::F32X4Mul +f32x4.nearest, TokenType::Unary, Opcode::F32X4Nearest +f32x4.neg, TokenType::Unary, Opcode::F32X4Neg +f32x4.ne, TokenType::Compare, Opcode::F32X4Ne +f32x4.pmax, TokenType::Binary, Opcode::F32X4PMax +f32x4.pmin, TokenType::Binary, Opcode::F32X4PMin +f32x4.relaxed_madd, TokenType::Ternary, Opcode::F32X4RelaxedMadd +f32x4.relaxed_max, TokenType::Binary, Opcode::F32X4RelaxedMax +f32x4.relaxed_min, TokenType::Binary, Opcode::F32X4RelaxedMin +f32x4.relaxed_nmadd, TokenType::Ternary, Opcode::F32X4RelaxedNmadd +f32x4.replace_lane, TokenType::SimdLaneOp, Opcode::F32X4ReplaceLane +f32x4.splat, TokenType::Unary, Opcode::F32X4Splat +f32x4.sqrt, TokenType::Unary, Opcode::F32X4Sqrt +f32x4.sub, TokenType::Binary, Opcode::F32X4Sub +f32x4.trunc, TokenType::Unary, Opcode::F32X4Trunc +f32x4.demote_f64x2_zero, TokenType::Unary, Opcode::F32X4DemoteF64X2Zero +f32x4, TokenType::F32X4 +f64.abs, TokenType::Unary, Opcode::F64Abs +f64.add, TokenType::Binary, Opcode::F64Add +f64.ceil, TokenType::Unary, Opcode::F64Ceil +f64.const, TokenType::Const, Opcode::F64Const +f64.convert_i32_s, TokenType::Convert, Opcode::F64ConvertI32S +f64.convert_i32_u, TokenType::Convert, Opcode::F64ConvertI32U +f64.convert_i64_s, TokenType::Convert, Opcode::F64ConvertI64S +f64.convert_i64_u, TokenType::Convert, Opcode::F64ConvertI64U +f64.copysign, TokenType::Binary, Opcode::F64Copysign +f64.div, TokenType::Binary, Opcode::F64Div +f64.eq, TokenType::Compare, Opcode::F64Eq +f64.floor, TokenType::Unary, Opcode::F64Floor +f64.ge, TokenType::Compare, Opcode::F64Ge +f64.gt, TokenType::Compare, Opcode::F64Gt +f64.le, TokenType::Compare, Opcode::F64Le +f64.load, TokenType::Load, Opcode::F64Load +f64.lt, TokenType::Compare, Opcode::F64Lt +f64.max, TokenType::Binary, Opcode::F64Max +f64.min, TokenType::Binary, Opcode::F64Min +f64.mul, TokenType::Binary, Opcode::F64Mul +f64.nearest, TokenType::Unary, Opcode::F64Nearest +f64.neg, TokenType::Unary, Opcode::F64Neg +f64.ne, TokenType::Compare, Opcode::F64Ne +f64.promote_f32, TokenType::Convert, Opcode::F64PromoteF32 +f64.reinterpret_i64, TokenType::Convert, Opcode::F64ReinterpretI64 +f64.sqrt, TokenType::Unary, Opcode::F64Sqrt +f64.store, TokenType::Store, Opcode::F64Store +f64.sub, TokenType::Binary, Opcode::F64Sub +f64.trunc, TokenType::Unary, Opcode::F64Trunc +f64, Type::F64 +f64x2.abs, TokenType::Unary, Opcode::F64X2Abs +f64x2.add, TokenType::Binary, Opcode::F64X2Add +f64x2.ceil, TokenType::Unary, Opcode::F64X2Ceil +f64x2.div, TokenType::Binary, Opcode::F64X2Div +f64x2.eq, TokenType::Compare, Opcode::F64X2Eq +f64x2.extract_lane, TokenType::SimdLaneOp, Opcode::F64X2ExtractLane +f64x2.floor, TokenType::Unary, Opcode::F64X2Floor +f64x2.ge, TokenType::Compare, Opcode::F64X2Ge +f64x2.gt, TokenType::Compare, Opcode::F64X2Gt +f64x2.le, TokenType::Compare, Opcode::F64X2Le +f64x2.lt, TokenType::Compare, Opcode::F64X2Lt +f64x2.max, TokenType::Binary, Opcode::F64X2Max +f64x2.min, TokenType::Binary, Opcode::F64X2Min +f64x2.mul, TokenType::Binary, Opcode::F64X2Mul +f64x2.nearest, TokenType::Unary, Opcode::F64X2Nearest +f64x2.neg, TokenType::Unary, Opcode::F64X2Neg +f64x2.ne, TokenType::Compare, Opcode::F64X2Ne +f64x2.pmax, TokenType::Binary, Opcode::F64X2PMax +f64x2.pmin, TokenType::Binary, Opcode::F64X2PMin +f64x2.relaxed_madd, TokenType::Ternary, Opcode::F64X2RelaxedMadd +f64x2.relaxed_max, TokenType::Binary, Opcode::F64X2RelaxedMax +f64x2.relaxed_min, TokenType::Binary, Opcode::F64X2RelaxedMin +f64x2.relaxed_nmadd, TokenType::Ternary, Opcode::F64X2RelaxedNmadd +f64x2.replace_lane, TokenType::SimdLaneOp, Opcode::F64X2ReplaceLane +f64x2.splat, TokenType::Unary, Opcode::F64X2Splat +f64x2.sqrt, TokenType::Unary, Opcode::F64X2Sqrt +f64x2.sub, TokenType::Binary, Opcode::F64X2Sub +f64x2.trunc, TokenType::Unary, Opcode::F64X2Trunc +f64x2.convert_low_i32x4_s, TokenType::Unary, Opcode::F64X2ConvertLowI32X4S +f64x2.convert_low_i32x4_u, TokenType::Unary, Opcode::F64X2ConvertLowI32X4U +f64x2.promote_low_f32x4, TokenType::Unary, Opcode::F64X2PromoteLowF32X4 +f64x2, TokenType::F64X2 +field, TokenType::Field +funcref, Type::FuncRef +func, Type::FuncRef, TokenType::Func +get, TokenType::Get +global.get, TokenType::GlobalGet, Opcode::GlobalGet +global.set, TokenType::GlobalSet, Opcode::GlobalSet +global, TokenType::Global +i16x8.abs, TokenType::Unary, Opcode::I16X8Abs +i16x8.add_sat_s, TokenType::Binary, Opcode::I16X8AddSatS +i16x8.add_sat_u, TokenType::Binary, Opcode::I16X8AddSatU +i16x8.add, TokenType::Binary, Opcode::I16X8Add +i16x8.all_true, TokenType::Unary, Opcode::I16X8AllTrue +i16x8.avgr_u, TokenType::Binary, Opcode::I16X8AvgrU +i16x8.bitmask, TokenType::Unary, Opcode::I16X8Bitmask +i16x8.dot_i8x16_i7x16_s, TokenType::Binary, Opcode::I16X8DotI8X16I7X16S +i16x8.eq, TokenType::Compare, Opcode::I16X8Eq +i16x8.extract_lane_s, TokenType::SimdLaneOp, Opcode::I16X8ExtractLaneS +i16x8.extract_lane_u, TokenType::SimdLaneOp, Opcode::I16X8ExtractLaneU +i16x8.ge_s, TokenType::Compare, Opcode::I16X8GeS +i16x8.ge_u, TokenType::Compare, Opcode::I16X8GeU +i16x8.gt_s, TokenType::Compare, Opcode::I16X8GtS +i16x8.gt_u, TokenType::Compare, Opcode::I16X8GtU +i16x8.le_s, TokenType::Compare, Opcode::I16X8LeS +i16x8.le_u, TokenType::Compare, Opcode::I16X8LeU +v128.load8x8_s, TokenType::Load, Opcode::V128Load8X8S +v128.load8x8_u, TokenType::Load, Opcode::V128Load8X8U +i16x8.lt_s, TokenType::Compare, Opcode::I16X8LtS +i16x8.lt_u, TokenType::Compare, Opcode::I16X8LtU +i16x8.max_s, TokenType::Binary, Opcode::I16X8MaxS +i16x8.max_u, TokenType::Binary, Opcode::I16X8MaxU +i16x8.min_s, TokenType::Binary, Opcode::I16X8MinS +i16x8.min_u, TokenType::Binary, Opcode::I16X8MinU +i16x8.mul, TokenType::Binary, Opcode::I16X8Mul +i16x8.narrow_i32x4_s, TokenType::Binary, Opcode::I16X8NarrowI32X4S +i16x8.narrow_i32x4_u, TokenType::Binary, Opcode::I16X8NarrowI32X4U +i16x8.neg, TokenType::Unary, Opcode::I16X8Neg +i16x8.q15mulr_sat_s, TokenType::Binary, Opcode::I16X8Q15mulrSatS +i16x8.ne, TokenType::Compare, Opcode::I16X8Ne +i16x8.relaxed_laneselect, TokenType::Ternary, Opcode::I16X8RelaxedLaneSelect +i16x8.relaxed_q15mulr_s, TokenType::Binary, Opcode::I16X8RelaxedQ15mulrS +i16x8.replace_lane, TokenType::SimdLaneOp, Opcode::I16X8ReplaceLane +i16x8.shl, TokenType::Binary, Opcode::I16X8Shl +i16x8.shr_s, TokenType::Binary, Opcode::I16X8ShrS +i16x8.shr_u, TokenType::Binary, Opcode::I16X8ShrU +i16x8.splat, TokenType::Unary, Opcode::I16X8Splat +i16x8.sub_sat_s, TokenType::Binary, Opcode::I16X8SubSatS +i16x8.sub_sat_u, TokenType::Binary, Opcode::I16X8SubSatU +i16x8.sub, TokenType::Binary, Opcode::I16X8Sub +i16x8.extadd_pairwise_i8x16_s, TokenType::Unary, Opcode::I16X8ExtaddPairwiseI8X16S +i16x8.extadd_pairwise_i8x16_u, TokenType::Unary, Opcode::I16X8ExtaddPairwiseI8X16U +i16x8.extmul_low_i8x16_s, TokenType::Binary, Opcode::I16X8ExtmulLowI8X16S +i16x8.extmul_high_i8x16_s, TokenType::Binary, Opcode::I16X8ExtmulHighI8X16S +i16x8.extmul_low_i8x16_u, TokenType::Binary, Opcode::I16X8ExtmulLowI8X16U +i16x8.extmul_high_i8x16_u, TokenType::Binary, Opcode::I16X8ExtmulHighI8X16U +i16x8, TokenType::I16X8 +i16x8.extend_high_i8x16_s, TokenType::Unary, Opcode::I16X8ExtendHighI8X16S +i16x8.extend_high_i8x16_u, TokenType::Unary, Opcode::I16X8ExtendHighI8X16U +i16x8.extend_low_i8x16_s, TokenType::Unary, Opcode::I16X8ExtendLowI8X16S +i16x8.extend_low_i8x16_u, TokenType::Unary, Opcode::I16X8ExtendLowI8X16U +i32.add, TokenType::Binary, Opcode::I32Add +i32.and, TokenType::Binary, Opcode::I32And +i32.atomic.load16_u, TokenType::AtomicLoad, Opcode::I32AtomicLoad16U +i32.atomic.load8_u, TokenType::AtomicLoad, Opcode::I32AtomicLoad8U +i32.atomic.load, TokenType::AtomicLoad, Opcode::I32AtomicLoad +i32.atomic.rmw16.add_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw16AddU +i32.atomic.rmw16.and_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw16AndU +i32.atomic.rmw16.cmpxchg_u, TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmw16CmpxchgU +i32.atomic.rmw16.or_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw16OrU +i32.atomic.rmw16.sub_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw16SubU +i32.atomic.rmw16.xchg_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw16XchgU +i32.atomic.rmw16.xor_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw16XorU +i32.atomic.rmw8.add_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw8AddU +i32.atomic.rmw8.and_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw8AndU +i32.atomic.rmw8.cmpxchg_u, TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmw8CmpxchgU +i32.atomic.rmw8.or_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw8OrU +i32.atomic.rmw8.sub_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw8SubU +i32.atomic.rmw8.xchg_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw8XchgU +i32.atomic.rmw8.xor_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw8XorU +i32.atomic.rmw.add, TokenType::AtomicRmw, Opcode::I32AtomicRmwAdd +i32.atomic.rmw.and, TokenType::AtomicRmw, Opcode::I32AtomicRmwAnd +i32.atomic.rmw.cmpxchg, TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmwCmpxchg +i32.atomic.rmw.or, TokenType::AtomicRmw, Opcode::I32AtomicRmwOr +i32.atomic.rmw.sub, TokenType::AtomicRmw, Opcode::I32AtomicRmwSub +i32.atomic.rmw.xchg, TokenType::AtomicRmw, Opcode::I32AtomicRmwXchg +i32.atomic.rmw.xor, TokenType::AtomicRmw, Opcode::I32AtomicRmwXor +i32.atomic.store16, TokenType::AtomicStore, Opcode::I32AtomicStore16 +i32.atomic.store8, TokenType::AtomicStore, Opcode::I32AtomicStore8 +i32.atomic.store, TokenType::AtomicStore, Opcode::I32AtomicStore +i32.clz, TokenType::Unary, Opcode::I32Clz +i32.const, TokenType::Const, Opcode::I32Const +i32.ctz, TokenType::Unary, Opcode::I32Ctz +i32.div_s, TokenType::Binary, Opcode::I32DivS +i32.div_u, TokenType::Binary, Opcode::I32DivU +i32.eq, TokenType::Compare, Opcode::I32Eq +i32.eqz, TokenType::Convert, Opcode::I32Eqz +i32.extend16_s, TokenType::Unary, Opcode::I32Extend16S +i32.extend8_s, TokenType::Unary, Opcode::I32Extend8S +i32.ge_s, TokenType::Compare, Opcode::I32GeS +i32.ge_u, TokenType::Compare, Opcode::I32GeU +i32.gt_s, TokenType::Compare, Opcode::I32GtS +i32.gt_u, TokenType::Compare, Opcode::I32GtU +i32.le_s, TokenType::Compare, Opcode::I32LeS +i32.le_u, TokenType::Compare, Opcode::I32LeU +i32.load16_s, TokenType::Load, Opcode::I32Load16S +i32.load16_u, TokenType::Load, Opcode::I32Load16U +i32.load8_s, TokenType::Load, Opcode::I32Load8S +i32.load8_u, TokenType::Load, Opcode::I32Load8U +i32.load, TokenType::Load, Opcode::I32Load +i32.lt_s, TokenType::Compare, Opcode::I32LtS +i32.lt_u, TokenType::Compare, Opcode::I32LtU +i32.mul, TokenType::Binary, Opcode::I32Mul +i32.ne, TokenType::Compare, Opcode::I32Ne +i32.or, TokenType::Binary, Opcode::I32Or +i32.popcnt, TokenType::Unary, Opcode::I32Popcnt +i32.reinterpret_f32, TokenType::Convert, Opcode::I32ReinterpretF32 +i32.rem_s, TokenType::Binary, Opcode::I32RemS +i32.rem_u, TokenType::Binary, Opcode::I32RemU +i32.rotl, TokenType::Binary, Opcode::I32Rotl +i32.rotr, TokenType::Binary, Opcode::I32Rotr +i32.shl, TokenType::Binary, Opcode::I32Shl +i32.shr_s, TokenType::Binary, Opcode::I32ShrS +i32.shr_u, TokenType::Binary, Opcode::I32ShrU +i32.store16, TokenType::Store, Opcode::I32Store16 +i32.store8, TokenType::Store, Opcode::I32Store8 +i32.store, TokenType::Store, Opcode::I32Store +i32.sub, TokenType::Binary, Opcode::I32Sub +i32.trunc_f32_s, TokenType::Convert, Opcode::I32TruncF32S +i32.trunc_f32_u, TokenType::Convert, Opcode::I32TruncF32U +i32.trunc_f64_s, TokenType::Convert, Opcode::I32TruncF64S +i32.trunc_f64_u, TokenType::Convert, Opcode::I32TruncF64U +i32.trunc_sat_f32_s, TokenType::Convert, Opcode::I32TruncSatF32S +i32.trunc_sat_f32_u, TokenType::Convert, Opcode::I32TruncSatF32U +i32.trunc_sat_f64_s, TokenType::Convert, Opcode::I32TruncSatF64S +i32.trunc_sat_f64_u, TokenType::Convert, Opcode::I32TruncSatF64U +i32, Type::I32 +i32.wrap_i64, TokenType::Convert, Opcode::I32WrapI64 +i32x4.abs, TokenType::Unary, Opcode::I32X4Abs +i32x4.add, TokenType::Binary, Opcode::I32X4Add +i32x4.all_true, TokenType::Unary, Opcode::I32X4AllTrue +i32x4.bitmask, TokenType::Unary, Opcode::I32X4Bitmask +i32x4.dot_i8x16_i7x16_add_s, TokenType::Ternary, Opcode::I32X4DotI8X16I7X16AddS +i32x4.eq, TokenType::Compare, Opcode::I32X4Eq +i32x4.extract_lane, TokenType::SimdLaneOp, Opcode::I32X4ExtractLane +i32x4.ge_s, TokenType::Compare, Opcode::I32X4GeS +i32x4.ge_u, TokenType::Compare, Opcode::I32X4GeU +i32x4.gt_s, TokenType::Compare, Opcode::I32X4GtS +i32x4.gt_u, TokenType::Compare, Opcode::I32X4GtU +i32x4.le_s, TokenType::Compare, Opcode::I32X4LeS +i32x4.le_u, TokenType::Compare, Opcode::I32X4LeU +i32x4.relaxed_trunc_f32x4_s, TokenType::Unary, Opcode::I32X4RelaxedTruncF32X4S +i32x4.relaxed_trunc_f32x4_u, TokenType::Unary, Opcode::I32X4RelaxedTruncF32X4U +i32x4.relaxed_trunc_f64x2_s_zero, TokenType::Unary, Opcode::I32X4RelaxedTruncF64X2SZero +i32x4.relaxed_trunc_f64x2_u_zero, TokenType::Unary, Opcode::I32X4RelaxedTruncF64X2UZero +v128.load16x4_s, TokenType::Load, Opcode::V128Load16X4S +v128.load16x4_u, TokenType::Load, Opcode::V128Load16X4U +i32x4.lt_s, TokenType::Compare, Opcode::I32X4LtS +i32x4.lt_u, TokenType::Compare, Opcode::I32X4LtU +i32x4.max_s, TokenType::Binary, Opcode::I32X4MaxS +i32x4.max_u, TokenType::Binary, Opcode::I32X4MaxU +i32x4.min_s, TokenType::Binary, Opcode::I32X4MinS +i32x4.min_u, TokenType::Binary, Opcode::I32X4MinU +i32x4.dot_i16x8_s, TokenType::Binary, Opcode::I32X4DotI16X8S +i32x4.mul, TokenType::Binary, Opcode::I32X4Mul +i32x4.neg, TokenType::Unary, Opcode::I32X4Neg +i32x4.ne, TokenType::Compare, Opcode::I32X4Ne +i32x4.relaxed_laneselect, TokenType::Ternary, Opcode::I32X4RelaxedLaneSelect +i32x4.replace_lane, TokenType::SimdLaneOp, Opcode::I32X4ReplaceLane +i32x4.shl, TokenType::Binary, Opcode::I32X4Shl +i32x4.shr_s, TokenType::Binary, Opcode::I32X4ShrS +i32x4.shr_u, TokenType::Binary, Opcode::I32X4ShrU +i32x4.splat, TokenType::Unary, Opcode::I32X4Splat +i32x4.sub, TokenType::Binary, Opcode::I32X4Sub +i32x4.extadd_pairwise_i16x8_s, TokenType::Unary, Opcode::I32X4ExtaddPairwiseI16X8S +i32x4.extadd_pairwise_i16x8_u, TokenType::Unary, Opcode::I32X4ExtaddPairwiseI16X8U +i32x4.extmul_low_i16x8_s, TokenType::Binary, Opcode::I32X4ExtmulLowI16X8S +i32x4.extmul_high_i16x8_s, TokenType::Binary, Opcode::I32X4ExtmulHighI16X8S +i32x4.extmul_low_i16x8_u, TokenType::Binary, Opcode::I32X4ExtmulLowI16X8U +i32x4.extmul_high_i16x8_u, TokenType::Binary, Opcode::I32X4ExtmulHighI16X8U +i32x4, TokenType::I32X4 +i32x4.trunc_sat_f32x4_s, TokenType::Unary, Opcode::I32X4TruncSatF32X4S +i32x4.trunc_sat_f32x4_u, TokenType::Unary, Opcode::I32X4TruncSatF32X4U +i32x4.extend_high_i16x8_s, TokenType::Unary, Opcode::I32X4ExtendHighI16X8S +i32x4.extend_high_i16x8_u, TokenType::Unary, Opcode::I32X4ExtendHighI16X8U +i32x4.extend_low_i16x8_s, TokenType::Unary, Opcode::I32X4ExtendLowI16X8S +i32x4.extend_low_i16x8_u, TokenType::Unary, Opcode::I32X4ExtendLowI16X8U +i32x4.trunc_sat_f64x2_s_zero, TokenType::Unary, Opcode::I32X4TruncSatF64X2SZero +i32x4.trunc_sat_f64x2_u_zero, TokenType::Unary, Opcode::I32X4TruncSatF64X2UZero +i32.xor, TokenType::Binary, Opcode::I32Xor +i64.add, TokenType::Binary, Opcode::I64Add +i64.and, TokenType::Binary, Opcode::I64And +i64.atomic.load16_u, TokenType::AtomicLoad, Opcode::I64AtomicLoad16U +i64.atomic.load32_u, TokenType::AtomicLoad, Opcode::I64AtomicLoad32U +i64.atomic.load8_u, TokenType::AtomicLoad, Opcode::I64AtomicLoad8U +i64.atomic.load, TokenType::AtomicLoad, Opcode::I64AtomicLoad +i64.atomic.rmw16.add_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw16AddU +i64.atomic.rmw16.and_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw16AndU +i64.atomic.rmw16.cmpxchg_u, TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw16CmpxchgU +i64.atomic.rmw16.or_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw16OrU +i64.atomic.rmw16.sub_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw16SubU +i64.atomic.rmw16.xchg_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw16XchgU +i64.atomic.rmw16.xor_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw16XorU +i64.atomic.rmw32.add_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw32AddU +i64.atomic.rmw32.and_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw32AndU +i64.atomic.rmw32.cmpxchg_u, TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw32CmpxchgU +i64.atomic.rmw32.or_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw32OrU +i64.atomic.rmw32.sub_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw32SubU +i64.atomic.rmw32.xchg_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw32XchgU +i64.atomic.rmw32.xor_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw32XorU +i64.atomic.rmw8.add_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw8AddU +i64.atomic.rmw8.and_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw8AndU +i64.atomic.rmw8.cmpxchg_u, TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw8CmpxchgU +i64.atomic.rmw8.or_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw8OrU +i64.atomic.rmw8.sub_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw8SubU +i64.atomic.rmw8.xchg_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw8XchgU +i64.atomic.rmw8.xor_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw8XorU +i64.atomic.rmw.add, TokenType::AtomicRmw, Opcode::I64AtomicRmwAdd +i64.atomic.rmw.and, TokenType::AtomicRmw, Opcode::I64AtomicRmwAnd +i64.atomic.rmw.cmpxchg, TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmwCmpxchg +i64.atomic.rmw.or, TokenType::AtomicRmw, Opcode::I64AtomicRmwOr +i64.atomic.rmw.sub, TokenType::AtomicRmw, Opcode::I64AtomicRmwSub +i64.atomic.rmw.xchg, TokenType::AtomicRmw, Opcode::I64AtomicRmwXchg +i64.atomic.rmw.xor, TokenType::AtomicRmw, Opcode::I64AtomicRmwXor +i64.atomic.store16, TokenType::AtomicStore, Opcode::I64AtomicStore16 +i64.atomic.store32, TokenType::AtomicStore, Opcode::I64AtomicStore32 +i64.atomic.store8, TokenType::AtomicStore, Opcode::I64AtomicStore8 +i64.atomic.store, TokenType::AtomicStore, Opcode::I64AtomicStore +i64.clz, TokenType::Unary, Opcode::I64Clz +i64.const, TokenType::Const, Opcode::I64Const +i64.ctz, TokenType::Unary, Opcode::I64Ctz +i64.div_s, TokenType::Binary, Opcode::I64DivS +i64.div_u, TokenType::Binary, Opcode::I64DivU +i64.eq, TokenType::Compare, Opcode::I64Eq +i64.eqz, TokenType::Convert, Opcode::I64Eqz +i64.extend16_s, TokenType::Unary, Opcode::I64Extend16S +i64.extend32_s, TokenType::Unary, Opcode::I64Extend32S +i64.extend8_s, TokenType::Unary, Opcode::I64Extend8S +i64.extend_i32_s, TokenType::Convert, Opcode::I64ExtendI32S +i64.extend_i32_u, TokenType::Convert, Opcode::I64ExtendI32U +i64.ge_s, TokenType::Compare, Opcode::I64GeS +i64.ge_u, TokenType::Compare, Opcode::I64GeU +i64.gt_s, TokenType::Compare, Opcode::I64GtS +i64.gt_u, TokenType::Compare, Opcode::I64GtU +i64.le_s, TokenType::Compare, Opcode::I64LeS +i64.le_u, TokenType::Compare, Opcode::I64LeU +i64.load16_s, TokenType::Load, Opcode::I64Load16S +i64.load16_u, TokenType::Load, Opcode::I64Load16U +i64.load32_s, TokenType::Load, Opcode::I64Load32S +i64.load32_u, TokenType::Load, Opcode::I64Load32U +i64.load8_s, TokenType::Load, Opcode::I64Load8S +i64.load8_u, TokenType::Load, Opcode::I64Load8U +i64.load, TokenType::Load, Opcode::I64Load +i64.lt_s, TokenType::Compare, Opcode::I64LtS +i64.lt_u, TokenType::Compare, Opcode::I64LtU +i64.mul, TokenType::Binary, Opcode::I64Mul +i64.ne, TokenType::Compare, Opcode::I64Ne +i64.or, TokenType::Binary, Opcode::I64Or +i64.popcnt, TokenType::Unary, Opcode::I64Popcnt +i64.reinterpret_f64, TokenType::Convert, Opcode::I64ReinterpretF64 +i64.rem_s, TokenType::Binary, Opcode::I64RemS +i64.rem_u, TokenType::Binary, Opcode::I64RemU +i64.rotl, TokenType::Binary, Opcode::I64Rotl +i64.rotr, TokenType::Binary, Opcode::I64Rotr +i64.shl, TokenType::Binary, Opcode::I64Shl +i64.shr_s, TokenType::Binary, Opcode::I64ShrS +i64.shr_u, TokenType::Binary, Opcode::I64ShrU +i64.store16, TokenType::Store, Opcode::I64Store16 +i64.store32, TokenType::Store, Opcode::I64Store32 +i64.store8, TokenType::Store, Opcode::I64Store8 +i64.store, TokenType::Store, Opcode::I64Store +i64.sub, TokenType::Binary, Opcode::I64Sub +i64.trunc_f32_s, TokenType::Convert, Opcode::I64TruncF32S +i64.trunc_f32_u, TokenType::Convert, Opcode::I64TruncF32U +i64.trunc_f64_s, TokenType::Convert, Opcode::I64TruncF64S +i64.trunc_f64_u, TokenType::Convert, Opcode::I64TruncF64U +i64.trunc_sat_f32_s, TokenType::Convert, Opcode::I64TruncSatF32S +i64.trunc_sat_f32_u, TokenType::Convert, Opcode::I64TruncSatF32U +i64.trunc_sat_f64_s, TokenType::Convert, Opcode::I64TruncSatF64S +i64.trunc_sat_f64_u, TokenType::Convert, Opcode::I64TruncSatF64U +i64, Type::I64 +i64x2.add, TokenType::Binary, Opcode::I64X2Add +i64x2.extract_lane, TokenType::SimdLaneOp, Opcode::I64X2ExtractLane +v128.load32x2_s, TokenType::Load, Opcode::V128Load32X2S +v128.load32x2_u, TokenType::Load, Opcode::V128Load32X2U +i64x2.mul, TokenType::Binary, Opcode::I64X2Mul +i64x2.eq, TokenType::Binary, Opcode::I64X2Eq +i64x2.ne, TokenType::Binary, Opcode::I64X2Ne +i64x2.lt_s, TokenType::Binary, Opcode::I64X2LtS +i64x2.gt_s, TokenType::Binary, Opcode::I64X2GtS +i64x2.le_s, TokenType::Binary, Opcode::I64X2LeS +i64x2.ge_s, TokenType::Binary, Opcode::I64X2GeS +i64x2.abs, TokenType::Unary, Opcode::I64X2Abs +i64x2.neg, TokenType::Unary, Opcode::I64X2Neg +i64x2.all_true, TokenType::Unary, Opcode::I64X2AllTrue +i64x2.bitmask, TokenType::Unary, Opcode::I64X2Bitmask +i64x2.extend_low_i32x4_s, TokenType::Unary, Opcode::I64X2ExtendLowI32X4S +i64x2.extend_high_i32x4_s, TokenType::Unary, Opcode::I64X2ExtendHighI32X4S +i64x2.extend_low_i32x4_u, TokenType::Unary, Opcode::I64X2ExtendLowI32X4U +i64x2.extend_high_i32x4_u, TokenType::Unary, Opcode::I64X2ExtendHighI32X4U +i64x2.relaxed_laneselect, TokenType::Ternary, Opcode::I64X2RelaxedLaneSelect +i64x2.replace_lane, TokenType::SimdLaneOp, Opcode::I64X2ReplaceLane +i64x2.shl, TokenType::Binary, Opcode::I64X2Shl +i64x2.shr_s, TokenType::Binary, Opcode::I64X2ShrS +i64x2.shr_u, TokenType::Binary, Opcode::I64X2ShrU +i64x2.splat, TokenType::Unary, Opcode::I64X2Splat +i64x2.sub, TokenType::Binary, Opcode::I64X2Sub +i64x2.extmul_low_i32x4_s, TokenType::Binary, Opcode::I64X2ExtmulLowI32X4S +i64x2.extmul_high_i32x4_s, TokenType::Binary, Opcode::I64X2ExtmulHighI32X4S +i64x2.extmul_low_i32x4_u, TokenType::Binary, Opcode::I64X2ExtmulLowI32X4U +i64x2.extmul_high_i32x4_u, TokenType::Binary, Opcode::I64X2ExtmulHighI32X4U +i64x2, TokenType::I64X2 +i64.xor, TokenType::Binary, Opcode::I64Xor +i8x16.abs, TokenType::Unary, Opcode::I8X16Abs +i8x16.add_sat_s, TokenType::Binary, Opcode::I8X16AddSatS +i8x16.add_sat_u, TokenType::Binary, Opcode::I8X16AddSatU +i8x16.add, TokenType::Binary, Opcode::I8X16Add +i8x16.all_true, TokenType::Unary, Opcode::I8X16AllTrue +i8x16.avgr_u, TokenType::Binary, Opcode::I8X16AvgrU +i8x16.bitmask, TokenType::Unary, Opcode::I8X16Bitmask +i8x16.eq, TokenType::Compare, Opcode::I8X16Eq +i8x16.extract_lane_s, TokenType::SimdLaneOp, Opcode::I8X16ExtractLaneS +i8x16.extract_lane_u, TokenType::SimdLaneOp, Opcode::I8X16ExtractLaneU +i8x16.ge_s, TokenType::Compare, Opcode::I8X16GeS +i8x16.ge_u, TokenType::Compare, Opcode::I8X16GeU +i8x16.gt_s, TokenType::Compare, Opcode::I8X16GtS +i8x16.gt_u, TokenType::Compare, Opcode::I8X16GtU +i8x16.le_s, TokenType::Compare, Opcode::I8X16LeS +i8x16.le_u, TokenType::Compare, Opcode::I8X16LeU +i8x16.lt_s, TokenType::Compare, Opcode::I8X16LtS +i8x16.lt_u, TokenType::Compare, Opcode::I8X16LtU +i8x16.max_s, TokenType::Binary, Opcode::I8X16MaxS +i8x16.max_u, TokenType::Binary, Opcode::I8X16MaxU +i8x16.min_s, TokenType::Binary, Opcode::I8X16MinS +i8x16.min_u, TokenType::Binary, Opcode::I8X16MinU +i8x16.narrow_i16x8_s, TokenType::Binary, Opcode::I8X16NarrowI16X8S +i8x16.narrow_i16x8_u, TokenType::Binary, Opcode::I8X16NarrowI16X8U +i8x16.neg, TokenType::Unary, Opcode::I8X16Neg +i8x16.popcnt, TokenType::Unary, Opcode::I8X16Popcnt +i8x16.ne, TokenType::Compare, Opcode::I8X16Ne +i8x16.relaxed_swizzle, TokenType::Binary, Opcode::I8X16RelaxedSwizzle +i8x16.relaxed_laneselect, TokenType::Ternary, Opcode::I8X16RelaxedLaneSelect +i8x16.replace_lane, TokenType::SimdLaneOp, Opcode::I8X16ReplaceLane +i8x16.shl, TokenType::Binary, Opcode::I8X16Shl +i8x16.shr_s, TokenType::Binary, Opcode::I8X16ShrS +i8x16.shr_u, TokenType::Binary, Opcode::I8X16ShrU +i8x16.splat, TokenType::Unary, Opcode::I8X16Splat +i8x16.sub_sat_s, TokenType::Binary, Opcode::I8X16SubSatS +i8x16.sub_sat_u, TokenType::Binary, Opcode::I8X16SubSatU +i8x16.sub, TokenType::Binary, Opcode::I8X16Sub +i8x16, TokenType::I8X16 +if, TokenType::If, Opcode::If +import, TokenType::Import +input, TokenType::Input +invoke, TokenType::Invoke +item, TokenType::Item +local.get, TokenType::LocalGet, Opcode::LocalGet +local.set, TokenType::LocalSet, Opcode::LocalSet +local.tee, TokenType::LocalTee, Opcode::LocalTee +local, TokenType::Local +loop, TokenType::Loop, Opcode::Loop +memory.atomic.notify, TokenType::AtomicNotify, Opcode::MemoryAtomicNotify +memory.atomic.wait32, TokenType::AtomicWait, Opcode::MemoryAtomicWait32 +memory.atomic.wait64, TokenType::AtomicWait, Opcode::MemoryAtomicWait64 +memory.copy, TokenType::MemoryCopy, Opcode::MemoryCopy +memory.fill, TokenType::MemoryFill, Opcode::MemoryFill +memory.grow, TokenType::MemoryGrow, Opcode::MemoryGrow +memory.init, TokenType::MemoryInit, Opcode::MemoryInit +memory.size, TokenType::MemorySize, Opcode::MemorySize +memory, TokenType::Memory +module, TokenType::Module +mut, TokenType::Mut +nan:arithmetic, TokenType::NanArithmetic +nan:canonical, TokenType::NanCanonical +nop, TokenType::Nop, Opcode::Nop +offset, TokenType::Offset +output, TokenType::Output +param, TokenType::Param +ref, TokenType::Ref +quote, TokenType::Quote +ref.extern, TokenType::RefExtern +ref.func, TokenType::RefFunc, Opcode::RefFunc +ref.is_null, TokenType::RefIsNull, Opcode::RefIsNull +ref.null, TokenType::RefNull, Opcode::RefNull +register, TokenType::Register +result, TokenType::Result +rethrow, TokenType::Rethrow, Opcode::Rethrow +return_call_indirect, TokenType::ReturnCallIndirect, Opcode::ReturnCallIndirect +return_call, TokenType::ReturnCall, Opcode::ReturnCall +return, TokenType::Return, Opcode::Return +select, TokenType::Select, Opcode::Select +shared, TokenType::Shared +start, TokenType::Start +struct, Type::Struct, TokenType::Struct +table.copy, TokenType::TableCopy, Opcode::TableCopy +table.fill, TokenType::TableFill, Opcode::TableFill +table.get, TokenType::TableGet, Opcode::TableGet +table.grow, TokenType::TableGrow, Opcode::TableGrow +table.init, TokenType::TableInit, Opcode::TableInit +table.set, TokenType::TableSet, Opcode::TableSet +table.size, TokenType::TableSize, Opcode::TableSize +table, TokenType::Table +then, TokenType::Then +throw, TokenType::Throw, Opcode::Throw +try, TokenType::Try, Opcode::Try +type, TokenType::Type +unreachable, TokenType::Unreachable, Opcode::Unreachable +v128.andnot, TokenType::Binary, Opcode::V128Andnot +v128.and, TokenType::Binary, Opcode::V128And +v128.bitselect, TokenType::Ternary, Opcode::V128BitSelect +v128.const, TokenType::Const, Opcode::V128Const +v128.load, TokenType::Load, Opcode::V128Load +v128.not, TokenType::Unary, Opcode::V128Not +v128.or, TokenType::Binary, Opcode::V128Or +v128.any_true, TokenType::Unary, Opcode::V128AnyTrue +v128.load32_zero, TokenType::Load, Opcode::V128Load32Zero +v128.load64_zero, TokenType::Load, Opcode::V128Load64Zero +v128.store, TokenType::Store, Opcode::V128Store +v128, Type::V128 +v128.xor, TokenType::Binary, Opcode::V128Xor +v128.load16_splat, TokenType::Load, Opcode::V128Load16Splat +v128.load32_splat, TokenType::Load, Opcode::V128Load32Splat +v128.load64_splat, TokenType::Load, Opcode::V128Load64Splat +v128.load8_splat, TokenType::Load, Opcode::V128Load8Splat +v128.load8_lane, TokenType::SimdLoadLane, Opcode::V128Load8Lane +v128.load16_lane, TokenType::SimdLoadLane, Opcode::V128Load16Lane +v128.load32_lane, TokenType::SimdLoadLane, Opcode::V128Load32Lane +v128.load64_lane, TokenType::SimdLoadLane, Opcode::V128Load64Lane +v128.store8_lane, TokenType::SimdStoreLane, Opcode::V128Store8Lane +v128.store16_lane, TokenType::SimdStoreLane, Opcode::V128Store16Lane +v128.store32_lane, TokenType::SimdStoreLane, Opcode::V128Store32Lane +v128.store64_lane, TokenType::SimdStoreLane, Opcode::V128Store64Lane +i8x16.shuffle, TokenType::SimdShuffleOp, Opcode::I8X16Shuffle +i8x16.swizzle, TokenType::Binary, Opcode::I8X16Swizzle diff --git a/third_party/wasm2c/src/lexer-source-line-finder.cc b/third_party/wasm2c/src/lexer-source-line-finder.cc new file mode 100644 index 0000000000..4d407e9d8e --- /dev/null +++ b/third_party/wasm2c/src/lexer-source-line-finder.cc @@ -0,0 +1,152 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/lexer-source-line-finder.h" + +#include <algorithm> + +#include "wabt/lexer-source.h" + +namespace wabt { + +LexerSourceLineFinder::LexerSourceLineFinder( + std::unique_ptr<LexerSource> source) + : source_(std::move(source)), + next_line_start_(0), + last_cr_(false), + eof_(false) { + source_->Seek(0); + // Line 0 should not be used; but it makes indexing simpler. + line_ranges_.emplace_back(0, 0); +} + +Result LexerSourceLineFinder::GetSourceLine(const Location& loc, + Offset max_line_length, + SourceLine* out_source_line) { + ColumnRange column_range(loc.first_column, loc.last_column); + OffsetRange original; + CHECK_RESULT(GetLineOffsets(loc.line, &original)); + + OffsetRange clamped = + ClampSourceLineOffsets(original, column_range, max_line_length); + bool has_start_ellipsis = original.start != clamped.start; + bool has_end_ellipsis = original.end != clamped.end; + + out_source_line->column_offset = clamped.start - original.start; + + if (has_start_ellipsis) { + out_source_line->line += "..."; + clamped.start += 3; + } + if (has_end_ellipsis) { + clamped.end -= 3; + } + + std::vector<char> read_line; + CHECK_RESULT(source_->ReadRange(clamped, &read_line)); + out_source_line->line.append(read_line.begin(), read_line.end()); + + if (has_end_ellipsis) { + out_source_line->line += "..."; + } + + return Result::Ok; +} + +bool LexerSourceLineFinder::IsLineCached(int line) const { + return static_cast<size_t>(line) < line_ranges_.size(); +} + +OffsetRange LexerSourceLineFinder::GetCachedLine(int line) const { + assert(IsLineCached(line)); + return line_ranges_[line]; +} + +Result LexerSourceLineFinder::GetLineOffsets(int find_line, + OffsetRange* out_range) { + if (IsLineCached(find_line)) { + *out_range = GetCachedLine(find_line); + return Result::Ok; + } + + const size_t kBufferSize = 1 << 16; + std::vector<char> buffer(kBufferSize); + + assert(!line_ranges_.empty()); + Offset buffer_file_offset = 0; + while (!IsLineCached(find_line) && !eof_) { + CHECK_RESULT(source_->Tell(&buffer_file_offset)); + size_t read_size = source_->Fill(buffer.data(), buffer.size()); + if (read_size < buffer.size()) { + eof_ = true; + } + + for (auto iter = buffer.begin(), end = iter + read_size; iter < end; + ++iter) { + if (*iter == '\n') { + // Don't include \n or \r in the line range. + Offset line_offset = + buffer_file_offset + (iter - buffer.begin()) - last_cr_; + line_ranges_.emplace_back(next_line_start_, line_offset); + next_line_start_ = line_offset + last_cr_ + 1; + } + last_cr_ = *iter == '\r'; + } + + if (eof_) { + // Add the final line as an empty range. + Offset end = buffer_file_offset + read_size; + line_ranges_.emplace_back(next_line_start_, end); + } + } + + if (IsLineCached(find_line)) { + *out_range = GetCachedLine(find_line); + return Result::Ok; + } else { + assert(eof_); + return Result::Error; + } +} + +// static +OffsetRange LexerSourceLineFinder::ClampSourceLineOffsets( + OffsetRange offset_range, + ColumnRange column_range, + Offset max_line_length) { + Offset line_length = offset_range.size(); + if (line_length > max_line_length) { + size_t column_count = column_range.size(); + size_t center_on; + if (column_count > max_line_length) { + // The column range doesn't fit, just center on first_column. + center_on = column_range.start - 1; + } else { + // the entire range fits, display it all in the center. + center_on = (column_range.start + column_range.end) / 2 - 1; + } + if (center_on > max_line_length / 2) { + offset_range.start += center_on - max_line_length / 2; + } + offset_range.start = + std::min(offset_range.start, offset_range.end - max_line_length); + offset_range.end = offset_range.start + max_line_length; + } + + return offset_range; +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/lexer-source.cc b/third_party/wasm2c/src/lexer-source.cc new file mode 100644 index 0000000000..eff56e2571 --- /dev/null +++ b/third_party/wasm2c/src/lexer-source.cc @@ -0,0 +1,67 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/lexer-source.h" + +#include <algorithm> + +namespace wabt { + +LexerSource::LexerSource(const void* data, Offset size) + : data_(data), size_(size), read_offset_(0) {} + +std::unique_ptr<LexerSource> LexerSource::Clone() { + LexerSource* result = new LexerSource(data_, size_); + result->read_offset_ = read_offset_; + return std::unique_ptr<LexerSource>(result); +} + +Result LexerSource::Tell(Offset* out_offset) { + *out_offset = read_offset_; + return Result::Ok; +} + +size_t LexerSource::Fill(void* dest, Offset size) { + Offset read_size = std::min(size, size_ - read_offset_); + if (read_size > 0) { + const void* src = static_cast<const char*>(data_) + read_offset_; + memcpy(dest, src, read_size); + read_offset_ += read_size; + } + return read_size; +} + +Result LexerSource::Seek(Offset offset) { + if (offset < size_) { + read_offset_ = offset; + return Result::Ok; + } + return Result::Error; +} + +Result LexerSource::ReadRange(OffsetRange range, std::vector<char>* out_data) { + OffsetRange clamped = range; + clamped.start = std::min(clamped.start, size_); + clamped.end = std::min(clamped.end, size_); + if (clamped.size()) { + out_data->resize(clamped.size()); + const void* src = static_cast<const char*>(data_) + clamped.start; + memcpy(out_data->data(), src, clamped.size()); + } + return Result::Ok; +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/literal.cc b/third_party/wasm2c/src/literal.cc new file mode 100644 index 0000000000..7076630f66 --- /dev/null +++ b/third_party/wasm2c/src/literal.cc @@ -0,0 +1,829 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/literal.h" + +#include <cassert> +#include <cerrno> +#include <cinttypes> +#include <cmath> +#include <cstdlib> +#include <cstring> +#include <limits> +#include <type_traits> + +namespace wabt { + +namespace { + +template <typename T> +struct FloatTraitsBase {}; + +// The "PlusOne" values are used because normal IEEE floats have an implicit +// leading one, so they have an additional bit of precision. + +template <> +struct FloatTraitsBase<float> { + using Uint = uint32_t; + static constexpr int kBits = sizeof(Uint) * 8; + static constexpr int kSigBits = 23; + static constexpr float kHugeVal = HUGE_VALF; + static constexpr int kMaxHexBufferSize = WABT_MAX_FLOAT_HEX; + + static float Strto(const char* s, char** endptr) { return strtof(s, endptr); } +}; + +template <> +struct FloatTraitsBase<double> { + using Uint = uint64_t; + static constexpr int kBits = sizeof(Uint) * 8; + static constexpr int kSigBits = 52; + static constexpr float kHugeVal = HUGE_VAL; + static constexpr int kMaxHexBufferSize = WABT_MAX_DOUBLE_HEX; + + static double Strto(const char* s, char** endptr) { + return strtod(s, endptr); + } +}; + +template <typename T> +struct FloatTraits : FloatTraitsBase<T> { + using Uint = typename FloatTraitsBase<T>::Uint; + using FloatTraitsBase<T>::kBits; + using FloatTraitsBase<T>::kSigBits; + + static constexpr int kExpBits = kBits - kSigBits - 1; + static constexpr int kSignShift = kBits - 1; + static constexpr Uint kSigMask = (Uint(1) << kSigBits) - 1; + static constexpr int kSigPlusOneBits = kSigBits + 1; + static constexpr Uint kSigPlusOneMask = (Uint(1) << kSigPlusOneBits) - 1; + static constexpr int kExpMask = (1 << kExpBits) - 1; + static constexpr int kMaxExp = 1 << (kExpBits - 1); + static constexpr int kMinExp = -kMaxExp + 1; + static constexpr int kExpBias = -kMinExp; + static constexpr Uint kQuietNanTag = Uint(1) << (kSigBits - 1); +}; + +template <typename T> +class FloatParser { + public: + using Traits = FloatTraits<T>; + using Uint = typename Traits::Uint; + using Float = T; + + static Result Parse(LiteralType, + const char* s, + const char* end, + Uint* out_bits); + + private: + static bool StringStartsWith(const char* start, + const char* end, + const char* prefix); + static Uint Make(bool sign, int exp, Uint sig); + static Uint ShiftAndRoundToNearest(Uint significand, + int shift, + bool seen_trailing_non_zero); + + static Result ParseFloat(const char* s, const char* end, Uint* out_bits); + static Result ParseNan(const char* s, const char* end, Uint* out_bits); + static Result ParseHex(const char* s, const char* end, Uint* out_bits); + static void ParseInfinity(const char* s, const char* end, Uint* out_bits); +}; + +template <typename T> +class FloatWriter { + public: + using Traits = FloatTraits<T>; + using Uint = typename Traits::Uint; + + static void WriteHex(char* out, size_t size, Uint bits); +}; + +// Return 1 if the non-NULL-terminated string starting with |start| and ending +// with |end| starts with the NULL-terminated string |prefix|. +template <typename T> +// static +bool FloatParser<T>::StringStartsWith(const char* start, + const char* end, + const char* prefix) { + while (start < end && *prefix) { + if (*start != *prefix) { + return false; + } + start++; + prefix++; + } + return *prefix == 0; +} + +// static +template <typename T> +Result FloatParser<T>::ParseFloat(const char* s, + const char* end, + Uint* out_bits) { + // Here is the normal behavior for strtof/strtod: + // + // input | errno | output | + // --------------------------------- + // overflow | ERANGE | +-HUGE_VAL | + // underflow | ERANGE | 0.0 | + // otherwise | 0 | value | + // + // So normally we need to clear errno before calling strto{f,d}, and check + // afterward whether it was set to ERANGE. + // + // glibc seems to have a bug where + // strtof("340282356779733661637539395458142568448") will return HUGE_VAL, + // but will not set errno to ERANGE. Since this function is only called when + // we know that we have parsed a "normal" number (i.e. not "inf"), we know + // that if we ever get HUGE_VAL, it must be overflow. + // + // The WebAssembly spec also ignores underflow, so we don't need to check for + // ERANGE at all. + + // WebAssembly floats can contain underscores, but strto* can't parse those, + // so remove them first. + assert(s <= end); + const size_t kBufferSize = end - s + 1; // +1 for \0. + char* buffer = static_cast<char*>(alloca(kBufferSize)); + auto buffer_end = + std::copy_if(s, end, buffer, [](char c) -> bool { return c != '_'; }); + assert(buffer_end < buffer + kBufferSize); + *buffer_end = 0; + + char* endptr; + Float value = Traits::Strto(buffer, &endptr); + if (endptr != buffer_end || + (value == Traits::kHugeVal || value == -Traits::kHugeVal)) { + return Result::Error; + } + + memcpy(out_bits, &value, sizeof(value)); + return Result::Ok; +} + +// static +template <typename T> +typename FloatParser<T>::Uint FloatParser<T>::Make(bool sign, + int exp, + Uint sig) { + assert(exp >= Traits::kMinExp && exp <= Traits::kMaxExp); + assert(sig <= Traits::kSigMask); + return (Uint(sign) << Traits::kSignShift) | + (Uint(exp + Traits::kExpBias) << Traits::kSigBits) | sig; +} + +// static +template <typename T> +typename FloatParser<T>::Uint FloatParser<T>::ShiftAndRoundToNearest( + Uint significand, + int shift, + bool seen_trailing_non_zero) { + assert(shift > 0); + // Round ties to even. + if ((significand & (Uint(1) << shift)) || seen_trailing_non_zero) { + significand += Uint(1) << (shift - 1); + } + significand >>= shift; + return significand; +} + +// static +template <typename T> +Result FloatParser<T>::ParseNan(const char* s, + const char* end, + Uint* out_bits) { + bool is_neg = false; + if (*s == '-') { + is_neg = true; + s++; + } else if (*s == '+') { + s++; + } + assert(StringStartsWith(s, end, "nan")); + s += 3; + + Uint tag; + if (s != end) { + tag = 0; + assert(StringStartsWith(s, end, ":0x")); + s += 3; + + for (; s < end; ++s) { + if (*s == '_') { + continue; + } + uint32_t digit; + CHECK_RESULT(ParseHexdigit(*s, &digit)); + tag = tag * 16 + digit; + // Check for overflow. + if (tag > Traits::kSigMask) { + return Result::Error; + } + } + + // NaN cannot have a zero tag, that is reserved for infinity. + if (tag == 0) { + return Result::Error; + } + } else { + tag = Traits::kQuietNanTag; + } + + *out_bits = Make(is_neg, Traits::kMaxExp, tag); + return Result::Ok; +} + +// static +template <typename T> +Result FloatParser<T>::ParseHex(const char* s, + const char* end, + Uint* out_bits) { + bool is_neg = false; + if (*s == '-') { + is_neg = true; + s++; + } else if (*s == '+') { + s++; + } + assert(StringStartsWith(s, end, "0x")); + s += 2; + + // Loop over the significand; everything up to the 'p'. + // This code is a bit nasty because we want to support extra zeroes anywhere + // without having to use many significand bits. + // e.g. + // 0x00000001.0p0 => significand = 1, significand_exponent = 0 + // 0x10000000.0p0 => significand = 1, significand_exponent = 28 + // 0x0.000001p0 => significand = 1, significand_exponent = -24 + bool seen_dot = false; + bool seen_trailing_non_zero = false; + Uint significand = 0; + int significand_exponent = 0; // Exponent adjustment due to dot placement. + for (; s < end; ++s) { + uint32_t digit; + if (*s == '_') { + continue; + } else if (*s == '.') { + seen_dot = true; + } else if (Succeeded(ParseHexdigit(*s, &digit))) { + if (Traits::kBits - Clz(significand) <= Traits::kSigPlusOneBits) { + significand = (significand << 4) + digit; + if (seen_dot) { + significand_exponent -= 4; + } + } else { + if (!seen_trailing_non_zero && digit != 0) { + seen_trailing_non_zero = true; + } + if (!seen_dot) { + significand_exponent += 4; + } + } + } else { + break; + } + } + + if (significand == 0) { + // 0 or -0. + *out_bits = Make(is_neg, Traits::kMinExp, 0); + return Result::Ok; + } + + int exponent = 0; + bool exponent_is_neg = false; + if (s < end) { + assert(*s == 'p' || *s == 'P'); + s++; + // Exponent is always positive, but significand_exponent is signed. + // significand_exponent_add is negated if exponent will be negative, so it + // can be easily summed to see if the exponent is too large (see below). + int significand_exponent_add = 0; + if (*s == '-') { + exponent_is_neg = true; + significand_exponent_add = -significand_exponent; + s++; + } else if (*s == '+') { + s++; + significand_exponent_add = significand_exponent; + } + + for (; s < end; ++s) { + if (*s == '_') { + continue; + } + + uint32_t digit = (*s - '0'); + assert(digit <= 9); + exponent = exponent * 10 + digit; + if (exponent + significand_exponent_add >= Traits::kMaxExp) { + break; + } + } + } + + if (exponent_is_neg) { + exponent = -exponent; + } + + int significand_bits = Traits::kBits - Clz(significand); + // -1 for the implicit 1 bit of the significand. + exponent += significand_exponent + significand_bits - 1; + + if (exponent <= Traits::kMinExp) { + // Maybe subnormal. + auto update_seen_trailing_non_zero = [&](int shift) { + assert(shift > 0); + auto mask = (Uint(1) << (shift - 1)) - 1; + seen_trailing_non_zero |= (significand & mask) != 0; + }; + + // Normalize significand. + if (significand_bits > Traits::kSigBits) { + int shift = significand_bits - Traits::kSigBits; + update_seen_trailing_non_zero(shift); + significand >>= shift; + } else if (significand_bits < Traits::kSigBits) { + significand <<= (Traits::kSigBits - significand_bits); + } + + int shift = Traits::kMinExp - exponent; + if (shift <= Traits::kSigBits) { + if (shift) { + update_seen_trailing_non_zero(shift); + significand = + ShiftAndRoundToNearest(significand, shift, seen_trailing_non_zero) & + Traits::kSigMask; + } + exponent = Traits::kMinExp; + + if (significand != 0) { + *out_bits = Make(is_neg, exponent, significand); + return Result::Ok; + } + } + + // Not subnormal, too small; return 0 or -0. + *out_bits = Make(is_neg, Traits::kMinExp, 0); + } else { + // Maybe Normal value. + if (significand_bits > Traits::kSigPlusOneBits) { + significand = ShiftAndRoundToNearest( + significand, significand_bits - Traits::kSigPlusOneBits, + seen_trailing_non_zero); + if (significand > Traits::kSigPlusOneMask) { + exponent++; + } + } else if (significand_bits < Traits::kSigPlusOneBits) { + significand <<= (Traits::kSigPlusOneBits - significand_bits); + } + + if (exponent >= Traits::kMaxExp) { + // Would be inf or -inf, but the spec doesn't allow rounding hex-floats to + // infinity. + return Result::Error; + } + + *out_bits = Make(is_neg, exponent, significand & Traits::kSigMask); + } + + return Result::Ok; +} + +// static +template <typename T> +void FloatParser<T>::ParseInfinity(const char* s, + const char* end, + Uint* out_bits) { + bool is_neg = false; + if (*s == '-') { + is_neg = true; + s++; + } else if (*s == '+') { + s++; + } + assert(StringStartsWith(s, end, "inf")); + *out_bits = Make(is_neg, Traits::kMaxExp, 0); +} + +// static +template <typename T> +Result FloatParser<T>::Parse(LiteralType literal_type, + const char* s, + const char* end, + Uint* out_bits) { +#if COMPILER_IS_MSVC + if (literal_type == LiteralType::Int && StringStartsWith(s, end, "0x")) { + // Some MSVC crt implementation of strtof doesn't support hex strings + literal_type = LiteralType::Hexfloat; + } +#endif + switch (literal_type) { + case LiteralType::Int: + case LiteralType::Float: + return ParseFloat(s, end, out_bits); + + case LiteralType::Hexfloat: + return ParseHex(s, end, out_bits); + + case LiteralType::Infinity: + ParseInfinity(s, end, out_bits); + return Result::Ok; + + case LiteralType::Nan: + return ParseNan(s, end, out_bits); + } + + WABT_UNREACHABLE; +} + +// static +template <typename T> +void FloatWriter<T>::WriteHex(char* out, size_t size, Uint bits) { + static constexpr int kNumNybbles = Traits::kBits / 4; + static constexpr int kTopNybbleShift = Traits::kBits - 4; + static constexpr Uint kTopNybble = Uint(0xf) << kTopNybbleShift; + static const char s_hex_digits[] = "0123456789abcdef"; + + char buffer[Traits::kMaxHexBufferSize]; + char* p = buffer; + bool is_neg = (bits >> Traits::kSignShift); + int exp = ((bits >> Traits::kSigBits) & Traits::kExpMask) - Traits::kExpBias; + Uint sig = bits & Traits::kSigMask; + + if (is_neg) { + *p++ = '-'; + } + if (exp == Traits::kMaxExp) { + // Infinity or nan. + if (sig == 0) { + strcpy(p, "inf"); + p += 3; + } else { + strcpy(p, "nan"); + p += 3; + if (sig != Traits::kQuietNanTag) { + strcpy(p, ":0x"); + p += 3; + // Skip leading zeroes. + int num_nybbles = kNumNybbles; + while ((sig & kTopNybble) == 0) { + sig <<= 4; + num_nybbles--; + } + while (num_nybbles) { + Uint nybble = (sig >> kTopNybbleShift) & 0xf; + *p++ = s_hex_digits[nybble]; + sig <<= 4; + --num_nybbles; + } + } + } + } else { + bool is_zero = sig == 0 && exp == Traits::kMinExp; + strcpy(p, "0x"); + p += 2; + *p++ = is_zero ? '0' : '1'; + + // Shift sig up so the top 4-bits are at the top of the Uint. + sig <<= Traits::kBits - Traits::kSigBits; + + if (sig) { + if (exp == Traits::kMinExp) { + // Subnormal; shift the significand up, and shift out the implicit 1. + Uint leading_zeroes = Clz(sig); + if (leading_zeroes < Traits::kSignShift) { + sig <<= leading_zeroes + 1; + } else { + sig = 0; + } + exp -= leading_zeroes; + } + + *p++ = '.'; + while (sig) { + int nybble = (sig >> kTopNybbleShift) & 0xf; + *p++ = s_hex_digits[nybble]; + sig <<= 4; + } + } + *p++ = 'p'; + if (is_zero) { + strcpy(p, "+0"); + p += 2; + } else { + if (exp < 0) { + *p++ = '-'; + exp = -exp; + } else { + *p++ = '+'; + } + if (exp >= 1000) { + *p++ = '1'; + } + if (exp >= 100) { + *p++ = '0' + (exp / 100) % 10; + } + if (exp >= 10) { + *p++ = '0' + (exp / 10) % 10; + } + *p++ = '0' + exp % 10; + } + } + + size_t len = p - buffer; + if (len >= size) { + len = size - 1; + } + memcpy(out, buffer, len); + out[len] = '\0'; +} + +} // end anonymous namespace + +Result ParseHexdigit(char c, uint32_t* out) { + if (static_cast<unsigned int>(c - '0') <= 9) { + *out = c - '0'; + return Result::Ok; + } else if (static_cast<unsigned int>(c - 'a') < 6) { + *out = 10 + (c - 'a'); + return Result::Ok; + } else if (static_cast<unsigned int>(c - 'A') < 6) { + *out = 10 + (c - 'A'); + return Result::Ok; + } + return Result::Error; +} + +Result ParseUint64(const char* s, const char* end, uint64_t* out) { + if (s == end) { + return Result::Error; + } + uint64_t value = 0; + if (*s == '0' && s + 1 < end && s[1] == 'x') { + s += 2; + if (s == end) { + return Result::Error; + } + constexpr uint64_t kMaxDiv16 = UINT64_MAX / 16; + constexpr uint64_t kMaxMod16 = UINT64_MAX % 16; + for (; s < end; ++s) { + uint32_t digit; + if (*s == '_') { + continue; + } + CHECK_RESULT(ParseHexdigit(*s, &digit)); + // Check for overflow. + if (value > kMaxDiv16 || (value == kMaxDiv16 && digit > kMaxMod16)) { + return Result::Error; + } + value = value * 16 + digit; + } + } else { + constexpr uint64_t kMaxDiv10 = UINT64_MAX / 10; + constexpr uint64_t kMaxMod10 = UINT64_MAX % 10; + for (; s < end; ++s) { + if (*s == '_') { + continue; + } + uint32_t digit = (*s - '0'); + if (digit > 9) { + return Result::Error; + } + // Check for overflow. + if (value > kMaxDiv10 || (value == kMaxDiv10 && digit > kMaxMod10)) { + return Result::Error; + } + value = value * 10 + digit; + } + } + if (s != end) { + return Result::Error; + } + *out = value; + return Result::Ok; +} + +Result ParseInt64(const char* s, + const char* end, + uint64_t* out, + ParseIntType parse_type) { + bool has_sign = false; + if (*s == '-' || *s == '+') { + if (parse_type == ParseIntType::UnsignedOnly) { + return Result::Error; + } + if (*s == '-') { + has_sign = true; + } + s++; + } + uint64_t value = 0; + Result result = ParseUint64(s, end, &value); + if (has_sign) { + // abs(INT64_MIN) == INT64_MAX + 1. + if (value > static_cast<uint64_t>(INT64_MAX) + 1) { + return Result::Error; + } + value = UINT64_MAX - value + 1; + } + *out = value; + return result; +} + +namespace { +uint32_t AddWithCarry(uint32_t x, uint32_t y, uint32_t* carry) { + // Increments *carry if the addition overflows, otherwise leaves carry alone. + if ((0xffffffff - x) < y) { + ++*carry; + } + return x + y; +} + +void Mul10(v128* v) { + // Multiply-by-10 decomposes into (x << 3) + (x << 1). We implement those + // operations with carrying from smaller quads of the v128 to the larger + // quads. + + constexpr uint32_t kTopThreeBits = 0xe0000000; + constexpr uint32_t kTopBit = 0x80000000; + + uint32_t carry_into_v1 = + ((v->u32(0) & kTopThreeBits) >> 29) + ((v->u32(0) & kTopBit) >> 31); + v->set_u32(0, AddWithCarry(v->u32(0) << 3, v->u32(0) << 1, &carry_into_v1)); + uint32_t carry_into_v2 = + ((v->u32(1) & kTopThreeBits) >> 29) + ((v->u32(1) & kTopBit) >> 31); + v->set_u32(1, AddWithCarry(v->u32(1) << 3, v->u32(1) << 1, &carry_into_v2)); + v->set_u32(1, AddWithCarry(v->u32(1), carry_into_v1, &carry_into_v2)); + uint32_t carry_into_v3 = + ((v->u32(2) & kTopThreeBits) >> 29) + ((v->u32(2) & kTopBit) >> 31); + v->set_u32(2, AddWithCarry(v->u32(2) << 3, v->u32(2) << 1, &carry_into_v3)); + v->set_u32(2, AddWithCarry(v->u32(2), carry_into_v2, &carry_into_v3)); + v->set_u32(3, v->u32(3) * 10 + carry_into_v3); +} +} // namespace + +Result ParseUint128(const char* s, const char* end, v128* out) { + if (s == end) { + return Result::Error; + } + + out->set_zero(); + + while (true) { + uint32_t digit = (*s - '0'); + if (digit > 9) { + return Result::Error; + } + + uint32_t carry_into_v1 = 0; + uint32_t carry_into_v2 = 0; + uint32_t carry_into_v3 = 0; + uint32_t overflow = 0; + out->set_u32(0, AddWithCarry(out->u32(0), digit, &carry_into_v1)); + out->set_u32(1, AddWithCarry(out->u32(1), carry_into_v1, &carry_into_v2)); + out->set_u32(2, AddWithCarry(out->u32(2), carry_into_v2, &carry_into_v3)); + out->set_u32(3, AddWithCarry(out->u32(3), carry_into_v3, &overflow)); + if (overflow) { + return Result::Error; + } + + ++s; + + if (s == end) { + break; + } + + Mul10(out); + } + return Result::Ok; +} + +template <typename U> +Result ParseInt(const char* s, + const char* end, + U* out, + ParseIntType parse_type) { + using S = typename std::make_signed<U>::type; + uint64_t value; + bool has_sign = false; + if (*s == '-' || *s == '+') { + if (parse_type == ParseIntType::UnsignedOnly) { + return Result::Error; + } + if (*s == '-') { + has_sign = true; + } + s++; + } + CHECK_RESULT(ParseUint64(s, end, &value)); + + if (has_sign) { + // abs(INTN_MIN) == INTN_MAX + 1. + if (value > static_cast<uint64_t>(std::numeric_limits<S>::max()) + 1) { + return Result::Error; + } + value = std::numeric_limits<U>::max() - value + 1; + } else { + if (value > static_cast<uint64_t>(std::numeric_limits<U>::max())) { + return Result::Error; + } + } + *out = static_cast<U>(value); + return Result::Ok; +} + +Result ParseInt8(const char* s, + const char* end, + uint8_t* out, + ParseIntType parse_type) { + return ParseInt(s, end, out, parse_type); +} + +Result ParseInt16(const char* s, + const char* end, + uint16_t* out, + ParseIntType parse_type) { + return ParseInt(s, end, out, parse_type); +} + +Result ParseInt32(const char* s, + const char* end, + uint32_t* out, + ParseIntType parse_type) { + return ParseInt(s, end, out, parse_type); +} + +Result ParseFloat(LiteralType literal_type, + const char* s, + const char* end, + uint32_t* out_bits) { + return FloatParser<float>::Parse(literal_type, s, end, out_bits); +} + +Result ParseDouble(LiteralType literal_type, + const char* s, + const char* end, + uint64_t* out_bits) { + return FloatParser<double>::Parse(literal_type, s, end, out_bits); +} + +void WriteFloatHex(char* buffer, size_t size, uint32_t bits) { + return FloatWriter<float>::WriteHex(buffer, size, bits); +} + +void WriteDoubleHex(char* buffer, size_t size, uint64_t bits) { + return FloatWriter<double>::WriteHex(buffer, size, bits); +} + +void WriteUint128(char* buffer, size_t size, v128 bits) { + uint64_t digits; + uint64_t remainder; + char reversed_buffer[40]; + size_t len = 0; + do { + remainder = bits.u32(3); + + for (int i = 3; i != 0; --i) { + digits = remainder / 10; + remainder = ((remainder - digits * 10) << 32) + bits.u32(i - 1); + bits.set_u32(i, digits); + } + + digits = remainder / 10; + remainder = remainder - digits * 10; + bits.set_u32(0, digits); + + char remainder_buffer[21]; + snprintf(remainder_buffer, 21, "%" PRIu64, remainder); + int remainder_buffer_len = strlen(remainder_buffer); + assert(len + remainder_buffer_len < sizeof(reversed_buffer)); + memcpy(&reversed_buffer[len], remainder_buffer, remainder_buffer_len); + len += remainder_buffer_len; + } while (!bits.is_zero()); + size_t truncated_tail = 0; + if (len >= size) { + truncated_tail = len - size + 1; + len = size - 1; + } + std::reverse_copy(reversed_buffer + truncated_tail, + reversed_buffer + len + truncated_tail, buffer); + buffer[len] = '\0'; +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/opcode-code-table.c b/third_party/wasm2c/src/opcode-code-table.c new file mode 100644 index 0000000000..e3fdc994e1 --- /dev/null +++ b/third_party/wasm2c/src/opcode-code-table.c @@ -0,0 +1,41 @@ +/* + * Copyright 2018 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/opcode-code-table.h" + +#include "wabt/config.h" + +#include <stdint.h> + +typedef enum WabtOpcodeEnum { +#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \ + text, decomp) \ + Name, +#include "wabt/opcode.def" +#undef WABT_OPCODE + Invalid, +} WabtOpcodeEnum; + +WABT_STATIC_ASSERT(Invalid <= WABT_OPCODE_CODE_TABLE_SIZE); + +/* The array index calculated below must match the one in Opcode::FromCode. */ +uint32_t WabtOpcodeCodeTable[WABT_OPCODE_CODE_TABLE_SIZE] = { +#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \ + text, decomp) \ + [(prefix << MAX_OPCODE_BITS) + code] = Name, +#include "wabt/opcode.def" +#undef WABT_OPCODE +}; diff --git a/third_party/wasm2c/src/opcode.cc b/third_party/wasm2c/src/opcode.cc new file mode 100644 index 0000000000..3a62fa7182 --- /dev/null +++ b/third_party/wasm2c/src/opcode.cc @@ -0,0 +1,429 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/opcode.h" + +#include "wabt/feature.h" + +namespace wabt { + +// static +Opcode::Info Opcode::infos_[] = { +#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \ + text, decomp) \ + {text, decomp, Type::rtype, {Type::type1, Type::type2, Type::type3}, \ + mem_size, prefix, code, PrefixCode(prefix, code)}, +#include "wabt/opcode.def" +#undef WABT_OPCODE + + {"<invalid>", "", Type::Void, {Type::Void, Type::Void, Type::Void}, 0, 0, 0, 0}, +}; + +#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \ + text, decomp) \ + /* static */ Opcode Opcode::Name##_Opcode(Opcode::Name); +#include "wabt/opcode.def" +#undef WABT_OPCODE + +Opcode::Info Opcode::GetInfo() const { + if (enum_ < Invalid) { + return infos_[enum_]; + } + + Info invalid_info = infos_[Opcode::Invalid]; + DecodeInvalidOpcode(enum_, &invalid_info.prefix, &invalid_info.code); + invalid_info.prefix_code = PrefixCode(invalid_info.prefix, invalid_info.code); + return invalid_info; +} + +bool Opcode::IsNaturallyAligned(Address alignment) const { + Address opcode_align = GetMemorySize(); + return alignment == WABT_USE_NATURAL_ALIGNMENT || alignment == opcode_align; +} + +Address Opcode::GetAlignment(Address alignment) const { + if (alignment == WABT_USE_NATURAL_ALIGNMENT) { + return GetMemorySize(); + } + return alignment; +} + +bool Opcode::IsEnabled(const Features& features) const { + switch (enum_) { + case Opcode::Try: + case Opcode::Catch: + case Opcode::Delegate: + case Opcode::Throw: + case Opcode::Rethrow: + return features.exceptions_enabled(); + + case Opcode::ReturnCallIndirect: + case Opcode::ReturnCall: + return features.tail_call_enabled(); + + case Opcode::I32TruncSatF32S: + case Opcode::I32TruncSatF32U: + case Opcode::I32TruncSatF64S: + case Opcode::I32TruncSatF64U: + case Opcode::I64TruncSatF32S: + case Opcode::I64TruncSatF32U: + case Opcode::I64TruncSatF64S: + case Opcode::I64TruncSatF64U: + return features.sat_float_to_int_enabled(); + + case Opcode::I32Extend8S: + case Opcode::I32Extend16S: + case Opcode::I64Extend8S: + case Opcode::I64Extend16S: + case Opcode::I64Extend32S: + return features.sign_extension_enabled(); + + case Opcode::MemoryAtomicNotify: + case Opcode::MemoryAtomicWait32: + case Opcode::MemoryAtomicWait64: + case Opcode::AtomicFence: + case Opcode::I32AtomicLoad: + case Opcode::I64AtomicLoad: + case Opcode::I32AtomicLoad8U: + case Opcode::I32AtomicLoad16U: + case Opcode::I64AtomicLoad8U: + case Opcode::I64AtomicLoad16U: + case Opcode::I64AtomicLoad32U: + case Opcode::I32AtomicStore: + case Opcode::I64AtomicStore: + case Opcode::I32AtomicStore8: + case Opcode::I32AtomicStore16: + case Opcode::I64AtomicStore8: + case Opcode::I64AtomicStore16: + case Opcode::I64AtomicStore32: + case Opcode::I32AtomicRmwAdd: + case Opcode::I64AtomicRmwAdd: + case Opcode::I32AtomicRmw8AddU: + case Opcode::I32AtomicRmw16AddU: + case Opcode::I64AtomicRmw8AddU: + case Opcode::I64AtomicRmw16AddU: + case Opcode::I64AtomicRmw32AddU: + case Opcode::I32AtomicRmwSub: + case Opcode::I64AtomicRmwSub: + case Opcode::I32AtomicRmw8SubU: + case Opcode::I32AtomicRmw16SubU: + case Opcode::I64AtomicRmw8SubU: + case Opcode::I64AtomicRmw16SubU: + case Opcode::I64AtomicRmw32SubU: + case Opcode::I32AtomicRmwAnd: + case Opcode::I64AtomicRmwAnd: + case Opcode::I32AtomicRmw8AndU: + case Opcode::I32AtomicRmw16AndU: + case Opcode::I64AtomicRmw8AndU: + case Opcode::I64AtomicRmw16AndU: + case Opcode::I64AtomicRmw32AndU: + case Opcode::I32AtomicRmwOr: + case Opcode::I64AtomicRmwOr: + case Opcode::I32AtomicRmw8OrU: + case Opcode::I32AtomicRmw16OrU: + case Opcode::I64AtomicRmw8OrU: + case Opcode::I64AtomicRmw16OrU: + case Opcode::I64AtomicRmw32OrU: + case Opcode::I32AtomicRmwXor: + case Opcode::I64AtomicRmwXor: + case Opcode::I32AtomicRmw8XorU: + case Opcode::I32AtomicRmw16XorU: + case Opcode::I64AtomicRmw8XorU: + case Opcode::I64AtomicRmw16XorU: + case Opcode::I64AtomicRmw32XorU: + case Opcode::I32AtomicRmwXchg: + case Opcode::I64AtomicRmwXchg: + case Opcode::I32AtomicRmw8XchgU: + case Opcode::I32AtomicRmw16XchgU: + case Opcode::I64AtomicRmw8XchgU: + case Opcode::I64AtomicRmw16XchgU: + case Opcode::I64AtomicRmw32XchgU: + case Opcode::I32AtomicRmwCmpxchg: + case Opcode::I64AtomicRmwCmpxchg: + case Opcode::I32AtomicRmw8CmpxchgU: + case Opcode::I32AtomicRmw16CmpxchgU: + case Opcode::I64AtomicRmw8CmpxchgU: + case Opcode::I64AtomicRmw16CmpxchgU: + case Opcode::I64AtomicRmw32CmpxchgU: + return features.threads_enabled(); + + case Opcode::V128Const: + case Opcode::V128Load: + case Opcode::V128Store: + case Opcode::I8X16Splat: + case Opcode::I16X8Splat: + case Opcode::I32X4Splat: + case Opcode::I64X2Splat: + case Opcode::F32X4Splat: + case Opcode::F64X2Splat: + case Opcode::I8X16ExtractLaneS: + case Opcode::I8X16ExtractLaneU: + case Opcode::I16X8ExtractLaneS: + case Opcode::I16X8ExtractLaneU: + case Opcode::I32X4ExtractLane: + case Opcode::I64X2ExtractLane: + case Opcode::F32X4ExtractLane: + case Opcode::F64X2ExtractLane: + case Opcode::I8X16ReplaceLane: + case Opcode::I16X8ReplaceLane: + case Opcode::I32X4ReplaceLane: + case Opcode::I64X2ReplaceLane: + case Opcode::F32X4ReplaceLane: + case Opcode::F64X2ReplaceLane: + case Opcode::I8X16Add: + case Opcode::I16X8Add: + case Opcode::I32X4Add: + case Opcode::I64X2Add: + case Opcode::I8X16Sub: + case Opcode::I16X8Sub: + case Opcode::I32X4Sub: + case Opcode::I64X2Sub: + case Opcode::I16X8Mul: + case Opcode::I32X4Mul: + case Opcode::I8X16Neg: + case Opcode::I16X8Neg: + case Opcode::I32X4Neg: + case Opcode::I64X2Neg: + case Opcode::I8X16AddSatS: + case Opcode::I8X16AddSatU: + case Opcode::I16X8AddSatS: + case Opcode::I16X8AddSatU: + case Opcode::I8X16SubSatS: + case Opcode::I8X16SubSatU: + case Opcode::I16X8SubSatS: + case Opcode::I16X8SubSatU: + case Opcode::I8X16Shl: + case Opcode::I16X8Shl: + case Opcode::I32X4Shl: + case Opcode::I64X2Shl: + case Opcode::I8X16ShrS: + case Opcode::I8X16ShrU: + case Opcode::I16X8ShrS: + case Opcode::I16X8ShrU: + case Opcode::I32X4ShrS: + case Opcode::I32X4ShrU: + case Opcode::I64X2ShrS: + case Opcode::I64X2ShrU: + case Opcode::V128And: + case Opcode::V128Or: + case Opcode::V128Xor: + case Opcode::V128Not: + case Opcode::V128BitSelect: + case Opcode::V128AnyTrue: + case Opcode::I8X16Bitmask: + case Opcode::I16X8Bitmask: + case Opcode::I32X4Bitmask: + case Opcode::I64X2Bitmask: + case Opcode::I8X16AllTrue: + case Opcode::I16X8AllTrue: + case Opcode::I32X4AllTrue: + case Opcode::I64X2AllTrue: + case Opcode::I8X16Eq: + case Opcode::I16X8Eq: + case Opcode::I32X4Eq: + case Opcode::F32X4Eq: + case Opcode::F64X2Eq: + case Opcode::I8X16Ne: + case Opcode::I16X8Ne: + case Opcode::I32X4Ne: + case Opcode::F32X4Ne: + case Opcode::F64X2Ne: + case Opcode::I8X16LtS: + case Opcode::I8X16LtU: + case Opcode::I16X8LtS: + case Opcode::I16X8LtU: + case Opcode::I32X4LtS: + case Opcode::I32X4LtU: + case Opcode::F32X4Lt: + case Opcode::F64X2Lt: + case Opcode::I8X16LeS: + case Opcode::I8X16LeU: + case Opcode::I16X8LeS: + case Opcode::I16X8LeU: + case Opcode::I32X4LeS: + case Opcode::I32X4LeU: + case Opcode::F32X4Le: + case Opcode::F64X2Le: + case Opcode::I8X16GtS: + case Opcode::I8X16GtU: + case Opcode::I16X8GtS: + case Opcode::I16X8GtU: + case Opcode::I32X4GtS: + case Opcode::I32X4GtU: + case Opcode::F32X4Gt: + case Opcode::F64X2Gt: + case Opcode::I8X16GeS: + case Opcode::I8X16GeU: + case Opcode::I16X8GeS: + case Opcode::I16X8GeU: + case Opcode::I32X4GeS: + case Opcode::I32X4GeU: + case Opcode::F32X4Ge: + case Opcode::F64X2Ge: + case Opcode::F32X4Neg: + case Opcode::F64X2Neg: + case Opcode::F32X4Abs: + case Opcode::F64X2Abs: + case Opcode::F32X4Min: + case Opcode::F32X4PMin: + case Opcode::F64X2Min: + case Opcode::F64X2PMin: + case Opcode::F32X4Max: + case Opcode::F32X4PMax: + case Opcode::F64X2Max: + case Opcode::F64X2PMax: + case Opcode::F32X4Add: + case Opcode::F64X2Add: + case Opcode::F32X4Sub: + case Opcode::F64X2Sub: + case Opcode::F32X4Div: + case Opcode::F64X2Div: + case Opcode::F32X4Mul: + case Opcode::F64X2Mul: + case Opcode::F32X4Sqrt: + case Opcode::F64X2Sqrt: + case Opcode::F32X4ConvertI32X4S: + case Opcode::F32X4ConvertI32X4U: + case Opcode::I32X4TruncSatF32X4S: + case Opcode::I32X4TruncSatF32X4U: + case Opcode::I8X16Swizzle: + case Opcode::I8X16Shuffle: + case Opcode::V128Load8Splat: + case Opcode::V128Load16Splat: + case Opcode::V128Load32Splat: + case Opcode::V128Load64Splat: + case Opcode::V128Load8Lane: + case Opcode::V128Load16Lane: + case Opcode::V128Load32Lane: + case Opcode::V128Load64Lane: + case Opcode::V128Store8Lane: + case Opcode::V128Store16Lane: + case Opcode::V128Store32Lane: + case Opcode::V128Store64Lane: + case Opcode::I8X16Abs: + case Opcode::I16X8Abs: + case Opcode::I32X4Abs: + return features.simd_enabled(); + + case Opcode::I8X16RelaxedSwizzle: + case Opcode::I32X4RelaxedTruncF32X4S: + case Opcode::I32X4RelaxedTruncF32X4U: + case Opcode::I32X4RelaxedTruncF64X2SZero: + case Opcode::I32X4RelaxedTruncF64X2UZero: + case Opcode::F32X4RelaxedMadd: + case Opcode::F32X4RelaxedNmadd: + case Opcode::F64X2RelaxedMadd: + case Opcode::F64X2RelaxedNmadd: + case Opcode::I8X16RelaxedLaneSelect: + case Opcode::I16X8RelaxedLaneSelect: + case Opcode::I32X4RelaxedLaneSelect: + case Opcode::I64X2RelaxedLaneSelect: + case Opcode::F32X4RelaxedMin: + case Opcode::F32X4RelaxedMax: + case Opcode::F64X2RelaxedMin: + case Opcode::F64X2RelaxedMax: + case Opcode::I16X8RelaxedQ15mulrS: + case Opcode::I16X8DotI8X16I7X16S: + case Opcode::I32X4DotI8X16I7X16AddS: + return features.relaxed_simd_enabled(); + + case Opcode::MemoryInit: + case Opcode::DataDrop: + case Opcode::MemoryCopy: + case Opcode::MemoryFill: + case Opcode::TableInit: + case Opcode::ElemDrop: + case Opcode::TableCopy: + return features.bulk_memory_enabled(); + + case Opcode::TableGet: + case Opcode::TableSet: + case Opcode::TableGrow: + case Opcode::TableSize: + case Opcode::RefNull: + case Opcode::RefIsNull: + return features.reference_types_enabled(); + + case Opcode::CallRef: + return features.function_references_enabled(); + + // Interpreter opcodes are never "enabled". + case Opcode::InterpAlloca: + case Opcode::InterpBrUnless: + case Opcode::InterpCallImport: + case Opcode::InterpData: + case Opcode::InterpDropKeep: + return false; + + default: + return true; + } +} + +uint32_t Opcode::GetSimdLaneCount() const { + switch (enum_) { + case Opcode::I8X16ExtractLaneS: + case Opcode::I8X16ExtractLaneU: + case Opcode::I8X16ReplaceLane: + case Opcode::V128Load8Lane: + case Opcode::V128Store8Lane: + return 16; + break; + case Opcode::I16X8ExtractLaneS: + case Opcode::I16X8ExtractLaneU: + case Opcode::I16X8ReplaceLane: + case Opcode::V128Load16Lane: + case Opcode::V128Store16Lane: + return 8; + break; + case Opcode::F32X4ExtractLane: + case Opcode::F32X4ReplaceLane: + case Opcode::I32X4ExtractLane: + case Opcode::I32X4ReplaceLane: + case Opcode::V128Load32Lane: + case Opcode::V128Store32Lane: + return 4; + break; + case Opcode::F64X2ExtractLane: + case Opcode::F64X2ReplaceLane: + case Opcode::I64X2ExtractLane: + case Opcode::I64X2ReplaceLane: + case Opcode::V128Load64Lane: + case Opcode::V128Store64Lane: + return 2; + break; + default: + WABT_UNREACHABLE; + } +} + +// Get the byte sequence for this opcode, including prefix. +std::vector<uint8_t> Opcode::GetBytes() const { + std::vector<uint8_t> result; + if (HasPrefix()) { + result.push_back(GetPrefix()); + uint8_t buffer[5]; + Offset length = + WriteU32Leb128Raw(buffer, buffer + sizeof(buffer), GetCode()); + assert(length != 0); + result.insert(result.end(), buffer, buffer + length); + } else { + result.push_back(GetCode()); + } + return result; +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/option-parser.cc b/third_party/wasm2c/src/option-parser.cc new file mode 100644 index 0000000000..88fe75780b --- /dev/null +++ b/third_party/wasm2c/src/option-parser.cc @@ -0,0 +1,351 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/option-parser.h" + +#include <cstdarg> +#include <cstdio> +#include <cstring> + +#include "wabt/config.h" + +namespace wabt { + +OptionParser::Option::Option(char short_name, + const std::string& long_name, + const std::string& metavar, + HasArgument has_argument, + const std::string& help, + const Callback& callback) + : short_name(short_name), + long_name(long_name), + metavar(metavar), + has_argument(has_argument == HasArgument::Yes), + help(help), + callback(callback) {} + +OptionParser::Argument::Argument(const std::string& name, + ArgumentCount count, + const Callback& callback) + : name(name), count(count), callback(callback) {} + +OptionParser::OptionParser(const char* program_name, const char* description) + : program_name_(program_name), + description_(description), + on_error_([this](const std::string& message) { DefaultError(message); }) { + // Add common options + AddOption("help", "Print this help message", [this]() { + PrintHelp(); + exit(0); + }); + AddOption("version", "Print version information", []() { + printf("%s\n", WABT_VERSION_STRING); + exit(0); + }); +} + +void OptionParser::AddOption(const Option& option) { + options_.emplace_back(option); +} + +void OptionParser::AddArgument(const std::string& name, + ArgumentCount count, + const Callback& callback) { + arguments_.emplace_back(name, count, callback); +} + +void OptionParser::AddOption(char short_name, + const char* long_name, + const char* help, + const NullCallback& callback) { + Option option(short_name, long_name, std::string(), HasArgument::No, help, + [callback](const char*) { callback(); }); + AddOption(option); +} + +void OptionParser::AddOption(const char* long_name, + const char* help, + const NullCallback& callback) { + Option option('\0', long_name, std::string(), HasArgument::No, help, + [callback](const char*) { callback(); }); + AddOption(option); +} + +void OptionParser::AddOption(char short_name, + const char* long_name, + const char* metavar, + const char* help, + const Callback& callback) { + Option option(short_name, long_name, metavar, HasArgument::Yes, help, + callback); + AddOption(option); +} + +void OptionParser::SetErrorCallback(const Callback& callback) { + on_error_ = callback; +} + +// static +int OptionParser::Match(const char* s, + const std::string& full, + bool has_argument) { + int i; + for (i = 0;; i++) { + if (full[i] == '\0') { + // Perfect match. Return +1, so it will be preferred over a longer option + // with the same prefix. + if (s[i] == '\0') { + return i + 1; + } + + // We want to fail if s is longer than full, e.g. --foobar vs. --foo. + // However, if s ends with an '=', it's OK. + if (!(has_argument && s[i] == '=')) { + return -1; + } + break; + } + if (s[i] == '\0') { + break; + } + if (s[i] != full[i]) { + return -1; + } + } + return i; +} + +void OptionParser::Errorf(const char* format, ...) { + WABT_SNPRINTF_ALLOCA(buffer, length, format); + std::string msg(program_name_); + msg += ": "; + msg += buffer; + msg += "\nTry '--help' for more information."; + on_error_(msg.c_str()); +} + +void OptionParser::DefaultError(const std::string& message) { + WABT_FATAL("%s\n", message.c_str()); +} + +void OptionParser::HandleArgument(size_t* arg_index, const char* arg_value) { + if (*arg_index >= arguments_.size()) { + Errorf("unexpected argument '%s'", arg_value); + return; + } + Argument& argument = arguments_[*arg_index]; + argument.callback(arg_value); + argument.handled_count++; + + if (argument.count == ArgumentCount::One) { + (*arg_index)++; + } +} + +void OptionParser::Parse(int argc, char* argv[]) { + size_t arg_index = 0; + bool processing_options = true; + + for (int i = 1; i < argc; ++i) { + const char* arg = argv[i]; + if (!processing_options || arg[0] != '-') { + // Non-option argument. + HandleArgument(&arg_index, arg); + continue; + } + + if (arg[1] == '-') { + if (arg[2] == '\0') { + // -- on its own means stop processing args, everything should + // be treated as positional. + processing_options = false; + continue; + } + // Long option. + int best_index = -1; + int best_length = 0; + int best_count = 0; + for (size_t j = 0; j < options_.size(); ++j) { + const Option& option = options_[j]; + if (!option.long_name.empty()) { + int match_length = + Match(&arg[2], option.long_name, option.has_argument); + if (match_length > best_length) { + best_index = j; + best_length = match_length; + best_count = 1; + } else if (match_length == best_length && best_length > 0) { + best_count++; + } + } + } + + if (best_count > 1) { + Errorf("ambiguous option '%s'", arg); + continue; + } else if (best_count == 0) { + Errorf("unknown option '%s'", arg); + continue; + } + + const Option& best_option = options_[best_index]; + const char* option_argument = nullptr; + if (best_option.has_argument) { + if (arg[best_length + 1] != 0 && // This byte is 0 on a full match. + arg[best_length + 2] == '=') { // +2 to skip "--". + option_argument = &arg[best_length + 3]; + } else { + if (i + 1 == argc || argv[i + 1][0] == '-') { + Errorf("option '--%s' requires argument", + best_option.long_name.c_str()); + continue; + } + ++i; + option_argument = argv[i]; + } + } + best_option.callback(option_argument); + } else { + // Short option. + if (arg[1] == '\0') { + // Just "-". + HandleArgument(&arg_index, arg); + continue; + } + + // Allow short names to be combined, e.g. "-d -v" => "-dv". + for (int k = 1; arg[k]; ++k) { + bool matched = false; + for (const Option& option : options_) { + if (option.short_name && arg[k] == option.short_name) { + const char* option_argument = nullptr; + if (option.has_argument) { + // A short option with a required argument cannot be followed + // by other short options_. + if (arg[k + 1] != '\0') { + Errorf("option '-%c' requires argument", option.short_name); + break; + } + + if (i + 1 == argc || argv[i + 1][0] == '-') { + Errorf("option '-%c' requires argument", option.short_name); + break; + } + ++i; + option_argument = argv[i]; + } + option.callback(option_argument); + matched = true; + break; + } + } + + if (!matched) { + Errorf("unknown option '-%c'", arg[k]); + continue; + } + } + } + } + + // For now, all arguments must be provided. Check that the last Argument was + // handled at least once. + if (!arguments_.empty() && arguments_.back().handled_count == 0) { + for (size_t i = arg_index; i < arguments_.size(); ++i) { + if (arguments_[i].count != ArgumentCount::ZeroOrMore) { + Errorf("expected %s argument.", arguments_[i].name.c_str()); + } + } + } +} + +void OptionParser::PrintHelp() { + printf("usage: %s [options]", program_name_.c_str()); + + for (size_t i = 0; i < arguments_.size(); ++i) { + Argument& argument = arguments_[i]; + switch (argument.count) { + case ArgumentCount::One: + printf(" %s", argument.name.c_str()); + break; + + case ArgumentCount::OneOrMore: + printf(" %s+", argument.name.c_str()); + break; + + case ArgumentCount::ZeroOrMore: + printf(" [%s]...", argument.name.c_str()); + break; + } + } + + printf("\n\n"); + printf("%s\n", description_.c_str()); + printf("options:\n"); + + const size_t kExtraSpace = 8; + size_t longest_name_length = 0; + for (const Option& option : options_) { + size_t length; + if (!option.long_name.empty()) { + length = option.long_name.size(); + if (!option.metavar.empty()) { + // +1 for '='. + length += option.metavar.size() + 1; + } + } else { + continue; + } + + if (length > longest_name_length) { + longest_name_length = length; + } + } + + for (const Option& option : options_) { + if (!option.short_name && option.long_name.empty()) { + continue; + } + + std::string line; + if (option.short_name) { + line += std::string(" -") + option.short_name + ", "; + } else { + line += " "; + } + + std::string flag; + if (!option.long_name.empty()) { + flag = "--"; + if (!option.metavar.empty()) { + flag += option.long_name + '=' + option.metavar; + } else { + flag += option.long_name; + } + } + + // +2 for "--" of the long flag name. + size_t remaining = longest_name_length + kExtraSpace + 2 - flag.size(); + line += flag + std::string(remaining, ' '); + + if (!option.help.empty()) { + line += option.help; + } + printf("%s\n", line.c_str()); + } +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/prebuilt/.clang-format b/third_party/wasm2c/src/prebuilt/.clang-format new file mode 100644 index 0000000000..9d159247d5 --- /dev/null +++ b/third_party/wasm2c/src/prebuilt/.clang-format @@ -0,0 +1,2 @@ +DisableFormat: true +SortIncludes: false diff --git a/third_party/wasm2c/src/prebuilt/lexer-keywords.cc b/third_party/wasm2c/src/prebuilt/lexer-keywords.cc new file mode 100644 index 0000000000..4a7da0e06d --- /dev/null +++ b/third_party/wasm2c/src/prebuilt/lexer-keywords.cc @@ -0,0 +1,1796 @@ +/* C++ code produced by gperf version 3.1 */ +/* Command-line: gperf -m 50 -L C++ -N InWordSet -E -t -c --output-file=src/prebuilt/lexer-keywords.cc src/lexer-keywords.txt */ +/* Computed positions: -k'1,3,5-8,10,12,15,17-19,23,27,$' */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>." +#endif + +#line 1 "src/lexer-keywords.txt" +struct TokenInfo { + TokenInfo(const char* name) : name(name) {} + TokenInfo(const char* name, TokenType token_type) + : name(name), token_type(token_type) {} + TokenInfo(const char* name, Type value_type) + : name(name), token_type(TokenType::ValueType), value_type(value_type) {} + TokenInfo(const char* name, Type value_type, TokenType token_type) + : name(name), token_type(token_type), value_type(value_type) {} + TokenInfo(const char* name, TokenType token_type, Opcode opcode) + : name(name), token_type(token_type), opcode(opcode) {} + + const char* name; + TokenType token_type; + union { + Type value_type; + Opcode opcode; + }; +}; +/* maximum key range = 2423, duplicates = 0 */ + +class Perfect_Hash +{ +private: + static inline unsigned int hash (const char *str, size_t len); +public: + static struct TokenInfo *InWordSet (const char *str, size_t len); +}; + +inline unsigned int +Perfect_Hash::hash (const char *str, size_t len) +{ + static unsigned short asso_values[] = + { + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 7, 2453, 2453, 532, + 325, 8, 13, 7, 327, 314, 111, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 220, 10, 35, 678, 60, + 49, 11, 538, 7, 399, 128, 15, 34, 9, 56, + 13, 64, 766, 737, 14, 9, 19, 7, 411, 435, + 122, 393, 105, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, 2453, + 2453, 2453, 2453, 2453, 2453, 2453, 2453 + }; + unsigned int hval = len; + + switch (hval) + { + default: + hval += asso_values[static_cast<unsigned char>(str[26])]; + [[fallthrough]]; + case 26: + case 25: + case 24: + case 23: + hval += asso_values[static_cast<unsigned char>(str[22])]; + [[fallthrough]]; + case 22: + case 21: + case 20: + case 19: + hval += asso_values[static_cast<unsigned char>(str[18])]; + [[fallthrough]]; + case 18: + hval += asso_values[static_cast<unsigned char>(str[17])]; + [[fallthrough]]; + case 17: + hval += asso_values[static_cast<unsigned char>(str[16])]; + [[fallthrough]]; + case 16: + case 15: + hval += asso_values[static_cast<unsigned char>(str[14])]; + [[fallthrough]]; + case 14: + case 13: + case 12: + hval += asso_values[static_cast<unsigned char>(str[11])]; + [[fallthrough]]; + case 11: + case 10: + hval += asso_values[static_cast<unsigned char>(str[9])]; + [[fallthrough]]; + case 9: + case 8: + hval += asso_values[static_cast<unsigned char>(str[7])]; + [[fallthrough]]; + case 7: + hval += asso_values[static_cast<unsigned char>(str[6])]; + [[fallthrough]]; + case 6: + hval += asso_values[static_cast<unsigned char>(str[5])]; + [[fallthrough]]; + case 5: + hval += asso_values[static_cast<unsigned char>(str[4])]; + [[fallthrough]]; + case 4: + case 3: + hval += asso_values[static_cast<unsigned char>(str[2]+1)]; + [[fallthrough]]; + case 2: + case 1: + hval += asso_values[static_cast<unsigned char>(str[0]+1)]; + break; + } + return hval + asso_values[static_cast<unsigned char>(str[len - 1])]; +} + +struct TokenInfo * +Perfect_Hash::InWordSet (const char *str, size_t len) +{ + enum + { + TOTAL_KEYWORDS = 590, + MIN_WORD_LENGTH = 2, + MAX_WORD_LENGTH = 32, + MIN_HASH_VALUE = 30, + MAX_HASH_VALUE = 2452 + }; + + static struct TokenInfo wordlist[] = + { + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, +#line 145 "src/lexer-keywords.txt" + {"f64", Type::F64}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 456 "src/lexer-keywords.txt" + {"i64", Type::I64}, + {""}, {""}, {""}, +#line 547 "src/lexer-keywords.txt" + {"mut", TokenType::Mut}, + {""}, {""}, {""}, +#line 115 "src/lexer-keywords.txt" + {"f32x4", TokenType::F32X4}, + {""}, {""}, +#line 128 "src/lexer-keywords.txt" + {"f64.ge", TokenType::Compare, Opcode::F64Ge}, +#line 67 "src/lexer-keywords.txt" + {"f32.ge", TokenType::Compare, Opcode::F32Ge}, +#line 130 "src/lexer-keywords.txt" + {"f64.le", TokenType::Compare, Opcode::F64Le}, +#line 69 "src/lexer-keywords.txt" + {"f32.le", TokenType::Compare, Opcode::F32Le}, + {""}, +#line 356 "src/lexer-keywords.txt" + {"i32x4", TokenType::I32X4}, +#line 138 "src/lexer-keywords.txt" + {"f64.ne", TokenType::Compare, Opcode::F64Ne}, +#line 77 "src/lexer-keywords.txt" + {"f32.ne", TokenType::Compare, Opcode::F32Ne}, +#line 40 "src/lexer-keywords.txt" + {"data", TokenType::Data}, + {""}, +#line 137 "src/lexer-keywords.txt" + {"f64.neg", TokenType::Unary, Opcode::F64Neg}, +#line 76 "src/lexer-keywords.txt" + {"f32.neg", TokenType::Unary, Opcode::F32Neg}, +#line 546 "src/lexer-keywords.txt" + {"module", TokenType::Module}, +#line 565 "src/lexer-keywords.txt" + {"return", TokenType::Return, Opcode::Return}, +#line 432 "src/lexer-keywords.txt" + {"i64.ne", TokenType::Compare, Opcode::I64Ne}, +#line 289 "src/lexer-keywords.txt" + {"i32.ne", TokenType::Compare, Opcode::I32Ne}, +#line 129 "src/lexer-keywords.txt" + {"f64.gt", TokenType::Compare, Opcode::F64Gt}, +#line 68 "src/lexer-keywords.txt" + {"f32.gt", TokenType::Compare, Opcode::F32Gt}, +#line 132 "src/lexer-keywords.txt" + {"f64.lt", TokenType::Compare, Opcode::F64Lt}, +#line 71 "src/lexer-keywords.txt" + {"f32.lt", TokenType::Compare, Opcode::F32Lt}, + {""}, {""}, +#line 559 "src/lexer-keywords.txt" + {"ref.null", TokenType::RefNull, Opcode::RefNull}, +#line 93 "src/lexer-keywords.txt" + {"f32x4.ge", TokenType::Compare, Opcode::F32X4Ge}, + {""}, +#line 95 "src/lexer-keywords.txt" + {"f32x4.le", TokenType::Compare, Opcode::F32X4Le}, +#line 101 "src/lexer-keywords.txt" + {"f32x4.neg", TokenType::Unary, Opcode::F32X4Neg}, +#line 33 "src/lexer-keywords.txt" + {"br", TokenType::Br, Opcode::Br}, +#line 43 "src/lexer-keywords.txt" + {"do", TokenType::Do}, +#line 102 "src/lexer-keywords.txt" + {"f32x4.ne", TokenType::Compare, Opcode::F32X4Ne}, + {""}, {""}, +#line 561 "src/lexer-keywords.txt" + {"result", TokenType::Result}, + {""}, +#line 341 "src/lexer-keywords.txt" + {"i32x4.neg", TokenType::Unary, Opcode::I32X4Neg}, + {""}, +#line 322 "src/lexer-keywords.txt" + {"i32x4.ge_u", TokenType::Compare, Opcode::I32X4GeU}, +#line 342 "src/lexer-keywords.txt" + {"i32x4.ne", TokenType::Compare, Opcode::I32X4Ne}, +#line 326 "src/lexer-keywords.txt" + {"i32x4.le_u", TokenType::Compare, Opcode::I32X4LeU}, +#line 94 "src/lexer-keywords.txt" + {"f32x4.gt", TokenType::Compare, Opcode::F32X4Gt}, +#line 321 "src/lexer-keywords.txt" + {"i32x4.ge_s", TokenType::Compare, Opcode::I32X4GeS}, +#line 96 "src/lexer-keywords.txt" + {"f32x4.lt", TokenType::Compare, Opcode::F32X4Lt}, +#line 325 "src/lexer-keywords.txt" + {"i32x4.le_s", TokenType::Compare, Opcode::I32X4LeS}, + {""}, +#line 324 "src/lexer-keywords.txt" + {"i32x4.gt_u", TokenType::Compare, Opcode::I32X4GtU}, +#line 577 "src/lexer-keywords.txt" + {"table", TokenType::Table}, +#line 334 "src/lexer-keywords.txt" + {"i32x4.lt_u", TokenType::Compare, Opcode::I32X4LtU}, + {""}, +#line 323 "src/lexer-keywords.txt" + {"i32x4.gt_s", TokenType::Compare, Opcode::I32X4GtS}, + {""}, +#line 333 "src/lexer-keywords.txt" + {"i32x4.lt_s", TokenType::Compare, Opcode::I32X4LtS}, + {""}, {""}, +#line 135 "src/lexer-keywords.txt" + {"f64.mul", TokenType::Binary, Opcode::F64Mul}, +#line 74 "src/lexer-keywords.txt" + {"f32.mul", TokenType::Binary, Opcode::F32Mul}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 431 "src/lexer-keywords.txt" + {"i64.mul", TokenType::Binary, Opcode::I64Mul}, +#line 288 "src/lexer-keywords.txt" + {"i32.mul", TokenType::Binary, Opcode::I32Mul}, + {""}, {""}, +#line 100 "src/lexer-keywords.txt" + {"f32x4.nearest", TokenType::Unary, Opcode::F32X4Nearest}, + {""}, +#line 99 "src/lexer-keywords.txt" + {"f32x4.mul", TokenType::Binary, Opcode::F32X4Mul}, + {""}, +#line 36 "src/lexer-keywords.txt" + {"call", TokenType::Call, Opcode::Call}, + {""}, +#line 433 "src/lexer-keywords.txt" + {"i64.or", TokenType::Binary, Opcode::I64Or}, +#line 290 "src/lexer-keywords.txt" + {"i32.or", TokenType::Binary, Opcode::I32Or}, + {""}, {""}, +#line 340 "src/lexer-keywords.txt" + {"i32x4.mul", TokenType::Binary, Opcode::I32X4Mul}, + {""}, +#line 136 "src/lexer-keywords.txt" + {"f64.nearest", TokenType::Unary, Opcode::F64Nearest}, +#line 75 "src/lexer-keywords.txt" + {"f32.nearest", TokenType::Unary, Opcode::F32Nearest}, +#line 535 "src/lexer-keywords.txt" + {"local", TokenType::Local}, + {""}, {""}, +#line 572 "src/lexer-keywords.txt" + {"table.get", TokenType::TableGet, Opcode::TableGet}, +#line 569 "src/lexer-keywords.txt" + {"struct", Type::Struct, TokenType::Struct}, +#line 575 "src/lexer-keywords.txt" + {"table.set", TokenType::TableSet, Opcode::TableSet}, +#line 86 "src/lexer-keywords.txt" + {"f32x4.ceil", TokenType::Unary, Opcode::F32X4Ceil}, +#line 180 "src/lexer-keywords.txt" + {"func", Type::FuncRef, TokenType::Func}, +#line 144 "src/lexer-keywords.txt" + {"f64.trunc", TokenType::Unary, Opcode::F64Trunc}, +#line 82 "src/lexer-keywords.txt" + {"f32.trunc", TokenType::Unary, Opcode::F32Trunc}, +#line 41 "src/lexer-keywords.txt" + {"declare", TokenType::Declare}, + {""}, +#line 142 "src/lexer-keywords.txt" + {"f64.store", TokenType::Store, Opcode::F64Store}, +#line 80 "src/lexer-keywords.txt" + {"f32.store", TokenType::Store, Opcode::F32Store}, + {""}, {""}, {""}, +#line 438 "src/lexer-keywords.txt" + {"i64.rotl", TokenType::Binary, Opcode::I64Rotl}, +#line 295 "src/lexer-keywords.txt" + {"i32.rotl", TokenType::Binary, Opcode::I32Rotl}, + {""}, +#line 446 "src/lexer-keywords.txt" + {"i64.store", TokenType::Store, Opcode::I64Store}, +#line 302 "src/lexer-keywords.txt" + {"i32.store", TokenType::Store, Opcode::I32Store}, + {""}, {""}, +#line 113 "src/lexer-keywords.txt" + {"f32x4.trunc", TokenType::Unary, Opcode::F32X4Trunc}, + {""}, {""}, +#line 439 "src/lexer-keywords.txt" + {"i64.rotr", TokenType::Binary, Opcode::I64Rotr}, +#line 296 "src/lexer-keywords.txt" + {"i32.rotr", TokenType::Binary, Opcode::I32Rotr}, + {""}, +#line 42 "src/lexer-keywords.txt" + {"delegate", TokenType::Delegate}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 532 "src/lexer-keywords.txt" + {"local.get", TokenType::LocalGet, Opcode::LocalGet}, + {""}, +#line 533 "src/lexer-keywords.txt" + {"local.set", TokenType::LocalSet, Opcode::LocalSet}, + {""}, +#line 534 "src/lexer-keywords.txt" + {"local.tee", TokenType::LocalTee, Opcode::LocalTee}, + {""}, {""}, {""}, +#line 367 "src/lexer-keywords.txt" + {"i64.and", TokenType::Binary, Opcode::I64And}, +#line 238 "src/lexer-keywords.txt" + {"i32.and", TokenType::Binary, Opcode::I32And}, +#line 85 "src/lexer-keywords.txt" + {"f32x4.add", TokenType::Binary, Opcode::F32X4Add}, + {""}, +#line 566 "src/lexer-keywords.txt" + {"select", TokenType::Select, Opcode::Select}, + {""}, {""}, {""}, {""}, {""}, +#line 315 "src/lexer-keywords.txt" + {"i32x4.add", TokenType::Binary, Opcode::I32X4Add}, + {""}, {""}, +#line 119 "src/lexer-keywords.txt" + {"f64.const", TokenType::Const, Opcode::F64Const}, +#line 57 "src/lexer-keywords.txt" + {"f32.const", TokenType::Const, Opcode::F32Const}, + {""}, +#line 109 "src/lexer-keywords.txt" + {"f32x4.replace_lane", TokenType::SimdLaneOp, Opcode::F32X4ReplaceLane}, + {""}, {""}, {""}, {""}, +#line 405 "src/lexer-keywords.txt" + {"i64.const", TokenType::Const, Opcode::I64Const}, +#line 267 "src/lexer-keywords.txt" + {"i32.const", TokenType::Const, Opcode::I32Const}, + {""}, +#line 344 "src/lexer-keywords.txt" + {"i32x4.replace_lane", TokenType::SimdLaneOp, Opcode::I32X4ReplaceLane}, + {""}, {""}, {""}, +#line 117 "src/lexer-keywords.txt" + {"f64.add", TokenType::Binary, Opcode::F64Add}, +#line 55 "src/lexer-keywords.txt" + {"f32.add", TokenType::Binary, Opcode::F32Add}, +#line 107 "src/lexer-keywords.txt" + {"f32x4.relaxed_min", TokenType::Binary, Opcode::F32X4RelaxedMin}, + {""}, {""}, {""}, {""}, {""}, +#line 366 "src/lexer-keywords.txt" + {"i64.add", TokenType::Binary, Opcode::I64Add}, +#line 237 "src/lexer-keywords.txt" + {"i32.add", TokenType::Binary, Opcode::I32Add}, + {""}, {""}, +#line 556 "src/lexer-keywords.txt" + {"ref.extern", TokenType::RefExtern}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, +#line 131 "src/lexer-keywords.txt" + {"f64.load", TokenType::Load, Opcode::F64Load}, +#line 70 "src/lexer-keywords.txt" + {"f32.load", TokenType::Load, Opcode::F32Load}, + {""}, +#line 134 "src/lexer-keywords.txt" + {"f64.min", TokenType::Binary, Opcode::F64Min}, +#line 73 "src/lexer-keywords.txt" + {"f32.min", TokenType::Binary, Opcode::F32Min}, + {""}, {""}, {""}, +#line 428 "src/lexer-keywords.txt" + {"i64.load", TokenType::Load, Opcode::I64Load}, +#line 285 "src/lexer-keywords.txt" + {"i32.load", TokenType::Load, Opcode::I32Load}, + {""}, +#line 118 "src/lexer-keywords.txt" + {"f64.ceil", TokenType::Unary, Opcode::F64Ceil}, +#line 56 "src/lexer-keywords.txt" + {"f32.ceil", TokenType::Unary, Opcode::F32Ceil}, +#line 98 "src/lexer-keywords.txt" + {"f32x4.min", TokenType::Binary, Opcode::F32X4Min}, + {""}, +#line 488 "src/lexer-keywords.txt" + {"i64.xor", TokenType::Binary, Opcode::I64Xor}, +#line 365 "src/lexer-keywords.txt" + {"i32.xor", TokenType::Binary, Opcode::I32Xor}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 576 "src/lexer-keywords.txt" + {"table.size", TokenType::TableSize, Opcode::TableSize}, + {""}, {""}, +#line 97 "src/lexer-keywords.txt" + {"f32x4.max", TokenType::Binary, Opcode::F32X4Max}, + {""}, +#line 343 "src/lexer-keywords.txt" + {"i32x4.relaxed_laneselect", TokenType::Ternary, Opcode::I32X4RelaxedLaneSelect}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, +#line 574 "src/lexer-keywords.txt" + {"table.init", TokenType::TableInit, Opcode::TableInit}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 417 "src/lexer-keywords.txt" + {"i64.ge_u", TokenType::Compare, Opcode::I64GeU}, +#line 276 "src/lexer-keywords.txt" + {"i32.ge_u", TokenType::Compare, Opcode::I32GeU}, +#line 421 "src/lexer-keywords.txt" + {"i64.le_u", TokenType::Compare, Opcode::I64LeU}, +#line 280 "src/lexer-keywords.txt" + {"i32.le_u", TokenType::Compare, Opcode::I32LeU}, +#line 416 "src/lexer-keywords.txt" + {"i64.ge_s", TokenType::Compare, Opcode::I64GeS}, +#line 275 "src/lexer-keywords.txt" + {"i32.ge_s", TokenType::Compare, Opcode::I32GeS}, +#line 420 "src/lexer-keywords.txt" + {"i64.le_s", TokenType::Compare, Opcode::I64LeS}, +#line 279 "src/lexer-keywords.txt" + {"i32.le_s", TokenType::Compare, Opcode::I32LeS}, +#line 419 "src/lexer-keywords.txt" + {"i64.gt_u", TokenType::Compare, Opcode::I64GtU}, +#line 278 "src/lexer-keywords.txt" + {"i32.gt_u", TokenType::Compare, Opcode::I32GtU}, +#line 430 "src/lexer-keywords.txt" + {"i64.lt_u", TokenType::Compare, Opcode::I64LtU}, +#line 287 "src/lexer-keywords.txt" + {"i32.lt_u", TokenType::Compare, Opcode::I32LtU}, +#line 418 "src/lexer-keywords.txt" + {"i64.gt_s", TokenType::Compare, Opcode::I64GtS}, +#line 277 "src/lexer-keywords.txt" + {"i32.gt_s", TokenType::Compare, Opcode::I32GtS}, +#line 429 "src/lexer-keywords.txt" + {"i64.lt_s", TokenType::Compare, Opcode::I64LtS}, +#line 286 "src/lexer-keywords.txt" + {"i32.lt_s", TokenType::Compare, Opcode::I32LtS}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 91 "src/lexer-keywords.txt" + {"f32x4.extract_lane", TokenType::SimdLaneOp, Opcode::F32X4ExtractLane}, + {""}, +#line 403 "src/lexer-keywords.txt" + {"i64.atomic.store", TokenType::AtomicStore, Opcode::I64AtomicStore}, +#line 265 "src/lexer-keywords.txt" + {"i32.atomic.store", TokenType::AtomicStore, Opcode::I32AtomicStore}, +#line 404 "src/lexer-keywords.txt" + {"i64.clz", TokenType::Unary, Opcode::I64Clz}, +#line 266 "src/lexer-keywords.txt" + {"i32.clz", TokenType::Unary, Opcode::I32Clz}, + {""}, {""}, +#line 320 "src/lexer-keywords.txt" + {"i32x4.extract_lane", TokenType::SimdLaneOp, Opcode::I32X4ExtractLane}, + {""}, {""}, {""}, {""}, {""}, +#line 406 "src/lexer-keywords.txt" + {"i64.ctz", TokenType::Unary, Opcode::I64Ctz}, +#line 268 "src/lexer-keywords.txt" + {"i32.ctz", TokenType::Unary, Opcode::I32Ctz}, +#line 108 "src/lexer-keywords.txt" + {"f32x4.relaxed_nmadd", TokenType::Ternary, Opcode::F32X4RelaxedNmadd}, + {""}, +#line 396 "src/lexer-keywords.txt" + {"i64.atomic.rmw.or", TokenType::AtomicRmw, Opcode::I64AtomicRmwOr}, +#line 259 "src/lexer-keywords.txt" + {"i32.atomic.rmw.or", TokenType::AtomicRmw, Opcode::I32AtomicRmwOr}, + {""}, {""}, {""}, +#line 105 "src/lexer-keywords.txt" + {"f32x4.relaxed_madd", TokenType::Ternary, Opcode::F32X4RelaxedMadd}, + {""}, +#line 330 "src/lexer-keywords.txt" + {"i32x4.relaxed_trunc_f64x2_u_zero", TokenType::Unary, Opcode::I32X4RelaxedTruncF64X2UZero}, +#line 383 "src/lexer-keywords.txt" + {"i64.atomic.rmw32.sub_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32SubU}, +#line 329 "src/lexer-keywords.txt" + {"i32x4.relaxed_trunc_f64x2_s_zero", TokenType::Unary, Opcode::I32X4RelaxedTruncF64X2SZero}, +#line 449 "src/lexer-keywords.txt" + {"i64.trunc_f32_u", TokenType::Convert, Opcode::I64TruncF32U}, +#line 305 "src/lexer-keywords.txt" + {"i32.trunc_f32_u", TokenType::Convert, Opcode::I32TruncF32U}, + {""}, {""}, +#line 448 "src/lexer-keywords.txt" + {"i64.trunc_f32_s", TokenType::Convert, Opcode::I64TruncF32S}, +#line 304 "src/lexer-keywords.txt" + {"i32.trunc_f32_s", TokenType::Convert, Opcode::I32TruncF32S}, + {""}, +#line 437 "src/lexer-keywords.txt" + {"i64.rem_u", TokenType::Binary, Opcode::I64RemU}, +#line 294 "src/lexer-keywords.txt" + {"i32.rem_u", TokenType::Binary, Opcode::I32RemU}, +#line 436 "src/lexer-keywords.txt" + {"i64.rem_s", TokenType::Binary, Opcode::I64RemS}, +#line 293 "src/lexer-keywords.txt" + {"i32.rem_s", TokenType::Binary, Opcode::I32RemS}, +#line 83 "src/lexer-keywords.txt" + {"f32", Type::F32}, + {""}, {""}, +#line 316 "src/lexer-keywords.txt" + {"i32x4.all_true", TokenType::Unary, Opcode::I32X4AllTrue}, + {""}, {""}, {""}, {""}, +#line 312 "src/lexer-keywords.txt" + {"i32", Type::I32}, +#line 564 "src/lexer-keywords.txt" + {"return_call", TokenType::ReturnCall, Opcode::ReturnCall}, + {""}, {""}, {""}, +#line 133 "src/lexer-keywords.txt" + {"f64.max", TokenType::Binary, Opcode::F64Max}, +#line 72 "src/lexer-keywords.txt" + {"f32.max", TokenType::Binary, Opcode::F32Max}, + {""}, {""}, +#line 445 "src/lexer-keywords.txt" + {"i64.store8", TokenType::Store, Opcode::I64Store8}, +#line 301 "src/lexer-keywords.txt" + {"i32.store8", TokenType::Store, Opcode::I32Store8}, +#line 380 "src/lexer-keywords.txt" + {"i64.atomic.rmw32.and_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32AndU}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 336 "src/lexer-keywords.txt" + {"i32x4.max_u", TokenType::Binary, Opcode::I32X4MaxU}, + {""}, +#line 335 "src/lexer-keywords.txt" + {"i32x4.max_s", TokenType::Binary, Opcode::I32X4MaxS}, + {""}, {""}, {""}, +#line 371 "src/lexer-keywords.txt" + {"i64.atomic.load", TokenType::AtomicLoad, Opcode::I64AtomicLoad}, +#line 241 "src/lexer-keywords.txt" + {"i32.atomic.load", TokenType::AtomicLoad, Opcode::I32AtomicLoad}, + {""}, {""}, {""}, +#line 153 "src/lexer-keywords.txt" + {"f64x2.ge", TokenType::Compare, Opcode::F64X2Ge}, + {""}, +#line 155 "src/lexer-keywords.txt" + {"f64x2.le", TokenType::Compare, Opcode::F64X2Le}, +#line 161 "src/lexer-keywords.txt" + {"f64x2.neg", TokenType::Unary, Opcode::F64X2Neg}, +#line 399 "src/lexer-keywords.txt" + {"i64.atomic.rmw.xor", TokenType::AtomicRmw, Opcode::I64AtomicRmwXor}, +#line 262 "src/lexer-keywords.txt" + {"i32.atomic.rmw.xor", TokenType::AtomicRmw, Opcode::I32AtomicRmwXor}, +#line 162 "src/lexer-keywords.txt" + {"f64x2.ne", TokenType::Compare, Opcode::F64X2Ne}, + {""}, +#line 382 "src/lexer-keywords.txt" + {"i64.atomic.rmw32.or_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32OrU}, + {""}, {""}, +#line 469 "src/lexer-keywords.txt" + {"i64x2.neg", TokenType::Unary, Opcode::I64X2Neg}, + {""}, {""}, +#line 463 "src/lexer-keywords.txt" + {"i64x2.ne", TokenType::Binary, Opcode::I64X2Ne}, +#line 379 "src/lexer-keywords.txt" + {"i64.atomic.rmw32.add_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32AddU}, +#line 154 "src/lexer-keywords.txt" + {"f64x2.gt", TokenType::Compare, Opcode::F64X2Gt}, +#line 467 "src/lexer-keywords.txt" + {"i64x2.ge_s", TokenType::Binary, Opcode::I64X2GeS}, +#line 156 "src/lexer-keywords.txt" + {"f64x2.lt", TokenType::Compare, Opcode::F64X2Lt}, +#line 466 "src/lexer-keywords.txt" + {"i64x2.le_s", TokenType::Binary, Opcode::I64X2LeS}, + {""}, {""}, {""}, +#line 394 "src/lexer-keywords.txt" + {"i64.atomic.rmw.and", TokenType::AtomicRmw, Opcode::I64AtomicRmwAnd}, +#line 257 "src/lexer-keywords.txt" + {"i32.atomic.rmw.and", TokenType::AtomicRmw, Opcode::I32AtomicRmwAnd}, +#line 465 "src/lexer-keywords.txt" + {"i64x2.gt_s", TokenType::Binary, Opcode::I64X2GtS}, + {""}, +#line 464 "src/lexer-keywords.txt" + {"i64x2.lt_s", TokenType::Binary, Opcode::I64X2LtS}, + {""}, {""}, {""}, {""}, +#line 558 "src/lexer-keywords.txt" + {"ref.is_null", TokenType::RefIsNull, Opcode::RefIsNull}, +#line 50 "src/lexer-keywords.txt" + {"tag", TokenType::Tag}, +#line 427 "src/lexer-keywords.txt" + {"i64.load8_u", TokenType::Load, Opcode::I64Load8U}, +#line 284 "src/lexer-keywords.txt" + {"i32.load8_u", TokenType::Load, Opcode::I32Load8U}, +#line 426 "src/lexer-keywords.txt" + {"i64.load8_s", TokenType::Load, Opcode::I64Load8S}, +#line 283 "src/lexer-keywords.txt" + {"i32.load8_s", TokenType::Load, Opcode::I32Load8S}, + {""}, {""}, +#line 106 "src/lexer-keywords.txt" + {"f32x4.relaxed_max", TokenType::Binary, Opcode::F32X4RelaxedMax}, + {""}, +#line 160 "src/lexer-keywords.txt" + {"f64x2.nearest", TokenType::Unary, Opcode::F64X2Nearest}, + {""}, +#line 159 "src/lexer-keywords.txt" + {"f64x2.mul", TokenType::Binary, Opcode::F64X2Mul}, +#line 181 "src/lexer-keywords.txt" + {"get", TokenType::Get}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 461 "src/lexer-keywords.txt" + {"i64x2.mul", TokenType::Binary, Opcode::I64X2Mul}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 393 "src/lexer-keywords.txt" + {"i64.atomic.rmw.add", TokenType::AtomicRmw, Opcode::I64AtomicRmwAdd}, +#line 256 "src/lexer-keywords.txt" + {"i32.atomic.rmw.add", TokenType::AtomicRmw, Opcode::I32AtomicRmwAdd}, + {""}, +#line 148 "src/lexer-keywords.txt" + {"f64x2.ceil", TokenType::Unary, Opcode::F64X2Ceil}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 440 "src/lexer-keywords.txt" + {"i64.shl", TokenType::Binary, Opcode::I64Shl}, +#line 297 "src/lexer-keywords.txt" + {"i32.shl", TokenType::Binary, Opcode::I32Shl}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 173 "src/lexer-keywords.txt" + {"f64x2.trunc", TokenType::Unary, Opcode::F64X2Trunc}, + {""}, +#line 338 "src/lexer-keywords.txt" + {"i32x4.min_u", TokenType::Binary, Opcode::I32X4MinU}, + {""}, +#line 337 "src/lexer-keywords.txt" + {"i32x4.min_s", TokenType::Binary, Opcode::I32X4MinS}, + {""}, +#line 345 "src/lexer-keywords.txt" + {"i32x4.shl", TokenType::Binary, Opcode::I32X4Shl}, + {""}, {""}, +#line 444 "src/lexer-keywords.txt" + {"i64.store32", TokenType::Store, Opcode::I64Store32}, +#line 548 "src/lexer-keywords.txt" + {"nan:arithmetic", TokenType::NanArithmetic}, + {""}, {""}, +#line 413 "src/lexer-keywords.txt" + {"i64.extend8_s", TokenType::Unary, Opcode::I64Extend8S}, +#line 274 "src/lexer-keywords.txt" + {"i32.extend8_s", TokenType::Unary, Opcode::I32Extend8S}, + {""}, {""}, {""}, {""}, {""}, +#line 560 "src/lexer-keywords.txt" + {"register", TokenType::Register}, + {""}, +#line 549 "src/lexer-keywords.txt" + {"nan:canonical", TokenType::NanCanonical}, + {""}, +#line 213 "src/lexer-keywords.txt" + {"i16x8.neg", TokenType::Unary, Opcode::I16X8Neg}, +#line 147 "src/lexer-keywords.txt" + {"f64x2.add", TokenType::Binary, Opcode::F64X2Add}, +#line 197 "src/lexer-keywords.txt" + {"i16x8.ge_u", TokenType::Compare, Opcode::I16X8GeU}, +#line 215 "src/lexer-keywords.txt" + {"i16x8.ne", TokenType::Compare, Opcode::I16X8Ne}, +#line 201 "src/lexer-keywords.txt" + {"i16x8.le_u", TokenType::Compare, Opcode::I16X8LeU}, + {""}, +#line 196 "src/lexer-keywords.txt" + {"i16x8.ge_s", TokenType::Compare, Opcode::I16X8GeS}, + {""}, +#line 200 "src/lexer-keywords.txt" + {"i16x8.le_s", TokenType::Compare, Opcode::I16X8LeS}, +#line 457 "src/lexer-keywords.txt" + {"i64x2.add", TokenType::Binary, Opcode::I64X2Add}, +#line 199 "src/lexer-keywords.txt" + {"i16x8.gt_u", TokenType::Compare, Opcode::I16X8GtU}, + {""}, +#line 205 "src/lexer-keywords.txt" + {"i16x8.lt_u", TokenType::Compare, Opcode::I16X8LtU}, +#line 385 "src/lexer-keywords.txt" + {"i64.atomic.rmw32.xor_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32XorU}, +#line 198 "src/lexer-keywords.txt" + {"i16x8.gt_s", TokenType::Compare, Opcode::I16X8GtS}, +#line 169 "src/lexer-keywords.txt" + {"f64x2.replace_lane", TokenType::SimdLaneOp, Opcode::F64X2ReplaceLane}, +#line 204 "src/lexer-keywords.txt" + {"i16x8.lt_s", TokenType::Compare, Opcode::I16X8LtS}, +#line 384 "src/lexer-keywords.txt" + {"i64.atomic.rmw32.xchg_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32XchgU}, + {""}, +#line 387 "src/lexer-keywords.txt" + {"i64.atomic.rmw8.and_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8AndU}, +#line 250 "src/lexer-keywords.txt" + {"i32.atomic.rmw8.and_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8AndU}, +#line 580 "src/lexer-keywords.txt" + {"try", TokenType::Try, Opcode::Try}, + {""}, +#line 477 "src/lexer-keywords.txt" + {"i64x2.replace_lane", TokenType::SimdLaneOp, Opcode::I64X2ReplaceLane}, + {""}, +#line 530 "src/lexer-keywords.txt" + {"invoke", TokenType::Invoke}, + {""}, {""}, {""}, +#line 167 "src/lexer-keywords.txt" + {"f64x2.relaxed_min", TokenType::Binary, Opcode::F64X2RelaxedMin}, + {""}, +#line 402 "src/lexer-keywords.txt" + {"i64.atomic.store8", TokenType::AtomicStore, Opcode::I64AtomicStore8}, +#line 264 "src/lexer-keywords.txt" + {"i32.atomic.store8", TokenType::AtomicStore, Opcode::I32AtomicStore8}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 210 "src/lexer-keywords.txt" + {"i16x8.mul", TokenType::Binary, Opcode::I16X8Mul}, + {""}, +#line 425 "src/lexer-keywords.txt" + {"i64.load32_u", TokenType::Load, Opcode::I64Load32U}, + {""}, +#line 423 "src/lexer-keywords.txt" + {"i64.load16_u", TokenType::Load, Opcode::I64Load16U}, +#line 282 "src/lexer-keywords.txt" + {"i32.load16_u", TokenType::Load, Opcode::I32Load16U}, +#line 424 "src/lexer-keywords.txt" + {"i64.load32_s", TokenType::Load, Opcode::I64Load32S}, + {""}, +#line 422 "src/lexer-keywords.txt" + {"i64.load16_s", TokenType::Load, Opcode::I64Load16S}, +#line 281 "src/lexer-keywords.txt" + {"i32.load16_s", TokenType::Load, Opcode::I32Load16S}, + {""}, {""}, {""}, {""}, +#line 386 "src/lexer-keywords.txt" + {"i64.atomic.rmw8.add_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8AddU}, +#line 249 "src/lexer-keywords.txt" + {"i32.atomic.rmw8.add_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8AddU}, + {""}, {""}, {""}, {""}, {""}, +#line 589 "src/lexer-keywords.txt" + {"v128.or", TokenType::Binary, Opcode::V128Or}, + {""}, {""}, +#line 158 "src/lexer-keywords.txt" + {"f64x2.min", TokenType::Binary, Opcode::F64X2Min}, + {""}, {""}, +#line 527 "src/lexer-keywords.txt" + {"if", TokenType::If, Opcode::If}, +#line 232 "src/lexer-keywords.txt" + {"i16x8", TokenType::I16X8}, +#line 554 "src/lexer-keywords.txt" + {"ref", TokenType::Ref}, +#line 594 "src/lexer-keywords.txt" + {"v128", Type::V128}, + {""}, {""}, {""}, +#line 578 "src/lexer-keywords.txt" + {"then", TokenType::Then}, + {""}, {""}, {""}, +#line 370 "src/lexer-keywords.txt" + {"i64.atomic.load8_u", TokenType::AtomicLoad, Opcode::I64AtomicLoad8U}, +#line 240 "src/lexer-keywords.txt" + {"i32.atomic.load8_u", TokenType::AtomicLoad, Opcode::I32AtomicLoad8U}, +#line 157 "src/lexer-keywords.txt" + {"f64x2.max", TokenType::Binary, Opcode::F64X2Max}, + {""}, +#line 476 "src/lexer-keywords.txt" + {"i64x2.relaxed_laneselect", TokenType::Ternary, Opcode::I64X2RelaxedLaneSelect}, + {""}, +#line 48 "src/lexer-keywords.txt" + {"else", TokenType::Else, Opcode::Else}, +#line 588 "src/lexer-keywords.txt" + {"v128.not", TokenType::Unary, Opcode::V128Not}, +#line 593 "src/lexer-keywords.txt" + {"v128.store", TokenType::Store, Opcode::V128Store}, + {""}, +#line 544 "src/lexer-keywords.txt" + {"memory.size", TokenType::MemorySize, Opcode::MemorySize}, + {""}, {""}, +#line 328 "src/lexer-keywords.txt" + {"i32x4.relaxed_trunc_f32x4_u", TokenType::Unary, Opcode::I32X4RelaxedTruncF32X4U}, + {""}, {""}, +#line 412 "src/lexer-keywords.txt" + {"i64.extend32_s", TokenType::Unary, Opcode::I64Extend32S}, +#line 327 "src/lexer-keywords.txt" + {"i32x4.relaxed_trunc_f32x4_s", TokenType::Unary, Opcode::I32X4RelaxedTruncF32X4S}, +#line 411 "src/lexer-keywords.txt" + {"i64.extend16_s", TokenType::Unary, Opcode::I64Extend16S}, +#line 273 "src/lexer-keywords.txt" + {"i32.extend16_s", TokenType::Unary, Opcode::I32Extend16S}, + {""}, {""}, {""}, +#line 188 "src/lexer-keywords.txt" + {"i16x8.add", TokenType::Binary, Opcode::I16X8Add}, +#line 45 "src/lexer-keywords.txt" + {"either", TokenType::Either}, +#line 51 "src/lexer-keywords.txt" + {"extern", Type::ExternRef, TokenType::Extern}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 49 "src/lexer-keywords.txt" + {"end", TokenType::End, Opcode::End}, + {""}, +#line 218 "src/lexer-keywords.txt" + {"i16x8.replace_lane", TokenType::SimdLaneOp, Opcode::I16X8ReplaceLane}, +#line 584 "src/lexer-keywords.txt" + {"v128.and", TokenType::Binary, Opcode::V128And}, +#line 415 "src/lexer-keywords.txt" + {"i64.extend_i32_u", TokenType::Convert, Opcode::I64ExtendI32U}, + {""}, +#line 414 "src/lexer-keywords.txt" + {"i64.extend_i32_s", TokenType::Convert, Opcode::I64ExtendI32S}, + {""}, +#line 392 "src/lexer-keywords.txt" + {"i64.atomic.rmw8.xor_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8XorU}, +#line 255 "src/lexer-keywords.txt" + {"i32.atomic.rmw8.xor_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8XorU}, + {""}, {""}, +#line 531 "src/lexer-keywords.txt" + {"item", TokenType::Item}, + {""}, +#line 151 "src/lexer-keywords.txt" + {"f64x2.extract_lane", TokenType::SimdLaneOp, Opcode::F64X2ExtractLane}, +#line 587 "src/lexer-keywords.txt" + {"v128.load", TokenType::Load, Opcode::V128Load}, + {""}, {""}, {""}, {""}, +#line 34 "src/lexer-keywords.txt" + {"call_indirect", TokenType::CallIndirect, Opcode::CallIndirect}, + {""}, +#line 458 "src/lexer-keywords.txt" + {"i64x2.extract_lane", TokenType::SimdLaneOp, Opcode::I64X2ExtractLane}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 168 "src/lexer-keywords.txt" + {"f64x2.relaxed_nmadd", TokenType::Ternary, Opcode::F64X2RelaxedNmadd}, +#line 89 "src/lexer-keywords.txt" + {"f32x4.div", TokenType::Binary, Opcode::F32X4Div}, + {""}, {""}, +#line 586 "src/lexer-keywords.txt" + {"v128.const", TokenType::Const, Opcode::V128Const}, + {""}, {""}, +#line 165 "src/lexer-keywords.txt" + {"f64x2.relaxed_madd", TokenType::Ternary, Opcode::F64X2RelaxedMadd}, + {""}, +#line 607 "src/lexer-keywords.txt" + {"v128.store64_lane", TokenType::SimdStoreLane, Opcode::V128Store64Lane}, +#line 583 "src/lexer-keywords.txt" + {"v128.andnot", TokenType::Binary, Opcode::V128Andnot}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 178 "src/lexer-keywords.txt" + {"field", TokenType::Field}, + {""}, {""}, +#line 451 "src/lexer-keywords.txt" + {"i64.trunc_f64_u", TokenType::Convert, Opcode::I64TruncF64U}, +#line 307 "src/lexer-keywords.txt" + {"i32.trunc_f64_u", TokenType::Convert, Opcode::I32TruncF64U}, + {""}, {""}, +#line 450 "src/lexer-keywords.txt" + {"i64.trunc_f64_s", TokenType::Convert, Opcode::I64TruncF64S}, +#line 306 "src/lexer-keywords.txt" + {"i32.trunc_f64_s", TokenType::Convert, Opcode::I32TruncF64S}, +#line 470 "src/lexer-keywords.txt" + {"i64x2.all_true", TokenType::Unary, Opcode::I64X2AllTrue}, + {""}, {""}, {""}, {""}, {""}, +#line 216 "src/lexer-keywords.txt" + {"i16x8.relaxed_laneselect", TokenType::Ternary, Opcode::I16X8RelaxedLaneSelect}, + {""}, {""}, {""}, {""}, {""}, +#line 177 "src/lexer-keywords.txt" + {"f64x2", TokenType::F64X2}, + {""}, +#line 92 "src/lexer-keywords.txt" + {"f32x4.floor", TokenType::Unary, Opcode::F32X4Floor}, +#line 595 "src/lexer-keywords.txt" + {"v128.xor", TokenType::Binary, Opcode::V128Xor}, + {""}, {""}, {""}, {""}, +#line 487 "src/lexer-keywords.txt" + {"i64x2", TokenType::I64X2}, + {""}, {""}, +#line 442 "src/lexer-keywords.txt" + {"i64.shr_u", TokenType::Binary, Opcode::I64ShrU}, +#line 299 "src/lexer-keywords.txt" + {"i32.shr_u", TokenType::Binary, Opcode::I32ShrU}, +#line 441 "src/lexer-keywords.txt" + {"i64.shr_s", TokenType::Binary, Opcode::I64ShrS}, +#line 298 "src/lexer-keywords.txt" + {"i32.shr_s", TokenType::Binary, Opcode::I32ShrS}, + {""}, {""}, {""}, {""}, {""}, +#line 347 "src/lexer-keywords.txt" + {"i32x4.shr_u", TokenType::Binary, Opcode::I32X4ShrU}, + {""}, +#line 346 "src/lexer-keywords.txt" + {"i32x4.shr_s", TokenType::Binary, Opcode::I32X4ShrS}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, +#line 557 "src/lexer-keywords.txt" + {"ref.func", TokenType::RefFunc, Opcode::RefFunc}, + {""}, {""}, {""}, +#line 389 "src/lexer-keywords.txt" + {"i64.atomic.rmw8.or_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8OrU}, +#line 252 "src/lexer-keywords.txt" + {"i32.atomic.rmw8.or_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8OrU}, + {""}, {""}, {""}, {""}, +#line 127 "src/lexer-keywords.txt" + {"f64.floor", TokenType::Unary, Opcode::F64Floor}, +#line 66 "src/lexer-keywords.txt" + {"f32.floor", TokenType::Unary, Opcode::F32Floor}, + {""}, +#line 600 "src/lexer-keywords.txt" + {"v128.load8_lane", TokenType::SimdLoadLane, Opcode::V128Load8Lane}, + {""}, +#line 460 "src/lexer-keywords.txt" + {"v128.load32x2_u", TokenType::Load, Opcode::V128Load32X2U}, + {""}, {""}, {""}, +#line 459 "src/lexer-keywords.txt" + {"v128.load32x2_s", TokenType::Load, Opcode::V128Load32X2S}, + {""}, {""}, {""}, +#line 563 "src/lexer-keywords.txt" + {"return_call_indirect", TokenType::ReturnCallIndirect, Opcode::ReturnCallIndirect}, +#line 543 "src/lexer-keywords.txt" + {"memory.init", TokenType::MemoryInit, Opcode::MemoryInit}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 166 "src/lexer-keywords.txt" + {"f64x2.relaxed_max", TokenType::Binary, Opcode::F64X2RelaxedMax}, + {""}, {""}, +#line 38 "src/lexer-keywords.txt" + {"catch_all", TokenType::CatchAll, Opcode::CatchAll}, +#line 112 "src/lexer-keywords.txt" + {"f32x4.sub", TokenType::Binary, Opcode::F32X4Sub}, + {""}, +#line 568 "src/lexer-keywords.txt" + {"start", TokenType::Start}, + {""}, {""}, {""}, {""}, {""}, +#line 349 "src/lexer-keywords.txt" + {"i32x4.sub", TokenType::Binary, Opcode::I32X4Sub}, +#line 224 "src/lexer-keywords.txt" + {"i16x8.sub_sat_u", TokenType::Binary, Opcode::I16X8SubSatU}, +#line 599 "src/lexer-keywords.txt" + {"v128.load8_splat", TokenType::Load, Opcode::V128Load8Splat}, + {""}, +#line 189 "src/lexer-keywords.txt" + {"i16x8.all_true", TokenType::Unary, Opcode::I16X8AllTrue}, +#line 223 "src/lexer-keywords.txt" + {"i16x8.sub_sat_s", TokenType::Binary, Opcode::I16X8SubSatS}, +#line 116 "src/lexer-keywords.txt" + {"f64.abs", TokenType::Unary, Opcode::F64Abs}, +#line 54 "src/lexer-keywords.txt" + {"f32.abs", TokenType::Unary, Opcode::F32Abs}, + {""}, {""}, {""}, {""}, {""}, +#line 581 "src/lexer-keywords.txt" + {"type", TokenType::Type}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 84 "src/lexer-keywords.txt" + {"f32x4.abs", TokenType::Unary, Opcode::F32X4Abs}, + {""}, {""}, +#line 398 "src/lexer-keywords.txt" + {"i64.atomic.rmw.xchg", TokenType::AtomicRmw, Opcode::I64AtomicRmwXchg}, +#line 261 "src/lexer-keywords.txt" + {"i32.atomic.rmw.xchg", TokenType::AtomicRmw, Opcode::I32AtomicRmwXchg}, +#line 63 "src/lexer-keywords.txt" + {"f32.demote_f64", TokenType::Convert, Opcode::F32DemoteF64}, + {""}, {""}, +#line 314 "src/lexer-keywords.txt" + {"i32x4.abs", TokenType::Unary, Opcode::I32X4Abs}, + {""}, +#line 207 "src/lexer-keywords.txt" + {"i16x8.max_u", TokenType::Binary, Opcode::I16X8MaxU}, + {""}, +#line 206 "src/lexer-keywords.txt" + {"i16x8.max_s", TokenType::Binary, Opcode::I16X8MaxS}, +#line 571 "src/lexer-keywords.txt" + {"table.fill", TokenType::TableFill, Opcode::TableFill}, +#line 478 "src/lexer-keywords.txt" + {"i64x2.shl", TokenType::Binary, Opcode::I64X2Shl}, + {""}, +#line 513 "src/lexer-keywords.txt" + {"i8x16.neg", TokenType::Unary, Opcode::I8X16Neg}, + {""}, +#line 500 "src/lexer-keywords.txt" + {"i8x16.ge_u", TokenType::Compare, Opcode::I8X16GeU}, +#line 515 "src/lexer-keywords.txt" + {"i8x16.ne", TokenType::Compare, Opcode::I8X16Ne}, +#line 504 "src/lexer-keywords.txt" + {"i8x16.le_u", TokenType::Compare, Opcode::I8X16LeU}, + {""}, +#line 499 "src/lexer-keywords.txt" + {"i8x16.ge_s", TokenType::Compare, Opcode::I8X16GeS}, + {""}, +#line 503 "src/lexer-keywords.txt" + {"i8x16.le_s", TokenType::Compare, Opcode::I8X16LeS}, + {""}, +#line 502 "src/lexer-keywords.txt" + {"i8x16.gt_u", TokenType::Compare, Opcode::I8X16GtU}, + {""}, +#line 506 "src/lexer-keywords.txt" + {"i8x16.lt_u", TokenType::Compare, Opcode::I8X16LtU}, +#line 529 "src/lexer-keywords.txt" + {"input", TokenType::Input}, +#line 501 "src/lexer-keywords.txt" + {"i8x16.gt_s", TokenType::Compare, Opcode::I8X16GtS}, + {""}, +#line 505 "src/lexer-keywords.txt" + {"i8x16.lt_s", TokenType::Compare, Opcode::I8X16LtS}, + {""}, {""}, +#line 203 "src/lexer-keywords.txt" + {"v128.load8x8_u", TokenType::Load, Opcode::V128Load8X8U}, + {""}, +#line 202 "src/lexer-keywords.txt" + {"v128.load8x8_s", TokenType::Load, Opcode::V128Load8X8S}, + {""}, {""}, {""}, +#line 555 "src/lexer-keywords.txt" + {"quote", TokenType::Quote}, + {""}, {""}, +#line 528 "src/lexer-keywords.txt" + {"import", TokenType::Import}, + {""}, +#line 567 "src/lexer-keywords.txt" + {"shared", TokenType::Shared}, +#line 604 "src/lexer-keywords.txt" + {"v128.store8_lane", TokenType::SimdStoreLane, Opcode::V128Store8Lane}, + {""}, +#line 187 "src/lexer-keywords.txt" + {"i16x8.add_sat_u", TokenType::Binary, Opcode::I16X8AddSatU}, + {""}, {""}, {""}, +#line 186 "src/lexer-keywords.txt" + {"i16x8.add_sat_s", TokenType::Binary, Opcode::I16X8AddSatS}, +#line 141 "src/lexer-keywords.txt" + {"f64.sqrt", TokenType::Unary, Opcode::F64Sqrt}, +#line 79 "src/lexer-keywords.txt" + {"f32.sqrt", TokenType::Unary, Opcode::F32Sqrt}, +#line 32 "src/lexer-keywords.txt" + {"br_table", TokenType::BrTable, Opcode::BrTable}, + {""}, +#line 552 "src/lexer-keywords.txt" + {"output", TokenType::Output}, + {""}, +#line 602 "src/lexer-keywords.txt" + {"v128.load32_lane", TokenType::SimdLoadLane, Opcode::V128Load32Lane}, + {""}, +#line 551 "src/lexer-keywords.txt" + {"offset", TokenType::Offset}, +#line 111 "src/lexer-keywords.txt" + {"f32x4.sqrt", TokenType::Unary, Opcode::F32X4Sqrt}, + {""}, {""}, +#line 545 "src/lexer-keywords.txt" + {"memory", TokenType::Memory}, +#line 381 "src/lexer-keywords.txt" + {"i64.atomic.rmw32.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw32CmpxchgU}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 123 "src/lexer-keywords.txt" + {"f64.convert_i64_u", TokenType::Convert, Opcode::F64ConvertI64U}, +#line 61 "src/lexer-keywords.txt" + {"f32.convert_i64_u", TokenType::Convert, Opcode::F32ConvertI64U}, + {""}, {""}, +#line 122 "src/lexer-keywords.txt" + {"f64.convert_i64_s", TokenType::Convert, Opcode::F64ConvertI64S}, +#line 60 "src/lexer-keywords.txt" + {"f32.convert_i64_s", TokenType::Convert, Opcode::F32ConvertI64S}, +#line 408 "src/lexer-keywords.txt" + {"i64.div_u", TokenType::Binary, Opcode::I64DivU}, +#line 270 "src/lexer-keywords.txt" + {"i32.div_u", TokenType::Binary, Opcode::I32DivU}, +#line 407 "src/lexer-keywords.txt" + {"i64.div_s", TokenType::Binary, Opcode::I64DivS}, +#line 269 "src/lexer-keywords.txt" + {"i32.div_s", TokenType::Binary, Opcode::I32DivS}, +#line 597 "src/lexer-keywords.txt" + {"v128.load32_splat", TokenType::Load, Opcode::V128Load32Splat}, + {""}, {""}, {""}, +#line 376 "src/lexer-keywords.txt" + {"i64.atomic.rmw16.sub_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16SubU}, +#line 246 "src/lexer-keywords.txt" + {"i32.atomic.rmw16.sub_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16SubU}, + {""}, {""}, {""}, +#line 37 "src/lexer-keywords.txt" + {"catch", TokenType::Catch, Opcode::Catch}, + {""}, {""}, {""}, +#line 553 "src/lexer-keywords.txt" + {"param", TokenType::Param}, + {""}, {""}, {""}, {""}, {""}, +#line 209 "src/lexer-keywords.txt" + {"i16x8.min_u", TokenType::Binary, Opcode::I16X8MinU}, + {""}, +#line 208 "src/lexer-keywords.txt" + {"i16x8.min_s", TokenType::Binary, Opcode::I16X8MinS}, + {""}, +#line 219 "src/lexer-keywords.txt" + {"i16x8.shl", TokenType::Binary, Opcode::I16X8Shl}, + {""}, +#line 110 "src/lexer-keywords.txt" + {"f32x4.splat", TokenType::Unary, Opcode::F32X4Splat}, + {""}, {""}, {""}, {""}, +#line 591 "src/lexer-keywords.txt" + {"v128.load32_zero", TokenType::Load, Opcode::V128Load32Zero}, + {""}, {""}, +#line 348 "src/lexer-keywords.txt" + {"i32x4.splat", TokenType::Unary, Opcode::I32X4Splat}, +#line 492 "src/lexer-keywords.txt" + {"i8x16.add", TokenType::Binary, Opcode::I8X16Add}, + {""}, +#line 373 "src/lexer-keywords.txt" + {"i64.atomic.rmw16.and_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16AndU}, +#line 243 "src/lexer-keywords.txt" + {"i32.atomic.rmw16.and_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16AndU}, + {""}, {""}, {""}, +#line 579 "src/lexer-keywords.txt" + {"throw", TokenType::Throw, Opcode::Throw}, +#line 369 "src/lexer-keywords.txt" + {"i64.atomic.load32_u", TokenType::AtomicLoad, Opcode::I64AtomicLoad32U}, +#line 104 "src/lexer-keywords.txt" + {"f32x4.pmin", TokenType::Binary, Opcode::F32X4PMin}, +#line 368 "src/lexer-keywords.txt" + {"i64.atomic.load16_u", TokenType::AtomicLoad, Opcode::I64AtomicLoad16U}, +#line 239 "src/lexer-keywords.txt" + {"i32.atomic.load16_u", TokenType::AtomicLoad, Opcode::I32AtomicLoad16U}, + {""}, {""}, +#line 518 "src/lexer-keywords.txt" + {"i8x16.replace_lane", TokenType::SimdLaneOp, Opcode::I8X16ReplaceLane}, +#line 30 "src/lexer-keywords.txt" + {"block", TokenType::Block, Opcode::Block}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 375 "src/lexer-keywords.txt" + {"i64.atomic.rmw16.or_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16OrU}, +#line 245 "src/lexer-keywords.txt" + {"i32.atomic.rmw16.or_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16OrU}, + {""}, {""}, {""}, {""}, {""}, +#line 372 "src/lexer-keywords.txt" + {"i64.atomic.rmw16.add_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16AddU}, +#line 242 "src/lexer-keywords.txt" + {"i32.atomic.rmw16.add_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16AddU}, + {""}, {""}, {""}, +#line 39 "src/lexer-keywords.txt" + {"data.drop", TokenType::DataDrop, Opcode::DataDrop}, + {""}, {""}, +#line 29 "src/lexer-keywords.txt" + {"binary", TokenType::Bin}, + {""}, {""}, +#line 190 "src/lexer-keywords.txt" + {"i16x8.avgr_u", TokenType::Binary, Opcode::I16X8AvgrU}, +#line 195 "src/lexer-keywords.txt" + {"i16x8.extract_lane_u", TokenType::SimdLaneOp, Opcode::I16X8ExtractLaneU}, + {""}, +#line 194 "src/lexer-keywords.txt" + {"i16x8.extract_lane_s", TokenType::SimdLaneOp, Opcode::I16X8ExtractLaneS}, + {""}, {""}, {""}, {""}, +#line 590 "src/lexer-keywords.txt" + {"v128.any_true", TokenType::Unary, Opcode::V128AnyTrue}, + {""}, +#line 149 "src/lexer-keywords.txt" + {"f64x2.div", TokenType::Binary, Opcode::F64X2Div}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 606 "src/lexer-keywords.txt" + {"v128.store32_lane", TokenType::SimdStoreLane, Opcode::V128Store32Lane}, + {""}, +#line 605 "src/lexer-keywords.txt" + {"v128.store16_lane", TokenType::SimdStoreLane, Opcode::V128Store16Lane}, +#line 401 "src/lexer-keywords.txt" + {"i64.atomic.store32", TokenType::AtomicStore, Opcode::I64AtomicStore32}, + {""}, +#line 542 "src/lexer-keywords.txt" + {"memory.grow", TokenType::MemoryGrow, Opcode::MemoryGrow}, +#line 517 "src/lexer-keywords.txt" + {"i8x16.relaxed_laneselect", TokenType::Ternary, Opcode::I8X16RelaxedLaneSelect}, + {""}, {""}, +#line 317 "src/lexer-keywords.txt" + {"i32x4.bitmask", TokenType::Unary, Opcode::I32X4Bitmask}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 562 "src/lexer-keywords.txt" + {"rethrow", TokenType::Rethrow, Opcode::Rethrow}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 582 "src/lexer-keywords.txt" + {"unreachable", TokenType::Unreachable, Opcode::Unreachable}, + {""}, +#line 152 "src/lexer-keywords.txt" + {"f64x2.floor", TokenType::Unary, Opcode::F64X2Floor}, + {""}, {""}, {""}, +#line 573 "src/lexer-keywords.txt" + {"table.grow", TokenType::TableGrow, Opcode::TableGrow}, +#line 410 "src/lexer-keywords.txt" + {"i64.eqz", TokenType::Convert, Opcode::I64Eqz}, +#line 272 "src/lexer-keywords.txt" + {"i32.eqz", TokenType::Convert, Opcode::I32Eqz}, + {""}, {""}, +#line 391 "src/lexer-keywords.txt" + {"i64.atomic.rmw8.xchg_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8XchgU}, +#line 254 "src/lexer-keywords.txt" + {"i32.atomic.rmw8.xchg_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8XchgU}, + {""}, {""}, {""}, {""}, {""}, +#line 443 "src/lexer-keywords.txt" + {"i64.store16", TokenType::Store, Opcode::I64Store16}, +#line 300 "src/lexer-keywords.txt" + {"i32.store16", TokenType::Store, Opcode::I32Store16}, +#line 480 "src/lexer-keywords.txt" + {"i64x2.shr_u", TokenType::Binary, Opcode::I64X2ShrU}, + {""}, +#line 479 "src/lexer-keywords.txt" + {"i64x2.shr_s", TokenType::Binary, Opcode::I64X2ShrS}, + {""}, {""}, +#line 570 "src/lexer-keywords.txt" + {"table.copy", TokenType::TableCopy, Opcode::TableCopy}, + {""}, +#line 541 "src/lexer-keywords.txt" + {"memory.fill", TokenType::MemoryFill, Opcode::MemoryFill}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, +#line 125 "src/lexer-keywords.txt" + {"f64.div", TokenType::Binary, Opcode::F64Div}, +#line 64 "src/lexer-keywords.txt" + {"f32.div", TokenType::Binary, Opcode::F32Div}, + {""}, +#line 25 "src/lexer-keywords.txt" + {"assert_return", TokenType::AssertReturn}, +#line 378 "src/lexer-keywords.txt" + {"i64.atomic.rmw16.xor_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16XorU}, +#line 248 "src/lexer-keywords.txt" + {"i32.atomic.rmw16.xor_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16XorU}, + {""}, {""}, +#line 377 "src/lexer-keywords.txt" + {"i64.atomic.rmw16.xchg_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16XchgU}, +#line 247 "src/lexer-keywords.txt" + {"i32.atomic.rmw16.xchg_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16XchgU}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, +#line 524 "src/lexer-keywords.txt" + {"i8x16.sub_sat_u", TokenType::Binary, Opcode::I8X16SubSatU}, + {""}, {""}, +#line 493 "src/lexer-keywords.txt" + {"i8x16.all_true", TokenType::Unary, Opcode::I8X16AllTrue}, +#line 523 "src/lexer-keywords.txt" + {"i8x16.sub_sat_s", TokenType::Binary, Opcode::I8X16SubSatS}, + {""}, {""}, +#line 172 "src/lexer-keywords.txt" + {"f64x2.sub", TokenType::Binary, Opcode::F64X2Sub}, + {""}, {""}, {""}, {""}, +#line 339 "src/lexer-keywords.txt" + {"i32x4.dot_i16x8_s", TokenType::Binary, Opcode::I32X4DotI16X8S}, + {""}, {""}, +#line 482 "src/lexer-keywords.txt" + {"i64x2.sub", TokenType::Binary, Opcode::I64X2Sub}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 526 "src/lexer-keywords.txt" + {"i8x16", TokenType::I8X16}, + {""}, {""}, {""}, +#line 508 "src/lexer-keywords.txt" + {"i8x16.max_u", TokenType::Binary, Opcode::I8X16MaxU}, + {""}, +#line 507 "src/lexer-keywords.txt" + {"i8x16.max_s", TokenType::Binary, Opcode::I8X16MaxS}, + {""}, {""}, {""}, +#line 146 "src/lexer-keywords.txt" + {"f64x2.abs", TokenType::Unary, Opcode::F64X2Abs}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 468 "src/lexer-keywords.txt" + {"i64x2.abs", TokenType::Unary, Opcode::I64X2Abs}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 221 "src/lexer-keywords.txt" + {"i16x8.shr_u", TokenType::Binary, Opcode::I16X8ShrU}, + {""}, +#line 220 "src/lexer-keywords.txt" + {"i16x8.shr_s", TokenType::Binary, Opcode::I16X8ShrS}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 390 "src/lexer-keywords.txt" + {"i64.atomic.rmw8.sub_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8SubU}, +#line 253 "src/lexer-keywords.txt" + {"i32.atomic.rmw8.sub_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8SubU}, + {""}, {""}, {""}, {""}, {""}, +#line 491 "src/lexer-keywords.txt" + {"i8x16.add_sat_u", TokenType::Binary, Opcode::I8X16AddSatU}, +#line 103 "src/lexer-keywords.txt" + {"f32x4.pmax", TokenType::Binary, Opcode::F32X4PMax}, + {""}, {""}, +#line 490 "src/lexer-keywords.txt" + {"i8x16.add_sat_s", TokenType::Binary, Opcode::I8X16AddSatS}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, +#line 455 "src/lexer-keywords.txt" + {"i64.trunc_sat_f64_u", TokenType::Convert, Opcode::I64TruncSatF64U}, +#line 311 "src/lexer-keywords.txt" + {"i32.trunc_sat_f64_u", TokenType::Convert, Opcode::I32TruncSatF64U}, +#line 47 "src/lexer-keywords.txt" + {"elem", TokenType::Elem}, + {""}, +#line 454 "src/lexer-keywords.txt" + {"i64.trunc_sat_f64_s", TokenType::Convert, Opcode::I64TruncSatF64S}, +#line 310 "src/lexer-keywords.txt" + {"i32.trunc_sat_f64_s", TokenType::Convert, Opcode::I32TruncSatF64S}, +#line 171 "src/lexer-keywords.txt" + {"f64x2.sqrt", TokenType::Unary, Opcode::F64X2Sqrt}, + {""}, {""}, {""}, +#line 52 "src/lexer-keywords.txt" + {"externref", Type::ExternRef}, +#line 603 "src/lexer-keywords.txt" + {"v128.load64_lane", TokenType::SimdLoadLane, Opcode::V128Load64Lane}, + {""}, +#line 351 "src/lexer-keywords.txt" + {"i32x4.extadd_pairwise_i16x8_u", TokenType::Unary, Opcode::I32X4ExtaddPairwiseI16X8U}, + {""}, +#line 350 "src/lexer-keywords.txt" + {"i32x4.extadd_pairwise_i16x8_s", TokenType::Unary, Opcode::I32X4ExtaddPairwiseI16X8S}, +#line 225 "src/lexer-keywords.txt" + {"i16x8.sub", TokenType::Binary, Opcode::I16X8Sub}, +#line 31 "src/lexer-keywords.txt" + {"br_if", TokenType::BrIf, Opcode::BrIf}, +#line 121 "src/lexer-keywords.txt" + {"f64.convert_i32_u", TokenType::Convert, Opcode::F64ConvertI32U}, +#line 59 "src/lexer-keywords.txt" + {"f32.convert_i32_u", TokenType::Convert, Opcode::F32ConvertI32U}, + {""}, +#line 24 "src/lexer-keywords.txt" + {"assert_malformed", TokenType::AssertMalformed}, +#line 120 "src/lexer-keywords.txt" + {"f64.convert_i32_s", TokenType::Convert, Opcode::F64ConvertI32S}, +#line 58 "src/lexer-keywords.txt" + {"f32.convert_i32_s", TokenType::Convert, Opcode::F32ConvertI32S}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 510 "src/lexer-keywords.txt" + {"i8x16.min_u", TokenType::Binary, Opcode::I8X16MinU}, + {""}, +#line 509 "src/lexer-keywords.txt" + {"i8x16.min_s", TokenType::Binary, Opcode::I8X16MinS}, + {""}, +#line 519 "src/lexer-keywords.txt" + {"i8x16.shl", TokenType::Binary, Opcode::I8X16Shl}, +#line 598 "src/lexer-keywords.txt" + {"v128.load64_splat", TokenType::Load, Opcode::V128Load64Splat}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 185 "src/lexer-keywords.txt" + {"i16x8.abs", TokenType::Unary, Opcode::I16X8Abs}, +#line 179 "src/lexer-keywords.txt" + {"funcref", Type::FuncRef}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 170 "src/lexer-keywords.txt" + {"f64x2.splat", TokenType::Unary, Opcode::F64X2Splat}, + {""}, {""}, {""}, {""}, {""}, +#line 516 "src/lexer-keywords.txt" + {"i8x16.relaxed_swizzle", TokenType::Binary, Opcode::I8X16RelaxedSwizzle}, + {""}, +#line 481 "src/lexer-keywords.txt" + {"i64x2.splat", TokenType::Unary, Opcode::I64X2Splat}, + {""}, {""}, {""}, {""}, +#line 592 "src/lexer-keywords.txt" + {"v128.load64_zero", TokenType::Load, Opcode::V128Load64Zero}, +#line 88 "src/lexer-keywords.txt" + {"f32x4.convert_i32x4_u", TokenType::Unary, Opcode::F32X4ConvertI32X4U}, + {""}, +#line 87 "src/lexer-keywords.txt" + {"f32x4.convert_i32x4_s", TokenType::Unary, Opcode::F32X4ConvertI32X4S}, + {""}, +#line 164 "src/lexer-keywords.txt" + {"f64x2.pmin", TokenType::Binary, Opcode::F64X2PMin}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, +#line 474 "src/lexer-keywords.txt" + {"i64x2.extend_low_i32x4_u", TokenType::Unary, Opcode::I64X2ExtendLowI32X4U}, + {""}, +#line 472 "src/lexer-keywords.txt" + {"i64x2.extend_low_i32x4_s", TokenType::Unary, Opcode::I64X2ExtendLowI32X4S}, + {""}, {""}, +#line 485 "src/lexer-keywords.txt" + {"i64x2.extmul_low_i32x4_u", TokenType::Binary, Opcode::I64X2ExtmulLowI32X4U}, +#line 184 "src/lexer-keywords.txt" + {"global", TokenType::Global}, +#line 483 "src/lexer-keywords.txt" + {"i64x2.extmul_low_i32x4_s", TokenType::Binary, Opcode::I64X2ExtmulLowI32X4S}, + {""}, {""}, +#line 494 "src/lexer-keywords.txt" + {"i8x16.avgr_u", TokenType::Binary, Opcode::I8X16AvgrU}, +#line 498 "src/lexer-keywords.txt" + {"i8x16.extract_lane_u", TokenType::SimdLaneOp, Opcode::I8X16ExtractLaneU}, + {""}, +#line 497 "src/lexer-keywords.txt" + {"i8x16.extract_lane_s", TokenType::SimdLaneOp, Opcode::I8X16ExtractLaneS}, + {""}, {""}, {""}, {""}, +#line 358 "src/lexer-keywords.txt" + {"i32x4.trunc_sat_f32x4_u", TokenType::Unary, Opcode::I32X4TruncSatF32X4U}, + {""}, {""}, {""}, +#line 357 "src/lexer-keywords.txt" + {"i32x4.trunc_sat_f32x4_s", TokenType::Unary, Opcode::I32X4TruncSatF32X4S}, +#line 332 "src/lexer-keywords.txt" + {"v128.load16x4_u", TokenType::Load, Opcode::V128Load16X4U}, + {""}, {""}, {""}, +#line 331 "src/lexer-keywords.txt" + {"v128.load16x4_s", TokenType::Load, Opcode::V128Load16X4S}, + {""}, {""}, {""}, {""}, +#line 395 "src/lexer-keywords.txt" + {"i64.atomic.rmw.cmpxchg", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmwCmpxchg}, +#line 258 "src/lexer-keywords.txt" + {"i32.atomic.rmw.cmpxchg", TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmwCmpxchg}, + {""}, {""}, +#line 360 "src/lexer-keywords.txt" + {"i32x4.extend_high_i16x8_u", TokenType::Unary, Opcode::I32X4ExtendHighI16X8U}, + {""}, +#line 359 "src/lexer-keywords.txt" + {"i32x4.extend_high_i16x8_s", TokenType::Unary, Opcode::I32X4ExtendHighI16X8S}, + {""}, {""}, +#line 355 "src/lexer-keywords.txt" + {"i32x4.extmul_high_i16x8_u", TokenType::Binary, Opcode::I32X4ExtmulHighI16X8U}, + {""}, +#line 353 "src/lexer-keywords.txt" + {"i32x4.extmul_high_i16x8_s", TokenType::Binary, Opcode::I32X4ExtmulHighI16X8S}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 182 "src/lexer-keywords.txt" + {"global.get", TokenType::GlobalGet, Opcode::GlobalGet}, +#line 471 "src/lexer-keywords.txt" + {"i64x2.bitmask", TokenType::Unary, Opcode::I64X2Bitmask}, +#line 183 "src/lexer-keywords.txt" + {"global.set", TokenType::GlobalSet, Opcode::GlobalSet}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, +#line 222 "src/lexer-keywords.txt" + {"i16x8.splat", TokenType::Unary, Opcode::I16X8Splat}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, +#line 388 "src/lexer-keywords.txt" + {"i64.atomic.rmw8.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw8CmpxchgU}, +#line 251 "src/lexer-keywords.txt" + {"i32.atomic.rmw8.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmw8CmpxchgU}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, +#line 114 "src/lexer-keywords.txt" + {"f32x4.demote_f64x2_zero", TokenType::Unary, Opcode::F32X4DemoteF64X2Zero}, + {""}, {""}, {""}, +#line 364 "src/lexer-keywords.txt" + {"i32x4.trunc_sat_f64x2_u_zero", TokenType::Unary, Opcode::I32X4TruncSatF64X2UZero}, + {""}, +#line 363 "src/lexer-keywords.txt" + {"i32x4.trunc_sat_f64x2_s_zero", TokenType::Unary, Opcode::I32X4TruncSatF64X2SZero}, + {""}, {""}, +#line 609 "src/lexer-keywords.txt" + {"i8x16.swizzle", TokenType::Binary, Opcode::I8X16Swizzle}, + {""}, {""}, {""}, {""}, {""}, +#line 585 "src/lexer-keywords.txt" + {"v128.bitselect", TokenType::Ternary, Opcode::V128BitSelect}, + {""}, {""}, +#line 53 "src/lexer-keywords.txt" + {"export", TokenType::Export}, +#line 212 "src/lexer-keywords.txt" + {"i16x8.narrow_i32x4_u", TokenType::Binary, Opcode::I16X8NarrowI32X4U}, + {""}, +#line 211 "src/lexer-keywords.txt" + {"i16x8.narrow_i32x4_s", TokenType::Binary, Opcode::I16X8NarrowI32X4S}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, +#line 601 "src/lexer-keywords.txt" + {"v128.load16_lane", TokenType::SimdLoadLane, Opcode::V128Load16Lane}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 374 "src/lexer-keywords.txt" + {"i64.atomic.rmw16.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw16CmpxchgU}, +#line 244 "src/lexer-keywords.txt" + {"i32.atomic.rmw16.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmw16CmpxchgU}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 191 "src/lexer-keywords.txt" + {"i16x8.bitmask", TokenType::Unary, Opcode::I16X8Bitmask}, +#line 217 "src/lexer-keywords.txt" + {"i16x8.relaxed_q15mulr_s", TokenType::Binary, Opcode::I16X8RelaxedQ15mulrS}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 596 "src/lexer-keywords.txt" + {"v128.load16_splat", TokenType::Load, Opcode::V128Load16Splat}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, +#line 521 "src/lexer-keywords.txt" + {"i8x16.shr_u", TokenType::Binary, Opcode::I8X16ShrU}, + {""}, +#line 520 "src/lexer-keywords.txt" + {"i8x16.shr_s", TokenType::Binary, Opcode::I8X16ShrS}, + {""}, {""}, +#line 143 "src/lexer-keywords.txt" + {"f64.sub", TokenType::Binary, Opcode::F64Sub}, +#line 81 "src/lexer-keywords.txt" + {"f32.sub", TokenType::Binary, Opcode::F32Sub}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 447 "src/lexer-keywords.txt" + {"i64.sub", TokenType::Binary, Opcode::I64Sub}, +#line 303 "src/lexer-keywords.txt" + {"i32.sub", TokenType::Binary, Opcode::I32Sub}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, +#line 236 "src/lexer-keywords.txt" + {"i16x8.extend_low_i8x16_u", TokenType::Unary, Opcode::I16X8ExtendLowI8X16U}, + {""}, +#line 235 "src/lexer-keywords.txt" + {"i16x8.extend_low_i8x16_s", TokenType::Unary, Opcode::I16X8ExtendLowI8X16S}, + {""}, {""}, +#line 230 "src/lexer-keywords.txt" + {"i16x8.extmul_low_i8x16_u", TokenType::Binary, Opcode::I16X8ExtmulLowI8X16U}, + {""}, +#line 228 "src/lexer-keywords.txt" + {"i16x8.extmul_low_i8x16_s", TokenType::Binary, Opcode::I16X8ExtmulLowI8X16S}, +#line 163 "src/lexer-keywords.txt" + {"f64x2.pmax", TokenType::Binary, Opcode::F64X2PMax}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 362 "src/lexer-keywords.txt" + {"i32x4.extend_low_i16x8_u", TokenType::Unary, Opcode::I32X4ExtendLowI16X8U}, + {""}, +#line 361 "src/lexer-keywords.txt" + {"i32x4.extend_low_i16x8_s", TokenType::Unary, Opcode::I32X4ExtendLowI16X8S}, +#line 35 "src/lexer-keywords.txt" + {"call_ref", TokenType::CallRef, Opcode::CallRef}, + {""}, +#line 354 "src/lexer-keywords.txt" + {"i32x4.extmul_low_i16x8_u", TokenType::Binary, Opcode::I32X4ExtmulLowI16X8U}, + {""}, +#line 352 "src/lexer-keywords.txt" + {"i32x4.extmul_low_i16x8_s", TokenType::Binary, Opcode::I32X4ExtmulLowI16X8S}, +#line 313 "src/lexer-keywords.txt" + {"i32.wrap_i64", TokenType::Convert, Opcode::I32WrapI64}, + {""}, {""}, {""}, {""}, {""}, +#line 525 "src/lexer-keywords.txt" + {"i8x16.sub", TokenType::Binary, Opcode::I8X16Sub}, +#line 453 "src/lexer-keywords.txt" + {"i64.trunc_sat_f32_u", TokenType::Convert, Opcode::I64TruncSatF32U}, +#line 309 "src/lexer-keywords.txt" + {"i32.trunc_sat_f32_u", TokenType::Convert, Opcode::I32TruncSatF32U}, + {""}, {""}, +#line 452 "src/lexer-keywords.txt" + {"i64.trunc_sat_f32_s", TokenType::Convert, Opcode::I64TruncSatF32S}, +#line 308 "src/lexer-keywords.txt" + {"i32.trunc_sat_f32_s", TokenType::Convert, Opcode::I32TruncSatF32S}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 318 "src/lexer-keywords.txt" + {"i32x4.dot_i8x16_i7x16_add_s", TokenType::Ternary, Opcode::I32X4DotI8X16I7X16AddS}, + {""}, {""}, {""}, {""}, +#line 124 "src/lexer-keywords.txt" + {"f64.copysign", TokenType::Binary, Opcode::F64Copysign}, +#line 62 "src/lexer-keywords.txt" + {"f32.copysign", TokenType::Binary, Opcode::F32Copysign}, + {""}, {""}, +#line 475 "src/lexer-keywords.txt" + {"i64x2.extend_high_i32x4_u", TokenType::Unary, Opcode::I64X2ExtendHighI32X4U}, + {""}, +#line 473 "src/lexer-keywords.txt" + {"i64x2.extend_high_i32x4_s", TokenType::Unary, Opcode::I64X2ExtendHighI32X4S}, + {""}, {""}, +#line 486 "src/lexer-keywords.txt" + {"i64x2.extmul_high_i32x4_u", TokenType::Binary, Opcode::I64X2ExtmulHighI32X4U}, +#line 489 "src/lexer-keywords.txt" + {"i8x16.abs", TokenType::Unary, Opcode::I8X16Abs}, +#line 484 "src/lexer-keywords.txt" + {"i64x2.extmul_high_i32x4_s", TokenType::Binary, Opcode::I64X2ExtmulHighI32X4S}, + {""}, +#line 140 "src/lexer-keywords.txt" + {"f64.reinterpret_i64", TokenType::Convert, Opcode::F64ReinterpretI64}, + {""}, +#line 20 "src/lexer-keywords.txt" + {"array", Type::Array, TokenType::Array}, + {""}, {""}, {""}, {""}, +#line 400 "src/lexer-keywords.txt" + {"i64.atomic.store16", TokenType::AtomicStore, Opcode::I64AtomicStore16}, +#line 263 "src/lexer-keywords.txt" + {"i32.atomic.store16", TokenType::AtomicStore, Opcode::I32AtomicStore16}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, +#line 126 "src/lexer-keywords.txt" + {"f64.eq", TokenType::Compare, Opcode::F64Eq}, +#line 65 "src/lexer-keywords.txt" + {"f32.eq", TokenType::Compare, Opcode::F32Eq}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 409 "src/lexer-keywords.txt" + {"i64.eq", TokenType::Compare, Opcode::I64Eq}, +#line 271 "src/lexer-keywords.txt" + {"i32.eq", TokenType::Compare, Opcode::I32Eq}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, +#line 90 "src/lexer-keywords.txt" + {"f32x4.eq", TokenType::Compare, Opcode::F32X4Eq}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 319 "src/lexer-keywords.txt" + {"i32x4.eq", TokenType::Compare, Opcode::I32X4Eq}, + {""}, +#line 22 "src/lexer-keywords.txt" + {"assert_exhaustion", TokenType::AssertExhaustion}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 44 "src/lexer-keywords.txt" + {"drop", TokenType::Drop, Opcode::Drop}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, +#line 23 "src/lexer-keywords.txt" + {"assert_invalid", TokenType::AssertInvalid}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 550 "src/lexer-keywords.txt" + {"nop", TokenType::Nop, Opcode::Nop}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, +#line 522 "src/lexer-keywords.txt" + {"i8x16.splat", TokenType::Unary, Opcode::I8X16Splat}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 536 "src/lexer-keywords.txt" + {"loop", TokenType::Loop, Opcode::Loop}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 538 "src/lexer-keywords.txt" + {"memory.atomic.wait32", TokenType::AtomicWait, Opcode::MemoryAtomicWait32}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 539 "src/lexer-keywords.txt" + {"memory.atomic.wait64", TokenType::AtomicWait, Opcode::MemoryAtomicWait64}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, +#line 397 "src/lexer-keywords.txt" + {"i64.atomic.rmw.sub", TokenType::AtomicRmw, Opcode::I64AtomicRmwSub}, +#line 260 "src/lexer-keywords.txt" + {"i32.atomic.rmw.sub", TokenType::AtomicRmw, Opcode::I32AtomicRmwSub}, +#line 495 "src/lexer-keywords.txt" + {"i8x16.bitmask", TokenType::Unary, Opcode::I8X16Bitmask}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 540 "src/lexer-keywords.txt" + {"memory.copy", TokenType::MemoryCopy, Opcode::MemoryCopy}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, +#line 514 "src/lexer-keywords.txt" + {"i8x16.popcnt", TokenType::Unary, Opcode::I8X16Popcnt}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, +#line 27 "src/lexer-keywords.txt" + {"assert_unlinkable", TokenType::AssertUnlinkable}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 608 "src/lexer-keywords.txt" + {"i8x16.shuffle", TokenType::SimdShuffleOp, Opcode::I8X16Shuffle}, + {""}, {""}, {""}, {""}, +#line 434 "src/lexer-keywords.txt" + {"i64.popcnt", TokenType::Unary, Opcode::I64Popcnt}, +#line 291 "src/lexer-keywords.txt" + {"i32.popcnt", TokenType::Unary, Opcode::I32Popcnt}, + {""}, {""}, {""}, {""}, +#line 175 "src/lexer-keywords.txt" + {"f64x2.convert_low_i32x4_u", TokenType::Unary, Opcode::F64X2ConvertLowI32X4U}, + {""}, +#line 174 "src/lexer-keywords.txt" + {"f64x2.convert_low_i32x4_s", TokenType::Unary, Opcode::F64X2ConvertLowI32X4S}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, +#line 227 "src/lexer-keywords.txt" + {"i16x8.extadd_pairwise_i8x16_u", TokenType::Unary, Opcode::I16X8ExtaddPairwiseI8X16U}, + {""}, +#line 226 "src/lexer-keywords.txt" + {"i16x8.extadd_pairwise_i8x16_s", TokenType::Unary, Opcode::I16X8ExtaddPairwiseI8X16S}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 537 "src/lexer-keywords.txt" + {"memory.atomic.notify", TokenType::AtomicNotify, Opcode::MemoryAtomicNotify}, + {""}, {""}, +#line 26 "src/lexer-keywords.txt" + {"assert_trap", TokenType::AssertTrap}, +#line 78 "src/lexer-keywords.txt" + {"f32.reinterpret_i32", TokenType::Convert, Opcode::F32ReinterpretI32}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 139 "src/lexer-keywords.txt" + {"f64.promote_f32", TokenType::Convert, Opcode::F64PromoteF32}, + {""}, {""}, {""}, {""}, +#line 192 "src/lexer-keywords.txt" + {"i16x8.dot_i8x16_i7x16_s", TokenType::Binary, Opcode::I16X8DotI8X16I7X16S}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 150 "src/lexer-keywords.txt" + {"f64x2.eq", TokenType::Compare, Opcode::F64X2Eq}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 462 "src/lexer-keywords.txt" + {"i64x2.eq", TokenType::Binary, Opcode::I64X2Eq}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 234 "src/lexer-keywords.txt" + {"i16x8.extend_high_i8x16_u", TokenType::Unary, Opcode::I16X8ExtendHighI8X16U}, + {""}, +#line 233 "src/lexer-keywords.txt" + {"i16x8.extend_high_i8x16_s", TokenType::Unary, Opcode::I16X8ExtendHighI8X16S}, + {""}, {""}, +#line 231 "src/lexer-keywords.txt" + {"i16x8.extmul_high_i8x16_u", TokenType::Binary, Opcode::I16X8ExtmulHighI8X16U}, +#line 21 "src/lexer-keywords.txt" + {"assert_exception", TokenType::AssertException}, +#line 229 "src/lexer-keywords.txt" + {"i16x8.extmul_high_i8x16_s", TokenType::Binary, Opcode::I16X8ExtmulHighI8X16S}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, +#line 435 "src/lexer-keywords.txt" + {"i64.reinterpret_f64", TokenType::Convert, Opcode::I64ReinterpretF64}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 193 "src/lexer-keywords.txt" + {"i16x8.eq", TokenType::Compare, Opcode::I16X8Eq}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 46 "src/lexer-keywords.txt" + {"elem.drop", TokenType::ElemDrop, Opcode::ElemDrop}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 214 "src/lexer-keywords.txt" + {"i16x8.q15mulr_sat_s", TokenType::Binary, Opcode::I16X8Q15mulrSatS}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 292 "src/lexer-keywords.txt" + {"i32.reinterpret_f32", TokenType::Convert, Opcode::I32ReinterpretF32}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, +#line 28 "src/lexer-keywords.txt" + {"atomic.fence", TokenType::AtomicFence, Opcode::AtomicFence}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, +#line 496 "src/lexer-keywords.txt" + {"i8x16.eq", TokenType::Compare, Opcode::I8X16Eq}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 512 "src/lexer-keywords.txt" + {"i8x16.narrow_i16x8_u", TokenType::Binary, Opcode::I8X16NarrowI16X8U}, + {""}, +#line 511 "src/lexer-keywords.txt" + {"i8x16.narrow_i16x8_s", TokenType::Binary, Opcode::I8X16NarrowI16X8S}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 176 "src/lexer-keywords.txt" + {"f64x2.promote_low_f32x4", TokenType::Unary, Opcode::F64X2PromoteLowF32X4} + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + unsigned int key = hash (str, len); + + if (key <= MAX_HASH_VALUE) + { + const char *s = wordlist[key].name; + + if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0') + return &wordlist[key]; + } + } + return 0; +} diff --git a/third_party/wasm2c/src/prebuilt/wasm2c_header_bottom.cc b/third_party/wasm2c/src/prebuilt/wasm2c_header_bottom.cc new file mode 100644 index 0000000000..7db8468ff7 --- /dev/null +++ b/third_party/wasm2c/src/prebuilt/wasm2c_header_bottom.cc @@ -0,0 +1,7 @@ +const char* s_header_bottom = R"w2c_template(#ifdef __cplusplus +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template(#endif +)w2c_template" +; diff --git a/third_party/wasm2c/src/prebuilt/wasm2c_header_top.cc b/third_party/wasm2c/src/prebuilt/wasm2c_header_top.cc new file mode 100644 index 0000000000..d36aaf0c0f --- /dev/null +++ b/third_party/wasm2c/src/prebuilt/wasm2c_header_top.cc @@ -0,0 +1,64 @@ +const char* s_header_top = R"w2c_template(#include <stdint.h> +)w2c_template" +R"w2c_template( +#include "wasm-rt.h" +)w2c_template" +R"w2c_template( +#if defined(WASM_RT_ENABLE_EXCEPTION_HANDLING) +)w2c_template" +R"w2c_template(#include "wasm-rt-exceptions.h" +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +#if defined(WASM_RT_ENABLE_SIMD) +)w2c_template" +R"w2c_template(#include "simde/wasm/simd128.h" +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +/* TODO(binji): only use stdint.h types in header */ +)w2c_template" +R"w2c_template(#ifndef WASM_RT_CORE_TYPES_DEFINED +)w2c_template" +R"w2c_template(#define WASM_RT_CORE_TYPES_DEFINED +)w2c_template" +R"w2c_template(typedef uint8_t u8; +)w2c_template" +R"w2c_template(typedef int8_t s8; +)w2c_template" +R"w2c_template(typedef uint16_t u16; +)w2c_template" +R"w2c_template(typedef int16_t s16; +)w2c_template" +R"w2c_template(typedef uint32_t u32; +)w2c_template" +R"w2c_template(typedef int32_t s32; +)w2c_template" +R"w2c_template(typedef uint64_t u64; +)w2c_template" +R"w2c_template(typedef int64_t s64; +)w2c_template" +R"w2c_template(typedef float f32; +)w2c_template" +R"w2c_template(typedef double f64; +)w2c_template" +R"w2c_template( +#if defined(WASM_RT_ENABLE_SIMD) +)w2c_template" +R"w2c_template(typedef simde_v128_t v128; +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +#endif +)w2c_template" +R"w2c_template( +#ifdef __cplusplus +)w2c_template" +R"w2c_template(extern "C" { +)w2c_template" +R"w2c_template(#endif +)w2c_template" +; diff --git a/third_party/wasm2c/src/prebuilt/wasm2c_source_declarations.cc b/third_party/wasm2c/src/prebuilt/wasm2c_source_declarations.cc new file mode 100644 index 0000000000..2e9ebe5d99 --- /dev/null +++ b/third_party/wasm2c/src/prebuilt/wasm2c_source_declarations.cc @@ -0,0 +1,1339 @@ +const char* s_source_declarations = R"w2c_template( +#define TRAP(x) (wasm_rt_trap(WASM_RT_TRAP_##x), 0) +)w2c_template" +R"w2c_template( +#if WASM_RT_USE_STACK_DEPTH_COUNT +)w2c_template" +R"w2c_template(#define FUNC_PROLOGUE \ +)w2c_template" +R"w2c_template( if (++wasm_rt_call_stack_depth > WASM_RT_MAX_CALL_STACK_DEPTH) \ +)w2c_template" +R"w2c_template( TRAP(EXHAUSTION); +)w2c_template" +R"w2c_template( +#define FUNC_EPILOGUE --wasm_rt_call_stack_depth +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define FUNC_PROLOGUE +)w2c_template" +R"w2c_template( +#define FUNC_EPILOGUE +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +#define UNREACHABLE TRAP(UNREACHABLE) +)w2c_template" +R"w2c_template( +static inline bool func_types_eq(const wasm_rt_func_type_t a, +)w2c_template" +R"w2c_template( const wasm_rt_func_type_t b) { +)w2c_template" +R"w2c_template( return (a == b) || LIKELY(a && b && !memcmp(a, b, 32)); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +#define CALL_INDIRECT(table, t, ft, x, ...) \ +)w2c_template" +R"w2c_template( (LIKELY((x) < table.size && table.data[x].func && \ +)w2c_template" +R"w2c_template( func_types_eq(ft, table.data[x].func_type)) || \ +)w2c_template" +R"w2c_template( TRAP(CALL_INDIRECT), \ +)w2c_template" +R"w2c_template( ((t)table.data[x].func)(__VA_ARGS__)) +)w2c_template" +R"w2c_template( +#ifdef SUPPORT_MEMORY64 +)w2c_template" +R"w2c_template(#define RANGE_CHECK(mem, offset, len) \ +)w2c_template" +R"w2c_template( do { \ +)w2c_template" +R"w2c_template( uint64_t res; \ +)w2c_template" +R"w2c_template( if (__builtin_add_overflow(offset, len, &res)) \ +)w2c_template" +R"w2c_template( TRAP(OOB); \ +)w2c_template" +R"w2c_template( if (UNLIKELY(res > mem->size)) \ +)w2c_template" +R"w2c_template( TRAP(OOB); \ +)w2c_template" +R"w2c_template( } while (0); +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define RANGE_CHECK(mem, offset, len) \ +)w2c_template" +R"w2c_template( if (UNLIKELY(offset + (uint64_t)len > mem->size)) \ +)w2c_template" +R"w2c_template( TRAP(OOB); +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +#if WASM_RT_MEMCHECK_GUARD_PAGES +)w2c_template" +R"w2c_template(#define MEMCHECK(mem, a, t) +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define MEMCHECK(mem, a, t) RANGE_CHECK(mem, a, sizeof(t)) +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +#ifdef __GNUC__ +)w2c_template" +R"w2c_template(#define wasm_asm __asm__ +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define wasm_asm(X) +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +#if WABT_BIG_ENDIAN +)w2c_template" +R"w2c_template(static inline void load_data(void* dest, const void* src, size_t n) { +)w2c_template" +R"w2c_template( if (!n) { +)w2c_template" +R"w2c_template( return; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( size_t i = 0; +)w2c_template" +R"w2c_template( u8* dest_chars = dest; +)w2c_template" +R"w2c_template( memcpy(dest, src, n); +)w2c_template" +R"w2c_template( for (i = 0; i < (n >> 1); i++) { +)w2c_template" +R"w2c_template( u8 cursor = dest_chars[i]; +)w2c_template" +R"w2c_template( dest_chars[i] = dest_chars[n - i - 1]; +)w2c_template" +R"w2c_template( dest_chars[n - i - 1] = cursor; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template(#define LOAD_DATA(m, o, i, s) \ +)w2c_template" +R"w2c_template( do { \ +)w2c_template" +R"w2c_template( RANGE_CHECK((&m), m.size - o - s, s); \ +)w2c_template" +R"w2c_template( load_data(&(m.data[m.size - o - s]), i, s); \ +)w2c_template" +R"w2c_template( } while (0) +)w2c_template" +R"w2c_template(#define DEFINE_LOAD(name, t1, t2, t3) \ +)w2c_template" +R"w2c_template( static inline t3 name(wasm_rt_memory_t* mem, u64 addr) { \ +)w2c_template" +R"w2c_template( MEMCHECK(mem, addr, t1); \ +)w2c_template" +R"w2c_template( t1 result; \ +)w2c_template" +R"w2c_template( wasm_rt_memcpy(&result, &mem->data[mem->size - addr - sizeof(t1)], \ +)w2c_template" +R"w2c_template( sizeof(t1)); \ +)w2c_template" +R"w2c_template( wasm_asm("" ::"r"(result)); \ +)w2c_template" +R"w2c_template( return (t3)(t2)result; \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +#define DEFINE_STORE(name, t1, t2) \ +)w2c_template" +R"w2c_template( static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value) { \ +)w2c_template" +R"w2c_template( MEMCHECK(mem, addr, t1); \ +)w2c_template" +R"w2c_template( t1 wrapped = (t1)value; \ +)w2c_template" +R"w2c_template( wasm_rt_memcpy(&mem->data[mem->size - addr - sizeof(t1)], &wrapped, \ +)w2c_template" +R"w2c_template( sizeof(t1)); \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(static inline void load_data(void* dest, const void* src, size_t n) { +)w2c_template" +R"w2c_template( if (!n) { +)w2c_template" +R"w2c_template( return; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( memcpy(dest, src, n); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template(#define LOAD_DATA(m, o, i, s) \ +)w2c_template" +R"w2c_template( do { \ +)w2c_template" +R"w2c_template( RANGE_CHECK((&m), o, s); \ +)w2c_template" +R"w2c_template( load_data(&(m.data[o]), i, s); \ +)w2c_template" +R"w2c_template( } while (0) +)w2c_template" +R"w2c_template(#define DEFINE_LOAD(name, t1, t2, t3) \ +)w2c_template" +R"w2c_template( static inline t3 name(wasm_rt_memory_t* mem, u64 addr) { \ +)w2c_template" +R"w2c_template( MEMCHECK(mem, addr, t1); \ +)w2c_template" +R"w2c_template( t1 result; \ +)w2c_template" +R"w2c_template( wasm_rt_memcpy(&result, &mem->data[addr], sizeof(t1)); \ +)w2c_template" +R"w2c_template( wasm_asm("" ::"r"(result)); \ +)w2c_template" +R"w2c_template( return (t3)(t2)result; \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +#define DEFINE_STORE(name, t1, t2) \ +)w2c_template" +R"w2c_template( static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value) { \ +)w2c_template" +R"w2c_template( MEMCHECK(mem, addr, t1); \ +)w2c_template" +R"w2c_template( t1 wrapped = (t1)value; \ +)w2c_template" +R"w2c_template( wasm_rt_memcpy(&mem->data[addr], &wrapped, sizeof(t1)); \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +DEFINE_LOAD(i32_load, u32, u32, u32) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i64_load, u64, u64, u64) +)w2c_template" +R"w2c_template(DEFINE_LOAD(f32_load, f32, f32, f32) +)w2c_template" +R"w2c_template(DEFINE_LOAD(f64_load, f64, f64, f64) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i32_load8_s, s8, s32, u32) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i64_load8_s, s8, s64, u64) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i32_load8_u, u8, u32, u32) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i64_load8_u, u8, u64, u64) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i32_load16_s, s16, s32, u32) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i64_load16_s, s16, s64, u64) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i32_load16_u, u16, u32, u32) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i64_load16_u, u16, u64, u64) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i64_load32_s, s32, s64, u64) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i64_load32_u, u32, u64, u64) +)w2c_template" +R"w2c_template(DEFINE_STORE(i32_store, u32, u32) +)w2c_template" +R"w2c_template(DEFINE_STORE(i64_store, u64, u64) +)w2c_template" +R"w2c_template(DEFINE_STORE(f32_store, f32, f32) +)w2c_template" +R"w2c_template(DEFINE_STORE(f64_store, f64, f64) +)w2c_template" +R"w2c_template(DEFINE_STORE(i32_store8, u8, u32) +)w2c_template" +R"w2c_template(DEFINE_STORE(i32_store16, u16, u32) +)w2c_template" +R"w2c_template(DEFINE_STORE(i64_store8, u8, u64) +)w2c_template" +R"w2c_template(DEFINE_STORE(i64_store16, u16, u64) +)w2c_template" +R"w2c_template(DEFINE_STORE(i64_store32, u32, u64) +)w2c_template" +R"w2c_template( +#if defined(WASM_RT_ENABLE_SIMD) +)w2c_template" +R"w2c_template( +#ifdef __x86_64__ +)w2c_template" +R"w2c_template(#define SIMD_FORCE_READ(var) wasm_asm("" ::"x"(var)); +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define SIMD_FORCE_READ(var) +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template(// TODO: equivalent constraint for ARM and other architectures +)w2c_template" +R"w2c_template( +#define DEFINE_SIMD_LOAD_FUNC(name, func, t) \ +)w2c_template" +R"w2c_template( static inline v128 name(wasm_rt_memory_t* mem, u64 addr) { \ +)w2c_template" +R"w2c_template( MEMCHECK(mem, addr, t); \ +)w2c_template" +R"w2c_template( v128 result = func((v128*)&mem->data[addr]); \ +)w2c_template" +R"w2c_template( SIMD_FORCE_READ(result); \ +)w2c_template" +R"w2c_template( return result; \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +#define DEFINE_SIMD_LOAD_LANE(name, func, t, lane) \ +)w2c_template" +R"w2c_template( static inline v128 name(wasm_rt_memory_t* mem, u64 addr, v128 vec) { \ +)w2c_template" +R"w2c_template( MEMCHECK(mem, addr, t); \ +)w2c_template" +R"w2c_template( v128 result = func((v128*)&mem->data[addr], vec, lane); \ +)w2c_template" +R"w2c_template( SIMD_FORCE_READ(result); \ +)w2c_template" +R"w2c_template( return result; \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +#define DEFINE_SIMD_STORE(name, t) \ +)w2c_template" +R"w2c_template( static inline void name(wasm_rt_memory_t* mem, u64 addr, v128 value) { \ +)w2c_template" +R"w2c_template( MEMCHECK(mem, addr, t); \ +)w2c_template" +R"w2c_template( simde_wasm_v128_store((v128*)&mem->data[addr], value); \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +#define DEFINE_SIMD_STORE_LANE(name, func, t, lane) \ +)w2c_template" +R"w2c_template( static inline void name(wasm_rt_memory_t* mem, u64 addr, v128 value) { \ +)w2c_template" +R"w2c_template( MEMCHECK(mem, addr, t); \ +)w2c_template" +R"w2c_template( func((v128*)&mem->data[addr], value, lane); \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +// clang-format off +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(v128_load, simde_wasm_v128_load, v128) +)w2c_template" +R"w2c_template( +DEFINE_SIMD_LOAD_FUNC(v128_load8_splat, simde_wasm_v128_load8_splat, u8) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(v128_load16_splat, simde_wasm_v128_load16_splat, u16) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(v128_load32_splat, simde_wasm_v128_load32_splat, u32) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(v128_load64_splat, simde_wasm_v128_load64_splat, u64) +)w2c_template" +R"w2c_template( +DEFINE_SIMD_LOAD_FUNC(i16x8_load8x8, simde_wasm_i16x8_load8x8, u64) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(u16x8_load8x8, simde_wasm_u16x8_load8x8, u64) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(i32x4_load16x4, simde_wasm_i32x4_load16x4, u64) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(u32x4_load16x4, simde_wasm_u32x4_load16x4, u64) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(i64x2_load32x2, simde_wasm_i64x2_load32x2, u64) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(u64x2_load32x2, simde_wasm_u64x2_load32x2, u64) +)w2c_template" +R"w2c_template( +DEFINE_SIMD_LOAD_FUNC(v128_load32_zero, simde_wasm_v128_load32_zero, u32) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(v128_load64_zero, simde_wasm_v128_load64_zero, u64) +)w2c_template" +R"w2c_template( +DEFINE_SIMD_LOAD_LANE(v128_load8_lane0, simde_wasm_v128_load8_lane, u8, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane1, simde_wasm_v128_load8_lane, u8, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane2, simde_wasm_v128_load8_lane, u8, 2) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane3, simde_wasm_v128_load8_lane, u8, 3) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane4, simde_wasm_v128_load8_lane, u8, 4) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane5, simde_wasm_v128_load8_lane, u8, 5) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane6, simde_wasm_v128_load8_lane, u8, 6) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane7, simde_wasm_v128_load8_lane, u8, 7) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane8, simde_wasm_v128_load8_lane, u8, 8) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane9, simde_wasm_v128_load8_lane, u8, 9) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane10, simde_wasm_v128_load8_lane, u8, 10) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane11, simde_wasm_v128_load8_lane, u8, 11) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane12, simde_wasm_v128_load8_lane, u8, 12) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane13, simde_wasm_v128_load8_lane, u8, 13) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane14, simde_wasm_v128_load8_lane, u8, 14) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane15, simde_wasm_v128_load8_lane, u8, 15) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane0, simde_wasm_v128_load16_lane, u16, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane1, simde_wasm_v128_load16_lane, u16, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane2, simde_wasm_v128_load16_lane, u16, 2) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane3, simde_wasm_v128_load16_lane, u16, 3) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane4, simde_wasm_v128_load16_lane, u16, 4) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane5, simde_wasm_v128_load16_lane, u16, 5) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane6, simde_wasm_v128_load16_lane, u16, 6) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane7, simde_wasm_v128_load16_lane, u16, 7) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load32_lane0, simde_wasm_v128_load32_lane, u32, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load32_lane1, simde_wasm_v128_load32_lane, u32, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load32_lane2, simde_wasm_v128_load32_lane, u32, 2) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load32_lane3, simde_wasm_v128_load32_lane, u32, 3) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load64_lane0, simde_wasm_v128_load64_lane, u64, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load64_lane1, simde_wasm_v128_load64_lane, u64, 1) +)w2c_template" +R"w2c_template( +DEFINE_SIMD_STORE(v128_store, v128) +)w2c_template" +R"w2c_template( +DEFINE_SIMD_STORE_LANE(v128_store8_lane0, simde_wasm_v128_store8_lane, u8, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane1, simde_wasm_v128_store8_lane, u8, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane2, simde_wasm_v128_store8_lane, u8, 2) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane3, simde_wasm_v128_store8_lane, u8, 3) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane4, simde_wasm_v128_store8_lane, u8, 4) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane5, simde_wasm_v128_store8_lane, u8, 5) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane6, simde_wasm_v128_store8_lane, u8, 6) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane7, simde_wasm_v128_store8_lane, u8, 7) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane8, simde_wasm_v128_store8_lane, u8, 8) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane9, simde_wasm_v128_store8_lane, u8, 9) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane10, simde_wasm_v128_store8_lane, u8, 10) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane11, simde_wasm_v128_store8_lane, u8, 11) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane12, simde_wasm_v128_store8_lane, u8, 12) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane13, simde_wasm_v128_store8_lane, u8, 13) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane14, simde_wasm_v128_store8_lane, u8, 14) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane15, simde_wasm_v128_store8_lane, u8, 15) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane0, simde_wasm_v128_store16_lane, u16, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane1, simde_wasm_v128_store16_lane, u16, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane2, simde_wasm_v128_store16_lane, u16, 2) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane3, simde_wasm_v128_store16_lane, u16, 3) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane4, simde_wasm_v128_store16_lane, u16, 4) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane5, simde_wasm_v128_store16_lane, u16, 5) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane6, simde_wasm_v128_store16_lane, u16, 6) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane7, simde_wasm_v128_store16_lane, u16, 7) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store32_lane0, simde_wasm_v128_store32_lane, u32, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store32_lane1, simde_wasm_v128_store32_lane, u32, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store32_lane2, simde_wasm_v128_store32_lane, u32, 2) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store32_lane3, simde_wasm_v128_store32_lane, u32, 3) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store64_lane0, simde_wasm_v128_store64_lane, u64, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store64_lane1, simde_wasm_v128_store64_lane, u64, 1) +)w2c_template" +R"w2c_template(// clang-format on +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +#if defined(_MSC_VER) +)w2c_template" +R"w2c_template( +// Adapted from +)w2c_template" +R"w2c_template(// https://github.com/nemequ/portable-snippets/blob/master/builtin/builtin.h +)w2c_template" +R"w2c_template( +static inline int I64_CLZ(unsigned long long v) { +)w2c_template" +R"w2c_template( unsigned long r = 0; +)w2c_template" +R"w2c_template(#if defined(_M_AMD64) || defined(_M_ARM) +)w2c_template" +R"w2c_template( if (_BitScanReverse64(&r, v)) { +)w2c_template" +R"w2c_template( return 63 - r; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template( if (_BitScanReverse(&r, (unsigned long)(v >> 32))) { +)w2c_template" +R"w2c_template( return 31 - r; +)w2c_template" +R"w2c_template( } else if (_BitScanReverse(&r, (unsigned long)v)) { +)w2c_template" +R"w2c_template( return 63 - r; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( return 64; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static inline int I32_CLZ(unsigned long v) { +)w2c_template" +R"w2c_template( unsigned long r = 0; +)w2c_template" +R"w2c_template( if (_BitScanReverse(&r, v)) { +)w2c_template" +R"w2c_template( return 31 - r; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return 32; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static inline int I64_CTZ(unsigned long long v) { +)w2c_template" +R"w2c_template( if (!v) { +)w2c_template" +R"w2c_template( return 64; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( unsigned long r = 0; +)w2c_template" +R"w2c_template(#if defined(_M_AMD64) || defined(_M_ARM) +)w2c_template" +R"w2c_template( _BitScanForward64(&r, v); +)w2c_template" +R"w2c_template( return (int)r; +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template( if (_BitScanForward(&r, (unsigned int)(v))) { +)w2c_template" +R"w2c_template( return (int)(r); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( + _BitScanForward(&r, (unsigned int)(v >> 32)); +)w2c_template" +R"w2c_template( return (int)(r + 32); +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static inline int I32_CTZ(unsigned long v) { +)w2c_template" +R"w2c_template( if (!v) { +)w2c_template" +R"w2c_template( return 32; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( unsigned long r = 0; +)w2c_template" +R"w2c_template( _BitScanForward(&r, v); +)w2c_template" +R"w2c_template( return (int)r; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +#define POPCOUNT_DEFINE_PORTABLE(f_n, T) \ +)w2c_template" +R"w2c_template( static inline u32 f_n(T x) { \ +)w2c_template" +R"w2c_template( x = x - ((x >> 1) & (T) ~(T)0 / 3); \ +)w2c_template" +R"w2c_template( x = (x & (T) ~(T)0 / 15 * 3) + ((x >> 2) & (T) ~(T)0 / 15 * 3); \ +)w2c_template" +R"w2c_template( x = (x + (x >> 4)) & (T) ~(T)0 / 255 * 15; \ +)w2c_template" +R"w2c_template( return (T)(x * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8; \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +POPCOUNT_DEFINE_PORTABLE(I32_POPCNT, u32) +)w2c_template" +R"w2c_template(POPCOUNT_DEFINE_PORTABLE(I64_POPCNT, u64) +)w2c_template" +R"w2c_template( +#undef POPCOUNT_DEFINE_PORTABLE +)w2c_template" +R"w2c_template( +#else +)w2c_template" +R"w2c_template( +#define I32_CLZ(x) ((x) ? __builtin_clz(x) : 32) +)w2c_template" +R"w2c_template(#define I64_CLZ(x) ((x) ? __builtin_clzll(x) : 64) +)w2c_template" +R"w2c_template(#define I32_CTZ(x) ((x) ? __builtin_ctz(x) : 32) +)w2c_template" +R"w2c_template(#define I64_CTZ(x) ((x) ? __builtin_ctzll(x) : 64) +)w2c_template" +R"w2c_template(#define I32_POPCNT(x) (__builtin_popcount(x)) +)w2c_template" +R"w2c_template(#define I64_POPCNT(x) (__builtin_popcountll(x)) +)w2c_template" +R"w2c_template( +#endif +)w2c_template" +R"w2c_template( +#define DIV_S(ut, min, x, y) \ +)w2c_template" +R"w2c_template( ((UNLIKELY((y) == 0)) \ +)w2c_template" +R"w2c_template( ? TRAP(DIV_BY_ZERO) \ +)w2c_template" +R"w2c_template( : (UNLIKELY((x) == min && (y) == -1)) ? TRAP(INT_OVERFLOW) \ +)w2c_template" +R"w2c_template( : (ut)((x) / (y))) +)w2c_template" +R"w2c_template( +#define REM_S(ut, min, x, y) \ +)w2c_template" +R"w2c_template( ((UNLIKELY((y) == 0)) \ +)w2c_template" +R"w2c_template( ? TRAP(DIV_BY_ZERO) \ +)w2c_template" +R"w2c_template( : (UNLIKELY((x) == min && (y) == -1)) ? 0 : (ut)((x) % (y))) +)w2c_template" +R"w2c_template( +#define I32_DIV_S(x, y) DIV_S(u32, INT32_MIN, (s32)x, (s32)y) +)w2c_template" +R"w2c_template(#define I64_DIV_S(x, y) DIV_S(u64, INT64_MIN, (s64)x, (s64)y) +)w2c_template" +R"w2c_template(#define I32_REM_S(x, y) REM_S(u32, INT32_MIN, (s32)x, (s32)y) +)w2c_template" +R"w2c_template(#define I64_REM_S(x, y) REM_S(u64, INT64_MIN, (s64)x, (s64)y) +)w2c_template" +R"w2c_template( +#define DIVREM_U(op, x, y) \ +)w2c_template" +R"w2c_template( ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) : ((x)op(y))) +)w2c_template" +R"w2c_template( +#define DIV_U(x, y) DIVREM_U(/, x, y) +)w2c_template" +R"w2c_template(#define REM_U(x, y) DIVREM_U(%, x, y) +)w2c_template" +R"w2c_template( +#define ROTL(x, y, mask) \ +)w2c_template" +R"w2c_template( (((x) << ((y) & (mask))) | ((x) >> (((mask) - (y) + 1) & (mask)))) +)w2c_template" +R"w2c_template(#define ROTR(x, y, mask) \ +)w2c_template" +R"w2c_template( (((x) >> ((y) & (mask))) | ((x) << (((mask) - (y) + 1) & (mask)))) +)w2c_template" +R"w2c_template( +#define I32_ROTL(x, y) ROTL(x, y, 31) +)w2c_template" +R"w2c_template(#define I64_ROTL(x, y) ROTL(x, y, 63) +)w2c_template" +R"w2c_template(#define I32_ROTR(x, y) ROTR(x, y, 31) +)w2c_template" +R"w2c_template(#define I64_ROTR(x, y) ROTR(x, y, 63) +)w2c_template" +R"w2c_template( +#define FMIN(x, y) \ +)w2c_template" +R"w2c_template( ((UNLIKELY((x) != (x))) \ +)w2c_template" +R"w2c_template( ? NAN \ +)w2c_template" +R"w2c_template( : (UNLIKELY((y) != (y))) \ +)w2c_template" +R"w2c_template( ? NAN \ +)w2c_template" +R"w2c_template( : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? x : y) \ +)w2c_template" +R"w2c_template( : (x < y) ? x : y) +)w2c_template" +R"w2c_template( +#define FMAX(x, y) \ +)w2c_template" +R"w2c_template( ((UNLIKELY((x) != (x))) \ +)w2c_template" +R"w2c_template( ? NAN \ +)w2c_template" +R"w2c_template( : (UNLIKELY((y) != (y))) \ +)w2c_template" +R"w2c_template( ? NAN \ +)w2c_template" +R"w2c_template( : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? y : x) \ +)w2c_template" +R"w2c_template( : (x > y) ? x : y) +)w2c_template" +R"w2c_template( +#define TRUNC_S(ut, st, ft, min, minop, max, x) \ +)w2c_template" +R"w2c_template( ((UNLIKELY((x) != (x))) \ +)w2c_template" +R"w2c_template( ? TRAP(INVALID_CONVERSION) \ +)w2c_template" +R"w2c_template( : (UNLIKELY(!((x)minop(min) && (x) < (max)))) ? TRAP(INT_OVERFLOW) \ +)w2c_template" +R"w2c_template( : (ut)(st)(x)) +)w2c_template" +R"w2c_template( +#define I32_TRUNC_S_F32(x) \ +)w2c_template" +R"w2c_template( TRUNC_S(u32, s32, f32, (f32)INT32_MIN, >=, 2147483648.f, x) +)w2c_template" +R"w2c_template(#define I64_TRUNC_S_F32(x) \ +)w2c_template" +R"w2c_template( TRUNC_S(u64, s64, f32, (f32)INT64_MIN, >=, (f32)INT64_MAX, x) +)w2c_template" +R"w2c_template(#define I32_TRUNC_S_F64(x) \ +)w2c_template" +R"w2c_template( TRUNC_S(u32, s32, f64, -2147483649., >, 2147483648., x) +)w2c_template" +R"w2c_template(#define I64_TRUNC_S_F64(x) \ +)w2c_template" +R"w2c_template( TRUNC_S(u64, s64, f64, (f64)INT64_MIN, >=, (f64)INT64_MAX, x) +)w2c_template" +R"w2c_template( +#define TRUNC_U(ut, ft, max, x) \ +)w2c_template" +R"w2c_template( ((UNLIKELY((x) != (x))) \ +)w2c_template" +R"w2c_template( ? TRAP(INVALID_CONVERSION) \ +)w2c_template" +R"w2c_template( : (UNLIKELY(!((x) > (ft)-1 && (x) < (max)))) ? TRAP(INT_OVERFLOW) \ +)w2c_template" +R"w2c_template( : (ut)(x)) +)w2c_template" +R"w2c_template( +#define I32_TRUNC_U_F32(x) TRUNC_U(u32, f32, 4294967296.f, x) +)w2c_template" +R"w2c_template(#define I64_TRUNC_U_F32(x) TRUNC_U(u64, f32, (f32)UINT64_MAX, x) +)w2c_template" +R"w2c_template(#define I32_TRUNC_U_F64(x) TRUNC_U(u32, f64, 4294967296., x) +)w2c_template" +R"w2c_template(#define I64_TRUNC_U_F64(x) TRUNC_U(u64, f64, (f64)UINT64_MAX, x) +)w2c_template" +R"w2c_template( +#define TRUNC_SAT_S(ut, st, ft, min, smin, minop, max, smax, x) \ +)w2c_template" +R"w2c_template( ((UNLIKELY((x) != (x))) \ +)w2c_template" +R"w2c_template( ? 0 \ +)w2c_template" +R"w2c_template( : (UNLIKELY(!((x)minop(min)))) \ +)w2c_template" +R"w2c_template( ? smin \ +)w2c_template" +R"w2c_template( : (UNLIKELY(!((x) < (max)))) ? smax : (ut)(st)(x)) +)w2c_template" +R"w2c_template( +#define I32_TRUNC_SAT_S_F32(x) \ +)w2c_template" +R"w2c_template( TRUNC_SAT_S(u32, s32, f32, (f32)INT32_MIN, INT32_MIN, >=, 2147483648.f, \ +)w2c_template" +R"w2c_template( INT32_MAX, x) +)w2c_template" +R"w2c_template(#define I64_TRUNC_SAT_S_F32(x) \ +)w2c_template" +R"w2c_template( TRUNC_SAT_S(u64, s64, f32, (f32)INT64_MIN, INT64_MIN, >=, (f32)INT64_MAX, \ +)w2c_template" +R"w2c_template( INT64_MAX, x) +)w2c_template" +R"w2c_template(#define I32_TRUNC_SAT_S_F64(x) \ +)w2c_template" +R"w2c_template( TRUNC_SAT_S(u32, s32, f64, -2147483649., INT32_MIN, >, 2147483648., \ +)w2c_template" +R"w2c_template( INT32_MAX, x) +)w2c_template" +R"w2c_template(#define I64_TRUNC_SAT_S_F64(x) \ +)w2c_template" +R"w2c_template( TRUNC_SAT_S(u64, s64, f64, (f64)INT64_MIN, INT64_MIN, >=, (f64)INT64_MAX, \ +)w2c_template" +R"w2c_template( INT64_MAX, x) +)w2c_template" +R"w2c_template( +#define TRUNC_SAT_U(ut, ft, max, smax, x) \ +)w2c_template" +R"w2c_template( ((UNLIKELY((x) != (x))) ? 0 \ +)w2c_template" +R"w2c_template( : (UNLIKELY(!((x) > (ft)-1))) \ +)w2c_template" +R"w2c_template( ? 0 \ +)w2c_template" +R"w2c_template( : (UNLIKELY(!((x) < (max)))) ? smax : (ut)(x)) +)w2c_template" +R"w2c_template( +#define I32_TRUNC_SAT_U_F32(x) \ +)w2c_template" +R"w2c_template( TRUNC_SAT_U(u32, f32, 4294967296.f, UINT32_MAX, x) +)w2c_template" +R"w2c_template(#define I64_TRUNC_SAT_U_F32(x) \ +)w2c_template" +R"w2c_template( TRUNC_SAT_U(u64, f32, (f32)UINT64_MAX, UINT64_MAX, x) +)w2c_template" +R"w2c_template(#define I32_TRUNC_SAT_U_F64(x) TRUNC_SAT_U(u32, f64, 4294967296., UINT32_MAX, x) +)w2c_template" +R"w2c_template(#define I64_TRUNC_SAT_U_F64(x) \ +)w2c_template" +R"w2c_template( TRUNC_SAT_U(u64, f64, (f64)UINT64_MAX, UINT64_MAX, x) +)w2c_template" +R"w2c_template( +#define DEFINE_REINTERPRET(name, t1, t2) \ +)w2c_template" +R"w2c_template( static inline t2 name(t1 x) { \ +)w2c_template" +R"w2c_template( t2 result; \ +)w2c_template" +R"w2c_template( memcpy(&result, &x, sizeof(result)); \ +)w2c_template" +R"w2c_template( return result; \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +DEFINE_REINTERPRET(f32_reinterpret_i32, u32, f32) +)w2c_template" +R"w2c_template(DEFINE_REINTERPRET(i32_reinterpret_f32, f32, u32) +)w2c_template" +R"w2c_template(DEFINE_REINTERPRET(f64_reinterpret_i64, u64, f64) +)w2c_template" +R"w2c_template(DEFINE_REINTERPRET(i64_reinterpret_f64, f64, u64) +)w2c_template" +R"w2c_template( +static float quiet_nanf(float x) { +)w2c_template" +R"w2c_template( uint32_t tmp; +)w2c_template" +R"w2c_template( memcpy(&tmp, &x, 4); +)w2c_template" +R"w2c_template( tmp |= 0x7fc00000lu; +)w2c_template" +R"w2c_template( memcpy(&x, &tmp, 4); +)w2c_template" +R"w2c_template( return x; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static double quiet_nan(double x) { +)w2c_template" +R"w2c_template( uint64_t tmp; +)w2c_template" +R"w2c_template( memcpy(&tmp, &x, 8); +)w2c_template" +R"w2c_template( tmp |= 0x7ff8000000000000llu; +)w2c_template" +R"w2c_template( memcpy(&x, &tmp, 8); +)w2c_template" +R"w2c_template( return x; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static double wasm_quiet(double x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nan(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return x; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static float wasm_quietf(float x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nanf(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return x; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static double wasm_floor(double x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nan(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return floor(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static float wasm_floorf(float x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nanf(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return floorf(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static double wasm_ceil(double x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nan(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return ceil(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static float wasm_ceilf(float x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nanf(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return ceilf(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static double wasm_trunc(double x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nan(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return trunc(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static float wasm_truncf(float x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nanf(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return truncf(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static float wasm_nearbyintf(float x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nanf(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return nearbyintf(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static double wasm_nearbyint(double x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nan(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return nearbyint(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static float wasm_fabsf(float x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( uint32_t tmp; +)w2c_template" +R"w2c_template( memcpy(&tmp, &x, 4); +)w2c_template" +R"w2c_template( tmp = tmp & ~(1UL << 31); +)w2c_template" +R"w2c_template( memcpy(&x, &tmp, 4); +)w2c_template" +R"w2c_template( return x; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return fabsf(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static double wasm_fabs(double x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( uint64_t tmp; +)w2c_template" +R"w2c_template( memcpy(&tmp, &x, 8); +)w2c_template" +R"w2c_template( tmp = tmp & ~(1ULL << 63); +)w2c_template" +R"w2c_template( memcpy(&x, &tmp, 8); +)w2c_template" +R"w2c_template( return x; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return fabs(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static double wasm_sqrt(double x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nan(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return sqrt(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static float wasm_sqrtf(float x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nanf(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return sqrtf(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static inline void memory_fill(wasm_rt_memory_t* mem, u32 d, u32 val, u32 n) { +)w2c_template" +R"w2c_template( RANGE_CHECK(mem, d, n); +)w2c_template" +R"w2c_template( memset(mem->data + d, val, n); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static inline void memory_copy(wasm_rt_memory_t* dest, +)w2c_template" +R"w2c_template( const wasm_rt_memory_t* src, +)w2c_template" +R"w2c_template( u32 dest_addr, +)w2c_template" +R"w2c_template( u32 src_addr, +)w2c_template" +R"w2c_template( u32 n) { +)w2c_template" +R"w2c_template( RANGE_CHECK(dest, dest_addr, n); +)w2c_template" +R"w2c_template( RANGE_CHECK(src, src_addr, n); +)w2c_template" +R"w2c_template( memmove(dest->data + dest_addr, src->data + src_addr, n); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static inline void memory_init(wasm_rt_memory_t* dest, +)w2c_template" +R"w2c_template( const u8* src, +)w2c_template" +R"w2c_template( u32 src_size, +)w2c_template" +R"w2c_template( u32 dest_addr, +)w2c_template" +R"w2c_template( u32 src_addr, +)w2c_template" +R"w2c_template( u32 n) { +)w2c_template" +R"w2c_template( if (UNLIKELY(src_addr + (uint64_t)n > src_size)) +)w2c_template" +R"w2c_template( TRAP(OOB); +)w2c_template" +R"w2c_template( LOAD_DATA((*dest), dest_addr, src + src_addr, n); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +typedef struct { +)w2c_template" +R"w2c_template( wasm_rt_func_type_t type; +)w2c_template" +R"w2c_template( wasm_rt_function_ptr_t func; +)w2c_template" +R"w2c_template( size_t module_offset; +)w2c_template" +R"w2c_template(} wasm_elem_segment_expr_t; +)w2c_template" +R"w2c_template( +static inline void funcref_table_init(wasm_rt_funcref_table_t* dest, +)w2c_template" +R"w2c_template( const wasm_elem_segment_expr_t* src, +)w2c_template" +R"w2c_template( u32 src_size, +)w2c_template" +R"w2c_template( u32 dest_addr, +)w2c_template" +R"w2c_template( u32 src_addr, +)w2c_template" +R"w2c_template( u32 n, +)w2c_template" +R"w2c_template( void* module_instance) { +)w2c_template" +R"w2c_template( if (UNLIKELY(src_addr + (uint64_t)n > src_size)) +)w2c_template" +R"w2c_template( TRAP(OOB); +)w2c_template" +R"w2c_template( if (UNLIKELY(dest_addr + (uint64_t)n > dest->size)) +)w2c_template" +R"w2c_template( TRAP(OOB); +)w2c_template" +R"w2c_template( for (u32 i = 0; i < n; i++) { +)w2c_template" +R"w2c_template( const wasm_elem_segment_expr_t* src_expr = &src[src_addr + i]; +)w2c_template" +R"w2c_template( dest->data[dest_addr + i] = +)w2c_template" +R"w2c_template( (wasm_rt_funcref_t){src_expr->type, src_expr->func, +)w2c_template" +R"w2c_template( (char*)module_instance + src_expr->module_offset}; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +// Currently Wasm only supports initializing externref tables with ref.null. +)w2c_template" +R"w2c_template(static inline void externref_table_init(wasm_rt_externref_table_t* dest, +)w2c_template" +R"w2c_template( u32 src_size, +)w2c_template" +R"w2c_template( u32 dest_addr, +)w2c_template" +R"w2c_template( u32 src_addr, +)w2c_template" +R"w2c_template( u32 n) { +)w2c_template" +R"w2c_template( if (UNLIKELY(src_addr + (uint64_t)n > src_size)) +)w2c_template" +R"w2c_template( TRAP(OOB); +)w2c_template" +R"w2c_template( if (UNLIKELY(dest_addr + (uint64_t)n > dest->size)) +)w2c_template" +R"w2c_template( TRAP(OOB); +)w2c_template" +R"w2c_template( for (u32 i = 0; i < n; i++) { +)w2c_template" +R"w2c_template( dest->data[dest_addr + i] = wasm_rt_externref_null_value; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +#define DEFINE_TABLE_COPY(type) \ +)w2c_template" +R"w2c_template( static inline void type##_table_copy(wasm_rt_##type##_table_t* dest, \ +)w2c_template" +R"w2c_template( const wasm_rt_##type##_table_t* src, \ +)w2c_template" +R"w2c_template( u32 dest_addr, u32 src_addr, u32 n) { \ +)w2c_template" +R"w2c_template( if (UNLIKELY(dest_addr + (uint64_t)n > dest->size)) \ +)w2c_template" +R"w2c_template( TRAP(OOB); \ +)w2c_template" +R"w2c_template( if (UNLIKELY(src_addr + (uint64_t)n > src->size)) \ +)w2c_template" +R"w2c_template( TRAP(OOB); \ +)w2c_template" +R"w2c_template( \ +)w2c_template" +R"w2c_template( memmove(dest->data + dest_addr, src->data + src_addr, \ +)w2c_template" +R"w2c_template( n * sizeof(wasm_rt_##type##_t)); \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +DEFINE_TABLE_COPY(funcref) +)w2c_template" +R"w2c_template(DEFINE_TABLE_COPY(externref) +)w2c_template" +R"w2c_template( +#define DEFINE_TABLE_GET(type) \ +)w2c_template" +R"w2c_template( static inline wasm_rt_##type##_t type##_table_get( \ +)w2c_template" +R"w2c_template( const wasm_rt_##type##_table_t* table, u32 i) { \ +)w2c_template" +R"w2c_template( if (UNLIKELY(i >= table->size)) \ +)w2c_template" +R"w2c_template( TRAP(OOB); \ +)w2c_template" +R"w2c_template( return table->data[i]; \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +DEFINE_TABLE_GET(funcref) +)w2c_template" +R"w2c_template(DEFINE_TABLE_GET(externref) +)w2c_template" +R"w2c_template( +#define DEFINE_TABLE_SET(type) \ +)w2c_template" +R"w2c_template( static inline void type##_table_set(const wasm_rt_##type##_table_t* table, \ +)w2c_template" +R"w2c_template( u32 i, const wasm_rt_##type##_t val) { \ +)w2c_template" +R"w2c_template( if (UNLIKELY(i >= table->size)) \ +)w2c_template" +R"w2c_template( TRAP(OOB); \ +)w2c_template" +R"w2c_template( table->data[i] = val; \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +DEFINE_TABLE_SET(funcref) +)w2c_template" +R"w2c_template(DEFINE_TABLE_SET(externref) +)w2c_template" +R"w2c_template( +#define DEFINE_TABLE_FILL(type) \ +)w2c_template" +R"w2c_template( static inline void type##_table_fill(const wasm_rt_##type##_table_t* table, \ +)w2c_template" +R"w2c_template( u32 d, const wasm_rt_##type##_t val, \ +)w2c_template" +R"w2c_template( u32 n) { \ +)w2c_template" +R"w2c_template( if (UNLIKELY((uint64_t)d + n > table->size)) \ +)w2c_template" +R"w2c_template( TRAP(OOB); \ +)w2c_template" +R"w2c_template( for (uint32_t i = d; i < d + n; i++) { \ +)w2c_template" +R"w2c_template( table->data[i] = val; \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +DEFINE_TABLE_FILL(funcref) +)w2c_template" +R"w2c_template(DEFINE_TABLE_FILL(externref) +)w2c_template" +R"w2c_template( +#if defined(__GNUC__) || defined(__clang__) +)w2c_template" +R"w2c_template(#define FUNC_TYPE_DECL_EXTERN_T(x) extern const char* const x +)w2c_template" +R"w2c_template(#define FUNC_TYPE_EXTERN_T(x) const char* const x +)w2c_template" +R"w2c_template(#define FUNC_TYPE_T(x) static const char* const x +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define FUNC_TYPE_DECL_EXTERN_T(x) extern const char x[] +)w2c_template" +R"w2c_template(#define FUNC_TYPE_EXTERN_T(x) const char x[] +)w2c_template" +R"w2c_template(#define FUNC_TYPE_T(x) static const char x[] +)w2c_template" +R"w2c_template(#endif +)w2c_template" +; diff --git a/third_party/wasm2c/src/prebuilt/wasm2c_source_includes.cc b/third_party/wasm2c/src/prebuilt/wasm2c_source_includes.cc new file mode 100644 index 0000000000..a17c5511b7 --- /dev/null +++ b/third_party/wasm2c/src/prebuilt/wasm2c_source_includes.cc @@ -0,0 +1,33 @@ +const char* s_source_includes = R"w2c_template(#include <assert.h> +)w2c_template" +R"w2c_template(#include <math.h> +)w2c_template" +R"w2c_template(#include <stdarg.h> +)w2c_template" +R"w2c_template(#include <stddef.h> +)w2c_template" +R"w2c_template(#include <string.h> +)w2c_template" +R"w2c_template(#if defined(__MINGW32__) +)w2c_template" +R"w2c_template(#include <malloc.h> +)w2c_template" +R"w2c_template(#elif defined(_MSC_VER) +)w2c_template" +R"w2c_template(#include <intrin.h> +)w2c_template" +R"w2c_template(#include <malloc.h> +)w2c_template" +R"w2c_template(#define alloca _alloca +)w2c_template" +R"w2c_template(#elif defined(__FreeBSD__) || defined(__OpenBSD__) +)w2c_template" +R"w2c_template(#include <stdlib.h> +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#include <alloca.h> +)w2c_template" +R"w2c_template(#endif +)w2c_template" +; diff --git a/third_party/wasm2c/src/resolve-names.cc b/third_party/wasm2c/src/resolve-names.cc new file mode 100644 index 0000000000..9f0b3b0269 --- /dev/null +++ b/third_party/wasm2c/src/resolve-names.cc @@ -0,0 +1,649 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/resolve-names.h" + +#include <cassert> +#include <cstdio> + +#include "wabt/cast.h" +#include "wabt/expr-visitor.h" +#include "wabt/ir.h" +#include "wabt/wast-lexer.h" + +namespace wabt { + +namespace { + +class NameResolver : public ExprVisitor::DelegateNop { + public: + NameResolver(Script* script, Errors* errors); + + Result VisitModule(Module* module); + Result VisitScript(Script* script); + + // Implementation of ExprVisitor::DelegateNop. + Result BeginBlockExpr(BlockExpr*) override; + Result EndBlockExpr(BlockExpr*) override; + Result OnBrExpr(BrExpr*) override; + Result OnBrIfExpr(BrIfExpr*) override; + Result OnBrTableExpr(BrTableExpr*) override; + Result OnCallExpr(CallExpr*) override; + Result OnCallIndirectExpr(CallIndirectExpr*) override; + Result OnCatchExpr(TryExpr*, Catch*) override; + Result OnDelegateExpr(TryExpr*) override; + Result OnReturnCallExpr(ReturnCallExpr*) override; + Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr*) override; + Result OnGlobalGetExpr(GlobalGetExpr*) override; + Result OnGlobalSetExpr(GlobalSetExpr*) override; + Result BeginIfExpr(IfExpr*) override; + Result EndIfExpr(IfExpr*) override; + Result OnLoadExpr(LoadExpr*) override; + Result OnLocalGetExpr(LocalGetExpr*) override; + Result OnLocalSetExpr(LocalSetExpr*) override; + Result OnLocalTeeExpr(LocalTeeExpr*) override; + Result BeginLoopExpr(LoopExpr*) override; + Result EndLoopExpr(LoopExpr*) override; + Result OnMemoryCopyExpr(MemoryCopyExpr*) override; + Result OnDataDropExpr(DataDropExpr*) override; + Result OnMemoryFillExpr(MemoryFillExpr*) override; + Result OnMemoryGrowExpr(MemoryGrowExpr*) override; + Result OnMemoryInitExpr(MemoryInitExpr*) override; + Result OnMemorySizeExpr(MemorySizeExpr*) override; + Result OnElemDropExpr(ElemDropExpr*) override; + Result OnTableCopyExpr(TableCopyExpr*) override; + Result OnTableInitExpr(TableInitExpr*) override; + Result OnTableGetExpr(TableGetExpr*) override; + Result OnTableSetExpr(TableSetExpr*) override; + Result OnTableGrowExpr(TableGrowExpr*) override; + Result OnTableSizeExpr(TableSizeExpr*) override; + Result OnTableFillExpr(TableFillExpr*) override; + Result OnRefFuncExpr(RefFuncExpr*) override; + Result OnStoreExpr(StoreExpr*) override; + Result BeginTryExpr(TryExpr*) override; + Result EndTryExpr(TryExpr*) override; + Result OnThrowExpr(ThrowExpr*) override; + Result OnRethrowExpr(RethrowExpr*) override; + Result OnSimdLoadLaneExpr(SimdLoadLaneExpr*) override; + Result OnSimdStoreLaneExpr(SimdStoreLaneExpr*) override; + + private: + void PrintError(const Location* loc, const char* fmt, ...); + void PushLabel(const std::string& label); + void PopLabel(); + void CheckDuplicateBindings(const BindingHash* bindings, const char* desc); + void PrintDuplicateBindingsError(const BindingHash::value_type&, + const BindingHash::value_type&, + const char* desc); + void ResolveLabelVar(Var* var); + void ResolveVar(const BindingHash* bindings, Var* var, const char* desc); + void ResolveFuncVar(Var* var); + void ResolveGlobalVar(Var* var); + void ResolveFuncTypeVar(Var* var); + void ResolveTableVar(Var* var); + void ResolveMemoryVar(Var* var); + void ResolveTagVar(Var* var); + void ResolveDataSegmentVar(Var* var); + void ResolveElemSegmentVar(Var* var); + void ResolveLocalVar(Var* var); + void ResolveBlockDeclarationVar(BlockDeclaration* decl); + void VisitFunc(Func* func); + void VisitExport(Export* export_); + void VisitGlobal(Global* global); + void VisitTag(Tag* tag); + void VisitElemSegment(ElemSegment* segment); + void VisitDataSegment(DataSegment* segment); + void VisitScriptModule(ScriptModule* script_module); + void VisitCommand(Command* command); + + Errors* errors_ = nullptr; + Script* script_ = nullptr; + Module* current_module_ = nullptr; + Func* current_func_ = nullptr; + ExprVisitor visitor_; + std::vector<std::string> labels_; + Result result_ = Result::Ok; +}; + +NameResolver::NameResolver(Script* script, Errors* errors) + : errors_(errors), script_(script), visitor_(this) {} + +} // end anonymous namespace + +void WABT_PRINTF_FORMAT(3, 4) NameResolver::PrintError(const Location* loc, + const char* format, + ...) { + result_ = Result::Error; + WABT_SNPRINTF_ALLOCA(buffer, length, format); + errors_->emplace_back(ErrorLevel::Error, *loc, buffer); +} + +void NameResolver::PushLabel(const std::string& label) { + labels_.push_back(label); +} + +void NameResolver::PopLabel() { + labels_.pop_back(); +} + +void NameResolver::CheckDuplicateBindings(const BindingHash* bindings, + const char* desc) { + bindings->FindDuplicates([this, desc](const BindingHash::value_type& a, + const BindingHash::value_type& b) { + PrintDuplicateBindingsError(a, b, desc); + }); +} + +void NameResolver::PrintDuplicateBindingsError(const BindingHash::value_type& a, + const BindingHash::value_type& b, + const char* desc) { + // Choose the location that is later in the file. + const Location& a_loc = a.second.loc; + const Location& b_loc = b.second.loc; + const Location& loc = a_loc.line > b_loc.line ? a_loc : b_loc; + PrintError(&loc, "redefinition of %s \"%s\"", desc, a.first.c_str()); +} + +void NameResolver::ResolveLabelVar(Var* var) { + if (var->is_name()) { + for (int i = labels_.size() - 1; i >= 0; --i) { + const std::string& label = labels_[i]; + if (label == var->name()) { + var->set_index(labels_.size() - i - 1); + return; + } + } + PrintError(&var->loc, "undefined label variable \"%s\"", + var->name().c_str()); + } +} + +void NameResolver::ResolveVar(const BindingHash* bindings, + Var* var, + const char* desc) { + if (var->is_name()) { + Index index = bindings->FindIndex(*var); + if (index == kInvalidIndex) { + PrintError(&var->loc, "undefined %s variable \"%s\"", desc, + var->name().c_str()); + return; + } + + var->set_index(index); + } +} + +void NameResolver::ResolveFuncVar(Var* var) { + ResolveVar(¤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::OnLoadExpr(LoadExpr* expr) { + ResolveMemoryVar(&expr->memidx); + return Result::Ok; +} + +Result NameResolver::OnLocalGetExpr(LocalGetExpr* expr) { + ResolveLocalVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnLocalSetExpr(LocalSetExpr* expr) { + ResolveLocalVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnLocalTeeExpr(LocalTeeExpr* expr) { + ResolveLocalVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnMemoryCopyExpr(MemoryCopyExpr* expr) { + ResolveMemoryVar(&expr->srcmemidx); + ResolveMemoryVar(&expr->destmemidx); + return Result::Ok; +} + +Result NameResolver::OnDataDropExpr(DataDropExpr* expr) { + ResolveDataSegmentVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnMemoryFillExpr(MemoryFillExpr* expr) { + ResolveMemoryVar(&expr->memidx); + return Result::Ok; +} + +Result NameResolver::OnMemoryGrowExpr(MemoryGrowExpr* expr) { + ResolveMemoryVar(&expr->memidx); + return Result::Ok; +} + +Result NameResolver::OnMemoryInitExpr(MemoryInitExpr* expr) { + ResolveDataSegmentVar(&expr->var); + ResolveMemoryVar(&expr->memidx); + return Result::Ok; +} + +Result NameResolver::OnMemorySizeExpr(MemorySizeExpr* expr) { + ResolveMemoryVar(&expr->memidx); + return Result::Ok; +} + +Result NameResolver::OnElemDropExpr(ElemDropExpr* expr) { + ResolveElemSegmentVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnTableCopyExpr(TableCopyExpr* expr) { + ResolveTableVar(&expr->dst_table); + ResolveTableVar(&expr->src_table); + return Result::Ok; +} + +Result NameResolver::OnTableInitExpr(TableInitExpr* expr) { + ResolveElemSegmentVar(&expr->segment_index); + ResolveTableVar(&expr->table_index); + return Result::Ok; +} + +Result NameResolver::OnTableGetExpr(TableGetExpr* expr) { + ResolveTableVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnTableSetExpr(TableSetExpr* expr) { + ResolveTableVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnTableGrowExpr(TableGrowExpr* expr) { + ResolveTableVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnTableSizeExpr(TableSizeExpr* expr) { + ResolveTableVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnTableFillExpr(TableFillExpr* expr) { + ResolveTableVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnRefFuncExpr(RefFuncExpr* expr) { + ResolveFuncVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnStoreExpr(StoreExpr* expr) { + ResolveMemoryVar(&expr->memidx); + return Result::Ok; +} + +Result NameResolver::BeginTryExpr(TryExpr* expr) { + PushLabel(expr->block.label); + ResolveBlockDeclarationVar(&expr->block.decl); + return Result::Ok; +} + +Result NameResolver::EndTryExpr(TryExpr*) { + PopLabel(); + return Result::Ok; +} + +Result NameResolver::OnCatchExpr(TryExpr*, Catch* catch_) { + if (!catch_->IsCatchAll()) { + ResolveTagVar(&catch_->var); + } + return Result::Ok; +} + +Result NameResolver::OnDelegateExpr(TryExpr* expr) { + // Pop the label here as a try-delegate has no `end` instruction. + PopLabel(); + + // We resolve *after* popping the label in order to ensure that the + // delegate label starts counting after the current try-delegate. + ResolveLabelVar(&expr->delegate_target); + + return Result::Ok; +} + +Result NameResolver::OnThrowExpr(ThrowExpr* expr) { + ResolveTagVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnRethrowExpr(RethrowExpr* expr) { + // Note: the variable refers to corresponding (enclosing) catch, using the try + // block label for context. + ResolveLabelVar(&expr->var); + return Result::Ok; +} + +Result NameResolver::OnSimdLoadLaneExpr(SimdLoadLaneExpr* expr) { + ResolveMemoryVar(&expr->memidx); + return Result::Ok; +} + +Result NameResolver::OnSimdStoreLaneExpr(SimdStoreLaneExpr* expr) { + ResolveMemoryVar(&expr->memidx); + return Result::Ok; +} + +void NameResolver::VisitFunc(Func* func) { + current_func_ = func; + if (func->decl.has_func_type) { + ResolveFuncTypeVar(&func->decl.type_var); + } + + func->bindings.FindDuplicates( + [=](const BindingHash::value_type& a, const BindingHash::value_type& b) { + const char* desc = + (a.second.index < func->GetNumParams()) ? "parameter" : "local"; + PrintDuplicateBindingsError(a, b, desc); + }); + + visitor_.VisitFunc(func); + current_func_ = nullptr; +} + +void NameResolver::VisitExport(Export* export_) { + switch (export_->kind) { + case ExternalKind::Func: + ResolveFuncVar(&export_->var); + break; + + case ExternalKind::Table: + ResolveTableVar(&export_->var); + break; + + case ExternalKind::Memory: + ResolveMemoryVar(&export_->var); + break; + + case ExternalKind::Global: + ResolveGlobalVar(&export_->var); + break; + + case ExternalKind::Tag: + ResolveTagVar(&export_->var); + break; + } +} + +void NameResolver::VisitGlobal(Global* global) { + visitor_.VisitExprList(global->init_expr); +} + +void NameResolver::VisitTag(Tag* tag) { + if (tag->decl.has_func_type) { + ResolveFuncTypeVar(&tag->decl.type_var); + } +} + +void NameResolver::VisitElemSegment(ElemSegment* segment) { + ResolveTableVar(&segment->table_var); + visitor_.VisitExprList(segment->offset); + for (ExprList& elem_expr : segment->elem_exprs) { + if (elem_expr.size() == 1 && + elem_expr.front().type() == ExprType::RefFunc) { + ResolveFuncVar(&cast<RefFuncExpr>(&elem_expr.front())->var); + } + } +} + +void NameResolver::VisitDataSegment(DataSegment* segment) { + ResolveMemoryVar(&segment->memory_var); + visitor_.VisitExprList(segment->offset); +} + +Result NameResolver::VisitModule(Module* module) { + current_module_ = module; + CheckDuplicateBindings(&module->elem_segment_bindings, "elem"); + CheckDuplicateBindings(&module->func_bindings, "function"); + CheckDuplicateBindings(&module->global_bindings, "global"); + CheckDuplicateBindings(&module->type_bindings, "type"); + CheckDuplicateBindings(&module->table_bindings, "table"); + CheckDuplicateBindings(&module->memory_bindings, "memory"); + CheckDuplicateBindings(&module->tag_bindings, "tag"); + + for (Func* func : module->funcs) + VisitFunc(func); + for (Export* export_ : module->exports) + VisitExport(export_); + for (Global* global : module->globals) + VisitGlobal(global); + for (Tag* tag : module->tags) + VisitTag(tag); + for (ElemSegment* elem_segment : module->elem_segments) + VisitElemSegment(elem_segment); + for (DataSegment* data_segment : module->data_segments) + VisitDataSegment(data_segment); + for (Var* start : module->starts) + ResolveFuncVar(start); + current_module_ = nullptr; + return result_; +} + +void NameResolver::VisitScriptModule(ScriptModule* script_module) { + if (auto* tsm = dyn_cast<TextScriptModule>(script_module)) { + VisitModule(&tsm->module); + } +} + +void NameResolver::VisitCommand(Command* command) { + switch (command->type) { + case CommandType::Module: + VisitModule(&cast<ModuleCommand>(command)->module); + break; + + case CommandType::ScriptModule: + VisitModule(&cast<ScriptModuleCommand>(command)->module); + break; + + case CommandType::Action: + case CommandType::AssertReturn: + case CommandType::AssertTrap: + case CommandType::AssertExhaustion: + case CommandType::AssertException: + case CommandType::Register: + /* Don't resolve a module_var, since it doesn't really behave like other + * vars. You can't reference a module by index. */ + break; + + case CommandType::AssertMalformed: + /* Malformed modules should not be text; the whole point of this + * assertion is to test for malformed binary modules. */ + break; + + case CommandType::AssertInvalid: { + auto* assert_invalid_command = cast<AssertInvalidCommand>(command); + /* The module may be invalid because the names cannot be resolved; we + * don't want to print errors or fail if that's the case, but we still + * should try to resolve names when possible. */ + Errors errors; + NameResolver new_resolver(script_, &errors); + new_resolver.VisitScriptModule(assert_invalid_command->module.get()); + break; + } + + case CommandType::AssertUnlinkable: + VisitScriptModule(cast<AssertUnlinkableCommand>(command)->module.get()); + break; + + case CommandType::AssertUninstantiable: + VisitScriptModule( + cast<AssertUninstantiableCommand>(command)->module.get()); + break; + } +} + +Result NameResolver::VisitScript(Script* script) { + for (const std::unique_ptr<Command>& command : script->commands) + VisitCommand(command.get()); + return result_; +} + +Result ResolveNamesModule(Module* module, Errors* errors) { + NameResolver resolver(nullptr, errors); + return resolver.VisitModule(module); +} + +Result ResolveNamesScript(Script* script, Errors* errors) { + NameResolver resolver(script, errors); + return resolver.VisitScript(script); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/sha256.cc b/third_party/wasm2c/src/sha256.cc new file mode 100644 index 0000000000..efcb27a1e6 --- /dev/null +++ b/third_party/wasm2c/src/sha256.cc @@ -0,0 +1,48 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/sha256.h" + +#if HAVE_OPENSSL_SHA_H +#include <openssl/sha.h> +#else +#include "picosha2.h" +#endif + +namespace wabt { + +/** + * SHA-256 the "input" sv into the output "digest". + * + * Uses OpenSSL's libcrypto or vendored PicoSHA2. + */ +void sha256(std::string_view input, std::string& digest) { + digest.clear(); + +#if HAVE_OPENSSL_SHA_H + digest.resize(SHA256_DIGEST_LENGTH); + if (!SHA256(reinterpret_cast<const uint8_t*>(input.data()), input.size(), + reinterpret_cast<uint8_t*>(digest.data()))) { + // should not be possible to fail here, but check (and abort) just in case + abort(); + } +#else + digest.resize(picosha2::k_digest_size); + picosha2::hash256(input, digest); +#endif +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/shared-validator.cc b/third_party/wasm2c/src/shared-validator.cc new file mode 100644 index 0000000000..ec596cad98 --- /dev/null +++ b/third_party/wasm2c/src/shared-validator.cc @@ -0,0 +1,1203 @@ +/* + * Copyright 2020 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/shared-validator.h" + +#include <algorithm> +#include <cinttypes> +#include <limits> + +namespace wabt { + +TypeVector SharedValidator::ToTypeVector(Index count, const Type* types) { + return TypeVector(&types[0], &types[count]); +} + +SharedValidator::SharedValidator(Errors* errors, const ValidateOptions& options) + : options_(options), errors_(errors), typechecker_(options.features) { + typechecker_.set_error_callback( + [this](const char* msg) { OnTypecheckerError(msg); }); +} + +Result WABT_PRINTF_FORMAT(3, 4) SharedValidator::PrintError(const Location& loc, + const char* format, + ...) { + WABT_SNPRINTF_ALLOCA(buffer, length, format); + errors_->emplace_back(ErrorLevel::Error, loc, buffer); + return Result::Error; +} + +void SharedValidator::OnTypecheckerError(const char* msg) { + PrintError(expr_loc_, "%s", msg); +} + +Result SharedValidator::OnFuncType(const Location& loc, + Index param_count, + const Type* param_types, + Index result_count, + const Type* result_types, + Index type_index) { + Result result = Result::Ok; + if (!options_.features.multi_value_enabled() && result_count > 1) { + result |= PrintError(loc, + "multiple result values are not supported without " + "multi-value enabled."); + } + func_types_.emplace( + num_types_++, + FuncType{ToTypeVector(param_count, param_types), + ToTypeVector(result_count, result_types), type_index}); + return result; +} + +Result SharedValidator::OnStructType(const Location&, + Index field_count, + TypeMut* fields) { + struct_types_.emplace(num_types_++, StructType{TypeMutVector( + &fields[0], &fields[field_count])}); + return Result::Ok; +} + +Result SharedValidator::OnArrayType(const Location&, TypeMut field) { + array_types_.emplace(num_types_++, ArrayType{field}); + return Result::Ok; +} + +Result SharedValidator::OnFunction(const Location& loc, Var sig_var) { + Result result = Result::Ok; + FuncType type; + result |= CheckFuncTypeIndex(sig_var, &type); + funcs_.push_back(type); + return result; +} + +Result SharedValidator::CheckLimits(const Location& loc, + const Limits& limits, + uint64_t absolute_max, + const char* desc) { + Result result = Result::Ok; + if (limits.initial > absolute_max) { + result |= + PrintError(loc, "initial %s (%" PRIu64 ") must be <= (%" PRIu64 ")", + desc, limits.initial, absolute_max); + } + + if (limits.has_max) { + if (limits.max > absolute_max) { + result |= PrintError(loc, "max %s (%" PRIu64 ") must be <= (%" PRIu64 ")", + desc, limits.max, absolute_max); + } + + if (limits.max < limits.initial) { + result |= PrintError( + loc, "max %s (%" PRIu64 ") must be >= initial %s (%" PRIu64 ")", desc, + limits.max, desc, limits.initial); + } + } + return result; +} + +Result SharedValidator::OnTable(const Location& loc, + Type elem_type, + const Limits& limits) { + Result result = Result::Ok; + if (tables_.size() > 0 && !options_.features.reference_types_enabled()) { + result |= PrintError(loc, "only one table allowed"); + } + result |= CheckLimits(loc, limits, UINT32_MAX, "elems"); + + if (limits.is_shared) { + result |= PrintError(loc, "tables may not be shared"); + } + if (elem_type != Type::FuncRef && + !options_.features.reference_types_enabled()) { + result |= PrintError(loc, "tables must have funcref type"); + } + if (!elem_type.IsRef()) { + result |= PrintError(loc, "tables must have reference types"); + } + + tables_.push_back(TableType{elem_type, limits}); + return result; +} + +Result SharedValidator::OnMemory(const Location& loc, const Limits& limits) { + Result result = Result::Ok; + if (memories_.size() > 0 && !options_.features.multi_memory_enabled()) { + result |= PrintError(loc, "only one memory block allowed"); + } + result |= CheckLimits( + loc, limits, limits.is_64 ? WABT_MAX_PAGES64 : WABT_MAX_PAGES32, "pages"); + + if (limits.is_shared) { + if (!options_.features.threads_enabled()) { + result |= PrintError(loc, "memories may not be shared"); + } else if (!limits.has_max) { + result |= PrintError(loc, "shared memories must have max sizes"); + } + } + + memories_.push_back(MemoryType{limits}); + return result; +} + +Result SharedValidator::OnGlobalImport(const Location& loc, + Type type, + bool mutable_) { + Result result = Result::Ok; + if (mutable_ && !options_.features.mutable_globals_enabled()) { + result |= PrintError(loc, "mutable globals cannot be imported"); + } + globals_.push_back(GlobalType{type, mutable_}); + ++num_imported_globals_; + return result; +} + +Result SharedValidator::OnGlobal(const Location& loc, + Type type, + bool mutable_) { + globals_.push_back(GlobalType{type, mutable_}); + return Result::Ok; +} + +Result SharedValidator::CheckType(const Location& loc, + Type actual, + Type expected, + const char* desc) { + if (Failed(TypeChecker::CheckType(actual, expected))) { + PrintError(loc, "type mismatch at %s. got %s, expected %s", desc, + actual.GetName().c_str(), expected.GetName().c_str()); + return Result::Error; + } + return Result::Ok; +} + +Result SharedValidator::OnTag(const Location& loc, Var sig_var) { + Result result = Result::Ok; + FuncType type; + result |= CheckFuncTypeIndex(sig_var, &type); + if (!type.results.empty()) { + result |= PrintError(loc, "Tag signature must have 0 results."); + } + tags_.push_back(TagType{type.params}); + return result; +} + +Result SharedValidator::OnExport(const Location& loc, + ExternalKind kind, + Var item_var, + std::string_view name) { + Result result = Result::Ok; + auto name_str = std::string(name); + if (export_names_.find(name_str) != export_names_.end()) { + result |= PrintError(loc, "duplicate export \"" PRIstringview "\"", + WABT_PRINTF_STRING_VIEW_ARG(name)); + } + export_names_.insert(name_str); + + switch (kind) { + case ExternalKind::Func: + result |= CheckFuncIndex(item_var); + declared_funcs_.insert(item_var.index()); + break; + + case ExternalKind::Table: + result |= CheckTableIndex(item_var); + break; + + case ExternalKind::Memory: + result |= CheckMemoryIndex(item_var); + break; + + case ExternalKind::Global: + result |= CheckGlobalIndex(item_var); + break; + + case ExternalKind::Tag: + result |= CheckTagIndex(item_var); + break; + } + return result; +} + +Result SharedValidator::OnStart(const Location& loc, Var func_var) { + Result result = Result::Ok; + if (starts_++ > 0) { + result |= PrintError(loc, "only one start function allowed"); + } + FuncType func_type; + result |= CheckFuncIndex(func_var, &func_type); + if (func_type.params.size() != 0) { + result |= PrintError(loc, "start function must be nullary"); + } + if (func_type.results.size() != 0) { + result |= PrintError(loc, "start function must not return anything"); + } + return result; +} + +Result SharedValidator::OnElemSegment(const Location& loc, + Var table_var, + SegmentKind kind) { + Result result = Result::Ok; + TableType table_type; + if (kind == SegmentKind::Active) { + result |= CheckTableIndex(table_var, &table_type); + } + // Type gets set later in OnElemSegmentElemType. + elems_.push_back( + ElemType{Type::Void, kind == SegmentKind::Active, table_type.element}); + return result; +} + +Result SharedValidator::OnElemSegmentElemType(const Location& loc, + Type elem_type) { + Result result = Result::Ok; + auto& elem = elems_.back(); + if (elem.is_active) { + // Check that the type of the elem segment matches the table in which + // it is active. + result |= CheckType(loc, elem.table_type, elem_type, "elem segment"); + } + elem.element = elem_type; + return result; +} + +Result SharedValidator::OnElemSegmentElemExpr_RefNull(const Location& loc, + Type type) { + return CheckType(loc, type, elems_.back().element, "elem expression"); +} + +Result SharedValidator::OnElemSegmentElemExpr_RefFunc(const Location& loc, + Var func_var) { + Result result = Result::Ok; + result |= + CheckType(loc, Type::FuncRef, elems_.back().element, "elem expression"); + result |= CheckFuncIndex(func_var); + declared_funcs_.insert(func_var.index()); + return result; +} + +Result SharedValidator::OnElemSegmentElemExpr_Other(const Location& loc) { + return PrintError(loc, + "invalid elem expression expression; must be either " + "ref.null or ref.func."); +} + +void SharedValidator::OnDataCount(Index count) { + data_segments_ = count; +} + +Result SharedValidator::OnDataSegment(const Location& loc, + Var memory_var, + SegmentKind kind) { + Result result = Result::Ok; + if (kind == SegmentKind::Active) { + result |= CheckMemoryIndex(memory_var); + } + return result; +} + +Result SharedValidator::CheckDeclaredFunc(Var func_var) { + if (declared_funcs_.count(func_var.index()) == 0) { + return PrintError(func_var.loc, + "function %" PRIindex + " is not declared in any elem sections", + func_var.index()); + } + return Result::Ok; +} + +Result SharedValidator::EndModule() { + // Verify that any ref.func used in init expressions for globals are + // mentioned in an elems section. This can't be done while process the + // globals because the global section comes before the elem section. + Result result = Result::Ok; + for (Var func_var : check_declared_funcs_) { + result |= CheckDeclaredFunc(func_var); + } + return result; +} + +Result SharedValidator::CheckIndex(Var var, Index max_index, const char* desc) { + if (var.index() >= max_index) { + return PrintError( + var.loc, "%s variable out of range: %" PRIindex " (max %" PRIindex ")", + desc, var.index(), max_index); + } + return Result::Ok; +} + +template <typename T> +Result SharedValidator::CheckIndexWithValue(Var var, + const std::vector<T>& values, + T* out, + const char* desc) { + Result result = CheckIndex(var, values.size(), desc); + if (out) { + *out = Succeeded(result) ? values[var.index()] : T{}; + } + return result; +} + +Result SharedValidator::CheckLocalIndex(Var local_var, Type* out_type) { + auto iter = std::upper_bound( + locals_.begin(), locals_.end(), local_var.index(), + [](Index index, const LocalDecl& decl) { return index < decl.end; }); + if (iter == locals_.end()) { + // TODO: better error + return PrintError(local_var.loc, "local variable out of range (max %u)", + GetLocalCount()); + } + *out_type = iter->type; + return Result::Ok; +} + +Result SharedValidator::CheckFuncTypeIndex(Var sig_var, FuncType* out) { + Result result = CheckIndex(sig_var, num_types_, "function type"); + if (Failed(result)) { + *out = FuncType{}; + return Result::Error; + } + + auto iter = func_types_.find(sig_var.index()); + if (iter == func_types_.end()) { + return PrintError(sig_var.loc, "type %d is not a function", + sig_var.index()); + } + + if (out) { + *out = iter->second; + } + return Result::Ok; +} + +Result SharedValidator::CheckFuncIndex(Var func_var, FuncType* out) { + return CheckIndexWithValue(func_var, funcs_, out, "function"); +} + +Result SharedValidator::CheckMemoryIndex(Var memory_var, MemoryType* out) { + return CheckIndexWithValue(memory_var, memories_, out, "memory"); +} + +Result SharedValidator::CheckTableIndex(Var table_var, TableType* out) { + return CheckIndexWithValue(table_var, tables_, out, "table"); +} + +Result SharedValidator::CheckGlobalIndex(Var global_var, GlobalType* out) { + return CheckIndexWithValue(global_var, globals_, out, "global"); +} + +Result SharedValidator::CheckTagIndex(Var tag_var, TagType* out) { + return CheckIndexWithValue(tag_var, tags_, out, "tag"); +} + +Result SharedValidator::CheckElemSegmentIndex(Var elem_segment_var, + ElemType* out) { + return CheckIndexWithValue(elem_segment_var, elems_, out, "elem_segment"); +} + +Result SharedValidator::CheckDataSegmentIndex(Var data_segment_var) { + return CheckIndex(data_segment_var, data_segments_, "data_segment"); +} + +Result SharedValidator::CheckBlockSignature(const Location& loc, + Opcode opcode, + Type sig_type, + TypeVector* out_param_types, + TypeVector* out_result_types) { + Result result = Result::Ok; + + if (sig_type.IsIndex()) { + Index sig_index = sig_type.GetIndex(); + FuncType func_type; + result |= CheckFuncTypeIndex(Var(sig_index, loc), &func_type); + + if (!func_type.params.empty() && !options_.features.multi_value_enabled()) { + result |= PrintError(loc, "%s params not currently supported.", + opcode.GetName()); + } + // Multiple results without --enable-multi-value is checked above in + // OnType. + + *out_param_types = func_type.params; + *out_result_types = func_type.results; + } else { + out_param_types->clear(); + *out_result_types = sig_type.GetInlineVector(); + } + + return result; +} + +Index SharedValidator::GetFunctionTypeIndex(Index func_index) const { + assert(func_index < funcs_.size()); + return funcs_[func_index].type_index; +} + +Result SharedValidator::BeginInitExpr(const Location& loc, Type type) { + expr_loc_ = loc; + in_init_expr_ = true; + return typechecker_.BeginInitExpr(type); +} + +Result SharedValidator::EndInitExpr() { + in_init_expr_ = false; + return typechecker_.EndInitExpr(); +} + +Result SharedValidator::BeginFunctionBody(const Location& loc, + Index func_index) { + expr_loc_ = loc; + locals_.clear(); + if (func_index < funcs_.size()) { + for (Type type : funcs_[func_index].params) { + // TODO: Coalesce parameters of the same type? + locals_.push_back(LocalDecl{type, GetLocalCount() + 1}); + } + return typechecker_.BeginFunction(funcs_[func_index].results); + } else { + // Signature isn't available, use empty. + return typechecker_.BeginFunction(TypeVector()); + } +} + +Result SharedValidator::EndFunctionBody(const Location& loc) { + expr_loc_ = loc; + return typechecker_.EndFunction(); +} + +Result SharedValidator::OnLocalDecl(const Location& loc, + Index count, + Type type) { + const auto max_locals = std::numeric_limits<Index>::max(); + if (count > max_locals - GetLocalCount()) { + PrintError(loc, "local count must be < 0x10000000"); + return Result::Error; + } + locals_.push_back(LocalDecl{type, GetLocalCount() + count}); + return Result::Ok; +} + +Index SharedValidator::GetLocalCount() const { + return locals_.empty() ? 0 : locals_.back().end; +} + +static bool is_power_of_two(uint32_t x) { + return x && ((x & (x - 1)) == 0); +} + +Result SharedValidator::CheckAlign(const Location& loc, + Address alignment, + Address natural_alignment) { + if (!is_power_of_two(alignment)) { + PrintError(loc, "alignment (%" PRIaddress ") must be a power of 2", + alignment); + return Result::Error; + } + if (alignment > natural_alignment) { + PrintError( + loc, + "alignment must not be larger than natural alignment (%" PRIaddress ")", + natural_alignment); + return Result::Error; + } + return Result::Ok; +} + +Result SharedValidator::CheckAtomicAlign(const Location& loc, + Address alignment, + Address natural_alignment) { + if (!is_power_of_two(alignment)) { + PrintError(loc, "alignment (%" PRIaddress ") must be a power of 2", + alignment); + return Result::Error; + } + if (alignment != natural_alignment) { + PrintError(loc, + "alignment must be equal to natural alignment (%" PRIaddress ")", + natural_alignment); + return Result::Error; + } + return Result::Ok; +} + +bool SharedValidator::ValidInitOpcode(Opcode opcode) const { + if (opcode == Opcode::GlobalGet || opcode == Opcode::I32Const || + opcode == Opcode::I64Const || opcode == Opcode::F32Const || + opcode == Opcode::F64Const || opcode == Opcode::RefFunc || + opcode == Opcode::RefNull) { + return true; + } + if (options_.features.extended_const_enabled()) { + if (opcode == Opcode::I32Mul || opcode == Opcode::I64Mul || + opcode == Opcode::I32Sub || opcode == Opcode::I64Sub || + opcode == Opcode::I32Add || opcode == Opcode::I64Add) { + return true; + } + } + return false; +} + +Result SharedValidator::CheckInstr(Opcode opcode, const Location& loc) { + expr_loc_ = loc; + if (in_init_expr_ && !ValidInitOpcode(opcode)) { + PrintError(loc, + "invalid initializer: instruction not valid in initializer " + "expression: %s", + opcode.GetName()); + return Result::Error; + } + return Result::Ok; +} + +Result SharedValidator::OnAtomicFence(const Location& loc, + uint32_t consistency_model) { + Result result = CheckInstr(Opcode::AtomicFence, loc); + if (consistency_model != 0) { + result |= PrintError( + loc, "unexpected atomic.fence consistency model (expected 0): %u", + consistency_model); + } + result |= typechecker_.OnAtomicFence(consistency_model); + return result; +} + +Result SharedValidator::OnAtomicLoad(const Location& loc, + Opcode opcode, + Var memidx, + Address alignment) { + Result result = CheckInstr(opcode, loc); + MemoryType mt; + result |= CheckMemoryIndex(memidx, &mt); + result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnAtomicLoad(opcode, mt.limits); + return result; +} + +Result SharedValidator::OnAtomicNotify(const Location& loc, + Opcode opcode, + Var memidx, + Address alignment) { + Result result = CheckInstr(opcode, loc); + MemoryType mt; + result |= CheckMemoryIndex(memidx, &mt); + result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnAtomicNotify(opcode, mt.limits); + return result; +} + +Result SharedValidator::OnAtomicRmwCmpxchg(const Location& loc, + Opcode opcode, + Var memidx, + Address alignment) { + Result result = CheckInstr(opcode, loc); + MemoryType mt; + result |= CheckMemoryIndex(memidx, &mt); + result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnAtomicRmwCmpxchg(opcode, mt.limits); + return result; +} + +Result SharedValidator::OnAtomicRmw(const Location& loc, + Opcode opcode, + Var memidx, + Address alignment) { + Result result = CheckInstr(opcode, loc); + MemoryType mt; + result |= CheckMemoryIndex(memidx, &mt); + result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnAtomicRmw(opcode, mt.limits); + return result; +} + +Result SharedValidator::OnAtomicStore(const Location& loc, + Opcode opcode, + Var memidx, + Address alignment) { + Result result = CheckInstr(opcode, loc); + MemoryType mt; + result |= CheckMemoryIndex(memidx, &mt); + result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnAtomicStore(opcode, mt.limits); + return result; +} + +Result SharedValidator::OnAtomicWait(const Location& loc, + Opcode opcode, + Var memidx, + Address alignment) { + Result result = CheckInstr(opcode, loc); + MemoryType mt; + result |= CheckMemoryIndex(memidx, &mt); + result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnAtomicWait(opcode, mt.limits); + return result; +} + +Result SharedValidator::OnBinary(const Location& loc, Opcode opcode) { + Result result = CheckInstr(opcode, loc); + result |= typechecker_.OnBinary(opcode); + return result; +} + +Result SharedValidator::OnBlock(const Location& loc, Type sig_type) { + Result result = CheckInstr(Opcode::Block, loc); + TypeVector param_types, result_types; + result |= CheckBlockSignature(loc, Opcode::Block, sig_type, ¶m_types, + &result_types); + result |= typechecker_.OnBlock(param_types, result_types); + return result; +} + +Result SharedValidator::OnBr(const Location& loc, Var depth) { + Result result = CheckInstr(Opcode::Br, loc); + result |= typechecker_.OnBr(depth.index()); + return result; +} + +Result SharedValidator::OnBrIf(const Location& loc, Var depth) { + Result result = CheckInstr(Opcode::BrIf, loc); + result |= typechecker_.OnBrIf(depth.index()); + return result; +} + +Result SharedValidator::BeginBrTable(const Location& loc) { + Result result = CheckInstr(Opcode::BrTable, loc); + result |= typechecker_.BeginBrTable(); + return result; +} + +Result SharedValidator::OnBrTableTarget(const Location& loc, Var depth) { + Result result = Result::Ok; + expr_loc_ = loc; + result |= typechecker_.OnBrTableTarget(depth.index()); + return result; +} + +Result SharedValidator::EndBrTable(const Location& loc) { + Result result = CheckInstr(Opcode::BrTable, loc); + result |= typechecker_.EndBrTable(); + return result; +} + +Result SharedValidator::OnCall(const Location& loc, Var func_var) { + Result result = CheckInstr(Opcode::Call, loc); + FuncType func_type; + result |= CheckFuncIndex(func_var, &func_type); + result |= typechecker_.OnCall(func_type.params, func_type.results); + return result; +} + +Result SharedValidator::OnCallIndirect(const Location& loc, + Var sig_var, + Var table_var) { + Result result = CheckInstr(Opcode::CallIndirect, loc); + FuncType func_type; + result |= CheckFuncTypeIndex(sig_var, &func_type); + result |= CheckTableIndex(table_var); + result |= typechecker_.OnCallIndirect(func_type.params, func_type.results); + return result; +} + +Result SharedValidator::OnCallRef(const Location& loc, + Index* function_type_index) { + Result result = CheckInstr(Opcode::CallRef, loc); + Index func_index; + result |= typechecker_.OnIndexedFuncRef(&func_index); + if (Failed(result)) { + return result; + } + FuncType func_type; + result |= CheckFuncTypeIndex(Var(func_index, loc), &func_type); + result |= typechecker_.OnCall(func_type.params, func_type.results); + if (Succeeded(result)) { + *function_type_index = func_index; + } + return result; +} + +Result SharedValidator::OnCatch(const Location& loc, + Var tag_var, + bool is_catch_all) { + Result result = CheckInstr(Opcode::Catch, loc); + if (is_catch_all) { + TypeVector empty; + result |= typechecker_.OnCatch(empty); + } else { + TagType tag_type; + result |= CheckTagIndex(tag_var, &tag_type); + result |= typechecker_.OnCatch(tag_type.params); + } + return result; +} + +Result SharedValidator::OnCompare(const Location& loc, Opcode opcode) { + Result result = CheckInstr(opcode, loc); + result |= typechecker_.OnCompare(opcode); + return result; +} + +Result SharedValidator::OnConst(const Location& loc, Type type) { + Result result = Result::Ok; + expr_loc_ = loc; + result |= typechecker_.OnConst(type); + return result; +} + +Result SharedValidator::OnConvert(const Location& loc, Opcode opcode) { + Result result = CheckInstr(opcode, loc); + result |= typechecker_.OnConvert(opcode); + return result; +} + +Result SharedValidator::OnDataDrop(const Location& loc, Var segment_var) { + Result result = CheckInstr(Opcode::DataDrop, loc); + result |= CheckDataSegmentIndex(segment_var); + result |= typechecker_.OnDataDrop(segment_var.index()); + return result; +} + +Result SharedValidator::OnDelegate(const Location& loc, Var depth) { + Result result = CheckInstr(Opcode::Delegate, loc); + result |= typechecker_.OnDelegate(depth.index()); + return result; +} + +Result SharedValidator::OnDrop(const Location& loc) { + Result result = CheckInstr(Opcode::Drop, loc); + result |= typechecker_.OnDrop(); + return result; +} + +Result SharedValidator::OnElemDrop(const Location& loc, Var segment_var) { + Result result = CheckInstr(Opcode::ElemDrop, loc); + result |= CheckElemSegmentIndex(segment_var); + result |= typechecker_.OnElemDrop(segment_var.index()); + return result; +} + +Result SharedValidator::OnElse(const Location& loc) { + // Don't call CheckInstr or update expr_loc_ here because if we fail we want + // the last expression in the If block to be reported as the error location, + // not the else itself. + Result result = Result::Ok; + result |= typechecker_.OnElse(); + return result; +} + +Result SharedValidator::OnEnd(const Location& loc) { + Result result = CheckInstr(Opcode::End, loc); + result |= typechecker_.OnEnd(); + return result; +} + +Result SharedValidator::OnGlobalGet(const Location& loc, Var global_var) { + Result result = CheckInstr(Opcode::GlobalGet, loc); + GlobalType global_type; + result |= CheckGlobalIndex(global_var, &global_type); + result |= typechecker_.OnGlobalGet(global_type.type); + if (Succeeded(result) && in_init_expr_) { + if (global_var.index() >= num_imported_globals_) { + result |= PrintError( + global_var.loc, + "initializer expression can only reference an imported global"); + } + if (global_type.mutable_) { + result |= PrintError( + loc, "initializer expression cannot reference a mutable global"); + } + } + + return result; +} + +Result SharedValidator::OnGlobalSet(const Location& loc, Var global_var) { + Result result = CheckInstr(Opcode::GlobalSet, loc); + GlobalType global_type; + result |= CheckGlobalIndex(global_var, &global_type); + if (!global_type.mutable_) { + result |= PrintError( + loc, "can't global.set on immutable global at index %" PRIindex ".", + global_var.index()); + } + result |= typechecker_.OnGlobalSet(global_type.type); + return result; +} + +Result SharedValidator::OnIf(const Location& loc, Type sig_type) { + Result result = CheckInstr(Opcode::If, loc); + TypeVector param_types, result_types; + result |= CheckBlockSignature(loc, Opcode::If, sig_type, ¶m_types, + &result_types); + result |= typechecker_.OnIf(param_types, result_types); + return result; +} + +Result SharedValidator::OnLoad(const Location& loc, + Opcode opcode, + Var memidx, + Address alignment) { + Result result = CheckInstr(opcode, loc); + MemoryType mt; + result |= CheckMemoryIndex(memidx, &mt); + result |= CheckAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnLoad(opcode, mt.limits); + return result; +} + +Result SharedValidator::OnLoadSplat(const Location& loc, + Opcode opcode, + Var memidx, + Address alignment) { + Result result = CheckInstr(opcode, loc); + MemoryType mt; + result |= CheckMemoryIndex(memidx, &mt); + result |= CheckAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnLoad(opcode, mt.limits); + return result; +} + +Result SharedValidator::OnLoadZero(const Location& loc, + Opcode opcode, + Var memidx, + Address alignment) { + Result result = CheckInstr(opcode, loc); + MemoryType mt; + result |= CheckMemoryIndex(memidx, &mt); + result |= CheckAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnLoad(opcode, mt.limits); + return result; +} + +Result SharedValidator::OnLocalGet(const Location& loc, Var local_var) { + CHECK_RESULT(CheckInstr(Opcode::LocalGet, loc)); + Result result = Result::Ok; + Type type = Type::Any; + result |= CheckLocalIndex(local_var, &type); + result |= typechecker_.OnLocalGet(type); + return result; +} + +Result SharedValidator::OnLocalSet(const Location& loc, Var local_var) { + CHECK_RESULT(CheckInstr(Opcode::LocalSet, loc)); + Result result = Result::Ok; + Type type = Type::Any; + result |= CheckLocalIndex(local_var, &type); + result |= typechecker_.OnLocalSet(type); + return result; +} + +Result SharedValidator::OnLocalTee(const Location& loc, Var local_var) { + CHECK_RESULT(CheckInstr(Opcode::LocalTee, loc)); + Result result = Result::Ok; + Type type = Type::Any; + result |= CheckLocalIndex(local_var, &type); + result |= typechecker_.OnLocalTee(type); + return result; +} + +Result SharedValidator::OnLoop(const Location& loc, Type sig_type) { + Result result = CheckInstr(Opcode::Loop, loc); + TypeVector param_types, result_types; + result |= CheckBlockSignature(loc, Opcode::Loop, sig_type, ¶m_types, + &result_types); + result |= typechecker_.OnLoop(param_types, result_types); + return result; +} + +Result SharedValidator::OnMemoryCopy(const Location& loc, + Var srcmemidx, + Var destmemidx) { + Result result = CheckInstr(Opcode::MemoryCopy, loc); + MemoryType srcmt; + MemoryType dstmt; + result |= CheckMemoryIndex(srcmemidx, &srcmt); + result |= CheckMemoryIndex(destmemidx, &dstmt); + result |= typechecker_.OnMemoryCopy(srcmt.limits, dstmt.limits); + return result; +} + +Result SharedValidator::OnMemoryFill(const Location& loc, Var memidx) { + Result result = CheckInstr(Opcode::MemoryFill, loc); + MemoryType mt; + result |= CheckMemoryIndex(memidx, &mt); + result |= typechecker_.OnMemoryFill(mt.limits); + return result; +} + +Result SharedValidator::OnMemoryGrow(const Location& loc, Var memidx) { + Result result = CheckInstr(Opcode::MemoryGrow, loc); + MemoryType mt; + result |= CheckMemoryIndex(memidx, &mt); + result |= typechecker_.OnMemoryGrow(mt.limits); + return result; +} + +Result SharedValidator::OnMemoryInit(const Location& loc, + Var segment_var, + Var memidx) { + Result result = CheckInstr(Opcode::MemoryInit, loc); + MemoryType mt; + result |= CheckMemoryIndex(memidx, &mt); + result |= CheckDataSegmentIndex(segment_var); + result |= typechecker_.OnMemoryInit(segment_var.index(), mt.limits); + return result; +} + +Result SharedValidator::OnMemorySize(const Location& loc, Var memidx) { + Result result = CheckInstr(Opcode::MemorySize, loc); + MemoryType mt; + result |= CheckMemoryIndex(memidx, &mt); + result |= typechecker_.OnMemorySize(mt.limits); + return result; +} + +Result SharedValidator::OnNop(const Location& loc) { + Result result = CheckInstr(Opcode::Nop, loc); + return result; +} + +Result SharedValidator::OnRefFunc(const Location& loc, Var func_var) { + Result result = CheckInstr(Opcode::RefFunc, loc); + result |= CheckFuncIndex(func_var); + if (Succeeded(result)) { + // References in initializer expressions are considered declarations, as + // opposed to references in function bodies that are considered usages. + if (in_init_expr_) { + declared_funcs_.insert(func_var.index()); + } else { + check_declared_funcs_.push_back(func_var); + } + Index func_type = GetFunctionTypeIndex(func_var.index()); + result |= typechecker_.OnRefFuncExpr(func_type); + } + return result; +} + +Result SharedValidator::OnRefIsNull(const Location& loc) { + Result result = CheckInstr(Opcode::RefIsNull, loc); + result |= typechecker_.OnRefIsNullExpr(); + return result; +} + +Result SharedValidator::OnRefNull(const Location& loc, Type type) { + Result result = CheckInstr(Opcode::RefNull, loc); + result |= typechecker_.OnRefNullExpr(type); + return result; +} + +Result SharedValidator::OnRethrow(const Location& loc, Var depth) { + Result result = CheckInstr(Opcode::Rethrow, loc); + result |= typechecker_.OnRethrow(depth.index()); + return result; +} + +Result SharedValidator::OnReturnCall(const Location& loc, Var func_var) { + Result result = CheckInstr(Opcode::ReturnCall, loc); + FuncType func_type; + result |= CheckFuncIndex(func_var, &func_type); + result |= typechecker_.OnReturnCall(func_type.params, func_type.results); + return result; +} + +Result SharedValidator::OnReturnCallIndirect(const Location& loc, + Var sig_var, + Var table_var) { + Result result = CheckInstr(Opcode::CallIndirect, loc); + result |= CheckTableIndex(table_var); + FuncType func_type; + result |= CheckFuncTypeIndex(sig_var, &func_type); + result |= + typechecker_.OnReturnCallIndirect(func_type.params, func_type.results); + return result; +} + +Result SharedValidator::OnReturn(const Location& loc) { + Result result = CheckInstr(Opcode::Return, loc); + result |= typechecker_.OnReturn(); + return result; +} + +Result SharedValidator::OnSelect(const Location& loc, + Index result_count, + Type* result_types) { + Result result = CheckInstr(Opcode::Select, loc); + if (result_count > 1) { + result |= + PrintError(loc, "invalid arity in select instruction: %" PRIindex ".", + result_count); + } else { + result |= typechecker_.OnSelect(ToTypeVector(result_count, result_types)); + } + return result; +} + +Result SharedValidator::OnSimdLaneOp(const Location& loc, + Opcode opcode, + uint64_t value) { + Result result = CheckInstr(opcode, loc); + result |= typechecker_.OnSimdLaneOp(opcode, value); + return result; +} + +Result SharedValidator::OnSimdLoadLane(const Location& loc, + Opcode opcode, + Var memidx, + Address alignment, + uint64_t value) { + Result result = CheckInstr(opcode, loc); + MemoryType mt; + result |= CheckMemoryIndex(memidx, &mt); + result |= CheckAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnSimdLoadLane(opcode, mt.limits, value); + return result; +} + +Result SharedValidator::OnSimdStoreLane(const Location& loc, + Opcode opcode, + Var memidx, + Address alignment, + uint64_t value) { + Result result = CheckInstr(opcode, loc); + MemoryType mt; + result |= CheckMemoryIndex(memidx, &mt); + result |= CheckAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnSimdStoreLane(opcode, mt.limits, value); + return result; +} + +Result SharedValidator::OnSimdShuffleOp(const Location& loc, + Opcode opcode, + v128 value) { + Result result = CheckInstr(opcode, loc); + result |= typechecker_.OnSimdShuffleOp(opcode, value); + return result; +} + +Result SharedValidator::OnStore(const Location& loc, + Opcode opcode, + Var memidx, + Address alignment) { + Result result = CheckInstr(opcode, loc); + MemoryType mt; + result |= CheckMemoryIndex(memidx, &mt); + result |= CheckAlign(loc, alignment, opcode.GetMemorySize()); + result |= typechecker_.OnStore(opcode, mt.limits); + return result; +} + +Result SharedValidator::OnTableCopy(const Location& loc, + Var dst_var, + Var src_var) { + Result result = CheckInstr(Opcode::TableCopy, loc); + TableType dst_table; + TableType src_table; + result |= CheckTableIndex(dst_var, &dst_table); + result |= CheckTableIndex(src_var, &src_table); + result |= typechecker_.OnTableCopy(); + result |= CheckType(loc, src_table.element, dst_table.element, "table.copy"); + return result; +} + +Result SharedValidator::OnTableFill(const Location& loc, Var table_var) { + Result result = CheckInstr(Opcode::TableFill, loc); + TableType table_type; + result |= CheckTableIndex(table_var, &table_type); + result |= typechecker_.OnTableFill(table_type.element); + return result; +} + +Result SharedValidator::OnTableGet(const Location& loc, Var table_var) { + Result result = CheckInstr(Opcode::TableGet, loc); + TableType table_type; + result |= CheckTableIndex(table_var, &table_type); + result |= typechecker_.OnTableGet(table_type.element); + return result; +} + +Result SharedValidator::OnTableGrow(const Location& loc, Var table_var) { + Result result = CheckInstr(Opcode::TableGrow, loc); + TableType table_type; + result |= CheckTableIndex(table_var, &table_type); + result |= typechecker_.OnTableGrow(table_type.element); + return result; +} + +Result SharedValidator::OnTableInit(const Location& loc, + Var segment_var, + Var table_var) { + Result result = CheckInstr(Opcode::TableInit, loc); + TableType table_type; + ElemType elem_type; + result |= CheckTableIndex(table_var, &table_type); + result |= CheckElemSegmentIndex(segment_var, &elem_type); + result |= typechecker_.OnTableInit(table_var.index(), segment_var.index()); + result |= CheckType(loc, elem_type.element, table_type.element, "table.init"); + return result; +} + +Result SharedValidator::OnTableSet(const Location& loc, Var table_var) { + Result result = CheckInstr(Opcode::TableSet, loc); + TableType table_type; + result |= CheckTableIndex(table_var, &table_type); + result |= typechecker_.OnTableSet(table_type.element); + return result; +} + +Result SharedValidator::OnTableSize(const Location& loc, Var table_var) { + Result result = CheckInstr(Opcode::TableSize, loc); + result |= CheckTableIndex(table_var); + result |= typechecker_.OnTableSize(); + return result; +} + +Result SharedValidator::OnTernary(const Location& loc, Opcode opcode) { + Result result = CheckInstr(opcode, loc); + result |= typechecker_.OnTernary(opcode); + return result; +} + +Result SharedValidator::OnThrow(const Location& loc, Var tag_var) { + Result result = CheckInstr(Opcode::Throw, loc); + TagType tag_type; + result |= CheckTagIndex(tag_var, &tag_type); + result |= typechecker_.OnThrow(tag_type.params); + return result; +} + +Result SharedValidator::OnTry(const Location& loc, Type sig_type) { + Result result = CheckInstr(Opcode::Try, loc); + TypeVector param_types, result_types; + result |= CheckBlockSignature(loc, Opcode::Try, sig_type, ¶m_types, + &result_types); + result |= typechecker_.OnTry(param_types, result_types); + return result; +} + +Result SharedValidator::OnUnary(const Location& loc, Opcode opcode) { + Result result = CheckInstr(opcode, loc); + result |= typechecker_.OnUnary(opcode); + return result; +} + +Result SharedValidator::OnUnreachable(const Location& loc) { + Result result = CheckInstr(Opcode::Unreachable, loc); + result |= typechecker_.OnUnreachable(); + return result; +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/stream.cc b/third_party/wasm2c/src/stream.cc new file mode 100644 index 0000000000..5f27e01b2e --- /dev/null +++ b/third_party/wasm2c/src/stream.cc @@ -0,0 +1,330 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/stream.h" + +#include <cassert> +#include <cctype> +#include <cerrno> + +#define DUMP_OCTETS_PER_LINE 16 +#define DUMP_OCTETS_PER_GROUP 2 + +#define ERROR0(msg) fprintf(stderr, "%s:%d: " msg, __FILE__, __LINE__) +#define ERROR(fmt, ...) \ + fprintf(stderr, "%s:%d: " fmt, __FILE__, __LINE__, __VA_ARGS__) + +namespace wabt { + +Stream::Stream(Stream* log_stream) + : offset_(0), result_(Result::Ok), log_stream_(log_stream) {} + +void Stream::AddOffset(ssize_t delta) { + offset_ += delta; +} + +void Stream::WriteDataAt(size_t at, + const void* src, + size_t size, + const char* desc, + PrintChars print_chars) { + if (Failed(result_)) { + return; + } + if (log_stream_) { + log_stream_->WriteMemoryDump(src, size, at, print_chars, nullptr, desc); + } + result_ = WriteDataImpl(at, src, size); +} + +void Stream::WriteData(const void* src, + size_t size, + const char* desc, + PrintChars print_chars) { + WriteDataAt(offset_, src, size, desc, print_chars); + offset_ += size; +} + +void Stream::MoveData(size_t dst_offset, size_t src_offset, size_t size) { + if (Failed(result_)) { + return; + } + if (log_stream_) { + log_stream_->Writef( + "; move data: [%" PRIzx ", %" PRIzx ") -> [%" PRIzx ", %" PRIzx ")\n", + src_offset, src_offset + size, dst_offset, dst_offset + size); + } + result_ = MoveDataImpl(dst_offset, src_offset, size); +} + +void Stream::Truncate(size_t size) { + if (Failed(result_)) { + return; + } + if (log_stream_) { + log_stream_->Writef("; truncate to %" PRIzd " (0x%" PRIzx ")\n", size, + size); + } + result_ = TruncateImpl(size); + if (Succeeded(result_) && offset_ > size) { + offset_ = size; + } +} + +void Stream::Writef(const char* format, ...) { + WABT_SNPRINTF_ALLOCA(buffer, length, format); + WriteData(buffer, length); +} + +void Stream::WriteMemoryDump(const void* start, + size_t size, + size_t offset, + PrintChars print_chars, + const char* prefix, + const char* desc) { + const uint8_t* p = static_cast<const uint8_t*>(start); + const uint8_t* end = p + size; + while (p < end) { + const uint8_t* line = p; + const uint8_t* line_end = p + DUMP_OCTETS_PER_LINE; + if (prefix) { + Writef("%s", prefix); + } + Writef("%07" PRIzx ": ", reinterpret_cast<intptr_t>(p) - + reinterpret_cast<intptr_t>(start) + offset); + while (p < line_end) { + for (int i = 0; i < DUMP_OCTETS_PER_GROUP; ++i, ++p) { + if (p < end) { + Writef("%02x", *p); + } else { + WriteChar(' '); + WriteChar(' '); + } + } + WriteChar(' '); + } + + if (print_chars == PrintChars::Yes) { + WriteChar(' '); + p = line; + for (int i = 0; i < DUMP_OCTETS_PER_LINE && p < end; ++i, ++p) + WriteChar(isprint(*p) ? *p : '.'); + } + + /* if there are multiple lines, only print the desc on the last one */ + if (p >= end && desc) { + Writef(" ; %s", desc); + } + WriteChar('\n'); + } +} + +Result OutputBuffer::WriteToFile(std::string_view filename) const { + std::string filename_str(filename); + FILE* file = fopen(filename_str.c_str(), "wb"); + if (!file) { + ERROR("unable to open %s for writing\n", filename_str.c_str()); + return Result::Error; + } + + if (data.empty()) { + fclose(file); + return Result::Ok; + } + + ssize_t bytes = fwrite(data.data(), 1, data.size(), file); + if (bytes < 0 || static_cast<size_t>(bytes) != data.size()) { + ERROR("failed to write %" PRIzd " bytes to %s\n", data.size(), + filename_str.c_str()); + fclose(file); + return Result::Error; + } + + fclose(file); + return Result::Ok; +} + +Result OutputBuffer::WriteToStdout() const { + if (data.empty()) { + return Result::Ok; + } + ssize_t bytes = fwrite(data.data(), 1, data.size(), stdout); + if (bytes < 0 || static_cast<size_t>(bytes) != data.size()) { + ERROR("failed to write %" PRIzd " bytes to stdout\n", data.size()); + return Result::Error; + } + return Result::Ok; +} + +MemoryStream::MemoryStream(Stream* log_stream) + : Stream(log_stream), buf_(new OutputBuffer()) {} + +MemoryStream::MemoryStream(std::unique_ptr<OutputBuffer>&& buf, + Stream* log_stream) + : Stream(log_stream), buf_(std::move(buf)) {} + +std::unique_ptr<OutputBuffer> MemoryStream::ReleaseOutputBuffer() { + return std::move(buf_); +} + +void MemoryStream::Clear() { + if (buf_) + buf_->clear(); + else + buf_.reset(new OutputBuffer()); +} + +Result MemoryStream::WriteDataImpl(size_t dst_offset, + const void* src, + size_t size) { + if (size == 0) { + return Result::Ok; + } + size_t end = dst_offset + size; + if (end > buf_->data.size()) { + buf_->data.resize(end); + } + uint8_t* dst = &buf_->data[dst_offset]; + memcpy(dst, src, size); + return Result::Ok; +} + +Result MemoryStream::MoveDataImpl(size_t dst_offset, + size_t src_offset, + size_t size) { + if (size == 0) { + return Result::Ok; + } + size_t src_end = src_offset + size; + size_t dst_end = dst_offset + size; + size_t end = src_end > dst_end ? src_end : dst_end; + if (end > buf_->data.size()) { + buf_->data.resize(end); + } + + uint8_t* dst = &buf_->data[dst_offset]; + uint8_t* src = &buf_->data[src_offset]; + memmove(dst, src, size); + return Result::Ok; +} + +Result MemoryStream::TruncateImpl(size_t size) { + if (size > buf_->data.size()) { + return Result::Error; + } + buf_->data.resize(size); + return Result::Ok; +} + +FileStream::FileStream(std::string_view filename, Stream* log_stream) + : Stream(log_stream), file_(nullptr), offset_(0), should_close_(false) { + std::string filename_str(filename); + file_ = fopen(filename_str.c_str(), "wb"); + + // TODO(binji): this is pretty cheesy, should come up with a better API. + if (file_) { + should_close_ = true; + } else { + ERROR("fopen name=\"%s\" failed, errno=%d\n", filename_str.c_str(), errno); + } +} + +FileStream::FileStream(FILE* file, Stream* log_stream) + : Stream(log_stream), file_(file), offset_(0), should_close_(false) {} + +FileStream::FileStream(FileStream&& other) { + *this = std::move(other); +} + +FileStream& FileStream::operator=(FileStream&& other) { + file_ = other.file_; + offset_ = other.offset_; + should_close_ = other.should_close_; + other.file_ = nullptr; + other.offset_ = 0; + other.should_close_ = false; + return *this; +} + +FileStream::~FileStream() { + // We don't want to close existing files (stdout/sterr, for example). + if (should_close_) { + fclose(file_); + } +} + +void FileStream::Flush() { + if (file_) { + fflush(file_); + } +} + +Result FileStream::WriteDataImpl(size_t at, const void* data, size_t size) { + if (!file_) { + return Result::Error; + } + if (size == 0) { + return Result::Ok; + } + if (at != offset_) { + if (fseek(file_, at, SEEK_SET) != 0) { + ERROR("fseek offset=%" PRIzd " failed, errno=%d\n", size, errno); + return Result::Error; + } + offset_ = at; + } + if (fwrite(data, size, 1, file_) != 1) { + ERROR("fwrite size=%" PRIzd " failed, errno=%d\n", size, errno); + return Result::Error; + } + offset_ += size; + return Result::Ok; +} + +Result FileStream::MoveDataImpl(size_t dst_offset, + size_t src_offset, + size_t size) { + if (!file_) { + return Result::Error; + } + if (size == 0) { + return Result::Ok; + } + // TODO(binji): implement if needed. + ERROR0("FileStream::MoveDataImpl not implemented!\n"); + return Result::Error; +} + +Result FileStream::TruncateImpl(size_t size) { + if (!file_) { + return Result::Error; + } + // TODO(binji): implement if needed. + ERROR0("FileStream::TruncateImpl not implemented!\n"); + return Result::Error; +} + +// static +std::unique_ptr<FileStream> FileStream::CreateStdout() { + return std::unique_ptr<FileStream>(new FileStream(stdout)); +} + +// static +std::unique_ptr<FileStream> FileStream::CreateStderr() { + return std::unique_ptr<FileStream>(new FileStream(stderr)); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/test-binary-reader.cc b/third_party/wasm2c/src/test-binary-reader.cc new file mode 100644 index 0000000000..b3e3540d4f --- /dev/null +++ b/third_party/wasm2c/src/test-binary-reader.cc @@ -0,0 +1,75 @@ +/* + * Copyright 2018 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" + +#include "wabt/binary-reader-nop.h" +#include "wabt/binary-reader.h" +#include "wabt/leb128.h" +#include "wabt/opcode.h" + +using namespace wabt; + +namespace { + +struct BinaryReaderError : BinaryReaderNop { + bool OnError(const Error& error) override { + first_error = error; + return true; // Error handled. + } + + Error first_error; +}; + +} // End of anonymous namespace + +TEST(BinaryReader, DisabledOpcodes) { + // Use the default features. + ReadBinaryOptions options; + + // Loop through all opcodes. + for (uint32_t i = 0; i < static_cast<uint32_t>(Opcode::Invalid); ++i) { + Opcode opcode(static_cast<Opcode::Enum>(i)); + if (opcode.IsEnabled(options.features)) { + continue; + } + + // Use a shorter name to make the clang-formatted table below look nicer. + std::vector<uint8_t> b = opcode.GetBytes(); + assert(b.size() <= 3); + b.resize(3); + + uint8_t data[] = { + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, // magic + version + 0x01, 0x04, 0x01, 0x60, 0x00, 0x00, // type section: 1 type, (func) + 0x03, 0x02, 0x01, 0x00, // func section: 1 func, type 0 + 0x0a, 0x07, 0x01, 0x05, 0x00, // code section: 1 func, 0 locals + b[0], b[1], b[2], // The instruction, padded with zeroes + 0x0b, // end + }; + const size_t size = sizeof(data); + + BinaryReaderError reader; + Result result = ReadBinary(data, size, &reader, options); + EXPECT_EQ(Result::Error, result); + + // This relies on the binary reader checking whether the opcode is allowed + // before reading any further data needed by the instruction. + const std::string& message = reader.first_error.message; + EXPECT_EQ(0u, message.find("unexpected opcode")) + << "Got error message: " << message; + } +} diff --git a/third_party/wasm2c/src/test-circular-array.cc b/third_party/wasm2c/src/test-circular-array.cc new file mode 100644 index 0000000000..a2a8919ef4 --- /dev/null +++ b/third_party/wasm2c/src/test-circular-array.cc @@ -0,0 +1,284 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" + +#include <memory> + +#include "wabt/circular-array.h" + +using namespace wabt; + +namespace { + +struct TestObject { + static int construct_count; + static int destruct_count; + + TestObject(int data = 0, int data2 = 0) : data(data), data2(data2) { + construct_count++; + } + + TestObject(const TestObject& other) { + *this = other; + construct_count++; + } + + TestObject& operator=(const TestObject& other) { + data = other.data; + data2 = other.data2; + return *this; + } + + TestObject(TestObject&& other) { *this = std::move(other); } + + TestObject& operator=(TestObject&& other) { + data = other.data; + data2 = other.data2; + other.moved = true; + return *this; + } + + ~TestObject() { + if (!moved) { + destruct_count++; + } + } + + int data = 0; + int data2 = 0; + bool moved = false; +}; + +// static +int TestObject::construct_count = 0; +// static +int TestObject::destruct_count = 0; + +class CircularArrayTest : public ::testing::Test { + protected: + virtual void SetUp() { + // Reset to 0 even if the previous test leaked objects to keep the tests + // independent. + TestObject::construct_count = 0; + TestObject::destruct_count = 0; + } + + virtual void TearDown() { + ASSERT_EQ(0, TestObject::construct_count - TestObject::destruct_count) + << "construct count: " << TestObject::construct_count + << ", destruct_count: " << TestObject::destruct_count; + } + + template <size_t N> + void AssertCircularArrayEq(const CircularArray<TestObject, N>& ca, + const std::vector<int>& expected) { + if (expected.empty()) { + ASSERT_EQ(0U, ca.size()); + ASSERT_TRUE(ca.empty()); + } else { + ASSERT_EQ(expected.size(), ca.size()); + ASSERT_FALSE(ca.empty()); + + ASSERT_EQ(expected.front(), ca.front().data); + ASSERT_EQ(expected.back(), ca.back().data); + + for (size_t i = 0; i < ca.size(); ++i) { + ASSERT_EQ(expected[i], ca.at(i).data); + ASSERT_EQ(expected[i], ca[i].data); + } + } + } +}; + +} // end anonymous namespace + +// Basic API tests + +TEST_F(CircularArrayTest, default_constructor) { + CircularArray<TestObject, 2> ca; +} + +TEST_F(CircularArrayTest, at) { + CircularArray<TestObject, 2> ca; + ca.push_back(TestObject(1)); + ca.push_back(TestObject(2)); + + ASSERT_EQ(1, ca.at(0).data); + ASSERT_EQ(2, ca.at(1).data); +} + +TEST_F(CircularArrayTest, const_at) { + CircularArray<TestObject, 2> ca; + const auto& cca = ca; + + ca.push_back(TestObject(1)); + ca.push_back(TestObject(2)); + + ASSERT_EQ(1, cca.at(0).data); + ASSERT_EQ(2, cca.at(1).data); +} + +TEST_F(CircularArrayTest, operator_brackets) { + CircularArray<TestObject, 2> ca; + ca.push_back(TestObject(1)); + ca.push_back(TestObject(2)); + + ASSERT_EQ(1, ca[0].data); + ASSERT_EQ(2, ca[1].data); +} + +TEST_F(CircularArrayTest, const_operator_brackets) { + CircularArray<TestObject, 2> ca; + const auto& cca = ca; + + ca.push_back(TestObject(1)); + ca.push_back(TestObject(2)); + + ASSERT_EQ(1, cca[0].data); + ASSERT_EQ(2, cca[1].data); +} + +TEST_F(CircularArrayTest, back) { + CircularArray<TestObject, 2> ca; + + ca.push_back(TestObject(1)); + ASSERT_EQ(1, ca.back().data); + + ca.push_back(TestObject(2)); + ASSERT_EQ(2, ca.back().data); +} + +TEST_F(CircularArrayTest, const_back) { + CircularArray<TestObject, 2> ca; + const auto& cca = ca; + + ca.push_back(TestObject(1)); + ASSERT_EQ(1, cca.back().data); + + ca.push_back(TestObject(2)); + ASSERT_EQ(2, cca.back().data); +} + +TEST_F(CircularArrayTest, empty) { + CircularArray<TestObject, 2> ca; + + ASSERT_TRUE(ca.empty()); + + ca.push_back(TestObject(1)); + ASSERT_FALSE(ca.empty()); + + ca.push_back(TestObject(2)); + ASSERT_FALSE(ca.empty()); + + ca.pop_back(); + ASSERT_FALSE(ca.empty()); + + ca.pop_back(); + ASSERT_TRUE(ca.empty()); +} + +TEST_F(CircularArrayTest, front) { + CircularArray<TestObject, 2> ca; + + ca.push_back(TestObject(1)); + ASSERT_EQ(1, ca.front().data); + + ca.push_back(TestObject(2)); + ASSERT_EQ(1, ca.front().data); +} + +TEST_F(CircularArrayTest, const_front) { + CircularArray<TestObject, 2> ca; + const auto& cca = ca; + + ca.push_back(TestObject(1)); + ASSERT_EQ(1, cca.front().data); + + ca.push_back(TestObject(2)); + ASSERT_EQ(1, cca.front().data); +} + +TEST_F(CircularArrayTest, size) { + CircularArray<TestObject, 2> ca; + + ASSERT_EQ(0U, ca.size()); + + ca.push_back(TestObject(1)); + ASSERT_EQ(1U, ca.size()); + + ca.push_back(TestObject(2)); + ASSERT_EQ(2U, ca.size()); + + ca.pop_back(); + ASSERT_EQ(1U, ca.size()); + + ca.pop_back(); + ASSERT_EQ(0U, ca.size()); +} + +TEST_F(CircularArrayTest, clear) { + CircularArray<TestObject, 2> ca; + + ca.push_back(TestObject(1)); + ca.push_back(TestObject(2)); + ASSERT_EQ(2U, ca.size()); + + ca.clear(); + ASSERT_EQ(0U, ca.size()); +} + +// More involved tests + +TEST_F(CircularArrayTest, circular) { + CircularArray<TestObject, 4> ca; + + ca.push_back(TestObject(1)); + AssertCircularArrayEq(ca, {1}); + + ca.push_back(TestObject(2)); + AssertCircularArrayEq(ca, {1, 2}); + + ca.push_back(TestObject(3)); + AssertCircularArrayEq(ca, {1, 2, 3}); + + ca.pop_front(); + AssertCircularArrayEq(ca, {2, 3}); + + ca.push_back(TestObject(4)); + AssertCircularArrayEq(ca, {2, 3, 4}); + + ca.pop_front(); + AssertCircularArrayEq(ca, {3, 4}); + + ca.pop_front(); + AssertCircularArrayEq(ca, {4}); + + ca.push_back(TestObject(5)); + AssertCircularArrayEq(ca, {4, 5}); + + ca.push_back(TestObject(6)); + AssertCircularArrayEq(ca, {4, 5, 6}); + + ca.pop_back(); + AssertCircularArrayEq(ca, {4, 5}); + + ca.pop_back(); + AssertCircularArrayEq(ca, {4}); + + ca.pop_front(); + AssertCircularArrayEq(ca, {}); +} diff --git a/third_party/wasm2c/src/test-filenames.cc b/third_party/wasm2c/src/test-filenames.cc new file mode 100644 index 0000000000..b734f58bda --- /dev/null +++ b/third_party/wasm2c/src/test-filenames.cc @@ -0,0 +1,61 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" + +#include "wabt/filenames.h" + +using namespace wabt; + +namespace { + +void assert_string_view_eq(const char* s, std::string_view sv) { + size_t len = std::strlen(s); + ASSERT_EQ(len, sv.size()); + for (size_t i = 0; i < len; ++i) { + ASSERT_EQ(s[i], sv[i]); + } +} + +} // end anonymous namespace + +TEST(filenames, GetBasename) { + assert_string_view_eq("foo.txt", GetBasename("/bar/dir/foo.txt")); + assert_string_view_eq("foo.txt", GetBasename("bar/dir/foo.txt")); + assert_string_view_eq("foo.txt", GetBasename("/foo.txt")); + assert_string_view_eq("foo.txt", GetBasename("\\bar\\dir\\foo.txt")); + assert_string_view_eq("foo.txt", GetBasename("bar\\dir\\foo.txt")); + assert_string_view_eq("foo.txt", GetBasename("\\foo.txt")); + assert_string_view_eq("foo.txt", GetBasename("foo.txt")); + assert_string_view_eq("foo", GetBasename("foo")); + assert_string_view_eq("", GetBasename("")); +} + +TEST(filenames, GetExtension) { + assert_string_view_eq(".txt", GetExtension("/bar/dir/foo.txt")); + assert_string_view_eq(".txt", GetExtension("bar/dir/foo.txt")); + assert_string_view_eq(".txt", GetExtension("foo.txt")); + assert_string_view_eq("", GetExtension("foo")); + assert_string_view_eq("", GetExtension("")); +} + +TEST(filenames, StripExtension) { + assert_string_view_eq("/bar/dir/foo", StripExtension("/bar/dir/foo.txt")); + assert_string_view_eq("bar/dir/foo", StripExtension("bar/dir/foo.txt")); + assert_string_view_eq("foo", StripExtension("foo.txt")); + assert_string_view_eq("foo", StripExtension("foo")); + assert_string_view_eq("", StripExtension("")); +} diff --git a/third_party/wasm2c/src/test-hexfloat.cc b/third_party/wasm2c/src/test-hexfloat.cc new file mode 100644 index 0000000000..d732c99202 --- /dev/null +++ b/third_party/wasm2c/src/test-hexfloat.cc @@ -0,0 +1,264 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cstdio> +#include <thread> +#include <vector> + +#include "gtest/gtest.h" + +#include "wabt/literal.h" + +#define FOREACH_UINT32_MULTIPLIER 1 + +#define FOREACH_UINT32(bits) \ + uint32_t last_bits = 0; \ + uint32_t bits = shard; \ + int last_top_byte = -1; \ + for (; bits >= last_bits; \ + last_bits = bits, bits += num_threads_ * FOREACH_UINT32_MULTIPLIER) + +#define LOG_COMPLETION(bits) \ + if (shard == 0) { \ + int top_byte = bits >> 24; \ + if (top_byte != last_top_byte) { \ + printf("value: 0x%08x (%d%%)\r", bits, \ + static_cast<int>(static_cast<double>(bits) * 100 / UINT32_MAX)); \ + fflush(stdout); \ + last_top_byte = top_byte; \ + } \ + } + +#define LOG_DONE() \ + if (shard == 0) { \ + printf("done.\n"); \ + fflush(stdout); \ + } + +using namespace wabt; + +template <typename T, typename F> +T bit_cast(F value) { + T result; + memcpy(&result, &value, sizeof(result)); + return result; +} + +static bool is_infinity_or_nan(uint32_t float_bits) { + return ((float_bits >> 23) & 0xff) == 0xff; +} + +static bool is_infinity_or_nan(uint64_t double_bits) { + return ((double_bits >> 52) & 0x7ff) == 0x7ff; +} + +class ThreadedTest : public ::testing::Test { + protected: + static constexpr int kDefaultNumThreads = 2; + + virtual void SetUp() { + num_threads_ = std::thread::hardware_concurrency(); + if (num_threads_ == 0) + num_threads_ = kDefaultNumThreads; + } + + virtual void RunShard(int shard) = 0; + + void RunThreads() { + std::vector<std::thread> threads; + + for (int i = 0; i < num_threads_; ++i) { + threads.emplace_back(&ThreadedTest::RunShard, this, i); + } + + for (std::thread& thread : threads) { + thread.join(); + } + } + + int num_threads_; +}; + +/* floats */ +class AllFloatsParseTest : public ThreadedTest { + protected: + virtual void RunShard(int shard) { + char buffer[100]; + FOREACH_UINT32(bits) { + LOG_COMPLETION(bits); + if (is_infinity_or_nan(bits)) + continue; + + float value = bit_cast<float>(bits); + int len = snprintf(buffer, sizeof(buffer), "%a", value); + + uint32_t me; + ASSERT_EQ(Result::Ok, + ParseFloat(LiteralType::Hexfloat, buffer, buffer + len, &me)); + ASSERT_EQ(me, bits); + } + LOG_DONE(); + } +}; + +TEST_F(AllFloatsParseTest, Run) { + RunThreads(); +} + +class AllFloatsWriteTest : public ThreadedTest { + protected: + virtual void RunShard(int shard) { + char buffer[100]; + FOREACH_UINT32(bits) { + LOG_COMPLETION(bits); + if (is_infinity_or_nan(bits)) + continue; + + WriteFloatHex(buffer, sizeof(buffer), bits); + + char* endptr; + float them_float = strtof(buffer, &endptr); + uint32_t them_bits = bit_cast<uint32_t>(them_float); + ASSERT_EQ(bits, them_bits); + } + LOG_DONE(); + } +}; + +TEST_F(AllFloatsWriteTest, Run) { + RunThreads(); +} + +class AllFloatsRoundtripTest : public ThreadedTest { + protected: + static LiteralType ClassifyFloat(uint32_t float_bits) { + if (is_infinity_or_nan(float_bits)) { + if (float_bits & 0x7fffff) { + return LiteralType::Nan; + } else { + return LiteralType::Infinity; + } + } else { + return LiteralType::Hexfloat; + } + } + + virtual void RunShard(int shard) { + char buffer[100]; + FOREACH_UINT32(bits) { + LOG_COMPLETION(bits); + WriteFloatHex(buffer, sizeof(buffer), bits); + int len = strlen(buffer); + + uint32_t new_bits; + ASSERT_EQ(Result::Ok, ParseFloat(ClassifyFloat(bits), buffer, + buffer + len, &new_bits)); + ASSERT_EQ(new_bits, bits); + } + LOG_DONE(); + } +}; + +TEST_F(AllFloatsRoundtripTest, Run) { + RunThreads(); +} + +/* doubles */ +class ManyDoublesParseTest : public ThreadedTest { + protected: + virtual void RunShard(int shard) { + char buffer[100]; + FOREACH_UINT32(halfbits) { + LOG_COMPLETION(halfbits); + uint64_t bits = (static_cast<uint64_t>(halfbits) << 32) | halfbits; + if (is_infinity_or_nan(bits)) + continue; + + double value = bit_cast<double>(bits); + int len = snprintf(buffer, sizeof(buffer), "%a", value); + + uint64_t me; + ASSERT_EQ(Result::Ok, + ParseDouble(LiteralType::Hexfloat, buffer, buffer + len, &me)); + ASSERT_EQ(me, bits); + } + LOG_DONE(); + } +}; + +TEST_F(ManyDoublesParseTest, Run) { + RunThreads(); +} + +class ManyDoublesWriteTest : public ThreadedTest { + protected: + virtual void RunShard(int shard) { + char buffer[100]; + FOREACH_UINT32(halfbits) { + LOG_COMPLETION(halfbits); + uint64_t bits = (static_cast<uint64_t>(halfbits) << 32) | halfbits; + if (is_infinity_or_nan(bits)) + continue; + + WriteDoubleHex(buffer, sizeof(buffer), bits); + + char* endptr; + double them_double = strtod(buffer, &endptr); + uint64_t them_bits = bit_cast<uint64_t>(them_double); + ASSERT_EQ(bits, them_bits); + } + LOG_DONE(); + } +}; + +TEST_F(ManyDoublesWriteTest, Run) { + RunThreads(); +} + +class ManyDoublesRoundtripTest : public ThreadedTest { + protected: + static LiteralType ClassifyDouble(uint64_t double_bits) { + if (is_infinity_or_nan(double_bits)) { + if (double_bits & 0xfffffffffffffULL) { + return LiteralType::Nan; + } else { + return LiteralType::Infinity; + } + } else { + return LiteralType::Hexfloat; + } + } + + virtual void RunShard(int shard) { + char buffer[100]; + FOREACH_UINT32(halfbits) { + LOG_COMPLETION(halfbits); + uint64_t bits = (static_cast<uint64_t>(halfbits) << 32) | halfbits; + WriteDoubleHex(buffer, sizeof(buffer), bits); + int len = strlen(buffer); + + uint64_t new_bits; + ASSERT_EQ(Result::Ok, ParseDouble(ClassifyDouble(bits), buffer, + buffer + len, &new_bits)); + ASSERT_EQ(new_bits, bits); + } + LOG_DONE(); + } +}; + +TEST_F(ManyDoublesRoundtripTest, Run) { + RunThreads(); +} diff --git a/third_party/wasm2c/src/test-interp.cc b/third_party/wasm2c/src/test-interp.cc new file mode 100644 index 0000000000..11e4770fca --- /dev/null +++ b/third_party/wasm2c/src/test-interp.cc @@ -0,0 +1,723 @@ +/* + * Copyright 2020 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" + +#include "wabt/binary-reader.h" +#include "wabt/error-formatter.h" + +#include "wabt/interp/binary-reader-interp.h" +#include "wabt/interp/interp.h" + +using namespace wabt; +using namespace wabt::interp; + +class InterpTest : public ::testing::Test { + public: + void ReadModule(const std::vector<u8>& data) { + Errors errors; + ReadBinaryOptions options; + Result result = ReadBinaryInterp("<internal>", data.data(), data.size(), + options, &errors, &module_desc_); + ASSERT_EQ(Result::Ok, result) + << FormatErrorsToString(errors, Location::Type::Binary); + } + + void Instantiate(const RefVec& imports = RefVec{}) { + mod_ = Module::New(store_, module_desc_); + RefPtr<Trap> trap; + inst_ = Instance::Instantiate(store_, mod_.ref(), imports, &trap); + ASSERT_TRUE(inst_) << trap->message(); + } + + DefinedFunc::Ptr GetFuncExport(interp::Index index) { + EXPECT_LT(index, inst_->exports().size()); + return store_.UnsafeGet<DefinedFunc>(inst_->exports()[index]); + } + + void ExpectBufferStrEq(OutputBuffer& buf, const char* str) { + std::string buf_str(buf.data.begin(), buf.data.end()); + EXPECT_STREQ(buf_str.c_str(), str); + } + + Store store_; + ModuleDesc module_desc_; + Module::Ptr mod_; + Instance::Ptr inst_; +}; + +TEST_F(InterpTest, Empty) { + ASSERT_TRUE(mod_.empty()); + ReadModule({0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00}); + Instantiate(); + ASSERT_FALSE(mod_.empty()); +} + +TEST_F(InterpTest, MVP) { + // (module + // (type (;0;) (func (param i32) (result i32))) + // (type (;1;) (func (param f32) (result f32))) + // (type (;2;) (func)) + // (import "foo" "bar" (func (;0;) (type 0))) + // (func (;1;) (type 1) (param f32) (result f32) + // (f32.const 0x1.5p+5 (;=42;))) + // (func (;2;) (type 2)) + // (table (;0;) 1 2 funcref) + // (memory (;0;) 1) + // (global (;0;) i32 (i32.const 1)) + // (export "quux" (func 1)) + // (start 2) + // (elem (;0;) (i32.const 0) 0 1) + // (data (;0;) (i32.const 2) "hello")) + ReadModule({ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x03, 0x60, + 0x01, 0x7f, 0x01, 0x7f, 0x60, 0x01, 0x7d, 0x01, 0x7d, 0x60, 0x00, 0x00, + 0x02, 0x0b, 0x01, 0x03, 0x66, 0x6f, 0x6f, 0x03, 0x62, 0x61, 0x72, 0x00, + 0x00, 0x03, 0x03, 0x02, 0x01, 0x02, 0x04, 0x05, 0x01, 0x70, 0x01, 0x01, + 0x02, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x06, 0x01, 0x7f, 0x00, 0x41, + 0x01, 0x0b, 0x07, 0x08, 0x01, 0x04, 0x71, 0x75, 0x75, 0x78, 0x00, 0x01, + 0x08, 0x01, 0x02, 0x09, 0x08, 0x01, 0x00, 0x41, 0x00, 0x0b, 0x02, 0x00, + 0x01, 0x0a, 0x0c, 0x02, 0x07, 0x00, 0x43, 0x00, 0x00, 0x28, 0x42, 0x0b, + 0x02, 0x00, 0x0b, 0x0b, 0x0b, 0x01, 0x00, 0x41, 0x02, 0x0b, 0x05, 0x68, + 0x65, 0x6c, 0x6c, 0x6f, + }); + + EXPECT_EQ(3u, module_desc_.func_types.size()); + EXPECT_EQ(1u, module_desc_.imports.size()); + EXPECT_EQ(2u, module_desc_.funcs.size()); + EXPECT_EQ(1u, module_desc_.tables.size()); + EXPECT_EQ(1u, module_desc_.memories.size()); + EXPECT_EQ(1u, module_desc_.globals.size()); + EXPECT_EQ(0u, module_desc_.tags.size()); + EXPECT_EQ(1u, module_desc_.exports.size()); + EXPECT_EQ(1u, module_desc_.starts.size()); + EXPECT_EQ(1u, module_desc_.elems.size()); + EXPECT_EQ(1u, module_desc_.datas.size()); +} + +namespace { + +// (func (export "fac") (param $n i32) (result i32) +// (local $result i32) +// (local.set $result (i32.const 1)) +// (loop (result i32) +// (local.set $result +// (i32.mul +// (br_if 1 (local.get $result) (i32.eqz (local.get $n))) +// (local.get $n))) +// (local.set $n (i32.sub (local.get $n) (i32.const 1))) +// (br 0))) +const std::vector<u8> s_fac_module = { + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, + 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x07, + 0x01, 0x03, 0x66, 0x61, 0x63, 0x00, 0x00, 0x0a, 0x22, 0x01, 0x20, + 0x01, 0x01, 0x7f, 0x41, 0x01, 0x21, 0x01, 0x03, 0x7f, 0x20, 0x01, + 0x20, 0x00, 0x45, 0x0d, 0x01, 0x20, 0x00, 0x6c, 0x21, 0x01, 0x20, + 0x00, 0x41, 0x01, 0x6b, 0x21, 0x00, 0x0c, 0x00, 0x0b, 0x0b, +}; + +} // namespace + +TEST_F(InterpTest, Disassemble) { + ReadModule(s_fac_module); + + MemoryStream stream; + module_desc_.istream.Disassemble(&stream); + auto buf = stream.ReleaseOutputBuffer(); + + ExpectBufferStrEq(*buf, +R"( 0| alloca 1 + 8| i32.const 1 + 16| local.set $2, %[-1] + 24| local.get $1 + 32| local.get $3 + 40| i32.eqz %[-1] + 44| br_unless @60, %[-1] + 52| br @116 + 60| local.get $3 + 68| i32.mul %[-2], %[-1] + 72| local.set $2, %[-1] + 80| local.get $2 + 88| i32.const 1 + 96| i32.sub %[-2], %[-1] + 100| local.set $3, %[-1] + 108| br @24 + 116| drop_keep $2 $1 + 128| return +)"); +} + +TEST_F(InterpTest, Fac) { + ReadModule(s_fac_module); + Instantiate(); + auto func = GetFuncExport(0); + + Values results; + Trap::Ptr trap; + Result result = func->Call(store_, {Value::Make(5)}, results, &trap); + + ASSERT_EQ(Result::Ok, result); + EXPECT_EQ(1u, results.size()); + EXPECT_EQ(120u, results[0].Get<u32>()); +} + +TEST_F(InterpTest, Fac_Trace) { + ReadModule(s_fac_module); + Instantiate(); + auto func = GetFuncExport(0); + + Values results; + Trap::Ptr trap; + MemoryStream stream; + Result result = func->Call(store_, {Value::Make(2)}, results, &trap, &stream); + ASSERT_EQ(Result::Ok, result); + + auto buf = stream.ReleaseOutputBuffer(); + ExpectBufferStrEq(*buf, +R"(#0. 0: V:1 | alloca 1 +#0. 8: V:2 | i32.const 1 +#0. 16: V:3 | local.set $2, 1 +#0. 24: V:2 | local.get $1 +#0. 32: V:3 | local.get $3 +#0. 40: V:4 | i32.eqz 2 +#0. 44: V:4 | br_unless @60, 0 +#0. 60: V:3 | local.get $3 +#0. 68: V:4 | i32.mul 1, 2 +#0. 72: V:3 | local.set $2, 2 +#0. 80: V:2 | local.get $2 +#0. 88: V:3 | i32.const 1 +#0. 96: V:4 | i32.sub 2, 1 +#0. 100: V:3 | local.set $3, 1 +#0. 108: V:2 | br @24 +#0. 24: V:2 | local.get $1 +#0. 32: V:3 | local.get $3 +#0. 40: V:4 | i32.eqz 1 +#0. 44: V:4 | br_unless @60, 0 +#0. 60: V:3 | local.get $3 +#0. 68: V:4 | i32.mul 2, 1 +#0. 72: V:3 | local.set $2, 2 +#0. 80: V:2 | local.get $2 +#0. 88: V:3 | i32.const 1 +#0. 96: V:4 | i32.sub 1, 1 +#0. 100: V:3 | local.set $3, 0 +#0. 108: V:2 | br @24 +#0. 24: V:2 | local.get $1 +#0. 32: V:3 | local.get $3 +#0. 40: V:4 | i32.eqz 0 +#0. 44: V:4 | br_unless @60, 1 +#0. 52: V:3 | br @116 +#0. 116: V:3 | drop_keep $2 $1 +#0. 128: V:1 | return +)"); +} + +TEST_F(InterpTest, Local_Trace) { + // (func (export "a") + // (local i32 i64 f32 f64) + // (local.set 0 (i32.const 0)) + // (local.set 1 (i64.const 1)) + // (local.set 2 (f32.const 2)) + // (local.set 3 (f64.const 3))) + ReadModule({ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, + 0x60, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x07, 0x05, 0x01, 0x01, + 0x61, 0x00, 0x00, 0x0a, 0x26, 0x01, 0x24, 0x04, 0x01, 0x7f, 0x01, + 0x7e, 0x01, 0x7d, 0x01, 0x7c, 0x41, 0x00, 0x21, 0x00, 0x42, 0x01, + 0x21, 0x01, 0x43, 0x00, 0x00, 0x00, 0x40, 0x21, 0x02, 0x44, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x21, 0x03, 0x0b, + }); + + Instantiate(); + auto func = GetFuncExport(0); + + Values results; + Trap::Ptr trap; + MemoryStream stream; + Result result = func->Call(store_, {}, results, &trap, &stream); + ASSERT_EQ(Result::Ok, result); + + auto buf = stream.ReleaseOutputBuffer(); + ExpectBufferStrEq(*buf, +R"(#0. 0: V:0 | alloca 4 +#0. 8: V:4 | i32.const 0 +#0. 16: V:5 | local.set $5, 0 +#0. 24: V:4 | i64.const 1 +#0. 36: V:5 | local.set $4, 1 +#0. 44: V:4 | f32.const 2 +#0. 52: V:5 | local.set $3, 2 +#0. 60: V:4 | f64.const 3 +#0. 72: V:5 | local.set $2, 3 +#0. 80: V:4 | drop_keep $4 $0 +#0. 92: V:0 | return +)"); +} + +TEST_F(InterpTest, HostFunc) { + // (import "" "f" (func $f (param i32) (result i32))) + // (func (export "g") (result i32) + // (call $f (i32.const 1))) + ReadModule({ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0a, + 0x02, 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x60, 0x00, 0x01, 0x7f, + 0x02, 0x06, 0x01, 0x00, 0x01, 0x66, 0x00, 0x00, 0x03, 0x02, + 0x01, 0x01, 0x07, 0x05, 0x01, 0x01, 0x67, 0x00, 0x01, 0x0a, + 0x08, 0x01, 0x06, 0x00, 0x41, 0x01, 0x10, 0x00, 0x0b, + }); + + auto host_func = + HostFunc::New(store_, FuncType{{ValueType::I32}, {ValueType::I32}}, + [](Thread& thread, const Values& params, Values& results, + Trap::Ptr* out_trap) -> Result { + results[0] = Value::Make(params[0].Get<u32>() + 1); + return Result::Ok; + }); + + Instantiate({host_func->self()}); + + Values results; + Trap::Ptr trap; + Result result = GetFuncExport(0)->Call(store_, {}, results, &trap); + + ASSERT_EQ(Result::Ok, result); + EXPECT_EQ(1u, results.size()); + EXPECT_EQ(2u, results[0].Get<u32>()); +} + +TEST_F(InterpTest, HostFunc_PingPong) { + // (import "" "f" (func $f (param i32) (result i32))) + // (func (export "g") (param i32) (result i32) + // (call $f (i32.add (local.get 0) (i32.const 1)))) + ReadModule({ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60, + 0x01, 0x7f, 0x01, 0x7f, 0x02, 0x06, 0x01, 0x00, 0x01, 0x66, 0x00, 0x00, + 0x03, 0x02, 0x01, 0x00, 0x07, 0x05, 0x01, 0x01, 0x67, 0x00, 0x01, 0x0a, + 0x0b, 0x01, 0x09, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x10, 0x00, 0x0b, + }); + + auto host_func = + HostFunc::New(store_, FuncType{{ValueType::I32}, {ValueType::I32}}, + [&](Thread& thread, const Values& params, Values& results, + Trap::Ptr* out_trap) -> Result { + auto val = params[0].Get<u32>(); + if (val < 10) { + return GetFuncExport(0)->Call( + store_, {Value::Make(val * 2)}, results, out_trap); + } + results[0] = Value::Make(val); + return Result::Ok; + }); + + Instantiate({host_func->self()}); + + // Should produce the following calls: + // g(1) -> f(2) -> g(4) -> f(5) -> g(10) -> f(11) -> return 11 + + Values results; + Trap::Ptr trap; + Result result = + GetFuncExport(0)->Call(store_, {Value::Make(1)}, results, &trap); + + ASSERT_EQ(Result::Ok, result); + EXPECT_EQ(1u, results.size()); + EXPECT_EQ(11u, results[0].Get<u32>()); +} + +TEST_F(InterpTest, HostFunc_PingPong_SameThread) { + // (import "" "f" (func $f (param i32) (result i32))) + // (func (export "g") (param i32) (result i32) + // (call $f (i32.add (local.get 0) (i32.const 1)))) + ReadModule({ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60, + 0x01, 0x7f, 0x01, 0x7f, 0x02, 0x06, 0x01, 0x00, 0x01, 0x66, 0x00, 0x00, + 0x03, 0x02, 0x01, 0x00, 0x07, 0x05, 0x01, 0x01, 0x67, 0x00, 0x01, 0x0a, + 0x0b, 0x01, 0x09, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x10, 0x00, 0x0b, + }); + + Thread thread(store_); + + auto host_func = + HostFunc::New(store_, FuncType{{ValueType::I32}, {ValueType::I32}}, + [&](Thread& t, const Values& params, Values& results, + Trap::Ptr* out_trap) -> Result { + auto val = params[0].Get<u32>(); + if (val < 10) { + return GetFuncExport(0)->Call(t, {Value::Make(val * 2)}, + results, out_trap); + } + results[0] = Value::Make(val); + return Result::Ok; + }); + + Instantiate({host_func->self()}); + + // Should produce the following calls: + // g(1) -> f(2) -> g(4) -> f(5) -> g(10) -> f(11) -> return 11 + + Values results; + Trap::Ptr trap; + Result result = + GetFuncExport(0)->Call(thread, {Value::Make(1)}, results, &trap); + + ASSERT_EQ(Result::Ok, result); + EXPECT_EQ(1u, results.size()); + EXPECT_EQ(11u, results[0].Get<u32>()); +} + +TEST_F(InterpTest, HostTrap) { + // (import "host" "a" (func $0)) + // (func $1 call $0) + // (start $1) + ReadModule({ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, + 0x60, 0x00, 0x00, 0x02, 0x0a, 0x01, 0x04, 0x68, 0x6f, 0x73, 0x74, + 0x01, 0x61, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x08, 0x01, 0x01, + 0x0a, 0x06, 0x01, 0x04, 0x00, 0x10, 0x00, 0x0b, + }); + + auto host_func = + HostFunc::New(store_, FuncType{{}, {}}, + [&](Thread& thread, const Values& params, Values& results, + Trap::Ptr* out_trap) -> Result { + *out_trap = Trap::New(store_, "boom"); + return Result::Error; + }); + + mod_ = Module::New(store_, module_desc_); + RefPtr<Trap> trap; + Instance::Instantiate(store_, mod_.ref(), {host_func->self()}, &trap); + + ASSERT_TRUE(trap); + ASSERT_EQ("boom", trap->message()); +} + +TEST_F(InterpTest, Rot13) { + // (import "host" "mem" (memory $mem 1)) + // (import "host" "fill_buf" (func $fill_buf (param i32 i32) (result i32))) + // (import "host" "buf_done" (func $buf_done (param i32 i32))) + // + // (func $rot13c (param $c i32) (result i32) + // (local $uc i32) + // + // ;; No change if < 'A'. + // (if (i32.lt_u (local.get $c) (i32.const 65)) + // (return (local.get $c))) + // + // ;; Clear 5th bit of c, to force uppercase. 0xdf = 0b11011111 + // (local.set $uc (i32.and (local.get $c) (i32.const 0xdf))) + // + // ;; In range ['A', 'M'] return |c| + 13. + // (if (i32.le_u (local.get $uc) (i32.const 77)) + // (return (i32.add (local.get $c) (i32.const 13)))) + // + // ;; In range ['N', 'Z'] return |c| - 13. + // (if (i32.le_u (local.get $uc) (i32.const 90)) + // (return (i32.sub (local.get $c) (i32.const 13)))) + // + // ;; No change for everything else. + // (return (local.get $c)) + // ) + // + // (func (export "rot13") + // (local $size i32) + // (local $i i32) + // + // ;; Ask host to fill memory [0, 1024) with data. + // (call $fill_buf (i32.const 0) (i32.const 1024)) + // + // ;; The host returns the size filled. + // (local.set $size) + // + // ;; Loop over all bytes and rot13 them. + // (block $exit + // (loop $top + // ;; if (i >= size) break + // (if (i32.ge_u (local.get $i) (local.get $size)) (br $exit)) + // + // ;; mem[i] = rot13c(mem[i]) + // (i32.store8 + // (local.get $i) + // (call $rot13c + // (i32.load8_u (local.get $i)))) + // + // ;; i++ + // (local.set $i (i32.add (local.get $i) (i32.const 1))) + // (br $top) + // ) + // ) + // + // (call $buf_done (i32.const 0) (local.get $size)) + // ) + ReadModule({ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x14, 0x04, 0x60, + 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x02, 0x7f, 0x7f, 0x00, 0x60, 0x01, + 0x7f, 0x01, 0x7f, 0x60, 0x00, 0x00, 0x02, 0x2d, 0x03, 0x04, 0x68, 0x6f, + 0x73, 0x74, 0x03, 0x6d, 0x65, 0x6d, 0x02, 0x00, 0x01, 0x04, 0x68, 0x6f, + 0x73, 0x74, 0x08, 0x66, 0x69, 0x6c, 0x6c, 0x5f, 0x62, 0x75, 0x66, 0x00, + 0x00, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x08, 0x62, 0x75, 0x66, 0x5f, 0x64, + 0x6f, 0x6e, 0x65, 0x00, 0x01, 0x03, 0x03, 0x02, 0x02, 0x03, 0x07, 0x09, + 0x01, 0x05, 0x72, 0x6f, 0x74, 0x31, 0x33, 0x00, 0x03, 0x0a, 0x74, 0x02, + 0x39, 0x01, 0x01, 0x7f, 0x20, 0x00, 0x41, 0xc1, 0x00, 0x49, 0x04, 0x40, + 0x20, 0x00, 0x0f, 0x0b, 0x20, 0x00, 0x41, 0xdf, 0x01, 0x71, 0x21, 0x01, + 0x20, 0x01, 0x41, 0xcd, 0x00, 0x4d, 0x04, 0x40, 0x20, 0x00, 0x41, 0x0d, + 0x6a, 0x0f, 0x0b, 0x20, 0x01, 0x41, 0xda, 0x00, 0x4d, 0x04, 0x40, 0x20, + 0x00, 0x41, 0x0d, 0x6b, 0x0f, 0x0b, 0x20, 0x00, 0x0f, 0x0b, 0x38, 0x01, + 0x02, 0x7f, 0x41, 0x00, 0x41, 0x80, 0x08, 0x10, 0x00, 0x21, 0x00, 0x02, + 0x40, 0x03, 0x40, 0x20, 0x01, 0x20, 0x00, 0x4f, 0x04, 0x40, 0x0c, 0x02, + 0x0b, 0x20, 0x01, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x10, 0x02, 0x3a, 0x00, + 0x00, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, 0x00, 0x0b, 0x0b, + 0x41, 0x00, 0x20, 0x00, 0x10, 0x01, 0x0b, + }); + + auto host_func = + HostFunc::New(store_, FuncType{{ValueType::I32}, {ValueType::I32}}, + [](Thread& thread, const Values& params, Values& results, + Trap::Ptr* out_trap) -> Result { + results[0] = Value::Make(params[0].Get<u32>() + 1); + return Result::Ok; + }); + + std::string string_data = "Hello, WebAssembly!"; + + auto memory = Memory::New(store_, MemoryType{Limits{1}}); + + auto fill_buf = [&](Thread& thread, const Values& params, Values& results, + Trap::Ptr* out_trap) -> Result { + // (param $ptr i32) (param $max_size i32) (result $size i32) + EXPECT_EQ(2u, params.size()); + EXPECT_EQ(1u, results.size()); + + u32 ptr = params[0].Get<u32>(); + u32 max_size = params[1].Get<u32>(); + u32 size = std::min(max_size, u32(string_data.size())); + + EXPECT_LT(ptr + size, memory->ByteSize()); + + std::copy(string_data.begin(), string_data.begin() + size, + memory->UnsafeData() + ptr); + + results[0].Set(size); + return Result::Ok; + }; + auto fill_buf_func = HostFunc::New( + store_, FuncType{{ValueType::I32, ValueType::I32}, {ValueType::I32}}, + fill_buf); + + auto buf_done = [&](Thread& thread, const Values& params, Values& results, + Trap::Ptr* out_trap) -> Result { + // (param $ptr i32) (param $size i32) + EXPECT_EQ(2u, params.size()); + EXPECT_EQ(0u, results.size()); + + u32 ptr = params[0].Get<u32>(); + u32 size = params[1].Get<u32>(); + + EXPECT_LT(ptr + size, memory->ByteSize()); + + string_data.resize(size); + std::copy(memory->UnsafeData() + ptr, memory->UnsafeData() + ptr + size, + string_data.begin()); + + return Result::Ok; + }; + auto buf_done_func = HostFunc::New( + store_, FuncType{{ValueType::I32, ValueType::I32}, {}}, buf_done); + + Instantiate({memory->self(), fill_buf_func->self(), buf_done_func->self()}); + + auto rot13 = GetFuncExport(0); + + Values results; + Trap::Ptr trap; + ASSERT_EQ(Result::Ok, rot13->Call(store_, {}, results, &trap)); + + ASSERT_EQ("Uryyb, JroNffrzoyl!", string_data); + + ASSERT_EQ(Result::Ok, rot13->Call(store_, {}, results, &trap)); + + ASSERT_EQ("Hello, WebAssembly!", string_data); +} + +class InterpGCTest : public InterpTest { + public: + void SetUp() override { before_new = store_.object_count(); } + + void TearDown() override { + // Reset instance and module, in case they were allocated. + inst_.reset(); + mod_.reset(); + + store_.Collect(); + EXPECT_EQ(before_new, store_.object_count()); + } + + size_t before_new; +}; + +TEST_F(InterpGCTest, Collect_Basic) { + auto foreign = Foreign::New(store_, nullptr); + auto after_new = store_.object_count(); + EXPECT_EQ(before_new + 1, after_new); + + // Remove root, but object is not destroyed until collect. + foreign.reset(); + EXPECT_EQ(after_new, store_.object_count()); +} + +TEST_F(InterpGCTest, Collect_GlobalCycle) { + auto gt = GlobalType{ValueType::ExternRef, Mutability::Var}; + auto g1 = Global::New(store_, gt, Value::Make(Ref::Null)); + auto g2 = Global::New(store_, gt, Value::Make(g1->self())); + g1->Set(store_, g2->self()); + + auto after_new = store_.object_count(); + EXPECT_EQ(before_new + 2, after_new); + + // Remove g1 root, but it's kept alive by g2. + g1.reset(); + store_.Collect(); + EXPECT_EQ(after_new, store_.object_count()); + + // Remove g2 root, now both should be removed. + g2.reset(); +} + +TEST_F(InterpGCTest, Collect_TableCycle) { + auto tt = TableType{ValueType::ExternRef, Limits{2}}; + auto t1 = Table::New(store_, tt); + auto t2 = Table::New(store_, tt); + auto t3 = Table::New(store_, tt); + + t1->Set(store_, 0, t1->self()); // t1 references itself. + t2->Set(store_, 0, t3->self()); + t3->Set(store_, 0, t2->self()); // t2 and t3 reference each other. + t3->Set(store_, 1, t1->self()); // t3 also references t1. + + auto after_new = store_.object_count(); + EXPECT_EQ(before_new + 3, after_new); + + // Remove t1 and t2 roots, but their kept alive by t3. + t1.reset(); + t2.reset(); + store_.Collect(); + EXPECT_EQ(after_new, store_.object_count()); + + // Remove t3 root, now all should be removed. + t3.reset(); +} + +TEST_F(InterpGCTest, Collect_Func) { + ReadModule(s_fac_module); + Instantiate(); + auto func = GetFuncExport(0); + + auto after_new = store_.object_count(); + EXPECT_EQ(before_new + 3, after_new); // module, instance, func. + + // Reset module and instance roots, but they'll be kept alive by the func. + mod_.reset(); + inst_.reset(); + store_.Collect(); + EXPECT_EQ(after_new, store_.object_count()); +} + +TEST_F(InterpGCTest, Collect_InstanceImport) { + // (import "" "f" (func)) + // (import "" "t" (table 0 funcref)) + // (import "" "m" (memory 0)) + // (import "" "g" (global i32)) + ReadModule({ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, + 0x60, 0x00, 0x00, 0x02, 0x19, 0x04, 0x00, 0x01, 0x66, 0x00, 0x00, + 0x00, 0x01, 0x74, 0x01, 0x70, 0x00, 0x00, 0x00, 0x01, 0x6d, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x67, 0x03, 0x7f, 0x00, + }); + auto f = HostFunc::New(store_, FuncType{{}, {}}, + [](Thread& thread, const Values&, Values&, + Trap::Ptr*) -> Result { return Result::Ok; }); + auto t = Table::New(store_, TableType{ValueType::FuncRef, Limits{0}}); + auto m = Memory::New(store_, MemoryType{Limits{0}}); + auto g = Global::New(store_, GlobalType{ValueType::I32, Mutability::Const}, + Value::Make(5)); + + Instantiate({f->self(), t->self(), m->self(), g->self()}); + auto after_new = store_.object_count(); + EXPECT_EQ(before_new + 6, after_new); // module, instance, f, t, m, g + + // Instance keeps all imports alive. + f.reset(); + t.reset(); + m.reset(); + g.reset(); + store_.Collect(); + EXPECT_EQ(after_new, store_.object_count()); +} + +TEST_F(InterpGCTest, Collect_InstanceExport) { + // (func (export "f")) + // (global (export "g") i32 (i32.const 0)) + // (table (export "t") 0 funcref) + // (memory (export "m") 0) + ReadModule({ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, + 0x60, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x04, 0x04, 0x01, 0x70, + 0x00, 0x00, 0x05, 0x03, 0x01, 0x00, 0x00, 0x06, 0x06, 0x01, 0x7f, + 0x00, 0x41, 0x00, 0x0b, 0x07, 0x11, 0x04, 0x01, 0x66, 0x00, 0x00, + 0x01, 0x67, 0x03, 0x00, 0x01, 0x74, 0x01, 0x00, 0x01, 0x6d, 0x02, + 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b, + }); + Instantiate(); + auto after_new = store_.object_count(); + EXPECT_EQ(before_new + 7, + after_new); // module, instance, f, t, m, g, g-init-func + + // Instance keeps all exports alive, except the init func which can be + // collected once its has been run + store_.Collect(); + EXPECT_EQ(after_new - 1, store_.object_count()); +} + +TEST_F(InterpGCTest, Collect_DeepRecursion) { + const size_t table_count = 65; + + TableType tt = TableType{ValueType::ExternRef, Limits{1}}; + + // Create a chain of tables, where each contains + // a single reference to the next table. + + Table::Ptr prev_table = Table::New(store_, tt); + + for (size_t i = 1; i < table_count; i++) { + Table::Ptr new_table = Table::New(store_, tt); + + new_table->Set(store_, 0, prev_table->self()); + + prev_table.reset(); + prev_table = std::move(new_table); + } + + store_.Collect(); + EXPECT_EQ(table_count + 1, store_.object_count()); + + // Remove the last root, now all should be removed. + prev_table.reset(); + + store_.Collect(); + EXPECT_EQ(1u, store_.object_count()); +} + +// TODO: Test for Thread keeping references alive as locals/params/stack values. +// This requires better tracking of references than currently exists in the +// interpreter. (see TODOs in Select/LocalGet/GlobalGet) diff --git a/third_party/wasm2c/src/test-intrusive-list.cc b/third_party/wasm2c/src/test-intrusive-list.cc new file mode 100644 index 0000000000..25059caba8 --- /dev/null +++ b/third_party/wasm2c/src/test-intrusive-list.cc @@ -0,0 +1,583 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" + +#include <memory> + +#include "wabt/intrusive-list.h" + +using namespace wabt; + +namespace { + +struct TestObject : intrusive_list_base<TestObject> { + static int creation_count; + + TestObject(int data = 0, int data2 = 0) : data(data), data2(data2) { + ++creation_count; + } + + // Allow move. + TestObject(TestObject&& other) { + // Don't increment creation_count; we're moving from other. + *this = std::move(other); + } + + TestObject& operator=(TestObject&& other) { + data = other.data; + data2 = other.data2; + other.moved = true; + return *this; + } + + // Disallow copy. + TestObject(const TestObject&) = delete; + TestObject& operator=(const TestObject&) = delete; + + ~TestObject() { + if (!moved) { + creation_count--; + } + } + + int data; + int data2; + bool moved = false; +}; + +// static +int TestObject::creation_count = 0; + +using TestObjectList = intrusive_list<TestObject>; + +class IntrusiveListTest : public ::testing::Test { + protected: + virtual void SetUp() { + // Reset to 0 even if the previous test leaked objects to keep the tests + // independent. + TestObject::creation_count = 0; + } + + virtual void TearDown() { ASSERT_EQ(0, TestObject::creation_count); } + + TestObjectList NewList(const std::vector<int>& data_values) { + TestObjectList result; + for (auto data_value : data_values) + result.emplace_back(data_value); + return result; + } + + void AssertListEq(const TestObjectList& list, + const std::vector<int>& expected) { + size_t count = 0; + for (const TestObject& node : list) { + ASSERT_EQ(expected[count++], node.data); + } + ASSERT_EQ(count, expected.size()); + } + + void AssertListEq(const TestObjectList& list, + const std::vector<int>& expected, + const std::vector<int>& expected2) { + assert(expected.size() == expected2.size()); + size_t count = 0; + for (const TestObject& node : list) { + ASSERT_EQ(expected[count], node.data); + ASSERT_EQ(expected2[count], node.data2); + count++; + } + ASSERT_EQ(count, expected.size()); + } +}; + +} // end anonymous namespace + +TEST_F(IntrusiveListTest, default_constructor) { + TestObjectList list; +} + +TEST_F(IntrusiveListTest, node_constructor) { + TestObjectList list(std::make_unique<TestObject>(1)); + AssertListEq(list, {1}); +} + +TEST_F(IntrusiveListTest, node_move_constructor) { + TestObjectList list(TestObject(1)); + AssertListEq(list, {1}); +} + +TEST_F(IntrusiveListTest, move_constructor) { + TestObjectList list1 = NewList({1, 2, 3}); + TestObjectList list2(std::move(list1)); + AssertListEq(list1, {}); + AssertListEq(list2, {1, 2, 3}); +} + +TEST_F(IntrusiveListTest, move_assignment_operator) { + TestObjectList list1 = NewList({1, 2, 3}); + TestObjectList list2; + + list2 = std::move(list1); + AssertListEq(list1, {}); + AssertListEq(list2, {1, 2, 3}); +} + +namespace { + +class IntrusiveListIteratorTest : public IntrusiveListTest { + protected: + virtual void SetUp() { + IntrusiveListTest::SetUp(); + + list_.emplace_back(1); + list_.emplace_back(2); + list_.emplace_back(3); + } + + virtual void TearDown() { + list_.clear(); + + IntrusiveListTest::TearDown(); + } + + template <typename Iter> + void TestForward(Iter first, Iter last, const std::vector<int>& expected) { + size_t count = 0; + while (first != last) { + ASSERT_EQ(expected[count], first->data); + ++first; + ++count; + } + ASSERT_EQ(count, expected.size()); + } + + template <typename Iter> + void TestBackward(Iter first, Iter last, const std::vector<int>& expected) { + size_t count = 0; + while (first != last) { + --last; + ASSERT_EQ(expected[count], last->data); + ++count; + } + ASSERT_EQ(count, expected.size()); + } + + TestObjectList list_; + const TestObjectList& clist_ = list_; +}; + +} // end of anonymous namespace + +TEST_F(IntrusiveListIteratorTest, begin_end_forward) { + TestForward(list_.begin(), list_.end(), {1, 2, 3}); + TestForward(clist_.begin(), clist_.end(), {1, 2, 3}); +} + +TEST_F(IntrusiveListIteratorTest, rbegin_rend_forward) { + TestForward(list_.rbegin(), list_.rend(), {3, 2, 1}); + TestForward(clist_.rbegin(), clist_.rend(), {3, 2, 1}); +} + +TEST_F(IntrusiveListIteratorTest, cbegin_cend_forward) { + TestForward(list_.cbegin(), list_.cend(), {1, 2, 3}); + TestForward(clist_.cbegin(), clist_.cend(), {1, 2, 3}); +} + +TEST_F(IntrusiveListIteratorTest, crbegin_crend_forward) { + TestForward(list_.crbegin(), list_.crend(), {3, 2, 1}); + TestForward(clist_.crbegin(), clist_.crend(), {3, 2, 1}); +} + +TEST_F(IntrusiveListIteratorTest, begin_end_backward) { + TestBackward(list_.begin(), list_.end(), {3, 2, 1}); + TestBackward(clist_.begin(), clist_.end(), {3, 2, 1}); +} + +TEST_F(IntrusiveListIteratorTest, rbegin_rend_backward) { + TestBackward(list_.rbegin(), list_.rend(), {1, 2, 3}); + TestBackward(clist_.rbegin(), clist_.rend(), {1, 2, 3}); +} + +TEST_F(IntrusiveListIteratorTest, cbegin_cend_backward) { + TestBackward(list_.cbegin(), list_.cend(), {3, 2, 1}); + TestBackward(clist_.cbegin(), clist_.cend(), {3, 2, 1}); +} + +TEST_F(IntrusiveListIteratorTest, crbegin_crend_backward) { + TestBackward(list_.crbegin(), list_.crend(), {1, 2, 3}); + TestBackward(clist_.crbegin(), clist_.crend(), {1, 2, 3}); +} + +TEST_F(IntrusiveListTest, size_empty) { + TestObjectList list; + ASSERT_EQ(0U, list.size()); + ASSERT_TRUE(list.empty()); + + list.emplace_back(1); + ASSERT_EQ(1U, list.size()); + ASSERT_FALSE(list.empty()); +} + +TEST_F(IntrusiveListTest, front_back) { + TestObjectList list; + + list.emplace_back(1); + ASSERT_EQ(1, list.front().data); + ASSERT_EQ(1, list.back().data); + + list.emplace_back(2); + ASSERT_EQ(1, list.front().data); + ASSERT_EQ(2, list.back().data); + + const TestObjectList& clist = list; + ASSERT_EQ(1, clist.front().data); + ASSERT_EQ(2, clist.back().data); +} + +TEST_F(IntrusiveListTest, emplace_front) { + TestObjectList list; + + // Pass an additional arg to show that forwarding works properly. + list.emplace_front(1, 100); + list.emplace_front(2, 200); + list.emplace_front(3, 300); + list.emplace_front(4, 400); + + AssertListEq(list, {4, 3, 2, 1}, {400, 300, 200, 100}); +} + +TEST_F(IntrusiveListTest, emplace_back) { + TestObjectList list; + + // Pass an additional arg to show that forwarding works properly. + list.emplace_back(1, 100); + list.emplace_back(2, 200); + list.emplace_back(3, 300); + list.emplace_back(4, 400); + + AssertListEq(list, {1, 2, 3, 4}, {100, 200, 300, 400}); +} + +TEST_F(IntrusiveListTest, push_front_pointer) { + TestObjectList list; + + list.push_front(std::make_unique<TestObject>(1)); + list.push_front(std::make_unique<TestObject>(2)); + list.push_front(std::make_unique<TestObject>(3)); + + AssertListEq(list, {3, 2, 1}); +} + +TEST_F(IntrusiveListTest, push_back_pointer) { + TestObjectList list; + + list.push_back(std::make_unique<TestObject>(1)); + list.push_back(std::make_unique<TestObject>(2)); + list.push_back(std::make_unique<TestObject>(3)); + + AssertListEq(list, {1, 2, 3}); +} + +TEST_F(IntrusiveListTest, push_front_move) { + TestObjectList list; + + list.push_front(TestObject(1)); + list.push_front(TestObject(2)); + list.push_front(TestObject(3)); + + AssertListEq(list, {3, 2, 1}); +} + +TEST_F(IntrusiveListTest, push_back_move) { + TestObjectList list; + + list.push_back(TestObject(1)); + list.push_back(TestObject(2)); + list.push_back(TestObject(3)); + + AssertListEq(list, {1, 2, 3}); +} + +TEST_F(IntrusiveListTest, pop_front) { + TestObjectList list = NewList({1, 2, 3, 4}); + + list.pop_front(); + AssertListEq(list, {2, 3, 4}); + list.pop_front(); + AssertListEq(list, {3, 4}); + list.pop_front(); + AssertListEq(list, {4}); + list.pop_front(); + AssertListEq(list, {}); +} + +TEST_F(IntrusiveListTest, pop_back) { + TestObjectList list = NewList({1, 2, 3, 4}); + + list.pop_back(); + AssertListEq(list, {1, 2, 3}); + list.pop_back(); + AssertListEq(list, {1, 2}); + list.pop_back(); + AssertListEq(list, {1}); + list.pop_back(); + AssertListEq(list, {}); +} + +TEST_F(IntrusiveListTest, extract_front) { + TestObjectList list = NewList({1, 2, 3}); + + std::unique_ptr<TestObject> t1(list.extract_front()); + ASSERT_EQ(1, t1->data); + AssertListEq(list, {2, 3}); + + std::unique_ptr<TestObject> t2(list.extract_front()); + ASSERT_EQ(2, t2->data); + AssertListEq(list, {3}); + + std::unique_ptr<TestObject> t3(list.extract_front()); + ASSERT_EQ(3, t3->data); + AssertListEq(list, {}); +} + +TEST_F(IntrusiveListTest, extract_back) { + TestObjectList list = NewList({1, 2, 3}); + + std::unique_ptr<TestObject> t1(list.extract_back()); + ASSERT_EQ(3, t1->data); + AssertListEq(list, {1, 2}); + + std::unique_ptr<TestObject> t2(list.extract_back()); + ASSERT_EQ(2, t2->data); + AssertListEq(list, {1}); + + std::unique_ptr<TestObject> t3(list.extract_back()); + ASSERT_EQ(1, t3->data); + AssertListEq(list, {}); +} + +TEST_F(IntrusiveListTest, emplace) { + TestObjectList list; + + // Pass an additional arg to show that forwarding works properly. + list.emplace(list.begin(), 2, 200); + list.emplace(list.end(), 4, 400); + list.emplace(std::next(list.begin()), 3, 300); + list.emplace(list.begin(), 1, 100); + + AssertListEq(list, {1, 2, 3, 4}, {100, 200, 300, 400}); +} + +TEST_F(IntrusiveListTest, insert_pointer) { + TestObjectList list; + + list.insert(list.begin(), std::make_unique<TestObject>(2)); + list.insert(list.end(), std::make_unique<TestObject>(4)); + list.insert(std::next(list.begin()), std::make_unique<TestObject>(3)); + list.insert(list.begin(), std::make_unique<TestObject>(1)); + + AssertListEq(list, {1, 2, 3, 4}); +} + +TEST_F(IntrusiveListTest, insert_move) { + TestObjectList list; + + list.insert(list.begin(), TestObject(2)); + list.insert(list.end(), TestObject(4)); + list.insert(std::next(list.begin()), TestObject(3)); + list.insert(list.begin(), TestObject(1)); + + AssertListEq(list, {1, 2, 3, 4}); +} + +TEST_F(IntrusiveListTest, extract) { + TestObjectList list = NewList({1, 2, 3, 4}); + + TestObjectList::iterator t1_iter = std::next(list.begin(), 0); + TestObjectList::iterator t2_iter = std::next(list.begin(), 1); + TestObjectList::iterator t3_iter = std::next(list.begin(), 2); + TestObjectList::iterator t4_iter = std::next(list.begin(), 3); + + std::unique_ptr<TestObject> t2(list.extract(t2_iter)); + ASSERT_EQ(2, t2->data); + AssertListEq(list, {1, 3, 4}); + + std::unique_ptr<TestObject> t4(list.extract(t4_iter)); + ASSERT_EQ(4, t4->data); + AssertListEq(list, {1, 3}); + + std::unique_ptr<TestObject> t1(list.extract(t1_iter)); + ASSERT_EQ(1, t1->data); + AssertListEq(list, {3}); + + std::unique_ptr<TestObject> t3(list.extract(t3_iter)); + ASSERT_EQ(3, t3->data); + AssertListEq(list, {}); +} + +TEST_F(IntrusiveListTest, erase) { + TestObjectList list = NewList({1, 2, 3, 4}); + + TestObjectList::iterator t1_iter = std::next(list.begin(), 0); + TestObjectList::iterator t2_iter = std::next(list.begin(), 1); + TestObjectList::iterator t3_iter = std::next(list.begin(), 2); + TestObjectList::iterator t4_iter = std::next(list.begin(), 3); + + // erase returns an iterator to the following node. + ASSERT_EQ(3, list.erase(t2_iter)->data); + AssertListEq(list, {1, 3, 4}); + + ASSERT_EQ(list.end(), list.erase(t4_iter)); + AssertListEq(list, {1, 3}); + + ASSERT_EQ(3, list.erase(t1_iter)->data); + AssertListEq(list, {3}); + + ASSERT_EQ(list.end(), list.erase(t3_iter)); + AssertListEq(list, {}); +} + +TEST_F(IntrusiveListTest, erase_range) { + TestObjectList list = NewList({1, 2, 3, 4, 5, 6}); + + // OK to erase an empty range. + list.erase(list.begin(), list.begin()); + list.erase(list.end(), list.end()); + + // Erase the first element (1). + list.erase(list.begin(), std::next(list.begin())); + AssertListEq(list, {2, 3, 4, 5, 6}); + + // Erase the last element (6). + list.erase(std::prev(list.end()), list.end()); + AssertListEq(list, {2, 3, 4, 5}); + + // Erase [3, 4] => [2, 5] + list.erase(std::next(list.begin()), std::prev(list.end())); + AssertListEq(list, {2, 5}); + + // Erase the rest. + list.erase(list.begin(), list.end()); + AssertListEq(list, {}); +} + +TEST_F(IntrusiveListTest, swap) { + TestObjectList list1 = NewList({1, 2, 3, 4}); + + TestObjectList list2 = NewList({100, 200, 300}); + + AssertListEq(list1, {1, 2, 3, 4}); + AssertListEq(list2, {100, 200, 300}); + + list1.swap(list2); + + AssertListEq(list1, {100, 200, 300}); + AssertListEq(list2, {1, 2, 3, 4}); +} + +TEST_F(IntrusiveListTest, clear) { + TestObjectList list = NewList({1, 2, 3, 4}); + + ASSERT_FALSE(list.empty()); + + list.clear(); + + ASSERT_EQ(0U, list.size()); + ASSERT_TRUE(list.empty()); +} + +TEST_F(IntrusiveListTest, splice_list) { + TestObjectList list1 = NewList({1, 2, 3}); + + // Splice at beginning. + TestObjectList list2 = NewList({100, 200}); + list1.splice(list1.begin(), list2); + AssertListEq(list1, {100, 200, 1, 2, 3}); + AssertListEq(list2, {}); + + // Splice at end. + TestObjectList list3 = NewList({500, 600, 700}); + list1.splice(list1.end(), list3); + AssertListEq(list1, {100, 200, 1, 2, 3, 500, 600, 700}); + AssertListEq(list3, {}); + + // Splice in the middle. + TestObjectList list4 = NewList({400}); + list1.splice(std::next(list1.begin(), 4), list4); + AssertListEq(list1, {100, 200, 1, 2, 400, 3, 500, 600, 700}); + AssertListEq(list4, {}); +} + +TEST_F(IntrusiveListTest, splice_list_move) { + TestObjectList list1 = NewList({1, 2, 3}); + + // Splice at beginning. + list1.splice(list1.begin(), NewList({100, 200})); + AssertListEq(list1, {100, 200, 1, 2, 3}); + + // Splice at end. + list1.splice(list1.end(), NewList({500, 600, 700})); + AssertListEq(list1, {100, 200, 1, 2, 3, 500, 600, 700}); + + // Splice in the middle. + list1.splice(std::next(list1.begin(), 4), NewList({400})); + AssertListEq(list1, {100, 200, 1, 2, 400, 3, 500, 600, 700}); +} + +TEST_F(IntrusiveListTest, splice_node) { + TestObjectList list1 = NewList({1, 2, 3}); + + // Splice at beginning. + TestObjectList list2 = NewList({100, 200}); + list1.splice(list1.begin(), list2, list2.begin()); + AssertListEq(list1, {100, 1, 2, 3}); + AssertListEq(list2, {200}); + + // Splice at end. + TestObjectList list3 = NewList({500, 600, 700}); + list1.splice(list1.end(), list3, std::next(list3.begin(), 2)); + AssertListEq(list1, {100, 1, 2, 3, 700}); + AssertListEq(list3, {500, 600}); + + // Splice in the middle. + TestObjectList list4 = NewList({400}); + list1.splice(std::next(list1.begin(), 3), list4, list4.begin()); + AssertListEq(list1, {100, 1, 2, 400, 3, 700}); + AssertListEq(list4, {}); +} + +TEST_F(IntrusiveListTest, splice_range) { + TestObjectList list1 = NewList({1, 2, 3}); + + // Splice at beginning. + TestObjectList list2 = NewList({100, 200, 300}); + list1.splice(list1.begin(), list2, list2.begin(), std::prev(list2.end())); + AssertListEq(list1, {100, 200, 1, 2, 3}); + AssertListEq(list2, {300}); + + // Splice at end. + TestObjectList list3 = NewList({500, 600, 700}); + list1.splice(list1.end(), list3, std::next(list3.begin()), list3.end()); + AssertListEq(list1, {100, 200, 1, 2, 3, 600, 700}); + AssertListEq(list3, {500}); + + // Splice in the middle. + TestObjectList list4 = NewList({400}); + list1.splice(std::next(list1.begin(), 4), list4, list4.begin(), list4.end()); + AssertListEq(list1, {100, 200, 1, 2, 400, 3, 600, 700}); + AssertListEq(list4, {}); +} diff --git a/third_party/wasm2c/src/test-literal.cc b/third_party/wasm2c/src/test-literal.cc new file mode 100644 index 0000000000..d9990d16f7 --- /dev/null +++ b/third_party/wasm2c/src/test-literal.cc @@ -0,0 +1,838 @@ +/* + * Copyright 2018 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cassert> +#include <cstdio> +#include <thread> +#include <vector> + +#include "gtest/gtest.h" + +#include "wabt/literal.h" + +using namespace wabt; + +namespace { + +enum ParseIntTypeCombo { + UnsignedOnly, + SignedAndUnsigned, + Both, +}; + +template <typename T> +void AssertIntEquals( + T expected, + const char* s, + Result (*parse_int)(const char*, const char*, T*, ParseIntType), + ParseIntTypeCombo parse_type = Both) { + const char* const end = s + strlen(s); + T actual; + if (parse_type == UnsignedOnly || parse_type == Both) { + ASSERT_EQ(Result::Ok, + parse_int(s, end, &actual, ParseIntType::UnsignedOnly)) + << s; + ASSERT_EQ(expected, actual); + } else { + ASSERT_EQ(Result::Error, + parse_int(s, end, &actual, ParseIntType::UnsignedOnly)) + << s; + } + + if (parse_type == SignedAndUnsigned || parse_type == Both) { + ASSERT_EQ(Result::Ok, + parse_int(s, end, &actual, ParseIntType::SignedAndUnsigned)) + << s; + ASSERT_EQ(expected, actual); + } else { + ASSERT_EQ(Result::Error, + parse_int(s, end, &actual, ParseIntType::SignedAndUnsigned)) + << s; + } +} + +void AssertInt8Equals(uint8_t expected, + const char* s, + ParseIntTypeCombo parse_type = Both) { + AssertIntEquals(expected, s, ParseInt8, parse_type); +} + +void AssertInt16Equals(uint16_t expected, + const char* s, + ParseIntTypeCombo parse_type = Both) { + AssertIntEquals(expected, s, ParseInt16, parse_type); +} + +void AssertInt32Equals(uint32_t expected, + const char* s, + ParseIntTypeCombo parse_type = Both) { + AssertIntEquals(expected, s, ParseInt32, parse_type); +} + +void AssertInt64Equals(uint64_t expected, + const char* s, + ParseIntTypeCombo parse_type = Both) { + AssertIntEquals(expected, s, ParseInt64, parse_type); +} + +void AssertUint128Equals(v128 expected, const char* s) { + const char* const end = s + strlen(s); + v128 actual; + ASSERT_EQ(Result::Ok, ParseUint128(s, end, &actual)) << s; + ASSERT_EQ(expected, actual); +} + +void AssertInt8Fails(const char* s) { + const char* const end = s + strlen(s); + uint8_t actual; + ASSERT_EQ(Result::Error, + ParseInt8(s, end, &actual, ParseIntType::SignedAndUnsigned)) + << s; + ASSERT_EQ(Result::Error, + ParseInt8(s, end, &actual, ParseIntType::UnsignedOnly)) + << s; +} + +void AssertInt16Fails(const char* s) { + const char* const end = s + strlen(s); + uint16_t actual; + ASSERT_EQ(Result::Error, + ParseInt16(s, end, &actual, ParseIntType::SignedAndUnsigned)) + << s; + ASSERT_EQ(Result::Error, + ParseInt16(s, end, &actual, ParseIntType::UnsignedOnly)) + << s; +} + +void AssertInt32Fails(const char* s) { + const char* const end = s + strlen(s); + uint32_t actual; + ASSERT_EQ(Result::Error, + ParseInt32(s, end, &actual, ParseIntType::SignedAndUnsigned)) + << s; + ASSERT_EQ(Result::Error, + ParseInt32(s, end, &actual, ParseIntType::UnsignedOnly)) + << s; +} + +void AssertInt64Fails(const char* s) { + const char* const end = s + strlen(s); + uint64_t actual; + ASSERT_EQ(Result::Error, + ParseInt64(s, end, &actual, ParseIntType::SignedAndUnsigned)) + << s; + ASSERT_EQ(Result::Error, + ParseInt64(s, end, &actual, ParseIntType::UnsignedOnly)) + << s; +} + +void AssertUint64Equals(uint64_t expected, const char* s) { + uint64_t actual; + ASSERT_EQ(Result::Ok, ParseUint64(s, s + strlen(s), &actual)) << s; + ASSERT_EQ(expected, actual); +} + +void AssertUint64Fails(const char* s) { + uint64_t actual_bits; + ASSERT_EQ(Result::Error, ParseUint64(s, s + strlen(s), &actual_bits)) << s; +} + +void AssertUint128Fails(const char* s) { + v128 actual; + ASSERT_EQ(Result::Error, ParseUint128(s, s + strlen(s), &actual)) << s; +} + +void AssertHexFloatEquals(uint32_t expected_bits, const char* s) { + uint32_t actual_bits; + ASSERT_EQ(Result::Ok, + ParseFloat(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits)) + << s; + ASSERT_EQ(expected_bits, actual_bits) << s; +} + +void AssertHexFloatFails(const char* s) { + uint32_t actual_bits; + ASSERT_EQ(Result::Error, + ParseFloat(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits)) + << s; +} + +void AssertHexDoubleEquals(uint64_t expected_bits, const char* s) { + uint64_t actual_bits; + ASSERT_EQ(Result::Ok, + ParseDouble(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits)) + << s; + ASSERT_EQ(expected_bits, actual_bits); +} + +void AssertHexDoubleFails(const char* s) { + uint64_t actual_bits; + ASSERT_EQ(Result::Error, + ParseDouble(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits)) + << s; +} + +} // end anonymous namespace + +TEST(ParseInt8, Both) { + AssertInt8Equals(0, "0"); + AssertInt8Equals(100, "100"); + AssertInt8Equals(123, "123"); + AssertInt8Equals(127, "127"); + AssertInt8Equals(255, "255"); + AssertInt8Equals(0xca, "0xca"); + AssertInt8Equals(0x7f, "0x7f"); + AssertInt8Equals(0x80, "0x80"); + AssertInt8Equals(0xff, "0xff"); +} + +TEST(ParseInt8, SignedAndUnsigned) { + AssertInt8Equals(128, "-128", SignedAndUnsigned); + AssertInt8Equals(-0x80, "-0x80", SignedAndUnsigned); + AssertInt8Equals(255, "-1", SignedAndUnsigned); + AssertInt8Equals(-1, "-0x1", SignedAndUnsigned); + AssertInt8Equals(1, "+1", SignedAndUnsigned); + AssertInt8Equals(-0x7b, "-0x7B", SignedAndUnsigned); + AssertInt8Equals(0xab, "+0xab", SignedAndUnsigned); +} + +TEST(ParseInt8, Invalid) { + AssertInt8Fails(""); + AssertInt8Fails("-100hello"); + AssertInt8Fails("0XAB"); + AssertInt8Fails("0xga"); + AssertInt8Fails("two"); +} + +TEST(ParseInt8, Underscores) { + AssertInt8Equals(123, "12_3", Both); + AssertInt8Equals(123, "+12_3", SignedAndUnsigned); + AssertInt8Equals(-123, "-1_23", SignedAndUnsigned); + AssertInt8Equals(19, "1______9", Both); + AssertInt8Equals(0xab, "0xa_b", Both); + AssertInt8Equals(0xab, "+0xa_b", SignedAndUnsigned); + AssertInt8Equals(-0x7b, "-0x7_b", SignedAndUnsigned); +} + +TEST(ParseInt8, Overflow) { + AssertInt8Fails("256"); + AssertInt8Fails("-129"); + AssertInt8Fails("0x100"); + AssertInt8Fails("-0x81"); + AssertInt8Fails("1231231231231231231231"); +} + +TEST(ParseInt16, Both) { + AssertInt16Equals(0, "0"); + AssertInt16Equals(1000, "1000"); + AssertInt16Equals(12345, "12345"); + AssertInt16Equals(32767, "32767"); + AssertInt16Equals(65535, "65535"); + AssertInt16Equals(0xcafe, "0xcafe"); + AssertInt16Equals(0x7fff, "0x7fff"); + AssertInt16Equals(0x8000, "0x8000"); + AssertInt16Equals(0xffff, "0xffff"); +} + +TEST(ParseInt16, SignedAndUnsigned) { + AssertInt16Equals(32768, "-32768", SignedAndUnsigned); + AssertInt16Equals(-0x8000, "-0x8000", SignedAndUnsigned); + AssertInt16Equals(65535, "-1", SignedAndUnsigned); + AssertInt16Equals(-1, "-0x1", SignedAndUnsigned); + AssertInt16Equals(1, "+1", SignedAndUnsigned); + AssertInt16Equals(-0x7bcd, "-0x7BCD", SignedAndUnsigned); + AssertInt16Equals(0xabcd, "+0xabcd", SignedAndUnsigned); +} + +TEST(ParseInt16, Invalid) { + AssertInt16Fails(""); + AssertInt16Fails("-100hello"); + AssertInt16Fails("0XABCD"); + AssertInt16Fails("0xgabb"); + AssertInt16Fails("two"); +} + +TEST(ParseInt16, Underscores) { + AssertInt16Equals(12345, "12_345", Both); + AssertInt16Equals(12345, "+12_345", SignedAndUnsigned); + AssertInt16Equals(-12345, "-123_45", SignedAndUnsigned); + AssertInt16Equals(19, "1______9", Both); + AssertInt16Equals(0xabcd, "0xa_b_c_d", Both); + AssertInt16Equals(0xabcd, "+0xa_b_c_d", SignedAndUnsigned); + AssertInt16Equals(-0x7bcd, "-0x7_b_c_d", SignedAndUnsigned); +} + +TEST(ParseInt16, Overflow) { + AssertInt16Fails("65536"); + AssertInt16Fails("-32769"); + AssertInt16Fails("0x10000"); + AssertInt16Fails("-0x8001"); + AssertInt16Fails("1231231231231231231231"); +} + +TEST(ParseInt32, Both) { + AssertInt32Equals(0, "0"); + AssertInt32Equals(1000, "1000"); + AssertInt32Equals(123456789, "123456789"); + AssertInt32Equals(2147483647, "2147483647"); + AssertInt32Equals(4294967295u, "4294967295"); + AssertInt32Equals(0xcafef00du, "0xcafef00d"); + AssertInt32Equals(0x7fffffff, "0x7fffffff"); + AssertInt32Equals(0x80000000u, "0x80000000"); + AssertInt32Equals(0xffffffffu, "0xffffffff"); +} + +TEST(ParseInt32, SignedAndUnsigned) { + AssertInt32Equals(2147483648, "-2147483648", SignedAndUnsigned); + AssertInt32Equals(-0x80000000ll, "-0x80000000", SignedAndUnsigned); + AssertInt32Equals(4294967295u, "-1", SignedAndUnsigned); + AssertInt32Equals(-1, "-0x1", SignedAndUnsigned); + AssertInt32Equals(1, "+1", SignedAndUnsigned); + AssertInt32Equals(-0xabcd, "-0xABCD", SignedAndUnsigned); + AssertInt32Equals(0xabcd, "+0xabcd", SignedAndUnsigned); +} + +TEST(ParseInt32, Invalid) { + AssertInt32Fails(""); + AssertInt32Fails("-100hello"); + AssertInt32Fails("0XABCDEF"); + AssertInt32Fails("0xgabba"); + AssertInt32Fails("two"); +} + +TEST(ParseInt32, Underscores) { + AssertInt32Equals(123456789, "12_345_6789", Both); + AssertInt32Equals(123456789, "+12_345_6789", SignedAndUnsigned); + AssertInt32Equals(-123456789, "-12345_6789", SignedAndUnsigned); + AssertInt32Equals(19, "1______9", Both); + AssertInt32Equals(0xabcd, "0xa_b_c_d", Both); + AssertInt32Equals(0xabcd, "+0xa_b_c_d", SignedAndUnsigned); + AssertInt32Equals(-0xabcd, "-0xa_b_c_d", SignedAndUnsigned); +} + +TEST(ParseInt32, Overflow) { + AssertInt32Fails("4294967296"); + AssertInt32Fails("-2147483649"); + AssertInt32Fails("0x100000000"); + AssertInt32Fails("-0x80000001"); + AssertInt32Fails("1231231231231231231231"); +} + +TEST(ParseInt64, Both) { + AssertInt64Equals(0, "0"); + AssertInt64Equals(1000, "1000"); + AssertInt64Equals(123456789, "123456789"); + AssertInt64Equals(9223372036854775807ull, "9223372036854775807"); + AssertInt64Equals(18446744073709551615ull, "18446744073709551615"); + AssertInt64Equals(0x7fffffffffffffffull, "0x7fffffffffffffff"); + AssertInt64Equals(0x8000000000000000ull, "0x8000000000000000"); + AssertInt64Equals(0xffffffffffffffffull, "0xffffffffffffffff"); +} + +TEST(ParseInt64, SignedAndUnsigned) { + AssertInt64Equals(9223372036854775808ull, "-9223372036854775808", + SignedAndUnsigned); + AssertInt64Equals(18446744073709551615ull, "-1", SignedAndUnsigned); + AssertInt64Equals(-1, "-0x1", SignedAndUnsigned); + AssertInt64Equals(1, "+1", SignedAndUnsigned); + AssertInt64Equals(-0x0bcdefabcdefabcdll, "-0x0BCDEFABCDEFABCD", + SignedAndUnsigned); + AssertInt64Equals(0xabcdefabcdefabcdll, "+0xabcdefabcdefabcd", + SignedAndUnsigned); +} + +TEST(ParseInt64, Invalid) { + AssertInt64Fails(""); + AssertInt64Fails("-100hello"); + AssertInt64Fails("0XABCDEF"); + AssertInt64Fails("0xgabba"); + AssertInt64Fails("two"); +} + +TEST(ParseInt64, Underscores) { + AssertInt64Equals(123456789, "12_345_6789", Both); + AssertInt64Equals(123456789, "+12_345_6789", SignedAndUnsigned); + AssertInt64Equals(-123456789, "-12345_6789", SignedAndUnsigned); + AssertInt64Equals(19, "1______9", Both); + AssertInt64Equals(0xabcd, "0xa_b_c_d", Both); + AssertInt64Equals(0xabcd, "+0xa_b_c_d", SignedAndUnsigned); + AssertInt64Equals(-0xabcd, "-0xa_b_c_d", SignedAndUnsigned); +} + +TEST(ParseInt64, Overflow) { + AssertInt64Fails("18446744073709551616"); + AssertInt64Fails("-9223372036854775809"); + AssertInt32Fails("0x10000000000000000"); + AssertInt32Fails("-0x80000000000000001"); + AssertInt64Fails("1231231231231231231231"); +} + +TEST(ParseUint64, Basic) { + AssertUint64Equals(0, "0"); + AssertUint64Equals(1000, "1000"); + AssertUint64Equals(123456789, "123456789"); + AssertUint64Equals(1844674407370955161ull, "1844674407370955161"); + AssertUint64Equals(18446744073709551615ull, "18446744073709551615"); + + AssertUint64Equals(0, "0x0"); + AssertUint64Equals(0x1000, "0x1000"); + AssertUint64Equals(0x123456789, "0x123456789"); + AssertUint64Equals(0xabcdef, "0xabcdef"); + AssertUint64Equals(0xffffffffffffffull, "0xffffffffffffff"); + AssertUint64Equals(0xfffffffffffffffull, "0xfffffffffffffff"); + + AssertUint64Equals(0xabcdefabcdefabcdull, "0xabcdefabcdefabcd"); +} + +TEST(ParseUint64, NoOctal) { + AssertUint64Equals(100, "0100"); + AssertUint64Equals(888, "0000888"); +} + +TEST(ParseUint64, Invalid) { + AssertUint64Fails(""); + AssertUint64Fails("-100"); + AssertUint64Fails("0XABCDEF"); + AssertUint64Fails("0xgabba"); + AssertUint64Fails("two"); +} + +TEST(ParseUint64, Underscores) { + AssertUint64Equals(123456789, "12_345_6789"); + AssertUint64Equals(19, "1______9"); + AssertUint64Equals(0xabcd, "0xa_b_c_d"); +} + +TEST(ParseUint64, Overflow) { + AssertUint64Fails("0x10000000000000000"); + AssertUint64Fails("18446744073709551616"); + AssertUint64Fails("62857453058642199420"); + AssertUint64Fails("82000999361882825820"); + AssertUint64Fails("126539114687237086210"); + AssertUint64Fails("10000000000000000000000000000000000000000"); +} + +TEST(ParseUint128, Basic) { + AssertUint128Equals({0, 0, 0, 0}, "0"); + AssertUint128Equals({1, 0, 0, 0}, "1"); + AssertUint128Equals({0x100f0e0d, 0x0c0b0a09, 0x08070605, 0x04030201}, + "5332529520247008778714484145835150861"); + AssertUint128Equals({0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, + "340282366920938463463374607431768211455"); + AssertUint128Equals({0, 0, 1, 0}, "18446744073709551616"); +} + +TEST(ParseUint128, Invalid) { + AssertUint128Fails(""); + AssertUint128Fails("-1"); + AssertUint128Fails("340282366920938463463374607431768211456"); + AssertUint128Fails("123a456"); +} + +TEST(ParseFloat, NonCanonical) { + AssertHexFloatEquals(0x3f800000, "0x00000000000000000000001.0p0"); + AssertHexFloatEquals(0x3f800000, "0x1.00000000000000000000000p0"); + AssertHexFloatEquals(0x3f800000, "0x0.0000000000000000000001p88"); +} + +TEST(ParseFloat, Basic) { + AssertHexFloatEquals(0, "0x0p0"); + AssertHexFloatEquals(0x3f800000, "0x1"); +} + +TEST(ParseFloat, Rounding) { + // |------- 23 bits -----| V-- extra bit + // + // 11111111111111111111101 0 ==> no rounding + AssertHexFloatEquals(0x7f7ffffd, "0x1.fffffap127"); + // 11111111111111111111101 1 ==> round up + AssertHexFloatEquals(0x7f7ffffe, "0x1.fffffbp127"); + // 11111111111111111111110 0 ==> no rounding + AssertHexFloatEquals(0x7f7ffffe, "0x1.fffffcp127"); + // 11111111111111111111110 1 ==> round down + AssertHexFloatEquals(0x7f7ffffe, "0x1.fffffdp127"); + // 11111111111111111111111 0 ==> no rounding + AssertHexFloatEquals(0x7f7fffff, "0x1.fffffep127"); +} + +// Duplicate the spec tests here for easier debugging. +TEST(ParseFloat, RoundingSpec) { + static const struct { + const char* input; + uint32_t output; + } kTests[] = { + {"+0x1.00000100000000000p-50", 0x26800000}, + {"-0x1.00000100000000000p-50", 0xa6800000}, + {"+0x1.00000100000000001p-50", 0x26800001}, + {"-0x1.00000100000000001p-50", 0xa6800001}, + {"+0x1.000001fffffffffffp-50", 0x26800001}, + {"-0x1.000001fffffffffffp-50", 0xa6800001}, + {"+0x1.00000200000000000p-50", 0x26800001}, + {"-0x1.00000200000000000p-50", 0xa6800001}, + {"+0x1.00000200000000001p-50", 0x26800001}, + {"-0x1.00000200000000001p-50", 0xa6800001}, + {"+0x1.000002fffffffffffp-50", 0x26800001}, + {"-0x1.000002fffffffffffp-50", 0xa6800001}, + {"+0x1.00000300000000000p-50", 0x26800002}, + {"-0x1.00000300000000000p-50", 0xa6800002}, + {"+0x1.00000300000000001p-50", 0x26800002}, + {"-0x1.00000300000000001p-50", 0xa6800002}, + {"+0x1.000003fffffffffffp-50", 0x26800002}, + {"-0x1.000003fffffffffffp-50", 0xa6800002}, + {"+0x1.00000400000000000p-50", 0x26800002}, + {"-0x1.00000400000000000p-50", 0xa6800002}, + {"+0x1.00000400000000001p-50", 0x26800002}, + {"-0x1.00000400000000001p-50", 0xa6800002}, + {"+0x1.000004fffffffffffp-50", 0x26800002}, + {"-0x1.000004fffffffffffp-50", 0xa6800002}, + {"+0x1.00000500000000000p-50", 0x26800002}, + {"-0x1.00000500000000000p-50", 0xa6800002}, + {"+0x1.00000500000000001p-50", 0x26800003}, + {"-0x1.00000500000000001p-50", 0xa6800003}, + {"+0x4000.004000000p-64", 0x26800000}, + {"-0x4000.004000000p-64", 0xa6800000}, + {"+0x4000.004000001p-64", 0x26800001}, + {"-0x4000.004000001p-64", 0xa6800001}, + {"+0x4000.007ffffffp-64", 0x26800001}, + {"-0x4000.007ffffffp-64", 0xa6800001}, + {"+0x4000.008000000p-64", 0x26800001}, + {"-0x4000.008000000p-64", 0xa6800001}, + {"+0x4000.008000001p-64", 0x26800001}, + {"-0x4000.008000001p-64", 0xa6800001}, + {"+0x4000.00bffffffp-64", 0x26800001}, + {"-0x4000.00bffffffp-64", 0xa6800001}, + {"+0x4000.00c000000p-64", 0x26800002}, + {"-0x4000.00c000000p-64", 0xa6800002}, + {"+0x4000.00c000001p-64", 0x26800002}, + {"-0x4000.00c000001p-64", 0xa6800002}, + {"+0x4000.00fffffffp-64", 0x26800002}, + {"-0x4000.00fffffffp-64", 0xa6800002}, + {"+0x4000.010000001p-64", 0x26800002}, + {"-0x4000.010000001p-64", 0xa6800002}, + {"+0x4000.013ffffffp-64", 0x26800002}, + {"-0x4000.013ffffffp-64", 0xa6800002}, + {"+0x4000.014000001p-64", 0x26800003}, + {"-0x4000.014000001p-64", 0xa6800003}, + {"+0x1.00000100000000000p+50", 0x58800000}, + {"-0x1.00000100000000000p+50", 0xd8800000}, + {"+0x1.00000100000000001p+50", 0x58800001}, + {"-0x1.00000100000000001p+50", 0xd8800001}, + {"+0x1.000001fffffffffffp+50", 0x58800001}, + {"-0x1.000001fffffffffffp+50", 0xd8800001}, + {"+0x1.00000200000000000p+50", 0x58800001}, + {"-0x1.00000200000000000p+50", 0xd8800001}, + {"+0x1.00000200000000001p+50", 0x58800001}, + {"-0x1.00000200000000001p+50", 0xd8800001}, + {"+0x1.000002fffffffffffp+50", 0x58800001}, + {"-0x1.000002fffffffffffp+50", 0xd8800001}, + {"+0x1.00000300000000000p+50", 0x58800002}, + {"-0x1.00000300000000000p+50", 0xd8800002}, + {"+0x1.00000300000000001p+50", 0x58800002}, + {"-0x1.00000300000000001p+50", 0xd8800002}, + {"+0x1.000003fffffffffffp+50", 0x58800002}, + {"-0x1.000003fffffffffffp+50", 0xd8800002}, + {"+0x1.00000400000000000p+50", 0x58800002}, + {"-0x1.00000400000000000p+50", 0xd8800002}, + {"+0x1.00000400000000001p+50", 0x58800002}, + {"-0x1.00000400000000001p+50", 0xd8800002}, + {"+0x1.000004fffffffffffp+50", 0x58800002}, + {"-0x1.000004fffffffffffp+50", 0xd8800002}, + {"+0x1.00000500000000000p+50", 0x58800002}, + {"-0x1.00000500000000000p+50", 0xd8800002}, + {"+0x1.00000500000000001p+50", 0x58800003}, + {"-0x1.00000500000000001p+50", 0xd8800003}, + {"+0x4000004000000", 0x58800000}, + {"-0x4000004000000", 0xd8800000}, + {"+0x4000004000001", 0x58800001}, + {"-0x4000004000001", 0xd8800001}, + {"+0x4000007ffffff", 0x58800001}, + {"-0x4000007ffffff", 0xd8800001}, + {"+0x4000008000000", 0x58800001}, + {"-0x4000008000000", 0xd8800001}, + {"+0x4000008000001", 0x58800001}, + {"-0x4000008000001", 0xd8800001}, + {"+0x400000bffffff", 0x58800001}, + {"-0x400000bffffff", 0xd8800001}, + {"+0x400000c000000", 0x58800002}, + {"-0x400000c000000", 0xd8800002}, + {"+0x0.00000100000000000p-126", 0x0}, + {"-0x0.00000100000000000p-126", 0x80000000}, + {"+0x0.00000100000000001p-126", 0x1}, + {"-0x0.00000100000000001p-126", 0x80000001}, + {"+0x0.00000101000000000p-126", 0x1}, + {"+0x0.000001fffffffffffp-126", 0x1}, + {"-0x0.000001fffffffffffp-126", 0x80000001}, + {"+0x0.00000200000000000p-126", 0x1}, + {"-0x0.00000200000000000p-126", 0x80000001}, + {"+0x0.00000200000000001p-126", 0x1}, + {"-0x0.00000200000000001p-126", 0x80000001}, + {"+0x0.000002fffffffffffp-126", 0x1}, + {"-0x0.000002fffffffffffp-126", 0x80000001}, + {"+0x0.00000300000000000p-126", 0x2}, + {"-0x0.00000300000000000p-126", 0x80000002}, + {"+0x0.00000300000000001p-126", 0x2}, + {"-0x0.00000300000000001p-126", 0x80000002}, + {"+0x0.000003fffffffffffp-126", 0x2}, + {"-0x0.000003fffffffffffp-126", 0x80000002}, + {"+0x0.00000400000000000p-126", 0x2}, + {"-0x0.00000400000000000p-126", 0x80000002}, + {"+0x0.00000400000000001p-126", 0x2}, + {"-0x0.00000400000000001p-126", 0x80000002}, + {"+0x0.000004fffffffffffp-126", 0x2}, + {"-0x0.000004fffffffffffp-126", 0x80000002}, + {"+0x0.00000500000000000p-126", 0x2}, + {"-0x0.00000500000000000p-126", 0x80000002}, + {"+0x0.00000500000000001p-126", 0x3}, + {"-0x0.00000500000000001p-126", 0x80000003}, + {"+0x1.fffffe8p127", 0x7f7fffff}, + {"-0x1.fffffe8p127", 0xff7fffff}, + {"+0x1.fffffefffffff8p127", 0x7f7fffff}, + {"-0x1.fffffefffffff8p127", 0xff7fffff}, + {"+0x1.fffffefffffffffffp127", 0x7f7fffff}, + {"-0x1.fffffefffffffffffp127", 0xff7fffff}, + }; + + for (auto test : kTests) { + AssertHexFloatEquals(test.output, test.input); + } +} + +TEST(ParseFloat, OutOfRange) { + AssertHexFloatFails("0x1p128"); + AssertHexFloatFails("-0x1p128"); + AssertHexFloatFails("0x1.ffffffp127"); + AssertHexFloatFails("-0x1.ffffffp127"); +} + +TEST(ParseDouble, NonCanonical) { + AssertHexDoubleEquals(0x3ff0000000000000, "0x00000000000000000000001.0p0"); + AssertHexDoubleEquals(0x3ff0000000000000, "0x1.00000000000000000000000p0"); + AssertHexDoubleEquals(0x3ff0000000000000, "0x0.0000000000000000000001p88"); +} + +TEST(ParseDouble, Rounding) { + // |-------------------- 52 bits ---------------------| V-- extra bit + // + // 1111111111111111111111111111111111111111111111111101 0 ==> no rounding + AssertHexDoubleEquals(0x7feffffffffffffd, "0x1.ffffffffffffd0p1023"); + // 1111111111111111111111111111111111111111111111111101 1 ==> round up + AssertHexDoubleEquals(0x7feffffffffffffe, "0x1.ffffffffffffd8p1023"); + // 1111111111111111111111111111111111111111111111111110 0 ==> no rounding + AssertHexDoubleEquals(0x7feffffffffffffe, "0x1.ffffffffffffe0p1023"); + // 1111111111111111111111111111111111111111111111111110 1 ==> round down + AssertHexDoubleEquals(0x7feffffffffffffe, "0x1.ffffffffffffe8p1023"); + // 1111111111111111111111111111111111111111111111111111 0 ==> no rounding + AssertHexDoubleEquals(0x7fefffffffffffff, "0x1.fffffffffffff0p1023"); +} + +TEST(ParseDouble, OutOfRange) { + AssertHexDoubleFails("0x1p1024"); + AssertHexDoubleFails("-0x1p1024"); + AssertHexDoubleFails("0x1.fffffffffffff8p1023"); + AssertHexDoubleFails("-0x1.fffffffffffff8p1023"); +} + +// Duplicate the spec tests here for easier debugging. +TEST(ParseDouble, RoundingSpec) { + static const struct { + const char* input; + uint64_t output; + } kTests[] = { + {"+0x1.000000000000080000000000p-600", 1905022642377719808ull}, + {"-0x1.000000000000080000000000p-600", 11128394679232495616ull}, + {"+0x1.000000000000080000000001p-600", 1905022642377719809ull}, + {"-0x1.000000000000080000000001p-600", 11128394679232495617ull}, + {"+0x1.0000000000000fffffffffffp-600", 1905022642377719809ull}, + {"-0x1.0000000000000fffffffffffp-600", 11128394679232495617ull}, + {"+0x1.000000000000100000000000p-600", 1905022642377719809ull}, + {"-0x1.000000000000100000000000p-600", 11128394679232495617ull}, + {"+0x1.000000000000100000000001p-600", 1905022642377719809ull}, + {"-0x1.000000000000100000000001p-600", 11128394679232495617ull}, + {"+0x1.00000000000017ffffffffffp-600", 1905022642377719809ull}, + {"-0x1.00000000000017ffffffffffp-600", 11128394679232495617ull}, + {"+0x1.000000000000180000000000p-600", 1905022642377719810ull}, + {"-0x1.000000000000180000000000p-600", 11128394679232495618ull}, + {"+0x1.000000000000180000000001p-600", 1905022642377719810ull}, + {"-0x1.000000000000180000000001p-600", 11128394679232495618ull}, + {"+0x1.0000000000001fffffffffffp-600", 1905022642377719810ull}, + {"-0x1.0000000000001fffffffffffp-600", 11128394679232495618ull}, + {"+0x1.000000000000200000000000p-600", 1905022642377719810ull}, + {"-0x1.000000000000200000000000p-600", 11128394679232495618ull}, + {"+0x1.000000000000200000000001p-600", 1905022642377719810ull}, + {"-0x1.000000000000200000000001p-600", 11128394679232495618ull}, + {"+0x1.00000000000027ffffffffffp-600", 1905022642377719810ull}, + {"-0x1.00000000000027ffffffffffp-600", 11128394679232495618ull}, + {"+0x1.000000000000280000000001p-600", 1905022642377719811ull}, + {"-0x1.000000000000280000000001p-600", 11128394679232495619ull}, + {"+0x8000000.000000400000000000p-627", 1905022642377719808ull}, + {"-0x8000000.000000400000000000p-627", 11128394679232495616ull}, + {"+0x8000000.000000400000000001p-627", 1905022642377719809ull}, + {"-0x8000000.000000400000000001p-627", 11128394679232495617ull}, + {"+0x8000000.0000007fffffffffffp-627", 1905022642377719809ull}, + {"-0x8000000.0000007fffffffffffp-627", 11128394679232495617ull}, + {"+0x8000000.000000800000000000p-627", 1905022642377719809ull}, + {"-0x8000000.000000800000000000p-627", 11128394679232495617ull}, + {"+0x8000000.000000800000000001p-627", 1905022642377719809ull}, + {"-0x8000000.000000800000000001p-627", 11128394679232495617ull}, + {"+0x8000000.000000bfffffffffffp-627", 1905022642377719809ull}, + {"-0x8000000.000000bfffffffffffp-627", 11128394679232495617ull}, + {"+0x8000000.000000c00000000000p-627", 1905022642377719810ull}, + {"-0x8000000.000000c00000000000p-627", 11128394679232495618ull}, + {"+0x8000000.000000c00000000001p-627", 1905022642377719810ull}, + {"-0x8000000.000000c00000000001p-627", 11128394679232495618ull}, + {"+0x8000000.000000ffffffffffffp-627", 1905022642377719810ull}, + {"-0x8000000.000000ffffffffffffp-627", 11128394679232495618ull}, + {"+0x8000000.000001000000000000p-627", 1905022642377719810ull}, + {"-0x8000000.000001000000000000p-627", 11128394679232495618ull}, + {"+0x8000000.000001000000000001p-627", 1905022642377719810ull}, + {"-0x8000000.000001000000000001p-627", 11128394679232495618ull}, + {"+0x8000000.0000013fffffffffffp-627", 1905022642377719810ull}, + {"-0x8000000.0000013fffffffffffp-627", 11128394679232495618ull}, + {"+0x8000000.000001400000000001p-627", 1905022642377719811ull}, + {"-0x8000000.000001400000000001p-627", 11128394679232495619ull}, + {"+0x1.000000000000080000000000p+600", 7309342195222315008ull}, + {"-0x1.000000000000080000000000p+600", 16532714232077090816ull}, + {"+0x1.000000000000080000000001p+600", 7309342195222315009ull}, + {"-0x1.000000000000080000000001p+600", 16532714232077090817ull}, + {"+0x1.0000000000000fffffffffffp+600", 7309342195222315009ull}, + {"-0x1.0000000000000fffffffffffp+600", 16532714232077090817ull}, + {"+0x1.000000000000100000000000p+600", 7309342195222315009ull}, + {"-0x1.000000000000100000000000p+600", 16532714232077090817ull}, + {"+0x1.000000000000100000000001p+600", 7309342195222315009ull}, + {"-0x1.000000000000100000000001p+600", 16532714232077090817ull}, + {"+0x1.00000000000017ffffffffffp+600", 7309342195222315009ull}, + {"-0x1.00000000000017ffffffffffp+600", 16532714232077090817ull}, + {"+0x1.000000000000180000000000p+600", 7309342195222315010ull}, + {"-0x1.000000000000180000000000p+600", 16532714232077090818ull}, + {"+0x1.000000000000180000000001p+600", 7309342195222315010ull}, + {"-0x1.000000000000180000000001p+600", 16532714232077090818ull}, + {"+0x1.0000000000001fffffffffffp+600", 7309342195222315010ull}, + {"-0x1.0000000000001fffffffffffp+600", 16532714232077090818ull}, + {"+0x1.000000000000200000000000p+600", 7309342195222315010ull}, + {"-0x1.000000000000200000000000p+600", 16532714232077090818ull}, + {"+0x1.000000000000200000000001p+600", 7309342195222315010ull}, + {"-0x1.000000000000200000000001p+600", 16532714232077090818ull}, + {"+0x1.00000000000027ffffffffffp+600", 7309342195222315010ull}, + {"-0x1.00000000000027ffffffffffp+600", 16532714232077090818ull}, + {"+0x1.000000000000280000000000p+600", 7309342195222315010ull}, + {"-0x1.000000000000280000000000p+600", 16532714232077090818ull}, + {"+0x1.000000000000280000000001p+600", 7309342195222315011ull}, + {"-0x1.000000000000280000000001p+600", 16532714232077090819ull}, + {"+0x2000000000000100000000000", 5044031582654955520ull}, + {"-0x2000000000000100000000000", 14267403619509731328ull}, + {"+0x2000000000000100000000001", 5044031582654955521ull}, + {"-0x2000000000000100000000001", 14267403619509731329ull}, + {"+0x20000000000001fffffffffff", 5044031582654955521ull}, + {"-0x20000000000001fffffffffff", 14267403619509731329ull}, + {"+0x2000000000000200000000000", 5044031582654955521ull}, + {"-0x2000000000000200000000000", 14267403619509731329ull}, + {"+0x2000000000000200000000001", 5044031582654955521ull}, + {"-0x2000000000000200000000001", 14267403619509731329ull}, + {"+0x20000000000002fffffffffff", 5044031582654955521ull}, + {"-0x20000000000002fffffffffff", 14267403619509731329ull}, + {"+0x2000000000000300000000000", 5044031582654955522ull}, + {"-0x2000000000000300000000000", 14267403619509731330ull}, + {"+0x2000000000000300000000001", 5044031582654955522ull}, + {"-0x2000000000000300000000001", 14267403619509731330ull}, + {"+0x20000000000003fffffffffff", 5044031582654955522ull}, + {"-0x20000000000003fffffffffff", 14267403619509731330ull}, + {"+0x2000000000000400000000000", 5044031582654955522ull}, + {"-0x2000000000000400000000000", 14267403619509731330ull}, + {"+0x2000000000000400000000001", 5044031582654955522ull}, + {"-0x2000000000000400000000001", 14267403619509731330ull}, + {"+0x20000000000004fffffffffff", 5044031582654955522ull}, + {"-0x20000000000004fffffffffff", 14267403619509731330ull}, + {"+0x2000000000000500000000000", 5044031582654955522ull}, + {"-0x2000000000000500000000000", 14267403619509731330ull}, + {"+0x2000000000000500000000001", 5044031582654955523ull}, + {"-0x2000000000000500000000001", 14267403619509731331ull}, + {"+0x0.000000000000080000000000p-1022", 0ull}, + {"-0x0.000000000000080000000000p-1022", 9223372036854775808ull}, + {"+0x0.000000000000080000000001p-1022", 1ull}, + {"-0x0.000000000000080000000001p-1022", 9223372036854775809ull}, + {"+0x0.0000000000000fffffffffffp-1022", 1ull}, + {"-0x0.0000000000000fffffffffffp-1022", 9223372036854775809ull}, + {"+0x0.000000000000100000000000p-1022", 1ull}, + {"-0x0.000000000000100000000000p-1022", 9223372036854775809ull}, + {"+0x0.000000000000100000000001p-1022", 1ull}, + {"-0x0.000000000000100000000001p-1022", 9223372036854775809ull}, + {"+0x0.00000000000017ffffffffffp-1022", 1ull}, + {"-0x0.00000000000017ffffffffffp-1022", 9223372036854775809ull}, + {"+0x0.000000000000180000000000p-1022", 2ull}, + {"-0x0.000000000000180000000000p-1022", 9223372036854775810ull}, + {"+0x0.000000000000180000000001p-1022", 2ull}, + {"-0x0.000000000000180000000001p-1022", 9223372036854775810ull}, + {"+0x0.0000000000001fffffffffffp-1022", 2ull}, + {"-0x0.0000000000001fffffffffffp-1022", 9223372036854775810ull}, + {"+0x0.000000000000200000000000p-1022", 2ull}, + {"-0x0.000000000000200000000000p-1022", 9223372036854775810ull}, + {"+0x0.000000000000200000000001p-1022", 2ull}, + {"-0x0.000000000000200000000001p-1022", 9223372036854775810ull}, + {"+0x0.00000000000027ffffffffffp-1022", 2ull}, + {"-0x0.00000000000027ffffffffffp-1022", 9223372036854775810ull}, + {"+0x0.000000000000280000000000p-1022", 2ull}, + {"-0x0.000000000000280000000000p-1022", 9223372036854775810ull}, + {"+0x1.000000000000280000000001p-1022", 4503599627370499ull}, + {"-0x1.000000000000280000000001p-1022", 9227875636482146307ull}, + {"+0x1.fffffffffffff4p1023", 9218868437227405311ull}, + {"-0x1.fffffffffffff4p1023", 18442240474082181119ull}, + {"+0x1.fffffffffffff7ffffffp1023", 9218868437227405311ull}, + {"-0x1.fffffffffffff7ffffffp1023", 18442240474082181119ull}, + }; + + for (auto test : kTests) { + AssertHexDoubleEquals(test.output, test.input); + } +} + +void AssertWriteUint128Equals(const v128& value, const std::string& expected) { + assert(expected.length() < 128); + char buffer[128]; + WriteUint128(buffer, 128, value); + std::string actual(buffer, buffer + expected.length()); + ASSERT_EQ(expected, actual); + ASSERT_EQ(buffer[expected.length()], '\0'); +} + +TEST(WriteUint128, Basic) { + AssertWriteUint128Equals({0, 0, 0, 0}, "0"); + AssertWriteUint128Equals({1, 0, 0, 0}, "1"); + AssertWriteUint128Equals({0x100f0e0d, 0x0c0b0a09, 0x08070605, 0x04030201}, + "5332529520247008778714484145835150861"); + AssertWriteUint128Equals({0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff}, + "272314856204801878456120017448021860915"); + AssertWriteUint128Equals({0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, + "340282366920938463463374607431768211455"); + AssertWriteUint128Equals({0, 0, 1, 0}, "18446744073709551616"); +} + +TEST(WriteUint128, BufferTooSmall) { + { + char buffer[20]; + WriteUint128(buffer, 20, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}); + ASSERT_EQ(buffer[19], '\0'); + std::string actual(buffer, buffer + 19); + ASSERT_EQ("3402823669209384634", actual); + } + + { + char buffer[3]; + WriteUint128(buffer, 3, {123, 0, 0, 0}); + ASSERT_EQ(buffer[0], '1'); + ASSERT_EQ(buffer[1], '2'); + ASSERT_EQ(buffer[2], '\0'); + } +} diff --git a/third_party/wasm2c/src/test-option-parser.cc b/third_party/wasm2c/src/test-option-parser.cc new file mode 100644 index 0000000000..757e6578ff --- /dev/null +++ b/third_party/wasm2c/src/test-option-parser.cc @@ -0,0 +1,180 @@ +// Copyright 2019 WebAssembly Community Group participants +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gtest/gtest.h" + +#include <string> + +#include "wabt/option-parser.h" + +using namespace wabt; + +#define ERROR_ENDING "\nTry '--help' for more information." + +TEST(OptionParser, LongFlag) { + bool flag = false; + OptionParser parser("prog", "desc"); + parser.AddOption("flag", "help", [&]() { flag = true; }); + const char* args[] = {"prog name", "--flag"}; + parser.Parse(2, const_cast<char**>(args)); + EXPECT_EQ(true, flag); +} + +TEST(OptionParser, ShortAndLongFlag) { + int count = 0; + OptionParser parser("prog", "desc"); + parser.AddOption('f', "flag", "help", [&]() { ++count; }); + const char* args[] = {"prog name", "-f", "--flag", "-f", "--flag"}; + parser.Parse(5, const_cast<char**>(args)); + EXPECT_EQ(4, count); +} + +TEST(OptionParser, ShortFlagCombined) { + int count = 0; + OptionParser parser("prog", "desc"); + parser.AddOption('a', "a", "help", [&]() { count += 1; }); + parser.AddOption('b', "b", "help", [&]() { count += 2; }); + const char* args[] = {"prog name", "-aa", "-abb"}; + parser.Parse(3, const_cast<char**>(args)); + EXPECT_EQ(7, count); +} + +TEST(OptionParser, UnknownShortOption) { + std::string error; + OptionParser parser("prog", "desc"); + parser.SetErrorCallback([&](const char* msg) { error = msg; }); + const char* args[] = {"prog name", "-f"}; + parser.Parse(2, const_cast<char**>(args)); + EXPECT_EQ(error, "prog: unknown option '-f'" ERROR_ENDING); +} + +TEST(OptionParser, UnknownLongOption) { + std::string error; + OptionParser parser("prog", "desc"); + parser.SetErrorCallback([&](const char* msg) { error = msg; }); + const char* args[] = {"prog name", "--foo"}; + parser.Parse(2, const_cast<char**>(args)); + EXPECT_EQ(error, "prog: unknown option '--foo'" ERROR_ENDING); +} + +TEST(OptionParser, ShortAndLongParam) { + std::string param; + OptionParser parser("prog", "desc"); + parser.AddOption('p', "param", "metavar", "help", + [&](const char* arg) { param += arg; }); + const char* args[] = {"prog name", "-p", "h", "--param", "el", "--param=lo"}; + parser.Parse(6, const_cast<char**>(args)); + EXPECT_EQ("hello", param); +} + +TEST(OptionParser, MissingParam) { + std::string error; + std::string param; + OptionParser parser("prog", "desc"); + parser.SetErrorCallback([&](const char* msg) { error = msg; }); + parser.AddOption('p', "param", "metavar", "help", + [&](const char* arg) { param = arg; }); + const char* args[] = {"prog name", "--param"}; + parser.Parse(2, const_cast<char**>(args)); + EXPECT_EQ("", param); + EXPECT_EQ(error, "prog: option '--param' requires argument" ERROR_ENDING); +} + +TEST(OptionParser, MissingArgument) { + std::string error; + OptionParser parser("prog", "desc"); + parser.AddArgument("arg", OptionParser::ArgumentCount::One, + [&](const char* arg) {}); + parser.SetErrorCallback([&](const char* msg) { error = msg; }); + const char* args[] = {"prog name"}; + parser.Parse(1, const_cast<char**>(args)); + EXPECT_EQ(error, "prog: expected arg argument." ERROR_ENDING); +} + +TEST(OptionParser, FlagCombinedAfterShortParam) { + std::string error; + std::string param; + bool has_x = false; + + OptionParser parser("prog", "desc"); + parser.SetErrorCallback([&](const char* msg) { error = msg; }); + parser.AddOption('p', "p", "metavar", "help", + [&](const char* arg) { param = arg; }); + parser.AddOption('x', "x", "help", [&]() { has_x = true; }); + const char* args[] = {"prog name", "-px", "stuff"}; + parser.Parse(3, const_cast<char**>(args)); + EXPECT_EQ("", param); + EXPECT_TRUE(has_x); + EXPECT_EQ(error, "prog: unexpected argument 'stuff'" ERROR_ENDING); +} + +TEST(OptionParser, OneArgument) { + std::string argument; + OptionParser parser("prog", "desc"); + parser.AddArgument("arg", OptionParser::ArgumentCount::One, + [&](const char* arg) { argument = arg; }); + const char* args[] = {"prog name", "hello"}; + parser.Parse(2, const_cast<char**>(args)); + EXPECT_EQ("hello", argument); +} + +TEST(OptionParser, TooManyArguments) { + std::string error; + OptionParser parser("prog", "desc"); + parser.SetErrorCallback([&](const char* msg) { error = msg; }); + parser.AddArgument("arg", OptionParser::ArgumentCount::One, + [&](const char* arg) {}); + const char* args[] = {"prog name", "hello", "goodbye"}; + parser.Parse(3, const_cast<char**>(args)); + EXPECT_EQ(error, "prog: unexpected argument 'goodbye'" ERROR_ENDING); +} + +TEST(OptionParser, OneOrMoreArguments) { + std::string argument; + OptionParser parser("prog", "desc"); + parser.AddArgument("arg", OptionParser::ArgumentCount::OneOrMore, + [&](const char* arg) { argument += arg; }); + const char* args[] = {"prog name", "hello", "goodbye"}; + parser.Parse(3, const_cast<char**>(args)); + EXPECT_EQ("hellogoodbye", argument); +} + +TEST(OptionParser, ZeroOrMoreArguments) { + std::string argument; + OptionParser parser("prog", "desc"); + parser.AddArgument("arg", OptionParser::ArgumentCount::ZeroOrMore, + [&](const char* arg) { argument += arg; }); + + const char* args_none[] = {"prog name"}; + parser.Parse(1, const_cast<char**>(args_none)); + EXPECT_EQ("", argument); + + const char* args_many[] = {"prog name", "hello", "goodbye"}; + parser.Parse(3, const_cast<char**>(args_many)); + EXPECT_EQ("hellogoodbye", argument); +} + +TEST(OptionParser, StopProccessing) { + std::string argument; + bool has_x = false; + OptionParser parser("prog", "desc"); + parser.AddArgument("arg", OptionParser::ArgumentCount::ZeroOrMore, + [&](const char* arg) { argument += arg; }); + parser.AddOption('x', "x", "help", [&]() { has_x = true; }); + + const char* args_many[] = {"prog name", "-x", "--", "foo", "-x", "-y", "bar"}; + parser.Parse(7, const_cast<char**>(args_many)); + EXPECT_TRUE(has_x); + EXPECT_EQ("foo-x-ybar", argument); +} diff --git a/third_party/wasm2c/src/test-utf8.cc b/third_party/wasm2c/src/test-utf8.cc new file mode 100644 index 0000000000..d19077e951 --- /dev/null +++ b/third_party/wasm2c/src/test-utf8.cc @@ -0,0 +1,167 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" + +#include "wabt/utf8.h" + +using namespace wabt; + +namespace { + +void assert_is_valid_utf8(bool expected, + int length, + int cu0 = 0, + int cu1 = 0, + int cu2 = 0, + int cu3 = 0) { + assert(length <= 4); + char buf[4] = {static_cast<char>(cu0), static_cast<char>(cu1), + static_cast<char>(cu2), static_cast<char>(cu3)}; + if (expected) { + // Make sure it fails if there are continuation bytes past the end of the + // string. + for (int bad_length = 1; bad_length < length; ++bad_length) { + ASSERT_FALSE(IsValidUtf8(buf, bad_length)) + << cu0 << ", " << cu1 << ", " << cu2 << ", " << cu3; + } + } + + ASSERT_TRUE(expected == IsValidUtf8(buf, length)) + << cu0 << ", " << cu1 << ", " << cu2 << ", " << cu3; +} + +bool is_in_range(int x, int low, int high) { + return x >= low && x < high; +} + +} // end anonymous namespace + +#define FOR_RANGE(var, low, high) for (int var = low; var < high; var++) +#define FOR_EACH_BYTE(var) FOR_RANGE(var, 0, 0x100) + +TEST(utf8, valid_empty) { + assert_is_valid_utf8(true, 0); +} + +TEST(utf8, valid_1_byte) { + FOR_RANGE(cu0, 0, 0x80) { assert_is_valid_utf8(true, 1, cu0); } +} + +TEST(utf8, invalid_continuation_bytes) { + FOR_RANGE(cu0, 0x80, 0xc0) { assert_is_valid_utf8(false, 1, cu0); } +} + +TEST(utf8, invalid_2_byte) { + FOR_RANGE(cu0, 0xc0, 0xc2) { assert_is_valid_utf8(false, 1, cu0); } +} + +TEST(utf8, valid_2_bytes) { + FOR_RANGE(cu0, 0xc2, 0xe0) { + FOR_EACH_BYTE(cu1) { + bool is_valid = is_in_range(cu1, 0x80, 0xc0); + assert_is_valid_utf8(is_valid, 2, cu0, cu1); + } + } +} + +TEST(utf8, valid_3_bytes_e0) { + int cu0 = 0xe0; + FOR_EACH_BYTE(cu1) { + FOR_EACH_BYTE(cu2) { + bool is_valid = + is_in_range(cu1, 0xa0, 0xc0) && is_in_range(cu2, 0x80, 0xc0); + assert_is_valid_utf8(is_valid, 3, cu0, cu1, cu2); + } + } +} + +TEST(utf8, valid_3_bytes) { + FOR_RANGE(cu0, 0xe1, 0xf0) { + // Handle 0xed in valid_3_bytes_ed. + if (cu0 == 0xed) { + continue; + } + + FOR_EACH_BYTE(cu1) { + FOR_EACH_BYTE(cu2) { + bool is_valid = + is_in_range(cu1, 0x80, 0xc0) && is_in_range(cu2, 0x80, 0xc0); + assert_is_valid_utf8(is_valid, 3, cu0, cu1, cu2); + } + } + } +} + +TEST(utf8, valid_3_bytes_ed) { + int cu0 = 0xed; + FOR_EACH_BYTE(cu1) { + FOR_EACH_BYTE(cu2) { + bool is_valid = + is_in_range(cu1, 0x80, 0xa0) && is_in_range(cu2, 0x80, 0xc0); + assert_is_valid_utf8(is_valid, 3, cu0, cu1, cu2); + } + } +} + +TEST(utf8, valid_4_bytes_f0) { + int cu0 = 0xf0; + FOR_EACH_BYTE(cu1) { + FOR_EACH_BYTE(cu2) { + FOR_EACH_BYTE(cu3) { + bool is_valid = is_in_range(cu1, 0x90, 0xc0) && + is_in_range(cu2, 0x80, 0xc0) && + is_in_range(cu3, 0x80, 0xc0); + assert_is_valid_utf8(is_valid, 4, cu0, cu1, cu2, cu3); + } + } + } +} + +TEST(utf8, valid_4_bytes) { + FOR_RANGE(cu0, 0xf1, 0xf4) { + FOR_EACH_BYTE(cu1) { + FOR_EACH_BYTE(cu2) { + FOR_EACH_BYTE(cu3) { + bool is_valid = is_in_range(cu1, 0x80, 0xc0) && + is_in_range(cu2, 0x80, 0xc0) && + is_in_range(cu3, 0x80, 0xc0); + assert_is_valid_utf8(is_valid, 4, cu0, cu1, cu2, cu3); + } + } + } + } +} + +TEST(utf8, valid_4_bytes_f4) { + int cu0 = 0xf4; + FOR_EACH_BYTE(cu1) { + FOR_EACH_BYTE(cu2) { + FOR_EACH_BYTE(cu3) { + bool is_valid = is_in_range(cu1, 0x80, 0x90) && + is_in_range(cu2, 0x80, 0xc0) && + is_in_range(cu3, 0x80, 0xc0); + assert_is_valid_utf8(is_valid, 4, cu0, cu1, cu2, cu3); + } + } + } +} + +TEST(utf8, invalid_4_bytes) { + FOR_RANGE(cu0, 0xf5, 0x100) { + assert_is_valid_utf8(false, 4, cu0, 0x80, 0x80, 0x80); + } +} diff --git a/third_party/wasm2c/src/test-wast-parser.cc b/third_party/wasm2c/src/test-wast-parser.cc new file mode 100644 index 0000000000..60fc601dbd --- /dev/null +++ b/third_party/wasm2c/src/test-wast-parser.cc @@ -0,0 +1,88 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" + +#include <memory> + +#include "wabt/wast-lexer.h" +#include "wabt/wast-parser.h" + +using namespace wabt; + +namespace { + +std::string repeat(std::string s, size_t count) { + std::string result; + for (size_t i = 0; i < count; ++i) { + result += s; + } + return result; +} + +Errors ParseInvalidModule(std::string text) { + Errors errors; + auto lexer = + WastLexer::CreateBufferLexer("test", text.c_str(), text.size(), &errors); + std::unique_ptr<Module> module; + Features features; + WastParseOptions options(features); + Result result = ParseWatModule(lexer.get(), &module, &errors, &options); + EXPECT_EQ(Result::Error, result); + + return errors; +} + +} // end of anonymous namespace + +TEST(WastParser, LongToken) { + std::string text; + text = "(module (memory "; + text += repeat("a", 0x5000); + text += "))"; + + Errors errors = ParseInvalidModule(text); + ASSERT_EQ(1u, errors.size()); + + ASSERT_EQ(ErrorLevel::Error, errors[0].error_level); + ASSERT_EQ(1, errors[0].loc.line); + ASSERT_EQ(17, errors[0].loc.first_column); + ASSERT_STREQ( + R"(unexpected token "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...", expected a natural number (e.g. 123).)", + errors[0].message.c_str()); +} + +TEST(WastParser, LongTokenSpace) { + std::string text; + text = "notparen"; + text += repeat(" ", 0x10000); + text += "notmodule"; + + Errors errors = ParseInvalidModule(text); + ASSERT_EQ(2u, errors.size()); + + ASSERT_EQ(ErrorLevel::Error, errors[0].error_level); + ASSERT_EQ(1, errors[0].loc.line); + ASSERT_EQ(1, errors[0].loc.first_column); + ASSERT_STREQ( + R"(unexpected token "notparen", expected a module field or a module.)", + errors[0].message.c_str()); + + ASSERT_EQ(1, errors[1].loc.line); + ASSERT_EQ(65545, errors[1].loc.first_column); + ASSERT_STREQ(R"(unexpected token notmodule, expected EOF.)", + errors[1].message.c_str()); +} diff --git a/third_party/wasm2c/src/token.cc b/third_party/wasm2c/src/token.cc new file mode 100644 index 0000000000..576fadd185 --- /dev/null +++ b/third_party/wasm2c/src/token.cc @@ -0,0 +1,99 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/token.h" + +namespace wabt { + +const char* GetTokenTypeName(TokenType token_type) { + static const char* s_names[] = { +#define WABT_TOKEN(name, string) string, +#define WABT_TOKEN_FIRST(name, string) +#define WABT_TOKEN_LAST(name, string) +#include "wabt/token.def" +#undef WABT_TOKEN +#undef WABT_TOKEN_FIRST +#undef WABT_TOKEN_LAST + }; + + static_assert( + WABT_ARRAY_SIZE(s_names) == WABT_ENUM_COUNT(TokenType), + "Expected TokenType names list length to match number of TokenTypes."); + + int x = static_cast<int>(token_type); + if (x < WABT_ENUM_COUNT(TokenType)) { + return s_names[x]; + } + + return "Invalid"; +} + +Token::Token(Location loc, TokenType token_type) + : loc(loc), token_type_(token_type) { + assert(IsTokenTypeBare(token_type_)); +} + +Token::Token(Location loc, TokenType token_type, Type type) + : loc(loc), token_type_(token_type) { + assert(HasType()); + Construct(type_, type); +} + +Token::Token(Location loc, TokenType token_type, std::string_view text) + : loc(loc), token_type_(token_type) { + assert(HasText()); + Construct(text_, text); +} + +Token::Token(Location loc, TokenType token_type, Opcode opcode) + : loc(loc), token_type_(token_type) { + assert(HasOpcode()); + Construct(opcode_, opcode); +} + +Token::Token(Location loc, TokenType token_type, const Literal& literal) + : loc(loc), token_type_(token_type) { + assert(HasLiteral()); + Construct(literal_, literal); +} + +std::string Token::to_string() const { + if (IsTokenTypeBare(token_type_)) { + return GetTokenTypeName(token_type_); + } else if (HasLiteral()) { + return std::string(literal_.text); + } else if (HasOpcode()) { + return opcode_.GetName(); + } else if (HasText()) { + return std::string(text_); + } else if (IsTokenTypeRefKind(token_type_)) { + return type_.GetRefKindName(); + } else { + assert(HasType()); + return type_.GetName(); + } +} + +std::string Token::to_string_clamp(size_t max_length) const { + std::string s = to_string(); + if (s.length() > max_length) { + return s.substr(0, max_length - 3) + "..."; + } else { + return s; + } +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/tools/wasm2c.cc b/third_party/wasm2c/src/tools/wasm2c.cc new file mode 100644 index 0000000000..83e5114af6 --- /dev/null +++ b/third_party/wasm2c/src/tools/wasm2c.cc @@ -0,0 +1,217 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cassert> +#include <cinttypes> +#include <cstdio> +#include <cstdlib> + +#include "wabt/apply-names.h" +#include "wabt/binary-reader-ir.h" +#include "wabt/binary-reader.h" +#include "wabt/error-formatter.h" +#include "wabt/feature.h" +#include "wabt/filenames.h" +#include "wabt/generate-names.h" +#include "wabt/ir.h" +#include "wabt/option-parser.h" +#include "wabt/result.h" +#include "wabt/stream.h" +#include "wabt/validator.h" +#include "wabt/wast-lexer.h" + +#include "wabt/c-writer.h" + +using namespace wabt; + +static int s_verbose; +static std::string s_infile; +static std::string s_outfile; +static unsigned int s_num_outputs = 1; +static Features s_features; +static WriteCOptions s_write_c_options; +static bool s_read_debug_names = true; +static std::unique_ptr<FileStream> s_log_stream; + +static const char s_description[] = + R"( Read a file in the WebAssembly binary format, and convert it to + a C source file and header. + +examples: + # parse binary file test.wasm and write test.c and test.h + $ wasm2c test.wasm -o test.c + + # parse test.wasm, write test.c and test.h, but ignore the debug names, if any + $ wasm2c test.wasm --no-debug-names -o test.c +)"; + +static const std::string supported_features[] = { + "multi-memory", "multi-value", "sign-extension", "saturating-float-to-int", + "exceptions", "memory64", "extended-const", "simd"}; + +static bool IsFeatureSupported(const std::string& feature) { + return std::find(std::begin(supported_features), std::end(supported_features), + feature) != std::end(supported_features); +}; + +static void ParseOptions(int argc, char** argv) { + OptionParser parser("wasm2c", s_description); + + parser.AddOption('v', "verbose", "Use multiple times for more info", []() { + s_verbose++; + s_log_stream = FileStream::CreateStderr(); + }); + parser.AddOption( + 'o', "output", "FILENAME", + "Output file for the generated C source file, by default use stdout", + [](const char* argument) { + s_outfile = argument; + ConvertBackslashToSlash(&s_outfile); + }); + parser.AddOption( + '\0', "num-outputs", "NUM", "Number of output files to write", + [](const char* argument) { s_num_outputs = atoi(argument); }); + parser.AddOption( + 'n', "module-name", "MODNAME", + "Unique name for the module being generated. This name is prefixed to\n" + "each of the generaed C symbols. By default, the module name from the\n" + "names section is used. If that is not present the name of the input\n" + "file is used as the default.\n", + [](const char* argument) { s_write_c_options.module_name = argument; }); + s_features.AddOptions(&parser); + parser.AddOption("no-debug-names", "Ignore debug names in the binary file", + []() { s_read_debug_names = false; }); + parser.AddArgument("filename", OptionParser::ArgumentCount::One, + [](const char* argument) { + s_infile = argument; + ConvertBackslashToSlash(&s_infile); + }); + parser.Parse(argc, argv); + s_write_c_options.features = &s_features; + + bool any_non_supported_feature = false; +#define WABT_FEATURE(variable, flag, default_, help) \ + any_non_supported_feature |= \ + (s_features.variable##_enabled() != default_) && \ + s_features.variable##_enabled() && !IsFeatureSupported(flag); +#include "wabt/feature.def" +#undef WABT_FEATURE + + if (any_non_supported_feature) { + fprintf(stderr, + "wasm2c currently only supports a limited set of features.\n"); + exit(1); + } +} + +// TODO(binji): copied from binary-writer-spec.cc, probably should share. +static std::string_view strip_extension(std::string_view s) { + std::string_view ext = s.substr(s.find_last_of('.')); + std::string_view result = s; + + if (ext == ".c") + result.remove_suffix(ext.length()); + return result; +} + +Result Wasm2cMain(Errors& errors) { + if (s_num_outputs < 1) { + fprintf(stderr, "Number of output files must be positive.\n"); + return Result::Error; + } + + std::vector<uint8_t> file_data; + CHECK_RESULT(ReadFile(s_infile.c_str(), &file_data)); + + Module module; + const bool kStopOnFirstError = true; + const bool kFailOnCustomSectionError = true; + ReadBinaryOptions options(s_features, s_log_stream.get(), s_read_debug_names, + kStopOnFirstError, kFailOnCustomSectionError); + CHECK_RESULT(ReadBinaryIr(s_infile.c_str(), file_data.data(), + file_data.size(), options, &errors, &module)); + CHECK_RESULT(ValidateModule(&module, &errors, s_features)); + CHECK_RESULT(GenerateNames(&module)); + /* TODO(binji): This shouldn't fail; if a name can't be applied + * (because the index is invalid, say) it should just be skipped. */ + ApplyNames(&module); + + if (!s_outfile.empty()) { + std::string header_name_full = + std::string(strip_extension(s_outfile)) + ".h"; + std::vector<FileStream> c_streams; + if (s_num_outputs == 1) { + c_streams.emplace_back(s_outfile.c_str()); + } else { + std::string output_prefix{strip_extension(s_outfile)}; + for (unsigned int i = 0; i < s_num_outputs; i++) { + c_streams.emplace_back(output_prefix + "_" + std::to_string(i) + ".c"); + } + } + std::vector<Stream*> c_stream_ptrs; + for (auto& s : c_streams) { + c_stream_ptrs.emplace_back(&s); + } + FileStream h_stream(header_name_full); + std::string_view header_name = GetBasename(header_name_full); + if (s_write_c_options.module_name.empty()) { + s_write_c_options.module_name = module.name; + if (s_write_c_options.module_name.empty()) { + // In the absence of module name in the names section use the filename. + s_write_c_options.module_name = StripExtension(GetBasename(s_infile)); + } + } + if (s_num_outputs == 1) { + CHECK_RESULT(WriteC(std::move(c_stream_ptrs), &h_stream, c_stream_ptrs[0], + std::string(header_name).c_str(), "", &module, + s_write_c_options)); + } else { + std::string header_impl_name_full = + std::string(strip_extension(s_outfile)) + "-impl.h"; + FileStream h_impl_stream(header_impl_name_full); + std::string_view header_impl_name = GetBasename(header_impl_name_full); + CHECK_RESULT(WriteC(std::move(c_stream_ptrs), &h_stream, &h_impl_stream, + std::string(header_name).c_str(), + std::string(header_impl_name).c_str(), &module, + s_write_c_options)); + } + } else { + FileStream stream(stdout); + CHECK_RESULT(WriteC({&stream}, &stream, &stream, "wasm.h", "", &module, + s_write_c_options)); + } + + return Result::Ok; +} + +int ProgramMain(int argc, char** argv) { + Result result; + + InitStdio(); + ParseOptions(argc, argv); + + Errors errors; + result = Wasm2cMain(errors); + FormatErrorsToFile(errors, Location::Type::Binary); + + return result != Result::Ok; +} + +int main(int argc, char** argv) { + WABT_TRY + return ProgramMain(argc, argv); + WABT_CATCH_BAD_ALLOC_AND_EXIT +} diff --git a/third_party/wasm2c/src/tracing.cc b/third_party/wasm2c/src/tracing.cc new file mode 100644 index 0000000000..21f512f587 --- /dev/null +++ b/third_party/wasm2c/src/tracing.cc @@ -0,0 +1,71 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define WABT_TRACING 1 +#include "wabt/tracing.h" + +namespace { + +size_t indent = 0; +const char* indent_text = " "; + +void Fill() { + for (size_t i = 0; i < indent; ++i) + fputs(indent_text, stderr); +} + +void Indent() { + Fill(); + ++indent; +} + +void Dedent() { + if (indent) { + --indent; + } + Fill(); +} + +} // end of anonymous namespace + +namespace wabt { + +// static + +TraceScope::TraceScope(const char* method) : method_(method) { + PrintEnter(method); + PrintNewline(); +} + +TraceScope::~TraceScope() { + Dedent(); + fputs("<- ", stderr); + fputs(method_, stderr); + fputc('\n', stderr); +} + +void TraceScope::PrintEnter(const char* method) { + Indent(); + fputs("-> ", stderr); + fputs(method, stderr); + fputs("(", stderr); +} + +void TraceScope::PrintNewline() { + fputs(")\n", stderr); +} + +} // end of namespace wabt diff --git a/third_party/wasm2c/src/type-checker.cc b/third_party/wasm2c/src/type-checker.cc new file mode 100644 index 0000000000..79b4f60496 --- /dev/null +++ b/third_party/wasm2c/src/type-checker.cc @@ -0,0 +1,989 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/type-checker.h" + +#include <cinttypes> + +namespace wabt { + +namespace { + +std::string TypesToString(const TypeVector& types, + const char* prefix = nullptr) { + std::string result = "["; + if (prefix) { + result += prefix; + } + + for (size_t i = 0; i < types.size(); ++i) { + result += types[i].GetName(); + if (i < types.size() - 1) { + result += ", "; + } + } + result += "]"; + return result; +} + +} // end anonymous namespace + +TypeChecker::Label::Label(LabelType label_type, + const TypeVector& param_types, + const TypeVector& result_types, + size_t limit) + : label_type(label_type), + param_types(param_types), + result_types(result_types), + type_stack_limit(limit), + unreachable(false) {} + +void TypeChecker::PrintError(const char* fmt, ...) { + if (error_callback_) { + WABT_SNPRINTF_ALLOCA(buffer, length, fmt); + error_callback_(buffer); + } +} + +Result TypeChecker::GetLabel(Index depth, Label** out_label) { + if (depth >= label_stack_.size()) { + assert(label_stack_.size() > 0); + PrintError("invalid depth: %" PRIindex " (max %" PRIzd ")", depth, + label_stack_.size() - 1); + *out_label = nullptr; + return Result::Error; + } + *out_label = &label_stack_[label_stack_.size() - depth - 1]; + return Result::Ok; +} + +Result TypeChecker::GetRethrowLabel(Index depth, Label** out_label) { + if (Failed(GetLabel(depth, out_label))) { + return Result::Error; + } + + if ((*out_label)->label_type == LabelType::Catch) { + return Result::Ok; + } + + std::string candidates; + for (Index idx = 0; idx < label_stack_.size(); idx++) { + LabelType type = label_stack_[label_stack_.size() - idx - 1].label_type; + if (type == LabelType::Catch) { + if (!candidates.empty()) { + candidates.append(", "); + } + candidates.append(std::to_string(idx)); + } + } + + if (candidates.empty()) { + PrintError("rethrow not in try catch block"); + } else { + PrintError("invalid rethrow depth: %" PRIindex " (catches: %s)", depth, + candidates.c_str()); + } + *out_label = nullptr; + return Result::Error; +} + +Result TypeChecker::GetCatchCount(Index depth, Index* out_count) { + Label* unused; + if (Failed(GetLabel(depth, &unused))) { + return Result::Error; + } + + Index catch_count = 0; + for (Index idx = 0; idx <= depth; idx++) { + LabelType type = label_stack_[label_stack_.size() - idx - 1].label_type; + if (type == LabelType::Catch) { + catch_count++; + } + } + *out_count = catch_count; + + return Result::Ok; +} + +Result TypeChecker::TopLabel(Label** out_label) { + return GetLabel(0, out_label); +} + +bool TypeChecker::IsUnreachable() { + Label* label; + if (Failed(TopLabel(&label))) { + return true; + } + return label->unreachable; +} + +void TypeChecker::ResetTypeStackToLabel(Label* label) { + type_stack_.resize(label->type_stack_limit); +} + +Result TypeChecker::SetUnreachable() { + Label* label; + CHECK_RESULT(TopLabel(&label)); + label->unreachable = true; + ResetTypeStackToLabel(label); + return Result::Ok; +} + +void TypeChecker::PushLabel(LabelType label_type, + const TypeVector& param_types, + const TypeVector& result_types) { + label_stack_.emplace_back(label_type, param_types, result_types, + type_stack_.size()); +} + +Result TypeChecker::PopLabel() { + label_stack_.pop_back(); + return Result::Ok; +} + +Result TypeChecker::CheckLabelType(Label* label, LabelType label_type) { + return label->label_type == label_type ? Result::Ok : Result::Error; +} + +Result TypeChecker::Check2LabelTypes(Label* label, + LabelType label_type1, + LabelType label_type2) { + return label->label_type == label_type1 || label->label_type == label_type2 + ? Result::Ok + : Result::Error; +} + +Result TypeChecker::GetThisFunctionLabel(Label** label) { + return GetLabel(label_stack_.size() - 1, label); +} + +Result TypeChecker::PeekType(Index depth, Type* out_type) { + Label* label; + CHECK_RESULT(TopLabel(&label)); + + if (label->type_stack_limit + depth >= type_stack_.size()) { + *out_type = Type::Any; + return label->unreachable ? Result::Ok : Result::Error; + } + *out_type = type_stack_[type_stack_.size() - depth - 1]; + return Result::Ok; +} + +Result TypeChecker::PeekAndCheckType(Index depth, Type expected) { + Type actual = Type::Any; + Result result = PeekType(depth, &actual); + return result | CheckType(actual, expected); +} + +Result TypeChecker::DropTypes(size_t drop_count) { + Label* label; + CHECK_RESULT(TopLabel(&label)); + if (label->type_stack_limit + drop_count > type_stack_.size()) { + ResetTypeStackToLabel(label); + return label->unreachable ? Result::Ok : Result::Error; + } + type_stack_.erase(type_stack_.end() - drop_count, type_stack_.end()); + return Result::Ok; +} + +void TypeChecker::PushType(Type type) { + if (type != Type::Void) { + type_stack_.push_back(type); + } +} + +void TypeChecker::PushTypes(const TypeVector& types) { + for (Type type : types) { + PushType(type); + } +} + +Result TypeChecker::CheckTypeStackEnd(const char* desc) { + Label* label; + CHECK_RESULT(TopLabel(&label)); + Result result = (type_stack_.size() == label->type_stack_limit) + ? Result::Ok + : Result::Error; + PrintStackIfFailedV(result, desc, {}, /*is_end=*/true); + return result; +} + +Result TypeChecker::CheckType(Type actual, Type expected) { + if (expected == Type::Any || actual == Type::Any) { + return Result::Ok; + } + + if (expected == Type::Reference && actual == Type::Reference) { + return expected.GetReferenceIndex() == actual.GetReferenceIndex() + ? Result::Ok + : Result::Error; + } + if (actual != expected) { + return Result::Error; + } + return Result::Ok; +} + +Result TypeChecker::CheckTypes(const TypeVector& actual, + const TypeVector& expected) { + if (actual.size() != expected.size()) { + return Result::Error; + } else { + Result result = Result::Ok; + for (size_t i = 0; i < actual.size(); i++) + result |= CheckType(actual[i], expected[i]); + return result; + } +} + +Result TypeChecker::CheckSignature(const TypeVector& sig, const char* desc) { + Result result = Result::Ok; + for (size_t i = 0; i < sig.size(); ++i) { + result |= PeekAndCheckType(sig.size() - i - 1, sig[i]); + } + PrintStackIfFailed(result, desc, sig); + return result; +} + +Result TypeChecker::CheckReturnSignature(const TypeVector& actual, + const TypeVector& expected, + const char* desc) { + Result result = CheckTypes(actual, expected); + if (Failed(result)) { + PrintError("return signatures have inconsistent types: expected %s, got %s", + TypesToString(expected).c_str(), TypesToString(actual).c_str()); + } + return result; +} + +Result TypeChecker::PopAndCheckSignature(const TypeVector& sig, + const char* desc) { + Result result = CheckSignature(sig, desc); + result |= DropTypes(sig.size()); + return result; +} + +Result TypeChecker::PopAndCheckCall(const TypeVector& param_types, + const TypeVector& result_types, + const char* desc) { + Result result = CheckSignature(param_types, desc); + result |= DropTypes(param_types.size()); + PushTypes(result_types); + return result; +} + +Result TypeChecker::PopAndCheck1Type(Type expected, const char* desc) { + Result result = Result::Ok; + result |= PeekAndCheckType(0, expected); + PrintStackIfFailed(result, desc, expected); + result |= DropTypes(1); + return result; +} + +Result TypeChecker::PopAndCheck2Types(Type expected1, + Type expected2, + const char* desc) { + Result result = Result::Ok; + result |= PeekAndCheckType(0, expected2); + result |= PeekAndCheckType(1, expected1); + PrintStackIfFailed(result, desc, expected1, expected2); + result |= DropTypes(2); + return result; +} + +Result TypeChecker::PopAndCheck3Types(Type expected1, + Type expected2, + Type expected3, + const char* desc) { + Result result = Result::Ok; + result |= PeekAndCheckType(0, expected3); + result |= PeekAndCheckType(1, expected2); + result |= PeekAndCheckType(2, expected1); + PrintStackIfFailed(result, desc, expected1, expected2, expected3); + result |= DropTypes(3); + return result; +} + +// Some paramater types depend on the memory being used. +// For example load/store operands, or memory.fill operands. +static Type GetMemoryParam(Type param, const Limits* limits) { + return limits ? limits->IndexType() : param; +} + +Result TypeChecker::CheckOpcode1(Opcode opcode, const Limits* limits) { + Result result = PopAndCheck1Type( + GetMemoryParam(opcode.GetParamType1(), limits), opcode.GetName()); + PushType(opcode.GetResultType()); + return result; +} + +Result TypeChecker::CheckOpcode2(Opcode opcode, const Limits* limits) { + Result result = + PopAndCheck2Types(GetMemoryParam(opcode.GetParamType1(), limits), + opcode.GetParamType2(), opcode.GetName()); + PushType(opcode.GetResultType()); + return result; +} + +Result TypeChecker::CheckOpcode3(Opcode opcode, + const Limits* limits1, + const Limits* limits2, + const Limits* limits3) { + Result result = PopAndCheck3Types( + GetMemoryParam(opcode.GetParamType1(), limits1), + GetMemoryParam(opcode.GetParamType2(), limits2), + GetMemoryParam(opcode.GetParamType3(), limits3), opcode.GetName()); + PushType(opcode.GetResultType()); + return result; +} + +void TypeChecker::PrintStackIfFailedV(Result result, + const char* desc, + const TypeVector& expected, + bool is_end) { + if (Succeeded(result)) { + return; + } + + size_t limit = 0; + Label* label; + if (Succeeded(TopLabel(&label))) { + limit = label->type_stack_limit; + } + + TypeVector actual; + size_t max_depth = type_stack_.size() - limit; + + // In general we want to print as many values of the actual stack as were + // expected. However, if the stack was expected to be empty, we should + // print some amount of the actual stack. + size_t actual_size; + if (expected.size() == 0) { + // Don't print too many elements if the stack is really deep. + const size_t kMaxActualStackToPrint = 4; + actual_size = std::min(kMaxActualStackToPrint, max_depth); + } else { + actual_size = std::min(expected.size(), max_depth); + } + + bool incomplete_actual_stack = actual_size != max_depth; + + for (size_t i = 0; i < actual_size; ++i) { + Type type; + Result result = PeekType(actual_size - i - 1, &type); + WABT_USE(result); + assert(Succeeded(result)); + actual.push_back(type); + } + + std::string message = "type mismatch in "; + if (is_end) { + message = "type mismatch at end of "; + } + message += desc; + message += ", expected "; + message += TypesToString(expected); + message += " but got "; + message += TypesToString(actual, incomplete_actual_stack ? "... " : nullptr); + + PrintError("%s", message.c_str()); +} + +Result TypeChecker::BeginFunction(const TypeVector& sig) { + type_stack_.clear(); + label_stack_.clear(); + PushLabel(LabelType::Func, TypeVector(), sig); + return Result::Ok; +} + +Result TypeChecker::OnAtomicLoad(Opcode opcode, const Limits& limits) { + return CheckOpcode1(opcode, &limits); +} + +Result TypeChecker::OnAtomicStore(Opcode opcode, const Limits& limits) { + return CheckOpcode2(opcode, &limits); +} + +Result TypeChecker::OnAtomicRmw(Opcode opcode, const Limits& limits) { + return CheckOpcode2(opcode, &limits); +} + +Result TypeChecker::OnAtomicRmwCmpxchg(Opcode opcode, const Limits& limits) { + return CheckOpcode3(opcode, &limits); +} + +Result TypeChecker::OnAtomicWait(Opcode opcode, const Limits& limits) { + return CheckOpcode3(opcode, &limits); +} + +Result TypeChecker::OnAtomicFence(uint32_t consistency_model) { + return Result::Ok; +} + +Result TypeChecker::OnAtomicNotify(Opcode opcode, const Limits& limits) { + return CheckOpcode2(opcode, &limits); +} + +Result TypeChecker::OnBinary(Opcode opcode) { + return CheckOpcode2(opcode); +} + +Result TypeChecker::OnBlock(const TypeVector& param_types, + const TypeVector& result_types) { + Result result = PopAndCheckSignature(param_types, "block"); + PushLabel(LabelType::Block, param_types, result_types); + PushTypes(param_types); + return result; +} + +Result TypeChecker::OnBr(Index depth) { + Result result = Result::Ok; + Label* label; + CHECK_RESULT(GetLabel(depth, &label)); + result |= CheckSignature(label->br_types(), "br"); + CHECK_RESULT(SetUnreachable()); + return result; +} + +Result TypeChecker::OnBrIf(Index depth) { + Result result = PopAndCheck1Type(Type::I32, "br_if"); + Label* label; + CHECK_RESULT(GetLabel(depth, &label)); + result |= PopAndCheckSignature(label->br_types(), "br_if"); + PushTypes(label->br_types()); + return result; +} + +Result TypeChecker::BeginBrTable() { + br_table_sig_ = nullptr; + return PopAndCheck1Type(Type::I32, "br_table"); +} + +Result TypeChecker::OnBrTableTarget(Index depth) { + Result result = Result::Ok; + Label* label; + CHECK_RESULT(GetLabel(depth, &label)); + TypeVector& label_sig = label->br_types(); + result |= CheckSignature(label_sig, "br_table"); + + // Make sure this label's signature is consistent with the previous labels' + // signatures. + if (br_table_sig_ == nullptr) { + br_table_sig_ = &label_sig; + } else { + if (br_table_sig_->size() != label_sig.size()) { + result |= Result::Error; + PrintError("br_table labels have inconsistent types: expected %s, got %s", + TypesToString(*br_table_sig_).c_str(), + TypesToString(label_sig).c_str()); + } + } + + return result; +} + +Result TypeChecker::EndBrTable() { + return SetUnreachable(); +} + +Result TypeChecker::OnCall(const TypeVector& param_types, + const TypeVector& result_types) { + return PopAndCheckCall(param_types, result_types, "call"); +} + +Result TypeChecker::OnCallIndirect(const TypeVector& param_types, + const TypeVector& result_types) { + Result result = PopAndCheck1Type(Type::I32, "call_indirect"); + result |= PopAndCheckCall(param_types, result_types, "call_indirect"); + return result; +} + +Result TypeChecker::OnIndexedFuncRef(Index* out_index) { + Type type; + CHECK_RESULT(PeekType(0, &type)); + Result result = Result::Ok; + if (!(type == Type::Any || type.IsReferenceWithIndex())) { + TypeVector actual; + actual.push_back(type); + std::string message = + "type mismatch in call_ref, expected reference but got " + + TypesToString(actual); + PrintError("%s", message.c_str()); + result = Result::Error; + } + if (Succeeded(result)) { + *out_index = type.GetReferenceIndex(); + } + result |= DropTypes(1); + return result; +} + +Result TypeChecker::OnReturnCall(const TypeVector& param_types, + const TypeVector& result_types) { + Result result = PopAndCheckSignature(param_types, "return_call"); + Label* func_label; + CHECK_RESULT(GetThisFunctionLabel(&func_label)); + result |= CheckReturnSignature(result_types, func_label->result_types, + "return_call"); + + CHECK_RESULT(SetUnreachable()); + return result; +} + +Result TypeChecker::OnReturnCallIndirect(const TypeVector& param_types, + const TypeVector& result_types) { + Result result = PopAndCheck1Type(Type::I32, "return_call_indirect"); + + result |= PopAndCheckSignature(param_types, "return_call_indirect"); + Label* func_label; + CHECK_RESULT(GetThisFunctionLabel(&func_label)); + result |= CheckReturnSignature(result_types, func_label->result_types, + "return_call_indirect"); + + CHECK_RESULT(SetUnreachable()); + return result; +} + +Result TypeChecker::OnCompare(Opcode opcode) { + return CheckOpcode2(opcode); +} + +Result TypeChecker::OnCatch(const TypeVector& sig) { + Result result = Result::Ok; + Label* label; + CHECK_RESULT(TopLabel(&label)); + result |= Check2LabelTypes(label, LabelType::Try, LabelType::Catch); + result |= PopAndCheckSignature(label->result_types, "try block"); + result |= CheckTypeStackEnd("try block"); + ResetTypeStackToLabel(label); + label->label_type = LabelType::Catch; + label->unreachable = false; + PushTypes(sig); + return result; +} + +Result TypeChecker::OnConst(Type type) { + PushType(type); + return Result::Ok; +} + +Result TypeChecker::OnConvert(Opcode opcode) { + return CheckOpcode1(opcode); +} + +Result TypeChecker::OnDelegate(Index depth) { + Result result = Result::Ok; + Label* label; + // Delegate starts counting after the current try, as the delegate + // instruction is not actually in the try block. + CHECK_RESULT(GetLabel(depth + 1, &label)); + + Label* try_label; + CHECK_RESULT(TopLabel(&try_label)); + result |= CheckLabelType(try_label, LabelType::Try); + result |= PopAndCheckSignature(try_label->result_types, "try block"); + result |= CheckTypeStackEnd("try block"); + ResetTypeStackToLabel(try_label); + + // Since an end instruction does not follow a delegate, we push + // the block results here and pop the label. + PushTypes(try_label->result_types); + PopLabel(); + return result; +} + +Result TypeChecker::OnDrop() { + Result result = Result::Ok; + result |= DropTypes(1); + PrintStackIfFailed(result, "drop", Type::Any); + return result; +} + +Result TypeChecker::OnElse() { + Result result = Result::Ok; + Label* label; + CHECK_RESULT(TopLabel(&label)); + result |= CheckLabelType(label, LabelType::If); + result |= PopAndCheckSignature(label->result_types, "`if true` branch"); + result |= CheckTypeStackEnd("`if true` branch"); + ResetTypeStackToLabel(label); + PushTypes(label->param_types); + label->label_type = LabelType::Else; + label->unreachable = false; + return result; +} + +Result TypeChecker::OnEnd(Label* label, + const char* sig_desc, + const char* end_desc) { + Result result = Result::Ok; + result |= PopAndCheckSignature(label->result_types, sig_desc); + result |= CheckTypeStackEnd(end_desc); + ResetTypeStackToLabel(label); + PushTypes(label->result_types); + PopLabel(); + return result; +} + +Result TypeChecker::OnEnd() { + Result result = Result::Ok; + static const char* s_label_type_name[] = { + "function", "initializer expression", "block", "loop", + "if", "`if false` branch", "try", "try catch"}; + WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(s_label_type_name) == kLabelTypeCount); + Label* label; + CHECK_RESULT(TopLabel(&label)); + assert(static_cast<int>(label->label_type) < kLabelTypeCount); + if (label->label_type == LabelType::If) { + // An if without an else will just pass the params through, so the result + // types must be the same as the param types. It has the same behavior as + // an empty else block. + CHECK_RESULT(OnElse()); + } + + const char* desc = s_label_type_name[static_cast<int>(label->label_type)]; + result |= OnEnd(label, desc, desc); + return result; +} + +Result TypeChecker::OnIf(const TypeVector& param_types, + const TypeVector& result_types) { + Result result = PopAndCheck1Type(Type::I32, "if"); + result |= PopAndCheckSignature(param_types, "if"); + PushLabel(LabelType::If, param_types, result_types); + PushTypes(param_types); + return result; +} + +Result TypeChecker::OnGlobalGet(Type type) { + PushType(type); + return Result::Ok; +} + +Result TypeChecker::OnGlobalSet(Type type) { + return PopAndCheck1Type(type, "global.set"); +} + +Result TypeChecker::OnLoad(Opcode opcode, const Limits& limits) { + return CheckOpcode1(opcode, &limits); +} + +Result TypeChecker::OnLocalGet(Type type) { + PushType(type); + return Result::Ok; +} + +Result TypeChecker::OnLocalSet(Type type) { + return PopAndCheck1Type(type, "local.set"); +} + +Result TypeChecker::OnLocalTee(Type type) { + Result result = Result::Ok; + result |= PopAndCheck1Type(type, "local.tee"); + PushType(type); + return result; +} + +Result TypeChecker::OnLoop(const TypeVector& param_types, + const TypeVector& result_types) { + Result result = PopAndCheckSignature(param_types, "loop"); + PushLabel(LabelType::Loop, param_types, result_types); + PushTypes(param_types); + return result; +} + +Result TypeChecker::OnMemoryCopy(const Limits& src_limits, + const Limits& dst_limits) { + Limits size_limits = src_limits; + // The memory64 proposal specifies that the type of the size argument should + // be the mimimum of the two memory types. + if (src_limits.is_64 && !dst_limits.is_64) { + size_limits = dst_limits; + } + return CheckOpcode3(Opcode::MemoryCopy, &src_limits, &dst_limits, + &size_limits); +} + +Result TypeChecker::OnDataDrop(uint32_t segment) { + return Result::Ok; +} + +Result TypeChecker::OnMemoryFill(const Limits& limits) { + return CheckOpcode3(Opcode::MemoryFill, &limits, nullptr, &limits); +} + +Result TypeChecker::OnMemoryGrow(const Limits& limits) { + Result result = PopAndCheck1Type(limits.IndexType(), "memory.grow"); + PushType(limits.IndexType()); + return result; +} + +Result TypeChecker::OnMemoryInit(uint32_t segment, const Limits& limits) { + return CheckOpcode3(Opcode::MemoryInit, &limits); +} + +Result TypeChecker::OnMemorySize(const Limits& limits) { + PushType(limits.IndexType()); + return Result::Ok; +} + +Result TypeChecker::OnTableCopy() { + return CheckOpcode3(Opcode::TableCopy); +} + +Result TypeChecker::OnElemDrop(uint32_t segment) { + return Result::Ok; +} + +Result TypeChecker::OnTableInit(uint32_t table, uint32_t segment) { + return CheckOpcode3(Opcode::TableInit); +} + +Result TypeChecker::OnTableGet(Type elem_type) { + Result result = PopAndCheck1Type(Type::I32, "table.get"); + PushType(elem_type); + return result; +} + +Result TypeChecker::OnTableSet(Type elem_type) { + return PopAndCheck2Types(Type::I32, elem_type, "table.set"); +} + +Result TypeChecker::OnTableGrow(Type elem_type) { + Result result = PopAndCheck2Types(elem_type, Type::I32, "table.grow"); + PushType(Type::I32); + return result; +} + +Result TypeChecker::OnTableSize() { + PushType(Type::I32); + return Result::Ok; +} + +Result TypeChecker::OnTableFill(Type elem_type) { + return PopAndCheck3Types(Type::I32, elem_type, Type::I32, "table.fill"); +} + +Result TypeChecker::OnRefFuncExpr(Index func_type) { + if (features_.function_references_enabled()) { + PushType(Type(Type::Reference, func_type)); + } else { + PushType(Type::FuncRef); + } + return Result::Ok; +} + +Result TypeChecker::OnRefNullExpr(Type type) { + PushType(type); + return Result::Ok; +} + +Result TypeChecker::OnRefIsNullExpr() { + Type type; + Result result = PeekType(0, &type); + if (!(type == Type::Any || type.IsRef())) { + TypeVector actual; + if (Succeeded(result)) { + actual.push_back(type); + } + std::string message = + "type mismatch in ref.is_null, expected reference but got " + + TypesToString(actual); + PrintError("%s", message.c_str()); + result = Result::Error; + } + result |= DropTypes(1); + PushType(Type::I32); + return result; +} + +Result TypeChecker::OnRethrow(Index depth) { + Result result = Result::Ok; + Label* label; + CHECK_RESULT(GetRethrowLabel(depth, &label)); + CHECK_RESULT(SetUnreachable()); + return result; +} + +Result TypeChecker::OnThrow(const TypeVector& sig) { + Result result = Result::Ok; + result |= PopAndCheckSignature(sig, "throw"); + CHECK_RESULT(SetUnreachable()); + return result; +} + +Result TypeChecker::OnReturn() { + Result result = Result::Ok; + Label* func_label; + CHECK_RESULT(GetThisFunctionLabel(&func_label)); + result |= PopAndCheckSignature(func_label->result_types, "return"); + CHECK_RESULT(SetUnreachable()); + return result; +} + +Result TypeChecker::OnSelect(const TypeVector& expected) { + Result result = Result::Ok; + Type type1 = Type::Any; + Type type2 = Type::Any; + Type result_type = Type::Any; + result |= PeekAndCheckType(0, Type::I32); + result |= PeekType(1, &type1); + result |= PeekType(2, &type2); + if (expected.empty()) { + if (type1.IsRef() || type2.IsRef()) { + result = Result::Error; + } else { + result |= CheckType(type1, type2); + result_type = type1; + } + } else { + assert(expected.size() == 1); + result |= CheckType(type1, expected[0]); + result |= CheckType(type2, expected[0]); + } + PrintStackIfFailed(result, "select", result_type, result_type, Type::I32); + result |= DropTypes(3); + PushType(result_type); + return result; +} + +Result TypeChecker::OnStore(Opcode opcode, const Limits& limits) { + return CheckOpcode2(opcode, &limits); +} + +Result TypeChecker::OnTry(const TypeVector& param_types, + const TypeVector& result_types) { + Result result = PopAndCheckSignature(param_types, "try"); + PushLabel(LabelType::Try, param_types, result_types); + PushTypes(param_types); + return result; +} + +Result TypeChecker::OnUnary(Opcode opcode) { + return CheckOpcode1(opcode); +} + +Result TypeChecker::OnTernary(Opcode opcode) { + return CheckOpcode3(opcode); +} + +Result TypeChecker::OnSimdLaneOp(Opcode opcode, uint64_t lane_idx) { + Result result = Result::Ok; + uint32_t lane_count = opcode.GetSimdLaneCount(); + if (lane_idx >= lane_count) { + PrintError("lane index must be less than %d (got %" PRIu64 ")", lane_count, + lane_idx); + result = Result::Error; + } + + switch (opcode) { + case Opcode::I8X16ExtractLaneS: + case Opcode::I8X16ExtractLaneU: + case Opcode::I16X8ExtractLaneS: + case Opcode::I16X8ExtractLaneU: + case Opcode::I32X4ExtractLane: + case Opcode::F32X4ExtractLane: + case Opcode::I64X2ExtractLane: + case Opcode::F64X2ExtractLane: + result |= CheckOpcode1(opcode); + break; + case Opcode::I8X16ReplaceLane: + case Opcode::I16X8ReplaceLane: + case Opcode::I32X4ReplaceLane: + case Opcode::F32X4ReplaceLane: + case Opcode::I64X2ReplaceLane: + case Opcode::F64X2ReplaceLane: + result |= CheckOpcode2(opcode); + break; + default: + WABT_UNREACHABLE; + } + return result; +} + +Result TypeChecker::OnSimdLoadLane(Opcode opcode, + const Limits& limits, + uint64_t lane_idx) { + Result result = Result::Ok; + uint32_t lane_count = opcode.GetSimdLaneCount(); + if (lane_idx >= lane_count) { + PrintError("lane index must be less than %d (got %" PRIu64 ")", lane_count, + lane_idx); + result = Result::Error; + } + result |= CheckOpcode2(opcode, &limits); + return result; +} + +Result TypeChecker::OnSimdStoreLane(Opcode opcode, + const Limits& limits, + uint64_t lane_idx) { + Result result = Result::Ok; + uint32_t lane_count = opcode.GetSimdLaneCount(); + if (lane_idx >= lane_count) { + PrintError("lane index must be less than %d (got %" PRIu64 ")", lane_count, + lane_idx); + result = Result::Error; + } + result |= CheckOpcode2(opcode, &limits); + return result; +} + +Result TypeChecker::OnSimdShuffleOp(Opcode opcode, v128 lane_idx) { + Result result = Result::Ok; + uint8_t simd_data[16]; + memcpy(simd_data, &lane_idx, 16); + for (int i = 0; i < 16; i++) { + if (simd_data[i] >= 32) { + PrintError("lane index must be less than 32 (got %d)", simd_data[i]); + result = Result::Error; + } + } + + result |= CheckOpcode2(opcode); + return result; +} + +Result TypeChecker::OnUnreachable() { + return SetUnreachable(); +} + +Result TypeChecker::EndFunction() { + Result result = Result::Ok; + Label* label; + CHECK_RESULT(TopLabel(&label)); + result |= CheckLabelType(label, LabelType::Func); + result |= OnEnd(label, "implicit return", "function"); + return result; +} + +Result TypeChecker::BeginInitExpr(Type type) { + type_stack_.clear(); + label_stack_.clear(); + PushLabel(LabelType::InitExpr, TypeVector(), {type}); + return Result::Ok; +} + +Result TypeChecker::EndInitExpr() { + Result result = Result::Ok; + Label* label; + CHECK_RESULT(TopLabel(&label)); + result |= CheckLabelType(label, LabelType::InitExpr); + result |= OnEnd(label, "initializer expression", "initializer expression"); + return result; +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/utf8.cc b/third_party/wasm2c/src/utf8.cc new file mode 100644 index 0000000000..dd95c8c63d --- /dev/null +++ b/third_party/wasm2c/src/utf8.cc @@ -0,0 +1,106 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/utf8.h" + +#include <cstdint> + +namespace wabt { + +namespace { + +// clang-format off +const int s_utf8_length[256] = { + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0 + 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 + 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xf0 +}; +// clang-format on + +// Returns true if this is a valid continuation byte. +bool IsCont(uint8_t c) { + return (c & 0xc0) == 0x80; +} + +} // end anonymous namespace + +bool IsValidUtf8(const char* s, size_t s_length) { + const uint8_t* p = reinterpret_cast<const uint8_t*>(s); + const uint8_t* end = p + s_length; + while (p < end) { + uint8_t cu0 = *p; + int length = s_utf8_length[cu0]; + if (p + length > end) { + return false; + } + + switch (length) { + case 0: + return false; + + case 1: + p++; + break; + + case 2: + p++; + if (!IsCont(*p++)) { + return false; + } + break; + + case 3: { + p++; + uint8_t cu1 = *p++; + uint8_t cu2 = *p++; + if (!(IsCont(cu1) && IsCont(cu2)) || + (cu0 == 0xe0 && cu1 < 0xa0) || // Overlong encoding. + (cu0 == 0xed && cu1 >= 0xa0)) // UTF-16 surrogate halves. + return false; + break; + } + + case 4: { + p++; + uint8_t cu1 = *p++; + uint8_t cu2 = *p++; + uint8_t cu3 = *p++; + if (!(IsCont(cu1) && IsCont(cu2) && IsCont(cu3)) || + (cu0 == 0xf0 && cu1 < 0x90) || // Overlong encoding. + (cu0 == 0xf4 && cu1 >= 0x90)) // Code point >= 0x11000. + return false; + break; + } + } + } + return true; +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/validator.cc b/third_party/wasm2c/src/validator.cc new file mode 100644 index 0000000000..1fa568abad --- /dev/null +++ b/third_party/wasm2c/src/validator.cc @@ -0,0 +1,1086 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/validator.h" + +#include <cassert> +#include <cinttypes> +#include <cstdarg> +#include <cstdio> + +#include "wabt/config.h" + +#include "wabt/binary-reader.h" +#include "wabt/cast.h" +#include "wabt/expr-visitor.h" +#include "wabt/ir.h" +#include "wabt/shared-validator.h" + +namespace wabt { + +namespace { + +class ScriptValidator { + public: + WABT_DISALLOW_COPY_AND_ASSIGN(ScriptValidator); + ScriptValidator(Errors*, const Script*, const ValidateOptions& options); + + Result CheckScript(); + + private: + struct ActionResult { + enum class Kind { + Error, + Types, + Type, + } kind; + + union { + const TypeVector* types; + Type type; + }; + }; + + void WABT_PRINTF_FORMAT(3, 4) + PrintError(const Location* loc, const char* fmt, ...); + void CheckTypeIndex(const Location* loc, + Type actual, + Type expected, + const char* desc, + Index index, + const char* index_kind); + void CheckResultTypes(const Location* loc, + const TypeVector& actual, + const TypeVector& expected, + const char* desc); + void CheckExpectation(const Location* loc, + const TypeVector& result_types, + const ConstVector& expected, + const char* desc); + void CheckExpectationTypes(const Location* loc, + const TypeVector& result_types, + const Expectation* expect, + const char* desc); + + const TypeVector* CheckInvoke(const InvokeAction* action); + Result CheckGet(const GetAction* action, Type* out_type); + ActionResult CheckAction(const Action* action); + void CheckCommand(const Command* command); + + const ValidateOptions& options_; + Errors* errors_ = nullptr; + const Script* script_ = nullptr; + + Result result_ = Result::Ok; +}; + +class Validator : public ExprVisitor::Delegate { + public: + Validator(Errors*, const Module* module, const ValidateOptions& options); + + Result CheckModule(); + + Result OnBinaryExpr(BinaryExpr*) override; + Result BeginBlockExpr(BlockExpr*) override; + Result EndBlockExpr(BlockExpr*) override; + Result OnBrExpr(BrExpr*) override; + Result OnBrIfExpr(BrIfExpr*) override; + Result OnBrTableExpr(BrTableExpr*) override; + Result OnCallExpr(CallExpr*) override; + Result OnCallIndirectExpr(CallIndirectExpr*) override; + Result OnCallRefExpr(CallRefExpr*) override; + Result OnCodeMetadataExpr(CodeMetadataExpr*) override; + Result OnCompareExpr(CompareExpr*) override; + Result OnConstExpr(ConstExpr*) override; + Result OnConvertExpr(ConvertExpr*) override; + Result OnDropExpr(DropExpr*) override; + Result OnGlobalGetExpr(GlobalGetExpr*) override; + Result OnGlobalSetExpr(GlobalSetExpr*) override; + Result BeginIfExpr(IfExpr*) override; + Result AfterIfTrueExpr(IfExpr*) override; + Result EndIfExpr(IfExpr*) override; + Result OnLoadExpr(LoadExpr*) override; + Result OnLocalGetExpr(LocalGetExpr*) override; + Result OnLocalSetExpr(LocalSetExpr*) override; + Result OnLocalTeeExpr(LocalTeeExpr*) override; + Result BeginLoopExpr(LoopExpr*) override; + Result EndLoopExpr(LoopExpr*) override; + Result OnMemoryCopyExpr(MemoryCopyExpr*) override; + Result OnDataDropExpr(DataDropExpr*) override; + Result OnMemoryFillExpr(MemoryFillExpr*) override; + Result OnMemoryGrowExpr(MemoryGrowExpr*) override; + Result OnMemoryInitExpr(MemoryInitExpr*) override; + Result OnMemorySizeExpr(MemorySizeExpr*) override; + Result OnTableCopyExpr(TableCopyExpr*) override; + Result OnElemDropExpr(ElemDropExpr*) override; + Result OnTableInitExpr(TableInitExpr*) override; + Result OnTableGetExpr(TableGetExpr*) override; + Result OnTableSetExpr(TableSetExpr*) override; + Result OnTableGrowExpr(TableGrowExpr*) override; + Result OnTableSizeExpr(TableSizeExpr*) override; + Result OnTableFillExpr(TableFillExpr*) override; + Result OnRefFuncExpr(RefFuncExpr*) override; + Result OnRefNullExpr(RefNullExpr*) override; + Result OnRefIsNullExpr(RefIsNullExpr*) override; + Result OnNopExpr(NopExpr*) override; + Result OnReturnExpr(ReturnExpr*) override; + Result OnReturnCallExpr(ReturnCallExpr*) override; + Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr*) override; + Result OnSelectExpr(SelectExpr*) override; + Result OnStoreExpr(StoreExpr*) override; + Result OnUnaryExpr(UnaryExpr*) override; + Result OnUnreachableExpr(UnreachableExpr*) override; + Result BeginTryExpr(TryExpr*) override; + Result OnCatchExpr(TryExpr*, Catch*) override; + Result OnDelegateExpr(TryExpr*) override; + Result EndTryExpr(TryExpr*) override; + Result OnThrowExpr(ThrowExpr*) override; + Result OnRethrowExpr(RethrowExpr*) override; + Result OnAtomicWaitExpr(AtomicWaitExpr*) override; + Result OnAtomicFenceExpr(AtomicFenceExpr*) override; + Result OnAtomicNotifyExpr(AtomicNotifyExpr*) override; + Result OnAtomicLoadExpr(AtomicLoadExpr*) override; + Result OnAtomicStoreExpr(AtomicStoreExpr*) override; + Result OnAtomicRmwExpr(AtomicRmwExpr*) override; + Result OnAtomicRmwCmpxchgExpr(AtomicRmwCmpxchgExpr*) override; + Result OnTernaryExpr(TernaryExpr*) override; + Result OnSimdLaneOpExpr(SimdLaneOpExpr*) override; + Result OnSimdLoadLaneExpr(SimdLoadLaneExpr*) override; + Result OnSimdStoreLaneExpr(SimdStoreLaneExpr*) override; + Result OnSimdShuffleOpExpr(SimdShuffleOpExpr*) override; + Result OnLoadSplatExpr(LoadSplatExpr*) override; + Result OnLoadZeroExpr(LoadZeroExpr*) override; + + private: + Type GetDeclarationType(const FuncDeclaration&); + Var GetFuncTypeIndex(const Location&, const FuncDeclaration&); + + const ValidateOptions& options_; + Errors* errors_ = nullptr; + SharedValidator validator_; + const Module* current_module_ = nullptr; + Result result_ = Result::Ok; +}; + +ScriptValidator::ScriptValidator(Errors* errors, + const Script* script, + const ValidateOptions& options) + : options_(options), errors_(errors), script_(script) {} + +void ScriptValidator::PrintError(const Location* loc, const char* format, ...) { + result_ = Result::Error; + WABT_SNPRINTF_ALLOCA(buffer, length, format); + errors_->emplace_back(ErrorLevel::Error, *loc, buffer); +} + +void ScriptValidator::CheckTypeIndex(const Location* loc, + Type actual, + Type expected, + const char* desc, + Index index, + const char* index_kind) { + if (Failed(TypeChecker::CheckType(actual, expected))) { + PrintError(loc, + "type mismatch for %s %" PRIindex " of %s. got %s, expected %s", + index_kind, index, desc, actual.GetName().c_str(), + expected.GetName().c_str()); + } +} + +void ScriptValidator::CheckResultTypes(const Location* loc, + const TypeVector& actual, + const TypeVector& expected, + const char* desc) { + if (actual.size() == expected.size()) { + for (size_t i = 0; i < actual.size(); ++i) { + CheckTypeIndex(loc, actual[i], expected[i], desc, i, "result"); + } + } else { + PrintError(loc, "expected %" PRIzd " results, got %" PRIzd, expected.size(), + actual.size()); + } +} + +void ScriptValidator::CheckExpectation(const Location* loc, + const TypeVector& result_types, + const ConstVector& expected, + const char* desc) { + // Here we take the concrete expected output types verify those actains + // the types that are the result of the action. + TypeVector actual_types; + for (auto ex : expected) { + actual_types.push_back(ex.type()); + } + CheckResultTypes(loc, actual_types, result_types, desc); +} + +void ScriptValidator::CheckExpectationTypes(const Location* loc, + const TypeVector& result_types, + const Expectation* expect, + const char* desc) { + switch (expect->type()) { + case ExpectationType::Values: { + CheckExpectation(loc, result_types, expect->expected, desc); + break; + } + + case ExpectationType::Either: { + auto* either = cast<EitherExpectation>(expect); + for (auto alt : either->expected) { + CheckExpectation(loc, result_types, {alt}, desc); + } + break; + } + } +} + +Type Validator::GetDeclarationType(const FuncDeclaration& decl) { + if (decl.has_func_type) { + return Type(decl.type_var.index()); + } + if (decl.sig.param_types.empty()) { + if (decl.sig.result_types.empty()) { + return Type::Void; + } + if (decl.sig.result_types.size() == 1) { + return decl.sig.result_types[0]; + } + } + return Type(current_module_->GetFuncTypeIndex(decl)); +} + +Var Validator::GetFuncTypeIndex(const Location& default_loc, + const FuncDeclaration& decl) { + if (decl.has_func_type) { + return decl.type_var; + } + return Var(current_module_->GetFuncTypeIndex(decl), default_loc); +} + +Result Validator::OnBinaryExpr(BinaryExpr* expr) { + result_ |= validator_.OnBinary(expr->loc, expr->opcode); + return Result::Ok; +} + +Result Validator::BeginBlockExpr(BlockExpr* expr) { + result_ |= + validator_.OnBlock(expr->loc, GetDeclarationType(expr->block.decl)); + return Result::Ok; +} + +Result Validator::EndBlockExpr(BlockExpr* expr) { + result_ |= validator_.OnEnd(expr->block.end_loc); + return Result::Ok; +} + +Result Validator::OnBrExpr(BrExpr* expr) { + result_ |= validator_.OnBr(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnBrIfExpr(BrIfExpr* expr) { + result_ |= validator_.OnBrIf(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnBrTableExpr(BrTableExpr* expr) { + result_ |= validator_.BeginBrTable(expr->loc); + for (const Var& var : expr->targets) { + result_ |= validator_.OnBrTableTarget(expr->loc, var); + } + result_ |= validator_.OnBrTableTarget(expr->loc, expr->default_target); + result_ |= validator_.EndBrTable(expr->loc); + return Result::Ok; +} + +Result Validator::OnCallExpr(CallExpr* expr) { + result_ |= validator_.OnCall(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnCallIndirectExpr(CallIndirectExpr* expr) { + result_ |= validator_.OnCallIndirect( + expr->loc, GetFuncTypeIndex(expr->loc, expr->decl), expr->table); + return Result::Ok; +} + +Result Validator::OnCallRefExpr(CallRefExpr* expr) { + Index function_type_index; + result_ |= validator_.OnCallRef(expr->loc, &function_type_index); + if (Succeeded(result_)) { + expr->function_type_index = Var{function_type_index, expr->loc}; + return Result::Ok; + } + + return Result::Error; +} + +Result Validator::OnCodeMetadataExpr(CodeMetadataExpr* expr) { + return Result::Ok; +} + +Result Validator::OnCompareExpr(CompareExpr* expr) { + result_ |= validator_.OnCompare(expr->loc, expr->opcode); + return Result::Ok; +} + +Result Validator::OnConstExpr(ConstExpr* expr) { + result_ |= validator_.OnConst(expr->loc, expr->const_.type()); + return Result::Ok; +} + +Result Validator::OnConvertExpr(ConvertExpr* expr) { + result_ |= validator_.OnConvert(expr->loc, expr->opcode); + return Result::Ok; +} + +Result Validator::OnDropExpr(DropExpr* expr) { + result_ |= validator_.OnDrop(expr->loc); + return Result::Ok; +} + +Result Validator::OnGlobalGetExpr(GlobalGetExpr* expr) { + result_ |= validator_.OnGlobalGet(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnGlobalSetExpr(GlobalSetExpr* expr) { + result_ |= validator_.OnGlobalSet(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::BeginIfExpr(IfExpr* expr) { + result_ |= validator_.OnIf(expr->loc, GetDeclarationType(expr->true_.decl)); + return Result::Ok; +} + +Result Validator::AfterIfTrueExpr(IfExpr* expr) { + if (!expr->false_.empty()) { + result_ |= validator_.OnElse(expr->true_.end_loc); + } + return Result::Ok; +} + +Result Validator::EndIfExpr(IfExpr* expr) { + result_ |= validator_.OnEnd(expr->false_.empty() ? expr->true_.end_loc + : expr->false_end_loc); + return Result::Ok; +} + +Result Validator::OnLoadExpr(LoadExpr* expr) { + result_ |= validator_.OnLoad(expr->loc, expr->opcode, expr->memidx, + expr->opcode.GetAlignment(expr->align)); + return Result::Ok; +} + +Result Validator::OnLocalGetExpr(LocalGetExpr* expr) { + result_ |= validator_.OnLocalGet(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnLocalSetExpr(LocalSetExpr* expr) { + result_ |= validator_.OnLocalSet(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnLocalTeeExpr(LocalTeeExpr* expr) { + result_ |= validator_.OnLocalTee(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::BeginLoopExpr(LoopExpr* expr) { + result_ |= validator_.OnLoop(expr->loc, GetDeclarationType(expr->block.decl)); + return Result::Ok; +} + +Result Validator::EndLoopExpr(LoopExpr* expr) { + result_ |= validator_.OnEnd(expr->block.end_loc); + return Result::Ok; +} + +Result Validator::OnMemoryCopyExpr(MemoryCopyExpr* expr) { + result_ |= + validator_.OnMemoryCopy(expr->loc, expr->srcmemidx, expr->destmemidx); + return Result::Ok; +} + +Result Validator::OnDataDropExpr(DataDropExpr* expr) { + result_ |= validator_.OnDataDrop(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnMemoryFillExpr(MemoryFillExpr* expr) { + result_ |= validator_.OnMemoryFill(expr->loc, expr->memidx); + return Result::Ok; +} + +Result Validator::OnMemoryGrowExpr(MemoryGrowExpr* expr) { + result_ |= validator_.OnMemoryGrow(expr->loc, expr->memidx); + return Result::Ok; +} + +Result Validator::OnMemoryInitExpr(MemoryInitExpr* expr) { + result_ |= validator_.OnMemoryInit(expr->loc, expr->var, expr->memidx); + return Result::Ok; +} + +Result Validator::OnMemorySizeExpr(MemorySizeExpr* expr) { + result_ |= validator_.OnMemorySize(expr->loc, expr->memidx); + return Result::Ok; +} + +Result Validator::OnTableCopyExpr(TableCopyExpr* expr) { + result_ |= + validator_.OnTableCopy(expr->loc, expr->dst_table, expr->src_table); + return Result::Ok; +} + +Result Validator::OnElemDropExpr(ElemDropExpr* expr) { + result_ |= validator_.OnElemDrop(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnTableInitExpr(TableInitExpr* expr) { + result_ |= + validator_.OnTableInit(expr->loc, expr->segment_index, expr->table_index); + return Result::Ok; +} + +Result Validator::OnTableGetExpr(TableGetExpr* expr) { + result_ |= validator_.OnTableGet(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnTableSetExpr(TableSetExpr* expr) { + result_ |= validator_.OnTableSet(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnTableGrowExpr(TableGrowExpr* expr) { + result_ |= validator_.OnTableGrow(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnTableSizeExpr(TableSizeExpr* expr) { + result_ |= validator_.OnTableSize(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnTableFillExpr(TableFillExpr* expr) { + result_ |= validator_.OnTableFill(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnRefFuncExpr(RefFuncExpr* expr) { + result_ |= validator_.OnRefFunc(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnRefNullExpr(RefNullExpr* expr) { + result_ |= validator_.OnRefNull(expr->loc, expr->type); + return Result::Ok; +} + +Result Validator::OnRefIsNullExpr(RefIsNullExpr* expr) { + result_ |= validator_.OnRefIsNull(expr->loc); + return Result::Ok; +} + +Result Validator::OnNopExpr(NopExpr* expr) { + result_ |= validator_.OnNop(expr->loc); + return Result::Ok; +} + +Result Validator::OnReturnExpr(ReturnExpr* expr) { + result_ |= validator_.OnReturn(expr->loc); + return Result::Ok; +} + +Result Validator::OnReturnCallExpr(ReturnCallExpr* expr) { + result_ |= validator_.OnReturnCall(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnReturnCallIndirectExpr(ReturnCallIndirectExpr* expr) { + result_ |= validator_.OnReturnCallIndirect( + expr->loc, GetFuncTypeIndex(expr->loc, expr->decl), expr->table); + return Result::Ok; +} + +Result Validator::OnSelectExpr(SelectExpr* expr) { + result_ |= validator_.OnSelect(expr->loc, expr->result_type.size(), + expr->result_type.data()); + // TODO: Existing behavior fails when select fails. +#if 0 + return Result::Ok; +#else + return result_; +#endif +} + +Result Validator::OnStoreExpr(StoreExpr* expr) { + result_ |= validator_.OnStore(expr->loc, expr->opcode, expr->memidx, + expr->opcode.GetAlignment(expr->align)); + return Result::Ok; +} + +Result Validator::OnUnaryExpr(UnaryExpr* expr) { + result_ |= validator_.OnUnary(expr->loc, expr->opcode); + return Result::Ok; +} + +Result Validator::OnUnreachableExpr(UnreachableExpr* expr) { + result_ |= validator_.OnUnreachable(expr->loc); + return Result::Ok; +} + +Result Validator::BeginTryExpr(TryExpr* expr) { + result_ |= validator_.OnTry(expr->loc, GetDeclarationType(expr->block.decl)); + return Result::Ok; +} + +Result Validator::OnCatchExpr(TryExpr*, Catch* catch_) { + result_ |= validator_.OnCatch(catch_->loc, catch_->var, catch_->IsCatchAll()); + return Result::Ok; +} + +Result Validator::OnDelegateExpr(TryExpr* expr) { + result_ |= validator_.OnDelegate(expr->loc, expr->delegate_target); + return Result::Ok; +} + +Result Validator::EndTryExpr(TryExpr* expr) { + result_ |= validator_.OnEnd(expr->block.end_loc); + return Result::Ok; +} + +Result Validator::OnThrowExpr(ThrowExpr* expr) { + result_ |= validator_.OnThrow(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnRethrowExpr(RethrowExpr* expr) { + result_ |= validator_.OnRethrow(expr->loc, expr->var); + return Result::Ok; +} + +Result Validator::OnAtomicWaitExpr(AtomicWaitExpr* expr) { + result_ |= validator_.OnAtomicWait(expr->loc, expr->opcode, expr->memidx, + expr->opcode.GetAlignment(expr->align)); + return Result::Ok; +} + +Result Validator::OnAtomicFenceExpr(AtomicFenceExpr* expr) { + result_ |= validator_.OnAtomicFence(expr->loc, expr->consistency_model); + return Result::Ok; +} + +Result Validator::OnAtomicNotifyExpr(AtomicNotifyExpr* expr) { + result_ |= validator_.OnAtomicNotify(expr->loc, expr->opcode, expr->memidx, + expr->opcode.GetAlignment(expr->align)); + return Result::Ok; +} + +Result Validator::OnAtomicLoadExpr(AtomicLoadExpr* expr) { + result_ |= validator_.OnAtomicLoad(expr->loc, expr->opcode, expr->memidx, + expr->opcode.GetAlignment(expr->align)); + return Result::Ok; +} + +Result Validator::OnAtomicStoreExpr(AtomicStoreExpr* expr) { + result_ |= validator_.OnAtomicStore(expr->loc, expr->opcode, expr->memidx, + expr->opcode.GetAlignment(expr->align)); + return Result::Ok; +} + +Result Validator::OnAtomicRmwExpr(AtomicRmwExpr* expr) { + result_ |= validator_.OnAtomicRmw(expr->loc, expr->opcode, expr->memidx, + expr->opcode.GetAlignment(expr->align)); + return Result::Ok; +} + +Result Validator::OnAtomicRmwCmpxchgExpr(AtomicRmwCmpxchgExpr* expr) { + result_ |= + validator_.OnAtomicRmwCmpxchg(expr->loc, expr->opcode, expr->memidx, + expr->opcode.GetAlignment(expr->align)); + return Result::Ok; +} + +Result Validator::OnTernaryExpr(TernaryExpr* expr) { + result_ |= validator_.OnTernary(expr->loc, expr->opcode); + return Result::Ok; +} + +Result Validator::OnSimdLaneOpExpr(SimdLaneOpExpr* expr) { + result_ |= validator_.OnSimdLaneOp(expr->loc, expr->opcode, expr->val); + return Result::Ok; +} + +Result Validator::OnSimdLoadLaneExpr(SimdLoadLaneExpr* expr) { + result_ |= validator_.OnSimdLoadLane(expr->loc, expr->opcode, expr->memidx, + expr->opcode.GetAlignment(expr->align), + expr->val); + return Result::Ok; +} + +Result Validator::OnSimdStoreLaneExpr(SimdStoreLaneExpr* expr) { + result_ |= validator_.OnSimdStoreLane(expr->loc, expr->opcode, expr->memidx, + expr->opcode.GetAlignment(expr->align), + expr->val); + return Result::Ok; +} + +Result Validator::OnSimdShuffleOpExpr(SimdShuffleOpExpr* expr) { + result_ |= validator_.OnSimdShuffleOp(expr->loc, expr->opcode, expr->val); + return Result::Ok; +} + +Result Validator::OnLoadSplatExpr(LoadSplatExpr* expr) { + result_ |= validator_.OnLoadSplat(expr->loc, expr->opcode, expr->memidx, + expr->opcode.GetAlignment(expr->align)); + return Result::Ok; +} + +Result Validator::OnLoadZeroExpr(LoadZeroExpr* expr) { + result_ |= validator_.OnLoadZero(expr->loc, expr->opcode, expr->memidx, + expr->opcode.GetAlignment(expr->align)); + return Result::Ok; +} + +Validator::Validator(Errors* errors, + const Module* module, + const ValidateOptions& options) + : options_(options), + errors_(errors), + validator_(errors_, options_), + current_module_(module) {} + +Result Validator::CheckModule() { + const Module* module = current_module_; + + // Type section. + for (const ModuleField& field : module->fields) { + if (auto* f = dyn_cast<TypeModuleField>(&field)) { + switch (f->type->kind()) { + case TypeEntryKind::Func: { + FuncType* func_type = cast<FuncType>(f->type.get()); + result_ |= validator_.OnFuncType( + field.loc, func_type->sig.param_types.size(), + func_type->sig.param_types.data(), + func_type->sig.result_types.size(), + func_type->sig.result_types.data(), + module->GetFuncTypeIndex(func_type->sig)); + break; + } + + case TypeEntryKind::Struct: { + StructType* struct_type = cast<StructType>(f->type.get()); + TypeMutVector type_muts; + for (auto&& field : struct_type->fields) { + type_muts.push_back(TypeMut{field.type, field.mutable_}); + } + result_ |= validator_.OnStructType(field.loc, type_muts.size(), + type_muts.data()); + break; + } + + case TypeEntryKind::Array: { + ArrayType* array_type = cast<ArrayType>(f->type.get()); + result_ |= validator_.OnArrayType( + field.loc, + TypeMut{array_type->field.type, array_type->field.mutable_}); + break; + } + } + } + } + + // Import section. + for (const ModuleField& field : module->fields) { + if (auto* f = dyn_cast<ImportModuleField>(&field)) { + switch (f->import->kind()) { + case ExternalKind::Func: { + auto&& func = cast<FuncImport>(f->import.get())->func; + result_ |= validator_.OnFunction( + field.loc, GetFuncTypeIndex(field.loc, func.decl)); + break; + } + + case ExternalKind::Table: { + auto&& table = cast<TableImport>(f->import.get())->table; + result_ |= + validator_.OnTable(field.loc, table.elem_type, table.elem_limits); + break; + } + + case ExternalKind::Memory: { + auto&& memory = cast<MemoryImport>(f->import.get())->memory; + result_ |= validator_.OnMemory(field.loc, memory.page_limits); + break; + } + + case ExternalKind::Global: { + auto&& global = cast<GlobalImport>(f->import.get())->global; + result_ |= validator_.OnGlobalImport(field.loc, global.type, + global.mutable_); + break; + } + + case ExternalKind::Tag: { + auto&& tag = cast<TagImport>(f->import.get())->tag; + result_ |= validator_.OnTag(field.loc, + GetFuncTypeIndex(field.loc, tag.decl)); + break; + } + } + } + } + + // Func section. + for (const ModuleField& field : module->fields) { + if (auto* f = dyn_cast<FuncModuleField>(&field)) { + result_ |= validator_.OnFunction( + field.loc, GetFuncTypeIndex(field.loc, f->func.decl)); + } + } + + // Table section. + for (const ModuleField& field : module->fields) { + if (auto* f = dyn_cast<TableModuleField>(&field)) { + result_ |= validator_.OnTable(field.loc, f->table.elem_type, + f->table.elem_limits); + } + } + + // Memory section. + for (const ModuleField& field : module->fields) { + if (auto* f = dyn_cast<MemoryModuleField>(&field)) { + result_ |= validator_.OnMemory(field.loc, f->memory.page_limits); + } + } + + // Global section. + for (const ModuleField& field : module->fields) { + if (auto* f = dyn_cast<GlobalModuleField>(&field)) { + result_ |= + validator_.OnGlobal(field.loc, f->global.type, f->global.mutable_); + + // Init expr. + result_ |= validator_.BeginInitExpr(field.loc, f->global.type); + ExprVisitor visitor(this); + result_ |= + visitor.VisitExprList(const_cast<ExprList&>(f->global.init_expr)); + result_ |= validator_.EndInitExpr(); + } + } + + // Tag section. + for (const ModuleField& field : module->fields) { + if (auto* f = dyn_cast<TagModuleField>(&field)) { + result_ |= + validator_.OnTag(field.loc, GetFuncTypeIndex(field.loc, f->tag.decl)); + } + } + + // Export section. + for (const ModuleField& field : module->fields) { + if (auto* f = dyn_cast<ExportModuleField>(&field)) { + result_ |= validator_.OnExport(field.loc, f->export_.kind, f->export_.var, + f->export_.name); + } + } + + // Start section. + for (const ModuleField& field : module->fields) { + if (auto* f = dyn_cast<StartModuleField>(&field)) { + result_ |= validator_.OnStart(field.loc, f->start); + } + } + + // Elem segment section. + for (const ModuleField& field : module->fields) { + if (auto* f = dyn_cast<ElemSegmentModuleField>(&field)) { + result_ |= validator_.OnElemSegment(field.loc, f->elem_segment.table_var, + f->elem_segment.kind); + + result_ |= validator_.OnElemSegmentElemType(field.loc, + f->elem_segment.elem_type); + + // Init expr. + if (f->elem_segment.kind == SegmentKind::Active) { + result_ |= validator_.BeginInitExpr(field.loc, Type::I32); + ExprVisitor visitor(this); + result_ |= visitor.VisitExprList( + const_cast<ExprList&>(f->elem_segment.offset)); + result_ |= validator_.EndInitExpr(); + } + + // Element expr. + for (auto&& elem_expr : f->elem_segment.elem_exprs) { + if (elem_expr.size() == 1) { + const Expr* expr = &elem_expr.front(); + switch (expr->type()) { + case ExprType::RefNull: + result_ |= validator_.OnElemSegmentElemExpr_RefNull( + expr->loc, cast<RefNullExpr>(expr)->type); + break; + case ExprType::RefFunc: + result_ |= validator_.OnElemSegmentElemExpr_RefFunc( + expr->loc, cast<RefFuncExpr>(expr)->var); + break; + default: + result_ |= validator_.OnElemSegmentElemExpr_Other(expr->loc); + break; + } + } else if (elem_expr.size() > 1) { + result_ |= validator_.OnElemSegmentElemExpr_Other(field.loc); + } + } + } + } + + // DataCount section. + validator_.OnDataCount(module->data_segments.size()); + + // Code section. + Index func_index = module->num_func_imports; + for (const ModuleField& field : module->fields) { + if (auto* f = dyn_cast<FuncModuleField>(&field)) { + const Location& body_start = f->func.loc; + const Location& body_end = + f->func.exprs.empty() ? body_start : f->func.exprs.back().loc; + result_ |= validator_.BeginFunctionBody(body_start, func_index++); + + for (auto&& decl : f->func.local_types.decls()) { + result_ |= validator_.OnLocalDecl(body_start, decl.second, decl.first); + } + + ExprVisitor visitor(this); + result_ |= visitor.VisitExprList(const_cast<ExprList&>(f->func.exprs)); + result_ |= validator_.EndFunctionBody(body_end); + } + } + + // Data segment section. + for (const ModuleField& field : module->fields) { + if (auto* f = dyn_cast<DataSegmentModuleField>(&field)) { + result_ |= validator_.OnDataSegment(field.loc, f->data_segment.memory_var, + f->data_segment.kind); + + // Init expr. + if (f->data_segment.kind == SegmentKind::Active) { + Type offset_type = Type::I32; + Index memory_index = module->GetMemoryIndex(f->data_segment.memory_var); + if (memory_index < module->memories.size() && + module->memories[memory_index]->page_limits.is_64) { + offset_type = Type::I64; + } + result_ |= validator_.BeginInitExpr(field.loc, offset_type); + ExprVisitor visitor(this); + result_ |= visitor.VisitExprList( + const_cast<ExprList&>(f->data_segment.offset)); + result_ |= validator_.EndInitExpr(); + } + } + } + + result_ |= validator_.EndModule(); + + return result_; +} + +// Returns the result type of the invoked function, checked by the caller; +// returning nullptr means that another error occured first, so the result type +// should be ignored. +const TypeVector* ScriptValidator::CheckInvoke(const InvokeAction* action) { + const Module* module = script_->GetModule(action->module_var); + if (!module) { + PrintError(&action->loc, "unknown module"); + return nullptr; + } + + const Export* export_ = module->GetExport(action->name); + if (!export_) { + PrintError(&action->loc, "unknown function export \"%s\"", + action->name.c_str()); + return nullptr; + } + + const Func* func = module->GetFunc(export_->var); + if (!func) { + // This error will have already been reported, just skip it. + return nullptr; + } + + size_t actual_args = action->args.size(); + size_t expected_args = func->GetNumParams(); + if (expected_args != actual_args) { + PrintError(&action->loc, + "too %s parameters to function. got %" PRIzd + ", expected %" PRIzd, + actual_args > expected_args ? "many" : "few", actual_args, + expected_args); + return nullptr; + } + for (size_t i = 0; i < actual_args; ++i) { + const Const* const_ = &action->args[i]; + CheckTypeIndex(&const_->loc, const_->type(), func->GetParamType(i), + "invoke", i, "argument"); + } + + return &func->decl.sig.result_types; +} + +Result ScriptValidator::CheckGet(const GetAction* action, Type* out_type) { + const Module* module = script_->GetModule(action->module_var); + if (!module) { + PrintError(&action->loc, "unknown module"); + return Result::Error; + } + + const Export* export_ = module->GetExport(action->name); + if (!export_) { + PrintError(&action->loc, "unknown global export \"%s\"", + action->name.c_str()); + return Result::Error; + } + + const Global* global = module->GetGlobal(export_->var); + if (!global) { + // This error will have already been reported, just skip it. + return Result::Error; + } + + *out_type = global->type; + return Result::Ok; +} + +ScriptValidator::ActionResult ScriptValidator::CheckAction( + const Action* action) { + ActionResult result; + ZeroMemory(result); + + switch (action->type()) { + case ActionType::Invoke: + result.types = CheckInvoke(cast<InvokeAction>(action)); + result.kind = + result.types ? ActionResult::Kind::Types : ActionResult::Kind::Error; + break; + + case ActionType::Get: + if (Succeeded(CheckGet(cast<GetAction>(action), &result.type))) { + result.kind = ActionResult::Kind::Type; + } else { + result.kind = ActionResult::Kind::Error; + } + break; + } + + return result; +} + +void ScriptValidator::CheckCommand(const Command* command) { + switch (command->type) { + case CommandType::Module: { + Validator module_validator(errors_, &cast<ModuleCommand>(command)->module, + options_); + module_validator.CheckModule(); + break; + } + + case CommandType::ScriptModule: { + Validator module_validator( + errors_, &cast<ScriptModuleCommand>(command)->module, options_); + module_validator.CheckModule(); + break; + } + + case CommandType::Action: + // Ignore result type. + CheckAction(cast<ActionCommand>(command)->action.get()); + break; + + case CommandType::Register: + case CommandType::AssertMalformed: + case CommandType::AssertInvalid: + case CommandType::AssertUnlinkable: + case CommandType::AssertUninstantiable: + // Ignore. + break; + + case CommandType::AssertReturn: { + auto* assert_return_command = cast<AssertReturnCommand>(command); + const Action* action = assert_return_command->action.get(); + ActionResult result = CheckAction(action); + const Expectation* expected = assert_return_command->expected.get(); + switch (result.kind) { + case ActionResult::Kind::Types: + CheckExpectationTypes(&action->loc, *result.types, expected, + "action"); + break; + + case ActionResult::Kind::Type: + CheckExpectationTypes(&action->loc, {result.type}, expected, + "action"); + break; + + case ActionResult::Kind::Error: + // Error occurred, don't do any further checks. + break; + } + break; + } + + case CommandType::AssertTrap: + // ignore result type. + CheckAction(cast<AssertTrapCommand>(command)->action.get()); + break; + case CommandType::AssertExhaustion: + // ignore result type. + CheckAction(cast<AssertExhaustionCommand>(command)->action.get()); + break; + case CommandType::AssertException: + // ignore result type. + CheckAction(cast<AssertExceptionCommand>(command)->action.get()); + break; + } +} + +Result ScriptValidator::CheckScript() { + for (const std::unique_ptr<Command>& command : script_->commands) + CheckCommand(command.get()); + return result_; +} + +} // end anonymous namespace + +Result ValidateScript(const Script* script, + Errors* errors, + const ValidateOptions& options) { + ScriptValidator validator(errors, script, options); + + return validator.CheckScript(); +} + +Result ValidateModule(const Module* module, + Errors* errors, + const ValidateOptions& options) { + Validator validator(errors, module, options); + + return validator.CheckModule(); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/wabt.post.js b/third_party/wasm2c/src/wabt.post.js new file mode 100644 index 0000000000..7ed0cb21de --- /dev/null +++ b/third_party/wasm2c/src/wabt.post.js @@ -0,0 +1,387 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var WABT_OK = 0; +var WABT_ERROR = 1; + +/// features and default enabled state +const FEATURES = Object.freeze({ + 'exceptions': false, + 'mutable_globals': true, + 'sat_float_to_int': true, + 'sign_extension': true, + 'simd': true, + 'threads': false, + 'function_references': false, + 'multi_value': true, + 'tail_call': false, + 'bulk_memory': true, + 'reference_types': true, + 'annotations': false, + 'code_metadata': false, + 'gc': false, + 'memory64': false, + 'multi_memory': false, + 'extended_const': false, + 'relaxed_simd': false, +}); + +/// If value is not undefined, return it. Otherwise return default_. +function maybeDefault(value, default_) { + if (value === undefined) { + return default_; + } + return value; +} + +/// Coerce value to boolean if not undefined. Otherwise return default_. +function booleanOrDefault(value, default_) { + return !!maybeDefault(value, default_); +} + +/// Allocate memory in the Module. +function malloc(size) { + var addr = Module._malloc(size); + if (addr == 0) { + throw new Error('out of memory'); + } + return addr; +} + +/// Convert an ArrayBuffer/TypedArray/string into a buffer that can be +/// used by the Module. +function allocateBuffer(buf) { + var addr; + var size; + if (buf instanceof ArrayBuffer) { + size = buf.byteLength; + addr = malloc(size); + (new Uint8Array(HEAP8.buffer, addr, size)).set(new Uint8Array(buf)) + } else if (ArrayBuffer.isView(buf)) { + size = buf.buffer.byteLength; + addr = malloc(size); + (new Uint8Array(HEAP8.buffer, addr, size)).set(buf); + } else if (typeof buf == 'string') { + size = buf.length; + addr = malloc(size); + writeAsciiToMemory(buf, addr, true); // don't null-terminate + } else { + throw new Error('unknown buffer type: ' + buf); + } + return {addr: addr, size: size}; +} + +function allocateCString(s) { + var size = s.length; + var addr = malloc(size); + writeAsciiToMemory(s, addr); + return {addr: addr, size: size}; +} + + +/// Features +function Features(obj) { + this.addr = Module._wabt_new_features(); + for ([f, v] of Object.entries(FEATURES)) { + this[f] = booleanOrDefault(obj[f], v); + } +} +Features.prototype = Object.create(Object.prototype); + +Features.prototype.destroy = function() { + Module._wabt_destroy_features(this.addr); +}; + +Object.keys(FEATURES).forEach(function(feature) { + Object.defineProperty(Features.prototype, feature, { + enumerable: true, + get: function() { + return Module['_wabt_' + feature + '_enabled'](this.addr); + }, + set: function(newValue) { + Module['_wabt_set_' + feature + '_enabled'](this.addr, newValue | 0); + } + }); +}); + + +/// Lexer +function Lexer(filename, buffer, errors) { + this.filenameObj = allocateCString(filename); + this.bufferObj = allocateBuffer(buffer); + this.addr = Module._wabt_new_wast_buffer_lexer( + this.filenameObj.addr, this.bufferObj.addr, this.bufferObj.size, + errors.addr); +} +Lexer.prototype = Object.create(Object.prototype); + +Lexer.prototype.destroy = function() { + Module._wabt_destroy_wast_lexer(this.addr); + Module._free(this.bufferObj.addr); + Module._free(this.filenameObj.addr); +}; + + +/// OutputBuffer +function OutputBuffer(addr) { + this.addr = addr; +} +OutputBuffer.prototype = Object.create(Object.prototype); + +OutputBuffer.prototype.toTypedArray = function() { + if (!this.addr) { + return null; + } + + var addr = Module._wabt_output_buffer_get_data(this.addr); + var size = Module._wabt_output_buffer_get_size(this.addr); + var buffer = new Uint8Array(size); + buffer.set(new Uint8Array(HEAPU8.buffer, addr, size)); + return buffer; +}; + +OutputBuffer.prototype.toString = function() { + if (!this.addr) { + return ''; + } + + var addr = Module._wabt_output_buffer_get_data(this.addr); + var size = Module._wabt_output_buffer_get_size(this.addr); + return UTF8ToString(addr, size); +}; + +OutputBuffer.prototype.destroy = function() { + Module._wabt_destroy_output_buffer(this.addr); +}; + + +/// Errors +function Errors(kind) { + this.kind = kind; + this.addr = Module._wabt_new_errors(); +} +Errors.prototype = Object.create(Object.prototype); + +Errors.prototype.format = function() { + var buffer; + switch (this.kind) { + case 'text': + buffer = new OutputBuffer( + Module._wabt_format_text_errors(this.addr, this.lexer.addr)); + break; + case 'binary': + buffer = new OutputBuffer(Module._wabt_format_binary_errors(this.addr)); + break; + default: + throw new Error('Invalid Errors kind: ' + this.kind); + } + var message = buffer.toString(); + buffer.destroy(); + return message; +}; + +Errors.prototype.destroy = function() { + Module._wabt_destroy_errors(this.addr); + if (this.lexer) { + this.lexer.destroy(); + } +}; + + +/// parseWat +function parseWat(filename, buffer, options) { + var errors = new Errors('text'); + var lexer = new Lexer(filename, buffer, errors); + errors.lexer = lexer; + var features = new Features(options || {}); + + try { + var parseResult_addr = + Module._wabt_parse_wat(lexer.addr, features.addr, errors.addr); + + var result = Module._wabt_parse_wat_result_get_result(parseResult_addr); + if (result !== WABT_OK) { + throw new Error('parseWat failed:\n' + errors.format()); + } + + var module_addr = + Module._wabt_parse_wat_result_release_module(parseResult_addr); + var result = new WasmModule(module_addr, errors); + // Clear errors so it isn't destroyed below. + errors = null; + return result; + } finally { + Module._wabt_destroy_parse_wat_result(parseResult_addr); + features.destroy(); + if (errors) { + errors.destroy(); + } + } +} + + +// readWasm +function readWasm(buffer, options) { + var bufferObj = allocateBuffer(buffer); + var errors = new Errors('binary'); + var readDebugNames = booleanOrDefault(options.readDebugNames, false); + var features = new Features(options); + + try { + var readBinaryResult_addr = Module._wabt_read_binary( + bufferObj.addr, bufferObj.size, readDebugNames, features.addr, + errors.addr); + + var result = + Module._wabt_read_binary_result_get_result(readBinaryResult_addr); + if (result !== WABT_OK) { + throw new Error('readWasm failed:\n' + errors.format()); + } + + var module_addr = + Module._wabt_read_binary_result_release_module(readBinaryResult_addr); + var result = new WasmModule(module_addr, errors); + // Clear errors so it isn't destroyed below. + errors = null; + return result; + } finally { + Module._wabt_destroy_read_binary_result(readBinaryResult_addr); + features.destroy(); + if (errors) { + errors.destroy(); + } + Module._free(bufferObj.addr); + } +} + + +// WasmModule (can't call it Module because emscripten has claimed it.) +function WasmModule(module_addr, errors) { + this.module_addr = module_addr; + this.errors = errors; +} +WasmModule.prototype = Object.create(Object.prototype); + +WasmModule.prototype.validate = function(options) { + var features = new Features(options || {}); + try { + var result = Module._wabt_validate_module( + this.module_addr, features.addr, this.errors.addr); + if (result !== WABT_OK) { + throw new Error('validate failed:\n' + this.errors.format()); + } + } finally { + features.destroy(); + } +}; + +WasmModule.prototype.resolveNames = function() { + // No-op, this is now part of text parsing. +}; + +WasmModule.prototype.generateNames = function() { + var result = Module._wabt_generate_names_module(this.module_addr); + if (result !== WABT_OK) { + throw new Error('generateNames failed.'); + } +}; + +WasmModule.prototype.applyNames = function() { + var result = Module._wabt_apply_names_module(this.module_addr); + if (result !== WABT_OK) { + throw new Error('applyNames failed.'); + } +}; + +WasmModule.prototype.toText = function(options) { + var foldExprs = booleanOrDefault(options.foldExprs, false); + var inlineExport = booleanOrDefault(options.inlineExport, false); + + var writeModuleResult_addr = + Module._wabt_write_text_module(this.module_addr, foldExprs, inlineExport); + + var result = + Module._wabt_write_module_result_get_result(writeModuleResult_addr); + + try { + if (result !== WABT_OK) { + throw new Error('toText failed.'); + } + + var outputBuffer = + new OutputBuffer(Module._wabt_write_module_result_release_output_buffer( + writeModuleResult_addr)); + + return outputBuffer.toString(); + + } finally { + if (outputBuffer) { + outputBuffer.destroy(); + } + Module._wabt_destroy_write_module_result(writeModuleResult_addr); + } +}; + +WasmModule.prototype.toBinary = function(options) { + var log = booleanOrDefault(options.log, false); + var canonicalize_lebs = booleanOrDefault(options.canonicalize_lebs, true); + var relocatable = booleanOrDefault(options.relocatable, false); + var write_debug_names = booleanOrDefault(options.write_debug_names, false); + + var writeModuleResult_addr = Module._wabt_write_binary_module( + this.module_addr, log, canonicalize_lebs, relocatable, write_debug_names); + + var result = + Module._wabt_write_module_result_get_result(writeModuleResult_addr); + + try { + if (result !== WABT_OK) { + throw new Error('toBinary failed.'); + } + + var binaryOutputBuffer = + new OutputBuffer(Module._wabt_write_module_result_release_output_buffer( + writeModuleResult_addr)); + var logOutputBuffer = new OutputBuffer( + Module._wabt_write_module_result_release_log_output_buffer( + writeModuleResult_addr)); + + return { + buffer: binaryOutputBuffer.toTypedArray(), + log: logOutputBuffer.toString() + }; + + } finally { + if (binaryOutputBuffer) { + binaryOutputBuffer.destroy(); + } + if (logOutputBuffer) { + logOutputBuffer.destroy(); + } + Module._wabt_destroy_write_module_result(writeModuleResult_addr); + } +}; + +WasmModule.prototype.destroy = function() { + Module._wabt_destroy_module(this.module_addr); + if (this.errors) { + this.errors.destroy(); + } +}; + +Module['parseWat'] = parseWat; +Module['readWasm'] = readWasm; +Module['FEATURES'] = FEATURES; diff --git a/third_party/wasm2c/src/wast-lexer.cc b/third_party/wasm2c/src/wast-lexer.cc new file mode 100644 index 0000000000..1f89c3ff47 --- /dev/null +++ b/third_party/wasm2c/src/wast-lexer.cc @@ -0,0 +1,626 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/wast-lexer.h" + +#include <cassert> +#include <cstdio> + +#include "wabt/config.h" + +#include "wabt/lexer-source.h" + +#define ERROR(...) Error(GetLocation(), __VA_ARGS__) + +namespace wabt { + +namespace { + +#include "prebuilt/lexer-keywords.cc" + +} // namespace + +WastLexer::WastLexer(std::unique_ptr<LexerSource> source, + std::string_view filename, + Errors* errors) + : source_(std::move(source)), + filename_(filename), + line_(1), + buffer_(static_cast<const char*>(source_->data())), + buffer_end_(buffer_ + source_->size()), + line_start_(buffer_), + token_start_(buffer_), + cursor_(buffer_), + errors_(errors) {} + +// static +std::unique_ptr<WastLexer> WastLexer::CreateBufferLexer( + std::string_view filename, + const void* data, + size_t size, + Errors* errors) { + return std::make_unique<WastLexer>(std::make_unique<LexerSource>(data, size), + filename, errors); +} + +Token WastLexer::GetToken() { + while (true) { + token_start_ = cursor_; + switch (PeekChar()) { + case kEof: + return BareToken(TokenType::Eof); + + case '(': + if (MatchString("(;")) { + if (ReadBlockComment()) { + continue; + } + return BareToken(TokenType::Eof); + } else if (MatchString("(@")) { + GetIdChars(); + // offset=2 to skip the "(@" prefix + return TextToken(TokenType::LparAnn, 2); + } else { + ReadChar(); + return BareToken(TokenType::Lpar); + } + break; + + case ')': + ReadChar(); + return BareToken(TokenType::Rpar); + + case ';': + if (MatchString(";;")) { + if (ReadLineComment()) { + continue; + } + return BareToken(TokenType::Eof); + } else { + ReadChar(); + ERROR("unexpected char"); + continue; + } + break; + + case ' ': + case '\t': + case '\r': + case '\n': + ReadWhitespace(); + continue; + + case '"': + return GetStringToken(); + + case '+': + case '-': + ReadChar(); + switch (PeekChar()) { + case 'i': + return GetInfToken(); + + case 'n': + return GetNanToken(); + + case '0': + return MatchString("0x") ? GetHexNumberToken(TokenType::Int) + : GetNumberToken(TokenType::Int); + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return GetNumberToken(TokenType::Int); + + default: + return GetReservedToken(); + } + break; + + case '0': + return MatchString("0x") ? GetHexNumberToken(TokenType::Nat) + : GetNumberToken(TokenType::Nat); + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return GetNumberToken(TokenType::Nat); + + case '$': + return GetIdChars(); // Initial $ is idchar, so this produces id token + + case 'a': + return GetNameEqNumToken("align=", TokenType::AlignEqNat); + + case 'i': + return GetInfToken(); + + case 'n': + return GetNanToken(); + + case 'o': + return GetNameEqNumToken("offset=", TokenType::OffsetEqNat); + + default: + if (IsKeyword(PeekChar())) { + return GetKeywordToken(); + } else if (IsIdChar(PeekChar())) { + return GetReservedToken(); + } else { + ReadChar(); + ERROR("unexpected char"); + continue; + } + } + } +} + +Location WastLexer::GetLocation() { + auto column = [=](const char* p) { + return std::max(1, static_cast<int>(p - line_start_ + 1)); + }; + return Location(filename_, line_, column(token_start_), column(cursor_)); +} + +std::string_view WastLexer::GetText(size_t offset) { + // Bounds checks are necessary because token_start may have been moved + // (e.g. if GetStringToken found a newline and reset token_start to + // point at it). + + if (token_start_ + offset >= buffer_end_) + return {}; + + if (cursor_ <= token_start_ + offset) + return {}; + + return std::string_view(token_start_ + offset, + (cursor_ - token_start_) - offset); +} + +Token WastLexer::BareToken(TokenType token_type) { + return Token(GetLocation(), token_type); +} + +Token WastLexer::LiteralToken(TokenType token_type, LiteralType literal_type) { + return Token(GetLocation(), token_type, Literal(literal_type, GetText())); +} + +Token WastLexer::TextToken(TokenType token_type, size_t offset) { + return Token(GetLocation(), token_type, GetText(offset)); +} + +int WastLexer::PeekChar() { + return cursor_ < buffer_end_ ? static_cast<uint8_t>(*cursor_) : kEof; +} + +int WastLexer::ReadChar() { + return cursor_ < buffer_end_ ? static_cast<uint8_t>(*cursor_++) : kEof; +} + +bool WastLexer::MatchChar(char c) { + if (PeekChar() == c) { + ReadChar(); + return true; + } + return false; +} + +bool WastLexer::MatchString(std::string_view s) { + const char* saved_cursor = cursor_; + for (char c : s) { + if (ReadChar() != c) { + cursor_ = saved_cursor; + return false; + } + } + return true; +} + +void WastLexer::Newline() { + line_++; + line_start_ = cursor_; +} + +bool WastLexer::ReadBlockComment() { + int nesting = 1; + while (true) { + switch (ReadChar()) { + case kEof: + ERROR("EOF in block comment"); + return false; + + case ';': + if (MatchChar(')') && --nesting == 0) { + return true; + } + break; + + case '(': + if (MatchChar(';')) { + nesting++; + } + break; + + case '\n': + Newline(); + break; + } + } +} + +bool WastLexer::ReadLineComment() { + while (true) { + switch (ReadChar()) { + case kEof: + return false; + + case '\n': + Newline(); + return true; + } + } +} + +void WastLexer::ReadWhitespace() { + while (true) { + switch (PeekChar()) { + case ' ': + case '\t': + case '\r': + ReadChar(); + break; + + case '\n': + ReadChar(); + Newline(); + break; + + default: + return; + } + } +} + +Token WastLexer::GetStringToken() { + const char* saved_token_start = token_start_; + bool has_error = false; + bool in_string = true; + ReadChar(); + while (in_string) { + switch (ReadChar()) { + case kEof: + return BareToken(TokenType::Eof); + + case '\n': + token_start_ = cursor_ - 1; + ERROR("newline in string"); + has_error = true; + Newline(); + continue; + + case '"': + if (PeekChar() == '"') { + ERROR("invalid string token"); + has_error = true; + } + in_string = false; + break; + + case '\\': { + switch (ReadChar()) { + case 't': + case 'n': + case 'r': + case '"': + case '\'': + case '\\': + // Valid escape. + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': // Hex byte escape. + if (IsHexDigit(PeekChar())) { + ReadChar(); + } else { + token_start_ = cursor_ - 2; + goto error; + } + break; + + case 'u': { + token_start_ = cursor_ - 2; + if (ReadChar() != '{') { + goto error; + } + + // Value must be a valid unicode scalar value. + uint32_t digit; + uint32_t scalar_value = 0; + + while (IsHexDigit(PeekChar())) { + ParseHexdigit(*cursor_++, &digit); + + scalar_value = (scalar_value << 4) | digit; + // Maximum value of a unicode code point. + if (scalar_value >= 0x110000) { + goto error; + } + } + + if (PeekChar() != '}') { + goto error; + } + + // Scalars between 0xd800 and 0xdfff are not allowed. + if ((scalar_value >= 0xd800 && scalar_value < 0xe000) || + token_start_ == cursor_ - 3) { + ReadChar(); + goto error; + } + break; + } + + default: + token_start_ = cursor_ - 2; + goto error; + + error: + ERROR("bad escape \"%.*s\"", + static_cast<int>(cursor_ - token_start_), token_start_); + has_error = true; + break; + } + break; + } + } + } + token_start_ = saved_token_start; + if (has_error) { + return Token(GetLocation(), TokenType::Invalid); + } + + return TextToken(TokenType::Text); +} + +// static +bool WastLexer::IsCharClass(int c, CharClass bit) { + // Generated by the following python script: + // + // def Range(c, lo, hi): return lo <= c <= hi + // def IsDigit(c): return Range(c, '0', '9') + // def IsHexDigit(c): return IsDigit(c) or Range(c.lower(), 'a', 'f') + // def IsKeyword(c): return Range(c, 'a', 'z') + // def IsIdChar(c): return Range(c, '!', '~') and c not in '"(),;[]{}' + // + // print ([0] + [ + // (8 if IsDigit(c) else 0) | + // (4 if IsHexDigit(c) else 0) | + // (2 if IsKeyword(c) else 0) | + // (1 if IsIdChar(c) else 0) + // for c in map(chr, range(0, 127)) + // ]) + static const char kCharClasses[257] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, + 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 1, 0, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, + 1, 1, 1, 7, 7, 7, 7, 7, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 1, 0, 1, + }; + + assert(c >= -1 && c < 256); + return (kCharClasses[c + 1] & static_cast<int>(bit)) != 0; +} + +bool WastLexer::ReadNum() { + if (IsDigit(PeekChar())) { + ReadChar(); + return MatchChar('_') || IsDigit(PeekChar()) ? ReadNum() : true; + } + return false; +} + +bool WastLexer::ReadHexNum() { + if (IsHexDigit(PeekChar())) { + ReadChar(); + return MatchChar('_') || IsHexDigit(PeekChar()) ? ReadHexNum() : true; + } + return false; +} + +WastLexer::ReservedChars WastLexer::ReadReservedChars() { + ReservedChars ret{ReservedChars::None}; + while (true) { + auto peek = PeekChar(); + if (IsIdChar(peek)) { + ReadChar(); + if (ret == ReservedChars::None) { + ret = ReservedChars::Id; + } + } else if (peek == '"') { + GetStringToken(); + ret = ReservedChars::Some; + } else { + break; + } + } + return ret; +} + +void WastLexer::ReadSign() { + if (PeekChar() == '+' || PeekChar() == '-') { + ReadChar(); + } +} + +Token WastLexer::GetNumberToken(TokenType token_type) { + if (ReadNum()) { + if (MatchChar('.')) { + token_type = TokenType::Float; + if (IsDigit(PeekChar()) && !ReadNum()) { + return GetReservedToken(); + } + } + if (MatchChar('e') || MatchChar('E')) { + token_type = TokenType::Float; + ReadSign(); + if (!ReadNum()) { + return GetReservedToken(); + } + } + if (NoTrailingReservedChars()) { + if (token_type == TokenType::Float) { + return LiteralToken(token_type, LiteralType::Float); + } else { + return LiteralToken(token_type, LiteralType::Int); + } + } + } + return GetReservedToken(); +} + +Token WastLexer::GetHexNumberToken(TokenType token_type) { + if (ReadHexNum()) { + if (MatchChar('.')) { + token_type = TokenType::Float; + if (IsHexDigit(PeekChar()) && !ReadHexNum()) { + return GetReservedToken(); + } + } + if (MatchChar('p') || MatchChar('P')) { + token_type = TokenType::Float; + ReadSign(); + if (!ReadNum()) { + return GetReservedToken(); + } + } + if (NoTrailingReservedChars()) { + if (token_type == TokenType::Float) { + return LiteralToken(token_type, LiteralType::Hexfloat); + } else { + return LiteralToken(token_type, LiteralType::Int); + } + } + } + return GetReservedToken(); +} + +Token WastLexer::GetInfToken() { + if (MatchString("inf")) { + if (NoTrailingReservedChars()) { + return LiteralToken(TokenType::Float, LiteralType::Infinity); + } + return GetReservedToken(); + } + return GetKeywordToken(); +} + +Token WastLexer::GetNanToken() { + if (MatchString("nan")) { + if (MatchChar(':')) { + if (MatchString("0x") && ReadHexNum() && NoTrailingReservedChars()) { + return LiteralToken(TokenType::Float, LiteralType::Nan); + } + } else if (NoTrailingReservedChars()) { + return LiteralToken(TokenType::Float, LiteralType::Nan); + } + } + return GetKeywordToken(); +} + +Token WastLexer::GetNameEqNumToken(std::string_view name, + TokenType token_type) { + if (MatchString(name)) { + if (MatchString("0x")) { + if (ReadHexNum() && NoTrailingReservedChars()) { + return TextToken(token_type, name.size()); + } + } else if (ReadNum() && NoTrailingReservedChars()) { + return TextToken(token_type, name.size()); + } + } + return GetKeywordToken(); +} + +Token WastLexer::GetIdChars() { + if (ReadReservedChars() == ReservedChars::Id) { + return TextToken(TokenType::Var); + } + + return TextToken(TokenType::Reserved); +} + +Token WastLexer::GetKeywordToken() { + ReadReservedChars(); + TokenInfo* info = + Perfect_Hash::InWordSet(token_start_, cursor_ - token_start_); + if (!info) { + return TextToken(TokenType::Reserved); + } + if (IsTokenTypeBare(info->token_type)) { + return BareToken(info->token_type); + } else if (IsTokenTypeType(info->token_type) || + IsTokenTypeRefKind(info->token_type)) { + return Token(GetLocation(), info->token_type, info->value_type); + } else { + assert(IsTokenTypeOpcode(info->token_type)); + return Token(GetLocation(), info->token_type, info->opcode); + } +} + +Token WastLexer::GetReservedToken() { + ReadReservedChars(); + return TextToken(TokenType::Reserved); +} + +void WastLexer::Error(Location loc, const char* format, ...) { + WABT_SNPRINTF_ALLOCA(buffer, length, format); + errors_->emplace_back(ErrorLevel::Error, loc, buffer); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/wast-parser.cc b/third_party/wasm2c/src/wast-parser.cc new file mode 100644 index 0000000000..a596f018dc --- /dev/null +++ b/third_party/wasm2c/src/wast-parser.cc @@ -0,0 +1,3616 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/wast-parser.h" + +#include "wabt/binary-reader-ir.h" +#include "wabt/binary-reader.h" +#include "wabt/cast.h" +#include "wabt/expr-visitor.h" +#include "wabt/resolve-names.h" +#include "wabt/stream.h" +#include "wabt/utf8.h" +#include "wabt/validator.h" + +#define WABT_TRACING 0 +#include "wabt/tracing.h" + +#define EXPECT(token_type) CHECK_RESULT(Expect(TokenType::token_type)) + +namespace wabt { + +namespace { + +static const size_t kMaxErrorTokenLength = 80; + +bool IsPowerOfTwo(uint32_t x) { + return x && ((x & (x - 1)) == 0); +} + +template <typename OutputIter> +void RemoveEscapes(std::string_view text, OutputIter dest) { + // Remove surrounding quotes; if any. This may be empty if the string was + // invalid (e.g. if it contained a bad escape sequence). + if (text.size() <= 2) { + return; + } + + text = text.substr(1, text.size() - 2); + + const char* src = text.data(); + const char* end = text.data() + text.size(); + + while (src < end) { + if (*src == '\\') { + src++; + switch (*src) { + case 'n': + *dest++ = '\n'; + break; + case 'r': + *dest++ = '\r'; + break; + case 't': + *dest++ = '\t'; + break; + case '\\': + *dest++ = '\\'; + break; + case '\'': + *dest++ = '\''; + break; + case '\"': + *dest++ = '\"'; + break; + case 'u': { + // The string should be validated already, + // so this must be a valid unicode escape sequence. + uint32_t digit; + uint32_t scalar_value = 0; + + // Skip u and { characters. + src += 2; + + do { + if (Succeeded(ParseHexdigit(src[0], &digit))) { + scalar_value = (scalar_value << 4) | digit; + } else { + assert(0); + } + src++; + } while (src[0] != '}'); + + // Maximum value of a unicode scalar value + assert(scalar_value < 0x110000); + + // Encode the unicode scalar value as UTF8 sequence + if (scalar_value < 0x80) { + *dest++ = static_cast<uint8_t>(scalar_value); + } else { + if (scalar_value < 0x800) { + *dest++ = static_cast<uint8_t>(0xc0 | (scalar_value >> 6)); + } else { + if (scalar_value < 0x10000) { + *dest++ = static_cast<uint8_t>(0xe0 | (scalar_value >> 12)); + } else { + *dest++ = static_cast<uint8_t>(0xf0 | (scalar_value >> 18)); + *dest++ = + static_cast<uint8_t>(0x80 | ((scalar_value >> 12) & 0x3f)); + } + + *dest++ = + static_cast<uint8_t>(0x80 | ((scalar_value >> 6) & 0x3f)); + } + + *dest++ = static_cast<uint8_t>(0x80 | (scalar_value & 0x3f)); + } + break; + } + default: { + // The string should be validated already, so we know this is a hex + // sequence. + uint32_t hi; + uint32_t lo; + if (Succeeded(ParseHexdigit(src[0], &hi)) && + Succeeded(ParseHexdigit(src[1], &lo))) { + *dest++ = (hi << 4) | lo; + } else { + assert(0); + } + src++; + break; + } + } + src++; + } else { + *dest++ = *src++; + } + } +} + +using TextVector = std::vector<std::string_view>; + +template <typename OutputIter> +void RemoveEscapes(const TextVector& texts, OutputIter out) { + for (std::string_view text : texts) + RemoveEscapes(text, out); +} + +bool IsPlainInstr(TokenType token_type) { + switch (token_type) { + case TokenType::Unreachable: + case TokenType::Nop: + case TokenType::Drop: + case TokenType::Select: + case TokenType::Br: + case TokenType::BrIf: + case TokenType::BrTable: + case TokenType::Return: + case TokenType::ReturnCall: + case TokenType::ReturnCallIndirect: + case TokenType::Call: + case TokenType::CallIndirect: + case TokenType::CallRef: + case TokenType::LocalGet: + case TokenType::LocalSet: + case TokenType::LocalTee: + case TokenType::GlobalGet: + case TokenType::GlobalSet: + case TokenType::Load: + case TokenType::Store: + case TokenType::Const: + case TokenType::Unary: + case TokenType::Binary: + case TokenType::Compare: + case TokenType::Convert: + case TokenType::MemoryCopy: + case TokenType::DataDrop: + case TokenType::MemoryFill: + case TokenType::MemoryGrow: + case TokenType::MemoryInit: + case TokenType::MemorySize: + case TokenType::TableCopy: + case TokenType::ElemDrop: + case TokenType::TableInit: + case TokenType::TableGet: + case TokenType::TableSet: + case TokenType::TableGrow: + case TokenType::TableSize: + case TokenType::TableFill: + case TokenType::Throw: + case TokenType::Rethrow: + case TokenType::RefFunc: + case TokenType::RefNull: + case TokenType::RefIsNull: + case TokenType::AtomicLoad: + case TokenType::AtomicStore: + case TokenType::AtomicRmw: + case TokenType::AtomicRmwCmpxchg: + case TokenType::AtomicNotify: + case TokenType::AtomicFence: + case TokenType::AtomicWait: + case TokenType::Ternary: + case TokenType::SimdLaneOp: + case TokenType::SimdLoadLane: + case TokenType::SimdStoreLane: + case TokenType::SimdShuffleOp: + return true; + default: + return false; + } +} + +bool IsBlockInstr(TokenType token_type) { + switch (token_type) { + case TokenType::Block: + case TokenType::Loop: + case TokenType::If: + case TokenType::Try: + return true; + default: + return false; + } +} + +bool IsPlainOrBlockInstr(TokenType token_type) { + return IsPlainInstr(token_type) || IsBlockInstr(token_type); +} + +bool IsExpr(TokenTypePair pair) { + return pair[0] == TokenType::Lpar && IsPlainOrBlockInstr(pair[1]); +} + +bool IsInstr(TokenTypePair pair) { + return IsPlainOrBlockInstr(pair[0]) || IsExpr(pair); +} + +bool IsLparAnn(TokenTypePair pair) { + return pair[0] == TokenType::LparAnn; +} + +bool IsCatch(TokenType token_type) { + return token_type == TokenType::Catch || token_type == TokenType::CatchAll; +} + +bool IsModuleField(TokenTypePair pair) { + if (pair[0] != TokenType::Lpar) { + return false; + } + + switch (pair[1]) { + case TokenType::Data: + case TokenType::Elem: + case TokenType::Tag: + case TokenType::Export: + case TokenType::Func: + case TokenType::Type: + case TokenType::Global: + case TokenType::Import: + case TokenType::Memory: + case TokenType::Start: + case TokenType::Table: + return true; + default: + return false; + } +} + +bool IsCommand(TokenTypePair pair) { + if (pair[0] != TokenType::Lpar) { + return false; + } + + switch (pair[1]) { + case TokenType::AssertException: + case TokenType::AssertExhaustion: + case TokenType::AssertInvalid: + case TokenType::AssertMalformed: + case TokenType::AssertReturn: + case TokenType::AssertTrap: + case TokenType::AssertUnlinkable: + case TokenType::Get: + case TokenType::Invoke: + case TokenType::Input: + case TokenType::Module: + case TokenType::Output: + case TokenType::Register: + return true; + default: + return false; + } +} + +bool IsEmptySignature(const FuncSignature& sig) { + return sig.result_types.empty() && sig.param_types.empty(); +} + +bool ResolveFuncTypeWithEmptySignature(const Module& module, + FuncDeclaration* decl) { + // Resolve func type variables where the signature was not specified + // explicitly, e.g.: (func (type 1) ...) + if (decl->has_func_type && IsEmptySignature(decl->sig)) { + const FuncType* func_type = module.GetFuncType(decl->type_var); + if (func_type) { + decl->sig = func_type->sig; + return true; + } + } + return false; +} + +void ResolveTypeName( + const Module& module, + Type& type, + Index index, + const std::unordered_map<uint32_t, std::string>& bindings) { + if (type != Type::Reference || type.GetReferenceIndex() != kInvalidIndex) { + return; + } + + const auto name_iterator = bindings.find(index); + assert(name_iterator != bindings.cend()); + const auto type_index = module.type_bindings.FindIndex(name_iterator->second); + assert(type_index != kInvalidIndex); + type = Type(Type::Reference, type_index); +} + +void ResolveTypeNames(const Module& module, FuncDeclaration* decl) { + assert(decl); + auto& signature = decl->sig; + + for (uint32_t param_index = 0; param_index < signature.GetNumParams(); + ++param_index) { + ResolveTypeName(module, signature.param_types[param_index], param_index, + signature.param_type_names); + } + + for (uint32_t result_index = 0; result_index < signature.GetNumResults(); + ++result_index) { + ResolveTypeName(module, signature.result_types[result_index], result_index, + signature.result_type_names); + } +} + +void ResolveImplicitlyDefinedFunctionType(const Location& loc, + Module* module, + const FuncDeclaration& decl) { + // Resolve implicitly defined function types, e.g.: (func (param i32) ...) + if (!decl.has_func_type) { + Index func_type_index = module->GetFuncTypeIndex(decl.sig); + if (func_type_index == kInvalidIndex) { + auto func_type_field = std::make_unique<TypeModuleField>(loc); + auto func_type = std::make_unique<FuncType>(); + func_type->sig = decl.sig; + func_type_field->type = std::move(func_type); + module->AppendField(std::move(func_type_field)); + } + } +} + +Result CheckTypeIndex(const Location& loc, + Type actual, + Type expected, + const char* desc, + Index index, + const char* index_kind, + Errors* errors) { + // Types must match exactly; no subtyping should be allowed. + if (actual != expected) { + errors->emplace_back( + ErrorLevel::Error, loc, + StringPrintf("type mismatch for %s %" PRIindex + " of %s. got %s, expected %s", + index_kind, index, desc, actual.GetName().c_str(), + expected.GetName().c_str())); + return Result::Error; + } + return Result::Ok; +} + +Result CheckTypes(const Location& loc, + const TypeVector& actual, + const TypeVector& expected, + const char* desc, + const char* index_kind, + Errors* errors) { + Result result = Result::Ok; + if (actual.size() == expected.size()) { + for (size_t i = 0; i < actual.size(); ++i) { + result |= CheckTypeIndex(loc, actual[i], expected[i], desc, i, index_kind, + errors); + } + } else { + errors->emplace_back( + ErrorLevel::Error, loc, + StringPrintf("expected %" PRIzd " %ss, got %" PRIzd, expected.size(), + index_kind, actual.size())); + result = Result::Error; + } + return result; +} + +Result CheckFuncTypeVarMatchesExplicit(const Location& loc, + const Module& module, + const FuncDeclaration& decl, + Errors* errors) { + Result result = Result::Ok; + if (decl.has_func_type) { + const FuncType* func_type = module.GetFuncType(decl.type_var); + if (func_type) { + result |= + CheckTypes(loc, decl.sig.result_types, func_type->sig.result_types, + "function", "result", errors); + result |= + CheckTypes(loc, decl.sig.param_types, func_type->sig.param_types, + "function", "argument", errors); + } else if (!(decl.sig.param_types.empty() && + decl.sig.result_types.empty())) { + // We want to check whether the function type at the explicit index + // matches the given param and result types. If they were omitted then + // they'll be resolved automatically (see + // ResolveFuncTypeWithEmptySignature), but if they are provided then we + // have to check. If we get here then the type var is invalid, so we + // can't check whether they match. + if (decl.type_var.is_index()) { + errors->emplace_back(ErrorLevel::Error, loc, + StringPrintf("invalid func type index %" PRIindex, + decl.type_var.index())); + } else { + errors->emplace_back(ErrorLevel::Error, loc, + StringPrintf("expected func type identifier %s", + decl.type_var.name().c_str())); + } + result = Result::Error; + } + } + return result; +} + +bool IsInlinableFuncSignature(const FuncSignature& sig) { + return sig.GetNumParams() == 0 && sig.GetNumResults() <= 1; +} + +class ResolveFuncTypesExprVisitorDelegate : public ExprVisitor::DelegateNop { + public: + explicit ResolveFuncTypesExprVisitorDelegate(Module* module, Errors* errors) + : module_(module), errors_(errors) {} + + void ResolveBlockDeclaration(const Location& loc, BlockDeclaration* decl) { + ResolveTypeNames(*module_, decl); + ResolveFuncTypeWithEmptySignature(*module_, decl); + if (!IsInlinableFuncSignature(decl->sig)) { + ResolveImplicitlyDefinedFunctionType(loc, module_, *decl); + } + } + + Result BeginBlockExpr(BlockExpr* expr) override { + ResolveBlockDeclaration(expr->loc, &expr->block.decl); + return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_, + expr->block.decl, errors_); + } + + Result BeginIfExpr(IfExpr* expr) override { + ResolveBlockDeclaration(expr->loc, &expr->true_.decl); + return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_, + expr->true_.decl, errors_); + } + + Result BeginLoopExpr(LoopExpr* expr) override { + ResolveBlockDeclaration(expr->loc, &expr->block.decl); + return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_, + expr->block.decl, errors_); + } + + Result BeginTryExpr(TryExpr* expr) override { + ResolveBlockDeclaration(expr->loc, &expr->block.decl); + return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_, + expr->block.decl, errors_); + } + + Result OnCallIndirectExpr(CallIndirectExpr* expr) override { + ResolveFuncTypeWithEmptySignature(*module_, &expr->decl); + ResolveImplicitlyDefinedFunctionType(expr->loc, module_, expr->decl); + return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_, expr->decl, + errors_); + } + + Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr* expr) override { + ResolveFuncTypeWithEmptySignature(*module_, &expr->decl); + ResolveImplicitlyDefinedFunctionType(expr->loc, module_, expr->decl); + return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_, expr->decl, + errors_); + } + + private: + Module* module_; + Errors* errors_; +}; + +Result ResolveFuncTypes(Module* module, Errors* errors) { + Result result = Result::Ok; + for (ModuleField& field : module->fields) { + Func* func = nullptr; + FuncDeclaration* decl = nullptr; + if (auto* func_field = dyn_cast<FuncModuleField>(&field)) { + func = &func_field->func; + decl = &func->decl; + } else if (auto* tag_field = dyn_cast<TagModuleField>(&field)) { + decl = &tag_field->tag.decl; + } else if (auto* import_field = dyn_cast<ImportModuleField>(&field)) { + if (auto* func_import = + dyn_cast<FuncImport>(import_field->import.get())) { + // Only check the declaration, not the function itself, since it is an + // import. + decl = &func_import->func.decl; + } else if (auto* tag_import = + dyn_cast<TagImport>(import_field->import.get())) { + decl = &tag_import->tag.decl; + } else { + continue; + } + } else { + continue; + } + + bool has_func_type_and_empty_signature = false; + + if (decl) { + ResolveTypeNames(*module, decl); + has_func_type_and_empty_signature = + ResolveFuncTypeWithEmptySignature(*module, decl); + ResolveImplicitlyDefinedFunctionType(field.loc, module, *decl); + result |= + CheckFuncTypeVarMatchesExplicit(field.loc, *module, *decl, errors); + } + + if (func) { + if (has_func_type_and_empty_signature) { + // The call to ResolveFuncTypeWithEmptySignature may have updated the + // function signature so there are parameters. Since parameters and + // local variables share the same index space, we need to increment the + // local indexes bound to a given name by the number of parameters in + // the function. + for (auto& [name, binding] : func->bindings) { + binding.index += func->GetNumParams(); + } + } + + ResolveFuncTypesExprVisitorDelegate delegate(module, errors); + ExprVisitor visitor(&delegate); + result |= visitor.VisitFunc(func); + } + } + return result; +} + +void AppendInlineExportFields(Module* module, + ModuleFieldList* fields, + Index index) { + Location last_field_loc = module->fields.back().loc; + + for (ModuleField& field : *fields) { + auto* export_field = cast<ExportModuleField>(&field); + export_field->export_.var = Var(index, last_field_loc); + } + + module->AppendFields(fields); +} + +} // End of anonymous namespace + +WastParser::WastParser(WastLexer* lexer, + Errors* errors, + WastParseOptions* options) + : lexer_(lexer), errors_(errors), options_(options) {} + +void WastParser::Error(Location loc, const char* format, ...) { + WABT_SNPRINTF_ALLOCA(buffer, length, format); + errors_->emplace_back(ErrorLevel::Error, loc, buffer); +} + +Token WastParser::GetToken() { + if (tokens_.empty()) { + tokens_.push_back(lexer_->GetToken()); + } + return tokens_.front(); +} + +Location WastParser::GetLocation() { + return GetToken().loc; +} + +TokenType WastParser::Peek(size_t n) { + while (tokens_.size() <= n) { + Token cur = lexer_->GetToken(); + if (cur.token_type() != TokenType::LparAnn) { + tokens_.push_back(cur); + } else { + // Custom annotation. For now, discard until matching Rpar, unless it is + // a code metadata annotation. In that case, we know how to parse it. + if (!options_->features.annotations_enabled()) { + Error(cur.loc, "annotations not enabled: %s", cur.to_string().c_str()); + tokens_.push_back(Token(cur.loc, TokenType::Invalid)); + continue; + } + if (options_->features.code_metadata_enabled() && + cur.text().find("metadata.code.") == 0) { + tokens_.push_back(cur); + continue; + } + int indent = 1; + while (indent > 0) { + cur = lexer_->GetToken(); + switch (cur.token_type()) { + case TokenType::Lpar: + case TokenType::LparAnn: + indent++; + break; + + case TokenType::Rpar: + indent--; + break; + + case TokenType::Eof: + indent = 0; + Error(cur.loc, "unterminated annotation"); + break; + + default: + break; + } + } + } + } + return tokens_.at(n).token_type(); +} + +TokenTypePair WastParser::PeekPair() { + return TokenTypePair{{Peek(), Peek(1)}}; +} + +bool WastParser::PeekMatch(TokenType type, size_t n) { + return Peek(n) == type; +} + +bool WastParser::PeekMatchLpar(TokenType type) { + return Peek() == TokenType::Lpar && Peek(1) == type; +} + +bool WastParser::PeekMatchExpr() { + return IsExpr(PeekPair()); +} + +bool WastParser::PeekMatchRefType() { + return options_->features.function_references_enabled() && + PeekMatchLpar(TokenType::Ref); +} + +bool WastParser::Match(TokenType type) { + if (PeekMatch(type)) { + Consume(); + return true; + } + return false; +} + +bool WastParser::MatchLpar(TokenType type) { + if (PeekMatchLpar(type)) { + Consume(); + Consume(); + return true; + } + return false; +} + +Result WastParser::Expect(TokenType type) { + if (!Match(type)) { + Token token = Consume(); + Error(token.loc, "unexpected token %s, expected %s.", + token.to_string_clamp(kMaxErrorTokenLength).c_str(), + GetTokenTypeName(type)); + return Result::Error; + } + + return Result::Ok; +} + +Token WastParser::Consume() { + assert(!tokens_.empty()); + Token token = tokens_.front(); + tokens_.pop_front(); + return token; +} + +Result WastParser::Synchronize(SynchronizeFunc func) { + static const int kMaxConsumed = 10; + for (int i = 0; i < kMaxConsumed; ++i) { + if (func(PeekPair())) { + return Result::Ok; + } + + Token token = Consume(); + if (token.token_type() == TokenType::Reserved) { + Error(token.loc, "unexpected token %s.", + token.to_string_clamp(kMaxErrorTokenLength).c_str()); + } + } + + return Result::Error; +} + +void WastParser::ErrorUnlessOpcodeEnabled(const Token& token) { + Opcode opcode = token.opcode(); + if (!opcode.IsEnabled(options_->features)) { + Error(token.loc, "opcode not allowed: %s", opcode.GetName()); + } +} + +Result WastParser::ErrorExpected(const std::vector<std::string>& expected, + const char* example) { + Token token = Consume(); + std::string expected_str; + if (!expected.empty()) { + expected_str = ", expected "; + for (size_t i = 0; i < expected.size(); ++i) { + if (i != 0) { + if (i == expected.size() - 1) { + expected_str += " or "; + } else { + expected_str += ", "; + } + } + + expected_str += expected[i]; + } + + if (example) { + expected_str += " (e.g. "; + expected_str += example; + expected_str += ")"; + } + } + + Error(token.loc, "unexpected token \"%s\"%s.", + token.to_string_clamp(kMaxErrorTokenLength).c_str(), + expected_str.c_str()); + return Result::Error; +} + +Result WastParser::ErrorIfLpar(const std::vector<std::string>& expected, + const char* example) { + if (Match(TokenType::Lpar)) { + GetToken(); + return ErrorExpected(expected, example); + } + return Result::Ok; +} + +bool WastParser::ParseBindVarOpt(std::string* name) { + WABT_TRACE(ParseBindVarOpt); + if (!PeekMatch(TokenType::Var)) { + return false; + } + Token token = Consume(); + *name = std::string(token.text()); + return true; +} + +Result WastParser::ParseVar(Var* out_var) { + WABT_TRACE(ParseVar); + if (PeekMatch(TokenType::Nat)) { + Token token = Consume(); + std::string_view sv = token.literal().text; + uint64_t index = kInvalidIndex; + if (Failed(ParseUint64(sv, &index))) { + // Print an error, but don't fail parsing. + Error(token.loc, "invalid int \"" PRIstringview "\"", + WABT_PRINTF_STRING_VIEW_ARG(sv)); + } + + *out_var = Var(index, token.loc); + return Result::Ok; + } else if (PeekMatch(TokenType::Var)) { + Token token = Consume(); + *out_var = Var(token.text(), token.loc); + return Result::Ok; + } else { + return ErrorExpected({"a numeric index", "a name"}, "12 or $foo"); + } +} + +bool WastParser::ParseVarOpt(Var* out_var, Var default_var) { + WABT_TRACE(ParseVarOpt); + if (PeekMatch(TokenType::Nat) || PeekMatch(TokenType::Var)) { + Result result = ParseVar(out_var); + // Should always succeed, the only way it could fail is if the token + // doesn't match. + assert(Succeeded(result)); + WABT_USE(result); + return true; + } else { + *out_var = default_var; + return false; + } +} + +Result WastParser::ParseOffsetExpr(ExprList* out_expr_list) { + WABT_TRACE(ParseOffsetExpr); + if (!ParseOffsetExprOpt(out_expr_list)) { + return ErrorExpected({"an offset expr"}, "(i32.const 123)"); + } + return Result::Ok; +} + +bool WastParser::ParseOffsetExprOpt(ExprList* out_expr_list) { + WABT_TRACE(ParseOffsetExprOpt); + if (MatchLpar(TokenType::Offset)) { + CHECK_RESULT(ParseTerminatingInstrList(out_expr_list)); + EXPECT(Rpar); + return true; + } else if (PeekMatchExpr()) { + CHECK_RESULT(ParseExpr(out_expr_list)); + return true; + } else { + return false; + } +} + +Result WastParser::ParseTextList(std::vector<uint8_t>* out_data) { + WABT_TRACE(ParseTextList); + if (!ParseTextListOpt(out_data)) { + // TODO(binji): Add error message here. + return Result::Error; + } + + return Result::Ok; +} + +bool WastParser::ParseTextListOpt(std::vector<uint8_t>* out_data) { + WABT_TRACE(ParseTextListOpt); + TextVector texts; + while (PeekMatch(TokenType::Text)) + texts.push_back(Consume().text()); + + RemoveEscapes(texts, std::back_inserter(*out_data)); + return !texts.empty(); +} + +Result WastParser::ParseVarList(VarVector* out_var_list) { + WABT_TRACE(ParseVarList); + Var var; + while (ParseVarOpt(&var)) { + out_var_list->emplace_back(var); + } + if (out_var_list->empty()) { + return ErrorExpected({"a var"}, "12 or $foo"); + } else { + return Result::Ok; + } +} + +bool WastParser::ParseElemExprOpt(ExprList* out_elem_expr) { + WABT_TRACE(ParseElemExprOpt); + bool item = MatchLpar(TokenType::Item); + ExprList exprs; + if (item) { + if (ParseTerminatingInstrList(&exprs) != Result::Ok) { + return false; + } + EXPECT(Rpar); + } else { + if (ParseExpr(&exprs) != Result::Ok) { + return false; + } + } + if (!exprs.size()) { + return false; + } + *out_elem_expr = std::move(exprs); + return true; +} + +bool WastParser::ParseElemExprListOpt(ExprListVector* out_list) { + ExprList elem_expr; + while (ParseElemExprOpt(&elem_expr)) { + out_list->push_back(std::move(elem_expr)); + } + return !out_list->empty(); +} + +bool WastParser::ParseElemExprVarListOpt(ExprListVector* out_list) { + WABT_TRACE(ParseElemExprVarListOpt); + Var var; + ExprList init_expr; + while (ParseVarOpt(&var)) { + init_expr.push_back(std::make_unique<RefFuncExpr>(var)); + out_list->push_back(std::move(init_expr)); + } + return !out_list->empty(); +} + +Result WastParser::ParseValueType(Var* out_type) { + WABT_TRACE(ParseValueType); + + const bool is_ref_type = PeekMatchRefType(); + const bool is_value_type = PeekMatch(TokenType::ValueType); + + if (!is_value_type && !is_ref_type) { + return ErrorExpected( + {"i32", "i64", "f32", "f64", "v128", "externref", "funcref"}); + } + + if (is_ref_type) { + EXPECT(Lpar); + EXPECT(Ref); + CHECK_RESULT(ParseVar(out_type)); + EXPECT(Rpar); + return Result::Ok; + } + + Token token = Consume(); + Type type = token.type(); + bool is_enabled; + switch (type) { + case Type::V128: + is_enabled = options_->features.simd_enabled(); + break; + case Type::FuncRef: + case Type::ExternRef: + is_enabled = options_->features.reference_types_enabled(); + break; + default: + is_enabled = true; + break; + } + + if (!is_enabled) { + Error(token.loc, "value type not allowed: %s", type.GetName().c_str()); + return Result::Error; + } + + *out_type = Var(type, GetLocation()); + return Result::Ok; +} + +Result WastParser::ParseValueTypeList( + TypeVector* out_type_list, + std::unordered_map<uint32_t, std::string>* type_names) { + WABT_TRACE(ParseValueTypeList); + while (true) { + if (!PeekMatchRefType() && !PeekMatch(TokenType::ValueType)) { + break; + } + + Var type; + CHECK_RESULT(ParseValueType(&type)); + + if (type.is_index()) { + out_type_list->push_back(Type(type.index())); + } else { + assert(type.is_name()); + assert(options_->features.function_references_enabled()); + type_names->emplace(out_type_list->size(), type.name()); + out_type_list->push_back(Type(Type::Reference, kInvalidIndex)); + } + } + + return Result::Ok; +} + +Result WastParser::ParseRefKind(Type* out_type) { + WABT_TRACE(ParseRefKind); + if (!IsTokenTypeRefKind(Peek())) { + return ErrorExpected({"func", "extern", "exn"}); + } + + Token token = Consume(); + Type type = token.type(); + + if ((type == Type::ExternRef && + !options_->features.reference_types_enabled()) || + ((type == Type::Struct || type == Type::Array) && + !options_->features.gc_enabled())) { + Error(token.loc, "value type not allowed: %s", type.GetName().c_str()); + return Result::Error; + } + + *out_type = type; + return Result::Ok; +} + +Result WastParser::ParseRefType(Type* out_type) { + WABT_TRACE(ParseRefType); + if (!PeekMatch(TokenType::ValueType)) { + return ErrorExpected({"funcref", "externref"}); + } + + Token token = Consume(); + Type type = token.type(); + if (type == Type::ExternRef && + !options_->features.reference_types_enabled()) { + Error(token.loc, "value type not allowed: %s", type.GetName().c_str()); + return Result::Error; + } + + *out_type = type; + return Result::Ok; +} + +bool WastParser::ParseRefTypeOpt(Type* out_type) { + WABT_TRACE(ParseRefTypeOpt); + if (!PeekMatch(TokenType::ValueType)) { + return false; + } + + Token token = Consume(); + Type type = token.type(); + if (type == Type::ExternRef && + !options_->features.reference_types_enabled()) { + return false; + } + + *out_type = type; + return true; +} + +Result WastParser::ParseQuotedText(std::string* text, bool check_utf8) { + WABT_TRACE(ParseQuotedText); + if (!PeekMatch(TokenType::Text)) { + return ErrorExpected({"a quoted string"}, "\"foo\""); + } + + Token token = Consume(); + RemoveEscapes(token.text(), std::back_inserter(*text)); + if (check_utf8 && !IsValidUtf8(text->data(), text->length())) { + Error(token.loc, "quoted string has an invalid utf-8 encoding"); + } + return Result::Ok; +} + +bool WastParser::ParseOffsetOpt(Address* out_offset) { + WABT_TRACE(ParseOffsetOpt); + if (PeekMatch(TokenType::OffsetEqNat)) { + Token token = Consume(); + uint64_t offset64; + std::string_view sv = token.text(); + if (Failed(ParseInt64(sv, &offset64, ParseIntType::SignedAndUnsigned))) { + Error(token.loc, "invalid offset \"" PRIstringview "\"", + WABT_PRINTF_STRING_VIEW_ARG(sv)); + } + // FIXME: make this depend on the current memory. + if (offset64 > UINT32_MAX) { + Error(token.loc, "offset must be less than or equal to 0xffffffff"); + } + *out_offset = offset64; + return true; + } else { + *out_offset = 0; + return false; + } +} + +bool WastParser::ParseAlignOpt(Address* out_align) { + WABT_TRACE(ParseAlignOpt); + if (PeekMatch(TokenType::AlignEqNat)) { + Token token = Consume(); + std::string_view sv = token.text(); + if (Failed(ParseInt64(sv, out_align, ParseIntType::UnsignedOnly))) { + Error(token.loc, "invalid alignment \"" PRIstringview "\"", + WABT_PRINTF_STRING_VIEW_ARG(sv)); + } + + if (!IsPowerOfTwo(*out_align)) { + Error(token.loc, "alignment must be power-of-two"); + } + + return true; + } else { + *out_align = WABT_USE_NATURAL_ALIGNMENT; + return false; + } +} + +Result WastParser::ParseMemidx(Location loc, Var* out_memidx) { + WABT_TRACE(ParseMemidx); + if (PeekMatchLpar(TokenType::Memory)) { + if (!options_->features.multi_memory_enabled()) { + Error(loc, "Specifying memory variable is not allowed"); + return Result::Error; + } + EXPECT(Lpar); + EXPECT(Memory); + CHECK_RESULT(ParseVar(out_memidx)); + EXPECT(Rpar); + } else { + if (ParseVarOpt(out_memidx, Var(0, loc)) && + !options_->features.multi_memory_enabled()) { + Error(loc, "Specifying memory variable is not allowed"); + return Result::Error; + } + } + return Result::Ok; +} + +Result WastParser::ParseLimitsIndex(Limits* out_limits) { + WABT_TRACE(ParseLimitsIndex); + + if (PeekMatch(TokenType::ValueType)) { + if (GetToken().type() == Type::I64) { + Consume(); + out_limits->is_64 = true; + } else if (GetToken().type() == Type::I32) { + Consume(); + out_limits->is_64 = false; + } + } + + return Result::Ok; +} + +Result WastParser::ParseLimits(Limits* out_limits) { + WABT_TRACE(ParseLimits); + + CHECK_RESULT(ParseNat(&out_limits->initial, out_limits->is_64)); + if (PeekMatch(TokenType::Nat)) { + CHECK_RESULT(ParseNat(&out_limits->max, out_limits->is_64)); + out_limits->has_max = true; + } else { + out_limits->has_max = false; + } + + if (Match(TokenType::Shared)) { + out_limits->is_shared = true; + } + + return Result::Ok; +} + +Result WastParser::ParseNat(uint64_t* out_nat, bool is_64) { + WABT_TRACE(ParseNat); + if (!PeekMatch(TokenType::Nat)) { + return ErrorExpected({"a natural number"}, "123"); + } + + Token token = Consume(); + std::string_view sv = token.literal().text; + if (Failed(ParseUint64(sv, out_nat)) || (!is_64 && *out_nat > 0xffffffffu)) { + Error(token.loc, "invalid int \"" PRIstringview "\"", + WABT_PRINTF_STRING_VIEW_ARG(sv)); + } + + return Result::Ok; +} + +Result WastParser::ParseModule(std::unique_ptr<Module>* out_module) { + WABT_TRACE(ParseModule); + auto module = std::make_unique<Module>(); + + if (PeekMatchLpar(TokenType::Module)) { + // Starts with "(module". Allow text and binary modules, but no quoted + // modules. + CommandPtr command; + CHECK_RESULT(ParseModuleCommand(nullptr, &command)); + if (isa<ModuleCommand>(command.get())) { + auto module_command = cast<ModuleCommand>(std::move(command)); + *module = std::move(module_command->module); + } else { + assert(isa<ScriptModuleCommand>(command.get())); + auto module_command = cast<ScriptModuleCommand>(std::move(command)); + *module = std::move(module_command->module); + } + } else if (IsModuleField(PeekPair())) { + // Parse an inline module (i.e. one with no surrounding (module)). + CHECK_RESULT(ParseModuleFieldList(module.get())); + } else { + ConsumeIfLpar(); + ErrorExpected({"a module field", "a module"}); + } + + EXPECT(Eof); + if (errors_->size() == 0) { + *out_module = std::move(module); + return Result::Ok; + } else { + return Result::Error; + } +} + +Result WastParser::ParseScript(std::unique_ptr<Script>* out_script) { + WABT_TRACE(ParseScript); + auto script = std::make_unique<Script>(); + + // Don't consume the Lpar yet, even though it is required. This way the + // sub-parser functions (e.g. ParseFuncModuleField) can consume it and keep + // the parsing structure more regular. + if (IsModuleField(PeekPair())) { + // Parse an inline module (i.e. one with no surrounding (module)). + auto command = std::make_unique<ModuleCommand>(); + command->module.loc = GetLocation(); + CHECK_RESULT(ParseModuleFieldList(&command->module)); + script->commands.emplace_back(std::move(command)); + } else if (IsCommand(PeekPair())) { + CHECK_RESULT(ParseCommandList(script.get(), &script->commands)); + } else { + ConsumeIfLpar(); + ErrorExpected({"a module field", "a command"}); + } + + EXPECT(Eof); + if (errors_->size() == 0) { + *out_script = std::move(script); + return Result::Ok; + } else { + return Result::Error; + } +} + +Result WastParser::ParseModuleFieldList(Module* module) { + WABT_TRACE(ParseModuleFieldList); + while (IsModuleField(PeekPair())) { + if (Failed(ParseModuleField(module))) { + CHECK_RESULT(Synchronize(IsModuleField)); + } + } + CHECK_RESULT(ResolveFuncTypes(module, errors_)); + CHECK_RESULT(ResolveNamesModule(module, errors_)); + return Result::Ok; +} + +Result WastParser::ParseModuleField(Module* module) { + WABT_TRACE(ParseModuleField); + switch (Peek(1)) { + case TokenType::Data: return ParseDataModuleField(module); + case TokenType::Elem: return ParseElemModuleField(module); + case TokenType::Tag: return ParseTagModuleField(module); + case TokenType::Export: return ParseExportModuleField(module); + case TokenType::Func: return ParseFuncModuleField(module); + case TokenType::Type: return ParseTypeModuleField(module); + case TokenType::Global: return ParseGlobalModuleField(module); + case TokenType::Import: return ParseImportModuleField(module); + case TokenType::Memory: return ParseMemoryModuleField(module); + case TokenType::Start: return ParseStartModuleField(module); + case TokenType::Table: return ParseTableModuleField(module); + default: + assert( + !"ParseModuleField should only be called if IsModuleField() is true"); + return Result::Error; + } +} + +Result WastParser::ParseDataModuleField(Module* module) { + WABT_TRACE(ParseDataModuleField); + EXPECT(Lpar); + Location loc = GetLocation(); + EXPECT(Data); + std::string name; + ParseBindVarOpt(&name); + auto field = std::make_unique<DataSegmentModuleField>(loc, name); + + if (PeekMatchLpar(TokenType::Memory)) { + EXPECT(Lpar); + EXPECT(Memory); + CHECK_RESULT(ParseVar(&field->data_segment.memory_var)); + EXPECT(Rpar); + CHECK_RESULT(ParseOffsetExpr(&field->data_segment.offset)); + } else if (ParseVarOpt(&field->data_segment.memory_var, Var(0, loc))) { + CHECK_RESULT(ParseOffsetExpr(&field->data_segment.offset)); + } else if (!ParseOffsetExprOpt(&field->data_segment.offset)) { + if (!options_->features.bulk_memory_enabled()) { + Error(loc, "passive data segments are not allowed"); + return Result::Error; + } + + field->data_segment.kind = SegmentKind::Passive; + } + + ParseTextListOpt(&field->data_segment.data); + EXPECT(Rpar); + module->AppendField(std::move(field)); + return Result::Ok; +} + +Result WastParser::ParseElemModuleField(Module* module) { + WABT_TRACE(ParseElemModuleField); + EXPECT(Lpar); + Location loc = GetLocation(); + EXPECT(Elem); + + // With MVP text format the name here was intended to refer to the table + // that the elem segment was part of, but we never did anything with this name + // since there was only one table anyway. + // With bulk-memory enabled this introduces a new name for the particular + // elem segment. + std::string initial_name; + bool has_name = ParseBindVarOpt(&initial_name); + + std::string segment_name = initial_name; + if (!options_->features.bulk_memory_enabled()) { + segment_name = ""; + } + auto field = std::make_unique<ElemSegmentModuleField>(loc, segment_name); + if (options_->features.reference_types_enabled() && + Match(TokenType::Declare)) { + field->elem_segment.kind = SegmentKind::Declared; + } + + // Optional table specifier + if (options_->features.bulk_memory_enabled()) { + if (PeekMatchLpar(TokenType::Table)) { + EXPECT(Lpar); + EXPECT(Table); + CHECK_RESULT(ParseVar(&field->elem_segment.table_var)); + EXPECT(Rpar); + } else { + ParseVarOpt(&field->elem_segment.table_var, Var(0, loc)); + } + } else { + if (has_name) { + field->elem_segment.table_var = Var(initial_name, loc); + } else { + ParseVarOpt(&field->elem_segment.table_var, Var(0, loc)); + } + } + + // Parse offset expression, if not declared/passive segment. + if (options_->features.bulk_memory_enabled()) { + if (field->elem_segment.kind != SegmentKind::Declared && + !ParseOffsetExprOpt(&field->elem_segment.offset)) { + field->elem_segment.kind = SegmentKind::Passive; + } + } else { + CHECK_RESULT(ParseOffsetExpr(&field->elem_segment.offset)); + } + + if (ParseRefTypeOpt(&field->elem_segment.elem_type)) { + ParseElemExprListOpt(&field->elem_segment.elem_exprs); + } else { + field->elem_segment.elem_type = Type::FuncRef; + if (PeekMatch(TokenType::Func)) { + EXPECT(Func); + } + ParseElemExprVarListOpt(&field->elem_segment.elem_exprs); + } + EXPECT(Rpar); + module->AppendField(std::move(field)); + return Result::Ok; +} + +Result WastParser::ParseTagModuleField(Module* module) { + WABT_TRACE(ParseTagModuleField); + if (!options_->features.exceptions_enabled()) { + Error(Consume().loc, "tag not allowed"); + return Result::Error; + } + EXPECT(Lpar); + EXPECT(Tag); + Location loc = GetLocation(); + + std::string name; + ParseBindVarOpt(&name); + + ModuleFieldList export_fields; + CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Tag)); + + if (PeekMatchLpar(TokenType::Import)) { + CheckImportOrdering(module); + auto import = std::make_unique<TagImport>(name); + Tag& tag = import->tag; + CHECK_RESULT(ParseInlineImport(import.get())); + CHECK_RESULT(ParseTypeUseOpt(&tag.decl)); + CHECK_RESULT(ParseUnboundFuncSignature(&tag.decl.sig)); + CHECK_RESULT(ErrorIfLpar({"type", "param", "result"})); + auto field = + std::make_unique<ImportModuleField>(std::move(import), GetLocation()); + module->AppendField(std::move(field)); + } else { + auto field = std::make_unique<TagModuleField>(loc, name); + CHECK_RESULT(ParseTypeUseOpt(&field->tag.decl)); + CHECK_RESULT(ParseUnboundFuncSignature(&field->tag.decl.sig)); + module->AppendField(std::move(field)); + } + + AppendInlineExportFields(module, &export_fields, module->tags.size() - 1); + + EXPECT(Rpar); + return Result::Ok; +} + +Result WastParser::ParseExportModuleField(Module* module) { + WABT_TRACE(ParseExportModuleField); + EXPECT(Lpar); + auto field = std::make_unique<ExportModuleField>(GetLocation()); + EXPECT(Export); + CHECK_RESULT(ParseQuotedText(&field->export_.name)); + CHECK_RESULT(ParseExportDesc(&field->export_)); + EXPECT(Rpar); + module->AppendField(std::move(field)); + return Result::Ok; +} + +Result WastParser::ParseFuncModuleField(Module* module) { + WABT_TRACE(ParseFuncModuleField); + EXPECT(Lpar); + Location loc = GetLocation(); + EXPECT(Func); + std::string name; + ParseBindVarOpt(&name); + + ModuleFieldList export_fields; + CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Func)); + + if (PeekMatchLpar(TokenType::Import)) { + CheckImportOrdering(module); + auto import = std::make_unique<FuncImport>(name); + Func& func = import->func; + CHECK_RESULT(ParseInlineImport(import.get())); + CHECK_RESULT(ParseTypeUseOpt(&func.decl)); + CHECK_RESULT(ParseFuncSignature(&func.decl.sig, &func.bindings)); + CHECK_RESULT(ErrorIfLpar({"type", "param", "result"})); + auto field = + std::make_unique<ImportModuleField>(std::move(import), GetLocation()); + module->AppendField(std::move(field)); + } else { + auto field = std::make_unique<FuncModuleField>(loc, name); + Func& func = field->func; + func.loc = GetLocation(); + CHECK_RESULT(ParseTypeUseOpt(&func.decl)); + CHECK_RESULT(ParseFuncSignature(&func.decl.sig, &func.bindings)); + TypeVector local_types; + CHECK_RESULT(ParseBoundValueTypeList( + TokenType::Local, &local_types, &func.bindings, + &func.decl.sig.param_type_names, func.GetNumParams())); + func.local_types.Set(local_types); + CHECK_RESULT(ParseTerminatingInstrList(&func.exprs)); + module->AppendField(std::move(field)); + } + + AppendInlineExportFields(module, &export_fields, module->funcs.size() - 1); + + EXPECT(Rpar); + return Result::Ok; +} + +Result WastParser::ParseTypeModuleField(Module* module) { + WABT_TRACE(ParseTypeModuleField); + EXPECT(Lpar); + auto field = std::make_unique<TypeModuleField>(GetLocation()); + EXPECT(Type); + + std::string name; + ParseBindVarOpt(&name); + EXPECT(Lpar); + Location loc = GetLocation(); + + if (Match(TokenType::Func)) { + auto func_type = std::make_unique<FuncType>(name); + BindingHash bindings; + CHECK_RESULT(ParseFuncSignature(&func_type->sig, &bindings)); + CHECK_RESULT(ErrorIfLpar({"param", "result"})); + field->type = std::move(func_type); + } else if (Match(TokenType::Struct)) { + if (!options_->features.gc_enabled()) { + Error(loc, "struct not allowed"); + return Result::Error; + } + auto struct_type = std::make_unique<StructType>(name); + CHECK_RESULT(ParseFieldList(&struct_type->fields)); + field->type = std::move(struct_type); + } else if (Match(TokenType::Array)) { + if (!options_->features.gc_enabled()) { + Error(loc, "array type not allowed"); + } + auto array_type = std::make_unique<ArrayType>(name); + CHECK_RESULT(ParseField(&array_type->field)); + field->type = std::move(array_type); + } else { + return ErrorExpected({"func", "struct", "array"}); + } + + EXPECT(Rpar); + EXPECT(Rpar); + module->AppendField(std::move(field)); + return Result::Ok; +} + +Result WastParser::ParseField(Field* field) { + WABT_TRACE(ParseField); + auto parse_mut_valuetype = [&]() -> Result { + // TODO: Share with ParseGlobalType? + if (MatchLpar(TokenType::Mut)) { + field->mutable_ = true; + Var type; + CHECK_RESULT(ParseValueType(&type)); + field->type = Type(type.index()); + EXPECT(Rpar); + } else { + field->mutable_ = false; + Var type; + CHECK_RESULT(ParseValueType(&type)); + field->type = Type(type.index()); + } + return Result::Ok; + }; + + if (MatchLpar(TokenType::Field)) { + ParseBindVarOpt(&field->name); + CHECK_RESULT(parse_mut_valuetype()); + EXPECT(Rpar); + } else { + CHECK_RESULT(parse_mut_valuetype()); + } + + return Result::Ok; +} + +Result WastParser::ParseFieldList(std::vector<Field>* fields) { + WABT_TRACE(ParseFieldList); + while (PeekMatch(TokenType::ValueType) || PeekMatch(TokenType::Lpar)) { + Field field; + CHECK_RESULT(ParseField(&field)); + fields->push_back(field); + } + return Result::Ok; +} + +Result WastParser::ParseGlobalModuleField(Module* module) { + WABT_TRACE(ParseGlobalModuleField); + EXPECT(Lpar); + Location loc = GetLocation(); + EXPECT(Global); + std::string name; + ParseBindVarOpt(&name); + + ModuleFieldList export_fields; + CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Global)); + + if (PeekMatchLpar(TokenType::Import)) { + CheckImportOrdering(module); + auto import = std::make_unique<GlobalImport>(name); + CHECK_RESULT(ParseInlineImport(import.get())); + CHECK_RESULT(ParseGlobalType(&import->global)); + auto field = + std::make_unique<ImportModuleField>(std::move(import), GetLocation()); + module->AppendField(std::move(field)); + } else { + auto field = std::make_unique<GlobalModuleField>(loc, name); + CHECK_RESULT(ParseGlobalType(&field->global)); + CHECK_RESULT(ParseTerminatingInstrList(&field->global.init_expr)); + module->AppendField(std::move(field)); + } + + AppendInlineExportFields(module, &export_fields, module->globals.size() - 1); + + EXPECT(Rpar); + return Result::Ok; +} + +Result WastParser::ParseImportModuleField(Module* module) { + WABT_TRACE(ParseImportModuleField); + EXPECT(Lpar); + Location loc = GetLocation(); + CheckImportOrdering(module); + EXPECT(Import); + std::string module_name; + std::string field_name; + CHECK_RESULT(ParseQuotedText(&module_name)); + CHECK_RESULT(ParseQuotedText(&field_name)); + EXPECT(Lpar); + + std::unique_ptr<ImportModuleField> field; + std::string name; + + switch (Peek()) { + case TokenType::Func: { + Consume(); + ParseBindVarOpt(&name); + auto import = std::make_unique<FuncImport>(name); + CHECK_RESULT(ParseTypeUseOpt(&import->func.decl)); + CHECK_RESULT( + ParseFuncSignature(&import->func.decl.sig, &import->func.bindings)); + CHECK_RESULT(ErrorIfLpar({"param", "result"})); + EXPECT(Rpar); + field = std::make_unique<ImportModuleField>(std::move(import), loc); + break; + } + + case TokenType::Table: { + Consume(); + ParseBindVarOpt(&name); + auto import = std::make_unique<TableImport>(name); + CHECK_RESULT(ParseLimits(&import->table.elem_limits)); + CHECK_RESULT(ParseRefType(&import->table.elem_type)); + EXPECT(Rpar); + field = std::make_unique<ImportModuleField>(std::move(import), loc); + break; + } + + case TokenType::Memory: { + Consume(); + ParseBindVarOpt(&name); + auto import = std::make_unique<MemoryImport>(name); + CHECK_RESULT(ParseLimitsIndex(&import->memory.page_limits)); + CHECK_RESULT(ParseLimits(&import->memory.page_limits)); + EXPECT(Rpar); + field = std::make_unique<ImportModuleField>(std::move(import), loc); + break; + } + + case TokenType::Global: { + Consume(); + ParseBindVarOpt(&name); + auto import = std::make_unique<GlobalImport>(name); + CHECK_RESULT(ParseGlobalType(&import->global)); + EXPECT(Rpar); + field = std::make_unique<ImportModuleField>(std::move(import), loc); + break; + } + + case TokenType::Tag: { + Consume(); + ParseBindVarOpt(&name); + auto import = std::make_unique<TagImport>(name); + CHECK_RESULT(ParseTypeUseOpt(&import->tag.decl)); + CHECK_RESULT(ParseUnboundFuncSignature(&import->tag.decl.sig)); + EXPECT(Rpar); + field = std::make_unique<ImportModuleField>(std::move(import), loc); + break; + } + + default: + return ErrorExpected({"an external kind"}); + } + + field->import->module_name = module_name; + field->import->field_name = field_name; + + module->AppendField(std::move(field)); + EXPECT(Rpar); + return Result::Ok; +} + +Result WastParser::ParseMemoryModuleField(Module* module) { + WABT_TRACE(ParseMemoryModuleField); + EXPECT(Lpar); + Location loc = GetLocation(); + EXPECT(Memory); + std::string name; + ParseBindVarOpt(&name); + + ModuleFieldList export_fields; + CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Memory)); + + if (PeekMatchLpar(TokenType::Import)) { + CheckImportOrdering(module); + auto import = std::make_unique<MemoryImport>(name); + CHECK_RESULT(ParseInlineImport(import.get())); + CHECK_RESULT(ParseLimitsIndex(&import->memory.page_limits)); + CHECK_RESULT(ParseLimits(&import->memory.page_limits)); + auto field = + std::make_unique<ImportModuleField>(std::move(import), GetLocation()); + module->AppendField(std::move(field)); + } else { + auto field = std::make_unique<MemoryModuleField>(loc, name); + CHECK_RESULT(ParseLimitsIndex(&field->memory.page_limits)); + if (MatchLpar(TokenType::Data)) { + auto data_segment_field = std::make_unique<DataSegmentModuleField>(loc); + DataSegment& data_segment = data_segment_field->data_segment; + data_segment.memory_var = Var(module->memories.size(), GetLocation()); + data_segment.offset.push_back(std::make_unique<ConstExpr>( + field->memory.page_limits.is_64 ? Const::I64(0) : Const::I32(0))); + data_segment.offset.back().loc = loc; + ParseTextListOpt(&data_segment.data); + EXPECT(Rpar); + + uint32_t byte_size = WABT_ALIGN_UP_TO_PAGE(data_segment.data.size()); + uint32_t page_size = WABT_BYTES_TO_PAGES(byte_size); + field->memory.page_limits.initial = page_size; + field->memory.page_limits.max = page_size; + field->memory.page_limits.has_max = true; + + module->AppendField(std::move(field)); + module->AppendField(std::move(data_segment_field)); + } else { + CHECK_RESULT(ParseLimits(&field->memory.page_limits)); + module->AppendField(std::move(field)); + } + } + + AppendInlineExportFields(module, &export_fields, module->memories.size() - 1); + + EXPECT(Rpar); + return Result::Ok; +} + +Result WastParser::ParseStartModuleField(Module* module) { + WABT_TRACE(ParseStartModuleField); + EXPECT(Lpar); + Location loc = GetLocation(); + if (module->starts.size() > 0) { + Error(loc, "multiple start sections"); + return Result::Error; + } + EXPECT(Start); + Var var; + CHECK_RESULT(ParseVar(&var)); + EXPECT(Rpar); + module->AppendField(std::make_unique<StartModuleField>(var, loc)); + return Result::Ok; +} + +Result WastParser::ParseTableModuleField(Module* module) { + WABT_TRACE(ParseTableModuleField); + EXPECT(Lpar); + Location loc = GetLocation(); + EXPECT(Table); + std::string name; + ParseBindVarOpt(&name); + + ModuleFieldList export_fields; + CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Table)); + + if (PeekMatchLpar(TokenType::Import)) { + CheckImportOrdering(module); + auto import = std::make_unique<TableImport>(name); + CHECK_RESULT(ParseInlineImport(import.get())); + CHECK_RESULT(ParseLimits(&import->table.elem_limits)); + CHECK_RESULT(ParseRefType(&import->table.elem_type)); + auto field = + std::make_unique<ImportModuleField>(std::move(import), GetLocation()); + module->AppendField(std::move(field)); + } else if (PeekMatch(TokenType::ValueType)) { + Type elem_type; + CHECK_RESULT(ParseRefType(&elem_type)); + + EXPECT(Lpar); + EXPECT(Elem); + + auto elem_segment_field = std::make_unique<ElemSegmentModuleField>(loc); + ElemSegment& elem_segment = elem_segment_field->elem_segment; + elem_segment.table_var = Var(module->tables.size(), GetLocation()); + elem_segment.offset.push_back(std::make_unique<ConstExpr>(Const::I32(0))); + elem_segment.offset.back().loc = loc; + elem_segment.elem_type = elem_type; + // Syntax is either an optional list of var (legacy), or a non-empty list + // of elem expr. + ExprList elem_expr; + if (ParseElemExprOpt(&elem_expr)) { + elem_segment.elem_exprs.push_back(std::move(elem_expr)); + // Parse the rest. + ParseElemExprListOpt(&elem_segment.elem_exprs); + } else { + ParseElemExprVarListOpt(&elem_segment.elem_exprs); + } + EXPECT(Rpar); + + auto table_field = std::make_unique<TableModuleField>(loc, name); + table_field->table.elem_limits.initial = elem_segment.elem_exprs.size(); + table_field->table.elem_limits.max = elem_segment.elem_exprs.size(); + table_field->table.elem_limits.has_max = true; + table_field->table.elem_type = elem_type; + module->AppendField(std::move(table_field)); + module->AppendField(std::move(elem_segment_field)); + } else { + auto field = std::make_unique<TableModuleField>(loc, name); + CHECK_RESULT(ParseLimits(&field->table.elem_limits)); + CHECK_RESULT(ParseRefType(&field->table.elem_type)); + module->AppendField(std::move(field)); + } + + AppendInlineExportFields(module, &export_fields, module->tables.size() - 1); + + EXPECT(Rpar); + return Result::Ok; +} + +Result WastParser::ParseExportDesc(Export* export_) { + WABT_TRACE(ParseExportDesc); + EXPECT(Lpar); + switch (Peek()) { + case TokenType::Func: export_->kind = ExternalKind::Func; break; + case TokenType::Table: export_->kind = ExternalKind::Table; break; + case TokenType::Memory: export_->kind = ExternalKind::Memory; break; + case TokenType::Global: export_->kind = ExternalKind::Global; break; + case TokenType::Tag: export_->kind = ExternalKind::Tag; break; + default: + return ErrorExpected({"an external kind"}); + } + Consume(); + CHECK_RESULT(ParseVar(&export_->var)); + EXPECT(Rpar); + return Result::Ok; +} + +Result WastParser::ParseInlineExports(ModuleFieldList* fields, + ExternalKind kind) { + WABT_TRACE(ParseInlineExports); + while (PeekMatchLpar(TokenType::Export)) { + EXPECT(Lpar); + auto field = std::make_unique<ExportModuleField>(GetLocation()); + field->export_.kind = kind; + EXPECT(Export); + CHECK_RESULT(ParseQuotedText(&field->export_.name)); + EXPECT(Rpar); + fields->push_back(std::move(field)); + } + return Result::Ok; +} + +Result WastParser::ParseInlineImport(Import* import) { + WABT_TRACE(ParseInlineImport); + EXPECT(Lpar); + EXPECT(Import); + CHECK_RESULT(ParseQuotedText(&import->module_name)); + CHECK_RESULT(ParseQuotedText(&import->field_name)); + EXPECT(Rpar); + return Result::Ok; +} + +Result WastParser::ParseTypeUseOpt(FuncDeclaration* decl) { + WABT_TRACE(ParseTypeUseOpt); + if (MatchLpar(TokenType::Type)) { + decl->has_func_type = true; + CHECK_RESULT(ParseVar(&decl->type_var)); + EXPECT(Rpar); + } else { + decl->has_func_type = false; + } + return Result::Ok; +} + +Result WastParser::ParseFuncSignature(FuncSignature* sig, + BindingHash* param_bindings) { + WABT_TRACE(ParseFuncSignature); + CHECK_RESULT(ParseBoundValueTypeList(TokenType::Param, &sig->param_types, + param_bindings, &sig->param_type_names)); + CHECK_RESULT(ParseResultList(&sig->result_types, &sig->result_type_names)); + return Result::Ok; +} + +Result WastParser::ParseUnboundFuncSignature(FuncSignature* sig) { + WABT_TRACE(ParseUnboundFuncSignature); + CHECK_RESULT(ParseUnboundValueTypeList(TokenType::Param, &sig->param_types, + &sig->param_type_names)); + CHECK_RESULT(ParseResultList(&sig->result_types, &sig->result_type_names)); + return Result::Ok; +} + +Result WastParser::ParseBoundValueTypeList( + TokenType token, + TypeVector* types, + BindingHash* bindings, + std::unordered_map<uint32_t, std::string>* type_names, + Index binding_index_offset) { + WABT_TRACE(ParseBoundValueTypeList); + while (MatchLpar(token)) { + if (PeekMatch(TokenType::Var)) { + std::string name; + Var type; + Location loc = GetLocation(); + ParseBindVarOpt(&name); + CHECK_RESULT(ParseValueType(&type)); + bindings->emplace(name, + Binding(loc, binding_index_offset + types->size())); + if (type.is_index()) { + types->push_back(Type(type.index())); + } else { + assert(type.is_name()); + assert(options_->features.function_references_enabled()); + type_names->emplace(binding_index_offset + types->size(), type.name()); + types->push_back(Type(Type::Reference, kInvalidIndex)); + } + } else { + CHECK_RESULT(ParseValueTypeList(types, type_names)); + } + EXPECT(Rpar); + } + return Result::Ok; +} + +Result WastParser::ParseUnboundValueTypeList( + TokenType token, + TypeVector* types, + std::unordered_map<uint32_t, std::string>* type_names) { + WABT_TRACE(ParseUnboundValueTypeList); + while (MatchLpar(token)) { + CHECK_RESULT(ParseValueTypeList(types, type_names)); + EXPECT(Rpar); + } + return Result::Ok; +} + +Result WastParser::ParseResultList( + TypeVector* result_types, + std::unordered_map<uint32_t, std::string>* type_names) { + WABT_TRACE(ParseResultList); + return ParseUnboundValueTypeList(TokenType::Result, result_types, type_names); +} + +Result WastParser::ParseInstrList(ExprList* exprs) { + WABT_TRACE(ParseInstrList); + ExprList new_exprs; + while (true) { + auto pair = PeekPair(); + if (IsInstr(pair)) { + if (Succeeded(ParseInstr(&new_exprs))) { + exprs->splice(exprs->end(), new_exprs); + } else { + CHECK_RESULT(Synchronize(IsInstr)); + } + } else if (IsLparAnn(pair)) { + if (Succeeded(ParseCodeMetadataAnnotation(&new_exprs))) { + exprs->splice(exprs->end(), new_exprs); + } else { + CHECK_RESULT(Synchronize(IsLparAnn)); + } + } else { + break; + } + } + return Result::Ok; +} + +Result WastParser::ParseTerminatingInstrList(ExprList* exprs) { + WABT_TRACE(ParseTerminatingInstrList); + Result result = ParseInstrList(exprs); + // An InstrList often has no further Lpar following it, because it would have + // gobbled it up. So if there is a following Lpar it is an error. If we + // handle it here we can produce a nicer error message. + CHECK_RESULT(ErrorIfLpar({"an instr"})); + return result; +} + +Result WastParser::ParseInstr(ExprList* exprs) { + WABT_TRACE(ParseInstr); + if (IsPlainInstr(Peek())) { + std::unique_ptr<Expr> expr; + CHECK_RESULT(ParsePlainInstr(&expr)); + exprs->push_back(std::move(expr)); + return Result::Ok; + } else if (IsBlockInstr(Peek())) { + std::unique_ptr<Expr> expr; + CHECK_RESULT(ParseBlockInstr(&expr)); + exprs->push_back(std::move(expr)); + return Result::Ok; + } else if (PeekMatchExpr()) { + return ParseExpr(exprs); + } else { + assert(!"ParseInstr should only be called when IsInstr() is true"); + return Result::Error; + } +} + +Result WastParser::ParseCodeMetadataAnnotation(ExprList* exprs) { + WABT_TRACE(ParseCodeMetadataAnnotation); + Token tk = Consume(); + std::string_view name = tk.text(); + name.remove_prefix(sizeof("metadata.code.") - 1); + std::string data_text; + CHECK_RESULT(ParseQuotedText(&data_text, false)); + std::vector<uint8_t> data(data_text.begin(), data_text.end()); + exprs->push_back(std::make_unique<CodeMetadataExpr>(name, std::move(data))); + EXPECT(Rpar); + return Result::Ok; +} + +template <typename T> +Result WastParser::ParsePlainInstrVar(Location loc, + std::unique_ptr<Expr>* out_expr) { + Var var; + CHECK_RESULT(ParseVar(&var)); + out_expr->reset(new T(var, loc)); + return Result::Ok; +} + +template <typename T> +Result WastParser::ParseMemoryInstrVar(Location loc, + std::unique_ptr<Expr>* out_expr) { + Var memidx; + Var var; + if (PeekMatchLpar(TokenType::Memory)) { + if (!options_->features.multi_memory_enabled()) { + Error(loc, "Specifying memory variable is not allowed"); + return Result::Error; + } + CHECK_RESULT(ParseMemidx(loc, &memidx)); + CHECK_RESULT(ParseVar(&var)); + out_expr->reset(new T(var, memidx, loc)); + } else { + CHECK_RESULT(ParseVar(&memidx)); + if (ParseVarOpt(&var, Var(0, loc))) { + if (!options_->features.multi_memory_enabled()) { + Error(loc, "Specifiying memory variable is not allowed"); + return Result::Error; + } + out_expr->reset(new T(var, memidx, loc)); + } else { + out_expr->reset(new T(memidx, var, loc)); + } + } + return Result::Ok; +} + +template <typename T> +Result WastParser::ParseLoadStoreInstr(Location loc, + Token token, + std::unique_ptr<Expr>* out_expr) { + Opcode opcode = token.opcode(); + Var memidx; + Address offset; + Address align; + CHECK_RESULT(ParseMemidx(loc, &memidx)); + ParseOffsetOpt(&offset); + ParseAlignOpt(&align); + out_expr->reset(new T(opcode, memidx, align, offset, loc)); + return Result::Ok; +} + +template <typename T> +Result WastParser::ParseSIMDLoadStoreInstr(Location loc, + Token token, + std::unique_ptr<Expr>* out_expr) { + ErrorUnlessOpcodeEnabled(token); + + Var memidx(0, loc); + + if (options_->features.multi_memory_enabled()) { + // We have to be a little careful when reading the memeory index. + // If there is just a single integer folloing the instruction that + // represents the lane index, so we check for either a pair of intergers + // or an integers followed by offset= or align=. + bool try_read_mem_index = true; + if (PeekMatch(TokenType::Nat)) { + // The next token could be a memory index or a lane index + if (!PeekMatch(TokenType::OffsetEqNat, 1) && + !PeekMatch(TokenType::AlignEqNat, 1) && + !PeekMatch(TokenType::Nat, 1)) { + try_read_mem_index = false; + } + } + if (try_read_mem_index) { + CHECK_RESULT(ParseMemidx(loc, &memidx)); + } + } + Address offset; + Address align; + ParseOffsetOpt(&offset); + ParseAlignOpt(&align); + + uint64_t lane_idx = 0; + Result result = ParseSimdLane(loc, &lane_idx); + + if (Failed(result)) { + return Result::Error; + } + + out_expr->reset(new T(token.opcode(), memidx, align, offset, lane_idx, loc)); + return Result::Ok; +} + +template <typename T> +Result WastParser::ParseMemoryExpr(Location loc, + std::unique_ptr<Expr>* out_expr) { + Var memidx; + CHECK_RESULT(ParseMemidx(loc, &memidx)); + out_expr->reset(new T(memidx, loc)); + return Result::Ok; +} + +template <typename T> +Result WastParser::ParseMemoryBinaryExpr(Location loc, + std::unique_ptr<Expr>* out_expr) { + Var srcmemidx; + Var destmemidx; + CHECK_RESULT(ParseMemidx(loc, &srcmemidx)); + CHECK_RESULT(ParseMemidx(loc, &destmemidx)); + out_expr->reset(new T(srcmemidx, destmemidx, loc)); + return Result::Ok; +} + +Result WastParser::ParseSimdLane(Location loc, uint64_t* lane_idx) { + if (!PeekMatch(TokenType::Nat) && !PeekMatch(TokenType::Int)) { + return ErrorExpected({"a natural number in range [0, 32)"}); + } + + Literal literal = Consume().literal(); + + Result result = + ParseInt64(literal.text, lane_idx, ParseIntType::UnsignedOnly); + + if (Failed(result)) { + Error(loc, "invalid literal \"" PRIstringview "\"", + WABT_PRINTF_STRING_VIEW_ARG(literal.text)); + return Result::Error; + } + + // The valid range is only [0, 32), but it's only malformed if it can't + // fit in a byte. + if (*lane_idx > 255) { + Error(loc, "lane index \"" PRIstringview "\" out-of-range [0, 32)", + WABT_PRINTF_STRING_VIEW_ARG(literal.text)); + return Result::Error; + } + + return Result::Ok; +} + +Result WastParser::ParsePlainInstr(std::unique_ptr<Expr>* out_expr) { + WABT_TRACE(ParsePlainInstr); + Location loc = GetLocation(); + switch (Peek()) { + case TokenType::Unreachable: + Consume(); + out_expr->reset(new UnreachableExpr(loc)); + break; + + case TokenType::Nop: + Consume(); + out_expr->reset(new NopExpr(loc)); + break; + + case TokenType::Drop: + Consume(); + out_expr->reset(new DropExpr(loc)); + break; + + case TokenType::Select: { + Consume(); + TypeVector result; + if (options_->features.reference_types_enabled() && + PeekMatchLpar(TokenType::Result)) { + CHECK_RESULT(ParseResultList(&result, nullptr)); + } + out_expr->reset(new SelectExpr(result, loc)); + break; + } + + case TokenType::Br: + Consume(); + CHECK_RESULT(ParsePlainInstrVar<BrExpr>(loc, out_expr)); + break; + + case TokenType::BrIf: + Consume(); + CHECK_RESULT(ParsePlainInstrVar<BrIfExpr>(loc, out_expr)); + break; + + case TokenType::BrTable: { + Consume(); + auto expr = std::make_unique<BrTableExpr>(loc); + CHECK_RESULT(ParseVarList(&expr->targets)); + expr->default_target = expr->targets.back(); + expr->targets.pop_back(); + *out_expr = std::move(expr); + break; + } + + case TokenType::Return: + Consume(); + out_expr->reset(new ReturnExpr(loc)); + break; + + case TokenType::Call: + Consume(); + CHECK_RESULT(ParsePlainInstrVar<CallExpr>(loc, out_expr)); + break; + + case TokenType::CallIndirect: { + Consume(); + auto expr = std::make_unique<CallIndirectExpr>(loc); + ParseVarOpt(&expr->table, Var(0, loc)); + CHECK_RESULT(ParseTypeUseOpt(&expr->decl)); + CHECK_RESULT(ParseUnboundFuncSignature(&expr->decl.sig)); + *out_expr = std::move(expr); + break; + } + + case TokenType::CallRef: { + ErrorUnlessOpcodeEnabled(Consume()); + out_expr->reset(new CallRefExpr(loc)); + break; + } + + case TokenType::ReturnCall: + ErrorUnlessOpcodeEnabled(Consume()); + CHECK_RESULT(ParsePlainInstrVar<ReturnCallExpr>(loc, out_expr)); + break; + + case TokenType::ReturnCallIndirect: { + ErrorUnlessOpcodeEnabled(Consume()); + auto expr = std::make_unique<ReturnCallIndirectExpr>(loc); + ParseVarOpt(&expr->table, Var(0, loc)); + CHECK_RESULT(ParseTypeUseOpt(&expr->decl)); + CHECK_RESULT(ParseUnboundFuncSignature(&expr->decl.sig)); + *out_expr = std::move(expr); + break; + } + + case TokenType::LocalGet: + Consume(); + CHECK_RESULT(ParsePlainInstrVar<LocalGetExpr>(loc, out_expr)); + break; + + case TokenType::LocalSet: + Consume(); + CHECK_RESULT(ParsePlainInstrVar<LocalSetExpr>(loc, out_expr)); + break; + + case TokenType::LocalTee: + Consume(); + CHECK_RESULT(ParsePlainInstrVar<LocalTeeExpr>(loc, out_expr)); + break; + + case TokenType::GlobalGet: + Consume(); + CHECK_RESULT(ParsePlainInstrVar<GlobalGetExpr>(loc, out_expr)); + break; + + case TokenType::GlobalSet: + Consume(); + CHECK_RESULT(ParsePlainInstrVar<GlobalSetExpr>(loc, out_expr)); + break; + + case TokenType::Load: + CHECK_RESULT(ParseLoadStoreInstr<LoadExpr>(loc, Consume(), out_expr)); + break; + + case TokenType::Store: + CHECK_RESULT(ParseLoadStoreInstr<StoreExpr>(loc, Consume(), out_expr)); + break; + + case TokenType::Const: { + Const const_; + CHECK_RESULT(ParseConst(&const_, ConstType::Normal)); + out_expr->reset(new ConstExpr(const_, loc)); + break; + } + + case TokenType::Unary: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + out_expr->reset(new UnaryExpr(token.opcode(), loc)); + break; + } + + case TokenType::Binary: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + out_expr->reset(new BinaryExpr(token.opcode(), loc)); + break; + } + + case TokenType::Compare: + out_expr->reset(new CompareExpr(Consume().opcode(), loc)); + break; + + case TokenType::Convert: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + out_expr->reset(new ConvertExpr(token.opcode(), loc)); + break; + } + + case TokenType::MemoryCopy: + ErrorUnlessOpcodeEnabled(Consume()); + CHECK_RESULT(ParseMemoryBinaryExpr<MemoryCopyExpr>(loc, out_expr)); + break; + + case TokenType::MemoryFill: + ErrorUnlessOpcodeEnabled(Consume()); + CHECK_RESULT(ParseMemoryExpr<MemoryFillExpr>(loc, out_expr)); + break; + + case TokenType::DataDrop: + ErrorUnlessOpcodeEnabled(Consume()); + CHECK_RESULT(ParsePlainInstrVar<DataDropExpr>(loc, out_expr)); + break; + + case TokenType::MemoryInit: + ErrorUnlessOpcodeEnabled(Consume()); + CHECK_RESULT(ParseMemoryInstrVar<MemoryInitExpr>(loc, out_expr)); + break; + + case TokenType::MemorySize: + Consume(); + CHECK_RESULT(ParseMemoryExpr<MemorySizeExpr>(loc, out_expr)); + break; + + case TokenType::MemoryGrow: + Consume(); + CHECK_RESULT(ParseMemoryExpr<MemoryGrowExpr>(loc, out_expr)); + break; + + case TokenType::TableCopy: { + ErrorUnlessOpcodeEnabled(Consume()); + Var dst(0, loc); + Var src(0, loc); + if (options_->features.reference_types_enabled()) { + ParseVarOpt(&dst, dst); + ParseVarOpt(&src, src); + } + out_expr->reset(new TableCopyExpr(dst, src, loc)); + break; + } + + case TokenType::ElemDrop: + ErrorUnlessOpcodeEnabled(Consume()); + CHECK_RESULT(ParsePlainInstrVar<ElemDropExpr>(loc, out_expr)); + break; + + case TokenType::TableInit: { + ErrorUnlessOpcodeEnabled(Consume()); + Var segment_index(0, loc); + CHECK_RESULT(ParseVar(&segment_index)); + Var table_index(0, loc); + if (ParseVarOpt(&table_index, table_index)) { + // Here are the two forms: + // + // table.init $elemidx ... + // table.init $tableidx $elemidx ... + // + // So if both indexes are provided, we need to swap them. + std::swap(segment_index, table_index); + } + out_expr->reset(new TableInitExpr(segment_index, table_index, loc)); + break; + } + + case TokenType::TableGet: { + ErrorUnlessOpcodeEnabled(Consume()); + Var table_index(0, loc); + ParseVarOpt(&table_index, table_index); + out_expr->reset(new TableGetExpr(table_index, loc)); + break; + } + + case TokenType::TableSet: { + ErrorUnlessOpcodeEnabled(Consume()); + Var table_index(0, loc); + ParseVarOpt(&table_index, table_index); + out_expr->reset(new TableSetExpr(table_index, loc)); + break; + } + + case TokenType::TableGrow: { + ErrorUnlessOpcodeEnabled(Consume()); + Var table_index(0, loc); + ParseVarOpt(&table_index, table_index); + out_expr->reset(new TableGrowExpr(table_index, loc)); + break; + } + + case TokenType::TableSize: { + ErrorUnlessOpcodeEnabled(Consume()); + Var table_index(0, loc); + ParseVarOpt(&table_index, table_index); + out_expr->reset(new TableSizeExpr(table_index, loc)); + break; + } + + case TokenType::TableFill: { + ErrorUnlessOpcodeEnabled(Consume()); + Var table_index(0, loc); + ParseVarOpt(&table_index, table_index); + out_expr->reset(new TableFillExpr(table_index, loc)); + break; + } + + case TokenType::RefFunc: + ErrorUnlessOpcodeEnabled(Consume()); + CHECK_RESULT(ParsePlainInstrVar<RefFuncExpr>(loc, out_expr)); + break; + + case TokenType::RefNull: { + ErrorUnlessOpcodeEnabled(Consume()); + Type type; + CHECK_RESULT(ParseRefKind(&type)); + out_expr->reset(new RefNullExpr(type, loc)); + break; + } + + case TokenType::RefIsNull: + ErrorUnlessOpcodeEnabled(Consume()); + out_expr->reset(new RefIsNullExpr(loc)); + break; + + case TokenType::Throw: + ErrorUnlessOpcodeEnabled(Consume()); + CHECK_RESULT(ParsePlainInstrVar<ThrowExpr>(loc, out_expr)); + break; + + case TokenType::Rethrow: + ErrorUnlessOpcodeEnabled(Consume()); + CHECK_RESULT(ParsePlainInstrVar<RethrowExpr>(loc, out_expr)); + break; + + case TokenType::AtomicNotify: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT(ParseLoadStoreInstr<AtomicNotifyExpr>(loc, token, out_expr)); + break; + } + + case TokenType::AtomicFence: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + uint32_t consistency_model = 0x0; + out_expr->reset(new AtomicFenceExpr(consistency_model, loc)); + break; + } + + case TokenType::AtomicWait: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT(ParseLoadStoreInstr<AtomicWaitExpr>(loc, token, out_expr)); + break; + } + + case TokenType::AtomicLoad: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT(ParseLoadStoreInstr<AtomicLoadExpr>(loc, token, out_expr)); + break; + } + + case TokenType::AtomicStore: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT(ParseLoadStoreInstr<AtomicStoreExpr>(loc, token, out_expr)); + break; + } + + case TokenType::AtomicRmw: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT(ParseLoadStoreInstr<AtomicRmwExpr>(loc, token, out_expr)); + break; + } + + case TokenType::AtomicRmwCmpxchg: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + CHECK_RESULT( + ParseLoadStoreInstr<AtomicRmwCmpxchgExpr>(loc, token, out_expr)); + break; + } + + case TokenType::Ternary: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + out_expr->reset(new TernaryExpr(token.opcode(), loc)); + break; + } + + case TokenType::SimdLaneOp: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + + uint64_t lane_idx = 0; + Result result = ParseSimdLane(loc, &lane_idx); + + if (Failed(result)) { + return Result::Error; + } + + out_expr->reset(new SimdLaneOpExpr(token.opcode(), lane_idx, loc)); + break; + } + + case TokenType::SimdLoadLane: { + CHECK_RESULT( + ParseSIMDLoadStoreInstr<SimdLoadLaneExpr>(loc, Consume(), out_expr)); + break; + } + + case TokenType::SimdStoreLane: { + CHECK_RESULT( + ParseSIMDLoadStoreInstr<SimdStoreLaneExpr>(loc, Consume(), out_expr)); + break; + } + + case TokenType::SimdShuffleOp: { + Token token = Consume(); + ErrorUnlessOpcodeEnabled(token); + v128 values; + for (int lane = 0; lane < 16; ++lane) { + Location loc = GetLocation(); + uint64_t lane_idx; + Result result = ParseSimdLane(loc, &lane_idx); + if (Failed(result)) { + return Result::Error; + } + + values.set_u8(lane, static_cast<uint8_t>(lane_idx)); + } + + out_expr->reset(new SimdShuffleOpExpr(token.opcode(), values, loc)); + break; + } + + default: + assert( + !"ParsePlainInstr should only be called when IsPlainInstr() is true"); + return Result::Error; + } + + return Result::Ok; +} + +Result WastParser::ParseSimdV128Const(Const* const_, + TokenType token_type, + ConstType const_type) { + WABT_TRACE(ParseSimdV128Const); + + uint8_t lane_count = 0; + bool integer = true; + switch (token_type) { + case TokenType::I8X16: { lane_count = 16; break; } + case TokenType::I16X8: { lane_count = 8; break; } + case TokenType::I32X4: { lane_count = 4; break; } + case TokenType::I64X2: { lane_count = 2; break; } + case TokenType::F32X4: { lane_count = 4; integer = false; break; } + case TokenType::F64X2: { lane_count = 2; integer = false; break; } + default: { + Error(const_->loc, + "Unexpected type at start of simd constant. " + "Expected one of: i8x16, i16x8, i32x4, i64x2, f32x4, f64x2. " + "Found \"%s\".", + GetTokenTypeName(token_type)); + return Result::Error; + } + } + Consume(); + + const_->loc = GetLocation(); + + for (int lane = 0; lane < lane_count; ++lane) { + Location loc = GetLocation(); + + // Check that the lane literal type matches the element type of the v128: + Token token = GetToken(); + switch (token.token_type()) { + case TokenType::Nat: + case TokenType::Int: + // OK. + break; + + case TokenType::Float: + case TokenType::NanArithmetic: + case TokenType::NanCanonical: + if (integer) { + goto error; + } + break; + + error: + default: + if (integer) { + return ErrorExpected({"a Nat or Integer literal"}, "123"); + } else { + return ErrorExpected({"a Float literal"}, "42.0"); + } + } + + Result result; + + // For each type, parse the next literal, bound check it, and write it to + // the array of bytes: + if (integer) { + std::string_view sv = Consume().literal().text; + + switch (lane_count) { + case 16: { + uint8_t value = 0; + result = ParseInt8(sv, &value, ParseIntType::SignedAndUnsigned); + const_->set_v128_u8(lane, value); + break; + } + case 8: { + uint16_t value = 0; + result = ParseInt16(sv, &value, ParseIntType::SignedAndUnsigned); + const_->set_v128_u16(lane, value); + break; + } + case 4: { + uint32_t value = 0; + result = ParseInt32(sv, &value, ParseIntType::SignedAndUnsigned); + const_->set_v128_u32(lane, value); + break; + } + case 2: { + uint64_t value = 0; + result = ParseInt64(sv, &value, ParseIntType::SignedAndUnsigned); + const_->set_v128_u64(lane, value); + break; + } + } + } else { + Const lane_const_; + switch (lane_count) { + case 4: + result = ParseF32(&lane_const_, const_type); + const_->set_v128_f32(lane, lane_const_.f32_bits()); + break; + + case 2: + result = ParseF64(&lane_const_, const_type); + const_->set_v128_f64(lane, lane_const_.f64_bits()); + break; + } + + const_->set_expected_nan(lane, lane_const_.expected_nan()); + } + + if (Failed(result)) { + Error(loc, "invalid literal \"%s\"", token.to_string().c_str()); + return Result::Error; + } + } + + return Result::Ok; +} + +Result WastParser::ParseExpectedNan(ExpectedNan* expected) { + WABT_TRACE(ParseExpectedNan); + TokenType token_type = Peek(); + switch (token_type) { + case TokenType::NanArithmetic: + *expected = ExpectedNan::Arithmetic; + break; + case TokenType::NanCanonical: + *expected = ExpectedNan::Canonical; + break; + default: + return Result::Error; + } + Consume(); + return Result::Ok; +} + +Result WastParser::ParseF32(Const* const_, ConstType const_type) { + ExpectedNan expected; + if (const_type == ConstType::Expectation && + Succeeded(ParseExpectedNan(&expected))) { + const_->set_f32(expected); + return Result::Ok; + } + + auto token = Consume(); + if (!token.HasLiteral()) { + return Result::Error; + } + + auto literal = token.literal(); + uint32_t f32_bits; + Result result = ParseFloat(literal.type, literal.text, &f32_bits); + const_->set_f32(f32_bits); + return result; +} + +Result WastParser::ParseF64(Const* const_, ConstType const_type) { + ExpectedNan expected; + if (const_type == ConstType::Expectation && + Succeeded(ParseExpectedNan(&expected))) { + const_->set_f64(expected); + return Result::Ok; + } + + auto token = Consume(); + if (!token.HasLiteral()) { + return Result::Error; + } + + auto literal = token.literal(); + uint64_t f64_bits; + Result result = ParseDouble(literal.type, literal.text, &f64_bits); + const_->set_f64(f64_bits); + return result; +} + +Result WastParser::ParseConst(Const* const_, ConstType const_type) { + WABT_TRACE(ParseConst); + Token opcode_token = Consume(); + Opcode opcode = opcode_token.opcode(); + const_->loc = GetLocation(); + Token token = GetToken(); + + // V128 is fully handled by ParseSimdV128Const: + if (opcode != Opcode::V128Const) { + switch (token.token_type()) { + case TokenType::Nat: + case TokenType::Int: + case TokenType::Float: + // OK. + break; + case TokenType::NanArithmetic: + case TokenType::NanCanonical: + break; + default: + return ErrorExpected({"a numeric literal"}, "123, -45, 6.7e8"); + } + } + + Result result; + switch (opcode) { + case Opcode::I32Const: { + auto token = Consume(); + if (!token.HasLiteral()) { + return Result::Error; + } + auto sv = token.literal().text; + uint32_t u32; + result = ParseInt32(sv, &u32, ParseIntType::SignedAndUnsigned); + const_->set_u32(u32); + break; + } + + case Opcode::I64Const: { + auto token = Consume(); + if (!token.HasLiteral()) { + return Result::Error; + } + auto sv = token.literal().text; + uint64_t u64; + result = ParseInt64(sv, &u64, ParseIntType::SignedAndUnsigned); + const_->set_u64(u64); + break; + } + + case Opcode::F32Const: + result = ParseF32(const_, const_type); + break; + + case Opcode::F64Const: + result = ParseF64(const_, const_type); + break; + + case Opcode::V128Const: + ErrorUnlessOpcodeEnabled(opcode_token); + // Parse V128 Simd Const (16 bytes). + result = ParseSimdV128Const(const_, token.token_type(), const_type); + // ParseSimdV128Const report error already, just return here if parser get + // errors. + if (Failed(result)) { + return Result::Error; + } + break; + + default: + assert(!"ParseConst called with invalid opcode"); + return Result::Error; + } + + if (Failed(result)) { + Error(const_->loc, "invalid literal \"%s\"", token.to_string().c_str()); + // Return if parser get errors. + return Result::Error; + } + + return Result::Ok; +} + +Result WastParser::ParseExternref(Const* const_) { + WABT_TRACE(ParseExternref); + Token token = Consume(); + if (!options_->features.reference_types_enabled()) { + Error(token.loc, "externref not allowed"); + return Result::Error; + } + + Literal literal; + std::string_view sv; + const_->loc = GetLocation(); + TokenType token_type = Peek(); + + switch (token_type) { + case TokenType::Nat: + case TokenType::Int: { + literal = Consume().literal(); + sv = literal.text; + break; + } + default: + return ErrorExpected({"a numeric literal"}, "123"); + } + + uint64_t ref_bits; + Result result = ParseInt64(sv, &ref_bits, ParseIntType::UnsignedOnly); + + const_->set_externref(static_cast<uintptr_t>(ref_bits)); + + if (Failed(result)) { + Error(const_->loc, "invalid literal \"" PRIstringview "\"", + WABT_PRINTF_STRING_VIEW_ARG(literal.text)); + // Return if parser get errors. + return Result::Error; + } + + return Result::Ok; +} + +Result WastParser::ParseConstList(ConstVector* consts, ConstType type) { + WABT_TRACE(ParseConstList); + while (PeekMatchLpar(TokenType::Const) || PeekMatchLpar(TokenType::RefNull) || + PeekMatchLpar(TokenType::RefExtern) || + PeekMatchLpar(TokenType::RefFunc)) { + Consume(); + Const const_; + switch (Peek()) { + case TokenType::Const: + CHECK_RESULT(ParseConst(&const_, type)); + break; + case TokenType::RefNull: { + auto token = Consume(); + Type type; + CHECK_RESULT(ParseRefKind(&type)); + ErrorUnlessOpcodeEnabled(token); + const_.loc = GetLocation(); + const_.set_null(type); + break; + } + case TokenType::RefFunc: { + auto token = Consume(); + ErrorUnlessOpcodeEnabled(token); + const_.loc = GetLocation(); + const_.set_funcref(); + break; + } + case TokenType::RefExtern: + CHECK_RESULT(ParseExternref(&const_)); + break; + default: + assert(!"unreachable"); + return Result::Error; + } + EXPECT(Rpar); + consts->push_back(const_); + } + + return Result::Ok; +} + +Result WastParser::ParseBlockInstr(std::unique_ptr<Expr>* out_expr) { + WABT_TRACE(ParseBlockInstr); + Location loc = GetLocation(); + + switch (Peek()) { + case TokenType::Block: { + Consume(); + auto expr = std::make_unique<BlockExpr>(loc); + CHECK_RESULT(ParseLabelOpt(&expr->block.label)); + CHECK_RESULT(ParseBlock(&expr->block)); + EXPECT(End); + CHECK_RESULT(ParseEndLabelOpt(expr->block.label)); + *out_expr = std::move(expr); + break; + } + + case TokenType::Loop: { + Consume(); + auto expr = std::make_unique<LoopExpr>(loc); + CHECK_RESULT(ParseLabelOpt(&expr->block.label)); + CHECK_RESULT(ParseBlock(&expr->block)); + EXPECT(End); + CHECK_RESULT(ParseEndLabelOpt(expr->block.label)); + *out_expr = std::move(expr); + break; + } + + case TokenType::If: { + Consume(); + auto expr = std::make_unique<IfExpr>(loc); + CHECK_RESULT(ParseLabelOpt(&expr->true_.label)); + CHECK_RESULT(ParseBlock(&expr->true_)); + if (Match(TokenType::Else)) { + CHECK_RESULT(ParseEndLabelOpt(expr->true_.label)); + CHECK_RESULT(ParseTerminatingInstrList(&expr->false_)); + expr->false_end_loc = GetLocation(); + } + EXPECT(End); + CHECK_RESULT(ParseEndLabelOpt(expr->true_.label)); + *out_expr = std::move(expr); + break; + } + + case TokenType::Try: { + ErrorUnlessOpcodeEnabled(Consume()); + auto expr = std::make_unique<TryExpr>(loc); + CatchVector catches; + CHECK_RESULT(ParseLabelOpt(&expr->block.label)); + CHECK_RESULT(ParseBlock(&expr->block)); + if (IsCatch(Peek())) { + CHECK_RESULT(ParseCatchInstrList(&expr->catches)); + expr->kind = TryKind::Catch; + } else if (PeekMatch(TokenType::Delegate)) { + Consume(); + Var var; + CHECK_RESULT(ParseVar(&var)); + expr->delegate_target = var; + expr->kind = TryKind::Delegate; + } + CHECK_RESULT(ErrorIfLpar({"a valid try clause"})); + expr->block.end_loc = GetLocation(); + if (expr->kind != TryKind::Delegate) { + EXPECT(End); + } + CHECK_RESULT(ParseEndLabelOpt(expr->block.label)); + *out_expr = std::move(expr); + break; + } + + default: + assert( + !"ParseBlockInstr should only be called when IsBlockInstr() is true"); + return Result::Error; + } + + return Result::Ok; +} + +Result WastParser::ParseLabelOpt(std::string* out_label) { + WABT_TRACE(ParseLabelOpt); + if (PeekMatch(TokenType::Var)) { + *out_label = std::string(Consume().text()); + } else { + out_label->clear(); + } + return Result::Ok; +} + +Result WastParser::ParseEndLabelOpt(const std::string& begin_label) { + WABT_TRACE(ParseEndLabelOpt); + Location loc = GetLocation(); + std::string end_label; + CHECK_RESULT(ParseLabelOpt(&end_label)); + if (!end_label.empty()) { + if (begin_label.empty()) { + Error(loc, "unexpected label \"%s\"", end_label.c_str()); + } else if (begin_label != end_label) { + Error(loc, "mismatching label \"%s\" != \"%s\"", begin_label.c_str(), + end_label.c_str()); + } + } + return Result::Ok; +} + +Result WastParser::ParseBlockDeclaration(BlockDeclaration* decl) { + WABT_TRACE(ParseBlockDeclaration); + FuncDeclaration func_decl; + CHECK_RESULT(ParseTypeUseOpt(&func_decl)); + CHECK_RESULT(ParseUnboundFuncSignature(&func_decl.sig)); + decl->has_func_type = func_decl.has_func_type; + decl->type_var = func_decl.type_var; + decl->sig = func_decl.sig; + return Result::Ok; +} + +Result WastParser::ParseBlock(Block* block) { + WABT_TRACE(ParseBlock); + CHECK_RESULT(ParseBlockDeclaration(&block->decl)); + CHECK_RESULT(ParseInstrList(&block->exprs)); + block->end_loc = GetLocation(); + return Result::Ok; +} + +Result WastParser::ParseExprList(ExprList* exprs) { + WABT_TRACE(ParseExprList); + ExprList new_exprs; + while (PeekMatchExpr()) { + if (Succeeded(ParseExpr(&new_exprs))) { + exprs->splice(exprs->end(), new_exprs); + } else { + CHECK_RESULT(Synchronize(IsExpr)); + } + } + return Result::Ok; +} + +Result WastParser::ParseExpr(ExprList* exprs) { + WABT_TRACE(ParseExpr); + if (!PeekMatch(TokenType::Lpar)) { + return Result::Error; + } + + if (IsPlainInstr(Peek(1))) { + Consume(); + std::unique_ptr<Expr> expr; + CHECK_RESULT(ParsePlainInstr(&expr)); + CHECK_RESULT(ParseExprList(exprs)); + CHECK_RESULT(ErrorIfLpar({"an expr"})); + exprs->push_back(std::move(expr)); + } else { + Location loc = GetLocation(); + + switch (Peek(1)) { + case TokenType::Block: { + Consume(); + Consume(); + auto expr = std::make_unique<BlockExpr>(loc); + CHECK_RESULT(ParseLabelOpt(&expr->block.label)); + CHECK_RESULT(ParseBlock(&expr->block)); + exprs->push_back(std::move(expr)); + break; + } + + case TokenType::Loop: { + Consume(); + Consume(); + auto expr = std::make_unique<LoopExpr>(loc); + CHECK_RESULT(ParseLabelOpt(&expr->block.label)); + CHECK_RESULT(ParseBlock(&expr->block)); + exprs->push_back(std::move(expr)); + break; + } + + case TokenType::If: { + Consume(); + Consume(); + auto expr = std::make_unique<IfExpr>(loc); + + CHECK_RESULT(ParseLabelOpt(&expr->true_.label)); + CHECK_RESULT(ParseBlockDeclaration(&expr->true_.decl)); + + if (PeekMatchExpr()) { + ExprList cond; + CHECK_RESULT(ParseExpr(&cond)); + exprs->splice(exprs->end(), cond); + } + + if (MatchLpar(TokenType::Then)) { + CHECK_RESULT(ParseTerminatingInstrList(&expr->true_.exprs)); + expr->true_.end_loc = GetLocation(); + EXPECT(Rpar); + + if (MatchLpar(TokenType::Else)) { + CHECK_RESULT(ParseTerminatingInstrList(&expr->false_)); + EXPECT(Rpar); + } else if (PeekMatchExpr()) { + CHECK_RESULT(ParseExpr(&expr->false_)); + } + expr->false_end_loc = GetLocation(); + } else if (PeekMatchExpr()) { + CHECK_RESULT(ParseExpr(&expr->true_.exprs)); + expr->true_.end_loc = GetLocation(); + if (PeekMatchExpr()) { + CHECK_RESULT(ParseExpr(&expr->false_)); + expr->false_end_loc = GetLocation(); + } + } else { + ConsumeIfLpar(); + return ErrorExpected({"then block"}, "(then ...)"); + } + + exprs->push_back(std::move(expr)); + break; + } + + case TokenType::Try: { + Consume(); + ErrorUnlessOpcodeEnabled(Consume()); + + auto expr = std::make_unique<TryExpr>(loc); + CHECK_RESULT(ParseLabelOpt(&expr->block.label)); + CHECK_RESULT(ParseBlockDeclaration(&expr->block.decl)); + EXPECT(Lpar); + EXPECT(Do); + CHECK_RESULT(ParseInstrList(&expr->block.exprs)); + EXPECT(Rpar); + if (PeekMatch(TokenType::Lpar)) { + Consume(); + TokenType type = Peek(); + switch (type) { + case TokenType::Catch: + case TokenType::CatchAll: + CHECK_RESULT(ParseCatchExprList(&expr->catches)); + expr->kind = TryKind::Catch; + break; + case TokenType::Delegate: { + Consume(); + Var var; + CHECK_RESULT(ParseVar(&var)); + expr->delegate_target = var; + expr->kind = TryKind::Delegate; + EXPECT(Rpar); + break; + } + default: + ErrorExpected({"catch", "catch_all", "delegate"}); + break; + } + } + CHECK_RESULT(ErrorIfLpar({"a valid try clause"})); + expr->block.end_loc = GetLocation(); + exprs->push_back(std::move(expr)); + break; + } + + default: + assert(!"ParseExpr should only be called when IsExpr() is true"); + return Result::Error; + } + } + + EXPECT(Rpar); + return Result::Ok; +} + +Result WastParser::ParseCatchInstrList(CatchVector* catches) { + WABT_TRACE(ParseCatchInstrList); + bool parsedCatch = false; + bool parsedCatchAll = false; + + while (IsCatch(Peek())) { + Catch catch_(GetLocation()); + + auto token = Consume(); + if (token.token_type() == TokenType::Catch) { + CHECK_RESULT(ParseVar(&catch_.var)); + } else { + if (parsedCatchAll) { + Error(token.loc, "multiple catch_all clauses not allowed"); + return Result::Error; + } + parsedCatchAll = true; + } + + CHECK_RESULT(ParseInstrList(&catch_.exprs)); + catches->push_back(std::move(catch_)); + parsedCatch = true; + } + + if (!parsedCatch) { + return ErrorExpected({"catch"}); + } + + return Result::Ok; +} + +Result WastParser::ParseCatchExprList(CatchVector* catches) { + WABT_TRACE(ParseCatchExprList); + bool parsedCatchAll = false; + + do { + Catch catch_(GetLocation()); + + auto token = Consume(); + if (token.token_type() == TokenType::Catch) { + CHECK_RESULT(ParseVar(&catch_.var)); + } else { + if (parsedCatchAll) { + Error(token.loc, "multiple catch_all clauses not allowed"); + return Result::Error; + } + parsedCatchAll = true; + } + + CHECK_RESULT(ParseTerminatingInstrList(&catch_.exprs)); + EXPECT(Rpar); + catches->push_back(std::move(catch_)); + } while (Match(TokenType::Lpar) && IsCatch(Peek())); + + return Result::Ok; +} + +Result WastParser::ParseGlobalType(Global* global) { + WABT_TRACE(ParseGlobalType); + if (MatchLpar(TokenType::Mut)) { + global->mutable_ = true; + Var type; + CHECK_RESULT(ParseValueType(&type)); + global->type = Type(type.index()); + CHECK_RESULT(ErrorIfLpar({"i32", "i64", "f32", "f64"})); + EXPECT(Rpar); + } else { + Var type; + CHECK_RESULT(ParseValueType(&type)); + global->type = Type(type.index()); + } + + return Result::Ok; +} + +Result WastParser::ParseCommandList(Script* script, + CommandPtrVector* commands) { + WABT_TRACE(ParseCommandList); + while (IsCommand(PeekPair())) { + CommandPtr command; + if (Succeeded(ParseCommand(script, &command))) { + commands->push_back(std::move(command)); + } else { + CHECK_RESULT(Synchronize(IsCommand)); + } + } + return Result::Ok; +} + +Result WastParser::ParseCommand(Script* script, CommandPtr* out_command) { + WABT_TRACE(ParseCommand); + switch (Peek(1)) { + case TokenType::AssertException: + return ParseAssertExceptionCommand(out_command); + + case TokenType::AssertExhaustion: + return ParseAssertExhaustionCommand(out_command); + + case TokenType::AssertInvalid: + return ParseAssertInvalidCommand(out_command); + + case TokenType::AssertMalformed: + return ParseAssertMalformedCommand(out_command); + + case TokenType::AssertReturn: + return ParseAssertReturnCommand(out_command); + + case TokenType::AssertTrap: + return ParseAssertTrapCommand(out_command); + + case TokenType::AssertUnlinkable: + return ParseAssertUnlinkableCommand(out_command); + + case TokenType::Get: + case TokenType::Invoke: + return ParseActionCommand(out_command); + + case TokenType::Module: + return ParseModuleCommand(script, out_command); + + case TokenType::Register: + return ParseRegisterCommand(out_command); + + case TokenType::Input: + return ParseInputCommand(out_command); + + case TokenType::Output: + return ParseOutputCommand(out_command); + + default: + assert(!"ParseCommand should only be called when IsCommand() is true"); + return Result::Error; + } +} + +Result WastParser::ParseAssertExceptionCommand(CommandPtr* out_command) { + WABT_TRACE(ParseAssertExceptionCommand); + return ParseAssertActionCommand<AssertExceptionCommand>( + TokenType::AssertException, out_command); +} + +Result WastParser::ParseAssertExhaustionCommand(CommandPtr* out_command) { + WABT_TRACE(ParseAssertExhaustionCommand); + return ParseAssertActionTextCommand<AssertExhaustionCommand>( + TokenType::AssertExhaustion, out_command); +} + +Result WastParser::ParseAssertInvalidCommand(CommandPtr* out_command) { + WABT_TRACE(ParseAssertInvalidCommand); + return ParseAssertScriptModuleCommand<AssertInvalidCommand>( + TokenType::AssertInvalid, out_command); +} + +Result WastParser::ParseAssertMalformedCommand(CommandPtr* out_command) { + WABT_TRACE(ParseAssertMalformedCommand); + return ParseAssertScriptModuleCommand<AssertMalformedCommand>( + TokenType::AssertMalformed, out_command); +} + +Result WastParser::ParseAssertReturnCommand(CommandPtr* out_command) { + WABT_TRACE(ParseAssertReturnCommand); + EXPECT(Lpar); + EXPECT(AssertReturn); + auto command = std::make_unique<AssertReturnCommand>(); + CHECK_RESULT(ParseAction(&command->action)); + CHECK_RESULT(ParseExpectedValues(&command->expected)); + EXPECT(Rpar); + *out_command = std::move(command); + return Result::Ok; +} + +Result WastParser::ParseAssertTrapCommand(CommandPtr* out_command) { + WABT_TRACE(ParseAssertTrapCommand); + EXPECT(Lpar); + EXPECT(AssertTrap); + if (PeekMatchLpar(TokenType::Module)) { + auto command = std::make_unique<AssertUninstantiableCommand>(); + CHECK_RESULT(ParseScriptModule(&command->module)); + CHECK_RESULT(ParseQuotedText(&command->text)); + *out_command = std::move(command); + } else { + auto command = std::make_unique<AssertTrapCommand>(); + CHECK_RESULT(ParseAction(&command->action)); + CHECK_RESULT(ParseQuotedText(&command->text)); + *out_command = std::move(command); + } + EXPECT(Rpar); + return Result::Ok; +} + +Result WastParser::ParseAssertUnlinkableCommand(CommandPtr* out_command) { + WABT_TRACE(ParseAssertUnlinkableCommand); + return ParseAssertScriptModuleCommand<AssertUnlinkableCommand>( + TokenType::AssertUnlinkable, out_command); +} + +Result WastParser::ParseActionCommand(CommandPtr* out_command) { + WABT_TRACE(ParseActionCommand); + auto command = std::make_unique<ActionCommand>(); + CHECK_RESULT(ParseAction(&command->action)); + *out_command = std::move(command); + return Result::Ok; +} + +Result WastParser::ParseModuleCommand(Script* script, CommandPtr* out_command) { + WABT_TRACE(ParseModuleCommand); + std::unique_ptr<ScriptModule> script_module; + CHECK_RESULT(ParseScriptModule(&script_module)); + + Module* module = nullptr; + + switch (script_module->type()) { + case ScriptModuleType::Text: { + auto command = std::make_unique<ModuleCommand>(); + module = &command->module; + *module = std::move(cast<TextScriptModule>(script_module.get())->module); + *out_command = std::move(command); + break; + } + + case ScriptModuleType::Binary: { + auto command = std::make_unique<ScriptModuleCommand>(); + module = &command->module; + auto* bsm = cast<BinaryScriptModule>(script_module.get()); + ReadBinaryOptions options; +#if WABT_TRACING + auto log_stream = FileStream::CreateStdout(); + options.log_stream = log_stream.get(); +#endif + options.features = options_->features; + Errors errors; + const char* filename = "<text>"; + ReadBinaryIr(filename, bsm->data.data(), bsm->data.size(), options, + &errors, module); + module->name = bsm->name; + module->loc = bsm->loc; + for (const auto& error : errors) { + assert(error.error_level == ErrorLevel::Error); + if (error.loc.offset == kInvalidOffset) { + Error(bsm->loc, "error in binary module: %s", error.message.c_str()); + } else { + Error(bsm->loc, "error in binary module: @0x%08" PRIzx ": %s", + error.loc.offset, error.message.c_str()); + } + } + + command->script_module = std::move(script_module); + *out_command = std::move(command); + break; + } + + case ScriptModuleType::Quoted: + return ErrorExpected({"a binary module", "a text module"}); + } + + // script is nullptr when ParseModuleCommand is called from ParseModule. + if (script) { + Index command_index = script->commands.size(); + + if (!module->name.empty()) { + script->module_bindings.emplace(module->name, + Binding(module->loc, command_index)); + } + + last_module_index_ = command_index; + } + + return Result::Ok; +} + +Result WastParser::ParseRegisterCommand(CommandPtr* out_command) { + WABT_TRACE(ParseRegisterCommand); + EXPECT(Lpar); + Location loc = GetLocation(); + EXPECT(Register); + std::string text; + Var var; + CHECK_RESULT(ParseQuotedText(&text)); + ParseVarOpt(&var, Var(last_module_index_, loc)); + EXPECT(Rpar); + out_command->reset(new RegisterCommand(text, var)); + return Result::Ok; +} + +Result WastParser::ParseInputCommand(CommandPtr*) { + // Parse the input command, but always fail since this command is not + // actually supported. + WABT_TRACE(ParseInputCommand); + EXPECT(Lpar); + Location loc = GetLocation(); + EXPECT(Input); + Error(loc, "input command is not supported"); + Var var; + std::string text; + ParseVarOpt(&var); + CHECK_RESULT(ParseQuotedText(&text)); + EXPECT(Rpar); + return Result::Error; +} + +Result WastParser::ParseOutputCommand(CommandPtr*) { + // Parse the output command, but always fail since this command is not + // actually supported. + WABT_TRACE(ParseOutputCommand); + EXPECT(Lpar); + Location loc = GetLocation(); + EXPECT(Output); + Error(loc, "output command is not supported"); + Var var; + std::string text; + ParseVarOpt(&var); + if (Peek() == TokenType::Text) { + CHECK_RESULT(ParseQuotedText(&text)); + } + EXPECT(Rpar); + return Result::Error; +} + +Result WastParser::ParseAction(ActionPtr* out_action) { + WABT_TRACE(ParseAction); + EXPECT(Lpar); + Location loc = GetLocation(); + + switch (Peek()) { + case TokenType::Invoke: { + Consume(); + auto action = std::make_unique<InvokeAction>(loc); + ParseVarOpt(&action->module_var, Var(last_module_index_, loc)); + CHECK_RESULT(ParseQuotedText(&action->name)); + CHECK_RESULT(ParseConstList(&action->args, ConstType::Normal)); + *out_action = std::move(action); + break; + } + + case TokenType::Get: { + Consume(); + auto action = std::make_unique<GetAction>(loc); + ParseVarOpt(&action->module_var, Var(last_module_index_, loc)); + CHECK_RESULT(ParseQuotedText(&action->name)); + *out_action = std::move(action); + break; + } + + default: + return ErrorExpected({"invoke", "get"}); + } + EXPECT(Rpar); + return Result::Ok; +} + +Result WastParser::ParseExpectedValues(ExpectationPtr* expectation) { + WABT_TRACE(ParseExpectedValues); + Location loc = GetLocation(); + if (PeekMatchLpar(TokenType::Either)) { + auto either = std::make_unique<EitherExpectation>(loc); + CHECK_RESULT(ParseEither(&either->expected)); + *expectation = std::move(either); + } else { + auto values = std::make_unique<ValueExpectation>(loc); + CHECK_RESULT(ParseConstList(&values->expected, ConstType::Expectation)); + *expectation = std::move(values); + } + return Result::Ok; +} + +Result WastParser::ParseEither(ConstVector* alternatives) { + WABT_TRACE(ParseEither); + MatchLpar(TokenType::Either); + CHECK_RESULT(ParseConstList(alternatives, ConstType::Expectation)); + EXPECT(Rpar); + return Result::Ok; +} + +Result WastParser::ParseScriptModule( + std::unique_ptr<ScriptModule>* out_module) { + WABT_TRACE(ParseScriptModule); + EXPECT(Lpar); + Location loc = GetLocation(); + EXPECT(Module); + std::string name; + ParseBindVarOpt(&name); + + switch (Peek()) { + case TokenType::Bin: { + Consume(); + std::vector<uint8_t> data; + // TODO(binji): The spec allows this to be empty, switch to + // ParseTextListOpt. + CHECK_RESULT(ParseTextList(&data)); + + auto bsm = std::make_unique<BinaryScriptModule>(); + bsm->name = name; + bsm->loc = loc; + bsm->data = std::move(data); + *out_module = std::move(bsm); + break; + } + + case TokenType::Quote: { + Consume(); + std::vector<uint8_t> data; + // TODO(binji): The spec allows this to be empty, switch to + // ParseTextListOpt. + CHECK_RESULT(ParseTextList(&data)); + + auto qsm = std::make_unique<QuotedScriptModule>(); + qsm->name = name; + qsm->loc = loc; + qsm->data = std::move(data); + *out_module = std::move(qsm); + break; + } + + default: { + auto tsm = std::make_unique<TextScriptModule>(); + tsm->module.name = name; + tsm->module.loc = loc; + if (IsModuleField(PeekPair())) { + CHECK_RESULT(ParseModuleFieldList(&tsm->module)); + } else if (!PeekMatch(TokenType::Rpar)) { + ConsumeIfLpar(); + return ErrorExpected({"a module field"}); + } + *out_module = std::move(tsm); + break; + } + } + + EXPECT(Rpar); + return Result::Ok; +} + +template <typename T> +Result WastParser::ParseAssertActionCommand(TokenType token_type, + CommandPtr* out_command) { + WABT_TRACE(ParseAssertActionCommand); + EXPECT(Lpar); + CHECK_RESULT(Expect(token_type)); + auto command = std::make_unique<T>(); + CHECK_RESULT(ParseAction(&command->action)); + EXPECT(Rpar); + *out_command = std::move(command); + return Result::Ok; +} + +template <typename T> +Result WastParser::ParseAssertActionTextCommand(TokenType token_type, + CommandPtr* out_command) { + WABT_TRACE(ParseAssertActionTextCommand); + EXPECT(Lpar); + CHECK_RESULT(Expect(token_type)); + auto command = std::make_unique<T>(); + CHECK_RESULT(ParseAction(&command->action)); + CHECK_RESULT(ParseQuotedText(&command->text)); + EXPECT(Rpar); + *out_command = std::move(command); + return Result::Ok; +} + +template <typename T> +Result WastParser::ParseAssertScriptModuleCommand(TokenType token_type, + CommandPtr* out_command) { + WABT_TRACE(ParseAssertScriptModuleCommand); + EXPECT(Lpar); + CHECK_RESULT(Expect(token_type)); + auto command = std::make_unique<T>(); + CHECK_RESULT(ParseScriptModule(&command->module)); + CHECK_RESULT(ParseQuotedText(&command->text)); + EXPECT(Rpar); + *out_command = std::move(command); + return Result::Ok; +} + +void WastParser::CheckImportOrdering(Module* module) { + if (module->funcs.size() != module->num_func_imports || + module->tables.size() != module->num_table_imports || + module->memories.size() != module->num_memory_imports || + module->globals.size() != module->num_global_imports || + module->tags.size() != module->num_tag_imports) { + Error(GetLocation(), + "imports must occur before all non-import definitions"); + } +} + +Result ParseWatModule(WastLexer* lexer, + std::unique_ptr<Module>* out_module, + Errors* errors, + WastParseOptions* options) { + assert(out_module != nullptr); + assert(options != nullptr); + WastParser parser(lexer, errors, options); + CHECK_RESULT(parser.ParseModule(out_module)); + return Result::Ok; +} + +Result ParseWastScript(WastLexer* lexer, + std::unique_ptr<Script>* out_script, + Errors* errors, + WastParseOptions* options) { + assert(out_script != nullptr); + assert(options != nullptr); + WastParser parser(lexer, errors, options); + CHECK_RESULT(parser.ParseScript(out_script)); + CHECK_RESULT(ResolveNamesScript(out_script->get(), errors)); + return Result::Ok; +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/wat-writer.cc b/third_party/wasm2c/src/wat-writer.cc new file mode 100644 index 0000000000..ea70edb389 --- /dev/null +++ b/third_party/wasm2c/src/wat-writer.cc @@ -0,0 +1,1789 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/wat-writer.h" + +#include <algorithm> +#include <array> +#include <cassert> +#include <cinttypes> +#include <cstdarg> +#include <cstdio> +#include <iterator> +#include <map> +#include <string> +#include <vector> + +#include "wabt/cast.h" +#include "wabt/common.h" +#include "wabt/expr-visitor.h" +#include "wabt/ir-util.h" +#include "wabt/ir.h" +#include "wabt/literal.h" +#include "wabt/stream.h" + +#define WABT_TRACING 0 +#include "wabt/tracing.h" + +#define INDENT_SIZE 2 +#define NO_FORCE_NEWLINE 0 +#define FORCE_NEWLINE 1 + +namespace wabt { + +namespace { + +static const uint8_t s_is_char_escaped[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + +// This table matches the characters allowed by wast-lexer.cc for `symbol`. +// The disallowed printable characters are: "(),;[]{} and <space>. +static const uint8_t s_valid_name_chars[256] = { + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x20 */ 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, + /* 0x30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, + /* 0x40 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + /* 0x50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, + /* 0x60 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + /* 0x70 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, +}; + +enum class NextChar { + None, + Space, + Newline, + ForceNewline, +}; + +struct ExprTree { + explicit ExprTree(const Expr* expr, Index result_count) + : expr(expr), result_count(result_count) {} + + const Expr* expr; + std::vector<ExprTree> children; + Index result_count; +}; + +class WatWriter : ModuleContext { + public: + WatWriter(Stream* stream, + const WriteWatOptions& options, + const Module& module) + : ModuleContext(module), options_(options), stream_(stream) {} + + Result WriteModule(); + + private: + void Indent(); + void Dedent(); + void WriteIndent(); + void WriteNextChar(); + void WriteDataWithNextChar(const void* src, size_t size); + void Writef(const char* format, ...); + void WritePutc(char c); + void WritePuts(const char* s, NextChar next_char); + void WritePutsSpace(const char* s); + void WritePutsNewline(const char* s); + void WriteNewline(bool force); + void WriteOpen(const char* name, NextChar next_char); + void WriteOpenNewline(const char* name); + void WriteOpenSpace(const char* name); + void WriteClose(NextChar next_char); + void WriteCloseNewline(); + void WriteCloseSpace(); + void WriteString(const std::string& str, NextChar next_char); + void WriteName(std::string_view str, NextChar next_char); + void WriteNameOrIndex(std::string_view str, Index index, NextChar next_char); + void WriteQuotedData(const void* data, size_t length); + void WriteQuotedString(std::string_view str, NextChar next_char); + void WriteVar(const Var& var, NextChar next_char); + void WriteVarUnlessZero(const Var& var, NextChar next_char); + void WriteMemoryVarUnlessZero(const Var& memidx, NextChar next_char); + void WriteTwoMemoryVarsUnlessBothZero(const Var& srcmemidx, + const Var& destmemidx, + NextChar next_char); + void WriteBrVar(const Var& var, NextChar next_char); + void WriteRefKind(Type type, NextChar next_char); + void WriteType(Type type, NextChar next_char); + void WriteTypes(const TypeVector& types, const char* name); + void WriteFuncSigSpace(const FuncSignature& func_sig); + void WriteBeginBlock(LabelType label_type, + const Block& block, + const char* text); + void WriteEndBlock(); + void WriteConst(const Const& const_); + void WriteExpr(const Expr* expr); + template <typename T> + void WriteLoadStoreExpr(const Expr* expr); + template <typename T> + void WriteMemoryLoadStoreExpr(const Expr* expr); + void WriteExprList(const ExprList& exprs); + void WriteInitExpr(const ExprList& expr); + template <typename T> + void WriteTypeBindings(const char* prefix, + const T& types, + const std::vector<std::string>& index_to_name, + Index binding_index_offset = 0); + void WriteBeginFunc(const Func& func); + void WriteFunc(const Func& func); + void WriteBeginGlobal(const Global& global); + void WriteGlobal(const Global& global); + void WriteTag(const Tag& tag); + void WriteLimits(const Limits& limits); + void WriteTable(const Table& table); + void WriteElemSegment(const ElemSegment& segment); + void WriteMemory(const Memory& memory); + void WriteDataSegment(const DataSegment& segment); + void WriteImport(const Import& import); + void WriteExport(const Export& export_); + void WriteTypeEntry(const TypeEntry& type); + void WriteField(const Field& field); + void WriteStartFunction(const Var& start); + + class ExprVisitorDelegate; + + void PushExpr(const Expr* expr, Index operand_count, Index result_count); + void FlushExprTree(const ExprTree& expr_tree); + void FlushExprTreeVector(const std::vector<ExprTree>&); + void FlushExprTreeStack(); + void WriteFoldedExpr(const Expr*); + void WriteFoldedExprList(const ExprList&); + + void BuildInlineExportMap(); + void WriteInlineExports(ExternalKind, Index); + bool IsInlineExport(const Export& export_); + void BuildInlineImportMap(); + void WriteInlineImport(ExternalKind, Index); + + const WriteWatOptions& options_; + Stream* stream_ = nullptr; + Result result_ = Result::Ok; + int indent_ = 0; + NextChar next_char_ = NextChar::None; + std::vector<ExprTree> expr_tree_stack_; + std::multimap<std::pair<ExternalKind, Index>, const Export*> + inline_export_map_; + std::vector<const Import*> inline_import_map_[kExternalKindCount]; + + Index func_index_ = 0; + Index global_index_ = 0; + Index table_index_ = 0; + Index memory_index_ = 0; + Index type_index_ = 0; + Index tag_index_ = 0; + Index data_segment_index_ = 0; + Index elem_segment_index_ = 0; +}; + +void WatWriter::Indent() { + indent_ += INDENT_SIZE; +} + +void WatWriter::Dedent() { + indent_ -= INDENT_SIZE; + assert(indent_ >= 0); +} + +void WatWriter::WriteIndent() { + static char s_indent[] = + " " + " "; + static size_t s_indent_len = sizeof(s_indent) - 1; + size_t to_write = indent_; + while (to_write >= s_indent_len) { + stream_->WriteData(s_indent, s_indent_len); + to_write -= s_indent_len; + } + if (to_write > 0) { + stream_->WriteData(s_indent, to_write); + } +} + +void WatWriter::WriteNextChar() { + switch (next_char_) { + case NextChar::Space: + stream_->WriteChar(' '); + break; + case NextChar::Newline: + case NextChar::ForceNewline: + stream_->WriteChar('\n'); + WriteIndent(); + break; + case NextChar::None: + break; + } + next_char_ = NextChar::None; +} + +void WatWriter::WriteDataWithNextChar(const void* src, size_t size) { + WriteNextChar(); + stream_->WriteData(src, size); +} + +void WABT_PRINTF_FORMAT(2, 3) WatWriter::Writef(const char* format, ...) { + WABT_SNPRINTF_ALLOCA(buffer, length, format); + /* default to following space */ + WriteDataWithNextChar(buffer, length); + next_char_ = NextChar::Space; +} + +void WatWriter::WritePutc(char c) { + stream_->WriteChar(c); +} + +void WatWriter::WritePuts(const char* s, NextChar next_char) { + size_t len = strlen(s); + WriteDataWithNextChar(s, len); + next_char_ = next_char; +} + +void WatWriter::WritePutsSpace(const char* s) { + WritePuts(s, NextChar::Space); +} + +void WatWriter::WritePutsNewline(const char* s) { + WritePuts(s, NextChar::Newline); +} + +void WatWriter::WriteNewline(bool force) { + if (next_char_ == NextChar::ForceNewline) { + WriteNextChar(); + } + next_char_ = force ? NextChar::ForceNewline : NextChar::Newline; +} + +void WatWriter::WriteOpen(const char* name, NextChar next_char) { + WritePuts("(", NextChar::None); + WritePuts(name, next_char); + Indent(); +} + +void WatWriter::WriteOpenNewline(const char* name) { + WriteOpen(name, NextChar::Newline); +} + +void WatWriter::WriteOpenSpace(const char* name) { + WriteOpen(name, NextChar::Space); +} + +void WatWriter::WriteClose(NextChar next_char) { + if (next_char_ != NextChar::ForceNewline) { + next_char_ = NextChar::None; + } + Dedent(); + WritePuts(")", next_char); +} + +void WatWriter::WriteCloseNewline() { + WriteClose(NextChar::Newline); +} + +void WatWriter::WriteCloseSpace() { + WriteClose(NextChar::Space); +} + +void WatWriter::WriteString(const std::string& str, NextChar next_char) { + WritePuts(str.c_str(), next_char); +} + +void WatWriter::WriteName(std::string_view str, NextChar next_char) { + // Debug names must begin with a $ for for wast file to be valid + assert(!str.empty() && str.front() == '$'); + bool has_invalid_chars = std::any_of( + str.begin(), str.end(), [](uint8_t c) { return !s_valid_name_chars[c]; }); + + if (has_invalid_chars) { + std::string valid_str; + std::transform(str.begin(), str.end(), std::back_inserter(valid_str), + [](uint8_t c) { return s_valid_name_chars[c] ? c : '_'; }); + WriteDataWithNextChar(valid_str.data(), valid_str.length()); + } else { + WriteDataWithNextChar(str.data(), str.length()); + } + + next_char_ = next_char; +} + +void WatWriter::WriteNameOrIndex(std::string_view str, + Index index, + NextChar next_char) { + if (!str.empty()) { + WriteName(str, next_char); + } else { + Writef("(;%u;)", index); + } +} + +void WatWriter::WriteQuotedData(const void* data, size_t length) { + const uint8_t* u8_data = static_cast<const uint8_t*>(data); + static const char s_hexdigits[] = "0123456789abcdef"; + WriteNextChar(); + WritePutc('\"'); + for (size_t i = 0; i < length; ++i) { + uint8_t c = u8_data[i]; + if (s_is_char_escaped[c]) { + WritePutc('\\'); + WritePutc(s_hexdigits[c >> 4]); + WritePutc(s_hexdigits[c & 0xf]); + } else { + WritePutc(c); + } + } + WritePutc('\"'); + next_char_ = NextChar::Space; +} + +void WatWriter::WriteQuotedString(std::string_view str, NextChar next_char) { + WriteQuotedData(str.data(), str.length()); + next_char_ = next_char; +} + +void WatWriter::WriteVar(const Var& var, NextChar next_char) { + if (var.is_index()) { + Writef("%" PRIindex, var.index()); + next_char_ = next_char; + } else { + WriteName(var.name(), next_char); + } +} + +bool VarIsZero(const Var& var) { + return var.is_index() && var.index() == 0; +} + +void WatWriter::WriteVarUnlessZero(const Var& var, NextChar next_char) { + if (!VarIsZero(var)) { + WriteVar(var, next_char); + } +} + +void WatWriter::WriteMemoryVarUnlessZero(const Var& memidx, + NextChar next_char) { + if (module.GetMemoryIndex(memidx) != 0) { + WriteVar(memidx, next_char); + } else { + next_char_ = next_char; + } +} + +void WatWriter::WriteTwoMemoryVarsUnlessBothZero(const Var& srcmemidx, + const Var& destmemidx, + NextChar next_char) { + if (module.GetMemoryIndex(srcmemidx) != 0 || + module.GetMemoryIndex(destmemidx) != 0) { + WriteVar(srcmemidx, NextChar::Space); + WriteVar(destmemidx, next_char); + } else { + next_char_ = next_char; + } +} + +void WatWriter::WriteBrVar(const Var& var, NextChar next_char) { + if (var.is_index()) { + if (var.index() < GetLabelStackSize()) { + Writef("%" PRIindex " (;@%" PRIindex ";)", var.index(), + GetLabelStackSize() - var.index() - 1); + } else { + Writef("%" PRIindex " (; INVALID ;)", var.index()); + } + next_char_ = next_char; + } else { + WriteString(var.name(), next_char); + } +} + +void WatWriter::WriteRefKind(Type type, NextChar next_char) { + WritePuts(type.GetRefKindName(), next_char); +} + +void WatWriter::WriteType(Type type, NextChar next_char) { + WritePuts(type.GetName().c_str(), next_char); +} + +void WatWriter::WriteTypes(const TypeVector& types, const char* name) { + if (types.size()) { + if (name) { + WriteOpenSpace(name); + } + for (Type type : types) { + WriteType(type, NextChar::Space); + } + if (name) { + WriteCloseSpace(); + } + } +} + +void WatWriter::WriteFuncSigSpace(const FuncSignature& func_sig) { + WriteTypes(func_sig.param_types, "param"); + WriteTypes(func_sig.result_types, "result"); +} + +void WatWriter::WriteBeginBlock(LabelType label_type, + const Block& block, + const char* text) { + WritePutsSpace(text); + bool has_label = !block.label.empty(); + if (has_label) { + WriteString(block.label, NextChar::Space); + } + WriteTypes(block.decl.sig.param_types, "param"); + WriteTypes(block.decl.sig.result_types, "result"); + if (!has_label) { + Writef(" ;; label = @%" PRIindex, GetLabelStackSize()); + } + WriteNewline(FORCE_NEWLINE); + BeginBlock(label_type, block); + Indent(); +} + +void WatWriter::WriteEndBlock() { + Dedent(); + EndBlock(); + WritePutsNewline(Opcode::End_Opcode.GetName()); +} + +void WatWriter::WriteConst(const Const& const_) { + switch (const_.type()) { + case Type::I32: + WritePutsSpace(Opcode::I32Const_Opcode.GetName()); + Writef("%d", static_cast<int32_t>(const_.u32())); + WriteNewline(NO_FORCE_NEWLINE); + break; + + case Type::I64: + WritePutsSpace(Opcode::I64Const_Opcode.GetName()); + Writef("%" PRId64, static_cast<int64_t>(const_.u64())); + WriteNewline(NO_FORCE_NEWLINE); + break; + + case Type::F32: { + WritePutsSpace(Opcode::F32Const_Opcode.GetName()); + char buffer[128]; + WriteFloatHex(buffer, 128, const_.f32_bits()); + WritePutsSpace(buffer); + Writef("(;=%g;)", Bitcast<float>(const_.f32_bits())); + WriteNewline(NO_FORCE_NEWLINE); + break; + } + + case Type::F64: { + WritePutsSpace(Opcode::F64Const_Opcode.GetName()); + char buffer[128]; + WriteDoubleHex(buffer, 128, const_.f64_bits()); + WritePutsSpace(buffer); + Writef("(;=%g;)", Bitcast<double>(const_.f64_bits())); + WriteNewline(NO_FORCE_NEWLINE); + break; + } + + case Type::V128: { + WritePutsSpace(Opcode::V128Const_Opcode.GetName()); + auto vec = const_.vec128(); + Writef("i32x4 0x%08x 0x%08x 0x%08x 0x%08x", vec.u32(0), vec.u32(1), + vec.u32(2), vec.u32(3)); + WriteNewline(NO_FORCE_NEWLINE); + break; + } + + default: + assert(0); + break; + } +} + +template <typename T> +void WatWriter::WriteLoadStoreExpr(const Expr* expr) { + auto typed_expr = cast<T>(expr); + WritePutsSpace(typed_expr->opcode.GetName()); + if (typed_expr->offset) { + Writef("offset=%" PRIaddress, typed_expr->offset); + } + if (!typed_expr->opcode.IsNaturallyAligned(typed_expr->align)) { + Writef("align=%" PRIaddress, typed_expr->align); + } + WriteNewline(NO_FORCE_NEWLINE); +} + +template <typename T> +void WatWriter::WriteMemoryLoadStoreExpr(const Expr* expr) { + auto typed_expr = cast<T>(expr); + WritePutsSpace(typed_expr->opcode.GetName()); + WriteMemoryVarUnlessZero(typed_expr->memidx, NextChar::Space); + if (typed_expr->offset) { + Writef("offset=%" PRIaddress, typed_expr->offset); + } + if (!typed_expr->opcode.IsNaturallyAligned(typed_expr->align)) { + Writef("align=%" PRIaddress, typed_expr->align); + } + WriteNewline(NO_FORCE_NEWLINE); +} + +class WatWriter::ExprVisitorDelegate : public ExprVisitor::Delegate { + public: + explicit ExprVisitorDelegate(WatWriter* writer) : writer_(writer) {} + + Result OnBinaryExpr(BinaryExpr*) override; + Result BeginBlockExpr(BlockExpr*) override; + Result EndBlockExpr(BlockExpr*) override; + Result OnBrExpr(BrExpr*) override; + Result OnBrIfExpr(BrIfExpr*) override; + Result OnBrTableExpr(BrTableExpr*) override; + Result OnCallExpr(CallExpr*) override; + Result OnCallIndirectExpr(CallIndirectExpr*) override; + Result OnCallRefExpr(CallRefExpr*) override; + Result OnCodeMetadataExpr(CodeMetadataExpr*) override; + Result OnCompareExpr(CompareExpr*) override; + Result OnConstExpr(ConstExpr*) override; + Result OnConvertExpr(ConvertExpr*) override; + Result OnDropExpr(DropExpr*) override; + Result OnGlobalGetExpr(GlobalGetExpr*) override; + Result OnGlobalSetExpr(GlobalSetExpr*) override; + Result BeginIfExpr(IfExpr*) override; + Result AfterIfTrueExpr(IfExpr*) override; + Result EndIfExpr(IfExpr*) override; + Result OnLoadExpr(LoadExpr*) override; + Result OnLocalGetExpr(LocalGetExpr*) override; + Result OnLocalSetExpr(LocalSetExpr*) override; + Result OnLocalTeeExpr(LocalTeeExpr*) override; + Result BeginLoopExpr(LoopExpr*) override; + Result EndLoopExpr(LoopExpr*) override; + Result OnMemoryCopyExpr(MemoryCopyExpr*) override; + Result OnDataDropExpr(DataDropExpr*) override; + Result OnMemoryFillExpr(MemoryFillExpr*) override; + Result OnMemoryGrowExpr(MemoryGrowExpr*) override; + Result OnMemoryInitExpr(MemoryInitExpr*) override; + Result OnMemorySizeExpr(MemorySizeExpr*) override; + Result OnTableCopyExpr(TableCopyExpr*) override; + Result OnElemDropExpr(ElemDropExpr*) override; + Result OnTableInitExpr(TableInitExpr*) override; + Result OnTableGetExpr(TableGetExpr*) override; + Result OnTableSetExpr(TableSetExpr*) override; + Result OnTableGrowExpr(TableGrowExpr*) override; + Result OnTableSizeExpr(TableSizeExpr*) override; + Result OnTableFillExpr(TableFillExpr*) override; + Result OnRefFuncExpr(RefFuncExpr*) override; + Result OnRefNullExpr(RefNullExpr*) override; + Result OnRefIsNullExpr(RefIsNullExpr*) override; + Result OnNopExpr(NopExpr*) override; + Result OnReturnExpr(ReturnExpr*) override; + Result OnReturnCallExpr(ReturnCallExpr*) override; + Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr*) override; + Result OnSelectExpr(SelectExpr*) override; + Result OnStoreExpr(StoreExpr*) override; + Result OnUnaryExpr(UnaryExpr*) override; + Result OnUnreachableExpr(UnreachableExpr*) override; + Result BeginTryExpr(TryExpr*) override; + Result OnCatchExpr(TryExpr*, Catch*) override; + Result OnDelegateExpr(TryExpr*) override; + Result EndTryExpr(TryExpr*) override; + Result OnThrowExpr(ThrowExpr*) override; + Result OnRethrowExpr(RethrowExpr*) override; + Result OnAtomicWaitExpr(AtomicWaitExpr*) override; + Result OnAtomicFenceExpr(AtomicFenceExpr*) override; + Result OnAtomicNotifyExpr(AtomicNotifyExpr*) override; + Result OnAtomicLoadExpr(AtomicLoadExpr*) override; + Result OnAtomicStoreExpr(AtomicStoreExpr*) override; + Result OnAtomicRmwExpr(AtomicRmwExpr*) override; + Result OnAtomicRmwCmpxchgExpr(AtomicRmwCmpxchgExpr*) override; + Result OnTernaryExpr(TernaryExpr*) override; + Result OnSimdLaneOpExpr(SimdLaneOpExpr*) override; + Result OnSimdLoadLaneExpr(SimdLoadLaneExpr*) override; + Result OnSimdStoreLaneExpr(SimdStoreLaneExpr*) override; + Result OnSimdShuffleOpExpr(SimdShuffleOpExpr*) override; + Result OnLoadSplatExpr(LoadSplatExpr*) override; + Result OnLoadZeroExpr(LoadZeroExpr*) override; + + private: + WatWriter* writer_; +}; + +Result WatWriter::ExprVisitorDelegate::OnBinaryExpr(BinaryExpr* expr) { + writer_->WritePutsNewline(expr->opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::BeginBlockExpr(BlockExpr* expr) { + writer_->WriteBeginBlock(LabelType::Block, expr->block, + Opcode::Block_Opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::EndBlockExpr(BlockExpr* expr) { + writer_->WriteEndBlock(); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnBrExpr(BrExpr* expr) { + writer_->WritePutsSpace(Opcode::Br_Opcode.GetName()); + writer_->WriteBrVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnBrIfExpr(BrIfExpr* expr) { + writer_->WritePutsSpace(Opcode::BrIf_Opcode.GetName()); + writer_->WriteBrVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnBrTableExpr(BrTableExpr* expr) { + writer_->WritePutsSpace(Opcode::BrTable_Opcode.GetName()); + for (const Var& var : expr->targets) { + writer_->WriteBrVar(var, NextChar::Space); + } + writer_->WriteBrVar(expr->default_target, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnCallExpr(CallExpr* expr) { + writer_->WritePutsSpace(Opcode::Call_Opcode.GetName()); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnCallIndirectExpr( + CallIndirectExpr* expr) { + writer_->WritePutsSpace(Opcode::CallIndirect_Opcode.GetName()); + writer_->WriteVarUnlessZero(expr->table, NextChar::Space); + writer_->WriteOpenSpace("type"); + writer_->WriteVar(expr->decl.type_var, NextChar::Newline); + writer_->WriteCloseNewline(); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnCallRefExpr(CallRefExpr* expr) { + writer_->WritePutsSpace(Opcode::CallRef_Opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnCodeMetadataExpr( + CodeMetadataExpr* expr) { + writer_->WriteOpen("@metadata.code.", NextChar::None); + writer_->WriteDataWithNextChar(expr->name.data(), expr->name.size()); + writer_->WritePutc(' '); + writer_->WriteQuotedData(expr->data.data(), expr->data.size()); + writer_->WriteCloseSpace(); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnCompareExpr(CompareExpr* expr) { + writer_->WritePutsNewline(expr->opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnConstExpr(ConstExpr* expr) { + writer_->WriteConst(expr->const_); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnConvertExpr(ConvertExpr* expr) { + writer_->WritePutsNewline(expr->opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnDropExpr(DropExpr* expr) { + writer_->WritePutsNewline(Opcode::Drop_Opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnGlobalGetExpr(GlobalGetExpr* expr) { + writer_->WritePutsSpace(Opcode::GlobalGet_Opcode.GetName()); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnGlobalSetExpr(GlobalSetExpr* expr) { + writer_->WritePutsSpace(Opcode::GlobalSet_Opcode.GetName()); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::BeginIfExpr(IfExpr* expr) { + writer_->WriteBeginBlock(LabelType::If, expr->true_, + Opcode::If_Opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::AfterIfTrueExpr(IfExpr* expr) { + if (!expr->false_.empty()) { + writer_->Dedent(); + writer_->WritePutsSpace(Opcode::Else_Opcode.GetName()); + writer_->Indent(); + writer_->WriteNewline(FORCE_NEWLINE); + } + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::EndIfExpr(IfExpr* expr) { + writer_->WriteEndBlock(); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnLoadExpr(LoadExpr* expr) { + writer_->WriteMemoryLoadStoreExpr<LoadExpr>(expr); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnLocalGetExpr(LocalGetExpr* expr) { + writer_->WritePutsSpace(Opcode::LocalGet_Opcode.GetName()); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnLocalSetExpr(LocalSetExpr* expr) { + writer_->WritePutsSpace(Opcode::LocalSet_Opcode.GetName()); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnLocalTeeExpr(LocalTeeExpr* expr) { + writer_->WritePutsSpace(Opcode::LocalTee_Opcode.GetName()); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::BeginLoopExpr(LoopExpr* expr) { + writer_->WriteBeginBlock(LabelType::Loop, expr->block, + Opcode::Loop_Opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::EndLoopExpr(LoopExpr* expr) { + writer_->WriteEndBlock(); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnMemoryCopyExpr(MemoryCopyExpr* expr) { + writer_->WritePutsSpace(Opcode::MemoryCopy_Opcode.GetName()); + writer_->WriteTwoMemoryVarsUnlessBothZero(expr->srcmemidx, expr->destmemidx, + NextChar::Space); + writer_->WriteNewline(NO_FORCE_NEWLINE); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnDataDropExpr(DataDropExpr* expr) { + writer_->WritePutsSpace(Opcode::DataDrop_Opcode.GetName()); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnMemoryFillExpr(MemoryFillExpr* expr) { + writer_->WritePutsSpace(Opcode::MemoryFill_Opcode.GetName()); + writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space); + writer_->WriteNewline(NO_FORCE_NEWLINE); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnMemoryGrowExpr(MemoryGrowExpr* expr) { + writer_->WritePutsSpace(Opcode::MemoryGrow_Opcode.GetName()); + writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space); + writer_->WriteNewline(NO_FORCE_NEWLINE); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnMemorySizeExpr(MemorySizeExpr* expr) { + writer_->WritePutsSpace(Opcode::MemorySize_Opcode.GetName()); + writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space); + writer_->WriteNewline(NO_FORCE_NEWLINE); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnMemoryInitExpr(MemoryInitExpr* expr) { + writer_->WritePutsSpace(Opcode::MemoryInit_Opcode.GetName()); + writer_->WriteVar(expr->var, NextChar::Space); + writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space); + writer_->WriteNewline(NO_FORCE_NEWLINE); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnTableCopyExpr(TableCopyExpr* expr) { + writer_->WritePutsSpace(Opcode::TableCopy_Opcode.GetName()); + if (!(VarIsZero(expr->dst_table) && VarIsZero(expr->src_table))) { + writer_->WriteVar(expr->dst_table, NextChar::Space); + writer_->WriteVar(expr->src_table, NextChar::Space); + } + writer_->WriteNewline(NO_FORCE_NEWLINE); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnElemDropExpr(ElemDropExpr* expr) { + writer_->WritePutsSpace(Opcode::ElemDrop_Opcode.GetName()); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnTableInitExpr(TableInitExpr* expr) { + writer_->WritePutsSpace(Opcode::TableInit_Opcode.GetName()); + writer_->WriteVarUnlessZero(expr->table_index, NextChar::Space); + writer_->WriteVar(expr->segment_index, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnTableGetExpr(TableGetExpr* expr) { + writer_->WritePutsSpace(Opcode::TableGet_Opcode.GetName()); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnTableSetExpr(TableSetExpr* expr) { + writer_->WritePutsSpace(Opcode::TableSet_Opcode.GetName()); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnTableGrowExpr(TableGrowExpr* expr) { + writer_->WritePutsSpace(Opcode::TableGrow_Opcode.GetName()); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnTableSizeExpr(TableSizeExpr* expr) { + writer_->WritePutsSpace(Opcode::TableSize_Opcode.GetName()); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnTableFillExpr(TableFillExpr* expr) { + writer_->WritePutsSpace(Opcode::TableFill_Opcode.GetName()); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnRefFuncExpr(RefFuncExpr* expr) { + writer_->WritePutsSpace(Opcode::RefFunc_Opcode.GetName()); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnRefNullExpr(RefNullExpr* expr) { + writer_->WritePutsSpace(Opcode::RefNull_Opcode.GetName()); + writer_->WriteRefKind(expr->type, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnRefIsNullExpr(RefIsNullExpr* expr) { + writer_->WritePutsNewline(Opcode::RefIsNull_Opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnNopExpr(NopExpr* expr) { + writer_->WritePutsNewline(Opcode::Nop_Opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnReturnExpr(ReturnExpr* expr) { + writer_->WritePutsNewline(Opcode::Return_Opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnReturnCallExpr(ReturnCallExpr* expr) { + writer_->WritePutsSpace(Opcode::ReturnCall_Opcode.GetName()); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnReturnCallIndirectExpr( + ReturnCallIndirectExpr* expr) { + writer_->WritePutsSpace(Opcode::ReturnCallIndirect_Opcode.GetName()); + writer_->WriteOpenSpace("type"); + writer_->WriteVar(expr->decl.type_var, NextChar::Space); + writer_->WriteCloseNewline(); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnSelectExpr(SelectExpr* expr) { + writer_->WritePutsSpace(Opcode::Select_Opcode.GetName()); + if (!expr->result_type.empty()) { + writer_->WriteTypes(expr->result_type, "result"); + } + writer_->WriteNewline(NO_FORCE_NEWLINE); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnStoreExpr(StoreExpr* expr) { + writer_->WriteMemoryLoadStoreExpr<StoreExpr>(expr); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnUnaryExpr(UnaryExpr* expr) { + writer_->WritePutsNewline(expr->opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnUnreachableExpr( + UnreachableExpr* expr) { + writer_->WritePutsNewline(Opcode::Unreachable_Opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::BeginTryExpr(TryExpr* expr) { + writer_->WriteBeginBlock(LabelType::Try, expr->block, + Opcode::Try_Opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnCatchExpr(TryExpr* expr, + Catch* catch_) { + writer_->Dedent(); + if (catch_->IsCatchAll()) { + writer_->WritePutsNewline(Opcode::CatchAll_Opcode.GetName()); + } else { + writer_->WritePutsSpace(Opcode::Catch_Opcode.GetName()); + writer_->WriteVar(catch_->var, NextChar::Newline); + } + writer_->Indent(); + writer_->SetTopLabelType(LabelType::Catch); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnDelegateExpr(TryExpr* expr) { + writer_->Dedent(); + writer_->EndBlock(); + writer_->WritePutsSpace(Opcode::Delegate_Opcode.GetName()); + writer_->WriteVar(expr->delegate_target, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::EndTryExpr(TryExpr* expr) { + writer_->WriteEndBlock(); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnThrowExpr(ThrowExpr* expr) { + writer_->WritePutsSpace(Opcode::Throw_Opcode.GetName()); + writer_->WriteVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnRethrowExpr(RethrowExpr* expr) { + writer_->WritePutsSpace(Opcode::Rethrow_Opcode.GetName()); + writer_->WriteBrVar(expr->var, NextChar::Newline); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnAtomicWaitExpr(AtomicWaitExpr* expr) { + writer_->WriteLoadStoreExpr<AtomicWaitExpr>(expr); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnAtomicFenceExpr( + AtomicFenceExpr* expr) { + assert(expr->consistency_model == 0); + writer_->WritePutsNewline(Opcode::AtomicFence_Opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnAtomicNotifyExpr( + AtomicNotifyExpr* expr) { + writer_->WriteLoadStoreExpr<AtomicNotifyExpr>(expr); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnAtomicLoadExpr(AtomicLoadExpr* expr) { + writer_->WriteLoadStoreExpr<AtomicLoadExpr>(expr); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnAtomicStoreExpr( + AtomicStoreExpr* expr) { + writer_->WriteLoadStoreExpr<AtomicStoreExpr>(expr); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnAtomicRmwExpr(AtomicRmwExpr* expr) { + writer_->WriteLoadStoreExpr<AtomicRmwExpr>(expr); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnAtomicRmwCmpxchgExpr( + AtomicRmwCmpxchgExpr* expr) { + writer_->WriteLoadStoreExpr<AtomicRmwCmpxchgExpr>(expr); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnTernaryExpr(TernaryExpr* expr) { + writer_->WritePutsNewline(expr->opcode.GetName()); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnSimdLaneOpExpr(SimdLaneOpExpr* expr) { + writer_->WritePutsSpace(expr->opcode.GetName()); + writer_->Writef("%" PRIu64, (expr->val)); + writer_->WriteNewline(NO_FORCE_NEWLINE); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnSimdLoadLaneExpr( + SimdLoadLaneExpr* expr) { + writer_->WritePutsSpace(expr->opcode.GetName()); + writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space); + if (expr->offset) { + writer_->Writef("offset=%" PRIaddress, expr->offset); + } + if (!expr->opcode.IsNaturallyAligned(expr->align)) { + writer_->Writef("align=%" PRIaddress, expr->align); + } + writer_->Writef("%" PRIu64, (expr->val)); + writer_->WriteNewline(NO_FORCE_NEWLINE); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnSimdStoreLaneExpr( + SimdStoreLaneExpr* expr) { + writer_->WritePutsSpace(expr->opcode.GetName()); + writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space); + if (expr->offset) { + writer_->Writef("offset=%" PRIaddress, expr->offset); + } + if (!expr->opcode.IsNaturallyAligned(expr->align)) { + writer_->Writef("align=%" PRIaddress, expr->align); + } + writer_->Writef("%" PRIu64, (expr->val)); + writer_->WriteNewline(NO_FORCE_NEWLINE); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnSimdShuffleOpExpr( + SimdShuffleOpExpr* expr) { + writer_->WritePutsSpace(expr->opcode.GetName()); + std::array<uint8_t, 16> values = Bitcast<std::array<uint8_t, 16>>(expr->val); + for (int32_t lane = 0; lane < 16; ++lane) { +#if WABT_BIG_ENDIAN + writer_->Writef("%u", values[15 - lane]); +#else + writer_->Writef("%u", values[lane]); +#endif + } + writer_->WriteNewline(NO_FORCE_NEWLINE); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnLoadSplatExpr(LoadSplatExpr* expr) { + writer_->WriteLoadStoreExpr<LoadSplatExpr>(expr); + return Result::Ok; +} + +Result WatWriter::ExprVisitorDelegate::OnLoadZeroExpr(LoadZeroExpr* expr) { + writer_->WriteLoadStoreExpr<LoadZeroExpr>(expr); + return Result::Ok; +} + +void WatWriter::WriteExpr(const Expr* expr) { + WABT_TRACE(WriteExprList); + ExprVisitorDelegate delegate(this); + ExprVisitor visitor(&delegate); + visitor.VisitExpr(const_cast<Expr*>(expr)); +} + +void WatWriter::WriteExprList(const ExprList& exprs) { + WABT_TRACE(WriteExprList); + ExprVisitorDelegate delegate(this); + ExprVisitor visitor(&delegate); + visitor.VisitExprList(const_cast<ExprList&>(exprs)); +} + +void WatWriter::WriteFoldedExpr(const Expr* expr) { + WABT_TRACE_ARGS(WriteFoldedExpr, "%s", GetExprTypeName(*expr)); + auto arity = GetExprArity(*expr); + PushExpr(expr, arity.nargs, arity.nreturns); +} + +void WatWriter::WriteFoldedExprList(const ExprList& exprs) { + WABT_TRACE(WriteFoldedExprList); + for (const Expr& expr : exprs) { + WriteFoldedExpr(&expr); + } +} + +void WatWriter::PushExpr(const Expr* expr, + Index operand_count, + Index result_count) { + WABT_TRACE_ARGS(PushExpr, "%s, %" PRIindex ", %" PRIindex "", + GetExprTypeName(*expr), operand_count, result_count); + + // Try to pop operand off the expr stack to use as this expr's children. One + // expr can have multiple return values (with the multi-value extension), so + // we have to iterate over each in reverse. + + auto first_operand = expr_tree_stack_.end(); + + Index current_count = 0; + if (operand_count > 0) { + for (auto iter = expr_tree_stack_.rbegin(); iter != expr_tree_stack_.rend(); + ++iter) { + assert(iter->result_count > 0); + current_count += iter->result_count; + + if (current_count == operand_count) { + first_operand = iter.base() - 1; + break; + } else if (current_count > operand_count) { + // We went over the number of operands this instruction wants; this can + // only happen when there are expressions on the stack with a + // result_count > 1. When this happens we can't fold, since any result + // we produce will not make sense. + break; + } + } + } + + ExprTree tree(expr, result_count); + + if (current_count == operand_count && operand_count > 0) { + auto last_operand = expr_tree_stack_.end(); + std::move(first_operand, last_operand, std::back_inserter(tree.children)); + expr_tree_stack_.erase(first_operand, last_operand); + } + + expr_tree_stack_.emplace_back(std::move(tree)); + if (current_count > operand_count || result_count == 0) { + FlushExprTreeStack(); + } +} + +void WatWriter::FlushExprTree(const ExprTree& expr_tree) { + WABT_TRACE_ARGS(FlushExprTree, "%s", GetExprTypeName(*expr_tree.expr)); + switch (expr_tree.expr->type()) { + case ExprType::Block: + WritePuts("(", NextChar::None); + WriteBeginBlock(LabelType::Block, cast<BlockExpr>(expr_tree.expr)->block, + Opcode::Block_Opcode.GetName()); + WriteFoldedExprList(cast<BlockExpr>(expr_tree.expr)->block.exprs); + FlushExprTreeStack(); + WriteCloseNewline(); + EndBlock(); + break; + + case ExprType::Loop: + WritePuts("(", NextChar::None); + WriteBeginBlock(LabelType::Loop, cast<LoopExpr>(expr_tree.expr)->block, + Opcode::Loop_Opcode.GetName()); + WriteFoldedExprList(cast<LoopExpr>(expr_tree.expr)->block.exprs); + FlushExprTreeStack(); + WriteCloseNewline(); + EndBlock(); + break; + + case ExprType::If: { + auto if_expr = cast<IfExpr>(expr_tree.expr); + WritePuts("(", NextChar::None); + WriteBeginBlock(LabelType::If, if_expr->true_, + Opcode::If_Opcode.GetName()); + FlushExprTreeVector(expr_tree.children); + WriteOpenNewline("then"); + WriteFoldedExprList(if_expr->true_.exprs); + FlushExprTreeStack(); + WriteCloseNewline(); + if (!if_expr->false_.empty()) { + WriteOpenNewline("else"); + WriteFoldedExprList(if_expr->false_); + FlushExprTreeStack(); + WriteCloseNewline(); + } + WriteCloseNewline(); + EndBlock(); + break; + } + + case ExprType::Try: { + auto try_expr = cast<TryExpr>(expr_tree.expr); + WritePuts("(", NextChar::None); + WriteBeginBlock(LabelType::Try, try_expr->block, + Opcode::Try_Opcode.GetName()); + WriteOpenNewline("do"); + FlushExprTreeVector(expr_tree.children); + WriteFoldedExprList(try_expr->block.exprs); + FlushExprTreeStack(); + WriteCloseNewline(); + switch (try_expr->kind) { + case TryKind::Catch: + for (const Catch& catch_ : try_expr->catches) { + WritePuts("(", NextChar::None); + if (catch_.IsCatchAll()) { + WritePutsNewline("catch_all"); + } else { + WritePutsSpace(Opcode::Catch_Opcode.GetName()); + WriteVar(catch_.var, NextChar::Newline); + } + Indent(); + WriteFoldedExprList(catch_.exprs); + FlushExprTreeStack(); + WriteCloseNewline(); + } + break; + case TryKind::Delegate: + WritePuts("(", NextChar::None); + WritePutsSpace(Opcode::Delegate_Opcode.GetName()); + WriteVar(try_expr->delegate_target, NextChar::None); + WritePuts(")", NextChar::Newline); + break; + case TryKind::Plain: + // Nothing to do. + break; + } + WriteCloseNewline(); + EndBlock(); + break; + } + + default: { + WritePuts("(", NextChar::None); + WriteExpr(expr_tree.expr); + Indent(); + FlushExprTreeVector(expr_tree.children); + WriteCloseNewline(); + break; + } + } +} + +void WatWriter::FlushExprTreeVector(const std::vector<ExprTree>& expr_trees) { + WABT_TRACE_ARGS(FlushExprTreeVector, "%zu", expr_trees.size()); + for (auto expr_tree : expr_trees) { + FlushExprTree(expr_tree); + } +} + +void WatWriter::FlushExprTreeStack() { + std::vector<ExprTree> stack_copy(std::move(expr_tree_stack_)); + expr_tree_stack_.clear(); + FlushExprTreeVector(stack_copy); +} + +void WatWriter::WriteInitExpr(const ExprList& expr) { + if (!expr.empty()) { + WritePuts("(", NextChar::None); + WriteExprList(expr); + /* clear the next char, so we don't write a newline after the expr */ + next_char_ = NextChar::None; + WritePuts(")", NextChar::Space); + } +} + +template <typename T> +void WatWriter::WriteTypeBindings(const char* prefix, + const T& types, + const std::vector<std::string>& index_to_name, + Index binding_index_offset) { + /* named params/locals must be specified by themselves, but nameless + * params/locals can be compressed, e.g.: + * (param $foo i32) + * (param i32 i64 f32) + */ + bool first = true; + bool prev_has_name = false; + size_t index = 0; + for (Type type : types) { + const std::string& name = index_to_name[binding_index_offset + index]; + bool has_name = !name.empty(); + if ((has_name || prev_has_name) && !first) { + WriteCloseSpace(); + } + if (has_name || prev_has_name || first) { + WriteOpenSpace(prefix); + } + if (has_name) { + WriteString(name, NextChar::Space); + } + WriteType(type, NextChar::Space); + prev_has_name = has_name; + first = false; + ++index; + } + if (types.size() != 0) { + WriteCloseSpace(); + } +} + +void WatWriter::WriteBeginFunc(const Func& func) { + WriteOpenSpace("func"); + WriteNameOrIndex(func.name, func_index_, NextChar::Space); + WriteInlineExports(ExternalKind::Func, func_index_); + WriteInlineImport(ExternalKind::Func, func_index_); + if (func.decl.has_func_type) { + WriteOpenSpace("type"); + WriteVar(func.decl.type_var, NextChar::None); + WriteCloseSpace(); + } + + if (module.IsImport(ExternalKind::Func, Var(func_index_, Location()))) { + // Imported functions can be written a few ways: + // + // 1. (import "module" "field" (func (type 0))) + // 2. (import "module" "field" (func (param i32) (result i32))) + // 3. (func (import "module" "field") (type 0)) + // 4. (func (import "module" "field") (param i32) (result i32)) + // 5. (func (import "module" "field") (type 0) (param i32) (result i32)) + // + // Note that the text format does not allow including the param/result + // explicitly when using the "(import..." syntax (#1 and #2). + if (options_.inline_import || !func.decl.has_func_type) { + WriteFuncSigSpace(func.decl.sig); + } + } + func_index_++; +} + +void WatWriter::WriteFunc(const Func& func) { + WriteBeginFunc(func); + std::vector<std::string> index_to_name; + MakeTypeBindingReverseMapping(func.GetNumParamsAndLocals(), func.bindings, + &index_to_name); + WriteTypeBindings("param", func.decl.sig.param_types, index_to_name); + WriteTypes(func.decl.sig.result_types, "result"); + WriteNewline(NO_FORCE_NEWLINE); + if (func.local_types.size()) { + WriteTypeBindings("local", func.local_types, index_to_name, + func.GetNumParams()); + } + WriteNewline(NO_FORCE_NEWLINE); + BeginFunc(func); + if (options_.fold_exprs) { + WriteFoldedExprList(func.exprs); + FlushExprTreeStack(); + } else { + WriteExprList(func.exprs); + } + EndFunc(); + WriteCloseNewline(); +} + +void WatWriter::WriteBeginGlobal(const Global& global) { + WriteOpenSpace("global"); + WriteNameOrIndex(global.name, global_index_, NextChar::Space); + WriteInlineExports(ExternalKind::Global, global_index_); + WriteInlineImport(ExternalKind::Global, global_index_); + if (global.mutable_) { + WriteOpenSpace("mut"); + WriteType(global.type, NextChar::Space); + WriteCloseSpace(); + } else { + WriteType(global.type, NextChar::Space); + } + global_index_++; +} + +void WatWriter::WriteGlobal(const Global& global) { + WriteBeginGlobal(global); + WriteInitExpr(global.init_expr); + WriteCloseNewline(); +} + +void WatWriter::WriteTag(const Tag& tag) { + WriteOpenSpace("tag"); + WriteNameOrIndex(tag.name, tag_index_, NextChar::Space); + WriteInlineExports(ExternalKind::Tag, tag_index_); + WriteInlineImport(ExternalKind::Tag, tag_index_); + if (tag.decl.has_func_type) { + WriteOpenSpace("type"); + WriteVar(tag.decl.type_var, NextChar::None); + WriteCloseSpace(); + } + WriteTypes(tag.decl.sig.param_types, "param"); + ++tag_index_; + WriteCloseNewline(); +} + +void WatWriter::WriteLimits(const Limits& limits) { + if (limits.is_64) { + Writef("i64"); + } + Writef("%" PRIu64, limits.initial); + if (limits.has_max) { + Writef("%" PRIu64, limits.max); + } + if (limits.is_shared) { + Writef("shared"); + } +} + +void WatWriter::WriteTable(const Table& table) { + WriteOpenSpace("table"); + WriteNameOrIndex(table.name, table_index_, NextChar::Space); + WriteInlineExports(ExternalKind::Table, table_index_); + WriteInlineImport(ExternalKind::Table, table_index_); + WriteLimits(table.elem_limits); + WriteType(table.elem_type, NextChar::None); + WriteCloseNewline(); + table_index_++; +} + +void WatWriter::WriteElemSegment(const ElemSegment& segment) { + WriteOpenSpace("elem"); + // The first name we encounter here, pre-bulk-memory, was intended to refer to + // the table while segment names were not supported at all. For this reason + // we cannot emit a segment name here without bulk-memory enabled, otherwise + // the name will be assumed to be the name of a table and parsing will fail. + if (options_.features.bulk_memory_enabled()) { + WriteNameOrIndex(segment.name, elem_segment_index_, NextChar::Space); + } else { + Writef("(;%u;)", elem_segment_index_); + } + + uint8_t flags = segment.GetFlags(&module); + + if ((flags & (SegPassive | SegExplicitIndex)) == SegExplicitIndex) { + WriteOpenSpace("table"); + WriteVar(segment.table_var, NextChar::Space); + WriteCloseSpace(); + } + + if (!(flags & SegPassive)) { + WriteInitExpr(segment.offset); + } + + if ((flags & SegDeclared) == SegDeclared) { + WritePuts("declare", NextChar::Space); + } + + if (flags & SegUseElemExprs) { + WriteType(segment.elem_type, NextChar::Space); + } else { + assert(segment.elem_type == Type::FuncRef); + WritePuts("func", NextChar::Space); + } + + for (const ExprList& expr : segment.elem_exprs) { + if (flags & SegUseElemExprs) { + WriteInitExpr(expr); + } else { + assert(expr.size() == 1); + assert(expr.front().type() == ExprType::RefFunc); + WriteVar(cast<const RefFuncExpr>(&expr.front())->var, NextChar::Space); + } + } + WriteCloseNewline(); + elem_segment_index_++; +} + +void WatWriter::WriteMemory(const Memory& memory) { + WriteOpenSpace("memory"); + WriteNameOrIndex(memory.name, memory_index_, NextChar::Space); + WriteInlineExports(ExternalKind::Memory, memory_index_); + WriteInlineImport(ExternalKind::Memory, memory_index_); + WriteLimits(memory.page_limits); + WriteCloseNewline(); + memory_index_++; +} + +void WatWriter::WriteDataSegment(const DataSegment& segment) { + WriteOpenSpace("data"); + WriteNameOrIndex(segment.name, data_segment_index_, NextChar::Space); + if (segment.kind != SegmentKind::Passive) { + WriteMemoryVarUnlessZero(segment.memory_var, NextChar::Space); + WriteInitExpr(segment.offset); + } + WriteQuotedData(segment.data.data(), segment.data.size()); + WriteCloseNewline(); + data_segment_index_++; +} + +void WatWriter::WriteImport(const Import& import) { + if (!options_.inline_import) { + WriteOpenSpace("import"); + WriteQuotedString(import.module_name, NextChar::Space); + WriteQuotedString(import.field_name, NextChar::Space); + } + + switch (import.kind()) { + case ExternalKind::Func: + WriteBeginFunc(cast<FuncImport>(&import)->func); + WriteCloseSpace(); + break; + + case ExternalKind::Table: + WriteTable(cast<TableImport>(&import)->table); + break; + + case ExternalKind::Memory: + WriteMemory(cast<MemoryImport>(&import)->memory); + break; + + case ExternalKind::Global: + WriteBeginGlobal(cast<GlobalImport>(&import)->global); + WriteCloseSpace(); + break; + + case ExternalKind::Tag: + WriteTag(cast<TagImport>(&import)->tag); + break; + } + + if (options_.inline_import) { + WriteNewline(NO_FORCE_NEWLINE); + } else { + WriteCloseNewline(); + } +} + +void WatWriter::WriteExport(const Export& export_) { + if (options_.inline_export && IsInlineExport(export_)) { + return; + } + WriteOpenSpace("export"); + WriteQuotedString(export_.name, NextChar::Space); + WriteOpenSpace(GetKindName(export_.kind)); + WriteVar(export_.var, NextChar::Space); + WriteCloseSpace(); + WriteCloseNewline(); +} + +void WatWriter::WriteTypeEntry(const TypeEntry& type) { + WriteOpenSpace("type"); + WriteNameOrIndex(type.name, type_index_++, NextChar::Space); + switch (type.kind()) { + case TypeEntryKind::Func: + WriteOpenSpace("func"); + WriteFuncSigSpace(cast<FuncType>(&type)->sig); + WriteCloseSpace(); + break; + + case TypeEntryKind::Struct: { + auto* struct_type = cast<StructType>(&type); + WriteOpenSpace("struct"); + Index field_index = 0; + for (auto&& field : struct_type->fields) { + // TODO: Write shorthand if there is no name. + WriteOpenSpace("field"); + WriteNameOrIndex(field.name, field_index++, NextChar::Space); + WriteField(field); + WriteCloseSpace(); + } + WriteCloseSpace(); + break; + } + + case TypeEntryKind::Array: { + auto* array_type = cast<ArrayType>(&type); + WriteOpenSpace("array"); + WriteField(array_type->field); + WriteCloseSpace(); + break; + } + } + WriteCloseNewline(); +} + +void WatWriter::WriteField(const Field& field) { + if (field.mutable_) { + WriteOpenSpace("mut"); + } + WriteType(field.type, NextChar::Space); + if (field.mutable_) { + WriteCloseSpace(); + } +} + +void WatWriter::WriteStartFunction(const Var& start) { + WriteOpenSpace("start"); + WriteVar(start, NextChar::None); + WriteCloseNewline(); +} + +Result WatWriter::WriteModule() { + BuildInlineExportMap(); + BuildInlineImportMap(); + WriteOpenSpace("module"); + if (module.name.empty()) { + WriteNewline(NO_FORCE_NEWLINE); + } else { + WriteName(module.name, NextChar::Newline); + } + for (const ModuleField& field : module.fields) { + switch (field.type()) { + case ModuleFieldType::Func: + WriteFunc(cast<FuncModuleField>(&field)->func); + break; + case ModuleFieldType::Global: + WriteGlobal(cast<GlobalModuleField>(&field)->global); + break; + case ModuleFieldType::Import: + WriteImport(*cast<ImportModuleField>(&field)->import); + break; + case ModuleFieldType::Tag: + WriteTag(cast<TagModuleField>(&field)->tag); + break; + case ModuleFieldType::Export: + WriteExport(cast<ExportModuleField>(&field)->export_); + break; + case ModuleFieldType::Table: + WriteTable(cast<TableModuleField>(&field)->table); + break; + case ModuleFieldType::ElemSegment: + WriteElemSegment(cast<ElemSegmentModuleField>(&field)->elem_segment); + break; + case ModuleFieldType::Memory: + WriteMemory(cast<MemoryModuleField>(&field)->memory); + break; + case ModuleFieldType::DataSegment: + WriteDataSegment(cast<DataSegmentModuleField>(&field)->data_segment); + break; + case ModuleFieldType::Type: + WriteTypeEntry(*cast<TypeModuleField>(&field)->type); + break; + case ModuleFieldType::Start: + WriteStartFunction(cast<StartModuleField>(&field)->start); + break; + } + } + WriteCloseNewline(); + /* force the newline to be written */ + WriteNextChar(); + return result_; +} + +void WatWriter::BuildInlineExportMap() { + if (!options_.inline_export) { + return; + } + + for (Export* export_ : module.exports) { + Index index = kInvalidIndex; + + // Exported imports can't be written with inline exports, unless the + // imports are also inline. For example, the following is invalid: + // + // (import "module" "field" (func (export "e"))) + // + // But this is valid: + // + // (func (export "e") (import "module" "field")) + // + if (!options_.inline_import && module.IsImport(*export_)) { + continue; + } + + switch (export_->kind) { + case ExternalKind::Func: + index = module.GetFuncIndex(export_->var); + break; + + case ExternalKind::Table: + index = module.GetTableIndex(export_->var); + break; + + case ExternalKind::Memory: + index = module.GetMemoryIndex(export_->var); + break; + + case ExternalKind::Global: + index = module.GetGlobalIndex(export_->var); + break; + + case ExternalKind::Tag: + index = module.GetTagIndex(export_->var); + break; + } + + if (index != kInvalidIndex) { + auto key = std::make_pair(export_->kind, index); + inline_export_map_.insert(std::make_pair(key, export_)); + } + } +} + +void WatWriter::WriteInlineExports(ExternalKind kind, Index index) { + if (!options_.inline_export) { + return; + } + + auto iter_pair = inline_export_map_.equal_range(std::make_pair(kind, index)); + for (auto iter = iter_pair.first; iter != iter_pair.second; ++iter) { + const Export* export_ = iter->second; + WriteOpenSpace("export"); + WriteQuotedString(export_->name, NextChar::None); + WriteCloseSpace(); + } +} + +bool WatWriter::IsInlineExport(const Export& export_) { + Index index{}; + switch (export_.kind) { + case ExternalKind::Func: + index = module.GetFuncIndex(export_.var); + break; + + case ExternalKind::Table: + index = module.GetTableIndex(export_.var); + break; + + case ExternalKind::Memory: + index = module.GetMemoryIndex(export_.var); + break; + + case ExternalKind::Global: + index = module.GetGlobalIndex(export_.var); + break; + + case ExternalKind::Tag: + index = module.GetTagIndex(export_.var); + break; + } + + return inline_export_map_.find(std::make_pair(export_.kind, index)) != + inline_export_map_.end(); +} + +void WatWriter::BuildInlineImportMap() { + if (!options_.inline_import) { + return; + } + + for (const Import* import : module.imports) { + inline_import_map_[static_cast<size_t>(import->kind())].push_back(import); + } +} + +void WatWriter::WriteInlineImport(ExternalKind kind, Index index) { + if (!options_.inline_import) { + return; + } + + size_t kind_index = static_cast<size_t>(kind); + + if (index >= inline_import_map_[kind_index].size()) { + return; + } + + const Import* import = inline_import_map_[kind_index][index]; + WriteOpenSpace("import"); + WriteQuotedString(import->module_name, NextChar::Space); + WriteQuotedString(import->field_name, NextChar::Space); + WriteCloseSpace(); +} + +} // end anonymous namespace + +Result WriteWat(Stream* stream, + const Module* module, + const WriteWatOptions& options) { + WatWriter wat_writer(stream, options, *module); + return wat_writer.WriteModule(); +} + +} // namespace wabt diff --git a/third_party/wasm2c/wasm2c/.gitignore b/third_party/wasm2c/wasm2c/.gitignore new file mode 100644 index 0000000000..094559b61b --- /dev/null +++ b/third_party/wasm2c/wasm2c/.gitignore @@ -0,0 +1,11 @@ +wasm-rt-impl.o +examples/**/*.o +examples/fac/fac +examples/rot13/rot13 +examples/rot13/rot13.c +examples/rot13/rot13.h +examples/rot13/rot13.wasm +examples/callback/callback +examples/callback/callback.c +examples/callback/callback.h +examples/callback/callback.wasm diff --git a/third_party/wasm2c/wasm2c/README.md b/third_party/wasm2c/wasm2c/README.md new file mode 100644 index 0000000000..5271e89274 --- /dev/null +++ b/third_party/wasm2c/wasm2c/README.md @@ -0,0 +1,631 @@ +# wasm2c: Convert wasm files to C source and header + +`wasm2c` takes a WebAssembly module and produces an equivalent C source and +header. Some examples: + +```sh +# 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 +``` + +## Tutorial: .wat -> .wasm -> .c + +Let's look at a simple example of a factorial function. + +```wasm +(memory $mem 1) +(func (export "fac") (param $x i32) (result i32) + (if (result i32) (i32.eq (local.get $x) (i32.const 0)) + (then (i32.const 1)) + (else + (i32.mul (local.get $x) (call 0 (i32.sub (local.get $x) (i32.const 1)))) + ) + ) +) +``` + +Save this to `fac.wat`. We can convert this to a `.wasm` file by using the +`wat2wasm` tool: + +```sh +$ wat2wasm fac.wat -o fac.wasm +``` + +We can then convert it to a C source and header by using the `wasm2c` tool: + +```sh +$ wasm2c fac.wasm -o fac.c +``` + +This generates two files, `fac.c` and `fac.h`. We'll take a closer look at +these files below, but first let's show a simple example of how to use these +files. + +## Using the generated module + +To actually use our `fac` module, we'll use create a new file, `main.c`, that +include `fac.h`, initializes the module, and calls `fac`. + +`wasm2c` generates a few C symbols based on the `fac.wasm` module. +The first is `w2c_fac`, a type that represents an instance of the +`fac` module. `wasm2c` generates functions that construct and free a +`w2c_fac` instance: `wasm2c_fac_instantiate` and +`wasm2c_fac_free`. Finally, `wasm2c` generates the exported `fac` +function itself (`w2c_fac_fac`), which acts on a `w2c_fac` instance. + +All the exported symbols shared a common module ID (`fac`) which, by default, is +based on the name section in the module or the name of input file. This prefix +can be overridden using the `-n/--module-name` command line flag. + +```c +#include <stdio.h> +#include <stdlib.h> + +#include "fac.h" + +int main(int argc, char** argv) { + /* Make sure there is at least one command-line argument. */ + if (argc < 2) { + printf("Invalid argument. Expected '%s NUMBER'\n", argv[0]); + return 1; + } + + /* Convert the argument from a string to an int. We'll implicitly cast the int + to a `u32`, which is what `fac` expects. */ + u32 x = atoi(argv[1]); + + /* Initialize the Wasm runtime. */ + wasm_rt_init(); + + /* Declare an instance of the `fac` module. */ + w2c_fac fac; + + /* Construct the module instance. */ + wasm2c_fac_instantiate(&fac); + + /* Call `fac`, using the mangled name. */ + u32 result = w2c_fac_fac(&fac, x); + + /* Print the result. */ + printf("fac(%u) -> %u\n", x, result); + + /* Free the fac module. */ + wasm2c_fac_free(&fac); + + /* Free the Wasm runtime state. */ + wasm_rt_free(); + + return 0; +} + +``` + +## Compiling the wasm2c output + +To compile the executable, we need to use `main.c` and the generated `fac.c`. +We'll also include `wasm-rt-impl.c` which has implementations of the various +`wasm_rt_*` functions used by `fac.c` and `fac.h`. + +```sh +$ cc -o fac main.c fac.c wasm-rt-impl.c +``` + +A note on compiling with optimization: wasm2c relies on certain +behavior from the C compiler to maintain conformance with the +WebAssembly specification, especially with regards to requirements to +convert "signaling" to "quiet" floating-point NaN values and for +infinite recursion to produce a trap. When compiling with optimization +(e.g. `-O2` or `-O3`), it's necessary to disable some optimizations to +preserve conformance. With GCC 11, adding the command-line arguments +`-fno-optimize-sibling-calls -frounding-math -fsignaling-nans` appears +to be sufficient. With clang 14, just `-fno-optimize-sibling-calls +-frounding-math` appears to be sufficient. + +Now let's test it out! + +```sh +$ ./fac 1 +fac(1) -> 1 +$ ./fac 5 +fac(5) -> 120 +$ ./fac 10 +fac(10) -> 3628800 +``` + +You can take a look at the all of these files in +[wasm2c/examples/fac](/wasm2c/examples/fac). + +## Looking at the generated header, `fac.h` + +The generated header file looks something like this: + +```c +/* Automatically generated by wasm2c */ +#ifndef FAC_H_GENERATED_ +#define FAC_H_GENERATED_ + +... + +#include "wasm-rt.h" + +... +#ifndef WASM_RT_CORE_TYPES_DEFINED +#define WASM_RT_CORE_TYPES_DEFINED + +... + +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct w2c_fac { + char dummy_member; +} w2c_fac; + +void wasm2c_fac_instantiate(w2c_fac*); +void wasm2c_fac_free(w2c_fac*); +wasm_rt_func_type_t wasm2c_fac_get_func_type(uint32_t param_count, uint32_t result_count, ...); + +/* export: 'fac' */ +u32 w2c_fac_fac(w2c_fac*, u32); + +#ifdef __cplusplus +} +#endif + +#endif /* FAC_H_GENERATED_ */ + +``` + +Let's look at each section. The outer `#ifndef` is standard C +boilerplate for a header. This `WASM_RT_CORE_TYPES_DEFINED` section +contains a number of definitions required for all WebAssembly +modules. The `extern "C"` part makes sure to not mangle the symbols if +using this header in C++. + +The included `wasm-rt.h` file also includes a number of relevant definitions. +First is the `wasm_rt_trap_t` enum, which is used to give the reason a trap +occurred. + +```c +typedef enum { + WASM_RT_TRAP_NONE, + WASM_RT_TRAP_OOB, + WASM_RT_TRAP_INT_OVERFLOW, + WASM_RT_TRAP_DIV_BY_ZERO, + WASM_RT_TRAP_INVALID_CONVERSION, + WASM_RT_TRAP_UNREACHABLE, + WASM_RT_TRAP_CALL_INDIRECT, + WASM_RT_TRAP_UNCAUGHT_EXCEPTION, + WASM_RT_TRAP_EXHAUSTION, +} wasm_rt_trap_t; +``` + +Next is the `wasm_rt_type_t` enum, which is used for specifying function +signatures. Six WebAssembly value types are included: + +```c +typedef enum { + WASM_RT_I32, + WASM_RT_I64, + WASM_RT_F32, + WASM_RT_F64, + WASM_RT_FUNCREF, + WASM_RT_EXTERNREF, +} wasm_rt_type_t; + +Next is `wasm_rt_function_ptr_t`, the function signature for a generic function +callback. Since a WebAssembly table can contain functions of any given +signature, it is necessary to convert them to a canonical form: + +```c +typedef void (*wasm_rt_function_ptr_t)(void); +``` + +Next is the definition for a function reference (in WebAssembly 1.0, +this was the type of all table elements, but funcrefs can now also be +used as ordinary values, and tables can alternately be declared as +type externref). In this structure, `wasm_rt_func_type_t` is an opaque +256-bit ID that can be looked up via the `Z_[modname]_get_func_type` +function. (A demonstration of this can be found in the `callback` +example.) `module_instance` is the pointer to the function's +originating module instance, which will be passed in when the func is +called. + +```c +typedef struct { + wasm_rt_func_type_t func_type; + wasm_rt_function_ptr_t func; + void* module_instance; +} wasm_rt_funcref_t; + +``` + +Next is the definition of a memory instance. The `data` field is a pointer to +`size` bytes of linear memory. The `size` field of `wasm_rt_memory_t` is the +current size of the memory instance in bytes, whereas `pages` is the current +size in pages (65536 bytes.) `max_pages` is the maximum number of pages as +specified by the module, or `0xffffffff` if there is no limit. + +```c +typedef struct { + uint8_t* data; + uint32_t pages, max_pages; + uint32_t size; +} wasm_rt_memory_t; +``` + +Next is the definition of a table instance. The `data` field is a pointer to +`size` elements. Like a memory instance, `size` is the current size of a table, +and `max_size` is the maximum size of the table, or `0xffffffff` if there is no +limit. + +```c +typedef struct { + wasm_rt_funcref_t* data; + uint32_t max_size; + uint32_t size; +} wasm_rt_funcref_table_t; +``` + +## Symbols that must be defined by the embedder + +Next in `wasm-rt.h` are a collection of function declarations that must be implemented by +the embedder (i.e. you) before this C source can be used. + +A C implementation of these functions is defined in +[`wasm-rt-impl.h`](wasm-rt-impl.h) and [`wasm-rt-impl.c`](wasm-rt-impl.c). + +```c +void wasm_rt_init(void); +bool wasm_rt_is_initialized(void); +void wasm_rt_free(void); +void wasm_rt_trap(wasm_rt_trap_t) __attribute__((noreturn)); +const char* wasm_rt_strerror(wasm_rt_trap_t trap); +void wasm_rt_allocate_memory(wasm_rt_memory_t*, uint32_t initial_pages, uint32_t max_pages, bool is64); +uint32_t wasm_rt_grow_memory(wasm_rt_memory_t*, uint32_t pages); +void wasm_rt_free_memory(wasm_rt_memory_t*); +void wasm_rt_allocate_funcref_table(wasm_rt_table_t*, uint32_t elements, uint32_t max_elements); +void wasm_rt_allocate_externref_table(wasm_rt_externref_table_t*, uint32_t elements, uint32_t max_elements); +void wasm_rt_free_funcref_table(wasm_rt_table_t*); +void wasm_rt_free_externref_table(wasm_rt_table_t*); +uint32_t wasm_rt_call_stack_depth; /* on platforms that don't use the signal handler to detect exhaustion */ +``` + +`wasm_rt_init` must be called by the embedder before anything else, to +initialize the runtime. `wasm_rt_free` frees any global +state. `wasm_rt_is_initialized` can be used to confirm that the +runtime has been initialized. + +`wasm_rt_trap` is a function that is called when the module traps. Some possible +implementations are to throw a C++ exception, or to just abort the program +execution. The default runtime included in wasm2c unwinds the stack using +`longjmp`. The host can overide this call to `longjmp` by compiling the runtime +with `WASM_RT_TRAP_HANDLER` defined to the name of a trap handler function. The +handler function should be a function taking a `wasm_rt_trap_t` as a parameter +and returning `void`. e.g. `-DWASM_RT_TRAP_HANDLER=my_trap_handler` + +`wasm_rt_allocate_memory` initializes a memory instance, and allocates at least +enough space for the given number of initial pages. The memory must be cleared +to zero. The `is64` parameter indicates if the memory is indexed with +an i32 or i64 address. + +`wasm_rt_grow_memory` must grow the given memory instance by the given number of +pages. If there isn't enough memory to do so, or the new page count would be +greater than the maximum page count, the function must fail by returning +`0xffffffff`. If the function succeeds, it must return the previous size of the +memory instance, in pages. The host can optionally be notified of failures by +compiling the runtime with `WASM_RT_GROW_FAILED_HANDLER` defined to the name of +a handler function. The handler function should be a function taking no +arguments and returning `void` . e.g. +`-DWASM_RT_GROW_FAILED_HANDLER=my_growfail_handler` + +`wasm_rt_free_memory` frees the memory instance. + +`wasm_rt_allocate_funcref_table` and the similar `..._externref_table` +initialize a table instance of the given type, and allocate at least +enough space for the given number of initial elements. The elements +must be cleared to zero. + +`wasm_rt_free_funcref_table` and `..._externref_table` free the table instance. + +`wasm_rt_call_stack_depth` is the current stack call depth. Since this is +shared between modules, it must be defined only once, by the embedder. +It is only used on platforms that don't use the signal handler to detect +exhaustion. + +### Runtime support for exception handling + +Several additional symbols must be defined if wasm2c is being run with support +for exceptions (`--enable-exceptions`). These are defined in +`wasm-rt-exceptions.h`. These symbols are: + +```c +void wasm_rt_load_exception(const char* tag, uint32_t size, const void* values); +WASM_RT_NO_RETURN void wasm_rt_throw(void); +WASM_RT_UNWIND_TARGET +WASM_RT_UNWIND_TARGET* wasm_rt_get_unwind_target(void); +void wasm_rt_set_unwind_target(WASM_RT_UNWIND_TARGET* target); +uint32_t wasm_rt_exception_tag(void); +uint32_t wasm_rt_exception_size(void); +void* wasm_rt_exception(void); +wasm_rt_try(target) +``` + +A C implementation of these functions is also available in +[`wasm-rt-exceptions-impl.c`](wasm-rt-exceptions-impl.c). + +`wasm_rt_load_exception` sets the active exception to a given tag, size, and contents. + +`wasm_rt_throw` throws the active exception. + +`WASM_RT_UNWIND_TARGET` is the type of an unwind target if an +exception is thrown and caught. + +`wasm_rt_get_unwind_target` gets the current unwind target if an exception is thrown. + +`wasm_rt_set_unwind_target` sets the unwind target if an exception is thrown. + +Three functions provide access to the active exception: +`wasm_rt_exception_tag`, `wasm_rt_exception_size`, and +`wasm_rt_exception` return its tag, size, and contents, respectively. + +`wasm_rt_try(target)` is a macro that captures the current calling +environment as an unwind target and stores it into `target`, which +must be of type `WASM_RT_UNWIND_TARGET`. + +## Exported symbols + +Finally, `fac.h` defines the module instance type (which in the case +of `fac` is essentially empty), and the exported symbols provided by +the module. In our example, the only function we exported was +`fac`. + +`wasm2c_fac_instantiate(w2c_fac*)` creates an instance of +the module and must be called before the module instance can be +used. `wasm2c_fac_free(w2c_fac*)` frees the instance. +`wasm2c_fac_get_func_type` can be used to look up a function type ID +at runtime. It is a variadic function where the first two arguments +give the number of parameters and results, and the following arguments +are the types from the wasm_rt_type_t enum described above. The +`callback` example demonstrates using this to pass a host function to +a WebAssembly module dynamically at runtime. + +```c +typedef struct w2c_fac { + char dummy_member; +} w2c_fac; + +void wasm2c_fac_instantiate(w2c_fac*); +void wasm2c_fac_free(w2c_fac*); +wasm_rt_func_type_t wasm2c_fac_get_func_type(uint32_t param_count, uint32_t result_count, ...); + +/* export: 'fac' */ +u32 w2c_fac_fac(w2c_fac*, u32); +``` + +## Handling other kinds of imports and exports of modules + +Exported functions are handled by declaring a prefixed equivalent +function in the header. If a module imports a function, `wasm2c` +declares the function in the output header file, and the host function +is responsible for defining the function. + +Exports of other kinds (globals, memories, tables) are handled +differently, since they are part of the module instance, and each +instance can have its own exports. For these cases, `wasm2c` provides +a function that takes in a module instance as argument, and returns +the corresponding export. For example, if `fac` exported a memory as +such: + +```wasm +(export "mem" (memory $mem)) +``` + +then `wasm2c` would declare the following function in the header: + +```c +/* export: 'mem' */ +wasm_rt_memory_t* w2c_fac_mem(w2c_fac* instance); + +``` + +which would be defined as: +```c +/* export: 'mem' */ +wasm_rt_memory_t* w2c_fac_mem(w2c_fac* instance) { + return &instance->w2c_mem; +} +``` + +## A quick look at `fac.c` + +The contents of `fac.c` are internals, but it is useful to see a little about +how it works. + +The first few hundred lines define macros that are used to implement the +various WebAssembly instructions. Their implementations may be interesting to +the curious reader, but are out of scope for this document. + +Following those definitions are various initialization functions (`init`, `free`, +`init_func_types`, `init_globals`, `init_memory`, `init_table`, and +`init_exports`.) In our example, most of these functions are empty, since the +module doesn't use any globals, memory or tables. + +The most interesting part is the definition of the function `fac`: + +```c +static u32 w2c_fac_fac_0(w2c_fac* instance, u32 var_p0) { + FUNC_PROLOGUE; + u32 var_i0, var_i1, var_i2; + var_i0 = var_p0; + var_i1 = 0u; + var_i0 = var_i0 == var_i1; + if (var_i0) { + var_i0 = 1u; + } else { + var_i0 = var_p0; + var_i1 = var_p0; + var_i2 = 1u; + var_i1 -= var_i2; + var_i1 = w2c_fac_fac_0(instance, var_i1); + var_i0 *= var_i1; + } + FUNC_EPILOGUE; + return var_i0; +} +``` + +If you look at the original WebAssembly text in the flat format, you can see +that there is a 1-1 mapping in the output: + +```wasm +(func $fac (param $x i32) (result i32) + local.get $x + i32.const 0 + i32.eq + if (result i32) + i32.const 1 + else + local.get $x + local.get $x + i32.const 1 + i32.sub + call 0 + i32.mul + end) +``` + +This looks different than the factorial function above because it is using the +"flat format" instead of the "folded format". You can use `wat-desugar` to +convert between the two to be sure: + +```sh +$ wat-desugar fac-flat.wat --fold -o fac-folded.wat +``` + +```wasm +(module + (func (;0;) (param i32) (result i32) + (if (result i32) ;; label = @1 + (i32.eq + (local.get 0) + (i32.const 0)) + (then + (i32.const 1)) + (else + (i32.mul + (local.get 0) + (call 0 + (i32.sub + (local.get 0) + (i32.const 1))))))) + (export "fac" (func 0)) + (type (;0;) (func (param i32) (result i32)))) +``` + +The formatting is different and the variable and function names are gone, but +the structure is the same. + +## Create multiple instances of a module + +Since information about the execution context, such as memories, is encapsulated +in the module instance structure, and a pointer to the structure is being passed through +function calls, multiple instances of the same module can be instantiated alongside +one another. + +We can take a look at another version of the `main` function for a `rot13` example. By +declaring two sets of context information, two instances of `rot13` can be instantiated +in the same address space. + +```c +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +#include "rot13.h" + +/* Define structure to hold the imports */ +typedef struct w2c_host { + wasm_rt_memory_t memory; + char* input; +} w2c_host; + +/* Accessor to access the memory member of the host */ +wasm_rt_memory_t* w2c_host_mem(w2c_host* instance) { + return &instance->memory; +} + +int main(int argc, char** argv) { + /* Make sure there is at least one command-line argument. */ + if (argc < 2) { + printf("Invalid argument. Expected '%s WORD...'\n", argv[0]); + return 1; + } + /* Initialize the Wasm runtime. */ + wasm_rt_init(); + + /* Create two `host` instances to store the memory and current string */ + w2c_host host_1, host_2; + wasm_rt_allocate_memory(&host_1.memory, 1, 1, false); + wasm_rt_allocate_memory(&host_2.memory, 1, 1, false); + + /* Construct the `rot13` module instances */ + w2c_rot13 rot13_1, rot13_2; + wasm2c_rot13_instantiate(&rot13_1, &host_1); + wasm2c_rot13_instantiate(&rot13_2, &host_2); + + /* Call `rot13` on the first two arguments. */ + assert(argc > 2); + host_1.input = argv[1]; + w2c_rot13_rot13(&rot13_1); + host_2.input = argv[2]; + w2c_rot13_rot13(&rot13_2); + + /* Free the rot13 instances. */ + wasm2c_rot13_free(&rot13_1); + wasm2c_rot13_free(&rot13_2); + + /* Free the Wasm runtime state. */ + wasm_rt_free(); + + return 0; +} + +/* Fill the wasm buffer with the input to be rot13'd. + * + * params: + * instance: An instance of the w2c_host structure + * ptr: The wasm memory address of the buffer to fill data. + * size: The size of the buffer in wasm memory. + * result: + * The number of bytes filled into the buffer. (Must be <= size). + */ +u32 w2c_host_fill_buf(w2c_host* instance, u32 ptr, u32 size) { + for (size_t i = 0; i < size; ++i) { + if (instance->input[i] == 0) { + return i; + } + instance->memory.data[ptr + i] = instance->input[i]; + } + return size; +} + +/* Called when the wasm buffer has been rot13'd. + * + * params: + * w2c_host: An instance of the w2c_host structure + * ptr: The wasm memory address of the buffer. + * size: The size of the buffer in wasm memory. + */ +void w2c_host_buf_done(w2c_host* instance, u32 ptr, u32 size) { + /* The output buffer is not necessarily null-terminated, so use the %*.s + * printf format to limit the number of characters printed. */ + printf("%s -> %.*s\n", instance->input, (int)size, &instance->memory.data[ptr]); +} +``` diff --git a/third_party/wasm2c/wasm2c/wasm-rt-impl.c b/third_party/wasm2c/wasm2c/wasm-rt-impl.c new file mode 100644 index 0000000000..db4458aedb --- /dev/null +++ b/third_party/wasm2c/wasm2c/wasm-rt-impl.c @@ -0,0 +1,422 @@ +/* + * 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 "wasm-rt-impl.h" + +#include <assert.h> +#include <math.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if WASM_RT_INSTALL_SIGNAL_HANDLER && !defined(_WIN32) +#include <signal.h> +#include <unistd.h> +#endif + +#ifdef _WIN32 +#include <windows.h> +#else +#include <sys/mman.h> +#endif + +#define PAGE_SIZE 65536 + +#if WASM_RT_INSTALL_SIGNAL_HANDLER +static bool g_signal_handler_installed = false; +#ifdef _WIN32 +static void* g_sig_handler_handle = 0; +#else +static char* g_alt_stack = 0; +#endif +#endif + +#if WASM_RT_USE_STACK_DEPTH_COUNT +WASM_RT_THREAD_LOCAL uint32_t wasm_rt_call_stack_depth; +WASM_RT_THREAD_LOCAL uint32_t wasm_rt_saved_call_stack_depth; +#endif + +WASM_RT_THREAD_LOCAL wasm_rt_jmp_buf g_wasm_rt_jmp_buf; + +#ifdef WASM_RT_TRAP_HANDLER +extern void WASM_RT_TRAP_HANDLER(wasm_rt_trap_t code); +#endif + +#ifdef WASM_RT_GROW_FAILED_HANDLER +extern void WASM_RT_GROW_FAILED_HANDLER(); +#endif + +void wasm_rt_trap(wasm_rt_trap_t code) { + assert(code != WASM_RT_TRAP_NONE); +#if WASM_RT_USE_STACK_DEPTH_COUNT + wasm_rt_call_stack_depth = wasm_rt_saved_call_stack_depth; +#endif + +#ifdef WASM_RT_TRAP_HANDLER + WASM_RT_TRAP_HANDLER(code); + wasm_rt_unreachable(); +#else + WASM_RT_LONGJMP(g_wasm_rt_jmp_buf, code); +#endif +} + +#ifdef _WIN32 +static void* os_mmap(size_t size) { + void* ret = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS); + return ret; +} + +static int os_munmap(void* addr, size_t size) { + // Windows can only unmap the whole mapping + (void)size; /* unused */ + BOOL succeeded = VirtualFree(addr, 0, MEM_RELEASE); + return succeeded ? 0 : -1; +} + +static int os_mprotect(void* addr, size_t size) { + if (size == 0) { + return 0; + } + void* ret = VirtualAlloc(addr, size, MEM_COMMIT, PAGE_READWRITE); + if (ret == addr) { + return 0; + } + VirtualFree(addr, 0, MEM_RELEASE); + return -1; +} + +static void os_print_last_error(const char* msg) { + DWORD errorMessageID = GetLastError(); + if (errorMessageID != 0) { + LPSTR messageBuffer = 0; + // The api creates the buffer that holds the message + size_t size = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&messageBuffer, 0, NULL); + (void)size; + printf("%s. %s\n", msg, messageBuffer); + LocalFree(messageBuffer); + } else { + printf("%s. No error code.\n", msg); + } +} + +#if WASM_RT_INSTALL_SIGNAL_HANDLER + +static LONG os_signal_handler(PEXCEPTION_POINTERS info) { + if (info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { + wasm_rt_trap(WASM_RT_TRAP_OOB); + } else if (info->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) { + wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION); + } + return EXCEPTION_CONTINUE_SEARCH; +} + +static void os_install_signal_handler(void) { + g_sig_handler_handle = + AddVectoredExceptionHandler(1 /* CALL_FIRST */, os_signal_handler); +} + +static void os_cleanup_signal_handler(void) { + RemoveVectoredExceptionHandler(g_sig_handler_handle); +} + +#endif + +#else +static void* os_mmap(size_t size) { + int map_prot = PROT_NONE; + int map_flags = MAP_ANONYMOUS | MAP_PRIVATE; + uint8_t* addr = mmap(NULL, size, map_prot, map_flags, -1, 0); + if (addr == MAP_FAILED) + return NULL; + return addr; +} + +static int os_munmap(void* addr, size_t size) { + return munmap(addr, size); +} + +static int os_mprotect(void* addr, size_t size) { + return mprotect(addr, size, PROT_READ | PROT_WRITE); +} + +static void os_print_last_error(const char* msg) { + perror(msg); +} + +#if WASM_RT_INSTALL_SIGNAL_HANDLER +static void os_signal_handler(int sig, siginfo_t* si, void* unused) { + if (si->si_code == SEGV_ACCERR) { + wasm_rt_trap(WASM_RT_TRAP_OOB); + } else { + wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION); + } +} + +static void os_install_signal_handler(void) { + /* Use alt stack to handle SIGSEGV from stack overflow */ + g_alt_stack = malloc(SIGSTKSZ); + if (g_alt_stack == NULL) { + perror("malloc failed"); + abort(); + } + + stack_t ss; + ss.ss_sp = g_alt_stack; + ss.ss_flags = 0; + ss.ss_size = SIGSTKSZ; + if (sigaltstack(&ss, NULL) != 0) { + perror("sigaltstack failed"); + abort(); + } + + struct sigaction sa; + memset(&sa, '\0', sizeof(sa)); + sa.sa_flags = SA_SIGINFO | SA_ONSTACK; + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = os_signal_handler; + + /* Install SIGSEGV and SIGBUS handlers, since macOS seems to use SIGBUS. */ + if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL) != 0) { + perror("sigaction failed"); + abort(); + } +} + +static void os_cleanup_signal_handler(void) { + /* Undo what was done in os_install_signal_handler */ + struct sigaction sa; + memset(&sa, '\0', sizeof(sa)); + sa.sa_handler = SIG_DFL; + if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL)) { + perror("sigaction failed"); + abort(); + } + + if (sigaltstack(NULL, NULL) != 0) { + perror("sigaltstack failed"); + abort(); + } + + free(g_alt_stack); +} +#endif + +#endif + +void wasm_rt_init(void) { +#if WASM_RT_INSTALL_SIGNAL_HANDLER + if (!g_signal_handler_installed) { + g_signal_handler_installed = true; + os_install_signal_handler(); + } +#endif +} + +bool wasm_rt_is_initialized(void) { +#if WASM_RT_INSTALL_SIGNAL_HANDLER + return g_signal_handler_installed; +#else + return true; +#endif +} + +void wasm_rt_free(void) { +#if WASM_RT_INSTALL_SIGNAL_HANDLER + os_cleanup_signal_handler(); + g_signal_handler_installed = false; +#endif +} + +#if WASM_RT_USE_MMAP + +static uint64_t get_allocation_size_for_mmap(wasm_rt_memory_t* memory) { + assert(!memory->is64 && + "memory64 is not yet compatible with WASM_RT_USE_MMAP"); +#if WASM_RT_MEMCHECK_GUARD_PAGES + /* Reserve 8GiB. */ + const uint64_t max_size = 0x200000000ul; + return max_size; +#else + if (memory->max_pages != 0) { + const uint64_t max_size = memory->max_pages * PAGE_SIZE; + return max_size; + } + + /* Reserve 4GiB. */ + const uint64_t max_size = 0x100000000ul; + return max_size; +#endif +} + +#endif + +void wasm_rt_allocate_memory(wasm_rt_memory_t* memory, + uint64_t initial_pages, + uint64_t max_pages, + bool is64) { + uint64_t byte_length = initial_pages * PAGE_SIZE; + memory->size = byte_length; + memory->pages = initial_pages; + memory->max_pages = max_pages; + memory->is64 = is64; + +#if WASM_RT_USE_MMAP + const uint64_t mmap_size = get_allocation_size_for_mmap(memory); + void* addr = os_mmap(mmap_size); + if (!addr) { + os_print_last_error("os_mmap failed."); + abort(); + } + int ret = os_mprotect(addr, byte_length); + if (ret != 0) { + os_print_last_error("os_mprotect failed."); + abort(); + } + memory->data = addr; +#else + memory->data = calloc(byte_length, 1); +#endif +} + +static uint64_t grow_memory_impl(wasm_rt_memory_t* memory, uint64_t delta) { + uint64_t old_pages = memory->pages; + uint64_t new_pages = memory->pages + delta; + if (new_pages == 0) { + return 0; + } + if (new_pages < old_pages || new_pages > memory->max_pages) { + return (uint64_t)-1; + } + uint64_t old_size = old_pages * PAGE_SIZE; + uint64_t new_size = new_pages * PAGE_SIZE; + uint64_t delta_size = delta * PAGE_SIZE; +#if WASM_RT_USE_MMAP + uint8_t* new_data = memory->data; + int ret = os_mprotect(new_data + old_size, delta_size); + if (ret != 0) { + return (uint64_t)-1; + } +#else + uint8_t* new_data = realloc(memory->data, new_size); + if (new_data == NULL) { + return (uint64_t)-1; + } +#if !WABT_BIG_ENDIAN + memset(new_data + old_size, 0, delta_size); +#endif +#endif +#if WABT_BIG_ENDIAN + memmove(new_data + new_size - old_size, new_data, old_size); + memset(new_data, 0, delta_size); +#endif + memory->pages = new_pages; + memory->size = new_size; + memory->data = new_data; + return old_pages; +} + +uint64_t wasm_rt_grow_memory(wasm_rt_memory_t* memory, uint64_t delta) { + uint64_t ret = grow_memory_impl(memory, delta); +#ifdef WASM_RT_GROW_FAILED_HANDLER + if (ret == -1) { + WASM_RT_GROW_FAILED_HANDLER(); + } +#endif + return ret; +} + +void wasm_rt_free_memory(wasm_rt_memory_t* memory) { +#if WASM_RT_USE_MMAP + const uint64_t mmap_size = get_allocation_size_for_mmap(memory); + os_munmap(memory->data, mmap_size); // ignore error +#else + free(memory->data); +#endif +} + +#define DEFINE_TABLE_OPS(type) \ + void wasm_rt_allocate_##type##_table(wasm_rt_##type##_table_t* table, \ + uint32_t elements, \ + uint32_t max_elements) { \ + table->size = elements; \ + table->max_size = max_elements; \ + table->data = calloc(table->size, sizeof(wasm_rt_##type##_t)); \ + } \ + void wasm_rt_free_##type##_table(wasm_rt_##type##_table_t* table) { \ + free(table->data); \ + } \ + uint32_t wasm_rt_grow_##type##_table(wasm_rt_##type##_table_t* table, \ + uint32_t delta, \ + wasm_rt_##type##_t init) { \ + uint32_t old_elems = table->size; \ + uint64_t new_elems = (uint64_t)table->size + delta; \ + if (new_elems == 0) { \ + return 0; \ + } \ + if ((new_elems < old_elems) || (new_elems > table->max_size)) { \ + return (uint32_t)-1; \ + } \ + void* new_data = \ + realloc(table->data, new_elems * sizeof(wasm_rt_##type##_t)); \ + if (!new_data) { \ + return (uint32_t)-1; \ + } \ + table->data = new_data; \ + table->size = new_elems; \ + for (uint32_t i = old_elems; i < new_elems; i++) { \ + table->data[i] = init; \ + } \ + return old_elems; \ + } + +DEFINE_TABLE_OPS(funcref) +DEFINE_TABLE_OPS(externref) + +const char* wasm_rt_strerror(wasm_rt_trap_t trap) { + switch (trap) { + case WASM_RT_TRAP_NONE: + return "No error"; + case WASM_RT_TRAP_OOB: +#if WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS + return "Out-of-bounds access in linear memory or a table, or call stack " + "exhausted"; +#else + return "Out-of-bounds access in linear memory or a table"; + case WASM_RT_TRAP_EXHAUSTION: + return "Call stack exhausted"; +#endif + case WASM_RT_TRAP_INT_OVERFLOW: + return "Integer overflow on divide or truncation"; + case WASM_RT_TRAP_DIV_BY_ZERO: + return "Integer divide by zero"; + case WASM_RT_TRAP_INVALID_CONVERSION: + return "Conversion from NaN to integer"; + case WASM_RT_TRAP_UNREACHABLE: + return "Unreachable instruction executed"; + case WASM_RT_TRAP_CALL_INDIRECT: + return "Invalid call_indirect"; + case WASM_RT_TRAP_UNCAUGHT_EXCEPTION: + return "Uncaught exception"; + } + return "invalid trap code"; +} diff --git a/third_party/wasm2c/wasm2c/wasm-rt-impl.h b/third_party/wasm2c/wasm2c/wasm-rt-impl.h new file mode 100644 index 0000000000..c7d7fdbf6a --- /dev/null +++ b/third_party/wasm2c/wasm2c/wasm-rt-impl.h @@ -0,0 +1,66 @@ +/* + * 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 WASM_RT_IMPL_H_ +#define WASM_RT_IMPL_H_ + +#include "wasm-rt.h" + +#ifdef _WIN32 +#include <windows.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** A setjmp buffer used for handling traps. */ +extern WASM_RT_THREAD_LOCAL wasm_rt_jmp_buf g_wasm_rt_jmp_buf; + +#if WASM_RT_USE_STACK_DEPTH_COUNT +/** Saved call stack depth that will be restored in case a trap occurs. */ +extern WASM_RT_THREAD_LOCAL uint32_t wasm_rt_saved_call_stack_depth; +#define WASM_RT_SAVE_STACK_DEPTH() \ + wasm_rt_saved_call_stack_depth = wasm_rt_call_stack_depth +#else +#define WASM_RT_SAVE_STACK_DEPTH() (void)0 +#endif + +/** + * Convenience macro to use before calling a wasm function. On first execution + * it will return `WASM_RT_TRAP_NONE` (i.e. 0). If the function traps, it will + * jump back and return the trap that occurred. + * + * ``` + * wasm_rt_trap_t code = wasm_rt_impl_try(); + * if (code != 0) { + * printf("A trap occurred with code: %d\n", code); + * ... + * } + * + * // Call the potentially-trapping function. + * my_wasm_func(); + * ``` + */ +#define wasm_rt_impl_try() \ + (WASM_RT_SAVE_STACK_DEPTH(), wasm_rt_set_unwind_target(&g_wasm_rt_jmp_buf), \ + WASM_RT_SETJMP(g_wasm_rt_jmp_buf)) + +#ifdef __cplusplus +} +#endif + +#endif /* WASM_RT_IMPL_H_ */ diff --git a/third_party/wasm2c/wasm2c/wasm-rt.h b/third_party/wasm2c/wasm2c/wasm-rt.h new file mode 100644 index 0000000000..9fd12dee03 --- /dev/null +++ b/third_party/wasm2c/wasm2c/wasm-rt.h @@ -0,0 +1,455 @@ +/* + * 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 WASM_RT_H_ +#define WASM_RT_H_ + +#include <setjmp.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __has_builtin +#define __has_builtin(x) 0 // Compatibility with non-clang compilers. +#endif + +#if __has_builtin(__builtin_expect) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#define LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define UNLIKELY(x) (x) +#define LIKELY(x) (x) +#endif + +#if __has_builtin(__builtin_memcpy) +#define wasm_rt_memcpy __builtin_memcpy +#else +#define wasm_rt_memcpy memcpy +#endif + +#if __has_builtin(__builtin_unreachable) +#define wasm_rt_unreachable __builtin_unreachable +#else +#define wasm_rt_unreachable abort +#endif + +#ifdef _MSC_VER +#define WASM_RT_THREAD_LOCAL __declspec(thread) +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#define WASM_RT_THREAD_LOCAL _Thread_local +#else +#define WASM_RT_THREAD_LOCAL +#endif + +/** + * Backward compatibility: Convert the previously exposed + * WASM_RT_MEMCHECK_SIGNAL_HANDLER macro to the ALLOCATION and CHECK macros that + * are now used. + */ +#if defined(WASM_RT_MEMCHECK_SIGNAL_HANDLER) + +#if WASM_RT_MEMCHECK_SIGNAL_HANDLER +#define WASM_RT_USE_MMAP 1 +#define WASM_RT_MEMCHECK_GUARD_PAGES 1 +#else +#define WASM_RT_USE_MMAP 0 +#define WASM_RT_MEMCHECK_BOUNDS_CHECK 1 +#endif + +#warning \ + "WASM_RT_MEMCHECK_SIGNAL_HANDLER has been deprecated in favor of WASM_RT_USE_MMAP and WASM_RT_MEMORY_CHECK_* macros" +#endif + +/** + * Specify if we use OR mmap/mprotect (+ Windows equivalents) OR malloc/realloc + * for the Wasm memory allocation and growth. mmap/mprotect guarantees memory + * will grow without being moved, while malloc ensures the virtual memory is + * consumed only as needed, but may relocate the memory to handle memory + * fragmentation. + * + * This defaults to malloc on 32-bit platforms or if memory64 support is needed. + * It defaults to mmap on 64-bit platforms assuming memory64 support is not + * needed (so we can use the guard based range checks below). + */ +#ifndef WASM_RT_USE_MMAP +#if UINTPTR_MAX > 0xffffffff && !SUPPORT_MEMORY64 +#define WASM_RT_USE_MMAP 1 +#else +#define WASM_RT_USE_MMAP 0 +#endif +#endif + +/** + * Set the range checking strategy for Wasm memories. + * + * GUARD_PAGES: memory accesses rely on unmapped pages/guard pages to trap + * out-of-bound accesses. + * + * BOUNDS_CHECK: memory accesses are checked with explicit bounds checks. + * + * This defaults to GUARD_PAGES as this is the fasest option, iff the + * requirements of GUARD_PAGES --- 64-bit platforms, MMAP allocation strategy, + * no 64-bit memories --- are met. This falls back to BOUNDS otherwise. + */ + +// Check if Guard checks are supported +#if UINTPTR_MAX > 0xffffffff && WASM_RT_USE_MMAP && !SUPPORT_MEMORY64 +#define WASM_RT_GUARD_PAGES_SUPPORTED 1 +#else +#define WASM_RT_GUARD_PAGES_SUPPORTED 0 +#endif + +// Specify defaults for memory checks if unspecified +#if !defined(WASM_RT_MEMCHECK_GUARD_PAGES) && \ + !defined(WASM_RT_MEMCHECK_BOUNDS_CHECK) +#if WASM_RT_GUARD_PAGES_SUPPORTED +#define WASM_RT_MEMCHECK_GUARD_PAGES 1 +#else +#define WASM_RT_MEMCHECK_BOUNDS_CHECK 1 +#endif +#endif + +// Ensure the macros are defined +#ifndef WASM_RT_MEMCHECK_GUARD_PAGES +#define WASM_RT_MEMCHECK_GUARD_PAGES 0 +#endif +#ifndef WASM_RT_MEMCHECK_BOUNDS_CHECK +#define WASM_RT_MEMCHECK_BOUNDS_CHECK 0 +#endif + +// Sanity check the use of guard pages +#if WASM_RT_MEMCHECK_GUARD_PAGES && !WASM_RT_GUARD_PAGES_SUPPORTED +#error \ + "WASM_RT_MEMCHECK_GUARD_PAGES not supported on this platform/configuration" +#endif + +#if WASM_RT_MEMCHECK_GUARD_PAGES && WASM_RT_MEMCHECK_BOUNDS_CHECK +#error \ + "Cannot use both WASM_RT_MEMCHECK_GUARD_PAGES and WASM_RT_MEMCHECK_BOUNDS_CHECK" + +#elif !WASM_RT_MEMCHECK_GUARD_PAGES && !WASM_RT_MEMCHECK_BOUNDS_CHECK +#error \ + "Must choose at least one from WASM_RT_MEMCHECK_GUARD_PAGES and WASM_RT_MEMCHECK_BOUNDS_CHECK" +#endif + +/** + * Some configurations above require the Wasm runtime to install a signal + * handler. However, this can be explicitly disallowed by the host using + * WASM_RT_SKIP_SIGNAL_RECOVERY. In this case, when the wasm code encounters an + * OOB access, it may either trap or abort. + */ +#ifndef WASM_RT_SKIP_SIGNAL_RECOVERY +#define WASM_RT_SKIP_SIGNAL_RECOVERY 0 +#endif + +#if WASM_RT_MEMCHECK_GUARD_PAGES && !WASM_RT_SKIP_SIGNAL_RECOVERY +#define WASM_RT_INSTALL_SIGNAL_HANDLER 1 +#else +#define WASM_RT_INSTALL_SIGNAL_HANDLER 0 +#endif + +#ifndef WASM_RT_USE_STACK_DEPTH_COUNT +/* The signal handler on POSIX can detect call stack overflows. On windows, or + * platforms without a signal handler, we use stack depth counting. */ +#if WASM_RT_INSTALL_SIGNAL_HANDLER && !defined(_WIN32) +#define WASM_RT_USE_STACK_DEPTH_COUNT 0 +#else +#define WASM_RT_USE_STACK_DEPTH_COUNT 1 +#endif +#endif + +#if WASM_RT_USE_STACK_DEPTH_COUNT +/** + * When the signal handler cannot be used to detect stack overflows, stack depth + * is limited explicitly. The maximum stack depth before trapping can be + * configured by defining this symbol before including wasm-rt when building the + * generated c files, for example: + * + * ``` + * cc -c -DWASM_RT_MAX_CALL_STACK_DEPTH=100 my_module.c -o my_module.o + * ``` + */ +#ifndef WASM_RT_MAX_CALL_STACK_DEPTH +#define WASM_RT_MAX_CALL_STACK_DEPTH 500 +#endif + +/** Current call stack depth. */ +extern WASM_RT_THREAD_LOCAL uint32_t wasm_rt_call_stack_depth; + +#endif + +#if defined(_MSC_VER) +#define WASM_RT_NO_RETURN __declspec(noreturn) +#else +#define WASM_RT_NO_RETURN __attribute__((noreturn)) +#endif + +#if defined(__APPLE__) && WASM_RT_INSTALL_SIGNAL_HANDLER +#define WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS 1 +#else +#define WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS 0 +#endif + +/** Reason a trap occurred. Provide this to `wasm_rt_trap`. */ +typedef enum { + WASM_RT_TRAP_NONE, /** No error. */ + WASM_RT_TRAP_OOB, /** Out-of-bounds access in linear memory or a table. */ + WASM_RT_TRAP_INT_OVERFLOW, /** Integer overflow on divide or truncation. */ + WASM_RT_TRAP_DIV_BY_ZERO, /** Integer divide by zero. */ + WASM_RT_TRAP_INVALID_CONVERSION, /** Conversion from NaN to integer. */ + WASM_RT_TRAP_UNREACHABLE, /** Unreachable instruction executed. */ + WASM_RT_TRAP_CALL_INDIRECT, /** Invalid call_indirect, for any reason. */ + WASM_RT_TRAP_UNCAUGHT_EXCEPTION, /* Exception thrown and not caught */ +#if WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS + WASM_RT_TRAP_EXHAUSTION = WASM_RT_TRAP_OOB, +#else + WASM_RT_TRAP_EXHAUSTION, /** Call stack exhausted. */ +#endif +} wasm_rt_trap_t; + +/** Value types. Used to define function signatures. */ +typedef enum { + WASM_RT_I32, + WASM_RT_I64, + WASM_RT_F32, + WASM_RT_F64, + WASM_RT_V128, + WASM_RT_FUNCREF, + WASM_RT_EXTERNREF, +} wasm_rt_type_t; + +/** + * A generic function pointer type, both for Wasm functions (`code`) + * and host functions (`hostcode`). All function pointers are stored + * in this canonical form, but must be cast to their proper signature + * to call. + */ +typedef void (*wasm_rt_function_ptr_t)(void); + +/** + * The type of a function (an arbitrary number of param and result types). + * This is represented as an opaque 256-bit ID. + */ +typedef const char* wasm_rt_func_type_t; + +/** A function instance (the runtime representation of a function). + * These can be stored in tables of type funcref, or used as values. */ +typedef struct { + /** The function's type. */ + wasm_rt_func_type_t func_type; + /** The function. The embedder must know the actual C signature of the + * function and cast to it before calling. */ + wasm_rt_function_ptr_t func; + /** A function instance is a closure of the function over an instance + * of the originating module. The module_instance element will be passed into + * the function at runtime. */ + void* module_instance; +} wasm_rt_funcref_t; + +/** Default (null) value of a funcref */ +static const wasm_rt_funcref_t wasm_rt_funcref_null_value = {NULL, NULL, NULL}; + +/** The type of an external reference (opaque to WebAssembly). */ +typedef void* wasm_rt_externref_t; + +/** Default (null) value of an externref */ +static const wasm_rt_externref_t wasm_rt_externref_null_value = NULL; + +/** A Memory object. */ +typedef struct { + /** The linear memory data, with a byte length of `size`. */ + uint8_t* data; + /** The current and maximum page count for this Memory object. If there is no + * maximum, `max_pages` is 0xffffffffu (i.e. UINT32_MAX). */ + uint64_t pages, max_pages; + /** The current size of the linear memory, in bytes. */ + uint64_t size; + /** Is this memory indexed by u64 (as opposed to default u32) */ + bool is64; +} wasm_rt_memory_t; + +/** A Table of type funcref. */ +typedef struct { + /** The table element data, with an element count of `size`. */ + wasm_rt_funcref_t* data; + /** The maximum element count of this Table object. If there is no maximum, + * `max_size` is 0xffffffffu (i.e. UINT32_MAX). */ + uint32_t max_size; + /** The current element count of the table. */ + uint32_t size; +} wasm_rt_funcref_table_t; + +/** A Table of type externref. */ +typedef struct { + /** The table element data, with an element count of `size`. */ + wasm_rt_externref_t* data; + /** The maximum element count of this Table object. If there is no maximum, + * `max_size` is 0xffffffffu (i.e. UINT32_MAX). */ + uint32_t max_size; + /** The current element count of the table. */ + uint32_t size; +} wasm_rt_externref_table_t; + +/** Initialize the runtime. */ +void wasm_rt_init(void); + +/** Is the runtime initialized? */ +bool wasm_rt_is_initialized(void); + +/** Free the runtime's state. */ +void wasm_rt_free(void); + +/** + * A hardened jmp_buf that allows checking for initialization before use + */ +typedef struct { + /* Is the jmp buf intialized? */ + bool initialized; + /* jmp_buf contents */ + jmp_buf buffer; +} wasm_rt_jmp_buf; + +#if WASM_RT_INSTALL_SIGNAL_HANDLER && !defined(_WIN32) +#define WASM_RT_SETJMP_SETBUF(buf) sigsetjmp(buf, 1) +#else +#define WASM_RT_SETJMP_SETBUF(buf) setjmp(buf) +#endif + +#define WASM_RT_SETJMP(buf) \ + ((buf).initialized = true, WASM_RT_SETJMP_SETBUF((buf).buffer)) + +#if WASM_RT_INSTALL_SIGNAL_HANDLER && !defined(_WIN32) +#define WASM_RT_LONGJMP_UNCHECKED(buf, val) siglongjmp(buf, val) +#else +#define WASM_RT_LONGJMP_UNCHECKED(buf, val) longjmp(buf, val) +#endif + +#define WASM_RT_LONGJMP(buf, val) \ + /* Abort on failure as this may be called in the trap handler */ \ + if (!((buf).initialized)) \ + abort(); \ + (buf).initialized = false; \ + WASM_RT_LONGJMP_UNCHECKED((buf).buffer, val) + +/** + * Stop execution immediately and jump back to the call to `wasm_rt_impl_try`. + * The result of `wasm_rt_impl_try` will be the provided trap reason. + * + * This is typically called by the generated code, and not the embedder. + */ +WASM_RT_NO_RETURN void wasm_rt_trap(wasm_rt_trap_t); + +/** + * Return a human readable error string based on a trap type. + */ +const char* wasm_rt_strerror(wasm_rt_trap_t trap); + +#define wasm_rt_try(target) WASM_RT_SETJMP(target) + +/** + * Initialize a Memory object with an initial page size of `initial_pages` and + * a maximum page size of `max_pages`, indexed with an i32 or i64. + * + * ``` + * wasm_rt_memory_t my_memory; + * // 1 initial page (65536 bytes), and a maximum of 2 pages, + * // indexed with an i32 + * wasm_rt_allocate_memory(&my_memory, 1, 2, false); + * ``` + */ +void wasm_rt_allocate_memory(wasm_rt_memory_t*, + uint64_t initial_pages, + uint64_t max_pages, + bool is64); + +/** + * Grow a Memory object by `pages`, and return the previous page count. If + * this new page count is greater than the maximum page count, the grow fails + * and 0xffffffffu (UINT32_MAX) is returned instead. + * + * ``` + * wasm_rt_memory_t my_memory; + * ... + * // Grow memory by 10 pages. + * uint32_t old_page_size = wasm_rt_grow_memory(&my_memory, 10); + * if (old_page_size == UINT32_MAX) { + * // Failed to grow memory. + * } + * ``` + */ +uint64_t wasm_rt_grow_memory(wasm_rt_memory_t*, uint64_t pages); + +/** + * Free a Memory object. + */ +void wasm_rt_free_memory(wasm_rt_memory_t*); + +/** + * Initialize a funcref Table object with an element count of `elements` and a + * maximum size of `max_elements`. + * + * ``` + * wasm_rt_funcref_table_t my_table; + * // 5 elements and a maximum of 10 elements. + * wasm_rt_allocate_funcref_table(&my_table, 5, 10); + * ``` + */ +void wasm_rt_allocate_funcref_table(wasm_rt_funcref_table_t*, + uint32_t elements, + uint32_t max_elements); + +/** + * Free a funcref Table object. + */ +void wasm_rt_free_funcref_table(wasm_rt_funcref_table_t*); + +/** + * Initialize an externref Table object with an element count + * of `elements` and a maximum size of `max_elements`. + * Usage as per wasm_rt_allocate_funcref_table. + */ +void wasm_rt_allocate_externref_table(wasm_rt_externref_table_t*, + uint32_t elements, + uint32_t max_elements); + +/** + * Free an externref Table object. + */ +void wasm_rt_free_externref_table(wasm_rt_externref_table_t*); + +/** + * Grow a Table object by `delta` elements (giving the new elements the value + * `init`), and return the previous element count. If this new element count is + * greater than the maximum element count, the grow fails and 0xffffffffu + * (UINT32_MAX) is returned instead. + */ +uint32_t wasm_rt_grow_funcref_table(wasm_rt_funcref_table_t*, + uint32_t delta, + wasm_rt_funcref_t init); +uint32_t wasm_rt_grow_externref_table(wasm_rt_externref_table_t*, + uint32_t delta, + wasm_rt_externref_t init); + +#ifdef __cplusplus +} +#endif + +#endif /* WASM_RT_H_ */ |