├── .circleci └── config.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── error.go ├── examples ├── boa │ ├── boa.go │ ├── boa.wasm │ └── boa_test.go ├── cstring │ ├── cstring.go │ ├── cstring │ │ ├── build.sh │ │ ├── cstring.c │ │ └── cstring.wasm │ └── cstring_test.go ├── libxml │ ├── README.md │ ├── bad_input.xml │ ├── input.xml │ ├── input.xsd │ ├── libxml.go │ ├── libxml2.wasm │ └── libxml_test.go └── sum │ ├── sum.go │ ├── sum.wasm │ └── sum_test.go ├── go-wasm3.h ├── include ├── m3_api_defs.h ├── m3_api_libc.h ├── m3_api_tracer.h ├── m3_api_wasi.h ├── m3_bind.h ├── m3_code.h ├── m3_compile.h ├── m3_config.h ├── m3_config_platforms.h ├── m3_core.h ├── m3_emit.h ├── m3_env.h ├── m3_exception.h ├── m3_exec.h ├── m3_exec_defs.h ├── m3_info.h ├── m3_math_utils.h └── wasm3.h ├── lib ├── darwin │ └── libm3.a └── linux │ └── libm3.a ├── wasm3.go └── wasm3_test.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | # specify the version 6 | - image: circleci/golang:1.12 7 | 8 | # Specify service dependencies here if necessary 9 | # CircleCI maintains a library of pre-built images 10 | # documented at https://circleci.com/docs/2.0/circleci-images/ 11 | # - image: circleci/postgres:9.4 12 | 13 | #### /go/src/github.com/circleci/go-tool 14 | #### /go/src/bitbucket.org/circleci/go-tool 15 | working_directory: /go/src/github.com/matiasinsaurralde/go-wasm3 16 | steps: 17 | - checkout 18 | 19 | # specify any bash command here prefixed with `run: ` 20 | - run: go get -v -t -d ./... 21 | - run: go test -v ./... 22 | -------------------------------------------------------------------------------- /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 | matias@insaurral.de. 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 | . 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 | . Translations are available at 128 | . 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Welcome to go-wasm3 contributing guide 2 | 3 | Thank you for investing your time in contributing to our project! :sparkles:. 4 | 5 | Read our [Code of Conduct](./CODE_OF_CONDUCT.md) to keep our community approachable and respectable. 6 | 7 | In this guide you will get an overview of the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR. 8 | 9 | Use the table of contents button at the top left corner of this document to get to a specific section of this guide quickly. 10 | 11 | ## New contributor guide 12 | 13 | To get an overview of the project, read the [README](README.md). Here are some resources to help you get started with open source contributions: 14 | 15 | - [Finding ways to contribute to open source on GitHub](https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github) 16 | - [Set up Git](https://docs.github.com/en/get-started/quickstart/set-up-git) 17 | - [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow) 18 | - [Collaborating with pull requests](https://docs.github.com/en/github/collaborating-with-pull-requests) 19 | 20 | ## Getting started 21 | 22 | ### Issues 23 | 24 | #### Create a new issue 25 | 26 | If you spot a problem with the project, [search if an issue already exists](https://github.com/matiasinsaurralde/go-wasm3/issues). If a related issue doesn't exist, you can open a new issue. 27 | 28 | Opportunities to create and/or solve issues may stem from the basics outlined in [Github's Community Health guidelines](https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/creating-a-default-community-health-file), such as adding **Issue templates** and **Pull request templates**. 29 | 30 | #### Solve an issue 31 | 32 | Scan through our [existing issues](https://github.com/github/docs/issues) to find one that interests you. You can narrow down the search using `labels` as filters. See [Labels](/contributing/how-to-use-labels.md) for more information. As a general rule, we don’t assign issues to anyone. If you find an issue to work on, you are welcome to open a PR with a fix. 33 | 34 | ### Make Changes 35 | 36 | #### Make changes in the UI 37 | 38 | Click **Make a contribution** at the bottom of any docs page to make small changes such as a typo, sentence fix, or a broken link. This takes you to the `.md` file where you can make your changes and [create a pull request](#pull-request) for a review. 39 | 40 | #### Make changes in GitHub Codespace 41 | 42 | For more information about using GitHub Codespace, see "[Working in a codespace](https://github.com/github/docs/blob/main/contributing/codespace.md)." 43 | 44 | #### Make changes locally 45 | 46 | 1. Fork the repository. 47 | 48 | [Fork the repo](https://docs.github.com/en/get-started/quickstart/fork-a-repo) so that you can make your changes without affecting the original project until you're ready to merge them. 49 | 50 | 2. Create a working branch and start with your changes! 51 | 52 | ### Commit your update 53 | 54 | Commit the changes once you are happy with them. See [Atom's contributing guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#git-commit-messages) to know how to use emoji for commit messages. 55 | 56 | Once your changes are ready, don't forget to [self-review](https://github.com/github/docs/blob/main/contributing/self-review.md) to speed up the review process :zap:. 57 | 58 | ### Pull Request 59 | 60 | When you're finished with the changes, create a pull request, also known as a PR. 61 | 62 | 63 | - Don't forget to [link PR to issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) if you are solving one. 64 | - Enable the checkbox to [allow maintainer edits](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork) so the branch can be updated for a merge. 65 | Once you submit your PR, a team member with"maintainer" privileges will review your proposal. We may ask questions or request for additional information. 66 | - We may ask for changes to be made before a PR can be merged, either using [suggested changes](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request) or pull request comments. You can apply suggested changes directly through the UI. You can make any other changes in your fork, then commit them to your branch. 67 | - As you update your PR and apply changes, mark each conversation as [resolved](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/commenting-on-a-pull-request#resolving-conversations). 68 | - If you run into any merge issues, checkout this [git tutorial](https://github.com/skills/resolve-merge-conflicts) to help you resolve merge conflicts and other issues. 69 | 70 | ### Your PR is merged 71 | 72 | Congratulations :tada::tada: The go-wasm3 team thanks you :sparkles:. 73 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Matias Insaurralde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | go-wasm3 2 | == 3 | 4 | [![GoDoc](https://godoc.org/github.com/matiasinsaurralde/go-wasm3?status.svg)](https://godoc.org/github.com/matiasinsaurralde/go-wasm3) 5 | [![Build status](https://circleci.com/gh/matiasinsaurralde/go-wasm3/tree/master.svg?style=shield)](https://circleci.com/gh/matiasinsaurralde/go-wasm3/tree/master) 6 | 7 | Golang wrapper for [WASM3](https://github.com/wasm3/wasm3), WIP. 8 | 9 | This is part of a series of WASM-related experiments: [go-wavm](https://github.com/matiasinsaurralde/go-wavm) and [go-wasm-benchmark](https://github.com/matiasinsaurralde/go-wasm-benchmark). 10 | 11 | ## Install/build 12 | 13 | This package ships with pre-built [`WASM3`](https://github.com/wasm3/wasm3) libraries (static builds) for OS X and Linux. If you want to hack around it, check [the original repository](https://github.com/wasm3/wasm3). 14 | 15 | If you're using one of the mentioned platforms, you may install the package using `go get`: 16 | 17 | ``` 18 | $ go get -u github.com/matiasinsaurralde/go-wasm3 19 | ``` 20 | 21 | To inspect or run the little sample use: 22 | 23 | ``` 24 | $ cd $GOPATH/src/github.com/matiasinsaurralde/go-wasm3/examples/sum 25 | $ go build # or "go run sum.go" 26 | $ ./sum 27 | ``` 28 | 29 | The output will look as follows: 30 | 31 | ``` 32 | 2020/01/15 09:51:24 Initializing WASM3 33 | 2020/01/15 09:51:24 Runtime ok 34 | 2020/01/15 09:51:24 Read WASM module (139 bytes) 35 | 2020/01/15 09:51:24 Module loaded 36 | 2020/01/15 09:51:24 Calling function 37 | Result: 3 38 | Result: 4 39 | ``` 40 | 41 | You will find additional sample projects in the next section. 42 | 43 | ## Sample projects 44 | 45 | ### boa 46 | 47 | This program uses the [boa](https://github.com/jasonwilliams/boa) engine to evaluate JS code. Boa is an embeddable JS engine written in Rust, for this sample it was compiled targeting WASM. 48 | 49 | Link [here](https://github.com/matiasinsaurralde/go-wasm3/tree/master/examples/boa). 50 | 51 | ### libxml 52 | 53 | This program loads [libxml2](https://github.com/GNOME/libxml2) as a WASM module (it's a custom build, full instructions [here](https://github.com/matiasinsaurralde/wasm-libxml2)). The library is used to validate a XML file against a XSD (both loaded from the Go side). 54 | 55 | Link [here](https://github.com/matiasinsaurralde/go-wasm3/tree/master/examples/libxml). 56 | 57 | 58 | ## Memory access 59 | 60 | Take the following sample program: 61 | 62 | ```c 63 | #include 64 | #include 65 | 66 | char* somecall() { 67 | // Allocate a few bytes on the heap: 68 | char* test = (char*) malloc(12*sizeof(char)); 69 | 70 | // Copy a string into the previously defined address: 71 | strcpy(test, "testingonly"); 72 | 73 | // Return the pointer: 74 | return test; 75 | } 76 | ``` 77 | 78 | Build it using `wasicc`, this will generate a `cstring.wasm` file (WASM module): 79 | 80 | ``` 81 | wasicc cstring.c -Wl,--export-all -o cstring.wasm 82 | ``` 83 | 84 | The following Go code will load the WASM module and retrieve the data after calling `somecall`: 85 | 86 | ```go 87 | // Initialize the runtime and load the module: 88 | env := wasm3.NewEnvironment() 89 | defer env.Destroy() 90 | runtime := wasm3.NewRuntime(env, 64*1024) 91 | defer runtime.Destroy() 92 | wasmBytes, err := ioutil.ReadFile("program.wasm") 93 | module, _ := env.ParseModule(wasmBytes) 94 | runtime.LoadModule(module) 95 | fn, _ := runtime.FindFunction(fnName) 96 | 97 | // Call somecall and get the pointer to our data: 98 | result := fn() 99 | 100 | // Reconstruct the string from memory: 101 | memoryLength = runtime.GetAllocatedMemoryLength() 102 | mem := runtime.GetMemory(memoryLength, 0) 103 | 104 | // Initialize a Go buffer: 105 | buf := new(bytes.Buffer) 106 | for n := 0; n < memoryLength; n++ { 107 | if n < result { 108 | continue 109 | } 110 | value := mem[n] 111 | if value == 0 { 112 | break 113 | } 114 | buf.WriteByte(value) 115 | } 116 | 117 | // Print the string: "testingonly" 118 | str := buf.String() 119 | fmt.Println(str) 120 | ``` 121 | 122 | For more details check [this](https://github.com/matiasinsaurralde/go-wasm3/tree/master/examples/cstring). 123 | 124 | ## Limitations and future 125 | 126 | This is a WIP. Stay tuned! 127 | 128 | ## Related projects 129 | 130 | A Rust wrapper is available [here](https://github.com/Veykril/wasm3-rs). 131 | 132 | ## License 133 | 134 | [MIT](https://github.com/matiasinsaurralde/go-wasm3/blob/master/LICENSE). 135 | 136 | [wasm3](https://github.com/wasm3/wasm3/blob/master/LICENSE) is also under this license. 137 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | package wasm3 2 | 3 | /* 4 | #include "go-wasm3.h" 5 | */ 6 | import "C" 7 | 8 | var( 9 | lastError string 10 | ) 11 | 12 | //export set_error 13 | func set_error(str ResultT) { 14 | lastError = C.GoString(str) 15 | } 16 | 17 | // LastErrorString returns the last runtime error 18 | func LastErrorString() string { 19 | return lastError 20 | } 21 | -------------------------------------------------------------------------------- /examples/boa/boa.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | golog "log" 7 | 8 | wasm3 "github.com/matiasinsaurralde/go-wasm3" 9 | ) 10 | 11 | var ( 12 | allocateFn wasm3.FunctionWrapper 13 | execFn wasm3.FunctionWrapper 14 | runtime *wasm3.Runtime 15 | print = golog.Print 16 | printf = golog.Printf 17 | ) 18 | 19 | const ( 20 | wasmFilename = "boa.wasm" 21 | ) 22 | 23 | func initRuntimeAndModule() error { 24 | runtime = wasm3.NewRuntime(&wasm3.Config{ 25 | Environment: wasm3.NewEnvironment(), 26 | StackSize: 1024 * 1024, 27 | }) 28 | 29 | wasmBytes, err := ioutil.ReadFile(wasmFilename) 30 | if err != nil { 31 | return err 32 | } 33 | 34 | module, err := runtime.ParseModule(wasmBytes) 35 | if err != nil { 36 | return err 37 | } 38 | _, err = runtime.LoadModule(module) 39 | if err != nil { 40 | return err 41 | } 42 | return nil 43 | } 44 | 45 | func mapCalls() error { 46 | var err error 47 | allocateFn, err = runtime.FindFunction("boa_alloc") 48 | if err != nil { 49 | return err 50 | } 51 | 52 | execFn, err = runtime.FindFunction("boa_exec3") 53 | if err != nil { 54 | return err 55 | } 56 | return nil 57 | } 58 | 59 | func allocate(input string) (int, error) { 60 | ptr, err := allocateFn(len(input)) 61 | if err != nil { 62 | return 0, nil 63 | } 64 | pos := ptr 65 | for _, ch := range input { 66 | runtime.Memory()[pos] = byte(ch) 67 | pos++ 68 | } 69 | return ptr, nil 70 | } 71 | 72 | func exec(ptr, length int) (string, error) { 73 | outPtr, err := execFn(ptr, length) 74 | if err != nil { 75 | return "", err 76 | } 77 | printf("\"boa_exec3\" returned, output pointer is %d\n", outPtr) 78 | buf := new(bytes.Buffer) 79 | for { 80 | ch := runtime.Memory()[outPtr] 81 | if ch == 0 { 82 | break 83 | } 84 | buf.WriteByte(ch) 85 | outPtr++ 86 | } 87 | printf("Read %d bytes from WASM memory, starting in %d\n", buf.Len(), outPtr) 88 | return buf.String(), nil 89 | } 90 | 91 | func main() { 92 | err := initRuntimeAndModule() 93 | if err != nil { 94 | panic(err) 95 | } 96 | defer runtime.Destroy() 97 | err = mapCalls() 98 | if err != nil { 99 | panic(err) 100 | } 101 | jsInput := "var s=\"test\"; typeof(s)" 102 | inputLength := len(jsInput) 103 | printf(("JS Input is: %s\n"), jsInput) 104 | 105 | ptr, err := allocate(jsInput) 106 | printf("Allocated %d bytes in WASM memory (\"boa_alloc\"), pointer is %d\n", inputLength, ptr) 107 | 108 | printf("Calling \"boa_exec3\" with arguments: (ptr=%d, length=%d)\n", ptr, inputLength) 109 | out, err := exec(ptr, inputLength) 110 | if err != nil { 111 | panic(err) 112 | } 113 | printf("JS output is: %s\n", out) 114 | } 115 | -------------------------------------------------------------------------------- /examples/boa/boa.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matiasinsaurralde/go-wasm3/94c5ed3e86a3281e423b7c1d4ea2e577bc0b7324/examples/boa/boa.wasm -------------------------------------------------------------------------------- /examples/boa/boa_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func init() { 8 | print = func(...interface{}) {} 9 | printf = func(string, ...interface{}) {} 10 | err := initRuntimeAndModule() 11 | if err != nil { 12 | panic(err) 13 | } 14 | err = mapCalls() 15 | if err != nil { 16 | panic(err) 17 | } 18 | } 19 | 20 | func boaCall(t testing.TB) { 21 | jsInput := "var s=\"test\"; typeof(s)" 22 | inputLength := len(jsInput) 23 | 24 | ptr, err := allocate(jsInput) 25 | out, err := exec(ptr, inputLength) 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | if out != "string" { 30 | t.Fatalf("Unexpected output: %s", out) 31 | } 32 | } 33 | 34 | func TestBoaCall(t *testing.T) { 35 | boaCall(t) 36 | } 37 | 38 | func BenchmarkBoaCall(b *testing.B) { 39 | for n := 0; n < b.N; n++ { 40 | boaCall(b) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/cstring/cstring.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "log" 7 | 8 | wasm3 "github.com/matiasinsaurralde/go-wasm3" 9 | ) 10 | 11 | const ( 12 | wasmFilename = "cstring/cstring.wasm" 13 | fnName = "somecall" 14 | ) 15 | 16 | func main() { 17 | log.Print("Initializing WASM3") 18 | runtime := wasm3.NewRuntime(&wasm3.Config{ 19 | Environment: wasm3.NewEnvironment(), 20 | StackSize: 64 * 1024, 21 | }) 22 | defer runtime.Destroy() 23 | log.Println("Runtime ok") 24 | 25 | wasmBytes, err := ioutil.ReadFile(wasmFilename) 26 | if err != nil { 27 | panic(err) 28 | } 29 | log.Printf("Read WASM module (%d bytes)\n", len(wasmBytes)) 30 | 31 | module, err := runtime.ParseModule(wasmBytes) 32 | if err != nil { 33 | panic(err) 34 | } 35 | module, err = runtime.LoadModule(module) 36 | if err != nil { 37 | panic(err) 38 | } 39 | log.Print("Loaded module") 40 | fn, err := runtime.FindFunction(fnName) 41 | if err != nil { 42 | panic(err) 43 | } 44 | log.Printf("Found '%s' function (using runtime.FindFunction)", fnName) 45 | memoryLength := runtime.GetAllocatedMemoryLength() 46 | log.Printf("Allocated memory (before function call) is: %d\n", memoryLength) 47 | result, _ := fn() 48 | memoryLength = runtime.GetAllocatedMemoryLength() 49 | log.Printf("Allocated memory (after function call) is: %d\n", memoryLength) 50 | 51 | // Reconstruct the string from memory: 52 | mem := runtime.Memory() 53 | buf := new(bytes.Buffer) 54 | for n := 0; n < memoryLength; n++ { 55 | if n < result { 56 | continue 57 | } 58 | value := mem[n] 59 | if value == 0 { 60 | break 61 | } 62 | buf.WriteByte(value) 63 | } 64 | log.Printf("Buffer length is: %d\n", buf.Len()) 65 | log.Printf("Buffer contains: %s\n", buf.String()) 66 | } 67 | -------------------------------------------------------------------------------- /examples/cstring/cstring/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | wasicc cstring.c -Wl,--export-all,--allow-undefined -o cstring.wasm 3 | -------------------------------------------------------------------------------- /examples/cstring/cstring/cstring.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | char* allocate(int n) { 5 | char* ptr = (char*) malloc(n*sizeof(char)); 6 | return ptr; 7 | } 8 | 9 | char* somecall() { 10 | char* test = (char*) malloc(12*sizeof(char)); 11 | strcpy(test, "testingonly"); 12 | return test; 13 | }; 14 | 15 | -------------------------------------------------------------------------------- /examples/cstring/cstring/cstring.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matiasinsaurralde/go-wasm3/94c5ed3e86a3281e423b7c1d4ea2e577bc0b7324/examples/cstring/cstring/cstring.wasm -------------------------------------------------------------------------------- /examples/cstring/cstring_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "testing" 7 | 8 | wasm3 "github.com/matiasinsaurralde/go-wasm3" 9 | ) 10 | 11 | var ( 12 | wasmBytes []byte 13 | ) 14 | 15 | func init() { 16 | var err error 17 | wasmBytes, err = ioutil.ReadFile(wasmFilename) 18 | if err != nil { 19 | panic(err) 20 | } 21 | } 22 | 23 | func TestCString(t *testing.T) { 24 | runtime := wasm3.NewRuntime(&wasm3.Config{ 25 | Environment: wasm3.NewEnvironment(), 26 | StackSize: 64 * 1024, 27 | }) 28 | defer runtime.Destroy() 29 | _, err := runtime.Load(wasmBytes) 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | fn, err := runtime.FindFunction(fnName) 34 | if err != nil { 35 | t.Fatal(err) 36 | } 37 | result, _ := fn() 38 | memoryLength := runtime.GetAllocatedMemoryLength() 39 | 40 | // Reconstruct the string from memory: 41 | mem := runtime.Memory() 42 | buf := new(bytes.Buffer) 43 | for n := 0; n < memoryLength; n++ { 44 | if n < result { 45 | continue 46 | } 47 | value := mem[n] 48 | if value == 0 { 49 | break 50 | } 51 | buf.WriteByte(value) 52 | } 53 | if buf.String() != "testingonly" { 54 | t.Fatal("Reconstructed string doesn't match") 55 | } 56 | } 57 | 58 | func BenchmarkCString(b *testing.B) { 59 | for n := 0; n < b.N; n++ { 60 | runtime := wasm3.NewRuntime(&wasm3.Config{ 61 | Environment: wasm3.NewEnvironment(), 62 | StackSize: 64 * 1024, 63 | }) 64 | defer runtime.Destroy() 65 | _, err := runtime.Load(wasmBytes) 66 | if err != nil { 67 | b.Fatal(err) 68 | } 69 | fn, err := runtime.FindFunction(fnName) 70 | if err != nil { 71 | b.Fatal(err) 72 | } 73 | result, _ := fn() 74 | memoryLength := runtime.GetAllocatedMemoryLength() 75 | 76 | // Reconstruct the string from memory: 77 | mem := runtime.Memory() 78 | buf := new(bytes.Buffer) 79 | for n := 0; n < memoryLength; n++ { 80 | if n < result { 81 | continue 82 | } 83 | value := mem[n] 84 | if value == 0 { 85 | break 86 | } 87 | buf.WriteByte(value) 88 | } 89 | if buf.String() != "testingonly" { 90 | b.Fatal("Reconstructed string doesn't match") 91 | } 92 | } 93 | } 94 | 95 | func BenchmarkCStringReentrant(b *testing.B) { 96 | runtime := wasm3.NewRuntime(&wasm3.Config{ 97 | Environment: wasm3.NewEnvironment(), 98 | StackSize: 64 * 1024, 99 | }) 100 | defer runtime.Destroy() 101 | _, err := runtime.Load(wasmBytes) 102 | if err != nil { 103 | b.Fatal(err) 104 | } 105 | fn, err := runtime.FindFunction(fnName) 106 | if err != nil { 107 | b.Fatal(err) 108 | } 109 | for n := 0; n < b.N; n++ { 110 | result, _ := fn() 111 | memoryLength := runtime.GetAllocatedMemoryLength() 112 | 113 | // Reconstruct the string from memory: 114 | mem := runtime.Memory() 115 | buf := new(bytes.Buffer) 116 | for n := 0; n < memoryLength; n++ { 117 | if n < result { 118 | continue 119 | } 120 | value := mem[n] 121 | if value == 0 { 122 | break 123 | } 124 | buf.WriteByte(value) 125 | } 126 | if buf.String() != "testingonly" { 127 | b.Fatal("Reconstructed string doesn't match") 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /examples/libxml/README.md: -------------------------------------------------------------------------------- 1 | libxml 2 | == 3 | 4 | More details [here](https://github.com/matiasinsaurralde/wasm-libxml2). 5 | 6 | Benchmark output: 7 | 8 | ``` 9 | % go test -bench=. -benchmem -v 10 | # github.com/matiasinsaurralde/go-wasm3/examples/libxml.test 11 | === RUN TestXMLValidation 12 | --- PASS: TestXMLValidation (0.01s) 13 | goos: darwin 14 | goarch: amd64 15 | pkg: github.com/matiasinsaurralde/go-wasm3/examples/libxml 16 | BenchmarkXMLValidation/Good_XML-8 1000 1498826 ns/op 88 B/op 5 allocs/op 17 | BenchmarkXMLValidation/Bad_XML-8 1000 2158523 ns/op 104 B/op 6 allocs/op 18 | PASS 19 | ok github.com/matiasinsaurralde/go-wasm3/examples/libxml 4.123s 20 | ``` -------------------------------------------------------------------------------- /examples/libxml/bad_input.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Item 1 5 | http://example.com/photo1.png 6 | Tag1, Tag2 7 | 32 8 | 540 9 | 60 10 | Big 11 | 12 | 13 | 14 | Item 2 15 | 16 | http://example.com/photo2.png 17 | Tag1 18 | 23 19 | 340 20 | 50 21 | 22 | 23 | -------------------------------------------------------------------------------- /examples/libxml/input.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Item 1 5 | http://example.com/photo1.png 6 | Tag1, Tag2 7 | 32 8 | 540 9 | 60 10 | Big 11 | 12 | 13 | 14 | Item 2 15 | http://example.com/photo2.png 16 | Tag1 17 | 23 18 | 340 19 | 50 20 | 21 | 22 | -------------------------------------------------------------------------------- /examples/libxml/input.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /examples/libxml/libxml.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io/ioutil" 5 | golog "log" 6 | 7 | wasm3 "github.com/matiasinsaurralde/go-wasm3" 8 | ) 9 | 10 | var ( 11 | allocateFn wasm3.FunctionWrapper 12 | newSchemaParserFn wasm3.FunctionWrapper 13 | validateFn wasm3.FunctionWrapper 14 | runtime *wasm3.Runtime 15 | print = golog.Print 16 | printf = golog.Printf 17 | ) 18 | 19 | const ( 20 | wasmFilename = "libxml2.wasm" 21 | ) 22 | 23 | func initRuntimeAndModule() error { 24 | runtime = wasm3.NewRuntime(&wasm3.Config{ 25 | Environment: wasm3.NewEnvironment(), 26 | StackSize: 1024 * 1024, 27 | EnableWASI: true, 28 | }) 29 | 30 | wasmBytes, err := ioutil.ReadFile(wasmFilename) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | module, err := runtime.ParseModule(wasmBytes) 36 | if err != nil { 37 | return err 38 | } 39 | _, err = runtime.LoadModule(module) 40 | if err != nil { 41 | return err 42 | } 43 | return nil 44 | } 45 | 46 | func mapCalls() error { 47 | var err error 48 | allocateFn, err = runtime.FindFunction("wasm_allocate") 49 | if err != nil { 50 | return err 51 | } 52 | 53 | newSchemaParserFn, err = runtime.FindFunction("wasm_new_schema_parser2") 54 | if err != nil { 55 | return err 56 | } 57 | 58 | validateFn, err = runtime.FindFunction("wasm_validate_xml") 59 | if err != nil { 60 | return err 61 | } 62 | return nil 63 | } 64 | 65 | func allocate(input []byte) (int, error) { 66 | ptr, err := allocateFn(len(input)) 67 | if err != nil { 68 | return 0, nil 69 | } 70 | pos := ptr 71 | for _, ch := range input { 72 | runtime.Memory()[pos] = byte(ch) 73 | pos++ 74 | } 75 | return ptr, nil 76 | } 77 | 78 | func newSchemaParser(ptr, length int) (int, error) { 79 | outPtr, err := newSchemaParserFn(ptr, length) 80 | if err != nil { 81 | return 0, err 82 | } 83 | return outPtr, nil 84 | } 85 | 86 | func validate(xmlPtr, xmlLength, schemaParserPtr int) (int, error) { 87 | out, err := validateFn(xmlPtr, xmlLength, schemaParserPtr) 88 | return out, err 89 | } 90 | 91 | func main() { 92 | err := initRuntimeAndModule() 93 | if err != nil { 94 | panic(err) 95 | } 96 | defer runtime.Destroy() 97 | err = mapCalls() 98 | if err != nil { 99 | panic(err) 100 | } 101 | xsdFile, err := ioutil.ReadFile("input.xsd") 102 | if err != nil { 103 | panic(err) 104 | } 105 | xsdPtr, err := allocate(xsdFile) 106 | if err != nil { 107 | panic(err) 108 | } 109 | printf("Allocated %d bytes in WASM memory (\"wasm_allocate\"), pointer is %d (XSD file)\n", len(xsdFile), xsdPtr) 110 | xmlFile, err := ioutil.ReadFile("input.xml") 111 | if err != nil { 112 | panic(err) 113 | } 114 | xmlPtr, err := allocate(xmlFile) 115 | if err != nil { 116 | panic(err) 117 | } 118 | printf("Allocated %d bytes in WASM memory (\"wasm_allocate\"), pointer is %d (XSD file)\n", len(xmlFile), xmlPtr) 119 | schemaParserPtr, err := newSchemaParser(xsdPtr, len(xsdFile)) 120 | if err != nil { 121 | panic(err) 122 | } 123 | printf("Schema parser was initialized (\"wasm_new_schema_parser2\"), pointer is %d\n", schemaParserPtr) 124 | 125 | out, err := validate(xmlPtr, len(xmlFile), schemaParserPtr) 126 | if err != nil { 127 | panic(err) 128 | } 129 | printf("\"wasm_validate_xml\" output is: %d", out) 130 | } 131 | -------------------------------------------------------------------------------- /examples/libxml/libxml2.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matiasinsaurralde/go-wasm3/94c5ed3e86a3281e423b7c1d4ea2e577bc0b7324/examples/libxml/libxml2.wasm -------------------------------------------------------------------------------- /examples/libxml/libxml_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io/ioutil" 5 | "testing" 6 | ) 7 | 8 | var ( 9 | badXMLData, _ = ioutil.ReadFile("bad_input.xml") 10 | goodXMLData, _ = ioutil.ReadFile("input.xml") 11 | xsdData, _ = ioutil.ReadFile("input.xsd") 12 | 13 | badXMLPtr int 14 | goodXMLPtr int 15 | xsdPtr int 16 | schemaParserPtr int 17 | ) 18 | 19 | func init() { 20 | print = func(...interface{}) {} 21 | printf = func(string, ...interface{}) {} 22 | err := initRuntimeAndModule() 23 | if err != nil { 24 | panic(err) 25 | } 26 | err = mapCalls() 27 | if err != nil { 28 | panic(err) 29 | } 30 | 31 | badXMLPtr, err = allocate(badXMLData) 32 | if err != nil { 33 | panic(err) 34 | } 35 | goodXMLPtr, err = allocate(goodXMLData) 36 | if err != nil { 37 | panic(err) 38 | } 39 | xsdPtr, err = allocate(xsdData) 40 | if err != nil { 41 | panic(err) 42 | } 43 | schemaParserPtr, err = newSchemaParser(xsdPtr, len(xsdData)) 44 | if err != nil { 45 | panic(err) 46 | } 47 | } 48 | 49 | func validateGoodXML() (int, error) { 50 | return validate(goodXMLPtr, len(goodXMLData), schemaParserPtr) 51 | } 52 | 53 | func validateBadXML() (int, error) { 54 | return validate(badXMLPtr, len(badXMLData), schemaParserPtr) 55 | } 56 | 57 | func TestXMLValidation(t *testing.T) { 58 | i, err := validateGoodXML() 59 | if i > 0 && err != nil { 60 | t.Fatal("Unexpected output, result should be a positive number, err should be nil") 61 | } 62 | i, _ = validateBadXML() 63 | if i != -1 { 64 | t.Fatal("Unexpected output, result should be -1") 65 | } 66 | } 67 | func BenchmarkXMLValidation(b *testing.B) { 68 | b.Run("Good XML", func(b *testing.B) { 69 | for n := 0; n < b.N; n++ { 70 | validateGoodXML() 71 | } 72 | }) 73 | b.Run("Bad XML", func(b *testing.B) { 74 | for n := 0; n < b.N; n++ { 75 | validateBadXML() 76 | } 77 | }) 78 | } 79 | -------------------------------------------------------------------------------- /examples/sum/sum.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io/ioutil" 5 | "log" 6 | 7 | wasm3 "github.com/matiasinsaurralde/go-wasm3" 8 | ) 9 | 10 | const ( 11 | wasmFilename = "sum.wasm" 12 | fnName = "sum" 13 | ) 14 | 15 | func main() { 16 | log.Print("Initializing WASM3") 17 | 18 | runtime := wasm3.NewRuntime(&wasm3.Config{ 19 | Environment: wasm3.NewEnvironment(), 20 | StackSize: 64 * 1024, 21 | }) 22 | log.Println("Runtime ok") 23 | 24 | wasmBytes, err := ioutil.ReadFile(wasmFilename) 25 | if err != nil { 26 | panic(err) 27 | } 28 | log.Printf("Read WASM module (%d bytes)\n", len(wasmBytes)) 29 | 30 | module, err := runtime.ParseModule(wasmBytes) 31 | if err != nil { 32 | panic(err) 33 | } 34 | module, err = runtime.LoadModule(module) 35 | if err != nil { 36 | panic(err) 37 | } 38 | log.Print("Loaded module") 39 | 40 | fn, err := runtime.FindFunction(fnName) 41 | if err != nil { 42 | panic(err) 43 | } 44 | log.Printf("Found '%s' function (using runtime.FindFunction)", fnName) 45 | result, _ := fn(1, 1) 46 | log.Print("Result is: ", result) 47 | 48 | // Different call approach, retrieving functions from the module object: 49 | fn2, err := module.GetFunctionByName("sum") 50 | if err != nil { 51 | panic(err) 52 | } 53 | log.Printf("Found '%s' function (using module.GetFunctionByName)", fnName) 54 | result, _ = fn2.Call(2, 2) 55 | log.Print("Result is: ", result) 56 | } 57 | -------------------------------------------------------------------------------- /examples/sum/sum.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matiasinsaurralde/go-wasm3/94c5ed3e86a3281e423b7c1d4ea2e577bc0b7324/examples/sum/sum.wasm -------------------------------------------------------------------------------- /examples/sum/sum_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io/ioutil" 5 | "testing" 6 | 7 | wasm3 "github.com/matiasinsaurralde/go-wasm3" 8 | ) 9 | 10 | var ( 11 | wasmBytes []byte 12 | ) 13 | 14 | func init() { 15 | var err error 16 | wasmBytes, err = ioutil.ReadFile(wasmFilename) 17 | if err != nil { 18 | panic(err) 19 | } 20 | } 21 | 22 | func TestSum(t *testing.T) { 23 | runtime := wasm3.NewRuntime(&wasm3.Config{ 24 | Environment: wasm3.NewEnvironment(), 25 | StackSize: 64 * 1024, 26 | }) 27 | defer runtime.Destroy() 28 | _, err := runtime.Load(wasmBytes) 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | fn, err := runtime.FindFunction(fnName) 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | result, _ := fn(1, 1) 37 | if result != 2 { 38 | t.Fatal("Result doesn't match") 39 | } 40 | } 41 | 42 | func BenchmarkSum(b *testing.B) { 43 | for n := 0; n < b.N; n++ { 44 | runtime := wasm3.NewRuntime(&wasm3.Config{ 45 | Environment: wasm3.NewEnvironment(), 46 | StackSize: 64 * 1024, 47 | }) 48 | defer runtime.Destroy() 49 | _, err := runtime.Load(wasmBytes) 50 | if err != nil { 51 | b.Fatal(err) 52 | } 53 | fn, err := runtime.FindFunction(fnName) 54 | if err != nil { 55 | b.Fatal(err) 56 | } 57 | fn(1, 2) 58 | } 59 | } 60 | 61 | func BenchmarkSumReentrant(b *testing.B) { 62 | runtime := wasm3.NewRuntime(&wasm3.Config{ 63 | Environment: wasm3.NewEnvironment(), 64 | StackSize: 64 * 1024, 65 | }) 66 | defer runtime.Destroy() 67 | _, err := runtime.Load(wasmBytes) 68 | if err != nil { 69 | b.Fatal(err) 70 | } 71 | fn, err := runtime.FindFunction(fnName) 72 | if err != nil { 73 | b.Fatal(err) 74 | } 75 | for n := 0; n < b.N; n++ { 76 | fn(1, 2) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /go-wasm3.h: -------------------------------------------------------------------------------- 1 | #include "m3_env.h" 2 | void set_error(M3Result); -------------------------------------------------------------------------------- /include/m3_api_defs.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_api_defs.h 3 | // 4 | // Created by Volodymyr Shymanskyy on 12/20/19. 5 | // Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. 6 | // 7 | 8 | #ifndef m3_api_defs_h 9 | #define m3_api_defs_h 10 | 11 | #include "m3_core.h" 12 | 13 | // TODO: perform bounds checks 14 | #define m3ApiOffsetToPtr(offset) (void*)((u8*)_mem + (u32)(offset)) 15 | #define m3ApiPtrToOffset(ptr) (u32)((u8*)ptr - (u8*)_mem) 16 | 17 | #define m3ApiReturnType(TYPE) TYPE* raw_return = ((TYPE*) (_sp)); 18 | #define m3ApiGetArg(TYPE, NAME) TYPE NAME = * ((TYPE *) (_sp++)); 19 | #define m3ApiGetArgMem(TYPE, NAME) TYPE NAME = (TYPE)m3ApiOffsetToPtr(* ((u32 *) (_sp++))); 20 | 21 | #define m3ApiRawFunction(NAME) const void * NAME (IM3Runtime runtime, uint64_t * _sp, void * _mem) 22 | #define m3ApiReturn(VALUE) { *raw_return = (VALUE); return m3Err_none; } 23 | #define m3ApiTrap(VALUE) { return VALUE; } 24 | #define m3ApiSuccess() { return m3Err_none; } 25 | 26 | # if defined(M3_BIG_ENDIAN) 27 | # define m3ApiReadMem16(ptr) __builtin_bswap16((* (u16 *)(ptr))) 28 | # define m3ApiReadMem32(ptr) __builtin_bswap32((* (u32 *)(ptr))) 29 | # define m3ApiReadMem64(ptr) __builtin_bswap64((* (u64 *)(ptr))) 30 | # define m3ApiWriteMem16(ptr, val) { * (u16 *)(ptr) = __builtin_bswap16((val)); } 31 | # define m3ApiWriteMem32(ptr, val) { * (u32 *)(ptr) = __builtin_bswap32((val)); } 32 | # define m3ApiWriteMem64(ptr, val) { * (u64 *)(ptr) = __builtin_bswap64((val)); } 33 | # else 34 | # define m3ApiReadMem16(ptr) (* (u16 *)(ptr)) 35 | # define m3ApiReadMem32(ptr) (* (u32 *)(ptr)) 36 | # define m3ApiReadMem64(ptr) (* (u64 *)(ptr)) 37 | # define m3ApiWriteMem16(ptr, val) { * (u16 *)(ptr) = (val); } 38 | # define m3ApiWriteMem32(ptr, val) { * (u32 *)(ptr) = (val); } 39 | # define m3ApiWriteMem64(ptr, val) { * (u64 *)(ptr) = (val); } 40 | # endif 41 | 42 | #endif // m3_api_defs_h 43 | -------------------------------------------------------------------------------- /include/m3_api_libc.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_api_libc.h 3 | // 4 | // Created by Volodymyr Shymanskyy on 11/20/19. 5 | // Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. 6 | // 7 | 8 | #ifndef m3_api_libc_h 9 | #define m3_api_libc_h 10 | 11 | #include "m3_core.h" 12 | 13 | d_m3BeginExternC 14 | 15 | M3Result m3_LinkLibC (IM3Module io_module); 16 | M3Result m3_LinkSpecTest (IM3Module io_module); 17 | 18 | d_m3EndExternC 19 | 20 | #endif // m3_api_libc_h 21 | -------------------------------------------------------------------------------- /include/m3_api_tracer.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_api_tracer.h 3 | // 4 | // Created by Volodymyr Shymanskyy on 02/18/20. 5 | // Copyright © 2020 Volodymyr Shymanskyy. All rights reserved. 6 | // 7 | 8 | #ifndef m3_api_tracer_h 9 | #define m3_api_tracer_h 10 | 11 | #include "m3_core.h" 12 | 13 | d_m3BeginExternC 14 | 15 | M3Result m3_LinkTracer (IM3Module io_module); 16 | 17 | d_m3EndExternC 18 | 19 | #endif // m3_api_tracer_h 20 | -------------------------------------------------------------------------------- /include/m3_api_wasi.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_api_wasi.h 3 | // 4 | // Created by Volodymyr Shymanskyy on 11/20/19. 5 | // Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. 6 | // 7 | 8 | #ifndef m3_api_wasi_h 9 | #define m3_api_wasi_h 10 | 11 | #include "m3_core.h" 12 | 13 | d_m3BeginExternC 14 | 15 | M3Result m3_LinkWASI (IM3Module io_module); 16 | 17 | d_m3EndExternC 18 | 19 | #endif // m3_api_wasi_h 20 | -------------------------------------------------------------------------------- /include/m3_bind.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_bind.h 3 | // 4 | // Created by Steven Massey on 2/27/20. 5 | // Copyright © 2020 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_bind_h 9 | #define m3_bind_h 10 | 11 | #include "m3_env.h" 12 | 13 | d_m3BeginExternC 14 | 15 | u8 ConvertTypeCharToTypeId (char i_code); 16 | M3Result SignatureToFuncType (IM3FuncType * o_functionType, ccstr_t i_signature); 17 | 18 | d_m3EndExternC 19 | 20 | #endif /* m3_bind_h */ 21 | -------------------------------------------------------------------------------- /include/m3_code.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_code.h 3 | // 4 | // Created by Steven Massey on 4/19/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_code_h 9 | #define m3_code_h 10 | 11 | #include "m3_core.h" 12 | 13 | d_m3BeginExternC 14 | 15 | typedef struct M3CodePage 16 | { 17 | M3CodePageHeader info; 18 | code_t code [1]; 19 | } 20 | M3CodePage; 21 | 22 | typedef M3CodePage * IM3CodePage; 23 | 24 | 25 | IM3CodePage NewCodePage (u32 i_minNumLines); 26 | 27 | void FreeCodePages (IM3CodePage * io_list); 28 | 29 | u32 NumFreeLines (IM3CodePage i_page); 30 | pc_t GetPageStartPC (IM3CodePage i_page); 31 | pc_t GetPagePC (IM3CodePage i_page); 32 | void EmitWord_impl (IM3CodePage i_page, void* i_word); 33 | void EmitWord32 (IM3CodePage i_page, u32 i_word); 34 | void EmitWord64 (IM3CodePage i_page, u64 i_word); 35 | 36 | void PushCodePage (IM3CodePage * io_list, IM3CodePage i_codePage); 37 | IM3CodePage PopCodePage (IM3CodePage * io_list); 38 | 39 | IM3CodePage GetEndCodePage (IM3CodePage i_list); // i_list = NULL is valid 40 | u32 CountCodePages (IM3CodePage i_list); // i_list = NULL is valid 41 | 42 | # ifdef DEBUG 43 | void dump_code_page (IM3CodePage i_codePage, pc_t i_startPC); 44 | # endif 45 | 46 | #define EmitWord(page, val) EmitWord_impl(page, (void*)(val)) 47 | 48 | d_m3EndExternC 49 | 50 | #endif // m3_code_h 51 | -------------------------------------------------------------------------------- /include/m3_compile.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_compile.h 3 | // 4 | // Created by Steven Massey on 4/17/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_compile_h 9 | #define m3_compile_h 10 | 11 | #include "m3_code.h" 12 | #include "m3_exec_defs.h" 13 | 14 | d_m3BeginExternC 15 | 16 | enum 17 | { 18 | c_waOp_block = 0x02, 19 | c_waOp_loop = 0x03, 20 | c_waOp_if = 0x04, 21 | c_waOp_else = 0x05, 22 | c_waOp_end = 0x0b, 23 | c_waOp_branch = 0x0c, 24 | c_waOp_branchTable = 0x0e, 25 | c_waOp_branchIf = 0x0d, 26 | c_waOp_call = 0x10, 27 | c_waOp_getLocal = 0x20, 28 | c_waOp_setLocal = 0x21, 29 | c_waOp_teeLocal = 0x22, 30 | }; 31 | 32 | //----------------------------------------------------------------------------------------------------------------------------------- 33 | 34 | // since the end location of a block is unknown when a branch is compiled, writing 35 | // the actual address must deferred. A linked-list of patch locations is kept in 36 | // M3CompilationScope. When the block compilation exits, it patches these addresses. 37 | typedef struct M3BranchPatch 38 | { 39 | struct M3BranchPatch * next; 40 | pc_t * location; 41 | } 42 | M3BranchPatch; 43 | 44 | typedef M3BranchPatch * IM3BranchPatch; 45 | 46 | 47 | typedef struct M3CompilationScope 48 | { 49 | struct M3CompilationScope * outer; 50 | 51 | pc_t pc; // used by ContinueLoop's 52 | IM3BranchPatch patches; 53 | i32 depth; 54 | // i32 loopDepth; 55 | i16 initStackIndex; 56 | u8 type; 57 | m3opcode_t opcode; 58 | bool isPolymorphic; 59 | } 60 | M3CompilationScope; 61 | 62 | typedef M3CompilationScope * IM3CompilationScope; 63 | 64 | // double the slot count when using 32-bit slots, since every wasm stack element could be a 64-bit type 65 | //static const u16 c_m3MaxFunctionSlots = d_m3MaxFunctionStackHeight * (d_m3Use32BitSlots + 1); 66 | 67 | typedef struct 68 | { 69 | IM3Runtime runtime; 70 | IM3Module module; 71 | 72 | bytes_t wasm; 73 | bytes_t wasmEnd; 74 | 75 | M3CompilationScope block; 76 | 77 | IM3Function function; 78 | 79 | IM3CodePage page; 80 | 81 | IM3BranchPatch releasedPatches; 82 | 83 | u32 numEmits; 84 | u32 numOpcodes; 85 | 86 | u16 firstDynamicStackIndex; 87 | u16 stackIndex; // current stack index 88 | 89 | u16 firstConstSlotIndex; 90 | u16 maxConstSlotIndex; // as const's are encountered during compilation this tracks their location in the "real" stack 91 | 92 | u16 firstLocalSlotIndex; 93 | u16 firstDynamicSlotIndex; // numArgs + numLocals + numReservedConstants. the first mutable slot available to the compiler. 94 | 95 | m3slot_t constants [d_m3MaxConstantTableSize]; 96 | 97 | // 'wasmStack' holds slot locations 98 | u16 wasmStack [d_m3MaxFunctionStackHeight]; 99 | u8 typeStack [d_m3MaxFunctionStackHeight]; 100 | 101 | // 'm3Slots' contains allocation usage counts 102 | u8 m3Slots [d_m3MaxFunctionSlots]; 103 | 104 | u16 maxAllocatedSlotPlusOne; 105 | 106 | u16 regStackIndexPlusOne [2]; 107 | 108 | m3opcode_t previousOpcode; 109 | } 110 | M3Compilation; 111 | 112 | typedef M3Compilation * IM3Compilation; 113 | 114 | typedef M3Result (* M3Compiler) (IM3Compilation, m3opcode_t); 115 | 116 | 117 | //----------------------------------------------------------------------------------------------------------------------------------- 118 | 119 | 120 | typedef struct M3OpInfo 121 | { 122 | #ifdef DEBUG 123 | const char * const name; 124 | #endif 125 | 126 | i8 stackOffset; 127 | u8 type; 128 | 129 | // for most operations: 130 | // [0]= top operand in register, [1]= top operand in stack, [2]= both operands in stack 131 | IM3Operation operations [4]; 132 | 133 | M3Compiler compiler; 134 | } 135 | M3OpInfo; 136 | 137 | typedef const M3OpInfo * IM3OpInfo; 138 | 139 | extern const M3OpInfo c_operations []; 140 | extern const M3OpInfo c_operationsFC []; 141 | 142 | static inline 143 | const M3OpInfo* GetOpInfo(m3opcode_t opcode) { 144 | switch (opcode >> 8) { 145 | case 0x00: return &c_operations[opcode]; 146 | case 0xFC: return &c_operationsFC[opcode & 0xFF]; 147 | default: return NULL; 148 | } 149 | } 150 | 151 | #ifdef DEBUG 152 | #define M3OP(...) { __VA_ARGS__ } 153 | #define M3OP_RESERVED { "reserved" } 154 | #else 155 | // Strip-off name 156 | #define M3OP(name, ...) { __VA_ARGS__ } 157 | #define M3OP_RESERVED { 0 } 158 | #endif 159 | 160 | #if d_m3HasFloat 161 | #define M3OP_F M3OP 162 | #else 163 | #define M3OP_F(...) { 0 } 164 | #endif 165 | 166 | //----------------------------------------------------------------------------------------------------------------------------------- 167 | 168 | u16 GetTypeNumSlots (u8 i_type); 169 | void AlignSlotIndexToType (u16 * io_slotIndex, u8 i_type); 170 | 171 | bool IsRegisterAllocated (IM3Compilation o, u32 i_register); 172 | bool IsRegisterLocation (i16 i_location); 173 | bool IsFpRegisterLocation (i16 i_location); 174 | bool IsIntRegisterLocation (i16 i_location); 175 | 176 | bool IsStackPolymorphic (IM3Compilation o); 177 | 178 | M3Result CompileBlock (IM3Compilation io, u8 i_blockType, u8 i_blockOpcode); 179 | 180 | M3Result Compile_BlockStatements (IM3Compilation io); 181 | M3Result Compile_Function (IM3Function io_function); 182 | 183 | u16 GetMaxUsedSlotPlusOne (IM3Compilation o); 184 | 185 | d_m3EndExternC 186 | 187 | #endif // m3_compile_h 188 | -------------------------------------------------------------------------------- /include/m3_config.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_config.h 3 | // 4 | // Created by Steven Massey on 5/4/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_config_h 9 | #define m3_config_h 10 | 11 | #include "m3_config_platforms.h" 12 | 13 | // general -------------------------------------------------------------------- 14 | 15 | # ifndef d_m3CodePageAlignSize 16 | # define d_m3CodePageAlignSize 4096 17 | # endif 18 | 19 | # ifndef d_m3EnableCodePageRefCounting 20 | # define d_m3EnableCodePageRefCounting 0 21 | # endif 22 | 23 | # ifndef d_m3MaxFunctionStackHeight 24 | # define d_m3MaxFunctionStackHeight 2000 // TODO: comment on upper limit 25 | # endif 26 | 27 | # ifndef d_m3MaxFunctionSlots 28 | # define d_m3MaxFunctionSlots 4000 // twice d_m3MaxFunctionStackHeight 29 | # endif 30 | 31 | # ifndef d_m3MaxConstantTableSize 32 | # define d_m3MaxConstantTableSize 120 33 | # endif 34 | 35 | # ifndef d_m3LogOutput 36 | # define d_m3LogOutput 1 37 | # endif 38 | 39 | # ifndef d_m3VerboseLogs 40 | # define d_m3VerboseLogs 1 41 | # endif 42 | 43 | # ifndef d_m3FixedHeap 44 | # define d_m3FixedHeap false 45 | //# define d_m3FixedHeap (32*1024) 46 | # endif 47 | 48 | # ifndef d_m3FixedHeapAlign 49 | # define d_m3FixedHeapAlign 16 50 | # endif 51 | 52 | # ifndef d_m3Use32BitSlots 53 | # define d_m3Use32BitSlots 1 54 | # endif 55 | 56 | # ifndef d_m3ProfilerSlotMask 57 | # define d_m3ProfilerSlotMask 0xFFFF 58 | # endif 59 | 60 | 61 | // profiling and tracing ------------------------------------------------------ 62 | 63 | # ifndef d_m3EnableOpProfiling 64 | # define d_m3EnableOpProfiling 0 // opcode usage counters 65 | # endif 66 | 67 | # ifndef d_m3EnableOpTracing 68 | # define d_m3EnableOpTracing 0 // only works with DEBUG 69 | # endif 70 | 71 | 72 | // logging -------------------------------------------------------------------- 73 | 74 | # ifndef d_m3LogParse 75 | # define d_m3LogParse 0 // .wasm binary decoding info 76 | # endif 77 | 78 | # ifndef d_m3LogModule 79 | # define d_m3LogModule 0 // wasm module info 80 | # endif 81 | 82 | # ifndef d_m3LogCompile 83 | # define d_m3LogCompile 0 // wasm -> metacode generation phase 84 | # endif 85 | 86 | # ifndef d_m3LogWasmStack 87 | # define d_m3LogWasmStack 0 // dump the wasm stack when pushed or popped 88 | # endif 89 | 90 | # ifndef d_m3LogEmit 91 | # define d_m3LogEmit 0 // metacode generation info 92 | # endif 93 | 94 | # ifndef d_m3LogCodePages 95 | # define d_m3LogCodePages 0 // dump metacode pages when released 96 | # endif 97 | 98 | # ifndef d_m3LogExec 99 | # define d_m3LogExec 0 // low-level interpreter specific logs 100 | # endif 101 | 102 | # ifndef d_m3LogRuntime 103 | # define d_m3LogRuntime 0 // higher-level runtime information 104 | # endif 105 | 106 | # ifndef d_m3LogStackTrace 107 | # define d_m3LogStackTrace 0 // dump the call stack when traps occur 108 | # endif 109 | 110 | # ifndef d_m3LogNativeStack 111 | # define d_m3LogNativeStack 0 // track the memory usage of the C-stack 112 | # endif 113 | 114 | 115 | // other ---------------------------------------------------------------------- 116 | 117 | # ifndef d_m3HasFloat 118 | # define d_m3HasFloat 1 // implement floating point ops 119 | # endif 120 | 121 | # ifndef d_m3SkipStackCheck 122 | # define d_m3SkipStackCheck 0 // skip stack overrun checks 123 | # endif 124 | 125 | # ifndef d_m3SkipMemoryBoundsCheck 126 | # define d_m3SkipMemoryBoundsCheck 0 // skip memory bounds checks 127 | # endif 128 | 129 | #endif // m3_config_h 130 | -------------------------------------------------------------------------------- /include/m3_config_platforms.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_config_platforms.h 3 | // 4 | // Created by Volodymyr Shymanskyy on 11/20/19. 5 | // Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. 6 | // 7 | 8 | #ifndef m3_config_platforms_h 9 | #define m3_config_platforms_h 10 | 11 | /* 12 | * Helpers 13 | */ 14 | 15 | #define M3_STR__(x) #x 16 | #define M3_STR(x) M3_STR__(x) 17 | 18 | #define M3_CONCAT__(a,b) a##b 19 | #define M3_CONCAT(a,b) M3_CONCAT__(a,b) 20 | 21 | # if !defined(__cplusplus) 22 | # define not ! 23 | # define and && 24 | # define or || 25 | # endif 26 | 27 | /* 28 | * Detect platform 29 | */ 30 | 31 | # if defined(__clang__) 32 | # define M3_COMPILER_CLANG 1 33 | # elif defined(__INTEL_COMPILER) 34 | # define M3_COMPILER_ICC 1 35 | # elif defined(__GNUC__) || defined(__GNUG__) 36 | # define M3_COMPILER_GCC 1 37 | # elif defined(_MSC_VER) 38 | # define M3_COMPILER_MSVC 1 39 | # else 40 | # warning "Compiler not detected" 41 | # endif 42 | 43 | # if defined(M3_COMPILER_CLANG) || defined(M3_COMPILER_GCC) 44 | # if defined(__wasm__) 45 | # define M3_ARCH "wasm" 46 | 47 | # elif defined(__x86_64__) 48 | # define M3_ARCH "x86_64" 49 | 50 | # elif defined(__i386__) 51 | # define M3_ARCH "i386" 52 | 53 | # elif defined(__aarch64__) 54 | # define M3_ARCH "arm64-v8a" 55 | 56 | # elif defined(__arm__) 57 | # if defined(__ARM_ARCH_7A__) 58 | # if defined(__ARM_NEON__) 59 | # if defined(__ARM_PCS_VFP) 60 | # define M3_ARCH "arm-v7a/NEON hard-float" 61 | # else 62 | # define M3_ARCH "arm-v7a/NEON" 63 | # endif 64 | # else 65 | # if defined(__ARM_PCS_VFP) 66 | # define M3_ARCH "arm-v7a hard-float" 67 | # else 68 | # define M3_ARCH "arm-v7a" 69 | # endif 70 | # endif 71 | # else 72 | # define M3_ARCH "arm" 73 | # endif 74 | 75 | # elif defined(__riscv) 76 | # if defined(__riscv_32e) 77 | # define _M3_ARCH_RV "rv32e" 78 | # elif __riscv_xlen == 128 79 | # define _M3_ARCH_RV "rv128i" 80 | # elif __riscv_xlen == 64 81 | # define _M3_ARCH_RV "rv64i" 82 | # elif __riscv_xlen == 32 83 | # define _M3_ARCH_RV "rv32i" 84 | # endif 85 | # if defined(__riscv_muldiv) 86 | # define _M3_ARCH_RV_M _M3_ARCH_RV "m" 87 | # else 88 | # define _M3_ARCH_RV_M _M3_ARCH_RV 89 | # endif 90 | # if defined(__riscv_atomic) 91 | # define _M3_ARCH_RV_A _M3_ARCH_RV_M "a" 92 | # else 93 | # define _M3_ARCH_RV_A _M3_ARCH_RV_M 94 | # endif 95 | # if defined(__riscv_flen) 96 | # define _M3_ARCH_RV_F _M3_ARCH_RV_A "f" 97 | # else 98 | # define _M3_ARCH_RV_F _M3_ARCH_RV_A 99 | # endif 100 | # if defined(__riscv_flen) && __riscv_flen >= 64 101 | # define _M3_ARCH_RV_D _M3_ARCH_RV_F "d" 102 | # else 103 | # define _M3_ARCH_RV_D _M3_ARCH_RV_F 104 | # endif 105 | # if defined(__riscv_compressed) 106 | # define _M3_ARCH_RV_C _M3_ARCH_RV_D "c" 107 | # else 108 | # define _M3_ARCH_RV_C _M3_ARCH_RV_D 109 | # endif 110 | # define M3_ARCH _M3_ARCH_RV_C 111 | 112 | # elif defined(__mips__) 113 | # if defined(__MIPSEB__) && defined(__mips64) 114 | # define M3_ARCH "mips64 " _MIPS_ARCH 115 | # elif defined(__MIPSEL__) && defined(__mips64) 116 | # define M3_ARCH "mips64el " _MIPS_ARCH 117 | # elif defined(__MIPSEB__) 118 | # define M3_ARCH "mips " _MIPS_ARCH 119 | # elif defined(__MIPSEL__) 120 | # define M3_ARCH "mipsel " _MIPS_ARCH 121 | # endif 122 | 123 | # elif defined(__PPC__) 124 | # if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) 125 | # define M3_ARCH "ppc64le" 126 | # elif defined(__PPC64__) 127 | # define M3_ARCH "ppc64" 128 | # else 129 | # define M3_ARCH "ppc" 130 | # endif 131 | 132 | # elif defined(__sparc__) 133 | # if defined(__arch64__) 134 | # define M3_ARCH "sparc64" 135 | # else 136 | # define M3_ARCH "sparc" 137 | # endif 138 | 139 | # elif defined(__s390x__) 140 | # define M3_ARCH "s390x" 141 | 142 | # elif defined(__alpha__) 143 | # define M3_ARCH "alpha" 144 | 145 | # elif defined(__m68k__) 146 | # define M3_ARCH "m68k" 147 | 148 | # elif defined(__xtensa__) 149 | # define M3_ARCH "xtensa" 150 | 151 | # elif defined(__arc__) 152 | # define M3_ARCH "arc32" 153 | 154 | # elif defined(__AVR__) 155 | # define M3_ARCH "avr" 156 | # endif 157 | # endif 158 | 159 | # if defined(M3_COMPILER_MSVC) 160 | # if defined(_M_X64) 161 | # define M3_ARCH "x86_64" 162 | # elif defined(_M_IX86) 163 | # define M3_ARCH "i386" 164 | # elif defined(_M_ARM64) 165 | # define M3_ARCH "arm64" 166 | # elif defined(_M_ARM) 167 | # define M3_ARCH "arm" 168 | # endif 169 | # endif 170 | 171 | # if !defined(M3_ARCH) 172 | # warning "Architecture not detected" 173 | # define M3_ARCH "unknown" 174 | # endif 175 | 176 | # if defined(M3_COMPILER_CLANG) 177 | # if defined(WIN32) 178 | # define M3_COMPILER_VER __VERSION__ " for Windows" 179 | # else 180 | # define M3_COMPILER_VER __VERSION__ 181 | # endif 182 | # elif defined(M3_COMPILER_GCC) 183 | # define M3_COMPILER_VER "GCC " __VERSION__ 184 | # elif defined(M3_COMPILER_MSVC) 185 | # define M3_COMPILER_VER "MSVC " M3_STR(_MSC_VER) 186 | # else 187 | # define M3_COMPILER_VER "unknown" 188 | # endif 189 | 190 | /* 191 | * Detect/define features 192 | */ 193 | 194 | # ifdef __has_feature 195 | # define M3_COMPILER_HAS_FEATURE(x) __has_feature(x) 196 | # else 197 | # define M3_COMPILER_HAS_FEATURE(x) 0 198 | # endif 199 | 200 | # ifdef __has_builtin 201 | # define M3_COMPILER_HAS_BUILTIN(x) __has_builtin(x) 202 | # else 203 | # define M3_COMPILER_HAS_BUILTIN(x) 0 204 | # endif 205 | 206 | # if defined(M3_COMPILER_MSVC) 207 | # include 208 | # if UINTPTR_MAX == 0xFFFFFFFF 209 | # define M3_SIZEOF_PTR 4 210 | # elif UINTPTR_MAX == 0xFFFFFFFFFFFFFFFFu 211 | # define M3_SIZEOF_PTR 8 212 | # else 213 | # error "Pointer size not supported" 214 | # endif 215 | # elif defined(__SIZEOF_POINTER__) 216 | # define M3_SIZEOF_PTR __SIZEOF_POINTER__ 217 | #else 218 | # error "Pointer size not detected" 219 | # endif 220 | 221 | # if defined(M3_COMPILER_MSVC) 222 | # define M3_LITTLE_ENDIAN //_byteswap_ushort, _byteswap_ulong, _byteswap_uint64 223 | # elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 224 | # define M3_LITTLE_ENDIAN 225 | # elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 226 | # define M3_BIG_ENDIAN 227 | # else 228 | # error "Byte order not detected" 229 | # endif 230 | 231 | // Byte swapping (for Big-Endian systems only) 232 | 233 | # if defined(M3_COMPILER_MSVC) 234 | # define m3_bswap16(x) _byteswap_ushort((x)) 235 | # define m3_bswap32(x) _byteswap_ulong((x)) 236 | # define m3_bswap64(x) _byteswap_uint64((x)) 237 | # elif defined(M3_COMPILER_GCC) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) 238 | // __builtin_bswap32/64 added in gcc 4.3, __builtin_bswap16 added in gcc 4.8 239 | # define m3_bswap16(x) __builtin_bswap16((x)) 240 | # define m3_bswap32(x) __builtin_bswap32((x)) 241 | # define m3_bswap64(x) __builtin_bswap64((x)) 242 | # elif defined(M3_COMPILER_CLANG) && M3_COMPILER_HAS_BUILTIN(__builtin_bswap16) 243 | # define m3_bswap16(x) __builtin_bswap16((x)) 244 | # define m3_bswap32(x) __builtin_bswap32((x)) 245 | # define m3_bswap64(x) __builtin_bswap64((x)) 246 | # else 247 | # include 248 | # if defined(__bswap_16) 249 | # define m3_bswap16(x) __bswap_16((x)) 250 | # define m3_bswap32(x) __bswap_32((x)) 251 | # define m3_bswap64(x) __bswap_64((x)) 252 | # else 253 | # warning "Using naive (probably slow) bswap operations" 254 | static inline 255 | uint16_t m3_bswap16(uint16_t x) { 256 | return ((( x >> 8 ) & 0xffu ) | (( x & 0xffu ) << 8 )); 257 | } 258 | static inline 259 | uint32_t m3_bswap32(uint32_t x) { 260 | return ((( x & 0xff000000u ) >> 24 ) | 261 | (( x & 0x00ff0000u ) >> 8 ) | 262 | (( x & 0x0000ff00u ) << 8 ) | 263 | (( x & 0x000000ffu ) << 24 )); 264 | } 265 | static inline 266 | uint64_t m3_bswap64(uint64_t x) { 267 | return ((( x & 0xff00000000000000ull ) >> 56 ) | 268 | (( x & 0x00ff000000000000ull ) >> 40 ) | 269 | (( x & 0x0000ff0000000000ull ) >> 24 ) | 270 | (( x & 0x000000ff00000000ull ) >> 8 ) | 271 | (( x & 0x00000000ff000000ull ) << 8 ) | 272 | (( x & 0x0000000000ff0000ull ) << 24 ) | 273 | (( x & 0x000000000000ff00ull ) << 40 ) | 274 | (( x & 0x00000000000000ffull ) << 56 )); 275 | } 276 | # endif 277 | # endif 278 | 279 | # if defined(M3_BIG_ENDIAN) 280 | # define M3_BSWAP_u8(X) {} 281 | # define M3_BSWAP_u16(X) { (X)=m3_bswap16((X)); } 282 | # define M3_BSWAP_u32(X) { (X)=m3_bswap32((X)); } 283 | # define M3_BSWAP_u64(X) { (X)=m3_bswap64((X)); } 284 | # define M3_BSWAP_i8(X) {} 285 | # define M3_BSWAP_i16(X) M3_BSWAP_u16(X) 286 | # define M3_BSWAP_i32(X) M3_BSWAP_u32(X) 287 | # define M3_BSWAP_i64(X) M3_BSWAP_u64(X) 288 | # define M3_BSWAP_f32(X) { union { f32 f; u32 i; } u; u.f = (X); M3_BSWAP_u32(u.i); (X) = u.f; } 289 | # define M3_BSWAP_f64(X) { union { f64 f; u64 i; } u; u.f = (X); M3_BSWAP_u64(u.i); (X) = u.f; } 290 | # else 291 | # define M3_BSWAP_u8(X) {} 292 | # define M3_BSWAP_u16(x) {} 293 | # define M3_BSWAP_u32(x) {} 294 | # define M3_BSWAP_u64(x) {} 295 | # define M3_BSWAP_i8(X) {} 296 | # define M3_BSWAP_i16(X) {} 297 | # define M3_BSWAP_i32(X) {} 298 | # define M3_BSWAP_i64(X) {} 299 | # define M3_BSWAP_f32(X) {} 300 | # define M3_BSWAP_f64(X) {} 301 | # endif 302 | 303 | # if defined(M3_COMPILER_GCC) || defined(M3_COMPILER_CLANG) || defined(M3_COMPILER_ICC) 304 | # define UNLIKELY(x) __builtin_expect(!!(x), 0) 305 | # define LIKELY(x) __builtin_expect(!!(x), 1) 306 | # else 307 | # define UNLIKELY(x) (x) 308 | # define LIKELY(x) (x) 309 | # endif 310 | 311 | 312 | # if defined(M3_COMPILER_MSVC) 313 | # define M3_WEAK //__declspec(selectany) 314 | # elif defined(__MINGW32__) 315 | # define M3_WEAK //__attribute__((selectany)) 316 | # else 317 | # define M3_WEAK __attribute__((weak)) 318 | # endif 319 | 320 | # ifndef M3_MIN 321 | # define M3_MIN(A,B) (((A) < (B)) ? (A) : (B)) 322 | # endif 323 | # ifndef M3_MAX 324 | # define M3_MAX(A,B) (((A) > (B)) ? (A) : (B)) 325 | # endif 326 | 327 | #define M3_INIT(field) memset(&field, 0, sizeof(field)) 328 | 329 | #define M3_COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) 330 | 331 | #if defined(__AVR__) 332 | 333 | # define PRIu64 "llu" 334 | # define PRIi64 "lli" 335 | 336 | # define d_m3ShortTypesDefined 337 | typedef double f64; 338 | typedef float f32; 339 | typedef uint64_t u64; 340 | typedef int64_t i64; 341 | typedef uint32_t u32; 342 | typedef int32_t i32; 343 | typedef short unsigned u16; 344 | typedef short i16; 345 | typedef uint8_t u8; 346 | typedef int8_t i8; 347 | 348 | #endif 349 | 350 | /* 351 | * Apply settings 352 | */ 353 | 354 | # if defined (M3_COMPILER_MSVC) 355 | # define vectorcall // For MSVC, better not to specify any call convention 356 | # elif defined(WIN32) 357 | # define vectorcall __vectorcall 358 | # elif defined (ESP8266) 359 | # include 360 | # define op_section //ICACHE_FLASH_ATTR 361 | # elif defined (ESP32) 362 | # if defined(M3_IN_IRAM) // the interpreter is in IRAM, attribute not needed 363 | # define op_section 364 | # else 365 | # include "esp_system.h" 366 | # define op_section IRAM_ATTR 367 | # endif 368 | # elif defined (FOMU) 369 | # define op_section __attribute__((section(".ramtext"))) 370 | # endif 371 | 372 | #ifndef vectorcall 373 | #define vectorcall 374 | #endif 375 | 376 | #ifndef op_section 377 | #define op_section 378 | #endif 379 | 380 | 381 | /* 382 | * Device-specific defaults 383 | */ 384 | 385 | # ifndef d_m3MaxFunctionStackHeight 386 | # if defined(ESP8266) || defined(ESP32) || defined(ARDUINO_AMEBA) || defined(TEENSYDUINO) 387 | # define d_m3MaxFunctionStackHeight 128 388 | # endif 389 | # endif 390 | 391 | # ifndef d_m3FixedHeap 392 | # if defined(ARDUINO_AMEBA) 393 | # define d_m3FixedHeap (128*1024) 394 | # elif defined(BLUE_PILL) || defined(FOMU) 395 | # define d_m3FixedHeap (12*1024) 396 | # elif defined(ARDUINO_ARCH_ARC32) // Arduino 101 397 | # define d_m3FixedHeap (10*1024) 398 | # endif 399 | # endif 400 | 401 | /* 402 | * Platform-specific defaults 403 | */ 404 | 405 | # if defined(ARDUINO) || defined(PARTICLE) || defined(PLATFORMIO) || defined(__MBED__) || \ 406 | defined(ESP8266) || defined(ESP32) || defined(BLUE_PILL) || defined(WM_W600) || defined(FOMU) 407 | # ifndef d_m3LogOutput 408 | # define d_m3LogOutput false 409 | # endif 410 | # ifndef d_m3VerboseLogs 411 | # define d_m3VerboseLogs false 412 | # endif 413 | # ifndef d_m3MaxFunctionStackHeight 414 | # define d_m3MaxFunctionStackHeight 64 415 | # endif 416 | # ifndef d_m3CodePageAlignSize 417 | # define d_m3CodePageAlignSize 1024 418 | # endif 419 | # endif 420 | 421 | #endif // m3_config_platforms_h 422 | -------------------------------------------------------------------------------- /include/m3_core.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_core.h 3 | // 4 | // Created by Steven Massey on 4/15/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_core_h 9 | #define m3_core_h 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "wasm3.h" 18 | #include "m3_config.h" 19 | 20 | # if defined(__cplusplus) 21 | # define d_m3BeginExternC extern "C" { 22 | # define d_m3EndExternC } 23 | # else 24 | # define d_m3BeginExternC 25 | # define d_m3EndExternC 26 | # endif 27 | 28 | d_m3BeginExternC 29 | 30 | #if !defined(d_m3ShortTypesDefined) 31 | #if d_m3HasFloat 32 | typedef double f64; 33 | typedef float f32; 34 | #endif 35 | 36 | typedef uint64_t u64; 37 | typedef int64_t i64; 38 | typedef uint32_t u32; 39 | typedef int32_t i32; 40 | typedef uint16_t u16; 41 | typedef int16_t i16; 42 | typedef uint8_t u8; 43 | typedef int8_t i8; 44 | #endif // d_m3ShortTypesDefined 45 | 46 | typedef const void * m3ret_t; 47 | typedef const void * voidptr_t; 48 | typedef const char * cstr_t; 49 | typedef const char * const ccstr_t; 50 | typedef const u8 * bytes_t; 51 | typedef const u8 * const cbytes_t; 52 | 53 | typedef u16 m3opcode_t; 54 | 55 | typedef i64 m3reg_t; 56 | 57 | # if d_m3Use32BitSlots 58 | typedef u32 m3slot_t; 59 | # else 60 | typedef u64 m3slot_t; 61 | # endif 62 | 63 | typedef m3slot_t * m3stack_t; 64 | 65 | typedef 66 | const void * const cvptr_t; 67 | 68 | # define d_m3Log_parse d_m3LogParse // required for m3logif 69 | # define d_m3Log_stack d_m3LogWasmStack 70 | # define d_m3Log_runtime d_m3LogRuntime 71 | # define d_m3Log_exec d_m3LogExec 72 | # define d_m3Log_emit d_m3LogEmit 73 | 74 | # if d_m3LogOutput && defined (DEBUG) 75 | 76 | # define d_m3Log(CATEGORY, FMT, ...) printf (" %8s | " FMT, #CATEGORY, ##__VA_ARGS__); 77 | 78 | # if d_m3LogParse 79 | # define m3log_parse(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 80 | # else 81 | # define m3log_parse(...) {} 82 | # endif 83 | 84 | # if d_m3LogCompile 85 | # define m3log_compile(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 86 | # else 87 | # define m3log_compile(...) {} 88 | # endif 89 | 90 | # if d_m3LogWasmStack 91 | # define m3log_stack(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 92 | # else 93 | # define m3log_stack(...) {} 94 | # endif 95 | 96 | # if d_m3LogEmit 97 | # define m3log_emit(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 98 | # else 99 | # define m3log_emit(...) {} 100 | # endif 101 | 102 | # if d_m3LogCodePages 103 | # define m3log_code(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 104 | # else 105 | # define m3log_code(...) {} 106 | # endif 107 | 108 | # if d_m3LogModule 109 | # define m3log_module(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 110 | # else 111 | # define m3log_module(...) {} 112 | # endif 113 | 114 | # if d_m3LogRuntime 115 | # define m3log_runtime(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 116 | # else 117 | # define m3log_runtime(...) {} 118 | # endif 119 | 120 | # if d_m3LogExec 121 | # define m3log_exec(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) 122 | # else 123 | # define m3log_exec(...) {} 124 | # endif 125 | 126 | # define m3log(CATEGORY, FMT, ...) m3log_##CATEGORY (CATEGORY, FMT "\n", ##__VA_ARGS__) 127 | # define m3logif(CATEGORY, STATEMENT) m3log_##CATEGORY (CATEGORY, ""); if (d_m3Log_##CATEGORY) { STATEMENT; printf ("\n"); } 128 | # else 129 | # define m3log(CATEGORY, FMT, ...) {} 130 | # define m3logif(CATEGORY, STATEMENT) {} 131 | # endif 132 | 133 | 134 | # if (defined(DEBUG) || defined(ASSERTS)) && !defined(NASSERTS) 135 | # define d_m3Assert(ASS) assert (ASS) 136 | # else 137 | # define d_m3Assert(ASS) 138 | # endif 139 | 140 | typedef void /*const*/ * code_t; 141 | typedef code_t const * /*__restrict__*/ pc_t; 142 | 143 | 144 | typedef struct M3MemoryHeader 145 | { 146 | IM3Runtime runtime; 147 | void * maxStack; 148 | size_t length; 149 | } 150 | M3MemoryHeader; 151 | 152 | 153 | typedef struct M3CodePageHeader 154 | { 155 | struct M3CodePage * next; 156 | 157 | u32 lineIndex; 158 | u32 numLines; 159 | u32 sequence; // this is just used for debugging; could be removed 160 | u32 usageCount; 161 | } 162 | M3CodePageHeader; 163 | 164 | 165 | #define d_m3CodePageFreeLinesThreshold 4+2 // max is: select _sss & CallIndirect + 2 for bridge 166 | 167 | #define d_m3MemPageSize 65536 168 | 169 | #define d_m3Reg0SlotAlias 30000 170 | #define d_m3Fp0SlotAlias 30001 171 | 172 | #define d_m3MaxSaneUtf8Length 2000 173 | #define d_m3MaxSaneFunctionArgCount 1000 // still insane, but whatever 174 | 175 | #define d_externalKind_function 0 176 | #define d_externalKind_table 1 177 | #define d_externalKind_memory 2 178 | #define d_externalKind_global 3 179 | 180 | static const char * const c_waTypes [] = { "nil", "i32", "i64", "f32", "f64", "void", "void *" }; 181 | static const char * const c_waCompactTypes [] = { "0", "i", "I", "f", "F", "v", "*" }; 182 | 183 | 184 | # if d_m3VerboseLogs 185 | 186 | M3Result m3Error (M3Result i_result, IM3Runtime i_runtime, IM3Module i_module, IM3Function i_function, 187 | const char * const i_file, u32 i_lineNum, const char * const i_errorMessage, ...); 188 | 189 | # define _m3Error(RESULT, RT, MOD, FUN, FILE, LINE, FORMAT, ...) \ 190 | m3Error (RESULT, RT, MOD, FUN, FILE, LINE, FORMAT, ##__VA_ARGS__) 191 | 192 | # else 193 | # define _m3Error(RESULT, RT, MOD, FUN, FILE, LINE, FORMAT, ...) (RESULT) 194 | # endif 195 | 196 | #define ErrorRuntime(RESULT, RUNTIME, FORMAT, ...) _m3Error (RESULT, RUNTIME, NULL, NULL, __FILE__, __LINE__, FORMAT, ##__VA_ARGS__) 197 | #define ErrorModule(RESULT, MOD, FORMAT, ...) _m3Error (RESULT, MOD->runtime, MOD, NULL, __FILE__, __LINE__, FORMAT, ##__VA_ARGS__) 198 | #define ErrorCompile(RESULT, COMP, FORMAT, ...) _m3Error (RESULT, COMP->runtime, COMP->module, NULL, __FILE__, __LINE__, FORMAT, ##__VA_ARGS__) 199 | 200 | #if d_m3LogNativeStack 201 | void m3StackCheckInit (); 202 | void m3StackCheck (); 203 | size_t m3StackGetMax (); 204 | #else 205 | #define m3StackCheckInit() 206 | #define m3StackCheck() 207 | #define m3StackGetMax() 0 208 | #endif 209 | 210 | void m3Abort (const char* message); 211 | 212 | M3Result m3_Malloc (void ** o_ptr, size_t i_size); 213 | M3Result m3_Realloc (void ** io_ptr, size_t i_newSize, size_t i_oldSize); 214 | void m3_Free (void ** io_ptr); 215 | M3Result m3_CopyMem (void ** o_to, const void * i_from, size_t i_size); 216 | 217 | #define m3Alloc(OPTR, STRUCT, NUM) m3_Malloc ((void **) OPTR, sizeof (STRUCT) * (NUM)) 218 | #define m3ReallocArray(PTR, STRUCT, NEW, OLD) m3_Realloc ((void **) (PTR), sizeof (STRUCT) * (NEW), sizeof (STRUCT) * (OLD)) 219 | #define m3Reallocate(_ptr, _newSize, _oldSize) m3_Realloc ((void **) _ptr, _newSize, _oldSize) 220 | #define m3Free(P) m3_Free ((void **)(& P)); 221 | #define m3CopyMem(_to, _from, _size) m3_CopyMem ((void **) _to, (void *) _from, _size) 222 | 223 | M3Result NormalizeType (u8 * o_type, i8 i_convolutedWasmType); 224 | 225 | bool IsIntType (u8 i_wasmType); 226 | bool IsFpType (u8 i_wasmType); 227 | bool Is64BitType (u8 i_m3Type); 228 | u32 SizeOfType (u8 i_m3Type); 229 | 230 | M3Result Read_u64 (u64 * o_value, bytes_t * io_bytes, cbytes_t i_end); 231 | M3Result Read_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end); 232 | #if d_m3HasFloat 233 | M3Result Read_f64 (f64 * o_value, bytes_t * io_bytes, cbytes_t i_end); 234 | M3Result Read_f32 (f32 * o_value, bytes_t * io_bytes, cbytes_t i_end); 235 | #endif 236 | M3Result Read_u8 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end); 237 | 238 | M3Result ReadLebUnsigned (u64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end); 239 | M3Result ReadLebSigned (i64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end); 240 | M3Result ReadLEB_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end); 241 | M3Result ReadLEB_u7 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end); 242 | M3Result ReadLEB_i7 (i8 * o_value, bytes_t * io_bytes, cbytes_t i_end); 243 | M3Result ReadLEB_i32 (i32 * o_value, bytes_t * io_bytes, cbytes_t i_end); 244 | M3Result ReadLEB_i64 (i64 * o_value, bytes_t * io_bytes, cbytes_t i_end); 245 | M3Result Read_utf8 (cstr_t * o_utf8, bytes_t * io_bytes, cbytes_t i_end); 246 | 247 | size_t SPrintArg (char * o_string, size_t i_n, m3stack_t i_sp, u8 i_type); 248 | 249 | void ReportError (IM3Runtime io_runtime, IM3Module i_module, IM3Function i_function, ccstr_t i_errorMessage, ccstr_t i_file, u32 i_lineNum); 250 | 251 | d_m3EndExternC 252 | 253 | #endif // m3_core_h 254 | -------------------------------------------------------------------------------- /include/m3_emit.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_emit.h 3 | // 4 | // Created by Steven Massey on 7/9/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_emit_h 9 | #define m3_emit_h 10 | 11 | #include "m3_compile.h" 12 | 13 | d_m3BeginExternC 14 | 15 | M3Result BridgeToNewPageIfNecessary (IM3Compilation o); 16 | M3Result EnsureCodePageNumLines (IM3Compilation o, u32 i_numLines); 17 | 18 | M3Result EmitOp (IM3Compilation o, IM3Operation i_operation); 19 | void EmitConstant32 (IM3Compilation o, const u32 i_immediate); 20 | void EmitSlotOffset (IM3Compilation o, const i32 i_offset); 21 | void EmitPointer (IM3Compilation o, const void * const i_pointer); 22 | void * ReservePointer (IM3Compilation o); 23 | 24 | pc_t GetPC (IM3Compilation o); 25 | 26 | d_m3EndExternC 27 | 28 | #endif // m3_emit_h 29 | -------------------------------------------------------------------------------- /include/m3_env.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_env.h 3 | // 4 | // Created by Steven Massey on 4/19/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_env_h 9 | #define m3_env_h 10 | 11 | #include "wasm3.h" 12 | #include "m3_code.h" 13 | #include "m3_exec.h" 14 | #include "m3_compile.h" 15 | 16 | d_m3BeginExternC 17 | 18 | typedef struct M3FuncType 19 | { 20 | struct M3FuncType * next; 21 | 22 | u32 numArgs; 23 | u8 returnType; 24 | u8 argTypes [3]; // M3FuncType is a dynamically sized object; these are padding 25 | } 26 | M3FuncType; 27 | 28 | typedef M3FuncType * IM3FuncType; 29 | 30 | M3Result AllocFuncType (IM3FuncType * o_functionType, u32 i_numArgs); 31 | bool AreFuncTypesEqual (const IM3FuncType i_typeA, const IM3FuncType i_typeB); 32 | 33 | 34 | //--------------------------------------------------------------------------------------------------------------------------------- 35 | typedef struct M3Function 36 | { 37 | struct M3Module * module; 38 | 39 | M3ImportInfo import; 40 | 41 | bytes_t wasm; 42 | bytes_t wasmEnd; 43 | 44 | cstr_t name; 45 | 46 | IM3FuncType funcType; 47 | 48 | pc_t compiled; 49 | 50 | # if (d_m3EnableCodePageRefCounting) 51 | IM3CodePage * codePageRefs; // array of all pages used 52 | u32 numCodePageRefs; 53 | # endif 54 | 55 | # if defined(DEBUG) 56 | u32 hits; 57 | # endif 58 | 59 | u16 maxStackSlots; 60 | 61 | u16 numArgSlots; 62 | 63 | u16 numLocals; // not including args 64 | u16 numLocalBytes; 65 | 66 | void * constants; 67 | u16 numConstantBytes; 68 | 69 | bool ownsWasmCode; 70 | } 71 | M3Function; 72 | 73 | void Function_Release (IM3Function i_function); 74 | void Function_FreeCompiledCode (IM3Function i_function); 75 | 76 | cstr_t GetFunctionImportModuleName (IM3Function i_function); 77 | cstr_t GetFunctionName (IM3Function i_function); 78 | u32 GetFunctionNumArgs (IM3Function i_function); 79 | u32 GetFunctionNumReturns (IM3Function i_function); 80 | u8 GetFunctionReturnType (IM3Function i_function); 81 | 82 | u32 GetFunctionNumArgsAndLocals (IM3Function i_function); 83 | 84 | cstr_t SPrintFunctionArgList (IM3Function i_function, m3stack_t i_sp); 85 | 86 | 87 | //--------------------------------------------------------------------------------------------------------------------------------- 88 | 89 | typedef struct M3MemoryInfo 90 | { 91 | u32 initPages; 92 | u32 maxPages; 93 | } 94 | M3MemoryInfo; 95 | 96 | 97 | typedef struct M3Memory 98 | { 99 | M3MemoryHeader * mallocated; 100 | 101 | u32 numPages; 102 | u32 maxPages; 103 | } 104 | M3Memory; 105 | 106 | typedef M3Memory * IM3Memory; 107 | 108 | 109 | //--------------------------------------------------------------------------------------------------------------------------------- 110 | 111 | typedef struct M3DataSegment 112 | { 113 | const u8 * initExpr; // wasm code 114 | const u8 * data; 115 | 116 | u32 initExprSize; 117 | u32 memoryRegion; 118 | u32 size; 119 | } 120 | M3DataSegment; 121 | 122 | 123 | void FreeImportInfo (M3ImportInfo * i_info); 124 | 125 | //--------------------------------------------------------------------------------------------------------------------------------- 126 | 127 | typedef struct M3Global 128 | { 129 | M3ImportInfo import; 130 | 131 | union 132 | { 133 | i64 intValue; 134 | #if d_m3HasFloat 135 | f64 f64Value; 136 | f32 f32Value; 137 | #endif 138 | }; 139 | 140 | bytes_t initExpr; // wasm code 141 | u32 initExprSize; 142 | u8 type; 143 | bool imported; 144 | bool isMutable; 145 | } 146 | M3Global; 147 | typedef M3Global * IM3Global; 148 | 149 | 150 | //--------------------------------------------------------------------------------------------------------------------------------- 151 | typedef struct M3Module 152 | { 153 | struct M3Runtime * runtime; 154 | struct M3Environment * environment; 155 | 156 | cstr_t name; 157 | 158 | u32 numFuncTypes; 159 | IM3FuncType * funcTypes; // array of pointers to list of FuncTypes 160 | 161 | u32 numImports; 162 | IM3Function * imports; // notice: "I" prefix. imports are pointers to functions in another module. 163 | 164 | u32 numFunctions; 165 | M3Function * functions; 166 | 167 | i32 startFunction; 168 | 169 | u32 numDataSegments; 170 | M3DataSegment * dataSegments; 171 | 172 | u32 importedGlobals; 173 | u32 numGlobals; 174 | M3Global * globals; 175 | 176 | u32 numElementSegments; 177 | bytes_t elementSection; 178 | bytes_t elementSectionEnd; 179 | 180 | IM3Function * table0; 181 | u32 table0Size; 182 | 183 | M3MemoryInfo memoryInfo; 184 | bool memoryImported; 185 | 186 | bool hasWasmCodeCopy; 187 | 188 | struct M3Module * next; 189 | } 190 | M3Module; 191 | 192 | M3Result Module_AddGlobal (IM3Module io_module, IM3Global * o_global, u8 i_type, bool i_mutable, bool i_isImported); 193 | 194 | M3Result Module_AddFunction (IM3Module io_module, u32 i_typeIndex, IM3ImportInfo i_importInfo /* can be null */); 195 | IM3Function Module_GetFunction (IM3Module i_module, u32 i_functionIndex); 196 | 197 | //--------------------------------------------------------------------------------------------------------------------------------- 198 | 199 | static const u32 c_m3NumTypesPerPage = 8; 200 | 201 | //--------------------------------------------------------------------------------------------------------------------------------- 202 | 203 | typedef struct M3Environment 204 | { 205 | // struct M3Runtime * runtimes; 206 | 207 | IM3FuncType funcTypes; // linked list 208 | 209 | M3CodePage * pagesReleased; 210 | } 211 | M3Environment; 212 | 213 | void Environment_Release (IM3Environment i_environment); 214 | 215 | // takes ownership of io_funcType and returns a pointer to the persistent version (could be same or different) 216 | void Environment_AddFuncType (IM3Environment i_environment, IM3FuncType * io_funcType); 217 | 218 | //--------------------------------------------------------------------------------------------------------------------------------- 219 | 220 | // OPTZ: function types need to move to the runtime structure so that all modules can share types 221 | // then type equality can be a simple pointer compare for indirect call checks 222 | typedef struct M3Runtime 223 | { 224 | M3Compilation compilation; 225 | 226 | IM3Environment environment; 227 | 228 | M3CodePage * pagesOpen; // linked list of code pages with writable space on them 229 | M3CodePage * pagesFull; // linked list of at-capacity pages 230 | 231 | u32 numCodePages; 232 | u32 numActiveCodePages; 233 | 234 | IM3Module modules; // linked list of imported modules 235 | 236 | void * stack; 237 | u32 stackSize; 238 | u32 numStackSlots; 239 | 240 | u32 argc; 241 | ccstr_t * argv; 242 | 243 | M3Result runtimeError; 244 | 245 | M3Memory memory; 246 | u32 memoryLimit; 247 | 248 | M3ErrorInfo error; 249 | #if d_m3VerboseLogs 250 | char error_message[256]; 251 | #endif 252 | i32 exit_code; 253 | } 254 | M3Runtime; 255 | 256 | void InitRuntime (IM3Runtime io_runtime, u32 i_stackSizeInBytes); 257 | void Runtime_Release (IM3Runtime io_runtime); 258 | 259 | M3Result ResizeMemory (IM3Runtime io_runtime, u32 i_numPages); 260 | 261 | typedef void * (* ModuleVisitor) (IM3Module i_module, void * i_info); 262 | void * ForEachModule (IM3Runtime i_runtime, ModuleVisitor i_visitor, void * i_info); 263 | 264 | void * v_FindFunction (IM3Module i_module, const char * const i_name); 265 | 266 | IM3CodePage AcquireCodePage (IM3Runtime io_runtime); 267 | IM3CodePage AcquireCodePageWithCapacity (IM3Runtime io_runtime, u32 i_lineCount); 268 | void ReleaseCodePage (IM3Runtime io_runtime, IM3CodePage i_codePage); 269 | 270 | M3Result m3Error (M3Result i_result, IM3Runtime i_runtime, IM3Module i_module, IM3Function i_function, const char * const i_file, u32 i_lineNum, const char * const i_errorMessage, ...); 271 | 272 | d_m3EndExternC 273 | 274 | #endif // m3_env_h 275 | -------------------------------------------------------------------------------- /include/m3_exception.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_exception.h 3 | // 4 | // Created by Steven Massey on 7/5/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_exception_h 9 | #define m3_exception_h 10 | 11 | #include "m3_config.h" 12 | 13 | // some macros to emulate try/catch 14 | #define EXCEPTION_PRINT //puts("Exc: " __FILE__ ":" M3_STR(__LINE__) "\n"); 15 | 16 | #define _try 17 | #define _(TRY) { result = TRY; if (result) { EXCEPTION_PRINT; goto _catch; } } 18 | #define _throw(ERROR) { result = ERROR; EXCEPTION_PRINT; goto _catch; } 19 | #define _throwif(ERROR, COND) if (UNLIKELY(COND)) \ 20 | { result = ERROR; EXCEPTION_PRINT; goto _catch; } 21 | 22 | #endif // m3_exception_h 23 | -------------------------------------------------------------------------------- /include/m3_exec.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_exec.h 3 | // 4 | // Created by Steven Massey on 4/17/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | 7 | 8 | #ifndef m3_exec_h 9 | #define m3_exec_h 10 | 11 | // TODO: all these functions could move over to the .c at some point. normally, i'd say screw it, 12 | // but it might prove useful to be able to compile m3_exec alone w/ optimizations while the remaining 13 | // code is at debug O0 14 | 15 | 16 | // About the naming convention of these operations/macros (_rs, _sr_, _ss, _srs, etc.) 17 | //------------------------------------------------------------------------------------------------------ 18 | // - 'r' means register and 's' means slot 19 | // - the first letter is the top of the stack 20 | // 21 | // so, for example, _rs means the first operand (the first thing pushed to the stack) is in a slot 22 | // and the second operand (the top of the stack) is in a register 23 | //------------------------------------------------------------------------------------------------------ 24 | 25 | 26 | #include "m3_exec_defs.h" 27 | #include "m3_math_utils.h" 28 | 29 | #include 30 | 31 | d_m3BeginExternC 32 | 33 | # define rewrite_op(OP) * ((void **) (_pc-1)) = (void*)(OP) 34 | 35 | # define d_m3RetSig static inline m3ret_t vectorcall 36 | # define d_m3Op(NAME) op_section d_m3RetSig op_##NAME (d_m3OpSig) 37 | 38 | # define d_m3OpDef(NAME) op_section m3ret_t vectorcall op_##NAME (d_m3OpSig) 39 | # define d_m3OpDecl(NAME) m3ret_t vectorcall op_##NAME (d_m3OpSig); 40 | 41 | # define immediate(TYPE) * ((TYPE *) _pc++) 42 | # define skip_immediate(TYPE) (_pc++) 43 | 44 | # define slot(TYPE) * (TYPE *) (_sp + immediate (i32)) 45 | # define slot_ptr(TYPE) (TYPE *) (_sp + immediate (i32)) 46 | 47 | #define nextOpDirect() ((IM3Operation)(* _pc))(_pc + 1, d_m3OpArgs) 48 | #define jumpOpDirect(PC) ((IM3Operation)(* PC))( PC + 1, d_m3OpArgs) 49 | 50 | # if d_m3EnableOpProfiling 51 | d_m3RetSig profileOp (d_m3OpSig, cstr_t i_operationName); 52 | # define nextOp() return profileOp (d_m3OpAllArgs, __FUNCTION__) 53 | # elif d_m3EnableOpTracing 54 | d_m3RetSig debugOp (d_m3OpSig, cstr_t i_operationName); 55 | # define nextOp() return debugOp (d_m3OpAllArgs, __FUNCTION__) 56 | # else 57 | # define nextOp() return nextOpDirect() 58 | # endif 59 | 60 | #define jumpOp(PC) jumpOpDirect((pc_t)PC) 61 | 62 | d_m3RetSig Call (d_m3OpSig) 63 | { 64 | m3ret_t possible_trap = m3_Yield (); 65 | if (UNLIKELY(possible_trap)) return possible_trap; 66 | 67 | return nextOpDirect(); 68 | } 69 | 70 | // TODO: OK, this needs some explanation here ;0 71 | 72 | #define d_m3CommutativeOpMacro(RES, REG, TYPE, NAME, OP, ...) \ 73 | d_m3Op(TYPE##_##NAME##_rs) \ 74 | { \ 75 | TYPE operand = slot (TYPE); \ 76 | OP((RES), operand, ((TYPE) REG), ##__VA_ARGS__); \ 77 | nextOp (); \ 78 | } \ 79 | d_m3Op(TYPE##_##NAME##_ss) \ 80 | { \ 81 | TYPE operand2 = slot (TYPE); \ 82 | TYPE operand1 = slot (TYPE); \ 83 | OP((RES), operand1, operand2, ##__VA_ARGS__); \ 84 | nextOp (); \ 85 | } 86 | 87 | #define d_m3OpMacro(RES, REG, TYPE, NAME, OP, ...) \ 88 | d_m3Op(TYPE##_##NAME##_sr) \ 89 | { \ 90 | TYPE operand = slot (TYPE); \ 91 | OP((RES), ((TYPE) REG), operand, ##__VA_ARGS__); \ 92 | nextOp (); \ 93 | } \ 94 | d_m3CommutativeOpMacro(RES, REG, TYPE,NAME, OP, ##__VA_ARGS__) 95 | 96 | // Accept macros 97 | #define d_m3CommutativeOpMacro_i(TYPE, NAME, MACRO, ...) d_m3CommutativeOpMacro ( _r0, _r0, TYPE, NAME, MACRO, ##__VA_ARGS__) 98 | #define d_m3OpMacro_i(TYPE, NAME, MACRO, ...) d_m3OpMacro ( _r0, _r0, TYPE, NAME, MACRO, ##__VA_ARGS__) 99 | #define d_m3CommutativeOpMacro_f(TYPE, NAME, MACRO, ...) d_m3CommutativeOpMacro (_fp0, _fp0, TYPE, NAME, MACRO, ##__VA_ARGS__) 100 | #define d_m3OpMacro_f(TYPE, NAME, MACRO, ...) d_m3OpMacro (_fp0, _fp0, TYPE, NAME, MACRO, ##__VA_ARGS__) 101 | 102 | #define M3_FUNC(RES, A, B, OP) (RES) = OP((A), (B)) // Accept functions: res = OP(a,b) 103 | #define M3_OPER(RES, A, B, OP) (RES) = ((A) OP (B)) // Accept operators: res = a OP b 104 | 105 | #define d_m3CommutativeOpFunc_i(TYPE, NAME, OP) d_m3CommutativeOpMacro_i (TYPE, NAME, M3_FUNC, OP) 106 | #define d_m3OpFunc_i(TYPE, NAME, OP) d_m3OpMacro_i (TYPE, NAME, M3_FUNC, OP) 107 | #define d_m3CommutativeOpFunc_f(TYPE, NAME, OP) d_m3CommutativeOpMacro_f (TYPE, NAME, M3_FUNC, OP) 108 | #define d_m3OpFunc_f(TYPE, NAME, OP) d_m3OpMacro_f (TYPE, NAME, M3_FUNC, OP) 109 | 110 | #define d_m3CommutativeOp_i(TYPE, NAME, OP) d_m3CommutativeOpMacro_i (TYPE, NAME, M3_OPER, OP) 111 | #define d_m3Op_i(TYPE, NAME, OP) d_m3OpMacro_i (TYPE, NAME, M3_OPER, OP) 112 | #define d_m3CommutativeOp_f(TYPE, NAME, OP) d_m3CommutativeOpMacro_f (TYPE, NAME, M3_OPER, OP) 113 | #define d_m3Op_f(TYPE, NAME, OP) d_m3OpMacro_f (TYPE, NAME, M3_OPER, OP) 114 | 115 | // compare needs to be distinct for fp 'cause the result must be _r0 116 | #define d_m3CompareOp_f(TYPE, NAME, OP) d_m3OpMacro (_r0, _fp0, TYPE, NAME, M3_OPER, OP) 117 | #define d_m3CommutativeCmpOp_f(TYPE, NAME, OP) d_m3CommutativeOpMacro (_r0, _fp0, TYPE, NAME, M3_OPER, OP) 118 | 119 | 120 | //----------------------- 121 | 122 | // signed 123 | d_m3CommutativeOp_i (i32, Equal, ==) d_m3CommutativeOp_i (i64, Equal, ==) 124 | d_m3CommutativeOp_i (i32, NotEqual, !=) d_m3CommutativeOp_i (i64, NotEqual, !=) 125 | 126 | d_m3Op_i (i32, LessThan, < ) d_m3Op_i (i64, LessThan, < ) 127 | d_m3Op_i (i32, GreaterThan, > ) d_m3Op_i (i64, GreaterThan, > ) 128 | d_m3Op_i (i32, LessThanOrEqual, <=) d_m3Op_i (i64, LessThanOrEqual, <=) 129 | d_m3Op_i (i32, GreaterThanOrEqual, >=) d_m3Op_i (i64, GreaterThanOrEqual, >=) 130 | 131 | // unsigned 132 | d_m3Op_i (u32, LessThan, < ) d_m3Op_i (u64, LessThan, < ) 133 | d_m3Op_i (u32, GreaterThan, > ) d_m3Op_i (u64, GreaterThan, > ) 134 | d_m3Op_i (u32, LessThanOrEqual, <=) d_m3Op_i (u64, LessThanOrEqual, <=) 135 | d_m3Op_i (u32, GreaterThanOrEqual, >=) d_m3Op_i (u64, GreaterThanOrEqual, >=) 136 | 137 | #if d_m3HasFloat 138 | d_m3CommutativeCmpOp_f (f32, Equal, ==) d_m3CommutativeCmpOp_f (f64, Equal, ==) 139 | d_m3CommutativeCmpOp_f (f32, NotEqual, !=) d_m3CommutativeCmpOp_f (f64, NotEqual, !=) 140 | d_m3CompareOp_f (f32, LessThan, < ) d_m3CompareOp_f (f64, LessThan, < ) 141 | d_m3CompareOp_f (f32, GreaterThan, > ) d_m3CompareOp_f (f64, GreaterThan, > ) 142 | d_m3CompareOp_f (f32, LessThanOrEqual, <=) d_m3CompareOp_f (f64, LessThanOrEqual, <=) 143 | d_m3CompareOp_f (f32, GreaterThanOrEqual, >=) d_m3CompareOp_f (f64, GreaterThanOrEqual, >=) 144 | #endif 145 | 146 | d_m3CommutativeOp_i (i32, Add, +) d_m3CommutativeOp_i (i64, Add, +) 147 | d_m3CommutativeOp_i (i32, Multiply, *) d_m3CommutativeOp_i (i64, Multiply, *) 148 | 149 | d_m3Op_i (i32, Subtract, -) d_m3Op_i (i64, Subtract, -) 150 | 151 | #define OP_SHL_32(X,N) ((X) << ((u32)(N) % 32)) 152 | #define OP_SHL_64(X,N) ((X) << ((u64)(N) % 64)) 153 | #define OP_SHR_32(X,N) ((X) >> ((u32)(N) % 32)) 154 | #define OP_SHR_64(X,N) ((X) >> ((u64)(N) % 64)) 155 | 156 | d_m3OpFunc_i (u32, ShiftLeft, OP_SHL_32) d_m3OpFunc_i (u64, ShiftLeft, OP_SHL_64) 157 | d_m3OpFunc_i (i32, ShiftRight, OP_SHR_32) d_m3OpFunc_i (i64, ShiftRight, OP_SHR_64) 158 | d_m3OpFunc_i (u32, ShiftRight, OP_SHR_32) d_m3OpFunc_i (u64, ShiftRight, OP_SHR_64) 159 | 160 | d_m3CommutativeOp_i (u32, And, &) 161 | d_m3CommutativeOp_i (u32, Or, |) 162 | d_m3CommutativeOp_i (u32, Xor, ^) 163 | 164 | d_m3CommutativeOp_i (u64, And, &) 165 | d_m3CommutativeOp_i (u64, Or, |) 166 | d_m3CommutativeOp_i (u64, Xor, ^) 167 | 168 | #if d_m3HasFloat 169 | d_m3CommutativeOp_f (f32, Add, +) d_m3CommutativeOp_f (f64, Add, +) 170 | d_m3CommutativeOp_f (f32, Multiply, *) d_m3CommutativeOp_f (f64, Multiply, *) 171 | d_m3Op_f (f32, Subtract, -) d_m3Op_f (f64, Subtract, -) 172 | d_m3Op_f (f32, Divide, /) d_m3Op_f (f64, Divide, /) 173 | #endif 174 | 175 | d_m3OpFunc_i(u32, Rotl, rotl32) 176 | d_m3OpFunc_i(u32, Rotr, rotr32) 177 | d_m3OpFunc_i(u64, Rotl, rotl64) 178 | d_m3OpFunc_i(u64, Rotr, rotr64) 179 | 180 | d_m3OpMacro_i(u32, Divide, OP_DIV_U); 181 | d_m3OpMacro_i(i32, Divide, OP_DIV_S, INT32_MIN); 182 | d_m3OpMacro_i(u64, Divide, OP_DIV_U); 183 | d_m3OpMacro_i(i64, Divide, OP_DIV_S, INT64_MIN); 184 | 185 | d_m3OpMacro_i(u32, Remainder, OP_REM_U); 186 | d_m3OpMacro_i(i32, Remainder, OP_REM_S, INT32_MIN); 187 | d_m3OpMacro_i(u64, Remainder, OP_REM_U); 188 | d_m3OpMacro_i(i64, Remainder, OP_REM_S, INT64_MIN); 189 | 190 | #if d_m3HasFloat 191 | d_m3OpFunc_f(f32, Min, min_f32); 192 | d_m3OpFunc_f(f32, Max, max_f32); 193 | d_m3OpFunc_f(f64, Min, min_f64); 194 | d_m3OpFunc_f(f64, Max, max_f64); 195 | 196 | d_m3OpFunc_f(f32, CopySign, copysignf); 197 | d_m3OpFunc_f(f64, CopySign, copysign); 198 | #endif 199 | 200 | // Unary operations 201 | // Note: This macro follows the principle of d_m3OpMacro 202 | 203 | #define d_m3UnaryMacro(RES, REG, TYPE, NAME, OP, ...) \ 204 | d_m3Op(TYPE##_##NAME##_r) \ 205 | { \ 206 | OP((RES), (TYPE) REG, ##__VA_ARGS__); \ 207 | nextOp (); \ 208 | } \ 209 | d_m3Op(TYPE##_##NAME##_s) \ 210 | { \ 211 | TYPE operand = slot (TYPE); \ 212 | OP((RES), operand, ##__VA_ARGS__); \ 213 | nextOp (); \ 214 | } 215 | 216 | #define M3_UNARY(RES, X, OP) (RES) = OP(X) 217 | #define d_m3UnaryOp_i(TYPE, NAME, OPERATION) d_m3UnaryMacro( _r0, _r0, TYPE, NAME, M3_UNARY, OPERATION) 218 | #define d_m3UnaryOp_f(TYPE, NAME, OPERATION) d_m3UnaryMacro(_fp0, _fp0, TYPE, NAME, M3_UNARY, OPERATION) 219 | 220 | #if d_m3HasFloat 221 | d_m3UnaryOp_f (f32, Abs, fabsf); d_m3UnaryOp_f (f64, Abs, fabs); 222 | d_m3UnaryOp_f (f32, Ceil, ceilf); d_m3UnaryOp_f (f64, Ceil, ceil); 223 | d_m3UnaryOp_f (f32, Floor, floorf); d_m3UnaryOp_f (f64, Floor, floor); 224 | d_m3UnaryOp_f (f32, Trunc, truncf); d_m3UnaryOp_f (f64, Trunc, trunc); 225 | d_m3UnaryOp_f (f32, Sqrt, sqrtf); d_m3UnaryOp_f (f64, Sqrt, sqrt); 226 | d_m3UnaryOp_f (f32, Nearest, rintf); d_m3UnaryOp_f (f64, Nearest, rint); 227 | d_m3UnaryOp_f (f32, Negate, -); d_m3UnaryOp_f (f64, Negate, -); 228 | #endif 229 | 230 | #define OP_EQZ(x) ((x) == 0) 231 | 232 | d_m3UnaryOp_i (i32, EqualToZero, OP_EQZ) 233 | d_m3UnaryOp_i (i64, EqualToZero, OP_EQZ) 234 | 235 | // clz(0), ctz(0) results are undefined for rest platforms, fix it 236 | #if (defined(__i386__) || defined(__x86_64__)) && !(defined(__AVX2__) || (defined(__ABM__) && defined(__BMI__))) 237 | #define OP_CLZ_32(x) (UNLIKELY((x) == 0) ? 32 : __builtin_clz(x)) 238 | #define OP_CTZ_32(x) (UNLIKELY((x) == 0) ? 32 : __builtin_ctz(x)) 239 | // for 64-bit instructions branchless approach more preferable 240 | #define OP_CLZ_64(x) (__builtin_clzll((x) | (1LL << 0)) + OP_EQZ(x)) 241 | #define OP_CTZ_64(x) (__builtin_ctzll((x) | (1LL << 63)) + OP_EQZ(x)) 242 | #elif defined(__ppc__) || defined(__ppc64__) 243 | // PowerPC is defined for __builtin_clz(0) and __builtin_ctz(0). 244 | // See (https://github.com/aquynh/capstone/blob/master/MathExtras.h#L99) 245 | #define OP_CLZ_32(x) __builtin_clz(x) 246 | #define OP_CTZ_32(x) __builtin_ctz(x) 247 | #define OP_CLZ_64(x) __builtin_clzll(x) 248 | #define OP_CTZ_64(x) __builtin_ctzll(x) 249 | #else 250 | #define OP_CLZ_32(x) (UNLIKELY((x) == 0) ? 32 : __builtin_clz(x)) 251 | #define OP_CTZ_32(x) (UNLIKELY((x) == 0) ? 32 : __builtin_ctz(x)) 252 | #define OP_CLZ_64(x) (UNLIKELY((x) == 0) ? 64 : __builtin_clzll(x)) 253 | #define OP_CTZ_64(x) (UNLIKELY((x) == 0) ? 64 : __builtin_ctzll(x)) 254 | #endif 255 | 256 | d_m3UnaryOp_i (u32, Clz, OP_CLZ_32) 257 | d_m3UnaryOp_i (u64, Clz, OP_CLZ_64) 258 | 259 | d_m3UnaryOp_i (u32, Ctz, OP_CTZ_32) 260 | d_m3UnaryOp_i (u64, Ctz, OP_CTZ_64) 261 | 262 | d_m3UnaryOp_i (u32, Popcnt, __builtin_popcount) 263 | d_m3UnaryOp_i (u64, Popcnt, __builtin_popcountll) 264 | 265 | #define OP_WRAP_I64(X) ((X) & 0x00000000ffffffff) 266 | 267 | d_m3Op(i32_Wrap_i64_r) 268 | { 269 | _r0 = OP_WRAP_I64((i64) _r0); 270 | nextOp (); 271 | } 272 | 273 | d_m3Op(i32_Wrap_i64_s) 274 | { 275 | i64 operand = slot (i64); 276 | _r0 = OP_WRAP_I64(operand); 277 | nextOp (); 278 | } 279 | 280 | // Integer sign extension operations 281 | #define OP_EXTEND8_S_I32(X) ((int32_t)(int8_t)(X)) 282 | #define OP_EXTEND16_S_I32(X) ((int32_t)(int16_t)(X)) 283 | #define OP_EXTEND8_S_I64(X) ((int64_t)(int8_t)(X)) 284 | #define OP_EXTEND16_S_I64(X) ((int64_t)(int16_t)(X)) 285 | #define OP_EXTEND32_S_I64(X) ((int64_t)(int32_t)(X)) 286 | 287 | d_m3UnaryOp_i (i32, Extend8_s, OP_EXTEND8_S_I32) 288 | d_m3UnaryOp_i (i32, Extend16_s, OP_EXTEND16_S_I32) 289 | d_m3UnaryOp_i (i64, Extend8_s, OP_EXTEND8_S_I64) 290 | d_m3UnaryOp_i (i64, Extend16_s, OP_EXTEND16_S_I64) 291 | d_m3UnaryOp_i (i64, Extend32_s, OP_EXTEND32_S_I64) 292 | 293 | #define d_m3TruncMacro(DEST, SRC, TYPE, NAME, FROM, OP, ...) \ 294 | d_m3Op(TYPE##_##NAME##_##FROM##_r_r) \ 295 | { \ 296 | OP((DEST), (FROM) SRC, ##__VA_ARGS__); \ 297 | nextOp (); \ 298 | } \ 299 | d_m3Op(TYPE##_##NAME##_##FROM##_r_s) \ 300 | { \ 301 | FROM * stack = slot_ptr (FROM); \ 302 | OP((DEST), (* stack), ##__VA_ARGS__); \ 303 | nextOp (); \ 304 | } \ 305 | d_m3Op(TYPE##_##NAME##_##FROM##_s_r) \ 306 | { \ 307 | TYPE * dest = slot_ptr (TYPE); \ 308 | OP((* dest), (FROM) SRC, ##__VA_ARGS__); \ 309 | nextOp (); \ 310 | } \ 311 | d_m3Op(TYPE##_##NAME##_##FROM##_s_s) \ 312 | { \ 313 | FROM * stack = slot_ptr (FROM); \ 314 | TYPE * dest = slot_ptr (TYPE); \ 315 | OP((* dest), (* stack), ##__VA_ARGS__); \ 316 | nextOp (); \ 317 | } 318 | 319 | #if d_m3HasFloat 320 | d_m3TruncMacro(_r0, _fp0, i32, Trunc, f32, OP_I32_TRUNC_F32) 321 | d_m3TruncMacro(_r0, _fp0, u32, Trunc, f32, OP_U32_TRUNC_F32) 322 | d_m3TruncMacro(_r0, _fp0, i32, Trunc, f64, OP_I32_TRUNC_F64) 323 | d_m3TruncMacro(_r0, _fp0, u32, Trunc, f64, OP_U32_TRUNC_F64) 324 | 325 | d_m3TruncMacro(_r0, _fp0, i64, Trunc, f32, OP_I64_TRUNC_F32) 326 | d_m3TruncMacro(_r0, _fp0, u64, Trunc, f32, OP_U64_TRUNC_F32) 327 | d_m3TruncMacro(_r0, _fp0, i64, Trunc, f64, OP_I64_TRUNC_F64) 328 | d_m3TruncMacro(_r0, _fp0, u64, Trunc, f64, OP_U64_TRUNC_F64) 329 | 330 | d_m3TruncMacro(_r0, _fp0, i32, TruncSat, f32, OP_I32_TRUNC_SAT_F32) 331 | d_m3TruncMacro(_r0, _fp0, u32, TruncSat, f32, OP_U32_TRUNC_SAT_F32) 332 | d_m3TruncMacro(_r0, _fp0, i32, TruncSat, f64, OP_I32_TRUNC_SAT_F64) 333 | d_m3TruncMacro(_r0, _fp0, u32, TruncSat, f64, OP_U32_TRUNC_SAT_F64) 334 | 335 | d_m3TruncMacro(_r0, _fp0, i64, TruncSat, f32, OP_I64_TRUNC_SAT_F32) 336 | d_m3TruncMacro(_r0, _fp0, u64, TruncSat, f32, OP_U64_TRUNC_SAT_F32) 337 | d_m3TruncMacro(_r0, _fp0, i64, TruncSat, f64, OP_I64_TRUNC_SAT_F64) 338 | d_m3TruncMacro(_r0, _fp0, u64, TruncSat, f64, OP_U64_TRUNC_SAT_F64) 339 | #endif 340 | 341 | #define d_m3TypeModifyOp(REG_TO, REG_FROM, TO, NAME, FROM) \ 342 | d_m3Op(TO##_##NAME##_##FROM##_r) \ 343 | { \ 344 | REG_TO = (TO) ((FROM) REG_FROM); \ 345 | nextOp (); \ 346 | } \ 347 | \ 348 | d_m3Op(TO##_##NAME##_##FROM##_s) \ 349 | { \ 350 | FROM from = slot (FROM); \ 351 | REG_TO = (TO) (from); \ 352 | nextOp (); \ 353 | } 354 | 355 | // Int to int 356 | d_m3TypeModifyOp (_r0, _r0, i64, Extend, i32); 357 | d_m3TypeModifyOp (_r0, _r0, i64, Extend, u32); 358 | 359 | // Float to float 360 | #if d_m3HasFloat 361 | d_m3TypeModifyOp (_fp0, _fp0, f32, Demote, f64); 362 | d_m3TypeModifyOp (_fp0, _fp0, f64, Promote, f32); 363 | #endif 364 | 365 | #define d_m3TypeConvertOp(REG_TO, REG_FROM, TO, NAME, FROM) \ 366 | d_m3Op(TO##_##NAME##_##FROM##_r_r) \ 367 | { \ 368 | REG_TO = (TO) ((FROM) REG_FROM); \ 369 | nextOp (); \ 370 | } \ 371 | \ 372 | d_m3Op(TO##_##NAME##_##FROM##_s_r) \ 373 | { \ 374 | slot (TO) = (TO) ((FROM) REG_FROM); \ 375 | nextOp (); \ 376 | } \ 377 | \ 378 | d_m3Op(TO##_##NAME##_##FROM##_r_s) \ 379 | { \ 380 | FROM from = slot (FROM); \ 381 | REG_TO = (TO) (from); \ 382 | nextOp (); \ 383 | } \ 384 | \ 385 | d_m3Op(TO##_##NAME##_##FROM##_s_s) \ 386 | { \ 387 | FROM from = slot (FROM); \ 388 | slot (TO) = (TO) (from); \ 389 | nextOp (); \ 390 | } 391 | 392 | // Int to float 393 | #if d_m3HasFloat 394 | d_m3TypeConvertOp (_fp0, _r0, f64, Convert, i32); 395 | d_m3TypeConvertOp (_fp0, _r0, f64, Convert, u32); 396 | d_m3TypeConvertOp (_fp0, _r0, f64, Convert, i64); 397 | d_m3TypeConvertOp (_fp0, _r0, f64, Convert, u64); 398 | 399 | d_m3TypeConvertOp (_fp0, _r0, f32, Convert, i32); 400 | d_m3TypeConvertOp (_fp0, _r0, f32, Convert, u32); 401 | d_m3TypeConvertOp (_fp0, _r0, f32, Convert, i64); 402 | d_m3TypeConvertOp (_fp0, _r0, f32, Convert, u64); 403 | #endif 404 | 405 | #define d_m3ReinterpretOp(REG, TO, SRC, FROM) \ 406 | d_m3Op(TO##_Reinterpret_##FROM##_r_r) \ 407 | { \ 408 | union { FROM c; TO t; } u; \ 409 | u.c = (FROM) SRC; \ 410 | REG = u.t; \ 411 | nextOp (); \ 412 | } \ 413 | \ 414 | d_m3Op(TO##_Reinterpret_##FROM##_r_s) \ 415 | { \ 416 | union { FROM c; TO t; } u; \ 417 | u.c = slot (FROM); \ 418 | REG = u.t; \ 419 | nextOp (); \ 420 | } \ 421 | \ 422 | d_m3Op(TO##_Reinterpret_##FROM##_s_r) \ 423 | { \ 424 | union { FROM c; TO t; } u; \ 425 | u.c = (FROM) SRC; \ 426 | slot (TO) = u.t; \ 427 | nextOp (); \ 428 | } \ 429 | \ 430 | d_m3Op(TO##_Reinterpret_##FROM##_s_s) \ 431 | { \ 432 | union { FROM c; TO t; } u; \ 433 | u.c = slot (FROM); \ 434 | slot (TO) = u.t; \ 435 | nextOp (); \ 436 | } 437 | 438 | #if d_m3HasFloat 439 | d_m3ReinterpretOp (_r0, i32, _fp0, f32) 440 | d_m3ReinterpretOp (_r0, i64, _fp0, f64) 441 | d_m3ReinterpretOp (_fp0, f32, _r0, i32) 442 | d_m3ReinterpretOp (_fp0, f64, _r0, i64) 443 | #endif 444 | 445 | d_m3OpDecl (Loop) 446 | d_m3OpDecl (If_r) 447 | d_m3OpDecl (If_s) 448 | 449 | 450 | #define d_m3Select_i(TYPE, REG) \ 451 | d_m3Op (Select_##TYPE##_rss) \ 452 | { \ 453 | i32 condition = (i32) _r0; \ 454 | \ 455 | TYPE operand2 = slot (TYPE); \ 456 | TYPE operand1 = slot (TYPE); \ 457 | \ 458 | REG = (condition) ? operand1 : operand2; \ 459 | \ 460 | nextOp (); \ 461 | } \ 462 | \ 463 | d_m3Op (Select_##TYPE##_srs) \ 464 | { \ 465 | i32 condition = slot (i32); \ 466 | \ 467 | TYPE operand2 = (TYPE) REG; \ 468 | TYPE operand1 = slot (TYPE); \ 469 | \ 470 | REG = (condition) ? operand1 : operand2; \ 471 | \ 472 | nextOp (); \ 473 | } \ 474 | \ 475 | d_m3Op (Select_##TYPE##_ssr) \ 476 | { \ 477 | i32 condition = slot (i32); \ 478 | \ 479 | TYPE operand2 = slot (TYPE); \ 480 | TYPE operand1 = (TYPE) REG; \ 481 | \ 482 | REG = (condition) ? operand1 : operand2; \ 483 | \ 484 | nextOp (); \ 485 | } \ 486 | \ 487 | d_m3Op (Select_##TYPE##_sss) \ 488 | { \ 489 | i32 condition = slot (i32); \ 490 | \ 491 | TYPE operand2 = slot (TYPE); \ 492 | TYPE operand1 = slot (TYPE); \ 493 | \ 494 | REG = (condition) ? operand1 : operand2; \ 495 | \ 496 | nextOp (); \ 497 | } 498 | 499 | 500 | d_m3Select_i (i32, _r0) 501 | d_m3Select_i (i64, _r0) 502 | 503 | 504 | #define d_m3Select_f(TYPE, REG, LABEL, SELECTOR) \ 505 | d_m3Op (Select_##TYPE##_##LABEL##ss) \ 506 | { \ 507 | i32 condition = (i32) SELECTOR; \ 508 | \ 509 | TYPE operand2 = slot (TYPE); \ 510 | TYPE operand1 = slot (TYPE); \ 511 | \ 512 | REG = (condition) ? operand1 : operand2; \ 513 | \ 514 | nextOp (); \ 515 | } \ 516 | \ 517 | d_m3Op (Select_##TYPE##_##LABEL##rs) \ 518 | { \ 519 | i32 condition = (i32) SELECTOR; \ 520 | \ 521 | TYPE operand2 = (TYPE) REG; \ 522 | TYPE operand1 = slot (TYPE); \ 523 | \ 524 | REG = (condition) ? operand1 : operand2; \ 525 | \ 526 | nextOp (); \ 527 | } \ 528 | \ 529 | d_m3Op (Select_##TYPE##_##LABEL##sr) \ 530 | { \ 531 | i32 condition = (i32) SELECTOR; \ 532 | \ 533 | TYPE operand2 = slot (TYPE); \ 534 | TYPE operand1 = (TYPE) REG; \ 535 | \ 536 | REG = (condition) ? operand1 : operand2; \ 537 | \ 538 | nextOp (); \ 539 | } 540 | 541 | #if d_m3HasFloat 542 | d_m3Select_f (f32, _fp0, r, _r0) 543 | d_m3Select_f (f32, _fp0, s, slot (i32)) 544 | 545 | d_m3Select_f (f64, _fp0, r, _r0) 546 | d_m3Select_f (f64, _fp0, s, slot (i32)) 547 | #endif 548 | 549 | d_m3Op (Return) 550 | { 551 | m3StackCheck(); 552 | return NULL; 553 | } 554 | 555 | 556 | d_m3OpDecl (Branch) 557 | 558 | 559 | d_m3Op (BranchIf_r) 560 | { 561 | i32 condition = (i32) _r0; 562 | pc_t branch = immediate (pc_t); 563 | 564 | if (condition) 565 | { 566 | return jumpOp (branch); 567 | } 568 | else nextOp (); 569 | } 570 | 571 | 572 | d_m3Op (BranchIf_s) 573 | { 574 | i32 condition = slot (i32); 575 | pc_t branch = immediate (pc_t); 576 | 577 | if (condition) 578 | { 579 | return jumpOp (branch); 580 | } 581 | else nextOp (); 582 | } 583 | 584 | 585 | // branching to blocks that produce a (int) value 586 | #define d_m3BranchIf(TYPE, LABEL, COND) \ 587 | d_m3Op (TYPE##_BranchIf_##LABEL##s) \ 588 | { \ 589 | i32 condition = (i32) COND; \ 590 | TYPE value = slot (TYPE); \ 591 | pc_t branch = immediate (pc_t); \ 592 | \ 593 | if (condition) \ 594 | { \ 595 | _r0 = value; \ 596 | return jumpOp (branch); \ 597 | } \ 598 | else nextOp (); \ 599 | } 600 | 601 | 602 | d_m3BranchIf (i32, r, _r0) 603 | d_m3BranchIf (i64, r, _r0) 604 | d_m3BranchIf (i32, s, slot (i32)) 605 | d_m3BranchIf (i64, s, slot (i32)) 606 | 607 | 608 | d_m3OpDecl (BranchTable) 609 | 610 | 611 | d_m3Op (ContinueLoop) 612 | { 613 | // TODO: this is where execution can "escape" the M3 code and callback to the client / fiber switch 614 | // OR it can go in the Loop operation. I think it's best to do here. adding code to the loop operation 615 | // has the potential to increase its native-stack usage. (don't forget ContinueLoopIf too.) 616 | 617 | void * loopId = immediate (void *); 618 | return loopId; 619 | } 620 | 621 | 622 | d_m3Op (ContinueLoopIf) 623 | { 624 | i32 condition = (i32) _r0; 625 | void * loopId = immediate (void *); 626 | 627 | if (condition) 628 | { 629 | return loopId; 630 | } 631 | else nextOp (); 632 | } 633 | 634 | 635 | 636 | d_m3OpDecl (Compile) 637 | d_m3OpDecl (Call) 638 | d_m3OpDecl (CallIndirect) 639 | d_m3OpDecl (CallRawFunction) 640 | d_m3OpDecl (CallRawFunctionEx) 641 | d_m3OpDecl (Entry) 642 | 643 | d_m3OpDecl (MemCurrent) 644 | d_m3OpDecl (MemGrow) 645 | 646 | 647 | d_m3Op (Const32) 648 | { 649 | u32 value = * (u32 *)_pc++; 650 | slot (u32) = value; 651 | nextOp (); 652 | } 653 | 654 | 655 | d_m3Op (Const64) 656 | { 657 | u64 value = * (u64 *)_pc; 658 | _pc += (M3_SIZEOF_PTR == 4) ? 2 : 1; 659 | slot (u64) = value; 660 | nextOp (); 661 | } 662 | 663 | 664 | d_m3Op (Unreachable) 665 | { m3log (exec, "*** trapping ***"); 666 | m3StackCheck(); 667 | return m3Err_trapUnreachable; 668 | } 669 | 670 | 671 | d_m3Op (End) 672 | { 673 | m3StackCheck(); 674 | return 0; 675 | } 676 | 677 | 678 | d_m3OpDecl (GetGlobal_s32) 679 | d_m3OpDecl (GetGlobal_s64) 680 | d_m3OpDecl (SetGlobal_i32) 681 | d_m3OpDecl (SetGlobal_i64) 682 | 683 | 684 | d_m3Op (SetGlobal_s32) 685 | { 686 | u32 * global = immediate (u32 *); 687 | * global = slot (u32); 688 | 689 | nextOp (); 690 | } 691 | 692 | 693 | d_m3Op (SetGlobal_s64) 694 | { 695 | u64 * global = immediate (u64 *); 696 | * global = slot (u64); 697 | 698 | nextOp (); 699 | } 700 | 701 | #if d_m3HasFloat 702 | d_m3Op (SetGlobal_f32) 703 | { 704 | f32 * global = immediate (f32 *); 705 | * global = _fp0; 706 | 707 | nextOp (); 708 | } 709 | 710 | 711 | d_m3Op (SetGlobal_f64) 712 | { 713 | f64 * global = immediate (f64 *); 714 | * global = _fp0; 715 | 716 | nextOp (); 717 | } 718 | #endif 719 | 720 | 721 | d_m3OpDecl (CopySlot_32) 722 | d_m3OpDecl (PreserveCopySlot_32) 723 | 724 | d_m3OpDecl (CopySlot_64) 725 | d_m3OpDecl (PreserveCopySlot_64) 726 | 727 | #define d_m3SetRegisterSetSlotDecl(TYPE) \ 728 | d_m3OpDecl (SetRegister_##TYPE) \ 729 | d_m3OpDecl (SetSlot_##TYPE) \ 730 | d_m3OpDecl (PreserveSetSlot_##TYPE) 731 | 732 | d_m3SetRegisterSetSlotDecl (i32) 733 | d_m3SetRegisterSetSlotDecl (i64) 734 | d_m3SetRegisterSetSlotDecl (f32) 735 | d_m3SetRegisterSetSlotDecl (f64) 736 | 737 | 738 | #if d_m3SkipMemoryBoundsCheck 739 | # define m3MemCheck(x) true 740 | #else 741 | # define m3MemCheck(x) LIKELY(x) 742 | #endif 743 | 744 | #ifdef DEBUG 745 | #define d_outOfBounds return ErrorRuntime (m3Err_trapOutOfBoundsMemoryAccess, \ 746 | _mem->runtime, "memory size: %zu; access offset: %zu", \ 747 | _mem->length, operand) 748 | #else 749 | #define d_outOfBounds return m3Err_trapOutOfBoundsMemoryAccess 750 | #endif 751 | 752 | // memcpy here is to support non-aligned access on some platforms. 753 | // TODO: check if this is optimized-out on x86/x64, and performance impact 754 | 755 | #define d_m3Load(REG,DEST_TYPE,SRC_TYPE) \ 756 | d_m3Op(DEST_TYPE##_Load_##SRC_TYPE##_r) \ 757 | { \ 758 | u32 offset = immediate (u32); \ 759 | u64 operand = (u32) _r0; \ 760 | operand += offset; \ 761 | \ 762 | if (m3MemCheck( \ 763 | operand + sizeof (SRC_TYPE) <= _mem->length \ 764 | )) { \ 765 | u8* src8 = m3MemData(_mem) + operand; \ 766 | SRC_TYPE value; \ 767 | memcpy(&value, src8, sizeof(value)); \ 768 | M3_BSWAP_##SRC_TYPE(value); \ 769 | REG = (DEST_TYPE)value; \ 770 | nextOp (); \ 771 | } else d_outOfBounds; \ 772 | } \ 773 | d_m3Op(DEST_TYPE##_Load_##SRC_TYPE##_s) \ 774 | { \ 775 | u64 operand = slot (u32); \ 776 | u32 offset = immediate (u32); \ 777 | operand += offset; \ 778 | \ 779 | if (m3MemCheck( \ 780 | operand + sizeof (SRC_TYPE) <= _mem->length \ 781 | )) { \ 782 | u8* src8 = m3MemData(_mem) + operand; \ 783 | SRC_TYPE value; \ 784 | memcpy(&value, src8, sizeof(value)); \ 785 | M3_BSWAP_##SRC_TYPE(value); \ 786 | REG = (DEST_TYPE)value; \ 787 | nextOp (); \ 788 | } else d_outOfBounds; \ 789 | } 790 | 791 | // printf ("get: %d -> %d\n", operand + offset, (i64) REG); 792 | 793 | 794 | #define d_m3Load_i(DEST_TYPE, SRC_TYPE) d_m3Load(_r0, DEST_TYPE, SRC_TYPE) 795 | #define d_m3Load_f(DEST_TYPE, SRC_TYPE) d_m3Load(_fp0, DEST_TYPE, SRC_TYPE) 796 | 797 | #if d_m3HasFloat 798 | d_m3Load_f (f32, f32); 799 | d_m3Load_f (f64, f64); 800 | #endif 801 | 802 | d_m3Load_i (i32, i8); 803 | d_m3Load_i (i32, u8); 804 | d_m3Load_i (i32, i16); 805 | d_m3Load_i (i32, u16); 806 | d_m3Load_i (i32, i32); 807 | 808 | d_m3Load_i (i64, i8); 809 | d_m3Load_i (i64, u8); 810 | d_m3Load_i (i64, i16); 811 | d_m3Load_i (i64, u16); 812 | d_m3Load_i (i64, i32); 813 | d_m3Load_i (i64, u32); 814 | d_m3Load_i (i64, i64); 815 | 816 | #define d_m3Store(REG, SRC_TYPE, DEST_TYPE) \ 817 | d_m3Op (SRC_TYPE##_Store_##DEST_TYPE##_rs) \ 818 | { \ 819 | u64 operand = slot (u32); \ 820 | u32 offset = immediate (u32); \ 821 | operand += offset; \ 822 | \ 823 | if (m3MemCheck( \ 824 | operand + sizeof (DEST_TYPE) <= _mem->length \ 825 | )) { \ 826 | u8* mem8 = m3MemData(_mem) + operand; \ 827 | DEST_TYPE val = (DEST_TYPE) REG; \ 828 | M3_BSWAP_##DEST_TYPE(val); \ 829 | memcpy(mem8, &val, sizeof(val)); \ 830 | nextOp (); \ 831 | } else d_outOfBounds; \ 832 | } \ 833 | d_m3Op (SRC_TYPE##_Store_##DEST_TYPE##_sr) \ 834 | { \ 835 | const SRC_TYPE value = slot (SRC_TYPE); \ 836 | u64 operand = (u32) _r0; \ 837 | u32 offset = immediate (u32); \ 838 | operand += offset; \ 839 | \ 840 | if (m3MemCheck( \ 841 | operand + sizeof (DEST_TYPE) <= _mem->length \ 842 | )) { \ 843 | u8* mem8 = m3MemData(_mem) + operand; \ 844 | DEST_TYPE val = (DEST_TYPE) value; \ 845 | M3_BSWAP_##DEST_TYPE(val); \ 846 | memcpy(mem8, &val, sizeof(val)); \ 847 | nextOp (); \ 848 | } else d_outOfBounds; \ 849 | } \ 850 | d_m3Op (SRC_TYPE##_Store_##DEST_TYPE##_ss) \ 851 | { \ 852 | const SRC_TYPE value = slot (SRC_TYPE); \ 853 | u64 operand = slot (u32); \ 854 | u32 offset = immediate (u32); \ 855 | operand += offset; \ 856 | \ 857 | if (m3MemCheck( \ 858 | operand + sizeof (DEST_TYPE) <= _mem->length \ 859 | )) { \ 860 | u8* mem8 = m3MemData(_mem) + operand; \ 861 | DEST_TYPE val = (DEST_TYPE) value; \ 862 | M3_BSWAP_##DEST_TYPE(val); \ 863 | memcpy(mem8, &val, sizeof(val)); \ 864 | nextOp (); \ 865 | } else d_outOfBounds; \ 866 | } 867 | 868 | // both operands can be in regs when storing a float 869 | #define d_m3StoreFp(REG, TYPE) \ 870 | d_m3Op (TYPE##_Store_##TYPE##_rr) \ 871 | { \ 872 | u64 operand = (u32) _r0; \ 873 | u32 offset = immediate (u32); \ 874 | operand += offset; \ 875 | \ 876 | if (m3MemCheck( \ 877 | operand + sizeof (TYPE) <= _mem->length \ 878 | )) { \ 879 | u8* mem8 = m3MemData(_mem) + operand; \ 880 | TYPE val = (TYPE) REG; \ 881 | M3_BSWAP_##TYPE(val); \ 882 | memcpy(mem8, &val, sizeof(val)); \ 883 | nextOp (); \ 884 | } else d_outOfBounds; \ 885 | } 886 | 887 | 888 | #define d_m3Store_i(SRC_TYPE, DEST_TYPE) d_m3Store(_r0, SRC_TYPE, DEST_TYPE) 889 | #define d_m3Store_f(SRC_TYPE, DEST_TYPE) d_m3Store(_fp0, SRC_TYPE, DEST_TYPE) d_m3StoreFp (_fp0, SRC_TYPE); 890 | 891 | #if d_m3HasFloat 892 | d_m3Store_f (f32, f32) 893 | d_m3Store_f (f64, f64) 894 | #endif 895 | 896 | d_m3Store_i (i32, u8) 897 | d_m3Store_i (i32, i16) 898 | d_m3Store_i (i32, i32) 899 | 900 | d_m3Store_i (i64, u8) 901 | d_m3Store_i (i64, i16) 902 | d_m3Store_i (i64, i32) 903 | d_m3Store_i (i64, i64) 904 | 905 | #undef m3MemCheck 906 | 907 | //--------------------------------------------------------------------------------------------------------------------- 908 | # if 0 //d_m3EnableOptimizations 909 | //--------------------------------------------------------------------------------------------------------------------- 910 | 911 | #define d_m3BinaryOpWith1_i(TYPE, NAME, OPERATION) \ 912 | d_m3Op(TYPE##_##NAME) \ 913 | { \ 914 | _r0 = _r0 OPERATION 1; \ 915 | nextOp (); \ 916 | } 917 | 918 | d_m3BinaryOpWith1_i (u64, Increment, +) 919 | d_m3BinaryOpWith1_i (u32, Decrement, -) 920 | 921 | d_m3BinaryOpWith1_i (u32, ShiftLeft1, <<) 922 | d_m3BinaryOpWith1_i (u64, ShiftLeft1, <<) 923 | 924 | d_m3BinaryOpWith1_i (u32, ShiftRight1, >>) 925 | d_m3BinaryOpWith1_i (u64, ShiftRight1, >>) 926 | 927 | //--------------------------------------------------------------------------------------------------------------------- 928 | # endif 929 | 930 | 931 | //--------------------------------------------------------------------------------------------------------------------- 932 | // debug/profiling 933 | //--------------------------------------------------------------------------------------------------------------------- 934 | #if d_m3EnableOpTracing 935 | d_m3RetSig debugOp (d_m3OpSig, cstr_t i_opcode) 936 | { 937 | char name [100]; 938 | strcpy (name, strstr (i_opcode, "op_") + 3); 939 | char * bracket = strstr (name, "("); 940 | if (bracket) { 941 | *bracket = 0; 942 | } 943 | 944 | puts (name); 945 | return nextOpDirect(); 946 | } 947 | # endif 948 | 949 | # if d_m3EnableOpTracing 950 | d_m3OpDecl (DumpStack) 951 | # endif 952 | 953 | # if d_m3EnableOpProfiling 954 | 955 | typedef struct M3ProfilerSlot 956 | { 957 | cstr_t opName; 958 | u64 hitCount; 959 | } 960 | M3ProfilerSlot; 961 | 962 | void ProfileHit (cstr_t i_operationName); 963 | 964 | d_m3RetSig profileOp (d_m3OpSig, cstr_t i_operationName) 965 | { 966 | ProfileHit (i_operationName); 967 | 968 | return nextOpDirect(); 969 | } 970 | # endif 971 | 972 | d_m3EndExternC 973 | 974 | #endif // m3_exec_h 975 | -------------------------------------------------------------------------------- /include/m3_exec_defs.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_exec_defs.h 3 | // 4 | // Created by Steven Massey on 5/1/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_exec_defs_h 9 | #define m3_exec_defs_h 10 | 11 | #include "m3_core.h" 12 | 13 | d_m3BeginExternC 14 | 15 | #if d_m3HasFloat 16 | 17 | # define d_m3OpSig pc_t _pc, m3stack_t _sp, M3MemoryHeader * _mem, m3reg_t _r0, f64 _fp0 18 | # define d_m3OpArgs _sp, _mem, _r0, _fp0 19 | # define d_m3OpAllArgs _pc, _sp, _mem, _r0, _fp0 20 | # define d_m3OpDefaultArgs 0, 0. 21 | # define d_m3ClearRegisters _r0 = 0; _fp0 = 0.; 22 | 23 | #else 24 | 25 | # define d_m3OpSig pc_t _pc, m3stack_t _sp, M3MemoryHeader * _mem, m3reg_t _r0 26 | # define d_m3OpArgs _sp, _mem, _r0 27 | # define d_m3OpAllArgs _pc, _sp, _mem, _r0 28 | # define d_m3OpDefaultArgs 0 29 | # define d_m3ClearRegisters _r0 = 0; 30 | 31 | #endif 32 | 33 | # define m3MemData(mem) (u8*)(((M3MemoryHeader*)(mem))+1) 34 | # define m3MemRuntime(mem) (((M3MemoryHeader*)(mem))->runtime) 35 | # define m3MemInfo(mem) (&(((M3MemoryHeader*)(mem))->runtime->memory)) 36 | 37 | typedef m3ret_t (vectorcall * IM3Operation) (d_m3OpSig); 38 | 39 | d_m3EndExternC 40 | 41 | #endif // m3_exec_defs_h 42 | -------------------------------------------------------------------------------- /include/m3_info.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_info.h 3 | // 4 | // Created by Steven Massey on 12/6/19. 5 | // Copyright © 2019 Steven Massey. All rights reserved. 6 | // 7 | 8 | #ifndef m3_info_h 9 | #define m3_info_h 10 | 11 | #include "m3_compile.h" 12 | 13 | d_m3BeginExternC 14 | 15 | #if d_m3LogOutput 16 | 17 | void dump_type_stack (IM3Compilation o); 18 | void log_opcode (IM3Compilation o, u8 i_opcode); 19 | const char * get_indention_string (IM3Compilation o); 20 | void emit_stack_dump (IM3Compilation o); 21 | void log_emit (IM3Compilation o, IM3Operation i_operation); 22 | 23 | cstr_t SPrintFuncTypeSignature (IM3FuncType i_funcType); 24 | 25 | #else // d_m3LogOutput 26 | 27 | #define dump_type_stack(...) {} 28 | #define log_opcode(...) {} 29 | #define get_indention_string(...) "" 30 | #define emit_stack_dump(...) {} 31 | #define log_emit(...) {} 32 | 33 | #endif // d_m3LogOutput 34 | 35 | d_m3EndExternC 36 | 37 | #endif // m3_info_h 38 | -------------------------------------------------------------------------------- /include/m3_math_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // m3_math_utils.h 3 | // 4 | // Created by Volodymyr Shymanksyy on 8/10/19. 5 | // Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. 6 | // 7 | 8 | #ifndef m3_math_utils_h 9 | #define m3_math_utils_h 10 | 11 | #include "m3_core.h" 12 | 13 | #include 14 | 15 | #if defined(M3_COMPILER_MSVC) 16 | 17 | #include 18 | 19 | #define __builtin_popcount __popcnt 20 | 21 | static inline 22 | int __builtin_ctz(uint32_t x) { 23 | unsigned long ret; 24 | _BitScanForward(&ret, x); 25 | return (int)ret; 26 | } 27 | 28 | static inline 29 | int __builtin_clz(uint32_t x) { 30 | unsigned long ret; 31 | _BitScanReverse(&ret, x); 32 | return (int)(31 ^ ret); 33 | } 34 | 35 | 36 | 37 | #ifdef _WIN64 38 | 39 | #define __builtin_popcountll __popcnt64 40 | 41 | static inline 42 | int __builtin_ctzll(uint64_t value) { 43 | unsigned long ret; 44 | _BitScanForward64(&ret, value); 45 | return (int)ret; 46 | } 47 | 48 | static inline 49 | int __builtin_clzll(uint64_t value) { 50 | unsigned long ret; 51 | _BitScanReverse64(&ret, value); 52 | return (int)(63 ^ ret); 53 | } 54 | 55 | #else // _WIN64 56 | 57 | #define __builtin_popcountll(x) (__popcnt((x) & 0xFFFFFFFF) + __popcnt((x) >> 32)) 58 | 59 | static inline 60 | int __builtin_ctzll(uint64_t value) { 61 | //if (value == 0) return 64; // Note: ctz(0) result is undefined anyway 62 | uint32_t msh = (uint32_t)(value >> 32); 63 | uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF); 64 | if (lsh != 0) return __builtin_ctz(lsh); 65 | return 32 + __builtin_ctz(msh); 66 | } 67 | 68 | static inline 69 | int __builtin_clzll(uint64_t value) { 70 | //if (value == 0) return 64; // Note: clz(0) result is undefined anyway 71 | uint32_t msh = (uint32_t)(value >> 32); 72 | uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF); 73 | if (msh != 0) return __builtin_clz(msh); 74 | return 32 + __builtin_clz(lsh); 75 | } 76 | 77 | #endif // _WIN64 78 | 79 | #endif // defined(M3_COMPILER_MSVC) 80 | 81 | 82 | // TODO: not sure why, signbit is actually defined in math.h 83 | #if (defined(ESP8266) || defined(ESP32)) && !defined(signbit) 84 | #define signbit(__x) \ 85 | ((sizeof(__x) == sizeof(float)) ? __signbitf(__x) : __signbitd(__x)) 86 | #endif 87 | 88 | #if defined(__AVR__) 89 | 90 | static inline 91 | float rintf( float arg ) { 92 | union { float f; uint32_t i; } u; 93 | u.f = arg; 94 | uint32_t ux = u.i & 0x7FFFFFFF; 95 | if (UNLIKELY(ux == 0 || ux > 0x5A000000)) { 96 | return arg; 97 | } 98 | return (float)lrint(arg); 99 | } 100 | 101 | static inline 102 | double rint( double arg ) { 103 | union { double f; uint32_t i[2]; } u; 104 | u.f = arg; 105 | uint32_t ux = u.i[1] & 0x7FFFFFFF; 106 | if (UNLIKELY((ux == 0 && u.i[0] == 0) || ux > 0x433FFFFF)) { 107 | return arg; 108 | } 109 | return (double)lrint(arg); 110 | } 111 | 112 | //TODO 113 | static inline 114 | uint64_t strtoull(const char* str, char** endptr, int base) { 115 | return 0; 116 | } 117 | 118 | #endif 119 | 120 | /* 121 | * Rotr, Rotl 122 | */ 123 | 124 | static inline 125 | u32 rotl32(u32 n, unsigned c) { 126 | const unsigned mask = CHAR_BIT * sizeof(n) - 1; 127 | c &= mask & 31; 128 | return (n << c) | (n >> ((-c) & mask)); 129 | } 130 | 131 | static inline 132 | u32 rotr32(u32 n, unsigned c) { 133 | const unsigned mask = CHAR_BIT * sizeof(n) - 1; 134 | c &= mask & 31; 135 | return (n >> c) | (n << ((-c) & mask)); 136 | } 137 | 138 | static inline 139 | u64 rotl64(u64 n, unsigned c) { 140 | const unsigned mask = CHAR_BIT * sizeof(n) - 1; 141 | c &= mask & 63; 142 | return (n << c) | (n >> ((-c) & mask)); 143 | } 144 | 145 | static inline 146 | u64 rotr64(u64 n, unsigned c) { 147 | const unsigned mask = CHAR_BIT * sizeof(n) - 1; 148 | c &= mask & 63; 149 | return (n >> c) | (n << ((-c) & mask)); 150 | } 151 | 152 | /* 153 | * Integer Div, Rem 154 | */ 155 | 156 | #define OP_DIV_U(RES, A, B) \ 157 | if (UNLIKELY(B == 0)) return m3Err_trapDivisionByZero; \ 158 | RES = A / B; 159 | 160 | #define OP_REM_U(RES, A, B) \ 161 | if (UNLIKELY(B == 0)) return m3Err_trapDivisionByZero; \ 162 | RES = A % B; 163 | 164 | // 2's complement detection 165 | #if (INT_MIN != -INT_MAX) 166 | 167 | #define OP_DIV_S(RES, A, B, TYPE_MIN) \ 168 | if (UNLIKELY(B == 0)) return m3Err_trapDivisionByZero; \ 169 | if (UNLIKELY(B == -1 and A == TYPE_MIN)) { \ 170 | return m3Err_trapIntegerOverflow; \ 171 | } \ 172 | RES = A / B; 173 | 174 | #define OP_REM_S(RES, A, B, TYPE_MIN) \ 175 | if (UNLIKELY(B == 0)) return m3Err_trapDivisionByZero; \ 176 | if (UNLIKELY(B == -1 and A == TYPE_MIN)) RES = 0; \ 177 | else RES = A % B; 178 | 179 | #else 180 | 181 | #define OP_DIV_S(RES, A, B, TYPE_MIN) OP_DIV_U(RES, A, B) 182 | #define OP_REM_S(RES, A, B, TYPE_MIN) OP_REM_U(RES, A, B) 183 | 184 | #endif 185 | 186 | /* 187 | * Trunc 188 | */ 189 | 190 | #define OP_TRUNC(RES, A, TYPE, RMIN, RMAX) \ 191 | if (UNLIKELY(isnan(A))) { \ 192 | return m3Err_trapIntegerConversion; \ 193 | } \ 194 | if (UNLIKELY(A <= RMIN or A >= RMAX)) { \ 195 | return m3Err_trapIntegerOverflow; \ 196 | } \ 197 | RES = (TYPE)A; 198 | 199 | 200 | #define OP_I32_TRUNC_F32(RES, A) OP_TRUNC(RES, A, i32, -2147483904.0f, 2147483648.0f) 201 | #define OP_U32_TRUNC_F32(RES, A) OP_TRUNC(RES, A, u32, -1.0f, 4294967296.0f) 202 | #define OP_I32_TRUNC_F64(RES, A) OP_TRUNC(RES, A, i32, -2147483649.0 , 2147483648.0 ) 203 | #define OP_U32_TRUNC_F64(RES, A) OP_TRUNC(RES, A, u32, -1.0 , 4294967296.0 ) 204 | 205 | #define OP_I64_TRUNC_F32(RES, A) OP_TRUNC(RES, A, i64, -9223373136366403584.0f, 9223372036854775808.0f) 206 | #define OP_U64_TRUNC_F32(RES, A) OP_TRUNC(RES, A, u64, -1.0f, 18446744073709551616.0f) 207 | #define OP_I64_TRUNC_F64(RES, A) OP_TRUNC(RES, A, i64, -9223372036854777856.0 , 9223372036854775808.0 ) 208 | #define OP_U64_TRUNC_F64(RES, A) OP_TRUNC(RES, A, u64, -1.0 , 18446744073709551616.0 ) 209 | 210 | #define OP_TRUNC_SAT(RES, A, TYPE, RMIN, RMAX, IMIN, IMAX) \ 211 | if (UNLIKELY(isnan(A))) { \ 212 | RES = 0; \ 213 | } else if (UNLIKELY(A <= RMIN)) { \ 214 | RES = IMIN; \ 215 | } else if (UNLIKELY(A >= RMAX)) { \ 216 | RES = IMAX; \ 217 | } else { \ 218 | RES = (TYPE)A; \ 219 | } 220 | 221 | #define OP_I32_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, i32, -2147483904.0f, 2147483648.0f, INT32_MIN, INT32_MAX) 222 | #define OP_U32_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, u32, -1.0f, 4294967296.0f, 0UL, UINT32_MAX) 223 | #define OP_I32_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, i32, -2147483649.0 , 2147483648.0, INT32_MIN, INT32_MAX) 224 | #define OP_U32_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, u32, -1.0 , 4294967296.0, 0UL, UINT32_MAX) 225 | 226 | #define OP_I64_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, i64, -9223373136366403584.0f, 9223372036854775808.0f, INT64_MIN, INT64_MAX) 227 | #define OP_U64_TRUNC_SAT_F32(RES, A) OP_TRUNC_SAT(RES, A, u64, -1.0f, 18446744073709551616.0f, 0ULL, UINT64_MAX) 228 | #define OP_I64_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, i64, -9223372036854777856.0 , 9223372036854775808.0, INT64_MIN, INT64_MAX) 229 | #define OP_U64_TRUNC_SAT_F64(RES, A) OP_TRUNC_SAT(RES, A, u64, -1.0 , 18446744073709551616.0, 0ULL, UINT64_MAX) 230 | 231 | /* 232 | * Min, Max 233 | */ 234 | 235 | #if d_m3HasFloat 236 | 237 | #include 238 | 239 | static inline 240 | f32 min_f32(f32 a, f32 b) { 241 | if (UNLIKELY(isnan(a) or isnan(b))) return NAN; 242 | if (UNLIKELY(a == 0 and a == b)) return signbit(a) ? a : b; 243 | return a > b ? b : a; 244 | } 245 | 246 | static inline 247 | f32 max_f32(f32 a, f32 b) { 248 | if (UNLIKELY(isnan(a) or isnan(b))) return NAN; 249 | if (UNLIKELY(a == 0 and a == b)) return signbit(a) ? b : a; 250 | return a > b ? a : b; 251 | } 252 | 253 | static inline 254 | f64 min_f64(f64 a, f64 b) { 255 | if (UNLIKELY(isnan(a) or isnan(b))) return NAN; 256 | if (UNLIKELY(a == 0 and a == b)) return signbit(a) ? a : b; 257 | return a > b ? b : a; 258 | } 259 | 260 | static inline 261 | f64 max_f64(f64 a, f64 b) { 262 | if (UNLIKELY(isnan(a) or isnan(b))) return NAN; 263 | if (UNLIKELY(a == 0 and a == b)) return signbit(a) ? b : a; 264 | return a > b ? a : b; 265 | } 266 | #endif 267 | 268 | #endif // m3_math_utils_h 269 | -------------------------------------------------------------------------------- /include/wasm3.h: -------------------------------------------------------------------------------- 1 | // 2 | // Wasm3, high performance WebAssembly interpreter 3 | // 4 | // Copyright © 2019 Steven Massey, Volodymyr Shymanskyy. 5 | // All rights reserved. 6 | // 7 | 8 | #ifndef wasm3_h 9 | #define wasm3_h 10 | 11 | #define M3_VERSION_MAJOR 0 12 | #define M3_VERSION_MINOR 4 13 | #define M3_VERSION_REV 7 14 | #define M3_VERSION "0.4.7" 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #if defined(__cplusplus) 21 | extern "C" { 22 | #endif 23 | 24 | typedef const char * M3Result; 25 | 26 | struct M3Environment; typedef struct M3Environment * IM3Environment; 27 | struct M3Runtime; typedef struct M3Runtime * IM3Runtime; 28 | struct M3Module; typedef struct M3Module * IM3Module; 29 | struct M3Function; typedef struct M3Function * IM3Function; 30 | 31 | 32 | typedef struct M3ErrorInfo 33 | { 34 | M3Result result; 35 | 36 | IM3Runtime runtime; 37 | IM3Module module; 38 | IM3Function function; 39 | 40 | const char * file; 41 | uint32_t line; 42 | 43 | const char * message; 44 | } 45 | M3ErrorInfo; 46 | 47 | 48 | enum // EWaTypes 49 | { 50 | c_m3Type_none = 0, 51 | c_m3Type_i32 = 1, 52 | c_m3Type_i64 = 2, 53 | c_m3Type_f32 = 3, 54 | c_m3Type_f64 = 4, 55 | 56 | c_m3Type_void, 57 | c_m3Type_ptr, 58 | c_m3Type_trap 59 | }; 60 | 61 | 62 | typedef struct M3ImportInfo 63 | { 64 | const char * moduleUtf8; 65 | const char * fieldUtf8; 66 | 67 | // unsigned char type; 68 | } 69 | M3ImportInfo; 70 | 71 | typedef M3ImportInfo * IM3ImportInfo; 72 | 73 | 74 | 75 | // ------------------------------------------------------------------------------------------------------------------------------- 76 | // error codes 77 | // ------------------------------------------------------------------------------------------------------------------------------- 78 | 79 | # if defined(M3_IMPLEMENT_ERROR_STRINGS) 80 | # define d_m3ErrorConst(LABEL, STRING) M3Result m3Err_##LABEL = { STRING }; 81 | # else 82 | # define d_m3ErrorConst(LABEL, STRING) extern M3Result m3Err_##LABEL; 83 | # endif 84 | 85 | // ------------------------------------------------------------------------------------------------------------------------------- 86 | 87 | d_m3ErrorConst (none, NULL) 88 | 89 | // general errors 90 | d_m3ErrorConst (typeListOverflow, "type list count exceeds 32 types") 91 | d_m3ErrorConst (mallocFailed, "memory allocation failed") 92 | 93 | // parse errors 94 | d_m3ErrorConst (incompatibleWasmVersion, "incompatible Wasm binary version") 95 | d_m3ErrorConst (wasmMalformed, "malformed Wasm binary") 96 | d_m3ErrorConst (misorderedWasmSection, "out of order Wasm section") 97 | d_m3ErrorConst (wasmUnderrun, "underrun while parsing Wasm binary") 98 | d_m3ErrorConst (wasmOverrun, "overrun while parsing Wasm binary") 99 | d_m3ErrorConst (wasmMissingInitExpr, "missing init_expr in Wasm binary") 100 | d_m3ErrorConst (lebOverflow, "LEB encoded value overflow") 101 | d_m3ErrorConst (missingUTF8, "invalid length UTF-8 string") 102 | d_m3ErrorConst (wasmSectionUnderrun, "section underrun while parsing Wasm binary") 103 | d_m3ErrorConst (wasmSectionOverrun, "section overrun while parsing Wasm binary") 104 | d_m3ErrorConst (invalidTypeId, "unknown value_type") 105 | d_m3ErrorConst (tooManyMemorySections, "Wasm MVP can only define one memory per module") 106 | 107 | // link errors 108 | d_m3ErrorConst (moduleAlreadyLinked, "attempting to bind module to multiple runtimes") 109 | d_m3ErrorConst (functionLookupFailed, "function lookup failed") 110 | d_m3ErrorConst (functionImportMissing, "missing imported function") 111 | 112 | d_m3ErrorConst (malformedFunctionSignature, "malformed function signature") 113 | d_m3ErrorConst (funcSignatureMissingReturnType,"function signature missing return type") 114 | 115 | // compilation errors 116 | d_m3ErrorConst (noCompiler, "no compiler found for opcode") 117 | d_m3ErrorConst (unknownOpcode, "unknown opcode") 118 | d_m3ErrorConst (functionStackOverflow, "compiling function overran its stack height limit") 119 | d_m3ErrorConst (functionStackUnderrun, "compiling function underran the stack") 120 | d_m3ErrorConst (mallocFailedCodePage, "memory allocation failed when acquiring a new M3 code page") 121 | d_m3ErrorConst (settingImmutableGlobal, "attempting to set an immutable global") 122 | d_m3ErrorConst (optimizerFailed, "optimizer failed") // not a fatal error. a result, 123 | 124 | // runtime errors 125 | d_m3ErrorConst (missingCompiledCode, "function is missing compiled m3 code") 126 | d_m3ErrorConst (wasmMemoryOverflow, "runtime ran out of memory") 127 | d_m3ErrorConst (globalMemoryNotAllocated, "global memory is missing from a module") 128 | d_m3ErrorConst (globaIndexOutOfBounds, "global index is too large") 129 | d_m3ErrorConst (argumentCountMismatch, "argument count mismatch") 130 | 131 | // traps 132 | d_m3ErrorConst (trapOutOfBoundsMemoryAccess, "[trap] out of bounds memory access") 133 | d_m3ErrorConst (trapDivisionByZero, "[trap] integer divide by zero") 134 | d_m3ErrorConst (trapIntegerOverflow, "[trap] integer overflow") 135 | d_m3ErrorConst (trapIntegerConversion, "[trap] invalid conversion to integer") 136 | d_m3ErrorConst (trapIndirectCallTypeMismatch, "[trap] indirect call type mismatch") 137 | d_m3ErrorConst (trapTableIndexOutOfRange, "[trap] undefined element") 138 | d_m3ErrorConst (trapTableElementIsNull, "[trap] null table element") 139 | d_m3ErrorConst (trapExit, "[trap] program called exit") 140 | d_m3ErrorConst (trapAbort, "[trap] program called abort") 141 | d_m3ErrorConst (trapUnreachable, "[trap] unreachable executed") 142 | d_m3ErrorConst (trapStackOverflow, "[trap] stack overflow") 143 | 144 | 145 | //------------------------------------------------------------------------------------------------------------------------------- 146 | // configuration, can be found in m3_config.h, m3_config_platforms.h, m3_core.h) 147 | //------------------------------------------------------------------------------------------------------------------------------- 148 | 149 | //------------------------------------------------------------------------------------------------------------------------------- 150 | // global environment than can host multiple runtimes 151 | //------------------------------------------------------------------------------------------------------------------------------- 152 | IM3Environment m3_NewEnvironment (void); 153 | 154 | void m3_FreeEnvironment (IM3Environment i_environment); 155 | 156 | //------------------------------------------------------------------------------------------------------------------------------- 157 | // execution context 158 | //------------------------------------------------------------------------------------------------------------------------------- 159 | 160 | IM3Runtime m3_NewRuntime (IM3Environment io_environment, 161 | uint32_t i_stackSizeInBytes, 162 | void * unused); 163 | 164 | void m3_FreeRuntime (IM3Runtime i_runtime); 165 | 166 | uint8_t * m3_GetMemory (IM3Runtime i_runtime, 167 | uint32_t * o_memorySizeInBytes, 168 | uint32_t i_memoryIndex); 169 | // Wasm currently only supports one memory region. i_memoryIndex should be zero. 170 | 171 | //------------------------------------------------------------------------------------------------------------------------------- 172 | // modules 173 | //------------------------------------------------------------------------------------------------------------------------------- 174 | 175 | M3Result m3_ParseModule (IM3Environment i_environment, 176 | IM3Module * o_module, 177 | const uint8_t * const i_wasmBytes, 178 | uint32_t i_numWasmBytes); 179 | // i_wasmBytes data must be persistent during the lifetime of the module 180 | 181 | void m3_FreeModule (IM3Module i_module); 182 | // Only unloaded modules need to be freed 183 | 184 | M3Result m3_LoadModule (IM3Runtime io_runtime, IM3Module io_module); 185 | // LoadModule transfers ownership of a module to the runtime. Do not free modules once successfully imported into the runtime 186 | 187 | typedef const void * (* M3RawCall) (IM3Runtime runtime, uint64_t * _sp, void * _mem); 188 | 189 | M3Result m3_LinkRawFunction (IM3Module io_module, 190 | const char * const i_moduleName, 191 | const char * const i_functionName, 192 | const char * const i_signature, 193 | M3RawCall i_function); 194 | 195 | typedef const void * (* M3RawCallEx) (IM3Runtime runtime, uint64_t * _sp, void * _mem, void * cookie); 196 | 197 | // m3_LinkRawFunctionEx links a native callback function that has a cookie parameter, allowing one native 198 | // callback to receive multiple m3 function calls. This ease for dynamic routing in the callback. 199 | M3Result m3_LinkRawFunctionEx (IM3Module io_module, 200 | const char * const i_moduleName, 201 | const char * const i_functionName, 202 | const char * const i_signature, 203 | M3RawCallEx i_function, 204 | void * i_cookie); 205 | 206 | //------------------------------------------------------------------------------------------------------------------------------- 207 | // functions 208 | //------------------------------------------------------------------------------------------------------------------------------- 209 | M3Result m3_Yield (void); 210 | 211 | M3Result m3_FindFunction (IM3Function * o_function, 212 | IM3Runtime i_runtime, 213 | const char * const i_functionName); 214 | 215 | M3Result m3_Call (IM3Function i_function); 216 | M3Result m3_CallWithArgs (IM3Function i_function, uint32_t i_argc, const char * const * i_argv); 217 | 218 | // IM3Functions are valid during the lifetime of the originating runtime 219 | 220 | void m3_GetErrorInfo (IM3Runtime i_runtime, M3ErrorInfo* info); 221 | void m3_ResetErrorInfo (IM3Runtime i_runtime); 222 | 223 | //------------------------------------------------------------------------------------------------------------------------------- 224 | // debug info 225 | //------------------------------------------------------------------------------------------------------------------------------- 226 | 227 | void m3_PrintRuntimeInfo (IM3Runtime i_runtime); 228 | void m3_PrintM3Info (void); 229 | void m3_PrintProfilerInfo (void); 230 | 231 | #if defined(__cplusplus) 232 | } 233 | #endif 234 | 235 | #endif // wasm3_h 236 | -------------------------------------------------------------------------------- /lib/darwin/libm3.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matiasinsaurralde/go-wasm3/94c5ed3e86a3281e423b7c1d4ea2e577bc0b7324/lib/darwin/libm3.a -------------------------------------------------------------------------------- /lib/linux/libm3.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matiasinsaurralde/go-wasm3/94c5ed3e86a3281e423b7c1d4ea2e577bc0b7324/lib/linux/libm3.a -------------------------------------------------------------------------------- /wasm3.go: -------------------------------------------------------------------------------- 1 | package wasm3 2 | 3 | /* 4 | #cgo darwin CFLAGS: -Iinclude 5 | #cgo darwin LDFLAGS: -L${SRCDIR}/lib/darwin -lm3 6 | #cgo linux CFLAGS: -Iinclude 7 | #cgo linux LDFLAGS: -L${SRCDIR}/lib/linux -lm3 -lm 8 | 9 | #include "m3_api_libc.h" 10 | #include "m3_api_wasi.h" 11 | #include "m3_env.h" 12 | #include "go-wasm3.h" 13 | 14 | // module_get_function is a helper function for the module Go struct 15 | IM3Function module_get_function(IM3Module i_module, int index) { 16 | IM3Function f = & i_module->functions [index]; 17 | return f; 18 | } 19 | 20 | int call(IM3Function i_function, uint32_t i_argc, int i_argv[]) { 21 | int result = 0; 22 | IM3Module module = i_function->module; 23 | IM3Runtime runtime = module->runtime; 24 | u64* stack = (u64*)(runtime->stack); 25 | IM3FuncType ftype = i_function->funcType; 26 | for (int i = 0; i < ftype->numArgs; i++) { 27 | int v = i_argv[i]; 28 | u64* s = &stack[i]; 29 | *(u64*)(s) = v; 30 | } 31 | m3StackCheckInit(); 32 | M3Result call_result = Call(i_function->compiled, (m3stack_t)(stack), runtime->memory.mallocated, d_m3OpDefaultArgs); 33 | if(call_result != NULL) { 34 | set_error(call_result); 35 | return -1; 36 | } 37 | switch (ftype->returnType) { 38 | case c_m3Type_i32: 39 | result = *(u32*)(stack); 40 | break; 41 | case c_m3Type_i64: 42 | default: 43 | result = *(u32*)(stack); 44 | }; 45 | return result; 46 | } 47 | 48 | int get_allocated_memory_length(IM3Runtime i_runtime) { 49 | return i_runtime->memory.mallocated->length; 50 | } 51 | 52 | u8* get_allocated_memory(IM3Runtime i_runtime) { 53 | return m3MemData(i_runtime->memory.mallocated); 54 | } 55 | */ 56 | import "C" 57 | 58 | import( 59 | "unsafe" 60 | "errors" 61 | "reflect" 62 | ) 63 | 64 | // RuntimeT is an alias for IM3Runtime 65 | type RuntimeT C.IM3Runtime 66 | // EnvironmentT is an alias for IM3Environment 67 | type EnvironmentT C.IM3Environment 68 | // ModuleT is an alias for IM3Module 69 | type ModuleT C.IM3Module 70 | // FunctionT is an alias for IM3Function 71 | type FunctionT C.IM3Function 72 | // ResultT is an alias for M3Result 73 | type ResultT C.M3Result 74 | 75 | var( 76 | errParseModule = errors.New("Parse error") 77 | errLoadModule = errors.New("Load error") 78 | errFuncLookupFailed = errors.New("Function lookup failed") 79 | ) 80 | 81 | // Config holds the runtime and environment configuration 82 | type Config struct { 83 | Environment *Environment 84 | StackSize uint 85 | EnableWASI bool 86 | } 87 | 88 | // Runtime wraps a WASM3 runtime 89 | type Runtime struct { 90 | ptr RuntimeT 91 | cfg *Config 92 | } 93 | 94 | // Ptr returns a IM3Runtime pointer 95 | func(r *Runtime) Ptr() C.IM3Runtime { 96 | return (C.IM3Runtime)(r.ptr) 97 | } 98 | 99 | // Load wraps the parse and load module calls. 100 | // This will be replaced by env.ParseModule and Runtime.LoadModule. 101 | func(r *Runtime) Load(wasmBytes []byte) (*Module, error) { 102 | result := C.m3Err_none 103 | bytes := C.CBytes(wasmBytes) 104 | length := len(wasmBytes) 105 | var module C.IM3Module 106 | result = C.m3_ParseModule( 107 | r.cfg.Environment.Ptr(), 108 | &module, 109 | (*C.uchar)(bytes), 110 | C.uint(length), 111 | ) 112 | if result != nil { 113 | return nil, errParseModule 114 | } 115 | result = C.m3_LoadModule( 116 | r.Ptr(), 117 | module, 118 | ) 119 | if result != nil { 120 | return nil, errLoadModule 121 | } 122 | result = C.m3_LinkSpecTest(r.Ptr().modules) 123 | if result != nil { 124 | return nil, errors.New("LinkSpecTest failed") 125 | } 126 | if r.cfg.EnableWASI { 127 | C.m3_LinkWASI(r.Ptr().modules) 128 | } 129 | m := NewModule((ModuleT)(module)) 130 | return m, nil 131 | } 132 | 133 | // LoadModule wraps m3_LoadModule and returns a module object 134 | func(r *Runtime) LoadModule(module *Module) (*Module, error) { 135 | result := C.m3Err_none 136 | result = C.m3_LoadModule( 137 | r.Ptr(), 138 | module.Ptr(), 139 | ) 140 | if result != nil { 141 | return nil, errLoadModule 142 | } 143 | result = C.m3_LinkSpecTest(r.Ptr().modules) 144 | if result != nil { 145 | return nil, errors.New("LinkSpecTest failed") 146 | } 147 | if r.cfg.EnableWASI { 148 | C.m3_LinkWASI(r.Ptr().modules) 149 | } 150 | return module, nil 151 | } 152 | 153 | // FindFunction calls m3_FindFunction and returns a call function 154 | func(r *Runtime) FindFunction(funcName string) (FunctionWrapper, error) { 155 | result := C.m3Err_none 156 | var f C.IM3Function 157 | cFuncName := C.CString(funcName) 158 | defer C.free(unsafe.Pointer(cFuncName)) 159 | result = C.m3_FindFunction( 160 | &f, 161 | r.Ptr(), 162 | cFuncName, 163 | ) 164 | if result != nil { 165 | return nil, errFuncLookupFailed 166 | } 167 | fn := &Function{ 168 | ptr: (FunctionT)(f), 169 | } 170 | // var fnWrapper FunctionWrapper 171 | // fnWrapper = fn.Call 172 | return FunctionWrapper(fn.Call), nil 173 | } 174 | 175 | // Destroy free calls m3_FreeRuntime 176 | func(r *Runtime) Destroy() { 177 | C.m3_FreeRuntime(r.Ptr()); 178 | r.cfg.Environment.Destroy() 179 | } 180 | 181 | // Memory allows access to runtime Memory. 182 | // Taken from Wasmer extension: https://github.com/wasmerio/go-ext-wasm 183 | func(r *Runtime) Memory() []byte { 184 | mem := C.get_allocated_memory( 185 | r.Ptr(), 186 | ) 187 | var data = (*uint8)(mem) 188 | length := r.GetAllocatedMemoryLength() 189 | var header reflect.SliceHeader 190 | header = *(*reflect.SliceHeader)(unsafe.Pointer(&header)) 191 | header.Data = uintptr(unsafe.Pointer(data)) 192 | header.Len = int(length) 193 | header.Cap = int(length) 194 | return *(*[]byte)(unsafe.Pointer(&header)) 195 | } 196 | 197 | // GetAllocatedMemoryLength returns the amount of allocated runtime memory 198 | func(r *Runtime) GetAllocatedMemoryLength() int { 199 | length := C.get_allocated_memory_length(r.Ptr()) 200 | return int(length) 201 | } 202 | 203 | // ParseModule is a helper that calls the same function in env. 204 | func(r *Runtime) ParseModule(wasmBytes []byte) (*Module, error) { 205 | return r.cfg.Environment.ParseModule(wasmBytes) 206 | } 207 | 208 | // NewRuntime initializes a new runtime 209 | // TODO: nativeStackInfo is passed as NULL 210 | func NewRuntime(cfg *Config) *Runtime { 211 | // env *Environment, stackSize uint 212 | ptr := C.m3_NewRuntime( 213 | cfg.Environment.Ptr(), 214 | C.uint(cfg.StackSize), 215 | nil, 216 | ) 217 | return &Runtime{ 218 | ptr: (RuntimeT)(ptr), 219 | cfg: cfg, 220 | } 221 | } 222 | 223 | // Module wraps a WASM3 module. 224 | type Module struct { 225 | ptr ModuleT 226 | numFunctions int 227 | } 228 | 229 | // Ptr returns a pointer to IM3Module 230 | func(m *Module) Ptr() C.IM3Module { 231 | return (C.IM3Module)(m.ptr) 232 | } 233 | 234 | // GetFunction provides access to IM3Function->functions 235 | func(m *Module) GetFunction(index uint) (*Function, error) { 236 | if uint(m.NumFunctions()) <= index { 237 | return nil, errFuncLookupFailed 238 | } 239 | ptr := C.module_get_function(m.Ptr(), C.int(index)) 240 | name := C.GoString(ptr.name) 241 | return &Function{ 242 | ptr: (FunctionT)(ptr), 243 | Name: name, 244 | }, nil 245 | } 246 | 247 | // GetFunctionByName is a helper to lookup functions by name 248 | // TODO: could be optimized by caching function names and pointer on the Go side, right after the load call. 249 | func(m *Module) GetFunctionByName(lookupName string) (*Function, error) { 250 | var fn *Function 251 | for i :=0 ; i < m.NumFunctions(); i++ { 252 | ptr := C.module_get_function(m.Ptr(), C.int(i)) 253 | name := C.GoString(ptr.name) 254 | if name != lookupName { 255 | continue 256 | } 257 | fn = &Function{ 258 | ptr: (FunctionT)(ptr), 259 | Name: name, 260 | } 261 | return fn, nil 262 | } 263 | return nil, errFuncLookupFailed 264 | } 265 | 266 | // NumFunctions provides access to numFunctions. 267 | func(m *Module) NumFunctions() int { 268 | // In case the number of functions hasn't been resolved yet, retrieve the int and keep it in the structure 269 | if m.numFunctions == -1 { 270 | m.numFunctions = int(m.Ptr().numFunctions) 271 | } 272 | return m.numFunctions 273 | } 274 | 275 | // NewModule wraps a WASM3 moduke 276 | func NewModule(ptr ModuleT) *Module { 277 | return &Module{ 278 | ptr: ptr, 279 | numFunctions: -1, 280 | } 281 | } 282 | 283 | // Function is a function wrapper 284 | type Function struct { 285 | ptr FunctionT 286 | // fnWrapper FunctionWrapper 287 | Name string 288 | } 289 | 290 | // FunctionWrapper is used to wrap WASM3 call methods and make the calls more idiomatic 291 | // TODO: this is very limited, we need to handle input and output types appropriately 292 | type FunctionWrapper func(args ...interface{}) (int, error) 293 | 294 | // Ptr returns a pointer to IM3Function 295 | func(f *Function) Ptr() C.IM3Function { 296 | return (C.IM3Function)(f.ptr) 297 | } 298 | 299 | // CallWithArgs wraps m3_CallWithArgs 300 | func(f *Function) CallWithArgs(args... string) { 301 | length := len(args) 302 | cArgs := make([]*C.char, length) 303 | for i, v := range args { 304 | cVal := C.CString(v) 305 | cArgs[i] = cVal 306 | } 307 | C.m3_CallWithArgs(f.Ptr(), C.uint(length), &cArgs[0]) 308 | } 309 | 310 | // Call implements a better call function 311 | // TODO: support diferent types 312 | func(f *Function) Call(args... interface{}) (int, error) { 313 | length := len(args) 314 | if length == 0 { 315 | result := C.call(f.Ptr(), 0, nil) 316 | if result == -1 { 317 | return int(result), errors.New(LastErrorString()) 318 | } 319 | return int(result), nil 320 | } 321 | cArgs := make([]C.int, length) 322 | for i, v := range args { 323 | val := v.(int) 324 | n := C.int(val) 325 | cArgs[i] = n 326 | } 327 | result := C.call(f.Ptr(), C.uint(length), &cArgs[0]) 328 | if result == -1 { 329 | return int(result), errors.New(LastErrorString()) 330 | } 331 | return int(result), nil 332 | } 333 | 334 | // Environment wraps a WASM3 environment 335 | type Environment struct { 336 | ptr EnvironmentT 337 | } 338 | 339 | // ParseModule wraps m3_ParseModule 340 | func(e *Environment) ParseModule(wasmBytes []byte) (*Module, error) { 341 | result := C.m3Err_none 342 | bytes := C.CBytes(wasmBytes) 343 | length := len(wasmBytes) 344 | var module C.IM3Module 345 | result = C.m3_ParseModule( 346 | e.Ptr(), 347 | &module, 348 | (*C.uchar)(bytes), 349 | C.uint(length), 350 | ) 351 | if result != nil { 352 | return nil, errParseModule 353 | } 354 | return NewModule((ModuleT)(module)), nil 355 | } 356 | // Ptr returns a pointer to IM3Environment 357 | func(e *Environment) Ptr() C.IM3Environment { 358 | return (C.IM3Environment)(e.ptr) 359 | } 360 | 361 | // Destroy calls m3_FreeEnvironment 362 | func(e *Environment) Destroy() { 363 | C.m3_FreeEnvironment(e.Ptr()) 364 | } 365 | 366 | // NewEnvironment initializes a new environment 367 | func NewEnvironment() *Environment { 368 | ptr := C.m3_NewEnvironment() 369 | return &Environment{ 370 | ptr: (EnvironmentT)(ptr), 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /wasm3_test.go: -------------------------------------------------------------------------------- 1 | package wasm3 2 | 3 | import ( 4 | "io/ioutil" 5 | "testing" 6 | ) 7 | 8 | const ( 9 | sumModulePath = "examples/sum/sum.wasm" 10 | ) 11 | 12 | var ( 13 | sumModuleBytes []byte 14 | ) 15 | 16 | func init() { 17 | var err error 18 | sumModuleBytes, err = ioutil.ReadFile(sumModulePath) 19 | if err != nil { 20 | panic(err) 21 | } 22 | } 23 | func TestEnvRuntimeCycle(t *testing.T) { 24 | runtime := NewRuntime(&Config{ 25 | Environment: NewEnvironment(), 26 | StackSize: 64 * 1024, 27 | }) 28 | defer runtime.Destroy() 29 | } 30 | 31 | func TestParseModule(t *testing.T) { 32 | env := NewEnvironment() 33 | _, err := env.ParseModule([]byte("")) 34 | if err == nil { 35 | t.Fatal("Invalid input should error") 36 | } 37 | module, err := env.ParseModule(sumModuleBytes) 38 | if err != nil { 39 | t.Fatal("Couldn't parse valid WASM module") 40 | } 41 | if module.ptr == nil { 42 | t.Fatal("Internal module pointer is nil") 43 | } 44 | } 45 | 46 | func TestLoadModule(t *testing.T) { 47 | runtime := NewRuntime(&Config{ 48 | Environment: NewEnvironment(), 49 | StackSize: 64 * 1024, 50 | }) 51 | defer runtime.Destroy() 52 | module, _ := runtime.ParseModule(sumModuleBytes) 53 | _, err := runtime.LoadModule(module) 54 | if err != nil { 55 | t.Fatal("Couldn't load sample module") 56 | } 57 | _, err = runtime.FindFunction("nonexistent") 58 | if err == nil { 59 | t.Fatal("No error when referencing a nonexistent function") 60 | } 61 | _, err = runtime.FindFunction("sum") 62 | if err != nil { 63 | t.Fatal("Couldn't find 'sum' test function") 64 | } 65 | } 66 | 67 | func TestModuleHelpers(t *testing.T) { 68 | env := NewEnvironment() 69 | module, _ := env.ParseModule(sumModuleBytes) 70 | if module.numFunctions != -1 { 71 | t.Fatal("Initial value for numFunctions should be -1") 72 | } 73 | fn, err := module.GetFunctionByName("sum") 74 | if err != nil { 75 | t.Fatal("Couldn't find 'sum' test function using name lookup") 76 | } 77 | if fn.Name == "" { 78 | t.Fatal("Function name is empty") 79 | } 80 | fn2, err := module.GetFunction(0) 81 | if err != nil { 82 | t.Fatal("Couldn't find 'sum' test function using index lookup") 83 | } 84 | if fn2.Name == "" { 85 | t.Fatal("Function name is empty") 86 | } 87 | if module.NumFunctions() != 1 { 88 | t.Fatal("Module NumFunctions should be 1") 89 | } 90 | } 91 | --------------------------------------------------------------------------------