├── .clang-format ├── .gitignore ├── .gitmodules ├── CODE_OF_CONDUCT.md ├── Contributing.md ├── LICENSE ├── Makefile ├── README.rst ├── host ├── assert.c ├── float_minmax.c ├── init.c ├── membase.S ├── memory.c ├── stdio.c ├── trap.c └── wart_main.c ├── src ├── ast_dumper.cc ├── ast_dumper.h ├── ast_visitor.h ├── sexpr_dump.cc ├── wac.py ├── waot_visitor.cc ├── waot_visitor.h ├── wart_trap.h ├── wasm_ast.cc ├── wasm_ast.h ├── wasm_parser_cxx.cc ├── wasm_parser_cxx.h └── wat.cc └── test ├── codegen ├── assertreturn.wast ├── assertreturnnan.wast ├── asserttrap.wast ├── binary.wast ├── call.wast ├── cast.wast ├── compare.wast ├── convert.wast ├── export.wast ├── function.wast ├── getlocal.wast ├── if.wast ├── import.wast ├── invoke.wast ├── load-offset.wast ├── memory-segment-many.wast ├── memory_assert.wast ├── module-empty.wast ├── module-multi.wast ├── return.wast ├── setlocal.wast ├── store-offset.wast └── unary.wast ├── lit.cfg ├── parser ├── assertreturn.wast ├── assertreturnnan.wast ├── bad-memory-empty.wast ├── binary.wast ├── block-nested.wast ├── block.wast ├── call-name-prefix.wast ├── call-named.wast ├── call.wast ├── callimport-defined-later.wast ├── callimport-named.wast ├── callimport.wast ├── cast.wast ├── compare.wast ├── const.wast ├── convert.wast ├── export.wast ├── func-named.wast ├── getlocal-index-after-param.wast ├── getlocal-index-mixed-named-unnamed.wast ├── getlocal-named.wast ├── getlocal-param-named.wast ├── getlocal-param.wast ├── getlocal.wast ├── if.wast ├── import.wast ├── invoke.wast ├── load-aligned.wast ├── load-offset.wast ├── load.wast ├── local-multi.wast ├── local.wast ├── memory-init-max-size.wast ├── memory-init-size.wast ├── memory-segment-1.wast ├── memory-segment-many.wast ├── module-empty.wast ├── module-multi.wast ├── param-binding.wast ├── param-multi.wast ├── param-type-1.wast ├── param-type-2.wast ├── result.wast ├── return-empty.wast ├── return.wast ├── setlocal.wast ├── store-aligned.wast ├── store-offset.wast ├── store.wast ├── typeinference.wast └── unary.wast ├── spec ├── address.wast ├── conversions.wast ├── f32.wast ├── f32_cmp.wast ├── f64.wast ├── f64_cmp.wast ├── float_exprs.wast ├── float_literals.wast ├── float_misc.wast ├── functions.wast ├── i32.wast ├── i64.wast ├── int_exprs.wast └── int_literals.wast └── system ├── assertreturn-fail.wast ├── assertreturn.wast ├── assertreturnnan-fail.wast ├── if.wast ├── invoke.wast ├── start-wrong-signature.wast └── start.wast /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Chromium 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | 30 | out/ 31 | *~ 32 | test/*/Output 33 | *.orig 34 | *.rej 35 | core 36 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/sexpr-wasm-prototype"] 2 | path = third_party/sexpr-wasm-prototype 3 | url = git@github.com:WebAssembly/sexpr-wasm-prototype.git 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | dschuff@chromium.org. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to WebAssembly 2 | 3 | Interested in participating? Please follow 4 | [the same contributing guidelines as the design repository][]. 5 | 6 | [the same contributing guidelines as the design repository]: https://github.com/WebAssembly/design/blob/master/Contributing.md 7 | 8 | Also, please be sure to read [the README.rst](README.rst) for this repository. 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .SUFFIXES: 2 | 3 | ALL = sexpr_dump wat libwart.a 4 | CC ?= gcc 5 | CXX ?= g++ 6 | 7 | CFLAGS ?= -Wall -Werror -g -O0 8 | # -fno-exceptions and -fno-rtti are required to link with LLVM (which uses these 9 | # flags by default) but we don't want ALL of LLVM's cflags (e.g. warning flags, 10 | # -fPIC) 11 | CXXFLAGS = -std=c++11 -fno-exceptions -fno-rtti 12 | LDFLAGS = 13 | 14 | PARSER_SRC = third_party/sexpr-wasm-prototype/src 15 | OUT_DIR = out 16 | 17 | VPATH = $(PARSER_SRC):src:$(OUT_DIR) 18 | 19 | PARSER_SRCS = wasm-check.c wasm-lexer.c wasm-parser.c wasm-vector.c wasm.c 20 | PARSER_OBJS = $(patsubst %.c, $(OUT_DIR)/%.o, $(PARSER_SRCS)) 21 | WASMGEN_SRCS = sexpr-wasm.c wasm-binary-writer.c wasm-writer.c 22 | WASMGEN_OBJS = $(patsubst %.c, $(OUT_DIR)/%.o, $(WASMGEN_SRCS)) 23 | 24 | PARSER_HEADERS = $(PARSER_SRC)/wasm.h $(PARSER_SRC)/wasm-parser.h $(PARSER_SRC)/wasm-common.h 25 | 26 | WASM_CPP_HEADERS = wasm_parser_cxx.h wasm_ast.h ast_visitor.h ast_dumper.h 27 | WASM_CPP_SRCS = wasm_parser_cxx.cc wasm_ast.cc ast_dumper.cc 28 | WASM_CPP_OBJS = $(patsubst %.cc, $(OUT_DIR)/%.o, $(WASM_CPP_SRCS)) 29 | 30 | WAOT_HEADERS = waot_visitor.h 31 | WAT_SRCS = waot_visitor.cc wat.cc 32 | WAT_OBJS = $(patsubst %.cc, $(OUT_DIR)/%.o, $(WAT_SRCS)) 33 | 34 | 35 | LLVM_PATH ?= /s/llvm-upstream/release_37/install 36 | LLVM_BUILD_PATH ?= $(LLVM_PATH)/../build 37 | LLVM_CONFIG = $(LLVM_PATH)/bin/llvm-config 38 | 39 | LLVM_CPPFLAGS := $(shell $(LLVM_CONFIG) --cppflags) 40 | LLVM_LIBS := $(shell $(LLVM_CONFIG) --libs) 41 | LLVM_LIBDIR := $(shell $(LLVM_CONFIG) --libdir) 42 | LLVM_SYSTEMLIBS := $(shell $(LLVM_CONFIG) --system-libs) 43 | 44 | # When we support Windows, that would probably be the time to turn this makefile 45 | # into something a bit more robust. 46 | OS := $(shell uname) 47 | ifeq ($(OS), Linux) 48 | # Support -DBUILD_SHARED_LIBS on Linux 49 | OS_LDFLAGS := -Wl,-rpath=$(LLVM_LIBDIR) -Wl,--as-needed 50 | else 51 | OS_LDFLAGS := 52 | endif 53 | 54 | LLVM_LDFLAGS := $(shell $(LLVM_CONFIG) --ldflags) $(OS_LDFLAGS) 55 | 56 | SANITIZE ?= 57 | ifneq ($(SANITIZE),) 58 | CFLAGS += -fsanitize=$(SANITIZE) 59 | LDFLAGS += -fsanitize=$(SANITIZE) 60 | endif 61 | 62 | 63 | .PHONY: all 64 | all: $(OUT_DIR) $(addprefix $(OUT_DIR)/,$(ALL)) 65 | 66 | $(OUT_DIR)/: 67 | mkdir $@ 68 | 69 | $(OUT_DIR)/%.o: %.c $(PARSER_HEADERS) 70 | $(CC) $(CFLAGS) -I$(PARSER_SRC) -c -Wno-unused-function -Wno-return-type -o $@ $< 71 | $(OUT_DIR)/%.o: %.cc $(PARSER_HEADERS) $(WASM_CPP_HEADERS) $(WAOT_HEADERS) 72 | $(CXX) $(LLVM_CPPFLAGS) $(CXXFLAGS) -I$(PARSER_SRC) -Wno-format $(CFLAGS) -c -o $@ $< 73 | 74 | $(OUT_DIR)/sexpr-wasm: out/sexpr-wasm.o $(PARSER_OBJS) $(WASMGEN_OBJS) 75 | $(CC) -o $@ $(PARSER_OBJS) $(WASMGEN_OBJS) $(LDFLAGS) 76 | 77 | $(OUT_DIR)/sexpr_dump: out/sexpr_dump.o $(PARSER_OBJS) $(WASM_CPP_OBJS) 78 | $(CXX) -o $@ out/sexpr_dump.o $(PARSER_OBJS) $(WASM_CPP_OBJS) $(LDFLAGS) $(LLVM_LDFLAGS) $(LLVM_LIBS) $(LLVM_SYSTEMLIBS) 79 | 80 | $(OUT_DIR)/wat: $(WAT_OBJS) $(PARSER_OBJS) $(WASM_CPP_OBJS) 81 | $(CXX) -o $@ $(WAT_OBJS) $(PARSER_OBJS) $(WASM_CPP_OBJS) $(LDFLAGS) $(LLVM_LDFLAGS) $(LLVM_LIBS) 82 | 83 | $(PARSER_SRC)/wasm-keywords.h: $(PARSER_SRC)/wasm-keywords.gperf 84 | gperf --compare-strncmp --readonly-tables --struct-type $< --output-file $@ 85 | 86 | #### RUNTIME ### 87 | RUNTIME_CC = $(CC) 88 | RUNTIME_CFLAGS = $(CFLAGS) -Wno-unused-function 89 | 90 | RUNTIME_SRCS = stdio.c wart_main.c assert.c trap.c float_minmax.c memory.c 91 | RUNTIME_OBJS = $(patsubst %.c, $(OUT_DIR)/%.o, $(RUNTIME_SRCS)) $(OUT_DIR)/membase.o 92 | 93 | $(OUT_DIR)/%.o: host/%.c 94 | $(RUNTIME_CC) $(RUNTIME_CFLAGS) -Isrc -c -o $@ $< 95 | $(OUT_DIR)/%.o: host/%.S 96 | $(CC) $(RUNTIME_CFLAGS) -c -o $@ $< 97 | 98 | $(OUT_DIR)/libwart.a: $(RUNTIME_OBJS) 99 | ar rcs $@ $(RUNTIME_OBJS) 100 | 101 | .PHONY: runtime 102 | TEST_CC = $(OUT_DIR)/wac.py 103 | 104 | $(TEST_CC): src/wac.py 105 | cp $< $(OUT_DIR) 106 | runtime: $(OUT_DIR)/libwart.a $(TEST_CC) 107 | 108 | 109 | #### TESTS #### 110 | .PHONY: test 111 | test: $(OUT_DIR) $(OUT_DIR)/sexpr_dump $(OUT_DIR)/sexpr-wasm $(OUT_DIR)/wat runtime 112 | PATH=$(PATH):$(LLVM_PATH)/bin $(LLVM_BUILD_PATH)/bin/llvm-lit -sv test/ 113 | #### CLEAN #### 114 | .PHONY: clean 115 | clean: 116 | rm -rf $(OUT_DIR) 117 | rm -rf test/*/Output 118 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | WebAssembly AOT Prototype (aka WAOT) 2 | ========== 3 | 4 | Ahead-of-time compiler and runtime for WebAssembly 5 | 6 | Overview 7 | -------- 8 | 9 | WAOT compiles WebAssembly s-expression files of the style used by the 10 | WebAssembly `spec `_ into native executables. 11 | 12 | It uses `sexpr-wasm-prototype `_ 13 | to parse s-expressions to an AST IR, and generates LLVM IR from that. 14 | It then translates the LLVM to a native object file and links it with a provided 15 | runtime library and the platform's standard C library. It is developed on Linux 16 | but should (most likely?) work on Mac OS as well. 17 | 18 | WAOT is intended to be an experimental platform for non-browser, non-Javascript 19 | uses of WebAssembly and inform its design. It's still in an early stage. I have 20 | thus far avoided the temptation to run the LLVM optimizers or look at the 21 | generated code. 22 | 23 | Components 24 | ---------- 25 | 26 | ``sexpr_dump`` 27 | S-expression dumper: parses an s-expression file into WAOT's AST and dumps it 28 | out again. Currently no desugaring is done, so files should round-trip. 29 | 30 | ``wat`` 31 | WebAssembly Translator: reads an s-expression file and outputs an LLVM 32 | assembly file. 33 | 34 | ``libwart`` 35 | WebAssembly runtime library: a target library against which compiled 36 | WebAssembly modules are linked. Includes definitions of functions defined in 37 | the spec interpreter's 'host' section (e.g. ``stdio.print``) and libgcc-style 38 | runtime support functions called by generated code (e.g. ``__assert_fail``) 39 | 40 | ``wac.py`` 41 | WebAssembly Compiler: an end-to-end compiler which runs ``wat`` and LLVM's 42 | ``llc`` tool to create an object file, and links it against ``libwart.a`` 43 | 44 | 45 | 46 | Building and Running 47 | -------------------- 48 | 49 | Clone as normal, but don't forget to update/init submodules as well:: 50 | 51 | $ git clone https://github.com/WebAssembly/wasm-aot-prototype 52 | $ git submodule update --init 53 | 54 | Build or download LLVM. I use a locally-built copy of LLVM 3.7, configured using 55 | CMake's ``BUILD_SHARED_LIBS`` option because it makes for fast link times. The 56 | Makefile's default linker flags are set up for that use case. 57 | Set LLVM_PATH in your environment to a directory containing the ``llvm-config`` 58 | program. If you want to run the tests, it must also contain ``llvm-lit`` and 59 | ``FileCheck`` 60 | (which means it probably needs to be a build directory rather than an install 61 | directory). 62 | 63 | Run ``make``. By default output goes into the ``out/`` directory, and current 64 | options for build targets include the above-mentioned components, plus the 65 | ``sexpr-wasm`` tool from 66 | `sexpr-wasm-prototype `_, 67 | (which is used for testing) and a ``test`` target. 68 | 69 | To run tests, run ``make test``. This will run the tests from the ``test/`` 70 | directory using ``llvm-lit`` and ``FileCheck``, which are described in the 71 | `LLVM command guide `_ 72 | 73 | 74 | License and Contributing 75 | ------------------------ 76 | 77 | Contributions are welcome, and should follow the same guidelines as the 78 | `WebAssembly design `_. 79 | The license can be found in the ``_ file. 80 | This repository also includes tests derived from the 81 | `sexpr-wasm-prototype `_ 82 | and `spec `_ repositories, which would covered 83 | by those licenses (spoiler alert: they are the same license). 84 | -------------------------------------------------------------------------------- /host/assert.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 WebAssembly Community Group participants 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | extern int32_t __wasm_exit_status; 23 | // Define an assert failure function for each type. We could have used a 24 | // vararg function, but we can't pass floats in ... because C promotes all 25 | // of those args to int/double. 26 | void __wasm_assert_fail_i32(int32_t line_num, 27 | int32_t expected, 28 | int32_t actual) { 29 | fprintf( 30 | stderr, 31 | "Assertion failure in assert_return on line %d: expected %d, got %d\n", 32 | line_num, 33 | expected, 34 | actual); 35 | __wasm_exit_status = 1; 36 | } 37 | 38 | void __wasm_assert_fail_i64(int32_t line_num, 39 | int64_t expected, 40 | int64_t actual) { 41 | fprintf(stderr, 42 | "Assertion failure in assert_return on line %d: expected %" PRId64 43 | ", got %" PRId64 "\n", 44 | line_num, 45 | expected, 46 | actual); 47 | __wasm_exit_status = 1; 48 | } 49 | 50 | void __wasm_assert_fail_f32(int32_t line_num, float expected, float actual) { 51 | fprintf( 52 | stderr, 53 | "Assertion failure in assert_return on line %d: expected %f, got %f\n", 54 | line_num, 55 | expected, 56 | actual); 57 | __wasm_exit_status = 1; 58 | } 59 | 60 | void __wasm_assert_fail_f64(int32_t line_num, double expected, double actual) { 61 | fprintf( 62 | stderr, 63 | "Assertion failure in assert_return on line %d: expected %a, got %a\n", 64 | line_num, 65 | expected, 66 | actual); 67 | __wasm_exit_status = 1; 68 | } 69 | 70 | void __wasm_assert_trap_fail(int32_t line_num) { 71 | fprintf(stderr, 72 | "Trap Assertion failure in assert_trap on line %d: no trap raised\n", 73 | line_num); 74 | __wasm_exit_status = 1; 75 | } 76 | 77 | void __wasm_assert_return_nan_f32(int32_t line_num, float value) { 78 | if (isnan(value)) 79 | return; 80 | fprintf(stderr, 81 | "Assertion failure in assert_return_nan on line %d: expected NaN, " 82 | "got %f\n", 83 | line_num, 84 | value); 85 | __wasm_exit_status = 1; 86 | } 87 | 88 | void __wasm_assert_return_nan_f64(int32_t line_num, double value) { 89 | if (isnan(value)) 90 | return; 91 | fprintf(stderr, 92 | "Assertion failure in assert_return_nan on line %d: expected NaN, " 93 | "got %lf\n", 94 | line_num, 95 | value); 96 | __wasm_exit_status = 1; 97 | } 98 | -------------------------------------------------------------------------------- /host/float_minmax.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 WebAssembly Community Group participants 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | float __wasm_float_min_f32(float lhs, float rhs) { 18 | // If either operand is NaN, return it 19 | if (lhs != lhs) 20 | return lhs; 21 | if (rhs != rhs) 22 | return rhs; 23 | return lhs < rhs ? lhs : rhs; 24 | } 25 | 26 | double __wasm_float_min_f64(double lhs, double rhs) { 27 | // If either operand is NaN, return it 28 | if (lhs != lhs) 29 | return lhs; 30 | if (rhs != rhs) 31 | return rhs; 32 | return lhs < rhs ? lhs : rhs; 33 | } 34 | 35 | float __wasm_float_max_f32(float lhs, float rhs) { 36 | // If either operand is NaN, return it 37 | if (lhs != lhs) 38 | return lhs; 39 | if (rhs != rhs) 40 | return rhs; 41 | return lhs > rhs ? lhs : rhs; 42 | } 43 | 44 | double __wasm_float_max_f64(double lhs, double rhs) { 45 | // If either operand is NaN, return it 46 | if (lhs != lhs) 47 | return lhs; 48 | if (rhs != rhs) 49 | return rhs; 50 | return lhs > rhs ? lhs : rhs; 51 | } 52 | -------------------------------------------------------------------------------- /host/init.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 WebAssembly Community Group participants 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | 19 | #include "wart_trap.h" 20 | 21 | /* For now, only support having one running module at a time. We expect the 22 | generated spec script code to instantiate the module by calling 23 | __wasm_allocate_memory and tear it down with __wasm_free_memory. 24 | */ 25 | static unsigned char* __wasm_linear_memory_base = NULL; 26 | const size_t kPageSize = 27 | 16 * 1024; // NaCl uses 16k, it should work everywhere. 28 | 29 | size_t __wasm_page_size() { 30 | return kPageSize; 31 | } 32 | 33 | void __wasm_allocate_memory(size_t initial_size) { 34 | int ret = posix_memalign(&__wasm_linear_memory_base, kPageSize, initial_size); 35 | if (ret == ENOMEM) { 36 | __wasm_report_error("Out of memory allocating heap of %zd bytes\n", 37 | initial_size); 38 | __wasm_trap(kOutOfMemory); 39 | } 40 | if (ret != 0) { 41 | __wasm_report_error( 42 | "Error allocating heap of %zd bytes with aligment %zd\n", 43 | initial_size, 44 | kPageSize); 45 | __wasm_trap(kUnknownInternalError); 46 | } 47 | } 48 | 49 | void __wasm_grow_memory(size_t delta) { 50 | if (delta & (kPageSize - 1) != 0) { 51 | __wasm_report_error( 52 | "grow_memory delta %zd is not a multiple of page size %zd\n", 53 | delta, 54 | kPageSize); 55 | __wasm_trap(kInvalidArgument); 56 | } 57 | } 58 | 59 | void __wasm_free_memory() { 60 | free(__wasm_linear_memory_base); 61 | } 62 | -------------------------------------------------------------------------------- /host/membase.S: -------------------------------------------------------------------------------- 1 | #if defined(__linux__) 2 | .type .wasm_membase,@object # @.membase 3 | .section .membase,"aw",@nobits 4 | .globl ".wasm_membase" 5 | .align 65536 6 | .wasm_membase: 7 | .zero 4294967296 8 | .size .wasm_membase, 4294967296 9 | #elif defined(__APPLE__) 10 | .globl "_.wasm_membase" 11 | .zerofill __DATA,.membase,"_.wasm_membase",4294967296,16 12 | #endif 13 | -------------------------------------------------------------------------------- /host/memory.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 WebAssembly Community Group participants 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "wart_trap.h" 19 | 20 | /* For now, only support having one running module at a time. We expect the 21 | generated spec script code to instantiate the module by calling 22 | __wasm_allocate_memory and tear it down with __wasm_free_memory. 23 | */ 24 | const size_t kPageSize = 25 | 64 * 1024; // NaCl uses 64k, it should work everywhere. 26 | 27 | size_t __wasm_page_size() { 28 | return kPageSize; 29 | } 30 | 31 | void __wasm_init_memory(void* base, size_t initial_size) { 32 | /* For now, do nothing. */ 33 | } 34 | 35 | void __wasm_grow_memory(size_t delta) { 36 | if ((delta & (kPageSize - 1)) != 0) { 37 | __wasm_report_error( 38 | "grow_memory delta %zd is not a multiple of page size %zd\n", 39 | delta, 40 | kPageSize); 41 | __wasm_trap(kInvalidArgument); 42 | } 43 | } 44 | 45 | void __wasm_fini_memory(void* base) { 46 | /* For now, do nothing. */ 47 | } 48 | 49 | void __wasm_init_segment(void* base, size_t address, size_t size, void* src) { 50 | memcpy((char*)base + address, src, size); 51 | } 52 | -------------------------------------------------------------------------------- /host/stdio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 WebAssembly Community Group participants 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | 19 | #define MODULE "stdio" 20 | 21 | #if defined(__linux__) 22 | #define PREFIX "." 23 | #elif defined(__APPLE__) 24 | #define PREFIX "_." 25 | #else 26 | #error "Unsupported platform" 27 | #endif 28 | 29 | #define EXPORT(retty, id, name, ...) \ 30 | retty id(__VA_ARGS__) asm(PREFIX MODULE "." name); \ 31 | retty id(__VA_ARGS__) 32 | 33 | EXPORT(void, print, "print", int n) { 34 | printf("%d\n", n); 35 | } 36 | -------------------------------------------------------------------------------- /host/trap.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 WebAssembly Community Group participants 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* The first trap implementation attempts to avoid relying on the OS or hardware 18 | exceptions. Future implementations will use more tricks. */ 19 | #include 20 | #include 21 | #include 22 | #include 23 | #ifdef DEBUG_TRAPS 24 | /* This is a GNU extension, but useful for debugging */ 25 | #include 26 | #endif 27 | 28 | #include "wart_trap.h" 29 | 30 | /* Defined in assert.c */ 31 | void __wasm_assert_trap_fail(int32_t assert_num); 32 | 33 | static jmp_buf __wasm_trap_env; 34 | static int __wasm_trap_handler_installed = 0; 35 | 36 | static const char* GetTrapName(enum TrapType type) { 37 | switch (type) { 38 | case kIntegerOverflow: 39 | return "runtime: integer overflow"; 40 | case kIntegerDivideByZero: 41 | return "runtime: integer divide by zero"; 42 | case kInvalidConversionToInteger: 43 | return "runtime: invalid conversion to integer"; 44 | case kMemoryBounds: 45 | return "runtime: out of bounds memory access"; 46 | case kMemorySizeOverflow: 47 | return "runtime: memory size overflow"; 48 | case kInvalidArgument: 49 | return "runtime: invalid argument"; 50 | case kOutOfMemory: 51 | return "runtime: out of memory"; 52 | case kUnknownInternalError: 53 | return "runtime: unknown internal error"; 54 | default: 55 | return "(unknown trap)"; 56 | } 57 | } 58 | 59 | void __wasm_trap(enum TrapType value) { 60 | /* If we don't have a trap handler installed, trap directly so we get a useful 61 | backtrace. */ 62 | if (!__wasm_trap_handler_installed) { 63 | __wasm_report_error("Trap executed: %s\n", GetTrapName(value)); 64 | #ifdef DEBUG_TRAPS 65 | void* bt_buf[4096]; 66 | backtrace_symbols_fd(bt_buf, backtrace(bt_buf, 4096), 2); 67 | #else 68 | abort(); 69 | #endif 70 | } 71 | longjmp(__wasm_trap_env, value); 72 | } 73 | 74 | /* TODO: Make this thread safe?. Or just assume assert_traps can't run in 75 | in parallel, which is probably more sane. */ 76 | typedef void (*invoke_fn)(void); 77 | void __wasm_assert_trap(int32_t assert_num, invoke_fn fn) { 78 | __wasm_trap_handler_installed = 1; 79 | int setjmp_ret = setjmp(__wasm_trap_env); 80 | if (!setjmp_ret) { 81 | // run the invoke 82 | fn(); 83 | // If the invoke returns, we didn't get a trap. 84 | __wasm_assert_trap_fail(assert_num); 85 | } 86 | __wasm_trap_handler_installed = 0; 87 | } 88 | 89 | void __wasm_report_error(char* fmt, ...) { 90 | va_list ap; 91 | va_start(ap, fmt); 92 | vfprintf(stderr, fmt, ap); 93 | va_end(ap); 94 | } 95 | -------------------------------------------------------------------------------- /host/wart_main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 WebAssembly Community Group participants 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | 20 | typedef void(InitFunction)(void); 21 | extern InitFunction* __wasm_init_array[]; 22 | 23 | typedef void(FiniFunction)(void); 24 | extern FiniFunction* __wasm_fini_array[]; 25 | 26 | int32_t __wasm_exit_status = 0; 27 | 28 | int main(int argc, char** argv) { 29 | /* Call module init constructors */ 30 | InitFunction** ini = __wasm_init_array; 31 | while (*ini) 32 | (*ini++)(); 33 | 34 | /* Eventually we should have a way to specify the entry point for modules, 35 | but for now, just expect that any necessary asserts are in the array 36 | along with the constructors. So when we get here we've already called 37 | all of the user code. */ 38 | /* Now call the destructors */ 39 | FiniFunction** fini = __wasm_fini_array; 40 | while (*fini) 41 | (*fini++)(); 42 | return __wasm_exit_status; 43 | } 44 | -------------------------------------------------------------------------------- /src/ast_dumper.cc: -------------------------------------------------------------------------------- 1 | #include "ast_dumper.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | static const char* TypeName(wasm::Type t) { 8 | switch (t) { 9 | case wasm::Type::kVoid: 10 | return "void"; 11 | case wasm::Type::kI32: 12 | return "i32"; 13 | case wasm::Type::kI64: 14 | return "i64"; 15 | case wasm::Type::kF32: 16 | return "f32"; 17 | case wasm::Type::kF64: 18 | return "f64"; 19 | case wasm::Type::kAny: 20 | return "(any)"; 21 | default: 22 | return "(unknown)"; 23 | } 24 | } 25 | 26 | static const char* MemTypeSizeName(wasm::MemType t) { 27 | switch (t) { 28 | case wasm::MemType::kI8: 29 | return "8"; 30 | case wasm::MemType::kI16: 31 | return "16"; 32 | case wasm::MemType::kI32: 33 | return "32"; 34 | case wasm::MemType::kI64: 35 | return "64"; 36 | default: 37 | return "(unknown)"; 38 | } 39 | } 40 | 41 | namespace wasm { 42 | // Freestanding utility function to dump an expression for debugging. 43 | void DumpExpr(Expression* expr, bool dump_types) { 44 | AstDumper dumper(dump_types); 45 | dumper.VisitExpression(expr); 46 | } 47 | 48 | void AstDumper::PrintType(const Expression& expr) { 49 | if (dump_types_) 50 | printf("[%s->%s]", TypeName(expr.expected_type), TypeName(expr.expr_type)); 51 | } 52 | 53 | void AstDumper::VisitModule(const Module& mod) { 54 | printf("(module\n"); 55 | for (auto& func : mod.functions) 56 | VisitFunction(*func); 57 | 58 | if (mod.initial_memory_size) { 59 | printf("(memory %u", mod.initial_memory_size); 60 | if (mod.max_memory_size) 61 | printf(" %u ", mod.max_memory_size); 62 | for (auto& seg : mod.segments) 63 | VisitSegment(*seg); 64 | printf(")\n"); 65 | } 66 | 67 | for (auto& imp : mod.imports) 68 | VisitImport(*imp); 69 | for (auto& ex : mod.exports) 70 | VisitExport(*ex); 71 | 72 | printf(")\n"); 73 | } 74 | 75 | // Functions can be declared with a single parameter list like 76 | // (func (param i32 i64)) or with a split parameter list like 77 | // (func (param i32) (param i32) 78 | // If any of the params have name bindings e.g. (param $n i32) then the 79 | // style must be used. However imports are required to use the single 80 | // style. If this is fixed, arg dumping can be shared more. 81 | static void dump_result(const Callable& c) { 82 | if (c.result_type != Type::kVoid) 83 | printf(" (result %s)", TypeName(c.result_type)); 84 | } 85 | 86 | void AstDumper::VisitImport(const Import& import) { 87 | printf("(import %s \"%s\" \"%s\"", 88 | import.local_name.c_str(), 89 | import.module_name.c_str(), 90 | import.func_name.c_str()); 91 | 92 | if (import.args.size()) { 93 | printf(" (param"); 94 | for (auto& arg : import.args) 95 | printf(" %s", TypeName(arg->type)); 96 | printf(")"); 97 | } 98 | dump_result(import); 99 | printf(")\n"); 100 | } 101 | 102 | void AstDumper::VisitExport(const Export& exp) { 103 | printf("(export \"%s\" ", exp.name.c_str()); 104 | if (exp.function->local_name.empty()) { 105 | printf("%u)", exp.function->index_in_module); 106 | } else { 107 | printf("%s)", exp.function->local_name.c_str()); 108 | } 109 | } 110 | 111 | template 112 | static void dump_var_list(T&& begin, T&& end, const char* name) { 113 | for (auto& var = begin; var != end; ++var) { 114 | printf(" (%s", name); 115 | if ((*var)->local_name.size()) 116 | printf(" %s", (*var)->local_name.c_str()); 117 | printf(" %s)", TypeName((*var)->type)); 118 | } 119 | } 120 | 121 | void AstDumper::VisitFunction(const Function& func) { 122 | printf(" (func "); 123 | if (func.local_name.size()) 124 | printf("%s ", func.local_name.c_str()); 125 | 126 | dump_var_list(func.args.begin(), func.args.end(), "param"); 127 | dump_result(func); 128 | dump_var_list( 129 | func.locals.begin() + func.args.size(), func.locals.end(), "local"); 130 | for (auto& expr : func.body) { 131 | VisitExpression(expr.get()); 132 | } 133 | printf(")\n"); 134 | } 135 | 136 | void AstDumper::VisitSegment(const Segment& seg) { 137 | printf("(segment %zu \"%s\")\n", seg.address, seg.as_string().c_str()); 138 | } 139 | 140 | void AstDumper::VisitNop(Expression* expr) { 141 | printf("(nop)"); 142 | } 143 | 144 | void AstDumper::VisitBlock(Expression* expr, 145 | UniquePtrVector* exprs) { 146 | printf("(block "); 147 | for (auto& e : *exprs) { 148 | VisitExpression(e.get()); 149 | } 150 | printf(") "); 151 | } 152 | 153 | void AstDumper::VisitIf(Expression* expr, 154 | Expression* condition, 155 | Expression* then, 156 | Expression* els) { 157 | printf(els ? "(if_else " : "(if "); 158 | VisitExpression(condition); 159 | VisitExpression(then); 160 | if (els) 161 | VisitExpression(els); 162 | printf(")\n"); 163 | } 164 | 165 | void AstDumper::VisitCall(CallExpression* expr, 166 | bool is_import, 167 | Callable* callee, 168 | int callee_index, 169 | UniquePtrVector* args) { 170 | printf(is_import ? "(call_import " : "(call "); 171 | if (callee->local_name.size()) { 172 | printf("%s ", callee->local_name.c_str()); 173 | } else { 174 | printf("%d ", callee_index); 175 | } 176 | for (auto& e : *args) { 177 | VisitExpression(e.get()); 178 | } 179 | printf(") "); 180 | } 181 | 182 | void AstDumper::VisitReturn(Expression* expr, 183 | UniquePtrVector* value) { 184 | printf("(return "); 185 | if (value->size()) 186 | VisitExpression(value->front().get()); 187 | printf(") "); 188 | } 189 | 190 | void AstDumper::VisitGetLocal(LocalExpression* expr, Variable* var) { 191 | printf("(get_local "); 192 | if (!var->local_name.empty()) { 193 | printf("%s)", var->local_name.c_str()); 194 | } else { 195 | printf("%d)", var->index); 196 | } 197 | } 198 | 199 | void AstDumper::VisitSetLocal(LocalExpression* expr, 200 | Variable* var, 201 | Expression* value) { 202 | printf("(set_local "); 203 | if (!var->local_name.empty()) { 204 | printf("%s ", var->local_name.c_str()); 205 | } else { 206 | printf("%d ", var->index); 207 | } 208 | VisitExpression(value); 209 | printf(")"); 210 | } 211 | 212 | void AstDumper::VisitMemory(Expression* expr, 213 | MemoryOperator memop, 214 | MemType mem_type, 215 | uint32_t mem_alignment, 216 | uint64_t mem_offset, 217 | bool is_signed, 218 | Expression* address, 219 | Expression* store_val) { 220 | assert(memop == kStore || !store_val); 221 | assert(memop == kLoad || !is_signed); 222 | const char* mem_op_name = memop == kLoad ? "load" : "store"; 223 | const char* mem_type_size_name = 224 | mem_type == expr->expr_type ? "" : MemTypeSizeName(mem_type); 225 | const char* sign_suffix = 226 | (memop == kStore || mem_type.IsFloatTy() || mem_type == expr->expr_type) 227 | ? "" 228 | : is_signed ? "_s" : "_u"; 229 | printf("(%s.%s%s%s offset=%" PRIu64 " align=%u", 230 | TypeName(expr->expr_type), 231 | mem_op_name, 232 | mem_type_size_name, 233 | sign_suffix, 234 | mem_offset, 235 | mem_alignment); 236 | VisitExpression(address); 237 | if (store_val) 238 | VisitExpression(store_val); 239 | printf(")\n"); 240 | } 241 | 242 | void AstDumper::VisitConst(Expression* expr, Literal* l) { 243 | switch (l->type) { 244 | case Type::kI32: 245 | printf("(%s.const 0x%x)", TypeName(l->type), l->value.i32); 246 | break; 247 | case Type::kI64: 248 | printf("(%s.const 0x%" PRIx64 ")", TypeName(l->type), l->value.i64); 249 | break; 250 | case Type::kF32: 251 | printf("(%s.const %a)", TypeName(l->type), l->value.f32); 252 | break; 253 | case Type::kF64: 254 | printf("(%s.const %a)", TypeName(l->type), l->value.f64); 255 | break; 256 | default: 257 | printf("unexpected type %d\n", static_cast(l->type)); 258 | assert(false); 259 | } 260 | } 261 | 262 | static const char* UnopName(UnaryOperator unop) { 263 | switch (unop) { 264 | case kClz: 265 | return "clz"; 266 | case kCtz: 267 | return "ctz"; 268 | case kPopcnt: 269 | return "popcnt"; 270 | case kNeg: 271 | return "neg"; 272 | case kAbs: 273 | return "abs"; 274 | case kCeil: 275 | return "ceil"; 276 | case kFloor: 277 | return "floor"; 278 | case kTrunc: 279 | return "trunc"; 280 | case kNearest: 281 | return "nearest"; 282 | case kSqrt: 283 | return "sqrt"; 284 | default: 285 | assert(false); 286 | } 287 | } 288 | 289 | void AstDumper::VisitUnop(Expression* expr, 290 | UnaryOperator unop, 291 | Expression* operand) { 292 | printf("(%s.%s ", TypeName(expr->expr_type), UnopName(unop)); 293 | VisitExpression(operand); 294 | printf(")\n"); 295 | } 296 | 297 | static const char* BinopName(BinaryOperator binop) { 298 | switch (binop) { 299 | case kAdd: 300 | return "add"; 301 | case kSub: 302 | return "sub"; 303 | case kMul: 304 | return "mul"; 305 | case kDivS: 306 | return "div_s"; 307 | case kDivU: 308 | return "div_u"; 309 | case kRemS: 310 | return "rem_s"; 311 | case kRemU: 312 | return "rem_u"; 313 | case kAnd: 314 | return "and"; 315 | case kOr: 316 | return "or"; 317 | case kXor: 318 | return "xor"; 319 | case kShl: 320 | return "shl"; 321 | case kShrU: 322 | return "shr_u"; 323 | case kShrS: 324 | return "shr_s"; 325 | case kDiv: 326 | return "div"; 327 | case kCopySign: 328 | return "copysign"; 329 | case kMin: 330 | return "min"; 331 | case kMax: 332 | return "max"; 333 | default: 334 | assert(false); 335 | } 336 | } 337 | 338 | void AstDumper::VisitBinop(Expression* expr, 339 | BinaryOperator binop, 340 | Expression* lhs, 341 | Expression* rhs) { 342 | printf("(%s.%s ", TypeName(expr->expr_type), BinopName(binop)); 343 | VisitExpression(lhs); 344 | VisitExpression(rhs); 345 | printf(")\n"); 346 | } 347 | 348 | static const char* CompareOpName(CompareOperator relop) { 349 | switch (relop) { 350 | case kEq: 351 | return "eq"; 352 | case kNE: 353 | return "ne"; 354 | case kLtS: 355 | return "lt_s"; 356 | case kLtU: 357 | return "lt_u"; 358 | case kLeS: 359 | return "le_s"; 360 | case kLeU: 361 | return "le_u"; 362 | case kGtS: 363 | return "gt_s"; 364 | case kGtU: 365 | return "gt_u"; 366 | case kGeS: 367 | return "ge_s"; 368 | case kGeU: 369 | return "ge_u"; 370 | case kLt: 371 | return "lt"; 372 | case kLe: 373 | return "le"; 374 | case kGt: 375 | return "gt"; 376 | case kGe: 377 | return "ge"; 378 | default: 379 | assert(false); // The switch is covered but gcc still warns :( 380 | } 381 | } 382 | 383 | void AstDumper::VisitCompare(Expression* expr, 384 | Type compare_type, 385 | CompareOperator relop, 386 | Expression* lhs, 387 | Expression* rhs) { 388 | printf("(%s.%s ", TypeName(compare_type), CompareOpName(relop)); 389 | VisitExpression(lhs); 390 | VisitExpression(rhs); 391 | printf(")\n"); 392 | } 393 | 394 | const char* ConversionOpName(ConversionOperator cvt) { 395 | switch (cvt) { 396 | case kExtendSInt32: 397 | return "extend_s"; 398 | case kExtendUInt32: 399 | return "extend_u"; 400 | case kWrapInt64: 401 | return "wrap"; 402 | case kTruncSFloat32: 403 | case kTruncSFloat64: 404 | return "trunc_s"; 405 | case kTruncUFloat32: 406 | case kTruncUFloat64: 407 | return "trunc_u"; 408 | case kReinterpretFloat: 409 | case kReinterpretInt: 410 | return "reinterpret"; 411 | case kConvertSInt32: 412 | case kConvertSInt64: 413 | return "convert_s"; 414 | case kConvertUInt32: 415 | case kConvertUInt64: 416 | return "convert_u"; 417 | case kPromoteFloat32: 418 | return "promote"; 419 | case kDemoteFloat64: 420 | return "demote"; 421 | default: 422 | assert(false && "Unexpected operator in ConversionOpName"); 423 | } 424 | } 425 | 426 | void AstDumper::VisitConversion(ConversionExpression* expr, 427 | ConversionOperator cvt, 428 | Expression* operand) { 429 | printf("(%s.%s/%s ", 430 | TypeName(expr->expr_type), 431 | ConversionOpName(cvt), 432 | TypeName(expr->operand_type)); 433 | VisitExpression(operand); 434 | printf(")\n"); 435 | } 436 | 437 | void AstDumper::VisitInvoke(TestScriptExpr* expr, 438 | Export* callee, 439 | UniquePtrVector* args) { 440 | // We could print the source loc info for invokes too, but that would require 441 | // putting a newline in the middle of assert statements or keeping track of 442 | // whether an invoke is inside an assert or not, so more trouble than it's 443 | // worth. 444 | printf("(invoke \"%s\" ", callee->name.c_str()); 445 | for (auto& e : *args) { 446 | VisitExpression(e.get()); 447 | } 448 | printf(")\n"); 449 | } 450 | 451 | void AstDumper::VisitAssertReturn(TestScriptExpr* expr, 452 | TestScriptExpr* invoke_arg, 453 | UniquePtrVector* expected) { 454 | printf( 455 | ";; %s:%d\n", expr->source_loc.filename.c_str(), expr->source_loc.line); 456 | printf("(assert_return "); 457 | Visit(invoke_arg); 458 | if (expected->size()) 459 | VisitExpression(expected->front().get()); 460 | printf(")\n"); 461 | } 462 | 463 | void AstDumper::VisitAssertReturnNaN(TestScriptExpr* expr, 464 | TestScriptExpr* invoke_arg) { 465 | printf( 466 | ";; %s:%d\n", expr->source_loc.filename.c_str(), expr->source_loc.line); 467 | printf("(assert_return_nan "); 468 | Visit(invoke_arg); 469 | printf(")\n"); 470 | } 471 | 472 | void AstDumper::VisitAssertTrap(TestScriptExpr* expr, 473 | TestScriptExpr* invoke_arg, 474 | const std::string& text) { 475 | printf( 476 | ";; %s:%d\n", expr->source_loc.filename.c_str(), expr->source_loc.line); 477 | printf("(assert_trap "); 478 | Visit(invoke_arg); 479 | printf(" \"%s\")\n", text.c_str()); 480 | } 481 | 482 | } // namespace wasm 483 | -------------------------------------------------------------------------------- /src/ast_dumper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 WebAssembly Community Group participants 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef AST_DUMPER_H 18 | #define AST_DUMPER_H 19 | 20 | #include "wasm_ast.h" 21 | #include "ast_visitor.h" 22 | 23 | namespace wasm { 24 | 25 | class AstDumper : public AstVisitor { 26 | public: 27 | AstDumper(bool dump_types) : dump_types_(dump_types) {} 28 | 29 | protected: 30 | void VisitModule(const Module& mod) override; 31 | void VisitImport(const Import& imp) override; 32 | void VisitExport(const Export& exp) override; 33 | void VisitFunction(const Function& func) override; 34 | void VisitSegment(const Segment& seg) override; 35 | 36 | void VisitExpression(Expression* expr) override { 37 | PrintType(*expr); 38 | AstVisitor::VisitExpression(expr); 39 | } 40 | void VisitNop(Expression* expr) override; 41 | void VisitBlock(Expression* expr, 42 | UniquePtrVector* exprs) override; 43 | void VisitIf(Expression* expr, 44 | Expression* condition, 45 | Expression* then, 46 | Expression* els) override; 47 | void VisitCall(CallExpression* expr, 48 | bool is_import, 49 | Callable* callee, 50 | int callee_index, 51 | UniquePtrVector* args) override; 52 | void VisitReturn(Expression* expr, 53 | UniquePtrVector* value) override; 54 | void VisitGetLocal(LocalExpression* expr, Variable* var) override; 55 | void VisitSetLocal(LocalExpression* expr, 56 | Variable* var, 57 | Expression* value) override; 58 | void VisitMemory(Expression* expr, 59 | MemoryOperator memop, 60 | MemType mem_type, 61 | uint32_t mem_alignment, 62 | uint64_t mem_offset, 63 | bool is_signed, 64 | Expression* address, 65 | Expression* store_val) override; 66 | void VisitConst(Expression* expr, Literal* l) override; 67 | void VisitUnop(Expression* expr, 68 | UnaryOperator unop, 69 | Expression* operand) override; 70 | void VisitBinop(Expression* epxr, 71 | BinaryOperator binop, 72 | Expression* lhs, 73 | Expression* rhs) override; 74 | void VisitCompare(Expression* expr, 75 | Type compare_type, 76 | CompareOperator relop, 77 | Expression* lhs, 78 | Expression* rhs) override; 79 | void VisitConversion(ConversionExpression* expr, 80 | ConversionOperator cvt, 81 | Expression* operand) override; 82 | 83 | void VisitInvoke(TestScriptExpr* expr, 84 | Export* callee, 85 | UniquePtrVector* args) override; 86 | void VisitAssertReturn(TestScriptExpr* expr, 87 | TestScriptExpr* invoke_arg, 88 | UniquePtrVector* expected) override; 89 | void VisitAssertReturnNaN(TestScriptExpr* expr, 90 | TestScriptExpr* invoke_arg) override; 91 | void VisitAssertTrap(TestScriptExpr* expr, 92 | TestScriptExpr* invoke_arg, 93 | const std::string& text) override; 94 | 95 | private: 96 | bool dump_types_; 97 | void PrintType(const Expression& expr); 98 | friend void DumpExpr(Expression* expr, bool dump_types); 99 | }; 100 | 101 | void DumpExpr(Expression* expr, bool dump_types); 102 | const char* ConversionOpName(ConversionOperator cvt); 103 | } 104 | #endif // AST_DUMPER_H 105 | -------------------------------------------------------------------------------- /src/ast_visitor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 WebAssembly Community Group participants 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef AST_VISITOR_H 18 | #define AST_VISITOR_H 19 | 20 | #include "wasm_ast.h" 21 | 22 | #include 23 | 24 | namespace wasm { 25 | 26 | // Visitor-ish pattern; abstracts the data layout of Expression, but driven by 27 | // the implementation. Visit is the entry. The implementation of VisitModule is 28 | // expected to call Visit{Function,Segment,Import}. The VisitFunction impl calls 29 | // VisitExpression. 30 | 31 | // The base VisitExpression implementation calls the apropriate derived-class 32 | // function for the expression type and passes in the relevant arguments. The 33 | // VisitFoo implementation is expected to then call VisitExpression the 34 | // expressions therein, but it can do whatever it needs to before and/or after 35 | // that. 36 | 37 | // TODO: We could do that same thing with VisitModule (e.g. pass it lists of 38 | // functions, segments, imports, exports) to make it uniform. Realistically it 39 | // might make more sense just to keep the extra data available to the derived 40 | // implementations though. 41 | 42 | template 43 | class AstVisitor { 44 | public: 45 | ModuleVal Visit(const Module& mod) { return VisitModule(mod); } 46 | ExprVal Visit(TestScriptExpr* script) { return VisitTestScriptExpr(script); } 47 | 48 | protected: 49 | virtual ModuleVal VisitModule(const Module& mod) { 50 | for (auto& func : mod.functions) 51 | VisitFunction(*func); 52 | 53 | if (mod.initial_memory_size) { 54 | for (auto& seg : mod.segments) 55 | VisitSegment(*seg); 56 | } 57 | for (auto& imp : mod.imports) 58 | VisitImport(*imp); 59 | for (auto& ex : mod.exports) 60 | VisitExport(*ex); 61 | return ModuleVal(); 62 | } 63 | virtual void VisitImport(const Import& imp) {} 64 | virtual void VisitExport(const Export& exp) {} 65 | virtual void VisitFunction(const Function& func) { 66 | for (auto& expr : func.body) 67 | VisitExpression(expr.get()); 68 | } 69 | virtual void VisitSegment(const Segment& seg) {} 70 | 71 | virtual ExprVal VisitExpression(Expression* expr) { 72 | switch (expr->kind) { 73 | case Expression::kNop: 74 | return VisitNop(expr); 75 | case Expression::kBlock: 76 | return VisitBlock(expr, &expr->exprs); 77 | case Expression::kIf: 78 | case Expression::kIfElse: 79 | return VisitIf(expr, 80 | expr->exprs[0].get(), 81 | expr->exprs[1].get(), 82 | expr->exprs.size() > 2 ? expr->exprs[2].get() : nullptr); 83 | case Expression::kCallDirect: { 84 | CallExpression* ce = static_cast(expr); 85 | return VisitCall( 86 | ce, ce->is_import, ce->callee, ce->callee_index, &ce->exprs); 87 | } 88 | case Expression::kReturn: 89 | // We don't support multiple returns anymore but it could still be void 90 | assert(expr->exprs.size() <= 1); 91 | return VisitReturn(expr, &expr->exprs); 92 | case Expression::kGetLocal: { 93 | LocalExpression* le = static_cast(expr); 94 | return VisitGetLocal(le, le->local_var); 95 | } 96 | case Expression::kSetLocal: { 97 | LocalExpression* le = static_cast(expr); 98 | return VisitSetLocal(le, le->local_var, le->exprs.front().get()); 99 | } 100 | case Expression::kConst: 101 | return VisitConst(expr, 102 | &static_cast(expr)->literal); 103 | case Expression::kUnary: { 104 | UnaryExpression* ue = static_cast(expr); 105 | return VisitUnop(ue, ue->unop, ue->exprs.front().get()); 106 | } 107 | case Expression::kBinary: { 108 | BinaryExpression* be = static_cast(expr); 109 | return VisitBinop( 110 | be, be->binop, be->exprs[0].get(), be->exprs[1].get()); 111 | } 112 | case Expression::kCompare: { 113 | CompareExpression* ce = static_cast(expr); 114 | return VisitCompare(ce, 115 | ce->compare_type, 116 | ce->relop, 117 | ce->exprs[0].get(), 118 | ce->exprs[1].get()); 119 | } 120 | case Expression::kConvert: { 121 | ConversionExpression* ce = static_cast(expr); 122 | return VisitConversion(ce, ce->cvt, ce->exprs.front().get()); 123 | } 124 | case Expression::kMemory: { 125 | MemoryExpression* me = static_cast(expr); 126 | return VisitMemory(me, 127 | me->memop, 128 | me->mem_type, 129 | me->alignment, 130 | me->offset, 131 | me->is_signed, 132 | me->exprs[0].get(), 133 | me->memop == kStore ? me->exprs[1].get() : nullptr); 134 | } 135 | default: 136 | assert(false); 137 | } 138 | } 139 | virtual ExprVal VisitNop(Expression* expr) { return ExprVal(); } 140 | virtual ExprVal VisitBlock(Expression* expr, 141 | UniquePtrVector* exprs) { 142 | for (auto& e : *exprs) 143 | VisitExpression(e.get()); 144 | return ExprVal(); 145 | } 146 | virtual ExprVal VisitIf(Expression* expr, 147 | Expression* condition, 148 | Expression* then, 149 | Expression* els) { 150 | VisitExpression(condition); 151 | VisitExpression(then); 152 | if (els) 153 | VisitExpression(els); 154 | return ExprVal(); 155 | } 156 | 157 | virtual ExprVal VisitCall(CallExpression* expr, 158 | bool is_import, 159 | Callable* callee, 160 | int callee_index, 161 | UniquePtrVector* args) { 162 | for (auto& e : *args) 163 | VisitExpression(e.get()); 164 | return ExprVal(); 165 | } 166 | virtual ExprVal VisitReturn(Expression* expr, 167 | UniquePtrVector* value) { 168 | if (value->size()) 169 | VisitExpression(value->front().get()); 170 | return ExprVal(); 171 | } 172 | virtual ExprVal VisitGetLocal(LocalExpression* expr, Variable* var) { 173 | return ExprVal(); 174 | } 175 | virtual ExprVal VisitSetLocal(LocalExpression* expr, 176 | Variable* var, 177 | Expression* value) { 178 | VisitExpression(value); 179 | return ExprVal(); 180 | } 181 | virtual ExprVal VisitConst(Expression* expr, Literal* l) { return ExprVal(); } 182 | virtual ExprVal VisitUnop(Expression* expr, 183 | UnaryOperator unop, 184 | Expression* operand) { 185 | VisitExpression(operand); 186 | return ExprVal(); 187 | } 188 | virtual ExprVal VisitBinop(Expression* epxr, 189 | BinaryOperator binop, 190 | Expression* lhs, 191 | Expression* rhs) { 192 | VisitExpression(lhs); 193 | VisitExpression(rhs); 194 | return ExprVal(); 195 | } 196 | virtual ExprVal VisitCompare(Expression* expr, 197 | Type compare_type, 198 | CompareOperator relop, 199 | Expression* lhs, 200 | Expression* rhs) { 201 | VisitExpression(lhs); 202 | VisitExpression(rhs); 203 | return ExprVal(); 204 | } 205 | virtual ExprVal VisitConversion(ConversionExpression* expr, 206 | ConversionOperator cvt, 207 | Expression* operand) { 208 | VisitExpression(operand); 209 | return ExprVal(); 210 | } 211 | virtual ExprVal VisitMemory(Expression* expr, 212 | MemoryOperator memop, 213 | MemType mem_type, 214 | uint32_t mem_alignment, 215 | uint64_t mem_offset, 216 | bool is_signed, 217 | Expression* address, 218 | Expression* store_val) { 219 | VisitExpression(address); 220 | if (store_val) 221 | VisitExpression(store_val); 222 | return ExprVal(); 223 | } 224 | 225 | ExprVal VisitTestScriptExpr(TestScriptExpr* expr) { 226 | switch (expr->opcode) { 227 | case TestScriptExpr::kInvoke: 228 | return VisitInvoke(expr, expr->callee, &expr->exprs); 229 | case TestScriptExpr::kAssertReturn: 230 | return VisitAssertReturn(expr, expr->invoke.get(), &expr->exprs); 231 | case TestScriptExpr::kAssertReturnNaN: 232 | return VisitAssertReturnNaN(expr, expr->invoke.get()); 233 | case TestScriptExpr::kAssertTrap: 234 | return VisitAssertTrap(expr, expr->invoke.get(), expr->trap_text); 235 | default: 236 | assert(false); 237 | } 238 | } 239 | virtual ExprVal VisitInvoke(TestScriptExpr* expr, 240 | Export* callee, 241 | UniquePtrVector* args) { 242 | for (auto& e : *args) 243 | VisitExpression(e.get()); 244 | return ExprVal(); 245 | } 246 | virtual ExprVal VisitAssertReturn(TestScriptExpr* expr, 247 | TestScriptExpr* arg, 248 | UniquePtrVector* expected) { 249 | Visit(arg); 250 | assert(expected->size() <= 1); 251 | if (expected->size()) 252 | VisitExpression(expected->front().get()); 253 | return ExprVal(); 254 | } 255 | virtual ExprVal VisitAssertReturnNaN(TestScriptExpr* expr, 256 | TestScriptExpr* arg) { 257 | Visit(arg); 258 | return ExprVal(); 259 | } 260 | virtual ExprVal VisitAssertTrap(TestScriptExpr* expr, 261 | TestScriptExpr* arg, 262 | const std::string& text) { 263 | Visit(arg); 264 | return ExprVal(); 265 | } 266 | }; 267 | } 268 | 269 | #endif // AST_VISITOR_H 270 | -------------------------------------------------------------------------------- /src/sexpr_dump.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 WebAssembly Community Group participants 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ast_dumper.h" 18 | #include "wasm.h" 19 | #include "wasm_parser_cxx.h" 20 | 21 | #include "llvm/Support/CommandLine.h" 22 | #include "llvm/Support/MemoryBuffer.h" 23 | #include "llvm/Support/raw_ostream.h" 24 | 25 | static llvm::cl::opt InputFilename( 26 | llvm::cl::Positional, 27 | llvm::cl::desc(""), 28 | llvm::cl::init("-")); 29 | 30 | static llvm::cl::opt DumpInput( 31 | "i", 32 | llvm::cl::desc("Dump input as well as output"), 33 | llvm::cl::init(false)); 34 | 35 | static llvm::cl::opt DumpTypes( 36 | "t", 37 | llvm::cl::desc("Dump types of expressions"), 38 | llvm::cl::init(false)); 39 | 40 | static llvm::cl::opt g_spec_test_script_mode( 41 | "spec-test-script", 42 | llvm::cl::desc( 43 | "Run in spec test script mode (allow multiple modules per file and" 44 | "test assertions"), 45 | llvm::cl::init(false)); 46 | 47 | int main(int argc, char** argv) { 48 | llvm::cl::ParseCommandLineOptions(argc, argv, "wasm IR dumper\n"); 49 | 50 | if (DumpInput) { 51 | llvm::ErrorOr > ErrorOrBuffer = 52 | llvm::MemoryBuffer::getFileOrSTDIN(InputFilename); 53 | if (ErrorOrBuffer.getError()) { 54 | llvm::errs() << "unable to read " << InputFilename << "\n"; 55 | return 1; 56 | } 57 | 58 | auto& Buffer = ErrorOrBuffer.get(); 59 | llvm::errs() << "INPUT:\n"; 60 | llvm::errs() << Buffer->getBuffer(); 61 | llvm::errs() << "OUTPUT:\n"; 62 | } 63 | wasm::Parser parser(InputFilename.c_str(), false); 64 | if (parser.Parse(g_spec_test_script_mode)) { 65 | return 1; 66 | } 67 | 68 | wasm::AstDumper dumper(DumpTypes); 69 | for (auto& module : parser.modules) { 70 | dumper.Visit(*module); 71 | } 72 | for (auto& script_expr : parser.test_script) { 73 | dumper.Visit(script_expr.get()); 74 | } 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /src/wac.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2016 WebAssembly Community Group participants 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | 18 | import argparse 19 | import os 20 | import subprocess 21 | import sys 22 | 23 | RUNTIME_LIB = 'wart' 24 | OS = os.uname()[0] 25 | if OS.startswith('Linux'): 26 | LINKER_SYM_FLAG = []#'-Wl,--section-start=.membase=0x100000000' 27 | elif OS.startswith('Darwin'): 28 | LINKER_SYM_FLAG = [] 29 | 30 | 31 | def find_runtime_dir(start_dir): 32 | lib_name = 'lib' + RUNTIME_LIB + '.a' 33 | if os.path.exists(os.path.join(start_dir, lib_name)): 34 | return os.path.abspath(start_dir) 35 | for d in [os.path.join(start_dir, x) for x in os.listdir(start_dir) 36 | if os.path.isdir(os.path.join(start_dir, x))]: 37 | f = find_runtime_dir(d) 38 | if f: 39 | return f 40 | return None 41 | 42 | def log_call_internal(verbose, args): 43 | if verbose: 44 | print >> sys.stderr, ' '.join(args) 45 | try: 46 | subprocess.check_call(args) 47 | except subprocess.CalledProcessError: 48 | print >> sys.stderr, 'Command Failed:' 49 | print >> sys.stderr, ' '.join(args) 50 | sys.exit(1) 51 | 52 | def Main(argv): 53 | parser = argparse.ArgumentParser( 54 | description="End-to-end compiler driver for waot tests") 55 | parser.add_argument('-o', '--output', help='Output file', default='a.out') 56 | parser.add_argument('-s', '--spec-test-script', 57 | help='Run translator in spec test script mode', 58 | action='store_true') 59 | parser.add_argument('-v', '--verbose', help='Log calls', 60 | action='store_true') 61 | parser.add_argument('inputs', metavar='INPUT', type=str, nargs='+', 62 | help='input file') 63 | options = parser.parse_args(argv) 64 | def log_call(args): 65 | return log_call_internal(options.verbose, args) 66 | 67 | file_dir = os.path.dirname(os.path.abspath(__file__)) 68 | runtime_libdir = ( 69 | find_runtime_dir(file_dir) or 70 | find_runtime_dir(os.path.join(os.path.dirname(file_dir), 'out'))) 71 | if not runtime_libdir: 72 | print 'Could not locate', 'lib' + RUNTIME_LIB + '.a' 73 | return 1 74 | 75 | outdir = os.path.dirname(os.path.abspath(options.output)) 76 | 77 | objs = [] 78 | for input in options.inputs: 79 | ll_temp = os.path.join(outdir, os.path.basename(input)) + '.ll' 80 | o_temp = os.path.join(outdir, os.path.basename(input)) + '.o' 81 | wat_flags = ['-o', ll_temp, input] 82 | if options.spec_test_script: 83 | wat_flags.append('-spec-test-script') 84 | log_call([os.path.join(runtime_libdir, 'wat')] + wat_flags) 85 | log_call(['llc', ll_temp, '-O0', '-filetype=obj', '-o', o_temp]) 86 | objs.append(o_temp) 87 | 88 | log_call(['gcc', '-o', options.output] + objs + LINKER_SYM_FLAG + 89 | ['-rdynamic', 90 | '-L'+runtime_libdir, '-l'+RUNTIME_LIB, '-lm']) 91 | 92 | if __name__ == '__main__': 93 | sys.exit(Main(sys.argv[1:])) 94 | -------------------------------------------------------------------------------- /src/waot_visitor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 WebAssembly Community Group participants 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ast_visitor.h" 18 | #include "wasm.h" 19 | #include "wasm_ast.h" 20 | 21 | #include "llvm/IR/LLVMContext.h" 22 | #include "llvm/IR/IRBuilder.h" 23 | #include "llvm/IR/Module.h" 24 | #include "llvm/IR/Value.h" 25 | 26 | #include 27 | 28 | namespace llvm { 29 | class BasicBlock; 30 | class Function; 31 | class GlobalVariable; 32 | } 33 | 34 | class WAOTVisitor : public wasm::AstVisitor { 35 | public: 36 | WAOTVisitor(llvm::Module* llvm_module) 37 | : module_(llvm_module), 38 | ctx_(llvm_module->getContext()), 39 | initfini_fn_ty_(llvm::FunctionType::get( 40 | llvm::Type::getVoidTy(llvm_module->getContext()), 41 | {}, 42 | false)->getPointerTo()), 43 | irb_(llvm_module->getContext()) {} 44 | 45 | bool SetEntryExport(const wasm::Callable* func) { 46 | llvm::Constant* start = functions_.at(func); 47 | if (start->getType() != initfini_fn_ty_) { 48 | return false; 49 | } 50 | AddInitFunc(llvm::cast(start)); 51 | return true; 52 | } 53 | void FinishLLVMModule(); 54 | 55 | protected: 56 | llvm::Module* VisitModule(const wasm::Module& mod) override; 57 | void VisitImport(const wasm::Import& imp) override; 58 | void VisitExport(const wasm::Export& exp) override; 59 | void VisitFunction(const wasm::Function& func) override; 60 | void VisitSegment(const wasm::Segment& seg) override; 61 | 62 | llvm::Value* VisitNop(wasm::Expression* expr) override; 63 | llvm::Value* VisitBlock( 64 | wasm::Expression* expr, 65 | wasm::UniquePtrVector* exprs) override; 66 | llvm::Value* VisitIf(wasm::Expression* expr, 67 | wasm::Expression* condition, 68 | wasm::Expression* then, 69 | wasm::Expression* els) override; 70 | llvm::Value* VisitCall( 71 | wasm::CallExpression* expr, 72 | bool is_import, 73 | wasm::Callable* callee, 74 | int callee_index, 75 | wasm::UniquePtrVector* args) override; 76 | llvm::Value* VisitReturn( 77 | wasm::Expression* expr, 78 | wasm::UniquePtrVector* value) override; 79 | llvm::Value* VisitGetLocal(wasm::LocalExpression* expr, 80 | wasm::Variable* var) override; 81 | llvm::Value* VisitSetLocal(wasm::LocalExpression* expr, 82 | wasm::Variable* var, 83 | wasm::Expression* value) override; 84 | llvm::Value* VisitMemory(wasm::Expression* expr, 85 | wasm::MemoryOperator memop, 86 | wasm::MemType mem_type, 87 | uint32_t mem_alignment, 88 | uint64_t mem_offset, 89 | bool is_signed, 90 | wasm::Expression* address, 91 | wasm::Expression* store_val) override; 92 | llvm::Value* VisitConst(wasm::Expression* expr, wasm::Literal* l) override; 93 | llvm::Value* VisitUnop(wasm::Expression* expr, 94 | wasm::UnaryOperator unop, 95 | wasm::Expression* operand) override; 96 | llvm::Value* VisitBinop(wasm::Expression* epxr, 97 | wasm::BinaryOperator binop, 98 | wasm::Expression* lhs, 99 | wasm::Expression* rhs) override; 100 | llvm::Value* VisitCompare(wasm::Expression* expr, 101 | wasm::Type compare_type, 102 | wasm::CompareOperator relop, 103 | wasm::Expression* lhs, 104 | wasm::Expression* rhs) override; 105 | llvm::Value* VisitConversion(wasm::ConversionExpression* expr, 106 | wasm::ConversionOperator cvt, 107 | wasm::Expression* operand) override; 108 | 109 | llvm::Value* VisitInvoke(wasm::TestScriptExpr* expr, 110 | wasm::Export* callee, 111 | wasm::UniquePtrVector*) override; 112 | llvm::Value* VisitAssertReturn( 113 | wasm::TestScriptExpr* expr, 114 | wasm::TestScriptExpr* arg, 115 | wasm::UniquePtrVector* expected) override; 116 | llvm::Value* VisitAssertReturnNaN(wasm::TestScriptExpr* expr, 117 | wasm::TestScriptExpr* arg) override; 118 | llvm::Value* VisitAssertTrap(wasm::TestScriptExpr* expr, 119 | wasm::TestScriptExpr* invoke, 120 | const std::string& text) override; 121 | 122 | private: 123 | llvm::Function* GetFunction(const wasm::Callable& func, 124 | llvm::Function::LinkageTypes linkage); 125 | llvm::Type* getLLVMType(wasm::Type ty); 126 | llvm::Function* CreateModuleConstructor(const wasm::Module& mod); 127 | llvm::Function* CreateModuleDestructor(const wasm::Module& mod); 128 | llvm::Constant* getAssertFailFunc(wasm::Type ty); 129 | llvm::Value* VisitIDiv(llvm::Instruction::BinaryOps opcode, 130 | llvm::Value* lhs, 131 | llvm::Value* rhs); 132 | void TrapIfNaN(llvm::Value* operand); 133 | void ToIntRangeCheck(llvm::Value* operand, 134 | llvm::Type* dest_type, 135 | bool is_signed); 136 | llvm::Constant* GetBinaryOpCallee(wasm::Type wasm_ty, 137 | wasm::BinaryOperator binop); 138 | llvm::Value* VisitCallBinop(wasm::Type wasm_ty, 139 | wasm::BinaryOperator binop, 140 | llvm::Value* lhs, 141 | llvm::Value* rhs, 142 | llvm::IRBuilder<>* current_irb); 143 | void AddInitFunc(llvm::Function* f) { 144 | init_funcs_.push_back(llvm::ConstantExpr::getBitCast( 145 | llvm::cast(f), initfini_fn_ty_)); 146 | } 147 | void AddFiniFunc(llvm::Function* f) { 148 | fini_funcs_.push_back(llvm::ConstantExpr::getBitCast( 149 | llvm::cast(f), initfini_fn_ty_)); 150 | } 151 | 152 | llvm::Module* module_ = nullptr; 153 | llvm::LLVMContext& ctx_; 154 | const wasm::Module* current_wasm_module_ = nullptr; 155 | // NULL-terminated list of module constructor and assert functions. 156 | // TODO: the ini list mechanism currently doesn't handle linking multiple 157 | // object files together. 158 | llvm::Type* initfini_fn_ty_; 159 | std::vector init_funcs_; 160 | std::vector fini_funcs_; 161 | 162 | std::unordered_map functions_; 163 | llvm::Function* current_func_ = nullptr; 164 | std::vector current_locals_; 165 | std::unordered_map 166 | segment_initializers_; 167 | llvm::IRBuilder<> irb_; 168 | }; 169 | -------------------------------------------------------------------------------- /src/wart_trap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 WebAssembly Community Group participants 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef TRAP_H 18 | #define TRAP_H 19 | 20 | /* This file is included from C runtime code and C++ compiler code. */ 21 | #ifdef __cplusplus 22 | namespace wart { 23 | #endif 24 | 25 | enum TrapType { 26 | // Reserve value 0 so this can be returned from setjmp 27 | kIntegerOverflow = 1, 28 | kIntegerDivideByZero, 29 | kInvalidConversionToInteger, 30 | kMemoryBounds, 31 | kMemorySizeOverflow, 32 | kInvalidArgument, 33 | // Internal errors 34 | kOutOfMemory, 35 | kUnknownInternalError, 36 | }; 37 | 38 | void __wasm_trap(enum TrapType trap_type); 39 | void __wasm_report_error(char* fmt, ...); 40 | 41 | #ifdef __cplusplus 42 | } // namespace wart 43 | #endif 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/wasm_ast.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 WebAssembly Community Group participants 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "wasm_ast.h" 18 | 19 | namespace wasm { 20 | 21 | // There's really no need for this to be in a separate cc file, other 22 | // that to avoid removing wasm_ast.cc altogether. If it turns out that 23 | // it's unneeded once we are doing real codegen, then it can go away. 24 | 25 | std::string Segment::as_string() const { 26 | return std::string(initial_data.begin(), initial_data.end()); 27 | } 28 | 29 | } // namespace wasm 30 | -------------------------------------------------------------------------------- /src/wasm_ast.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 WebAssembly Community Group participants 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef WASM_AST_H 18 | #define WASM_AST_H 19 | 20 | #include "wasm.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace wasm { 28 | 29 | class Module; 30 | template 31 | using UniquePtrVector = std::vector>; 32 | 33 | static_assert(WASM_TYPE_ALL == 15, "wasm::Type enum needs to be adjusted"); 34 | // Type is basically just an enum, but implicitly convertible from WasmType. 35 | // It also includes an "unknown" state used during construction/type fixup. 36 | class Type { 37 | public: 38 | typedef enum Type_ { 39 | kVoid = WASM_TYPE_VOID, 40 | kI32 = WASM_TYPE_I32, 41 | kI64 = WASM_TYPE_I64, 42 | kF32 = WASM_TYPE_F32, 43 | kF64 = WASM_TYPE_F64, 44 | kAny = WASM_TYPE_ALL, 45 | kUnknown, 46 | } Type_; 47 | Type(Type_ t) : value_(t) {} 48 | Type(WasmType t) : value_(static_cast(t)) { 49 | assert(t <= WASM_TYPE_ALL && "Bad Type initializer"); 50 | } 51 | operator Type_() const { return value_; } 52 | explicit operator WasmType() const { return static_cast(value_); } 53 | 54 | private: 55 | Type_ value_ = kUnknown; 56 | }; 57 | 58 | class MemType { 59 | public: 60 | typedef enum Type_ { 61 | // TODO: remove this type distinction and just go with size instead? 62 | kI8, 63 | kI16, 64 | kI32, 65 | kI64, 66 | kF32, 67 | kF64, 68 | kUnknown, 69 | } Type_; 70 | MemType(Type_ t) : value_(t) {} 71 | operator Type_() const { return value_; } 72 | // Compare against a value type. 73 | bool operator==(const Type& other) const { 74 | return (value_ == kI32 && other == Type::kI32) || 75 | (value_ == kI64 && other == Type::kI64) || 76 | (value_ == kF32 && other == Type::kF32) || 77 | (value_ == kF64 && other == Type::kF64); 78 | } 79 | bool IsFloatTy() const { return value_ == kF32 || value_ == kF64; } 80 | unsigned GetSizeInBits() const { 81 | static_assert(kI8 == 0, "wrong value for kI8"); 82 | static_assert(kI16 == 1, "wrong value for kI16"); 83 | static_assert(kI32 == 2, "wrong value for kI32"); 84 | static_assert(kI64 == 3, "wrong value for kI64"); 85 | static_assert(kF32 == 4, "wrong value for kF32"); 86 | static_assert(kF64 == 5, "wrong value for kF64"); 87 | return value_ <= kI64 ? 8 << value_ : 32 << (value_ - kF32); 88 | }; 89 | 90 | private: 91 | Type_ value_ = kUnknown; 92 | }; 93 | 94 | // TODO: do we need a hierarchy of operators like the spec? 95 | enum UnaryOperator { 96 | // Int 97 | kClz, 98 | kCtz, 99 | kPopcnt, 100 | // FP 101 | kNeg, 102 | kAbs, 103 | kCeil, 104 | kFloor, 105 | kTrunc, 106 | kNearest, 107 | kSqrt, 108 | }; 109 | 110 | enum BinaryOperator { 111 | kAdd, 112 | kSub, 113 | kMul, 114 | // Int 115 | kDivS, 116 | kDivU, 117 | kRemS, 118 | kRemU, 119 | kAnd, 120 | kOr, 121 | kXor, 122 | kShl, 123 | kShrU, 124 | kShrS, 125 | // FP 126 | kDiv, 127 | kCopySign, 128 | kMin, 129 | kMax, 130 | }; 131 | 132 | enum CompareOperator { 133 | kEq, 134 | kNE, 135 | // Int 136 | kLtS, 137 | kLtU, 138 | kLeS, 139 | kLeU, 140 | kGtS, 141 | kGtU, 142 | kGeS, 143 | kGeU, 144 | // FP 145 | kLt, 146 | kLe, 147 | kGt, 148 | kGe, 149 | }; 150 | 151 | enum ConversionOperator { 152 | // Int 153 | kExtendSInt32, 154 | kExtendUInt32, 155 | kWrapInt64, 156 | kTruncSFloat32, 157 | kTruncUFloat32, 158 | kTruncSFloat64, 159 | kTruncUFloat64, 160 | kReinterpretFloat, 161 | // FP 162 | kConvertSInt32, 163 | kConvertUInt32, 164 | kConvertSInt64, 165 | kConvertUInt64, 166 | kPromoteFloat32, 167 | kDemoteFloat64, 168 | kReinterpretInt, 169 | }; 170 | 171 | enum MemoryOperator { 172 | kLoad, 173 | kStore, 174 | }; 175 | 176 | class Literal { 177 | public: 178 | Type type = Type::kUnknown; 179 | union { 180 | uint32_t i32; 181 | uint64_t i64; 182 | float f32; 183 | double f64; 184 | } value; 185 | }; 186 | 187 | class Variable { 188 | public: 189 | Variable(Type t) : type(t) {} 190 | Type type = Type::kUnknown; 191 | int index; 192 | std::string local_name; // Empty if none bound 193 | }; 194 | 195 | class Callable { 196 | public: 197 | Callable(Type t, const WasmStringSlice& name) 198 | : result_type(t), local_name(name.start, name.length) {} 199 | Type result_type = Type::kVoid; 200 | std::string local_name; // Empty if none bound 201 | UniquePtrVector locals; // Includes the args at the front 202 | std::vector args; // Convenience pointers to the args 203 | }; 204 | 205 | class Expression { 206 | public: 207 | // ExpressionKind is used instead of RTTI. Only AstVisitor::VisitExpression 208 | // actually 209 | // needs to care about this field currently. 210 | enum ExpressionKind { 211 | kNop, 212 | kBlock, 213 | kIf, 214 | kIfElse, 215 | kCallDirect, 216 | kReturn, 217 | kGetLocal, 218 | kSetLocal, 219 | kConst, 220 | kUnary, 221 | kBinary, 222 | kCompare, 223 | kConvert, 224 | kMemory, 225 | }; 226 | Expression(ExpressionKind k, Type ty, Type expected_ty) 227 | : kind(k), expr_type(ty), expected_type(expected_ty) {} 228 | ExpressionKind kind; 229 | Type expr_type = Type::kUnknown; 230 | Type expected_type = Type::kUnknown; 231 | // Operands 232 | UniquePtrVector exprs; 233 | }; 234 | 235 | class ConstantExpression final : public Expression { 236 | public: 237 | ConstantExpression(Type ty, Type expected_ty) 238 | : Expression(kConst, ty, expected_ty) { 239 | literal.type = ty; 240 | }; 241 | Literal literal; 242 | }; 243 | 244 | class CallExpression final : public Expression { 245 | public: 246 | CallExpression(int idx, bool imp, Callable* c, Type expected_ty) 247 | : Expression(kCallDirect, c->result_type, expected_ty), 248 | callee_index(idx), 249 | is_import(imp), 250 | callee(c) {}; 251 | int callee_index; 252 | bool is_import; 253 | Callable* callee; 254 | }; 255 | 256 | class LocalExpression final : public Expression { 257 | public: 258 | // TODO: After updating to AST parser, consider standardizing on factories 259 | // everywhere, or constructors everywhere. 260 | static LocalExpression* GetGetLocal(Variable* v, Type expected_ty) { 261 | return new LocalExpression(kGetLocal, v, expected_ty); 262 | } 263 | static LocalExpression* GetSetLocal(Variable* v, Type expected_ty) { 264 | return new LocalExpression(kSetLocal, v, expected_ty); 265 | } 266 | Variable* local_var; 267 | 268 | private: 269 | LocalExpression(ExpressionKind k, Variable* v, Type expected_ty) 270 | : Expression(k, v->type, expected_ty), local_var(v) {}; 271 | }; 272 | 273 | class MemoryExpression final : public Expression { 274 | public: 275 | MemoryExpression(MemoryOperator op, 276 | Type ty, 277 | Type expected_ty, 278 | MemType mem_ty, 279 | uint32_t align, 280 | uint64_t off, 281 | bool sign) 282 | : Expression(kMemory, ty, expected_ty), 283 | memop(op), 284 | mem_type(mem_ty), 285 | alignment(align), 286 | offset(off), 287 | is_signed(sign) {}; 288 | MemoryOperator memop; 289 | MemType mem_type = MemType::kUnknown; 290 | uint32_t alignment; 291 | uint64_t offset; 292 | bool is_signed; 293 | }; 294 | 295 | class UnaryExpression final : public Expression { 296 | public: 297 | UnaryExpression(UnaryOperator op, Type ty, Type expected_ty) 298 | : Expression(kUnary, ty, expected_ty), unop(op) {}; 299 | UnaryOperator unop; 300 | }; 301 | 302 | class BinaryExpression final : public Expression { 303 | public: 304 | BinaryExpression(BinaryOperator op, Type ty, Type expected_ty) 305 | : Expression(kBinary, ty, expected_ty), binop(op) {}; 306 | BinaryOperator binop; 307 | }; 308 | 309 | class CompareExpression final : public Expression { 310 | public: 311 | CompareExpression(Type ct, CompareOperator op, Type expected_ty) 312 | : Expression(kCompare, Type::kI32, expected_ty), 313 | compare_type(ct), 314 | relop(op) {}; 315 | Type compare_type = Type::kUnknown; 316 | CompareOperator relop; 317 | }; 318 | 319 | class ConversionExpression final : public Expression { 320 | public: 321 | ConversionExpression(ConversionOperator op, 322 | Type op_ty, 323 | Type result_ty, 324 | Type expected_ty) 325 | : Expression(kConvert, result_ty, expected_ty), 326 | cvt(op), 327 | operand_type(op_ty) {}; 328 | ConversionOperator cvt; 329 | // Technically redundant with cvt, but handy to have. 330 | Type operand_type = Type::kUnknown; 331 | }; 332 | 333 | class Function : public Callable { 334 | public: 335 | Function(Type t, const WasmStringSlice& name, int idx) 336 | : Callable(t, name), index_in_module(idx) {} 337 | UniquePtrVector body; 338 | int index_in_module = 0; 339 | }; 340 | 341 | class Export { 342 | public: 343 | Export(Function* f, const WasmStringSlice& n, Module* m) 344 | : function(f), name(n.start, n.length), module(m) {} 345 | Function* function; 346 | std::string name; 347 | Module* module; 348 | }; 349 | 350 | class Import : public Callable { 351 | public: 352 | Import(Type t, 353 | const WasmStringSlice& name, 354 | const WasmStringSlice& m, 355 | const WasmStringSlice& f) 356 | : Callable(t, name), 357 | module_name(m.start, m.length), 358 | func_name(f.start, f.length) {} 359 | std::string module_name; 360 | std::string func_name; 361 | }; 362 | 363 | class Segment { 364 | public: 365 | Segment(size_t sz, size_t addr) : size(sz), address(addr) {} 366 | std::string as_string() const; 367 | size_t size = 0; 368 | size_t address = 0; 369 | std::vector initial_data; 370 | }; 371 | 372 | class Module { 373 | public: 374 | UniquePtrVector segments; 375 | UniquePtrVector functions; 376 | UniquePtrVector exports; 377 | UniquePtrVector imports; 378 | uint32_t initial_memory_size = 0; 379 | uint32_t max_memory_size = 0; 380 | std::string name; 381 | }; 382 | 383 | class SourceLocation { 384 | public: 385 | SourceLocation(const WasmLocation& loc) 386 | : filename(loc.filename), line(loc.first_line), col(loc.first_column) {} 387 | // For now there's only ever one file per run, but it's simpler to keep the 388 | // filename here. If there gets to be SourceLocs for every expr, we probably 389 | // want to dedup the filename info. 390 | std::string filename = ""; 391 | int line = 0; 392 | int col = 0; 393 | // TODO: add last line/col 394 | }; 395 | 396 | // For spec repo test scripts. The spec test script operations are basically 397 | // expressions but have no wasm opcodes and they exist outside modules, so at 398 | // the top level they have their own classes, which contain Expressions. Each 399 | // script expression refers to the module that immediately preceeds it in the 400 | // file (the parser checks this and sets up the mapping when creating the AST). 401 | // TODO: make this a hierarchy like exprs. 402 | class TestScriptExpr { 403 | public: 404 | typedef enum { 405 | kAssertInvalid, 406 | kInvoke, 407 | kAssertReturn, 408 | kAssertReturnNaN, 409 | kAssertTrap 410 | } Opcode; 411 | TestScriptExpr(Module* mod, Opcode op, const WasmLocation loc) 412 | : module(mod), opcode(op), source_loc(loc), type(Type::kUnknown) {} 413 | 414 | Module* module; 415 | Opcode opcode; 416 | SourceLocation source_loc; 417 | Export* callee; // Invoke 418 | Type type; // AssertReturn, Invoke 419 | std::unique_ptr invoke; // AssertReturn, AssertTrap 420 | std::string trap_text; // AssertTrap 421 | UniquePtrVector exprs; // Invoke args, AR expectation 422 | }; 423 | 424 | } // namespace wasm 425 | #endif // WASM_AST_H 426 | -------------------------------------------------------------------------------- /src/wasm_parser_cxx.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 WebAssembly Community Group participants 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef WASM_CPP 18 | #define WASM_CPP 19 | 20 | #include 21 | 22 | #include "wasm.h" 23 | #include "wasm_ast.h" 24 | 25 | namespace wasm { 26 | 27 | class Parser { 28 | public: 29 | Parser(const std::string& filename, bool desugar) 30 | : filename_(filename), desugar_(desugar) { 31 | (void)(desugar_); 32 | } 33 | int Parse(bool spec_script_mode) { 34 | scanner_ = wasm_new_scanner(filename_.c_str()); 35 | if (!scanner_) 36 | return 1; 37 | int result = wasm_parse(scanner_, &parser_); 38 | result = result || parser_.errors; 39 | wasm_free_scanner(scanner_); 40 | WasmScript* script = &parser_.script; 41 | if (result != WASM_OK) { 42 | wasm_destroy_script(script); 43 | return result; 44 | } 45 | 46 | result = wasm_check_script(script); 47 | if (result != WASM_OK) { 48 | wasm_destroy_script(script); 49 | return result; 50 | } 51 | 52 | result = result || ConvertAST(*script, spec_script_mode); 53 | return result; 54 | } 55 | 56 | UniquePtrVector modules; 57 | UniquePtrVector test_script; 58 | 59 | private: 60 | typedef void (*ErrorCallback)(const char*); 61 | static void DefaultErrorCallback(const char* message) { 62 | fputs("Error: ", stderr); 63 | fputs(message, stderr); 64 | } 65 | int ConvertAST(const WasmScript& script, bool spec_script_mode); 66 | Module* ConvertModule(WasmModule* in_mod); 67 | void ConvertExprArg(WasmExpr* in_expr, 68 | Expression* out_expr, 69 | Type expected_type); 70 | void ConvertBlockArgs(const WasmExprPtrVector& in_vec, 71 | UniquePtrVector* out_vec, 72 | Type expected_type); 73 | Expression* ConvertExpression(WasmExpr* in_expr, Type expected_type); 74 | TestScriptExpr* ConvertInvoke(const WasmCommandInvoke& invoke); 75 | TestScriptExpr* ConvertTestScriptExpr(WasmCommand* command); 76 | 77 | ErrorCallback error_callback_ = DefaultErrorCallback; 78 | WasmScanner scanner_; 79 | WasmParser parser_ = {}; 80 | 81 | std::string filename_; 82 | bool desugar_; 83 | Module* out_module_ = nullptr; 84 | Function* out_func_ = nullptr; 85 | WasmModule* in_module_ = nullptr; 86 | WasmFunc* in_func_ = nullptr; 87 | std::unordered_map exports_map_; 88 | }; 89 | 90 | } // namespace wasm 91 | #endif // WASM_CPP 92 | -------------------------------------------------------------------------------- /src/wat.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 WebAssembly Community Group participants 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ast_dumper.h" 18 | #include "waot_visitor.h" 19 | #include "wasm.h" 20 | #include "wasm_parser_cxx.h" 21 | 22 | #include "llvm/ADT/STLExtras.h" 23 | #include "llvm/IR/IRBuilder.h" 24 | #include "llvm/IR/IRPrintingPasses.h" 25 | #include "llvm/IR/LLVMContext.h" 26 | #include "llvm/IR/PassManager.h" 27 | #include "llvm/IR/Verifier.h" 28 | #include "llvm/Support/CommandLine.h" 29 | #include "llvm/Support/FileSystem.h" 30 | #include "llvm/Support/ManagedStatic.h" 31 | #include "llvm/Support/MemoryBuffer.h" 32 | #include "llvm/Support/Path.h" 33 | #include "llvm/Support/PrettyStackTrace.h" 34 | #include "llvm/Support/raw_ostream.h" 35 | #include "llvm/Support/Signals.h" 36 | #include "llvm/Support/ToolOutputFile.h" 37 | 38 | using llvm::errs; 39 | 40 | static llvm::cl::opt g_input_filename( 41 | llvm::cl::Positional, 42 | llvm::cl::desc("input sexpr file"), 43 | llvm::cl::init("-")); 44 | 45 | static llvm::cl::opt g_output_filename( 46 | "o", 47 | llvm::cl::desc("output LLVM file"), 48 | llvm::cl::value_desc("filename")); 49 | 50 | static llvm::cl::opt g_dump_input( 51 | "i", 52 | llvm::cl::desc("Dump input as well as output"), 53 | llvm::cl::init(false)); 54 | 55 | static llvm::cl::opt g_dump_ast( 56 | "a", 57 | llvm::cl::desc("Dump AST as well as output"), 58 | llvm::cl::init(false)); 59 | 60 | static llvm::cl::opt g_print_asm( 61 | "S", 62 | llvm::cl::desc("Print LLVM assembly output"), 63 | llvm::cl::init(true)); 64 | 65 | static llvm::cl::opt g_spec_test_script_mode( 66 | "spec-test-script", 67 | llvm::cl::desc( 68 | "Run in spec test script mode (allow multiple modules per file and" 69 | "test assertions"), 70 | llvm::cl::init(false)); 71 | 72 | static llvm::cl::opt g_disable_verify( 73 | "disable-verify", 74 | llvm::cl::desc("Disable the module verifier"), 75 | llvm::cl::init(false)); 76 | 77 | int main(int argc, char** argv) { 78 | llvm::sys::PrintStackTraceOnErrorSignal(); 79 | llvm::PrettyStackTraceProgram X(argc, argv); 80 | llvm::llvm_shutdown_obj Shutdown; // Call llvm_shutdown() on exit. 81 | llvm::cl::ParseCommandLineOptions(argc, argv, "wasm IR dumper\n"); 82 | 83 | // Get the input buffer. 84 | llvm::ErrorOr > ErrorOrBuffer = 85 | llvm::MemoryBuffer::getFileOrSTDIN(g_input_filename); 86 | 87 | if (ErrorOrBuffer.getError()) { 88 | errs() << "unable to read " << g_input_filename << "\n"; 89 | return 1; 90 | } 91 | auto& Buffer = ErrorOrBuffer.get(); 92 | 93 | // Open the output file. Default to standard output. 94 | if (g_output_filename.empty()) 95 | g_output_filename = "-"; 96 | 97 | std::error_code EC; 98 | auto output = llvm::make_unique( 99 | g_output_filename, EC, llvm::sys::fs::F_None); 100 | if (EC) { 101 | errs() << EC.message() << '\n'; 102 | return 1; 103 | } 104 | 105 | if (g_dump_input) { 106 | errs() << "INPUT:\n"; 107 | errs() << Buffer->getBuffer(); 108 | errs() << "OUTPUT:\n"; 109 | } 110 | wasm::Parser parser(g_input_filename.c_str(), false); 111 | if (parser.Parse(g_spec_test_script_mode)) { 112 | return 1; 113 | } 114 | 115 | llvm::LLVMContext& context = llvm::getGlobalContext(); 116 | llvm::ModulePassManager mpm{}; 117 | assert(g_print_asm); // For now, only support printing assembly. 118 | if (!g_disable_verify) 119 | mpm.addPass(llvm::VerifierPass()); 120 | mpm.addPass(llvm::PrintModulePass(output->os())); 121 | 122 | parser.modules.front()->name = llvm::sys::path::stem(g_input_filename); 123 | auto llvm_module = 124 | llvm::make_unique(parser.modules.front()->name, context); 125 | 126 | int i = 1; 127 | for (auto& mod : parser.modules) { 128 | // TODO: switch this to name based on line number 129 | std::string name("module"); 130 | llvm::raw_string_ostream modname(name); 131 | modname << i; 132 | modname.flush(); 133 | if (mod->name.empty()) 134 | mod->name = name; 135 | } 136 | WAOTVisitor converter(llvm_module.get()); 137 | wasm::AstDumper dumper(true); 138 | for (auto& module : parser.modules) { 139 | if (g_dump_ast) 140 | dumper.Visit(*module); 141 | converter.Visit(*module); 142 | } 143 | 144 | if (g_spec_test_script_mode) { 145 | for (auto& script_expr : parser.test_script) { 146 | if (g_dump_ast) 147 | dumper.Visit(script_expr.get()); 148 | converter.Visit(script_expr.get()); 149 | } 150 | } else { 151 | // If there's an export called "_start", add it to the end of the ini list. 152 | if (parser.modules.size() != 1) { 153 | fprintf(stderr, "error: only 1 module allowed per file\n"); 154 | exit(1); 155 | } 156 | for (auto& exp : parser.modules.front()->exports) { 157 | if (exp->name == "_start") { 158 | if (!converter.SetEntryExport(exp->function)) { 159 | fprintf(stderr, "error: _start export is not of type void ()*\n"); 160 | exit(1); 161 | } 162 | } 163 | } 164 | } 165 | converter.FinishLLVMModule(); 166 | 167 | mpm.run(*llvm_module); 168 | output->keep(); 169 | 170 | return 0; 171 | } 172 | -------------------------------------------------------------------------------- /test/codegen/assertreturn.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -spec-test-script -S %s | FileCheck %s 2 | ;; Check that the -spec-test-script flag is required to accept this file. 3 | ;; RUN: not wat -S %s 4 | 5 | ;; CHECK: @__wasm_init_array = appending global [7 x void ()*] [void ()* @.assertreturn_ctor, void ()* @AssertReturn_19, void ()* @AssertReturn_31, void ()* @AssertReturn_42, void ()* @AssertTrap_47, void ()* @AssertTrap_48, void ()* null] 6 | ;; CHECK: @__wasm_fini_array = appending global [2 x void ()*] [void ()* @.assertreturn_dtor, void ()* null] 7 | 8 | (module 9 | (func $foo (result i32) (i32.const 0)) 10 | ;; CHECK: define internal i32 @"$foo" 11 | (export "foo" $foo) 12 | (func $bar (param f32) (result f32) (f32.const 1.0)) 13 | ;; CHECK: define internal float @"$bar" 14 | (export "bar" $bar) 15 | (func $baz (i32.const 0)) 16 | (export "baz" $baz) 17 | ) 18 | 19 | (assert_return (invoke "foo") (i32.const 0)) 20 | ;; CHECK: define void @AssertReturn_19() 21 | ;; CHECK: call i32 @Invoke_19 22 | ;; CHECK: %assert_check = icmp eq i32 %0, 0 23 | ;; CHECK: br i1 %assert_check, label %AssertSuccess, label %AssertFail 24 | ;; CHECK: AssertSuccess: 25 | ;; CHECK: ret void 26 | ;; CHECK: AssertFail: 27 | ;; CHECK: call void @__wasm_assert_fail_i32(i32 19, i32 0, i32 %0) 28 | ;; CHECK: define i32 @Invoke 29 | ;; CHECK: call i32 @"$foo"() 30 | 31 | (assert_return (invoke "bar" (f32.const 0)) (f32.const 0)) 32 | ;; CHECK: define void @AssertReturn_31() 33 | ;; CHECK: call float @Invoke 34 | ;; CHECK: bitcast float %0 to i32 35 | ;; CHECK: icmp eq i32 %invoke_result_int, 0 36 | ;; CHECK: AssertFail: 37 | ;; CHECK: call void @__wasm_assert_fail_f32(i32 31, float 0.000000e+00, float %0) 38 | ;; CHECK: define float @Invoke 39 | ;; CHECK: call float @"$bar"(float 0.000000e+00 40 | 41 | 42 | (assert_return (invoke "baz")) 43 | ;; CHECK: define void @AssertReturn_42() 44 | ;; CHECK: call void @Invoke_42() 45 | ;; CHECK: ret void 46 | 47 | (assert_trap (invoke "foo") "foo") 48 | (assert_trap (invoke "bar" (f32.const 1)) "bar") 49 | -------------------------------------------------------------------------------- /test/codegen/assertreturnnan.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -spec-test-script -S %s | FileCheck %s 2 | ;; Check that the -spec-test-script flag is required to accept this file. 3 | ;; RUN: not wat -S %s 4 | 5 | (module 6 | (func $add (param $x f32) (param $y f32) (result f32) (f32.add (get_local $x) (get_local $y))) 7 | (export "add" $add) 8 | (func $ret_f64 (result f64) (f64.const 0.0)) 9 | (export "ret_f64" $ret_f64) 10 | ) 11 | 12 | (assert_return_nan (invoke "add" (f32.const -0x0p+0) (f32.const -nan))) 13 | ;; CHECK: define void @AssertReturnNaN_12() 14 | ;; CHECK: %0 = call float @Invoke_12 15 | ;; CHECK: call void @__wasm_assert_return_nan_f32(i32 12, float %0) 16 | 17 | (assert_return_nan (invoke "ret_f64" )) 18 | ;; CHECK: define void @AssertReturnNaN_17() 19 | ;; CHECK: %0 = call double @Invoke_17 20 | ;; CHECK: call void @__wasm_assert_return_nan_f64(i32 17, double %0) 21 | -------------------------------------------------------------------------------- /test/codegen/asserttrap.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -spec-test-script -S %s | FileCheck %s 2 | ;; Check that the -spec-test-script flag is required to accept this file. 3 | ;; RUN: not wat -S %s 4 | (module 5 | (func $foo (result i32) (i32.const 0)) 6 | ;; CHECK: define internal i32 @"$foo" 7 | (export "foo" $foo) 8 | (func $bar (param f32) (result f32) (f32.const 1.0)) 9 | ;; CHECK: define internal float @"$bar" 10 | (export "bar" $bar) 11 | (func $voidret (param f64) (f64.const 1.0)) 12 | ;; CHECK: define internal void @"$voidret" 13 | (export "voidret" $voidret) 14 | ) 15 | 16 | 17 | 18 | (assert_trap (invoke "foo") "foo") 19 | ;; CHECK: define void @AssertTrap_18() 20 | ;; CHECK: call void @__wasm_assert_trap(i32 18, void ()* @Invoke 21 | ;; @Invoke should have void return instead of i32 and no args 22 | ;; CHECK: define void @Invoke_18() 23 | (assert_trap (invoke "bar" (f32.const 1)) "bar") 24 | ;; CHECK: define void @AssertTrap_23() 25 | ;; CHECK: call void @__wasm_assert_trap(i32 23, void ()* @Invoke_23 26 | ;; @Invoke should have void return and no args 27 | ;; CHECK: define void @Invoke_23() 28 | -------------------------------------------------------------------------------- /test/codegen/binary.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -S %s | FileCheck %s 2 | (module 3 | (func 4 | (local i32) (local i32) 5 | (local i64) (local i64) 6 | (local f32) (local f32) 7 | (local f64) (local f64) 8 | (i32.add (get_local 0) (get_local 1)) 9 | ;; CHECK: add i32 %get_local, %get_local8 10 | (i64.add (get_local 2) (get_local 3)) 11 | ;; CHECK: add i64 %get_local9, %get_local10 12 | (f32.add (get_local 4) (get_local 5)) 13 | ;; CHECK: fadd float %get_local11, %get_local12 14 | (f64.add (get_local 6) (get_local 7)) 15 | ;; CHECK: fadd double %get_local13, %get_local14 16 | (i32.sub (get_local 0) (get_local 1)) 17 | ;; CHECK: sub i32 18 | (i64.sub (get_local 2) (get_local 3)) 19 | ;; CHECK: sub i64 20 | (f32.sub (get_local 4) (get_local 5)) 21 | ;; CHECK: fsub float 22 | (f64.sub (get_local 6) (get_local 7)) 23 | ;; CHECK: fsub double 24 | (i32.mul (get_local 0) (get_local 1)) 25 | ;; CHECK: mul i32 26 | (i64.mul (get_local 2) (get_local 3)) 27 | ;; CHECK: mul i64 28 | (f32.mul (get_local 4) (get_local 5)) 29 | ;; CHECK: fmul float 30 | (f64.mul (get_local 6) (get_local 7)) 31 | ;; CHECK: fmul double 32 | (i32.div_s (get_local 0) (get_local 1)) 33 | ;; CHECK: sdiv i32 34 | (i64.div_s (get_local 2) (get_local 3)) 35 | ;; CHECK: sdiv i64 36 | (i32.div_u (get_local 0) (get_local 1)) 37 | ;; CHECK: udiv i32 38 | (i64.div_u (get_local 2) (get_local 3)) 39 | ;; CHECK: udiv i64 40 | (f32.div (get_local 4) (get_local 5)) 41 | ;; CHECK: fdiv float 42 | (f64.div (get_local 6) (get_local 7)) 43 | ;; CHECK: fdiv double 44 | (i32.rem_s (get_local 0) (get_local 1)) 45 | ;; CHECK: srem i32 46 | (i64.rem_s (get_local 2) (get_local 3)) 47 | ;; CHECK: srem i64 48 | (i32.rem_u (get_local 0) (get_local 1)) 49 | ;; CHECK: urem i32 50 | (i64.rem_u (get_local 2) (get_local 3)) 51 | ;; CHECK: urem i64 52 | (f32.min (get_local 4) (get_local 5)) 53 | ;; CHECK: call float @__wasm_float_min_f32(float %get_local{{.*}}, float %get_local{{.*}}) 54 | (f64.min (get_local 6) (get_local 7)) 55 | ;; CHECK: call double @__wasm_float_min_f64(double %get_local{{.*}}, double %get_local{{.*}}) 56 | (f32.max (get_local 4) (get_local 5)) 57 | ;; CHECK: call float @__wasm_float_max_f32(float %get_local{{.*}}, float %get_local{{.*}}) 58 | (f64.max (get_local 6) (get_local 7)) 59 | ;; CHECK: call double @__wasm_float_max_f64(double %get_local{{.*}}, double %get_local{{.*}}) 60 | (i32.and (get_local 0) (get_local 1)) 61 | ;; CHECK: and i32 62 | (i64.and (get_local 2) (get_local 3)) 63 | ;; CHECK: and i64 64 | (i32.or (get_local 0) (get_local 1)) 65 | ;; CHECK: or i32 66 | (i64.or (get_local 2) (get_local 3)) 67 | ;; CHECK: or i64 68 | (i32.xor (get_local 0) (get_local 1)) 69 | ;; CHECK: xor i32 70 | (i64.xor (get_local 2) (get_local 3)) 71 | ;; CHECK: xor i64 72 | (i32.shl (get_local 0) (get_local 1)) 73 | ;; CHECK: %shamt_check = icmp uge i32 %get_local{{.*}}, 32 74 | ;; CHECK: %shift_result = shl i32 %get_local{{.*}}, %get_local 75 | ;; CHECK: select i1 %shamt_check, i32 0, i32 %shift_result 76 | (i64.shl (get_local 2) (get_local 3)) 77 | ;; CHECK: %shamt_check{{.*}} = icmp uge i64 %get_local{{.*}}, 64 78 | ;; CHECK: %shift_result{{.*}} = shl i64 %get_local{{.*}}, %get_local 79 | ;; CHECK: select i1 %shamt_check{{.*}}, i64 0, i64 %shift_result 80 | (i32.shr_u (get_local 0) (get_local 1)) 81 | ;; CHECK: %shamt_check{{.*}} = icmp uge i32 %get_local{{.*}}, 32 82 | ;; CHECK: %shift_result{{.*}} = lshr i32 %get_local{{.*}}, %get_local 83 | ;; CHECK: select i1 %shamt_check{{.*}}, i32 0, i32 %shift_result 84 | (i64.shr_u (get_local 2) (get_local 3)) 85 | ;; CHECK: %shamt_check{{.*}} = icmp uge i64 %get_local{{.*}}, 64 86 | ;; CHECK: %shift_result{{.*}} = lshr i64 %get_local{{.*}}, %get_local 87 | ;; CHECK: select i1 %shamt_check{{.*}}, i64 0, i64 %shift_result 88 | (i32.shr_s (get_local 0) (get_local 1)) 89 | ;; CHECK: %shamt_check{{.*}} = icmp uge i32 %get_local{{.*}}, 32 90 | ;; CHECK: %shamt = select i1 %shamt_check{{.*}}, i32 31, i32 %get_local 91 | ;; CHECK: %shift_expr{{.*}} = ashr i32 %get_local{{.*}}, %shamt 92 | (i64.shr_s (get_local 2) (get_local 3)) 93 | ;; CHECK: %shamt_check{{.*}} = icmp uge i64 %get_local{{.*}}, 64 94 | ;; CHECK: %shamt{{.*}} = select i1 %shamt_check{{.*}}, i64 63, i64 %get_local 95 | ;; CHECK: %shift_expr{{.*}} = ashr i64 %get_local{{.*}}, %shamt 96 | (f32.copysign (get_local 4) (get_local 5)) 97 | ;; CHECK: call float @llvm.copysign.f32(float %get_local{{.*}}, float %get_local{{.*}}) 98 | (f64.copysign (get_local 6) (get_local 7))) 99 | ;; CHECK: call double @llvm.copysign.f64(double %get_local{{.*}}, double %get_local{{.*}}) 100 | ) 101 | -------------------------------------------------------------------------------- /test/codegen/call.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat %s | FileCheck %s 2 | (module 3 | (func (param i32) 4 | ;; CHECK: define internal void @0 5 | (call 0 (i32.const 1))) 6 | ;; CHECK: call void @0(i32 1) 7 | (func (param i64) (call 1 (i64.const 2))) 8 | ;; CHECK: define internal void @1 9 | ;; CHECK: call void @1(i64 2) 10 | (func $foo (param $p1 f32) (call $foo (f32.const 0.333))) 11 | ;; CHECK: define internal void @"$foo" 12 | ;; CHECK: call void @"$foo"(float 0x3FD54FDF40000000) 13 | (func $bar (param f32 f64 i64 i32 i64 i64) 14 | ;; CHECK: define internal void @"$bar" 15 | (call $bar (f32.const 0) 16 | ;; CHECK: call void @"$bar"( 17 | ;; CHECK: float 0.000000e+00, 18 | (f64.const 6.283185307179586) 19 | ;; CHECK: double 0x401921FB54442D18, 20 | (i64.const -9223372036854775808) 21 | ;; CHECK: i64 -9223372036854775808 22 | (i32.const -1) 23 | ;; CHECK: i32 -1 24 | (i64.const -2) 25 | ;; CHECK: i64 -2 26 | (i64.const 18446744073709551615) 27 | ;; CHECK: i64 -1 28 | ) 29 | ) 30 | ) -------------------------------------------------------------------------------- /test/codegen/cast.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -S %s | FileCheck %s 2 | (module 3 | (func (local i32)(local i64)(local f32)(local f64) 4 | (f32.reinterpret/i32 (get_local 0)) 5 | ;; CHECK: bitcast i32 %get_local to float 6 | (i32.reinterpret/f32 (get_local 2)) 7 | ;; CHECK: bitcast float %get_local{{.+}} to i32 8 | (f64.reinterpret/i64 (get_local 1)) 9 | ;; CHECK: bitcast i64 %get_local{{.*}} to double 10 | (i64.reinterpret/f64 (get_local 3)))) 11 | ;; CHECK: bitcast double %get_local{{.*}} to i64 12 | -------------------------------------------------------------------------------- /test/codegen/compare.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -S %s | FileCheck %s 2 | (module 3 | (func 4 | (local i32) (local i32) 5 | (local i64) (local i64) 6 | (local f32) (local f32) 7 | (local f64) (local f64) 8 | (i32.eq (get_local 0) (get_local 1)) 9 | ;; CHECK: icmp eq i32 %get_local, %get_local8 10 | (i64.eq (get_local 2) (get_local 3)) 11 | ;; CHECK: icmp eq i64 %get_local9, %get_local10 12 | (f32.eq (get_local 4) (get_local 5)) 13 | ;; CHECK: fcmp oeq float %get_local12, %get_local13 14 | (f64.eq (get_local 6) (get_local 7)) 15 | ;; CHECK: fcmp oeq double %get_local15, %get_local16 16 | (i32.ne (get_local 0) (get_local 1)) 17 | ;; CHECK: icmp ne i32 %get_local18, %get_local19 18 | (i64.ne (get_local 2) (get_local 3)) 19 | ;; CHECK: icmp ne i64 20 | (f32.ne (get_local 4) (get_local 5)) 21 | ;; CHECK: fcmp une float 22 | (f64.ne (get_local 6) (get_local 7)) 23 | ;; CHECK: fcmp une double 24 | (i32.lt_s (get_local 0) (get_local 1)) 25 | ;; CHECK: icmp slt i32 26 | (i64.lt_s (get_local 2) (get_local 3)) 27 | ;; CHECK: icmp slt i64 28 | (i32.lt_u (get_local 0) (get_local 1)) 29 | ;; CHECK: icmp ult i32 30 | (i64.lt_u (get_local 2) (get_local 3)) 31 | ;; CHECK: icmp ult i64 32 | (f32.lt (get_local 4) (get_local 5)) 33 | ;; CHECK: fcmp olt float 34 | (f64.lt (get_local 6) (get_local 7)) 35 | ;; CHECK: fcmp olt double 36 | (i32.le_s (get_local 0) (get_local 1)) 37 | ;; CHECK: icmp sle i32 38 | (i64.le_s (get_local 2) (get_local 3)) 39 | ;; CHECK: icmp sle i64 40 | (i32.le_u (get_local 0) (get_local 1)) 41 | ;; CHECK: icmp ule i32 42 | (i64.le_u (get_local 2) (get_local 3)) 43 | ;; CHECK: icmp ule i64 44 | (f32.le (get_local 4) (get_local 5)) 45 | ;; CHECK: fcmp ole float 46 | (f64.le (get_local 6) (get_local 7)) 47 | ;; CHECK: fcmp ole double 48 | (i32.gt_s (get_local 0) (get_local 1)) 49 | ;; CHECK: icmp sgt i32 50 | (i64.gt_s (get_local 2) (get_local 3)) 51 | ;; CHECK: icmp sgt i64 52 | (i32.gt_u (get_local 0) (get_local 1)) 53 | ;; CHECK: icmp ugt i32 54 | (i64.gt_u (get_local 2) (get_local 3)) 55 | ;; CHECK: icmp ugt i64 56 | (f32.gt (get_local 4) (get_local 5)) 57 | ;; CHECK: fcmp ogt float 58 | (f64.gt (get_local 6) (get_local 7)) 59 | ;; CHECK: fcmp ogt double 60 | (i32.ge_s (get_local 0) (get_local 1)) 61 | ;; CHECK: icmp sge i32 62 | (i64.ge_s (get_local 2) (get_local 3)) 63 | ;; CHECK: icmp sge i64 64 | (i32.ge_u (get_local 0) (get_local 1)) 65 | ;; CHECK: icmp uge i32 66 | (i64.ge_u (get_local 2) (get_local 3)) 67 | ;; CHECK: icmp uge i64 68 | (f32.ge (get_local 4) (get_local 5)) 69 | ;; CHECK: fcmp oge float 70 | (f64.ge (get_local 6) (get_local 7))) 71 | ;; CHECK: fcmp oge double 72 | (func (result i32) (return (i32.eq (i32.const 0) (i32.const 1)))) 73 | ;; CHECK: ret i32 0 74 | (func (param i32) (param i32) (result i32) 75 | (i32.eq (get_local 0) (get_local 1))) 76 | ;; CHECK: zext 77 | ;; CHECK: ret i32 78 | ) -------------------------------------------------------------------------------- /test/codegen/convert.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -S %s | FileCheck %s 2 | (module 3 | (func (local i32)(local i64)(local f32)(local f64) 4 | (i32.wrap/i64 (get_local 1)) 5 | ;; CHECK: trunc i64 %get_local to i32 6 | (i64.extend_u/i32 (get_local 0)) 7 | ;; CHECK: zext i32 %get_local{{.+}} to i64 8 | (i64.extend_s/i32 (get_local 0)) 9 | ;; CHECK: sext i32 %get_local{{.+}} to i64 10 | (i32.trunc_s/f32 (get_local 2)) 11 | ;; CHECK: fptosi float %get_local{{.+}} to i32 12 | (i32.trunc_u/f32 (get_local 2)) 13 | ;; CHECK: fptoui float %get_local{{.+}} to i32 14 | (i32.trunc_s/f64 (get_local 3)) 15 | ;; CHECK: fptosi double %get_local{{.+}} to i32 16 | (i32.trunc_u/f64 (get_local 3)) 17 | ;; CHECK: fptoui double %get_local{{.+}} to i32 18 | (i64.trunc_s/f32 (get_local 2)) 19 | ;; CHECK: fptosi float %get_local{{.+}} to i64 20 | (i64.trunc_u/f32 (get_local 2)) 21 | ;; CHECK: fptoui float %get_local{{.+}} to i64 22 | (i64.trunc_s/f64 (get_local 3)) 23 | ;; CHECK: fptosi double %get_local{{.+}} to i64 24 | (i64.trunc_u/f64 (get_local 3)) 25 | ;; CHECK fptoui double %get_local{{.+}} to i64 26 | (f32.convert_s/i32 (get_local 0)) 27 | ;; CHECK: sitofp i32 %get_local{{.+}} to float 28 | (f32.convert_u/i32 (get_local 0)) 29 | ;; CHECK: uitofp i32 %get_local{{.+}} to float 30 | (f32.convert_s/i64 (get_local 1)) 31 | ;; CHECK: sitofp i64 %get_local{{.+}} to float 32 | (f32.convert_u/i64 (get_local 1)) 33 | ;; CHECK: uitofp i64 %get_local{{.+}} to float 34 | (f64.convert_s/i32 (get_local 0)) 35 | ;; CHECK: sitofp i32 %get_local{{.+}} to double 36 | (f64.convert_u/i32 (get_local 0)) 37 | ;; CHECK: uitofp i32 %get_local{{.+}} to double 38 | (f64.convert_s/i64 (get_local 1)) 39 | ;; CHECK: sitofp i64 %get_local{{.+}} to double 40 | (f64.convert_u/i64 (get_local 1)) 41 | ;; CHECK: uitofp i64 %get_local{{.+}} to double 42 | (f32.demote/f64 (get_local 3)) 43 | ;; CHECK: fptrunc double %get_local{{.+}} to float 44 | (f64.promote/f32 (get_local 2)))) 45 | ;; CHECK: fpext float %get_local{{.+}} to double 46 | -------------------------------------------------------------------------------- /test/codegen/export.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -S %s | FileCheck %s 2 | (module 3 | (func (nop)) 4 | ;; CHECK: @.export.nop = alias void ()* @0 5 | (export "nop" 0)) 6 | -------------------------------------------------------------------------------- /test/codegen/function.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -S %s | FileCheck %s 2 | (module 3 | 4 | ;; Unnamed function 5 | (func ) 6 | ;; CHECK: define internal void @0() 7 | ;; Named function 8 | (func $foo) 9 | ;; CHECK: define internal void @"$foo" 10 | 11 | ;; Result type 12 | (func $result (result f64)(f64.const 0)) 13 | ;; CHECK: define internal double @"$result"() 14 | 15 | ;; Function arg types and names 16 | (func $argtypes (param i32 f32 i64 f64)) 17 | ;; CHECK: define internal void @"$argtypes"(i32, float, i64, double) 18 | (func $namedarg (param $foo i32)) 19 | ;; CHECK: define internal void @"$namedarg"(i32 %"$foo") 20 | (func $mixednames (param i64)(param $bar f32)(param f64)) 21 | ;; CHECK: define internal void @"$mixednames"(i64, float %"$bar", double) 22 | 23 | ;; Locals 24 | (func $local (local i32 f64)) 25 | ;; CHECK-LABEL: $local 26 | ;; CHECK: alloca i32 27 | ;; CHECK: alloca double 28 | (func $namedlocal (local $foo i32) (local $bar i64)) 29 | ;; CHECK-LABEL: $namedlocal 30 | ;; CHECK: %"$foo" = alloca i32 31 | ;; CHECK: %"$bar" = alloca i64 32 | ) -------------------------------------------------------------------------------- /test/codegen/getlocal.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -S %s | FileCheck %s 2 | (module 3 | ;; Unnamed local 4 | (func (local i32) (get_local 0)) 5 | ;; CHECK: define internal void @0() 6 | ;; CHECK: %local = alloca i32 7 | ;; %get_local = load i32, i32* %local 8 | 9 | ;; Unnamed arg 10 | (func (param i32) (result i32) (get_local 0)) 11 | ;; CHECK: define internal i32 @1(i32) 12 | ;; CHECK: %arg = alloca i32 13 | ;; CHECK: store i32 %0, i32* %arg 14 | ;; CHECK: %get_local = load i32, i32* %arg 15 | ;; CHECK: ret i32 %get_local 16 | 17 | ;; Named local 18 | (func (local $foo i32) (get_local $foo)) 19 | ;; CHECK: define internal void @2() 20 | ;; CHECK: %"$foo" = alloca i32 21 | ;; CHECK: %get_local = load i32, i32* %"$foo" 22 | 23 | ;; Named float arg 24 | (func (param $n f32) (result f32) (get_local $n)) 25 | ;; CHECK: define internal float @3(float %"$n") 26 | ;; CHECK: %"$n1" = alloca float 27 | ;; CHECK: store float %"$n", float* %"$n1" 28 | ;; CHECK: %get_local = load float, float* %"$n1" 29 | ;; CHECK: ret float %get_local 30 | 31 | ;; get local with param 32 | (func (param i32) (local i32) (get_local 1)) 33 | ;; CHECK: define internal void @4(i32) 34 | ;; CHECK: %arg = alloca i32 35 | ;; CHECK: %local = alloca i32 36 | ;; CHECK: %get_local = load i32, i32* %local 37 | 38 | ;; Mixed locals and params 39 | (func (param i32) (param $n f32) (result f32) 40 | ;; CHECK: define internal float @5(i32, float %"$n") 41 | ;; CHECK: %arg = alloca i32 42 | ;; CHECK: %"$n1" = alloca float 43 | (local i32 i64) 44 | (local $m f64) 45 | ;; CHECK: %local = alloca i32 46 | ;; CHECK: %local2 = alloca i64 47 | ;; CHECK: %"$m" = alloca double 48 | 49 | ;; CHECK: store i32 %0, i32* %arg 50 | ;; CHECK: store float %"$n", float* %"$n1" 51 | (get_local 0) 52 | ;; CHECK: load i32, i32* %arg 53 | (get_local 1) 54 | ;; CHECK: load float, float* %"$n1" 55 | (get_local 2) 56 | ;; CHECK: load i32, i32* %local 57 | (get_local 3) 58 | ;; CHECK: load i64, i64* %local2 59 | (get_local $m) ;; 4 60 | ;; CHECK: load double, double* %"$m" 61 | (get_local 4) 62 | ;; CHECK: load double, double* %"$m" 63 | (get_local $n) ;; 1 64 | ;; CHECK: %get_local8 = load float, float* %"$n1" 65 | ;; CHECK: ret float %get_local8 66 | ) 67 | ) 68 | -------------------------------------------------------------------------------- /test/codegen/if.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -S %s | FileCheck %s 2 | (module 3 | (func (param i32) (if (i32.const 1) (get_local 0))) 4 | ;; CHECK: br i1 true, label %if.then, label %if.else 5 | ;; CHECK: if.then: 6 | ;; CHECK: load 7 | ;; CHECK: br label %if.end 8 | ;; CHECK: if.else: 9 | ;; CHECK-NEXT: br label %if.end 10 | ;; CHECK: if.end: 11 | ;; CHECK: ret void 12 | 13 | (func (param i32) (result i32) 14 | (if_else (get_local 0) 15 | (return (i32.const 2)) 16 | (return (i32.const 3)))) 17 | ;; CHECK: %get_local = load i32 18 | ;; CHECK: %if_cmp = icmp ne i32 %get_local, 0 19 | ;; CHECK: br i1 %if_cmp, label %if.then, label %if.else 20 | ;; CHECK: if.then: 21 | ;; CHECK: ret i32 2 22 | ;; CHECK: if.else: 23 | ;; CHECK: ret i32 3 24 | ;; CHECK: if.end 25 | ;; CHECK: unreachable 26 | 27 | ;; The spec interpreter likes this but our parser complains of a type mismatch. 28 | ;;(func (result i32) 29 | ;; (if_else (i32.const 2) 30 | ;; (return (i32.const 1)) (i32.const 3))) 31 | 32 | (func (param i64) (param i64) (result i64) (return 33 | (if_else (i32.const 1) (get_local 0)(get_local 1)))) 34 | ;; CHECK: br i1 true, label %if.then, label %if.else 35 | ;; CHECK: if.then: 36 | ;; CHECK-NEXT: %get_local = load i64, i64* %arg 37 | ;; CHECK: if.else: 38 | ;; CHECK-NEXT: %get_local2 = load i64, i64* %arg1 39 | ;; CHECK: if.end: 40 | ;; CHECK-NEXT: %if.result = phi i64 [ %get_local, %if.then ], [ %get_local2, %if.else ] 41 | ;; CHECK-NEXT: ret i64 %if.result 42 | 43 | 44 | (func (param f32) (param f32) (result f32) 45 | (block 46 | (i64.const 2) 47 | (nop) 48 | (if_else (i32.const 0) (f64.const 1)(f64.const 2)) 49 | ;; CHECK: br i1 false, label %if.then, label %if.else 50 | ;; CHECK: if.then 51 | ;; CHECK-NEXT: br label %if.end 52 | ;; CHECK: if.else 53 | ;; CHECK-NEXT: br label %if.end 54 | ;; CHECK: if.end 55 | (if_else (i32.const 1) (get_local 1) (get_local 0)) 56 | ;; CHECK: br i1 true, label %if.then2, label %if.else3 57 | ;; CHECK: if.then2 58 | ;; CHECK: get_local 59 | ;; CHECK: if.else3 60 | ;; CHECK: get_local5 61 | ;; CHECK: if.end4: 62 | ;; CHECK: %if.result = phi float [ %get_local, %if.then2 ], [ %get_local5, %if.else3 ] 63 | ) 64 | ) 65 | 66 | 67 | (func (param i32) (param i32) (result i64) 68 | (if_else (get_local 0) 69 | ;; CHECK: %get_local = load i32, i32* %arg 70 | ;; CHECK: %if_cmp = icmp ne i32 %get_local, 0 71 | ;; CHECK: br i1 %if_cmp, label %if.then, label %if.else 72 | ;; CHECK: if.then: 73 | (if_else (get_local 1) (i64.const 1) (i64.const 2)) 74 | ;; CHECK: get_local2 = load i32, i32* %arg1 75 | ;; CHECK: if_cmp3 = icmp ne i32 %get_local2, 0 76 | ;; CHECK: br i1 %if_cmp3, label %if.then4, label %if.else5 77 | 78 | ;; CHECK: if.else: 79 | ;; CHECK-NEXT: br label %if.end 80 | ;; CHECK: if.end: 81 | ;; CHECK-NEXT: %if.result7 = phi i64 [ %if.result, %if.end6 ], [ 3, %if.else ] 82 | ;; CHECK-NEXT: ret i64 %if.result7 83 | (i64.const 3) 84 | ;; CHECK: if.then4: 85 | ;; CHECK-NEXT: br label %if.end6 86 | ;; CHECK: if.else5: 87 | ;; CHECK-NEXT: br label %if.end6 88 | ;; CHECK: if.end6 89 | ;; CHECK: %if.result = phi i64 [ 1, %if.then4 ], [ 2, %if.else5 ] 90 | ;; CHECK-NEXT: br label %if.end 91 | ) 92 | ) 93 | ) 94 | -------------------------------------------------------------------------------- /test/codegen/import.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat %s | FileCheck %s 2 | (module 3 | 4 | ;; unnamed 5 | (import "foo" "bar" (param i32) (result i64)) 6 | ;; CHECK: declare i64 @.foo.bar(i32) 7 | 8 | ;; named 9 | (import $print_i32 "stdio" "print" (param i32)) 10 | ;; CHECK: declare void @.stdio.print(i32) 11 | (import $add_i32 "math" "add" (param i32 i32) (result i32)) 12 | ;; CHECK: declare i32 @.math.add(i32, i32) 13 | (import $f32 "test" "f32" (param f32) (result f32)) 14 | ;; CHECK: declare float @.test.f32(float) 15 | (import $f64 "test" "f64" (param f64) (result f64)) 16 | ;; CHECK: declare double @.test.f64(double) 17 | (import $i64 "test" "i64" (param i64) (result i64)) 18 | ;; CHECK: declare i64 @.test.i64(i64) 19 | 20 | ;; call_import 21 | (func $foo (call_import 1 (i32.const 0))) 22 | ;; CHECK: call void @.stdio.print(i32 0) 23 | (func $bar (result f32) (call_import $f32 (f32.const 1.0))) 24 | ;; CHECK: %0 = call float @.test.f32(float 1.000000e+00) 25 | ) 26 | -------------------------------------------------------------------------------- /test/codegen/invoke.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -spec-test-script -S %s | FileCheck %s 2 | ;; Check that the -spec-test-script flag is required to accept this file. 3 | ;; RUN: not wat -S %s 4 | 5 | ;; CHECK: @__wasm_init_array = appending global [5 x void ()*] [void ()* @.invoke_ctor, void ()* bitcast (i32 ()* @Invoke_13 to void ()*), void ()* bitcast (i32 ()* @Invoke_17 to void ()*), void ()* bitcast (i32 ()* @Invoke_21 to void ()*), void ()* null] 6 | ;; CHECK: @__wasm_fini_array = appending global [2 x void ()*] [void ()* @.invoke_dtor, void ()* null] 7 | 8 | (module 9 | (export "test" $test) 10 | (func $test (param i32) (result i32) (i32.const 3))) 11 | ;; CHECK: define internal i32 @"$test"(i32) 12 | 13 | (invoke "test" (i32.const 1)) 14 | ;; CHECK: define i32 @Invoke_13 15 | ;; CHECK: call i32 @"$test"(i32 1) 16 | 17 | (invoke "test" (i32.const 100)) 18 | ;; CHECK: define i32 @Invoke_17 19 | ;; CHECK: call i32 @"$test"(i32 100) 20 | 21 | (invoke "test" (i32.const -30)) 22 | ;; CHECK: define i32 @Invoke_21 23 | ;; CHECK: call i32 @"$test"(i32 -30) 24 | -------------------------------------------------------------------------------- /test/codegen/load-offset.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -S %s | FileCheck %s 2 | 3 | (module 4 | (func (local i32) 5 | (i32.load offset=0 (i32.const 0)) 6 | ;; CHECK: load i32, i32* bitcast ({{.*}}@.wasm_membase to i32*) 7 | (i32.load offset=0 (i32.const 5)) 8 | ;; CHECK: load i32, i32* bitcast (i8* getelementptr inbounds ({{.*}}@.wasm_membase, i32 0, i32 5) to i32*) 9 | (i32.load offset=0 (get_local 0)) 10 | ;; CHECK: get_local = load i32, i32* %local 11 | ;; CHECK: %effective_addr = add i32 %get_local, 0 12 | ;; CHECK: %0 = getelementptr i8, i8* getelementptr inbounds ({{.*}}@.wasm_membase, i32 0, i32 0), i32 %effective_addr 13 | ;; CHECK: %1 = bitcast i8* %0 to i32* 14 | ;; CHECK: load i32, i32* %1 15 | 16 | 17 | (i64.load offset=1 (i32.const 3)) 18 | ;; CHECK: load i64, i64* bitcast (i8* getelementptr{{.*}}@.wasm_membase, i32 0, i32 4) to i64 19 | (i64.load offset=1 (get_local 0)) 20 | ;; CHECK: load i32, i32* %local 21 | ;; CHECK: add i32 %get_local{{[0-9]}}, 1 22 | ;; CHECK: load i64 23 | 24 | (i64.load8_s offset=2 (i32.const 0)) 25 | ;; CHECK: %[[V1:.+]] = load i8, 26 | ;; CHECK: sext i8 %[[V1]] to i64 27 | (i64.load16_s offset=3 (i32.const 0)) 28 | ;; CHECK: %[[V1:.+]] = load i16, 29 | ;; CHECK: sext i16 %[[V1]] to i64 30 | (i64.load32_s offset=4 (i32.const 0)) 31 | ;; CHECK: %[[V1:.+]] = load i32, 32 | ;; CHECK: sext i32 %[[V1]] to i64 33 | (i64.load8_u offset=5 (i32.const 0)) 34 | ;; CHECK: %[[V1:.+]] = load i8, 35 | ;; CHECK: zext i8 %[[V1]] to i64 36 | (i64.load16_u offset=6 (i32.const 0)) 37 | ;; CHECK: %[[V1:.+]] = load i16, 38 | ;; CHECK: zext i16 %[[V1]] to i64 39 | (i64.load32_u offset=7 (i32.const 0)) 40 | ;; CHECK: %[[V1:.+]] = load i32, 41 | ;; CHECK: zext i32 %[[V1]] to i64 42 | (i32.load8_s offset=8 (i32.const 0)) 43 | ;; CHECK: %[[V1:.+]] = load i8, 44 | ;; CHECK: sext i8 %[[V1]] to i32 45 | (i32.load16_s offset=9 (i32.const 0)) 46 | ;; CHECK: %[[V1:.+]] = load i16, 47 | ;; CHECK: sext i16 %[[V1]] to i32 48 | (i32.load8_u offset=10 (i32.const 0)) 49 | ;; CHECK: %[[V1:.+]] = load i8, 50 | ;; CHECK: zext i8 %[[V1]] to i32 51 | (i32.load16_u offset=11 (i32.const 0)) 52 | ;; CHECK: %[[V1:.+]] = load i16, 53 | ;; CHECK: zext i16 %[[V1]] to i32 54 | (f32.load offset=12 (i32.const 0)) 55 | ;; CHECK: load float, 56 | (f64.load offset=13 (i32.const 0)) 57 | ;; CHECK: load double 58 | 59 | (i32.load offset=0 align=1 (i32.const 0)) 60 | (i64.load offset=1 align=2 (i32.const 0)) 61 | (i64.load8_s offset=2 align=4 (i32.const 0)) 62 | (i64.load16_s offset=3 align=8 (i32.const 0)) 63 | (i64.load32_s offset=4 align=16 (i32.const 0)) 64 | (i64.load8_u offset=5 align=32 (i32.const 0)) 65 | (i64.load16_u offset=6 align=64 (i32.const 0)) 66 | (i64.load32_u offset=7 align=128 (i32.const 0)) 67 | (i32.load8_s offset=8 align=64 (i32.const 0)) 68 | (i32.load16_s offset=9 align=32 (i32.const 0)) 69 | (i32.load8_u offset=10 align=16 (i32.const 0)) 70 | (i32.load16_u offset=11 align=8 (i32.const 0)) 71 | (f32.load offset=12 align=4 (i32.const 0)) 72 | (f64.load offset=13 align=2 (i32.const 0)))) 73 | -------------------------------------------------------------------------------- /test/codegen/memory-segment-many.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -spec-test-script -S %s | FileCheck %s 2 | ;; Check that the -spec-test-script flag is required to accept this file. 3 | ;; RU N: not wat -S %s 4 | (module 5 | (memory 100 6 | (segment 0 "hi") 7 | (segment 4 "hello") 8 | (segment 10 "goodbye") 9 | (segment 20 "adios"))) 10 | 11 | ;; CHECK: @.memory-segment-many.segment_0 = internal global [2 x i8] c"hi" 12 | ;; CHECK: @.memory-segment-many.segment_4 = internal global [5 x i8] c"hello" 13 | ;; CHECK: @.memory-segment-many.segment_10 = internal global [7 x i8] c"goodbye" 14 | ;; CHECK: @.memory-segment-many.segment_20 = internal global [5 x i8] c"adios" 15 | ;; CHECK: @.wasm_membase = external global [4294967296 x i8], section ".membase" 16 | 17 | ;; CHECK: define internal void @.memory-segment-many_ctor() { 18 | ;; CHECK: call void @__wasm_init_memory({{.*}}, i64 100) 19 | ;; CHECK: call void{{.*}}@__wasm_init_segment{{.*}}@.wasm_membase{{.*}}, i64 0, i64 2 20 | ;; CHECK: call void{{.*}}@__wasm_init_segment{{.*}}@.wasm_membase{{.*}} i64 4, i64 5 21 | ;; CHECK: call void{{.*}}@__wasm_init_segment{{.*}}@.wasm_membase{{.*}} i64 10, i64 7 22 | ;; CHECK: call void{{.*}}@__wasm_init_segment{{.*}}@.wasm_membase{{.*}} i64 20, i64 5 -------------------------------------------------------------------------------- /test/codegen/memory_assert.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -spec-test-script -S %s | FileCheck %s 2 | ;; Check that the -spec-test-script flag is required to accept this file. 3 | ;; RUN: not wat -S %s 4 | (module (memory 100) 5 | (func $foo (result i32) (i32.const 0)) 6 | (export "foo" $foo) 7 | ) 8 | 9 | ;; CHECK: @__wasm_fini_array = appending global [3 x void ()*] [void ()* @.memory_assert_dtor, void ()* @.module1_dtor, void ()* null] 10 | 11 | ;; CHECK: define internal void @.memory_assert_ctor() { 12 | ;; CHECK: call void @__wasm_init_memory(i8* getelementptr {{.*}} @.wasm_membase{{.*}}, i64 100) 13 | ;; CHECK: define internal void @.memory_assert_dtor() { 14 | ;; CHECK: call void @__wasm_fini_memory(i8* {{.*}} @.wasm_membase 15 | 16 | ;; CHECK: define internal void @.module1_ctor() { 17 | ;; CHECK: call void @__wasm_init_memory(i8* getelementptr {{.*}} @.wasm_membase{{.*}}, i64 200) 18 | ;; CHECK: define internal void @.module1_dtor() { 19 | ;; CHECK: call void @__wasm_fini_memory(i8* {{.*}} @.wasm_membase 20 | 21 | 22 | 23 | (assert_return (invoke "foo") (i32.const 0)) 24 | 25 | ;; CHECK: call i32 @Invoke_ 26 | 27 | 28 | (module (memory 200) 29 | (func $foo (result f32) (f32.const nan)) 30 | (export "foo" $foo) 31 | ) 32 | 33 | 34 | (assert_return_nan (invoke "foo")) 35 | 36 | ;; CHECK: call float @Invoke_ 37 | 38 | (assert_trap (invoke "foo") "") 39 | ;; CHECK: call void @__wasm_assert_trap 40 | -------------------------------------------------------------------------------- /test/codegen/module-empty.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -S %s | FileCheck %s 2 | (module) 3 | ;; CHECK: ModuleID -------------------------------------------------------------------------------- /test/codegen/module-multi.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -spec-test-script -S %s | FileCheck %s 2 | ;; Check that the -spec-test-script flag is required to accept this file. 3 | ;; RUN: not wat -S %s 4 | (module (func $foo) 5 | (export "foo" $foo)) 6 | ;; CHECK: define internal void @"$foo 7 | (module (func $foo) 8 | (export "foo" $foo)) 9 | ;; CHECK: define internal void @"$foo 10 | -------------------------------------------------------------------------------- /test/codegen/return.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat %s | FileCheck %s 2 | (module 3 | 4 | (func (return)) 5 | ;; CHECK: define internal void @0 6 | ;; CHECK: ret void 7 | (func (result i32) 8 | ;; CHECK: define internal i32 @1 9 | (return (i32.const 42))) 10 | ;; CHECK: ret i32 42 11 | (func (result f64) 12 | ;; CHECK: define internal double @2 13 | (return (f64.const 0.1))) 14 | ;; CHECK: ret double 1.000000e-01 15 | 16 | ;; Implicit return 17 | (func (result i32) (i32.const 1)) 18 | ;; CHECK: define internal i32 19 | ;; ret i32 1 20 | (func ) 21 | ;; CHECK: define internal void 22 | ;; CHECK: ret void 23 | (func (result i32) (call 1)) 24 | ;; CHECK: define internal i32 25 | ;; CHECK: %0 = call i32 @1 26 | ;; CHECK: ret i32 %0 27 | ) 28 | -------------------------------------------------------------------------------- /test/codegen/setlocal.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -S %s | FileCheck %s 2 | (module 3 | ;; Unnamed local 4 | (func (local i32) 5 | (set_local 0 (i32.const 0))) 6 | ;; CHECK: define internal void @0() 7 | ;; CHECK: %local = alloca i32 8 | ;; CHECK: store i32 0, i32* %local 9 | 10 | ;; Unnamed arg 11 | (func (param i32) (set_local 0 (i32.const 0))) 12 | ;; CHECK: define internal void @1(i32) 13 | ;; CHECK: %arg = alloca i32 14 | ;; CHECK: store i32 %0, i32* %arg 15 | ;; CHECK: store i32 0, i32* %arg 16 | 17 | ;; Named local 18 | (func 19 | (local $n i32) 20 | (set_local $n (i32.const 12))) 21 | ;; CHECK: define internal void @2() 22 | ;; CHECK: %"$n" = alloca i32 23 | ;; CHECK: store i32 12, i32* %"$n" 24 | 25 | ;; Named float arg 26 | (func (param $n f32) (set_local $n (f32.const 1))) 27 | ;; CHECK: define internal void @3(float %"$n") 28 | ;; CHECK: %"$n1" = alloca float 29 | ;; CHECK: store float %"$n", float* %"$n1" 30 | ;; CHECK: store float 1.000000e+00, float* %"$n1" 31 | 32 | ;; set local with param 33 | (func (param i32) (local i32) (set_local 1 (i32.const 0))) 34 | ;; CHECK: define internal void @4(i32) 35 | ;; CHECK: %arg = alloca i32 36 | ;; CHECK: %local = alloca i32 37 | ;; CHECK: store i32 %0, i32* %arg 38 | ;; CHECK: store i32 0, i32* %local 39 | 40 | ;; Mixed locals and params 41 | (func (param i32) (param $n f32) 42 | ;; CHECK: define internal void @5(i32, float %"$n") 43 | ;; CHECK: %arg = alloca i32 44 | ;; CHECK: %"$n1" = alloca float 45 | (local i32 i64) 46 | (local $m f64) 47 | ;; CHECK: %local = alloca i32 48 | ;; CHECK: %local2 = alloca i64 49 | ;; CHECK: %"$m" = alloca double 50 | ;; CHECK: store i32 %0, i32* %arg 51 | ;; CHECK: store float %"$n", float* %"$n1" 52 | (set_local 0 (i32.const 0)) 53 | ;; CHECK: store i32 0, i32* %arg 54 | (set_local 1 (f32.const 0)) 55 | ;; CHECK: store float 0.000000e+00, float* %"$n1" 56 | (set_local $n (f32.const 0)) ;; 1 57 | ;; CHECK: store float 0.000000e+00, float* %"$n1" 58 | (set_local 2 (i32.const 0)) 59 | ;; CHECK: store i32 0, i32* %local 60 | (set_local 3 (i64.const 0)) 61 | ;; CHECK: store i64 0, i64* %local2 62 | (set_local $m (f64.const 0)) ;; 4 63 | ;; CHECK: store double 0.000000e+00, double* %"$m" 64 | (set_local 4 (f64.const 0))) 65 | ;; CHECK: store double 0.000000e+00, double* %"$m" 66 | ) -------------------------------------------------------------------------------- /test/codegen/store-offset.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -S %s | FileCheck %s 2 | 3 | (module 4 | (func (local i32 i32) 5 | (i32.store8 offset=0 (i32.const 0) (i32.const 0)) 6 | ;; CHECK: store i8 0, i8* getelementptr inbounds{{.*}}@.wasm_membase, i32 0, i32 0 7 | (i32.store16 offset=1 (i32.const 0) (i32.const 0)) 8 | ;; CHECK: store i16 0, i16* bitcast (i8* getelementptr inbounds{{.*}}@.wasm_membase, i32 0, i32 1 9 | (i32.store offset=2 (i32.const 0) (i32.const 0)) 10 | ;; CHECK: store i32 0, i32* bitcast (i8* getelementptr inbounds{{.*}}@.wasm_membase, i32 0, i32 2 11 | 12 | (i32.store offset=2 (i32.const 0) (i32.const 0)) 13 | ;; CHECK: store i32 0, i32* bitcast (i8* getelementptr inbounds{{.*}}@.wasm_membase, i32 0, i32 2 14 | (i32.store offset=2 (get_local 0) (get_local 1)) 15 | ;; TODO: test the logic for effective addr here. it should be the same as load, and may change soon 16 | 17 | (i64.store offset=3 (i32.const 0) (i64.const 0)) 18 | ;; CHECK: store i64 0, i64* bitcast (i8* getelementptr inbounds{{.*}}@.wasm_membase, i32 0, i32 3 19 | (i64.store8 offset=4 (i32.const 0) (i64.const 0x101)) 20 | ;; CHECK: store i8 1, i8* getelementptr inbounds{{.*}}@.wasm_membase, i32 0, i32 4 21 | (i64.store16 offset=5 (i32.const 0) (i64.const 0x10001)) 22 | ;; CHECK: store i16 1, i16* bitcast (i8* getelementptr inbounds{{.*}}@.wasm_membase, i32 0, i32 5 23 | (i64.store32 offset=6 (i32.const 0) (i64.const 0x100000001)) 24 | ;; CHECK: store i32 1, i32* bitcast (i8* getelementptr inbounds{{.*}}@.wasm_membase, i32 0, i32 6 25 | (f32.store offset=7 (i32.const 0) (f32.const 1.0)) 26 | ;; CHECK: store float 1.000000e+00, float* bitcast (i8* getelementptr inbounds{{.*}}@.wasm_membase, i32 0, i32 7 27 | (f64.store offset=8 (i32.const 0) (f64.const 2.0)) 28 | ;; CHECK: store double 2.000000e+00, double* bitcast (i8* getelementptr inbounds{{.*}}@.wasm_membase, i32 0, i32 8 29 | 30 | ;; alignment must come after 31 | (i32.store8 offset=0 align=1 (i32.const 0) (i32.const 0)) 32 | (i32.store16 offset=1 align=2 (i32.const 0) (i32.const 0)) 33 | (i32.store offset=2 align=4 (i32.const 0) (i32.const 0)) 34 | (i64.store offset=3 align=8 (i32.const 0) (i64.const 0)) 35 | (i64.store8 offset=4 align=16 (i32.const 0) (i64.const 0)) 36 | (i64.store16 offset=5 align=8 (i32.const 0) (i64.const 0)) 37 | (i64.store32 offset=6 align=4 (i32.const 0) (i64.const 0)) 38 | (f32.store offset=7 align=2 (i32.const 0) (f32.const 0)) 39 | (f64.store offset=8 align=1 (i32.const 0) (f64.const 0)))) 40 | -------------------------------------------------------------------------------- /test/codegen/unary.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wat -S %s | FileCheck %s 2 | (module 3 | (func (param f32) (param f64) 4 | (f32.neg (get_local 0)) 5 | ;; CHECK: fsub float -0.000000e+00, %get_local 6 | (f64.neg (get_local 1)) 7 | ;; CHECK: fsub double -0.000000e+00, %get_local 8 | (f32.abs (f32.const 0)) 9 | ;; CHECK: call float @llvm.fabs.f32 10 | (f64.abs (f64.const 0)) 11 | ;; CHECK: call double @llvm.fabs.f64 12 | (f32.sqrt (f32.const 0)) 13 | ;; CHECK: call float @llvm.sqrt.f32 14 | (f64.sqrt (f64.const 0)) 15 | ;; CHECK: call double @llvm.sqrt.f64 16 | (i32.clz (i32.const 0)) 17 | ;; CHECK: call i32 @llvm.ctlz.i32(i32 0, i1 false) 18 | (i64.clz (i64.const 0)) 19 | ;; CHECK: call i64 @llvm.ctlz.i64(i64 0, i1 false) 20 | (i32.ctz (i32.const 0)) 21 | ;; CHECK: call i32 @llvm.cttz.i32(i32 0, i1 false) 22 | (i64.ctz (i64.const 0)) 23 | ;; CHECK: call i64 @llvm.cttz.i64(i64 0, i1 false) 24 | (i32.popcnt (i32.const 0)) 25 | ;; CHECK: call i32 @llvm.ctpop.i32(i32 0) 26 | (i64.popcnt (i64.const 0)) 27 | ;; CHECK: call i64 @llvm.ctpop.i64(i64 0) 28 | (f32.ceil (f32.const 0)) 29 | ;; CHECK: call float @llvm.ceil.f32 30 | (f64.ceil (f64.const 0)) 31 | ;; CHECK: call double @llvm.ceil.f64 32 | (f32.floor (f32.const 0)) 33 | ;; CHECK: call float @llvm.floor.f32 34 | (f64.floor (f64.const 0)) 35 | ;; CHECK: call double @llvm.floor.f64 36 | (f32.trunc (f32.const 0)) 37 | ;; CHECK: call float @llvm.trunc.f32 38 | (f64.trunc (f64.const 0)) 39 | ;; CHECK: call double @llvm.trunc.f64 40 | (f32.nearest (f32.const 0)) 41 | ;; CHECK: call float @llvm.rint.f32 42 | (f64.nearest (f64.const 0)))) 43 | ;; CHECK: call double @llvm.rint.f64 -------------------------------------------------------------------------------- /test/lit.cfg: -------------------------------------------------------------------------------- 1 | import lit.formats 2 | 3 | # name: The name of this test suite. 4 | config.name = 'waot' 5 | 6 | # testFormat: The test format to use to interpret tests. 7 | config.test_format = lit.formats.ShTest() 8 | 9 | # suffixes: A list of file extensions to treat as test files. 10 | config.suffixes = ['.wast'] 11 | 12 | 13 | config.test_source_root = os.path.dirname(__file__) 14 | config.test_exec_root = config.test_source_root 15 | 16 | bin_dir = os.path.join(os.path.dirname(config.test_source_root), 'out') 17 | config.environment['PATH'] = config.environment['PATH'] + ':' + bin_dir 18 | -------------------------------------------------------------------------------- /test/parser/assertreturn.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump -spec-test-script %s > %t1 3 | ;; RUN: sexpr-wasm --spec %t1 > %t2 4 | ;; RUN: sexpr-wasm --spec %s | diff - %t2 5 | ;; Printing src locs with assert statements means that files with different 6 | ;; names have different output when dumped, so we can't do the round tripping 7 | ;; test, but, that's OK for asserts. 8 | ;; Test that the -spec-test-script flag is required for multi-module 9 | ;; RUN: not sexpr_dump %s 10 | ;; Test that the line number info is correct. 11 | ;; RUN: sexpr_dump -spec-test-script %s | FileCheck %s 12 | (module 13 | (func $foo (result i32) (i32.const 0)) 14 | (export "foo" $foo) 15 | (func $bar (param f32) (result f32) (f32.const 1.0)) 16 | (export "bar" $bar)) 17 | 18 | ;; CHECK: assertreturn.wast:19 19 | (assert_return (invoke "foo") (i32.const 0)) 20 | ;; CHECK: assertreturn.wast:21 21 | (assert_return (invoke "bar" (f32.const 0)) (f32.const 0)) 22 | ;; ok to use more complex exprs 23 | (assert_return 24 | (invoke "bar" 25 | (f32.const 10)) 26 | (f32.const 11)) 27 | 28 | (assert_return (invoke "foo") (i32.const 2)) 29 | 30 | (assert_trap (invoke "foo") "foo") 31 | (assert_trap (invoke "bar" (f32.const 1)) "bar") -------------------------------------------------------------------------------- /test/parser/assertreturnnan.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump -spec-test-script %s > %t1 3 | ;; RUN: sexpr-wasm --spec %t1 > %t2 4 | ;; RUN: sexpr-wasm --spec %s | diff - %t2 5 | ;; Printing src locs with assert statements means that files with different 6 | ;; names have different output when dumped, so we can't do the round tripping 7 | ;; test, but, that's OK for asserts. 8 | ;; Test that the -spec-test-script flag is required for mult-module 9 | ;; RUN: not sexpr_dump %s 10 | ;; Test that the line number info is correct. 11 | ;; RUN: sexpr_dump -spec-test-script %s | FileCheck %s 12 | (module 13 | (func $foo (param f32) (result f32) 14 | (f32.div (get_local 0) (f32.const 0))) 15 | (export "foo" $foo)) 16 | 17 | ;; CHECK: assertreturnnan.wast:19 18 | (assert_return_nan 19 | (invoke "foo" (f32.const 0))) 20 | -------------------------------------------------------------------------------- /test/parser/bad-memory-empty.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: not sexpr_dump %s 2>&1 | FileCheck %s 2 | ;; CHECK: bad-memory-empty.wast:3:16: syntax error, unexpected ), expecting INT 3 | (module (memory)) 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/parser/binary.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func 9 | (i32.add (i32.const 0) (i32.const 0)) 10 | (i64.add (i64.const 0) (i64.const 0)) 11 | (f32.add (f32.const 0) (f32.const 0)) 12 | (f64.add (f64.const 0) (f64.const 0)) 13 | (i32.sub (i32.const 0) (i32.const 0)) 14 | (i64.sub (i64.const 0) (i64.const 0)) 15 | (f32.sub (f32.const 0) (f32.const 0)) 16 | (f64.sub (f64.const 0) (f64.const 0)) 17 | (i32.mul (i32.const 0) (i32.const 0)) 18 | (i64.mul (i64.const 0) (i64.const 0)) 19 | (f32.mul (f32.const 0) (f32.const 0)) 20 | (f64.mul (f64.const 0) (f64.const 0)) 21 | (i32.div_s (i32.const 0) (i32.const 0)) 22 | (i64.div_s (i64.const 0) (i64.const 0)) 23 | (i32.div_u (i32.const 0) (i32.const 0)) 24 | (i64.div_u (i64.const 0) (i64.const 0)) 25 | (f32.div (f32.const 0) (f32.const 0)) 26 | (f64.div (f64.const 0) (f64.const 0)) 27 | (i32.rem_s (i32.const 0) (i32.const 0)) 28 | (i64.rem_s (i64.const 0) (i64.const 0)) 29 | (i32.rem_u (i32.const 0) (i32.const 0)) 30 | (i64.rem_u (i64.const 0) (i64.const 0)) 31 | (f32.min (f32.const 0) (f32.const 0)) 32 | (f64.min (f64.const 0) (f64.const 0)) 33 | (f32.max (f32.const 0) (f32.const 0)) 34 | (f64.max (f64.const 0) (f64.const 0)) 35 | (i32.and (i32.const 0) (i32.const 0)) 36 | (i64.and (i64.const 0) (i64.const 0)) 37 | (i32.or (i32.const 0) (i32.const 0)) 38 | (i64.or (i64.const 0) (i64.const 0)) 39 | (i32.xor (i32.const 0) (i32.const 0)) 40 | (i64.xor (i64.const 0) (i64.const 0)) 41 | (i32.shl (i32.const 0) (i32.const 0)) 42 | (i64.shl (i64.const 0) (i64.const 0)) 43 | (i32.shr_u (i32.const 0) (i32.const 0)) 44 | (i64.shr_u (i64.const 0) (i64.const 0)) 45 | (i32.shr_s (i32.const 0) (i32.const 0)) 46 | (i64.shr_s (i64.const 0) (i64.const 0)) 47 | (f32.copysign (f32.const 0) (f32.const 0)) 48 | (f64.copysign (f64.const 0) (f64.const 0)))) 49 | -------------------------------------------------------------------------------- /test/parser/block-nested.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module (func (block (nop) (block (nop) (nop)) (nop)))) 8 | -------------------------------------------------------------------------------- /test/parser/block.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module (func (block (nop) (block (nop) (nop))))) 8 | -------------------------------------------------------------------------------- /test/parser/call-name-prefix.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | ;; Test that the callee name is printed 8 | ;; RUN: sexpr_dump %s | FileCheck %s 9 | (module 10 | (func $foomore 11 | (call $foo (i32.const 0))) 12 | ;; CHECK: (call $foo (i32.const 13 | (func $foo (param i32) 14 | (nop))) 15 | -------------------------------------------------------------------------------- /test/parser/call-named.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | ;; Test that the callee name is printed 8 | ;; RUN: sexpr_dump %s | FileCheck %s 9 | (module 10 | (func $foo (param f32) 11 | (call $foo (f32.const 0.0)))) 12 | ;; CHECK: (call $foo (f32.const -------------------------------------------------------------------------------- /test/parser/call.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module (func (param i32) 8 | (call 0 (i32.const 1))) 9 | (func (param i32) (call 1 (i32.const 2))) 10 | (func (call 2)) 11 | (func (call 1 (i32.const 1))) 12 | ) 13 | -------------------------------------------------------------------------------- /test/parser/callimport-defined-later.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func 9 | (call_import 0 (f32.const 0))) 10 | (import "foo" "bar" (param f32))) 11 | -------------------------------------------------------------------------------- /test/parser/callimport-named.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN : sexpr-wasm -d %s | diff - %t2;; TODO: investigate? 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | ;; Check that the callee name is printed 8 | ;; RUN: sexpr_dump %s | FileCheck %s 9 | (module 10 | (import $bar "foo" "bar" (param f32)) 11 | (func 12 | (call_import $bar (f32.const 0)))) 13 | ;; CHECK: (call_import $bar (f32.const -------------------------------------------------------------------------------- /test/parser/callimport.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (import "foo" "bar" (param i32) (result i32)) 9 | (func (param i32) (result i32) 10 | (call_import 0 (i32.const 0)))) 11 | -------------------------------------------------------------------------------- /test/parser/cast.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | ;; int->float reinterpret unsupported currently 8 | (module 9 | (func 10 | (f32.reinterpret/i32 (i32.const 0)) 11 | (i32.reinterpret/f32 (f32.const 0)) 12 | (f64.reinterpret/i64 (i64.const 0)) 13 | (i64.reinterpret/f64 (f64.const 0)))) 14 | -------------------------------------------------------------------------------- /test/parser/compare.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func 9 | (i32.eq (i32.const 0) (i32.const 0)) 10 | (i64.eq (i64.const 0) (i64.const 0)) 11 | (f32.eq (f32.const 0) (f32.const 0)) 12 | (f64.eq (f64.const 0) (f64.const 0)) 13 | (i32.ne (i32.const 0) (i32.const 0)) 14 | (i64.ne (i64.const 0) (i64.const 0)) 15 | (f32.ne (f32.const 0) (f32.const 0)) 16 | (f64.ne (f64.const 0) (f64.const 0)) 17 | (i32.lt_s (i32.const 0) (i32.const 0)) 18 | (i64.lt_s (i64.const 0) (i64.const 0)) 19 | (i32.lt_u (i32.const 0) (i32.const 0)) 20 | (i64.lt_u (i64.const 0) (i64.const 0)) 21 | (f32.lt (f32.const 0) (f32.const 0)) 22 | (f64.lt (f64.const 0) (f64.const 0)) 23 | (i32.le_s (i32.const 0) (i32.const 0)) 24 | (i64.le_s (i64.const 0) (i64.const 0)) 25 | (i32.le_u (i32.const 0) (i32.const 0)) 26 | (i64.le_u (i64.const 0) (i64.const 0)) 27 | (f32.le (f32.const 0) (f32.const 0)) 28 | (f64.le (f64.const 0) (f64.const 0)) 29 | (i32.gt_s (i32.const 0) (i32.const 0)) 30 | (i64.gt_s (i64.const 0) (i64.const 0)) 31 | (i32.gt_u (i32.const 0) (i32.const 0)) 32 | (i64.gt_u (i64.const 0) (i64.const 0)) 33 | (f32.gt (f32.const 0) (f32.const 0)) 34 | (f64.gt (f64.const 0) (f64.const 0)) 35 | (i32.ge_s (i32.const 0) (i32.const 0)) 36 | (i64.ge_s (i64.const 0) (i64.const 0)) 37 | (i32.ge_u (i32.const 0) (i32.const 0)) 38 | (i64.ge_u (i64.const 0) (i64.const 0)) 39 | (f32.ge (f32.const 0) (f32.const 0)) 40 | (f64.ge (f64.const 0) (f64.const 0)))) 41 | -------------------------------------------------------------------------------- /test/parser/const.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func 9 | (i32.const 0) 10 | (i32.const -2147483648) 11 | (i32.const 4294967295) 12 | (i32.const -0x80000000) 13 | (i32.const 0xffffffff) 14 | (i64.const 0) 15 | (i64.const -9223372036854775808) 16 | (i64.const 18446744073709551615) 17 | (i64.const -0x8000000000000000) 18 | (i64.const 0xffffffffffffffff) 19 | (f32.const 0.0) 20 | (f32.const 1e23) 21 | (f32.const 1.234567e-5) 22 | (f64.const 0.0) 23 | (f64.const -0.987654321) 24 | (f64.const 6.283185307179586))) 25 | -------------------------------------------------------------------------------- /test/parser/convert.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func 9 | (i32.wrap/i64 (i64.const 0)) 10 | (i64.extend_u/i32 (i32.const 0)) 11 | (i64.extend_s/i32 (i32.const 0)) 12 | (i32.trunc_s/f32 (f32.const 0)) 13 | (i32.trunc_u/f32 (f32.const 0)) 14 | (i32.trunc_s/f64 (f64.const 0)) 15 | (i32.trunc_u/f64 (f64.const 0)) 16 | (i64.trunc_s/f32 (f32.const 0)) 17 | (i64.trunc_u/f32 (f32.const 0)) 18 | (i64.trunc_s/f64 (f64.const 0)) 19 | (i64.trunc_u/f64 (f64.const 0)) 20 | (f32.convert_s/i32 (i32.const 0)) 21 | (f32.convert_u/i32 (i32.const 0)) 22 | (f32.convert_s/i64 (i64.const 0)) 23 | (f32.convert_u/i64 (i64.const 0)) 24 | (f64.convert_s/i32 (i32.const 0)) 25 | (f64.convert_u/i32 (i32.const 0)) 26 | (f64.convert_s/i64 (i64.const 0)) 27 | (f64.convert_u/i64 (i64.const 0)) 28 | (f32.demote/f64 (f64.const 0)) 29 | (f64.promote/f32 (f32.const 0)))) 30 | -------------------------------------------------------------------------------- /test/parser/export.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func ) 9 | (export "nop" 0)) 10 | -------------------------------------------------------------------------------- /test/parser/func-named.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module (func $foo)) 8 | -------------------------------------------------------------------------------- /test/parser/getlocal-index-after-param.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module (func (param i32) (local i32) (get_local 1))) 8 | -------------------------------------------------------------------------------- /test/parser/getlocal-index-mixed-named-unnamed.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func (param i32) (param $n f32) 9 | (local i32 i64) 10 | (local $m f64) 11 | (get_local 0) 12 | (get_local 1) 13 | (get_local $n) ;; 1 14 | (get_local 2) 15 | (get_local 3) 16 | (get_local $m) ;; 4 17 | (get_local 4))) 18 | -------------------------------------------------------------------------------- /test/parser/getlocal-named.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module (func (local $foo i32) (get_local $foo))) -------------------------------------------------------------------------------- /test/parser/getlocal-param-named.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module (func (param $n i32) (get_local $n))) 8 | -------------------------------------------------------------------------------- /test/parser/getlocal-param.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func (param i32) (get_local 0)) 9 | (func (param i64) (local i32) (get_local 0) (get_local 1)) 10 | (func (param i64) (local i32) (get_local 1) (get_local 0)) 11 | (func (param i64) (result f32) (local i32) 12 | (get_local 0) 13 | (get_local 1) 14 | (f32.const 1.0)) 15 | ) 16 | -------------------------------------------------------------------------------- /test/parser/getlocal.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func (local i32) (get_local 0)) 9 | (func (local i32) (local i64) (get_local 1)(get_local 0)) 10 | ) 11 | -------------------------------------------------------------------------------- /test/parser/if.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func (if (i32.const 1) (nop))) 9 | (func (if_else (i32.const 0) (nop) (nop))) 10 | (func (result i32) 11 | (if_else (i32.const 1) 12 | (return (i32.const 2)) 13 | (return (i32.const 3)))) 14 | (func (result i32) 15 | (if_else (i32.const 2) 16 | (return (i32.const 1)) (i32.const 3))) 17 | (func (param i64) (param i64) (result i64) (return 18 | (if_else (i32.const 1) (get_local 0)(get_local 1)))) 19 | (func (result i64) (return 20 | (if_else (i32.const 1) (i64.const 2)(i64.const 3)))) 21 | (func (param f32) (param f32) (result f32) 22 | (block 23 | (i64.const 2) 24 | (nop) 25 | (if_else (i32.const 0) (f64.const 1)(f64.const 2)) 26 | (if_else (i32.const 1) (get_local 1) (get_local 0)) 27 | ) 28 | ) 29 | (func (param i32) (param i32) (result i64) 30 | (if_else (get_local 0) 31 | (if_else (get_local 1) (i64.const 1) (i64.const 2)) 32 | (i64.const 3) 33 | ) 34 | ) 35 | ) 36 | -------------------------------------------------------------------------------- /test/parser/import.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | ;; unnamed 9 | (import "foo" "bar" (param i32) (result i64)) 10 | 11 | ;; named 12 | (import $print_i32 "stdio" "print" (param i32)) 13 | (import $add_i32 "math" "add" (param i32 i32) (result i32)) 14 | (import $f32 "test" "f32" (param f32) (result f32)) 15 | (import $f64 "test" "f64" (param f64) (result f64)) 16 | (import $i64 "test" "i64" (param i64) (result i64))) -------------------------------------------------------------------------------- /test/parser/invoke.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump -spec-test-script %s > %t1 3 | ;; RUN: sexpr-wasm --spec %t1 > %t2 4 | ;; RUN: sexpr-wasm --spec %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump -spec-test-script %t1 | diff %t1 - 7 | ;; Test that the -spec-test-script flag is required for multi-module 8 | ;; RUN: not sexpr_dump %s 9 | (module 10 | (export "test" $test) 11 | (func $t (param i32) (result i32) (i32.const 3)) 12 | (func $test (param i32) (result i32) (i32.const 3)) 13 | (export "test2" $test2) 14 | (func $test2 (param i32) (result i32) (i32.const 3)) 15 | ) 16 | 17 | (invoke "test" (i32.const 1)) 18 | (invoke "test" (i32.const 100)) 19 | (invoke "test" (i32.const -30)) 20 | (invoke "test2" (i32.const -30)) 21 | -------------------------------------------------------------------------------- /test/parser/load-aligned.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func 9 | (i32.load align=4 (i32.const 0)) 10 | (i64.load align=4 (i32.const 0)) 11 | (i64.load8_s align=8 (i32.const 0)) 12 | (i64.load16_s align=1 (i32.const 0)) 13 | (i64.load32_s align=2 (i32.const 0)) 14 | (i64.load8_u align=4 (i32.const 0)) 15 | (i64.load16_u align=8 (i32.const 0)) 16 | (i64.load32_u align=1 (i32.const 0)) 17 | (i32.load8_s align=8 (i32.const 0)) 18 | (i32.load16_s align=1 (i32.const 0)) 19 | (i32.load8_u align=4 (i32.const 0)) 20 | (i32.load16_u align=8 (i32.const 0)) 21 | (f32.load align=2 (i32.const 0)) 22 | (f64.load align=8 (i32.const 0)))) 23 | -------------------------------------------------------------------------------- /test/parser/load-offset.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func 9 | (i32.load offset=0 (i32.const 0)) 10 | (i64.load offset=1 (i32.const 0)) 11 | (i64.load8_s offset=2 (i32.const 0)) 12 | (i64.load16_s offset=3 (i32.const 0)) 13 | (i64.load32_s offset=4 (i32.const 0)) 14 | (i64.load8_u offset=5 (i32.const 0)) 15 | (i64.load16_u offset=6 (i32.const 0)) 16 | (i64.load32_u offset=7 (i32.const 0)) 17 | (i32.load8_s offset=8 (i32.const 0)) 18 | (i32.load16_s offset=9 (i32.const 0)) 19 | (i32.load8_u offset=10 (i32.const 0)) 20 | (i32.load16_u offset=11 (i32.const 0)) 21 | (f32.load offset=12 (i32.const 0)) 22 | (f64.load offset=13 (i32.const 0)) 23 | 24 | (i32.load offset=0 align=1 (i32.const 0)) 25 | (i64.load offset=1 align=2 (i32.const 0)) 26 | (i64.load8_s offset=2 align=4 (i32.const 0)) 27 | (i64.load16_s offset=3 align=8 (i32.const 0)) 28 | (i64.load32_s offset=4 align=16 (i32.const 0)) 29 | (i64.load8_u offset=5 align=32 (i32.const 0)) 30 | (i64.load16_u offset=6 align=64 (i32.const 0)) 31 | (i64.load32_u offset=7 align=128 (i32.const 0)) 32 | (i32.load8_s offset=8 align=64 (i32.const 0)) 33 | (i32.load16_s offset=9 align=32 (i32.const 0)) 34 | (i32.load8_u offset=10 align=16 (i32.const 0)) 35 | (i32.load16_u offset=11 align=8 (i32.const 0)) 36 | (f32.load offset=12 align=4 (i32.const 0)) 37 | (f64.load offset=13 align=2 (i32.const 0)))) 38 | -------------------------------------------------------------------------------- /test/parser/load.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func 9 | (i32.load (i32.const 0)) 10 | (i32.load8_s (i32.const 0)) 11 | (i32.load16_s (i32.const 0)) 12 | (i32.load8_u (i32.const 0)) 13 | (i32.load16_u (i32.const 0)) 14 | (i64.load (i32.const 0)) 15 | (i64.load8_s (i32.const 0)) 16 | (i64.load16_s (i32.const 0)) 17 | (i64.load32_s (i32.const 0)) 18 | (i64.load8_u (i32.const 0)) 19 | (i64.load16_u (i32.const 0)) 20 | (i64.load32_u (i32.const 0)) 21 | (f32.load (i32.const 0)) 22 | (f64.load (i32.const 0)))) 23 | -------------------------------------------------------------------------------- /test/parser/local-multi.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module (func (local i32) (local $n i64))) 8 | -------------------------------------------------------------------------------- /test/parser/local.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module (func (local i32))) 8 | -------------------------------------------------------------------------------- /test/parser/memory-init-max-size.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module (memory 100 200)) 8 | -------------------------------------------------------------------------------- /test/parser/memory-init-size.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module (memory 100)) 8 | -------------------------------------------------------------------------------- /test/parser/memory-segment-1.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module (memory 100 (segment 0 "hello, world!"))) 8 | -------------------------------------------------------------------------------- /test/parser/memory-segment-many.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (memory 100 9 | (segment 0 "hi") 10 | (segment 4 "hello") 11 | (segment 10 "goodbye") 12 | (segment 20 "adios"))) 13 | -------------------------------------------------------------------------------- /test/parser/module-empty.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module) 8 | -------------------------------------------------------------------------------- /test/parser/module-multi.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module) 8 | (module) -------------------------------------------------------------------------------- /test/parser/param-binding.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module (func (param $foo i32))) 8 | -------------------------------------------------------------------------------- /test/parser/param-multi.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module (func (param i32) (param $n f64))) 8 | -------------------------------------------------------------------------------- /test/parser/param-type-1.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module (func (param i32))) 8 | -------------------------------------------------------------------------------- /test/parser/param-type-2.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module (func (param i32 f32))) 8 | -------------------------------------------------------------------------------- /test/parser/result.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module (func (result i32) (i32.const 0))) 8 | -------------------------------------------------------------------------------- /test/parser/return-empty.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func (return))) 9 | -------------------------------------------------------------------------------- /test/parser/return.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func (result i32) 9 | (return (i32.const 42)))) 10 | -------------------------------------------------------------------------------- /test/parser/setlocal.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func (local i32) 9 | (set_local 0 (i32.const 0))) 10 | (func (param i32) (set_local 0 (i32.const 0))) 11 | (func 12 | (local $n i32) 13 | (set_local $n (i32.const 12))) 14 | (func (param $n i32) (set_local $n (i32.const 0))) 15 | (func (param i32) (local i32) (set_local 1 (i32.const 0))) 16 | (func (param i32) (param $n f32) 17 | (local i32 i64) 18 | (local $m f64) 19 | (set_local 0 (i32.const 0)) 20 | (set_local 1 (f32.const 0)) 21 | (set_local $n (f32.const 0)) ;; 1 22 | (set_local 2 (i32.const 0)) 23 | (set_local 3 (i64.const 0)) 24 | (set_local $m (f64.const 0)) ;; 4 25 | (set_local 4 (f64.const 0))) 26 | ) -------------------------------------------------------------------------------- /test/parser/store-aligned.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func 9 | (i32.store8 align=1 (i32.const 0) (i32.const 0)) 10 | (i32.store16 align=2 (i32.const 0) (i32.const 0)) 11 | (i32.store align=4 (i32.const 0) (i32.const 0)) 12 | (i64.store align=8 (i32.const 0) (i64.const 0)) 13 | (i64.store8 align=1 (i32.const 0) (i64.const 0)) 14 | (i64.store16 align=2 (i32.const 0) (i64.const 0)) 15 | (i64.store32 align=4 (i32.const 0) (i64.const 0)) 16 | (f32.store align=4 (i32.const 0) (f32.const 0)) 17 | (f64.store align=8 (i32.const 0) (f64.const 0)))) 18 | -------------------------------------------------------------------------------- /test/parser/store-offset.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func 9 | (i32.store8 offset=0 (i32.const 0) (i32.const 0)) 10 | (i32.store16 offset=1 (i32.const 0) (i32.const 0)) 11 | (i32.store offset=2 (i32.const 0) (i32.const 0)) 12 | (i64.store offset=3 (i32.const 0) (i64.const 0)) 13 | (i64.store8 offset=4 (i32.const 0) (i64.const 0)) 14 | (i64.store16 offset=5 (i32.const 0) (i64.const 0)) 15 | (i64.store32 offset=6 (i32.const 0) (i64.const 0)) 16 | (f32.store offset=7 (i32.const 0) (f32.const 0)) 17 | (f64.store offset=8 (i32.const 0) (f64.const 0)) 18 | 19 | ;; alignment must come after 20 | (i32.store8 offset=0 align=1 (i32.const 0) (i32.const 0)) 21 | (i32.store16 offset=1 align=2 (i32.const 0) (i32.const 0)) 22 | (i32.store offset=2 align=4 (i32.const 0) (i32.const 0)) 23 | (i64.store offset=3 align=8 (i32.const 0) (i64.const 0)) 24 | (i64.store8 offset=4 align=16 (i32.const 0) (i64.const 0)) 25 | (i64.store16 offset=5 align=8 (i32.const 0) (i64.const 0)) 26 | (i64.store32 offset=6 align=4 (i32.const 0) (i64.const 0)) 27 | (f32.store offset=7 align=2 (i32.const 0) (f32.const 0)) 28 | (f64.store offset=8 align=1 (i32.const 0) (f64.const 0)))) 29 | -------------------------------------------------------------------------------- /test/parser/store.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func 9 | (i32.store8 (i32.const 0) (i32.const 0)) 10 | (i32.store16 (i32.const 0) (i32.const 0)) 11 | (i32.store (i32.const 0) (i32.const 0)) 12 | (i64.store (i32.const 0) (i64.const 0)) 13 | (i64.store8 (i32.const 0) (i64.const 0)) 14 | (i64.store16 (i32.const 0) (i64.const 0)) 15 | (i64.store32 (i32.const 0) (i64.const 0)) 16 | (f32.store (i32.const 0) (f32.const 0)) 17 | (f64.store (i32.const 0) (f64.const 0)))) 18 | -------------------------------------------------------------------------------- /test/parser/typeinference.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump -spec-test-script %s > %t1 3 | ;; RUN: sexpr-wasm --spec %t1 > %t2 4 | ;; RUN : sexpr-wasm --spec %s | diff - %t2 ;; TODO: investigate? 5 | ;; Test the type inference 6 | ;; RUN: sexpr_dump -spec-test-script -t %s | FileCheck %s 7 | (module 8 | (import "foo" "bar" (param i64)(result f32)) 9 | ;; Check param expectation, void return expectation 10 | (func (param i32) 11 | (call 0 (i32.const 1))) 12 | ;; CHECK: [void->void](call 0 [i32->i32](i32.const 13 | 14 | ;; Check param expectation, void return expectation, void block-list expectation, 15 | ;; and passthrough last-block-expr expectation 16 | (func (param f64) 17 | (call 1 (block 18 | ;; CHECK: [void->void](call 1 [f64->f64](block 19 | (i32.const 1) 20 | ;; CHECK: [void->i32](i32.const 21 | (nop) 22 | ;; CHECK: [void->void](nop) 23 | (f64.const 2))) 24 | ;; CHECK: [f64->f64](f64.const 25 | (i32.const 1) 26 | ;; CHECK: [void->i32](i32.const 27 | ) 28 | 29 | ;; check that type is passed through blocks/implicit blocks 30 | (func (param f64) (param i32) (result i64) 31 | (local f32) (local i64) (local f64) (local i32) 32 | (get_local 0) 33 | ;; CHECK: [void->f64](get_local 0) 34 | (get_local 1) 35 | ;; CHECK: [void->i32](get_local 1) 36 | (get_local 2) 37 | ;; CHECK: [void->f32](get_local 2) 38 | (get_local 3) 39 | ;; CHECK: [void->i64](get_local 3) 40 | (get_local 4) 41 | ;; CHECK: [void->f64](get_local 4) 42 | (get_local 5) 43 | ;; CHECK: [void->i32](get_local 5) 44 | (get_local 3) 45 | ;; CHECK: [i64->i64](get_local 3) 46 | ) 47 | 48 | ;; check polymorphic return 49 | (func(param f32) (result f32) (return (get_local 0))) 50 | ;; CHECK: [f32->f32](return 51 | ;; CHECK: [f32->f32](get_local 52 | 53 | ;; check set_local 54 | (func (param i64) (param f64) (local f64) 55 | (set_local 1 (block (nop)(i32.const 1)(get_local 2))) 56 | ) 57 | ;; CHECK: [void->f64](set_local 1 58 | ;; CHECK: [f64->f64](block 59 | ;; CHECK: [void->void](nop) 60 | ;; CHECK: [void->i32](i32.const 61 | ;; CHECK: [f64->f64](get_local 2 62 | 63 | ;; call_import 64 | (func (local i64) (call_import 0 (get_local 0))) 65 | ;; CHECK: [void->f32](call_import 66 | ;; CHECK: [i64->i64](get_local 0 67 | (func $foo (param f64) (result f64) (f64.const 2)) 68 | (export "foo" $foo) ;; param f64 69 | 70 | (func (i64.extend_u/i32 (i32.const 0))) 71 | ;; CHECK: i64.extend_u/i32 [i32->i32](i32.const 0 72 | 73 | (func (f64.add (f64.const 4)(f64.const 5))) 74 | ;; CHECK: [f64->f64](f64.const 75 | ) 76 | ;; invoke 77 | (invoke "foo" (f64.const 1.0)) 78 | ;; CHECK: [f64->f64](f64.const 79 | ;; CHECK: assert_return 80 | 81 | (assert_return_nan (invoke "foo" (f64.const 5))) 82 | ;; CHECK: [f64->f64](f64.const 83 | -------------------------------------------------------------------------------- /test/parser/unary.wast: -------------------------------------------------------------------------------- 1 | ;; Test that the binary encoding of the dump matches that of the original 2 | ;; RUN: sexpr_dump %s > %t1 3 | ;; RUN: sexpr-wasm -d %t1 > %t2 4 | ;; RUN: sexpr-wasm -d %s | diff - %t2 5 | ;; Test that round-tripping is stable 6 | ;; RUN: sexpr_dump %t1 | diff %t1 - 7 | (module 8 | (func 9 | (f32.neg (f32.const 0)) 10 | (f64.neg (f64.const 0)) 11 | (f32.abs (f32.const 0)) 12 | (f64.abs (f64.const 0)) 13 | (f32.sqrt (f32.const 0)) 14 | (f64.sqrt (f64.const 0)) 15 | (i32.clz (i32.const 0)) 16 | (i64.clz (i64.const 0)) 17 | (i32.ctz (i32.const 0)) 18 | (i64.ctz (i64.const 0)) 19 | (i32.popcnt (i32.const 0)) 20 | (i64.popcnt (i64.const 0)) 21 | (f32.ceil (f32.const 0)) 22 | (f64.ceil (f64.const 0)) 23 | (f32.floor (f32.const 0)) 24 | (f64.floor (f64.const 0)) 25 | (f32.trunc (f32.const 0)) 26 | (f64.trunc (f64.const 0)) 27 | (f32.nearest (f32.const 0)) 28 | (f64.nearest (f64.const 0)))) 29 | -------------------------------------------------------------------------------- /test/spec/address.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wac.py --spec-test-script %s -o %t1 2 | ;; RUN: %t1 3 | (module 4 | (memory 1024 (segment 0 "abcdefghijklmnopqrstuvwxyz")) 5 | (import $print "stdio" "print" (param i32)) 6 | 7 | (func $good (param $i i32) 8 | (call_import $print (i32.load8_u offset=0 (get_local $i))) ;; 97 'a' 9 | (call_import $print (i32.load8_u offset=1 (get_local $i))) ;; 98 'b' 10 | (call_import $print (i32.load8_u offset=2 (get_local $i))) ;; 99 'c' 11 | (call_import $print (i32.load8_u offset=25 (get_local $i))) ;; 122 'z' 12 | 13 | (call_import $print (i32.load16_u offset=0 (get_local $i))) ;; 25185 'ab' 14 | (call_import $print (i32.load16_u offset=1 align=1 (get_local $i))) ;; 25442 'bc' 15 | (call_import $print (i32.load16_u offset=2 (get_local $i))) ;; 25699 'cd' 16 | (call_import $print (i32.load16_u offset=25 align=1 (get_local $i))) ;; 122 'z\0' 17 | 18 | (call_import $print (i32.load offset=0 (get_local $i))) ;; 1684234849 'abcd' 19 | (call_import $print (i32.load offset=1 align=1 (get_local $i))) ;; 1701077858 'bcde' 20 | (call_import $print (i32.load offset=2 align=2 (get_local $i))) ;; 1717920867 'cdef' 21 | (call_import $print (i32.load offset=25 align=1 (get_local $i))) ;; 122 'z\0\0\0' 22 | ) 23 | (export "good" $good) 24 | 25 | (func $bad2 (param $i i32) (i32.load offset=4294967295 (get_local $i))) 26 | (export "bad2" $bad2) 27 | ) 28 | 29 | (assert_return (invoke "good" (i32.const 0))) 30 | (assert_return (invoke "good" (i32.const 995))) 31 | ;;(assert_trap (invoke "good" (i32.const 996)) "out of bounds memory access") 32 | ;;(assert_trap (invoke "bad2" (i32.const 0)) "out of bounds memory access") 33 | ;;(assert_trap (invoke "bad2" (i32.const 1)) "out of bounds memory access") 34 | 35 | ;;(assert_invalid (module (memory 1024) (func $bad1 (param $i i32) (i32.load offset=4294967296 (get_local $i))) ) "offset too large") 36 | -------------------------------------------------------------------------------- /test/spec/float_literals.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wac.py --spec-test-script %s -o %t1 2 | ;; RUN: %t1 3 | 4 | (module 5 | (func $f32.nan (result i32) (i32.reinterpret/f32 (f32.const nan))) 6 | (func $f32.positive_nan (result i32) (i32.reinterpret/f32 (f32.const +nan))) 7 | (func $f32.negative_nan (result i32) (i32.reinterpret/f32 (f32.const -nan))) 8 | (func $f32.plain_nan (result i32) (i32.reinterpret/f32 (f32.const nan:0x400000))) 9 | (func $f32.informally_known_as_plain_snan (result i32) (i32.reinterpret/f32 (f32.const nan:0x200000))) 10 | (func $f32.allones_nan (result i32) (i32.reinterpret/f32 (f32.const -nan:0x7fffff))) 11 | (func $f32.misc_nan (result i32) (i32.reinterpret/f32 (f32.const nan:0x012345))) 12 | (func $f32.misc_positive_nan (result i32) (i32.reinterpret/f32 (f32.const +nan:0x304050))) 13 | (func $f32.misc_negative_nan (result i32) (i32.reinterpret/f32 (f32.const -nan:0x2abcde))) 14 | (func $f32.infinity (result i32) (i32.reinterpret/f32 (f32.const infinity))) 15 | (func $f32.positive_infinity (result i32) (i32.reinterpret/f32 (f32.const +infinity))) 16 | (func $f32.negative_infinity (result i32) (i32.reinterpret/f32 (f32.const -infinity))) 17 | (func $f32.zero (result i32) (i32.reinterpret/f32 (f32.const 0.0))) 18 | (func $f32.positive_zero (result i32) (i32.reinterpret/f32 (f32.const +0.0))) 19 | (func $f32.negative_zero (result i32) (i32.reinterpret/f32 (f32.const -0.0))) 20 | (func $f32.decimal (result i32) (i32.reinterpret/f32 (f32.const 6.283185482e+00))) 21 | (func $f32.hexadecimal (result i32) (i32.reinterpret/f32 (f32.const 0x1.921fb6p+2))) 22 | (func $f32.min_positive (result i32) (i32.reinterpret/f32 (f32.const 0x1p-149))) 23 | (func $f32.max_finite (result i32) (i32.reinterpret/f32 (f32.const 0x1.fffffep+127))) 24 | (func $f32.trailing_dot (result i32) (i32.reinterpret/f32 (f32.const 0x1.p4))) 25 | (func $f32.max_subnormal (result i32) (i32.reinterpret/f32 (f32.const 1.1754942106924410e-38))) 26 | 27 | (func $f64.nan (result i64) (i64.reinterpret/f64 (f64.const nan))) 28 | (func $f64.positive_nan (result i64) (i64.reinterpret/f64 (f64.const +nan))) 29 | (func $f64.negative_nan (result i64) (i64.reinterpret/f64 (f64.const -nan))) 30 | (func $f64.plain_nan (result i64) (i64.reinterpret/f64 (f64.const nan:0x8000000000000))) 31 | (func $f64.informally_known_as_plain_snan (result i64) (i64.reinterpret/f64 (f64.const nan:0x4000000000000))) 32 | (func $f64.allones_nan (result i64) (i64.reinterpret/f64 (f64.const -nan:0xfffffffffffff))) 33 | (func $f64.misc_nan (result i64) (i64.reinterpret/f64 (f64.const nan:0x0123456789abc))) 34 | (func $f64.misc_positive_nan (result i64) (i64.reinterpret/f64 (f64.const +nan:0x3040506070809))) 35 | (func $f64.misc_negative_nan (result i64) (i64.reinterpret/f64 (f64.const -nan:0x2abcdef012345))) 36 | (func $f64.infinity (result i64) (i64.reinterpret/f64 (f64.const infinity))) 37 | (func $f64.positive_infinity (result i64) (i64.reinterpret/f64 (f64.const +infinity))) 38 | (func $f64.negative_infinity (result i64) (i64.reinterpret/f64 (f64.const -infinity))) 39 | (func $f64.zero (result i64) (i64.reinterpret/f64 (f64.const 0.0))) 40 | (func $f64.positive_zero (result i64) (i64.reinterpret/f64 (f64.const +0.0))) 41 | (func $f64.negative_zero (result i64) (i64.reinterpret/f64 (f64.const -0.0))) 42 | (func $f64.decimal (result i64) (i64.reinterpret/f64 (f64.const 6.28318530717958623))) 43 | (func $f64.hexadecimal (result i64) (i64.reinterpret/f64 (f64.const 0x1.921fb54442d18p+2))) 44 | (func $f64.min_positive (result i64) (i64.reinterpret/f64 (f64.const 0x0.0000000000001p-1022))) 45 | (func $f64.max_finite (result i64) (i64.reinterpret/f64 (f64.const 0x1.fffffffffffffp+1023))) 46 | (func $f64.trailing_dot (result i64) (i64.reinterpret/f64 (f64.const 0x1.p4))) 47 | (func $f64.max_subnormal (result i64) (i64.reinterpret/f64 (f64.const 2.2250738585072011e-308))) 48 | (export "f32.nan" $f32.nan) 49 | (export "f32.positive_nan" $f32.positive_nan) 50 | (export "f32.negative_nan" $f32.negative_nan) 51 | (export "f32.plain_nan" $f32.plain_nan) 52 | (export "f32.informally_known_as_plain_snan" $f32.informally_known_as_plain_snan) 53 | (export "f32.allones_nan" $f32.allones_nan) 54 | (export "f32.misc_nan" $f32.misc_nan) 55 | (export "f32.misc_positive_nan" $f32.misc_positive_nan) 56 | (export "f32.misc_negative_nan" $f32.misc_negative_nan) 57 | (export "f32.infinity" $f32.infinity) 58 | (export "f32.positive_infinity" $f32.positive_infinity) 59 | (export "f32.negative_infinity" $f32.negative_infinity) 60 | (export "f32.zero" $f32.zero) 61 | (export "f32.positive_zero" $f32.positive_zero) 62 | (export "f32.negative_zero" $f32.negative_zero) 63 | (export "f32.decimal" $f32.decimal) 64 | (export "f32.hexadecimal" $f32.hexadecimal) 65 | (export "f32.min_positive" $f32.min_positive) 66 | (export "f32.max_finite" $f32.max_finite) 67 | (export "f32.trailing_dot" $f32.trailing_dot) 68 | (export "f32.max_subnormal" $f32.max_subnormal) 69 | 70 | (export "f64.nan" $f64.nan) 71 | (export "f64.positive_nan" $f64.positive_nan) 72 | (export "f64.negative_nan" $f64.negative_nan) 73 | (export "f64.plain_nan" $f64.plain_nan) 74 | (export "f64.informally_known_as_plain_snan" $f64.informally_known_as_plain_snan) 75 | (export "f64.allones_nan" $f64.allones_nan) 76 | (export "f64.misc_nan" $f64.misc_nan) 77 | (export "f64.misc_positive_nan" $f64.misc_positive_nan) 78 | (export "f64.misc_negative_nan" $f64.misc_negative_nan) 79 | (export "f64.infinity" $f64.infinity) 80 | (export "f64.positive_infinity" $f64.positive_infinity) 81 | (export "f64.negative_infinity" $f64.negative_infinity) 82 | (export "f64.zero" $f64.zero) 83 | (export "f64.positive_zero" $f64.positive_zero) 84 | (export "f64.negative_zero" $f64.negative_zero) 85 | (export "f64.decimal" $f64.decimal) 86 | (export "f64.hexadecimal" $f64.hexadecimal) 87 | (export "f64.min_positive" $f64.min_positive) 88 | (export "f64.max_finite" $f64.max_finite) 89 | (export "f64.trailing_dot" $f64.trailing_dot) 90 | (export "f64.max_subnormal" $f64.max_subnormal) 91 | ) 92 | 93 | (assert_return (invoke "f32.nan") (i32.const 0x7fc00000)) 94 | (assert_return (invoke "f32.positive_nan") (i32.const 0x7fc00000)) 95 | (assert_return (invoke "f32.negative_nan") (i32.const 0xffc00000)) 96 | (assert_return (invoke "f32.plain_nan") (i32.const 0x7fc00000)) 97 | ;; These are broken. presumably we don't handle NaNs properly 98 | ;;(assert_return (invoke "f32.informally_known_as_plain_snan") (i32.const 0x7fa00000)) 99 | (assert_return (invoke "f32.allones_nan") (i32.const 0xffffffff)) 100 | ;;(assert_return (invoke "f32.misc_nan") (i32.const 0x7f812345)) 101 | ;;(assert_return (invoke "f32.misc_positive_nan") (i32.const 0x7fb04050)) 102 | ;;(assert_return (invoke "f32.misc_negative_nan") (i32.const 0xffaabcde)) 103 | (assert_return (invoke "f32.infinity") (i32.const 0x7f800000)) 104 | (assert_return (invoke "f32.positive_infinity") (i32.const 0x7f800000)) 105 | (assert_return (invoke "f32.negative_infinity") (i32.const 0xff800000)) 106 | (assert_return (invoke "f32.zero") (i32.const 0)) 107 | (assert_return (invoke "f32.positive_zero") (i32.const 0)) 108 | (assert_return (invoke "f32.negative_zero") (i32.const 0x80000000)) 109 | (assert_return (invoke "f32.decimal") (i32.const 0x40c90fdb)) 110 | (assert_return (invoke "f32.hexadecimal") (i32.const 0x40c90fdb)) 111 | (assert_return (invoke "f32.min_positive") (i32.const 1)) 112 | (assert_return (invoke "f32.max_finite") (i32.const 0x7f7fffff)) 113 | (assert_return (invoke "f32.trailing_dot") (i32.const 0x41800000)) 114 | (assert_return (invoke "f32.max_subnormal") (i32.const 0x7fffff)) 115 | 116 | (assert_return (invoke "f64.nan") (i64.const 0x7ff8000000000000)) 117 | (assert_return (invoke "f64.positive_nan") (i64.const 0x7ff8000000000000)) 118 | (assert_return (invoke "f64.negative_nan") (i64.const 0xfff8000000000000)) 119 | (assert_return (invoke "f64.plain_nan") (i64.const 0x7ff8000000000000)) 120 | (assert_return (invoke "f64.informally_known_as_plain_snan") (i64.const 0x7ff4000000000000)) 121 | (assert_return (invoke "f64.allones_nan") (i64.const 0xffffffffffffffff)) 122 | (assert_return (invoke "f64.misc_nan") (i64.const 0x7ff0123456789abc)) 123 | (assert_return (invoke "f64.misc_positive_nan") (i64.const 0x7ff3040506070809)) 124 | (assert_return (invoke "f64.misc_negative_nan") (i64.const 0xfff2abcdef012345)) 125 | (assert_return (invoke "f64.infinity") (i64.const 0x7ff0000000000000)) 126 | (assert_return (invoke "f64.positive_infinity") (i64.const 0x7ff0000000000000)) 127 | (assert_return (invoke "f64.negative_infinity") (i64.const 0xfff0000000000000)) 128 | (assert_return (invoke "f64.zero") (i64.const 0)) 129 | (assert_return (invoke "f64.positive_zero") (i64.const 0)) 130 | (assert_return (invoke "f64.negative_zero") (i64.const 0x8000000000000000)) 131 | (assert_return (invoke "f64.decimal") (i64.const 0x401921fb54442d18)) 132 | (assert_return (invoke "f64.hexadecimal") (i64.const 0x401921fb54442d18)) 133 | (assert_return (invoke "f64.min_positive") (i64.const 1)) 134 | (assert_return (invoke "f64.max_finite") (i64.const 0x7fefffffffffffff)) 135 | (assert_return (invoke "f64.trailing_dot") (i64.const 0x4030000000000000)) 136 | (assert_return (invoke "f64.max_subnormal") (i64.const 0xfffffffffffff)) 137 | -------------------------------------------------------------------------------- /test/spec/functions.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wac.py --spec-test-script %s -o %t1 2 | ;; RUN: %t1 3 | (module 4 | (func $return-none (i32.const 1)) 5 | (export "return-none" $return-none) 6 | ) 7 | 8 | (assert_return (invoke "return-none")) 9 | -------------------------------------------------------------------------------- /test/spec/int_exprs.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wac.py --spec-test-script %s -o %t1 2 | ;; RUN: %t1 3 | ;; Test that x+1>n is not folded to x 48 | 49 | (module 50 | (func $i32.no_fold_shl_shr_s (param $x i32) (result i32) 51 | (i32.shr_s (i32.shl (get_local $x) (i32.const 1)) (i32.const 1))) 52 | (export "i32.no_fold_shl_shr_s" $i32.no_fold_shl_shr_s) 53 | (func $i32.no_fold_shl_shr_u (param $x i32) (result i32) 54 | (i32.shr_u (i32.shl (get_local $x) (i32.const 1)) (i32.const 1))) 55 | (export "i32.no_fold_shl_shr_u" $i32.no_fold_shl_shr_u) 56 | 57 | (func $i64.no_fold_shl_shr_s (param $x i64) (result i64) 58 | (i64.shr_s (i64.shl (get_local $x) (i64.const 1)) (i64.const 1))) 59 | (export "i64.no_fold_shl_shr_s" $i64.no_fold_shl_shr_s) 60 | (func $i64.no_fold_shl_shr_u (param $x i64) (result i64) 61 | (i64.shr_u (i64.shl (get_local $x) (i64.const 1)) (i64.const 1))) 62 | (export "i64.no_fold_shl_shr_u" $i64.no_fold_shl_shr_u) 63 | ) 64 | 65 | (assert_return (invoke "i32.no_fold_shl_shr_s" (i32.const 0x80000000)) (i32.const 0)) 66 | (assert_return (invoke "i32.no_fold_shl_shr_u" (i32.const 0x80000000)) (i32.const 0)) 67 | (assert_return (invoke "i64.no_fold_shl_shr_s" (i64.const 0x8000000000000000)) (i64.const 0)) 68 | (assert_return (invoke "i64.no_fold_shl_shr_u" (i64.const 0x8000000000000000)) (i64.const 0)) 69 | 70 | ;; Test that x>>n<&1 | FileCheck %s 3 | 4 | (module 5 | (func $foo (result i32) (i32.const 0)) 6 | (export "foo" $foo) 7 | 8 | (func $bar (param f32) (result f32) (f32.const 1.0)) 9 | (export "bar" $bar) 10 | (func $baz (param i64) (result i64) (get_local 0)) 11 | (export "baz" $baz) 12 | (func $quux (param f64) (result f64) (get_local 0)) 13 | (export "quux" $quux) 14 | ) 15 | 16 | (assert_return (invoke "foo") (i32.const 0)) 17 | 18 | ;; Compares to 2, gets 1. 19 | (assert_return (invoke "bar" (f32.const 0)) (f32.const 2)) 20 | ;; CHECK: failure in assert_return on line 19: expected 2.000000, got 1 21 | 22 | (assert_return (invoke "bar" (f32.const 3)) (f32.const 6)) 23 | ;; CHECK: failure in assert_return on line 22: expected 6.000000, got 1 24 | 25 | (assert_return (invoke "baz" (i64.const 123456789)) (i64.const 123456789)) 26 | ;; Should succeed 27 | (assert_return (invoke "baz" (i64.const 123456789)) (i64.const 1234567890)) 28 | ;; CHECK: failure in assert_return on line 27: expected 1234567890, got 123456789 29 | 30 | (assert_return (invoke "quux" (f64.const 0.123456789)) (f64.const 0.123456789)) 31 | ;; Should succeed 32 | (assert_return (invoke "quux" (f64.const 0x1.123456789p-7)) (f64.const 0x1.091a2b3c48p-6)) 33 | ;; CHECK: failure in assert_return on line 32: expected 0x1.091a2b3c48p-6, got 0x1.123456789p-7 34 | -------------------------------------------------------------------------------- /test/system/assertreturn.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wac.py --spec-test-script %s -o %t1 2 | ;; RUN: %t1 3 | (module 4 | (func $foo (result i32) (i32.const 0)) 5 | (export "foo" $foo) 6 | (func $bar (param f32) (result f32) (get_local 0)) 7 | (export "bar" $bar) 8 | (func $ret_arg (param f64) (result f64)(get_local 0)) 9 | (export "ret_arg" $ret_arg) 10 | ) 11 | 12 | (assert_return (invoke "foo") (i32.const 0)) 13 | 14 | (assert_return (invoke "bar" (f32.const 0)) (f32.const 0)) 15 | 16 | (assert_return_nan (invoke "ret_arg" (f64.const nan))) 17 | (assert_return_nan (invoke "ret_arg" (f64.const -nan))) 18 | (assert_return_nan (invoke "bar" (f32.const -nan))) 19 | -------------------------------------------------------------------------------- /test/system/assertreturnnan-fail.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wac.py --spec-test-script %s -o %t1 2 | ;; RUN: not %t1 2>&1 | FileCheck %s 3 | 4 | (module 5 | (func $foo (result i32) (i32.const 0)) 6 | (export "foo" $foo) 7 | (func $bar (param f32) (result f32) (get_local 0)) 8 | (export "bar" $bar) 9 | (func $ret_arg (param f64) (result f64)(get_local 0)) 10 | (export "ret_arg" $ret_arg) 11 | ) 12 | 13 | (assert_return (invoke "foo") (i32.const 0)) 14 | 15 | (assert_return (invoke "bar" (f32.const 0)) (f32.const 1)) 16 | 17 | (assert_return_nan (invoke "ret_arg" (f64.const 0))) 18 | ;; CHECK: Assertion failure in assert_return_nan on line 17: expected NaN, got 0 19 | -------------------------------------------------------------------------------- /test/system/if.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wac.py --spec-test-script %s -o %t1 2 | ;; RUN: %t1 3 | (module 4 | (func $noelse (param i32) (param i32) (result i32) 5 | (if (get_local 0) (return (get_local 0))) 6 | (get_local 1)) 7 | (export "noelse" $noelse) 8 | 9 | (func $returns (param i32) (result i32) 10 | (if_else (get_local 0) 11 | (return (i32.const 2)) 12 | (return (i32.const 3)))) 13 | (export "returns" $returns) 14 | 15 | ;; This test previously disabled because of parser, now because of codgen. 16 | ;; (func (result i32) 17 | ;; (if_else (i32.const 2) 18 | ;; (return (i32.const 1)) (i32.const 3))) 19 | 20 | (func $getlocals (param i32) (param i64) (param i64) (result i64) (return 21 | (if_else (get_local 0) (get_local 1)(get_local 2)))) 22 | (export "getlocals" $getlocals) 23 | 24 | (func $block (param f32) (param f32) (param i32) (result f32) 25 | (block 26 | (i64.const 2) 27 | (nop) 28 | (if_else (i32.const 0) (f64.const 1)(f64.const 2)) 29 | (if_else (get_local 2) (get_local 1) (get_local 0)) 30 | ) 31 | ) 32 | (export "block" $block) 33 | 34 | (func $nested (param i32) (param i32) (result i64) 35 | (if (get_local 0) 36 | (if_else (get_local 1) (return (i64.const 1)) (return (i64.const 2))) 37 | ) 38 | (return (i64.const 3)) 39 | ) 40 | (export "nested" $nested) 41 | ) 42 | 43 | (assert_return (invoke "noelse" (i32.const -1) (i32.const 3)) (i32.const -1)) 44 | (assert_return (invoke "noelse" (i32.const 1) (i32.const 55)) (i32.const 1)) 45 | (assert_return (invoke "noelse" (i32.const 0) (i32.const 55)) (i32.const 55)) 46 | 47 | (assert_return (invoke "returns" (i32.const 1)) (i32.const 2)) 48 | (assert_return (invoke "returns" (i32.const 0)) (i32.const 3)) 49 | 50 | (assert_return (invoke "getlocals" (i32.const 1) (i64.const 55) (i64.const 2)) (i64.const 55)) 51 | (assert_return (invoke "getlocals" (i32.const 0) (i64.const 55) (i64.const 2)) (i64.const 2)) 52 | 53 | (assert_return (invoke "block" (f32.const 3)(f32.const 4)(i32.const 1)) (f32.const 4)) 54 | (assert_return (invoke "block" (f32.const 3)(f32.const 4)(i32.const 0)) (f32.const 3)) 55 | 56 | (assert_return (invoke "nested" (i32.const 0) (i32.const 0)) (i64.const 3)) 57 | (assert_return (invoke "nested" (i32.const 1) (i32.const 0)) (i64.const 2)) 58 | (assert_return (invoke "nested" (i32.const 0) (i32.const 1)) (i64.const 3)) 59 | (assert_return (invoke "nested" (i32.const 1) (i32.const 1)) (i64.const 1)) 60 | -------------------------------------------------------------------------------- /test/system/invoke.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wac.py --spec-test-script %s -o %t1 2 | ;; RUN: %t1 | FileCheck %s 3 | (module 4 | (export "test" $test) 5 | ;; TODO: make this call print with its argument 6 | (func $test (param i32) (call_import 0 (i32.const 3))) 7 | (import "stdio" "print" (param i32))) 8 | 9 | (invoke "test" (i32.const 1)) 10 | (invoke "test" (i32.const 100)) 11 | (invoke "test" (i32.const -30)) 12 | 13 | ;; CHECK: 3 14 | -------------------------------------------------------------------------------- /test/system/start-wrong-signature.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: not wac.py %s -o %t1 2>&1 2 | (module 3 | (func $m (result i32) (return (i32.const 1))) 4 | (export "_start" 0) 5 | (import "stdio" "print" (param i32)) 6 | ) 7 | 8 | ;; CHECK: export is not of type void ()* -------------------------------------------------------------------------------- /test/system/start.wast: -------------------------------------------------------------------------------- 1 | ;; RUN: wac.py %s -o %t1 2 | ;; RUN: %t1 | FileCheck %s 3 | (module 4 | (func $main (call_import 0 (i32.const 3))) 5 | (export "_start" 0) 6 | (import "stdio" "print" (param i32)) 7 | ) 8 | 9 | ;; CHECK: 3 --------------------------------------------------------------------------------