First iteration of wasmer-reborn
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
CHANGELOG.md merge=union
|
25
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
#
|
||||
src/ @syrusakbary @MarkMcCaskey
|
||||
|
||||
# Backends
|
||||
lib/compiler-singlepass @losfair @nlewycky
|
||||
lib/compiler-cranelift @syrusakbary @nlewycky
|
||||
lib/compiler-llvm @nlewycky @losfair
|
||||
|
||||
# Runtime
|
||||
lib/runtime @syrusakbary @Hywan
|
||||
lib/c-api @Hywan
|
||||
|
||||
# Frontend integrations
|
||||
|
||||
## Emscripten
|
||||
lib/emscripten @MarkMcCaskey @syrusakbary
|
||||
|
||||
## WASI
|
||||
lib/wasi @MarkMcCaskey
|
||||
|
||||
# Examples
|
||||
examples @syrusakbary
|
||||
|
||||
# Examples
|
||||
tests @syrusakbary @MarkMcCaskey
|
50
.github/ISSUE_TEMPLATE/---bug-report.md
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
---
|
||||
name: "\U0001F41E Bug report"
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: "\U0001F41E bug"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Thanks for the bug report! -->
|
||||
|
||||
### Describe the bug
|
||||
|
||||
<!--
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
Copy and paste the result of executing the following in your shell, so we can know the version of wasmer, Rust (if available) and architecture of your environment.
|
||||
-->
|
||||
|
||||
```sh
|
||||
echo "`wasmer -V` | `rustc -V` | `uname -m`"
|
||||
```
|
||||
|
||||
|
||||
### Steps to reproduce
|
||||
<!--
|
||||
Include steps that will help us recreate the issue.
|
||||
|
||||
For example,
|
||||
1. Go to '…'
|
||||
2. Compile with '…'
|
||||
3. Run '…'
|
||||
4. See error
|
||||
|
||||
If applicable, add a link to a test case (as a zip file or link to a repository we can clone).
|
||||
-->
|
||||
|
||||
### Expected behavior
|
||||
<!-- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
### Actual behavior
|
||||
|
||||
<!--
|
||||
A clear and concise description of what actually happened.
|
||||
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
-->
|
||||
|
||||
### Additional context
|
||||
<!-- Add any other context about the problem here. -->
|
26
.github/ISSUE_TEMPLATE/---feature-request.md
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
name: "\U0001F389 Feature request"
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: "\U0001F389 enhancement"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Thanks for proposing a new feature!
|
||||
|
||||
### Motivation
|
||||
|
||||
A clear and concise description of what the motivation for the new feature is, and what problem it is solving.
|
||||
|
||||
### Proposed solution
|
||||
|
||||
A clear and concise description of the feature you would like to add, and how it solves the motivating problem.
|
||||
|
||||
### Alternatives
|
||||
|
||||
A clear and concise description of any alternative solutions or features you've considered, and why you're proposed solution is better.
|
||||
|
||||
### Additional context
|
||||
|
||||
Add any other context or screenshots about the feature request here.
|
16
.github/ISSUE_TEMPLATE/--question.md
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
name: "❓ Question"
|
||||
about: Ask a question about this project
|
||||
title: ''
|
||||
labels: "❓ question"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Summary
|
||||
|
||||
A clear and concise summary of your question.
|
||||
|
||||
### Additional details
|
||||
|
||||
Provide any additional details here.
|
15
.github/pull_request_template.md
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<!--
|
||||
Prior to submitting a PR, review the CONTRIBUTING.md document for recommendations on how to test:
|
||||
https://github.com/wasmerio/wasmer/blob/master/CONTRIBUTING.md#pull-requests
|
||||
|
||||
-->
|
||||
|
||||
# Description
|
||||
<!--
|
||||
Provide details regarding the change including motivation,
|
||||
links to related issues, and the context of the PR.
|
||||
-->
|
||||
|
||||
# Review
|
||||
|
||||
- [ ] Add a short description of the the change to the CHANGELOG.md file
|
14
.gitignore
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
**/target
|
||||
**/*.rs.bk
|
||||
/artifacts
|
||||
.DS_Store
|
||||
.idea
|
||||
**/.vscode
|
||||
install/
|
||||
capi/
|
||||
api-docs/
|
||||
api-docs-repo/
|
||||
|
||||
# Generated by tests on Android
|
||||
/avd
|
||||
/core
|
611
ATTRIBUTIONS.md
Normal file
@ -0,0 +1,611 @@
|
||||
# Wasmer Attributions
|
||||
|
||||
Wasmer is a community effort and makes use of code from various other
|
||||
projects. Listed below are notable sections of code that are licensed
|
||||
from other projects and the relevant license of those projects.
|
||||
|
||||
These are the projects that were used as inspiration and/or that we are using code from:
|
||||
|
||||
- [Nebulet](https://github.com/nebulet/nebulet): as the base for creating a great Rust WebAssembly runtime
|
||||
- [WAVM](https://github.com/wavm/wavm): for their great integration and testing framework
|
||||
- [wasmtime](https://github.com/CraneStation/wasmtime):
|
||||
For their wast, environ, jit, runtime and debugging with
|
||||
the `__jit_debug_register_code` function
|
||||
in Rust, the structure of using Cranelift with the GDB JIT
|
||||
interface including implementation details regarding the structure
|
||||
of generating debug information for each function with Cranelift
|
||||
(for example, the sorting of the extended basic blocks before
|
||||
processing the instructions), and the API for transforming DWARF
|
||||
see [wasm-debug's attribution file](https://github.com/wasmerio/wasm-debug/blob/master/ATTRIBUTIONS.md)
|
||||
for more information.
|
||||
- [stackoverflow](https://stackoverflow.com/a/45795699/1072990): to create an efficient HashMap with pair keys
|
||||
- [Emscripten](https://github.com/kripken/emscripten): for emtests test sources to ensure compatibility
|
||||
- [The WebAssembly spec](https://github.com/WebAssembly/spec/tree/master/test): for implementation details of WebAssembly and spectests
|
||||
|
||||
Please let us know if you believe there is an error or omission in
|
||||
this list and we will do our best to correct it.
|
||||
|
||||
## Licenses
|
||||
|
||||
### Nebulet
|
||||
|
||||
```text
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
|
||||
### WAVM
|
||||
|
||||
```text
|
||||
Copyright (c) 2018, Andrew Scheidecker
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of WAVM nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The contents of [Test/spec](Test/spec) is covered by the license in [Test/spec/LICENSE](Test/spec/LICENSE).
|
||||
|
||||
[Source/ThirdParty/dtoa/dtoa.c](Source/ThirdParty/dtoa/dtoa.c) is covered by the license in that file.
|
||||
|
||||
[Source/ThirdParty/libunwind](Source/ThirdParty/libunwind) is covered by the license in [Source/ThirdParty/libunwind/LICENSE.TXT](Source/ThirdParty/libunwind/LICENSE.TXT).
|
||||
|
||||
[Source/ThirdParty/xxhash](Source/ThirdParty/xxhash) is covered by the license in [Source/ThirdParty/xxhash/LICENSE](Source/ThirdParty/xxhash/LICENSE).
|
||||
```
|
||||
|
||||
### Greenwasm
|
||||
|
||||
```text
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
```
|
||||
|
||||
### Wasmtime
|
||||
|
||||
```text
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
--- LLVM Exceptions to the Apache 2.0 License ----
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into an Object form of such source code, you
|
||||
may redistribute such embedded portions in such Object form without complying
|
||||
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
|
||||
|
||||
In addition, if you combine or link compiled forms of this Software with
|
||||
software that is licensed under the GPLv2 ("Combined Software") and if a
|
||||
court of competent jurisdiction determines that the patent provision (Section
|
||||
3), the indemnity provision (Section 9) or other Section of the License
|
||||
conflicts with the conditions of the GPLv2, you may retroactively and
|
||||
prospectively choose to deem waived or otherwise exclude such Section(s) of
|
||||
the License, but only in their entirety and only with respect to the Combined
|
||||
Software.
|
||||
```
|
||||
|
||||
### Emscripten
|
||||
```text
|
||||
Emscripten is available under 2 licenses, the MIT license and the
|
||||
University of Illinois/NCSA Open Source License.
|
||||
|
||||
Both are permissive open source licenses, with little if any
|
||||
practical difference between them.
|
||||
|
||||
The reason for offering both is that (1) the MIT license is
|
||||
well-known, while (2) the University of Illinois/NCSA Open Source
|
||||
License allows Emscripten's code to be integrated upstream into
|
||||
LLVM, which uses that license, should the opportunity arise.
|
||||
|
||||
The full text of both licenses follows.
|
||||
|
||||
==============================================================================
|
||||
|
||||
Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
|
||||
Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file.
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal with the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimers.
|
||||
|
||||
Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimers
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
Neither the names of Mozilla,
|
||||
nor the names of its contributors may be used to endorse
|
||||
or promote products derived from this Software without specific prior
|
||||
written permission.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
|
||||
This program uses portions of Node.js source code located in src/library_path.js,
|
||||
in accordance with the terms of the MIT license. Node's license follows:
|
||||
|
||||
"""
|
||||
Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
The musl libc project is bundled in this repo, and it has the MIT license, see
|
||||
system/lib/libc/musl/COPYRIGHT
|
||||
|
||||
The third_party/ subdirectory contains code with other licenses. None of it is
|
||||
used by default, but certain options use it (e.g., the optional closure compiler
|
||||
flag will run closure compiler from third_party/).
|
||||
|
||||
```
|
356
CHANGELOG.md
Normal file
@ -0,0 +1,356 @@
|
||||
# Changelog
|
||||
|
||||
## **[Unreleased]**
|
||||
|
||||
- [#1382](https://github.com/wasmerio/wasmer/pull/1382) Refactored test infranstructure (part 2)
|
||||
- [#1380](https://github.com/wasmerio/wasmer/pull/1380) Refactored test infranstructure (part 1)
|
||||
- [#1357](https://github.com/wasmerio/wasmer/pull/1357) Refactored bin commands into separate files
|
||||
- [#1331](https://github.com/wasmerio/wasmer/pull/1331) Implement the `record` type and instrutions for WIT
|
||||
- [#1345](https://github.com/wasmerio/wasmer/pull/1345) Adding ARM testing in Azure Pipelines
|
||||
- [#1335](https://github.com/wasmerio/wasmer/pull/1335) Change mutability of `memory` to `const` in `wasmer_memory_data_length` in the C API
|
||||
- [#1329](https://github.com/wasmerio/wasmer/pull/1329) New numbers and strings instructions for WIT
|
||||
- [#1332](https://github.com/wasmerio/wasmer/pull/1332) Add option to `CompilerConfig` to force compiler IR verification off even when `debug_assertions` are enabled. This can be used to make debug builds faster, which may be important if you're creating a library that wraps Wasmer and depend on the speed of debug builds.
|
||||
- [#1320](https://github.com/wasmerio/wasmer/pull/1320) Change `custom_sections` field in `ModuleInfo` to be more standards compliant by allowing multiple custom sections with the same name. To get the old behavior with the new API, you can add `.last().unwrap()` to accesses. For example, `module_info.custom_sections["custom_section_name"].last().unwrap()`.
|
||||
- [#1313](https://github.com/wasmerio/wasmer/pull/1313) Add new high-level public API through `wasmer` crate. Includes many updates including:
|
||||
- Minor improvement: `imports!` macro now handles no trailing comma as well as a trailing comma in namespaces and between namespaces.
|
||||
- New methods on `Module`: `exports`, `imports`, and `custom_sections`.
|
||||
- New way to get exports from an instance with `let func_name: Func<i32, i64> = instance.exports.get("func_name");`.
|
||||
- Improved `Table` APIs including `set` which now allows setting functions directly. TODO: update this more if `Table::get` gets made public in this PR
|
||||
- TODO: finish the list of changes here
|
||||
- [#1303](https://github.com/wasmerio/wasmer/pull/1303) NaN canonicalization for singlepass backend.
|
||||
- [#1292](https://github.com/wasmerio/wasmer/pull/1292) Experimental Support for Android (x86_64 and AArch64)
|
||||
- [#1305](https://github.com/wasmerio/wasmer/pull/1305) Handle panics from DynamicFunc.
|
||||
- [#1301](https://github.com/wasmerio/wasmer/pull/1301) Update supported stable Rust version to 1.41.1.
|
||||
- [#1300](https://github.com/wasmerio/wasmer/pull/1300) Add support for multiple versions of WASI tests: wasitests now test all versions of WASI.
|
||||
- [#1285](https://github.com/wasmerio/wasmer/pull/1285) Greatly improve errors in `wasmer-interface-types`
|
||||
- [#1283](https://github.com/wasmerio/wasmer/pull/1283) Workaround for floating point arguments and return values in `DynamicFunc`s.
|
||||
|
||||
## 0.16.2 - 2020-03-11
|
||||
|
||||
- [#1294](https://github.com/wasmerio/wasmer/pull/1294) Fix bug related to system calls in WASI that rely on reading from WasmPtrs as arrays of length 0. `WasmPtr` will now succeed on length 0 arrays again.
|
||||
|
||||
## 0.16.1 - 2020-03-11
|
||||
|
||||
- [#1291](https://github.com/wasmerio/wasmer/pull/1291) Fix installation packaging script to package the `wax` command.
|
||||
|
||||
## 0.16.0 - 2020-03-11
|
||||
|
||||
- [#1286](https://github.com/wasmerio/wasmer/pull/1286) Updated Windows Wasmer icons. Add wax
|
||||
- [#1284](https://github.com/wasmerio/wasmer/pull/1284) Implement string and memory instructions in `wasmer-interface-types`
|
||||
- [#1272](https://github.com/wasmerio/wasmer/pull/1272) Fix off-by-one error bug when accessing memory with a `WasmPtr` that contains the last valid byte of memory. Also changes the behavior of `WasmPtr<T, Array>` with a length of 0 and `WasmPtr<T>` where `std::mem::size_of::<T>()` is 0 to always return `None`
|
||||
|
||||
## 0.15.0 - 2020-03-04
|
||||
|
||||
- [#1263](https://github.com/wasmerio/wasmer/pull/1263) Changed the behavior of some WASI syscalls to now handle preopened directories more properly. Changed default `--debug` logging to only show Wasmer-related messages.
|
||||
- [#1217](https://github.com/wasmerio/wasmer/pull/1217) Polymorphic host functions based on dynamic trampoline generation.
|
||||
- [#1252](https://github.com/wasmerio/wasmer/pull/1252) Allow `/` in wasi `--mapdir` wasm path.
|
||||
- [#1212](https://github.com/wasmerio/wasmer/pull/1212) Add support for GDB JIT debugging:
|
||||
- Add `--generate-debug-info` and `-g` flags to `wasmer run` to generate debug information during compilation. The debug info is passed via the GDB JIT interface to a debugger to allow source-level debugging of Wasm files. Currently only available on clif-backend.
|
||||
- Break public middleware APIs: there is now a `source_loc` parameter that should be passed through if applicable.
|
||||
- Break compiler trait methods such as `feed_local`, `feed_event` as well as `ModuleCodeGenerator::finalize`.
|
||||
|
||||
## 0.14.1 - 2020-02-24
|
||||
|
||||
- [#1245](https://github.com/wasmerio/wasmer/pull/1245) Use Ubuntu 16.04 in CI so that we use an earlier version of GLIBC.
|
||||
- [#1234](https://github.com/wasmerio/wasmer/pull/1234) Check for unused excluded spectest failures.
|
||||
- [#1232](https://github.com/wasmerio/wasmer/pull/1232) `wasmer-interface-types` has a WAT decoder.
|
||||
|
||||
## 0.14.0 - 2020-02-20
|
||||
|
||||
- [#1233](https://github.com/wasmerio/wasmer/pull/1233) Improved Wasmer C API release artifacts.
|
||||
- [#1216](https://github.com/wasmerio/wasmer/pull/1216) `wasmer-interface-types` receives a binary encoder.
|
||||
- [#1228](https://github.com/wasmerio/wasmer/pull/1228) Singlepass cleanup: Resolve several FIXMEs and remove protect_unix.
|
||||
- [#1218](https://github.com/wasmerio/wasmer/pull/1218) Enable Cranelift verifier in debug mode. Fix bug with table indices being the wrong type.
|
||||
- [#787](https://github.com/wasmerio/wasmer/pull/787) New crate `wasmer-interface-types` to implement WebAssembly Interface Types.
|
||||
- [#1213](https://github.com/wasmerio/wasmer/pull/1213) Fixed WASI `fdstat` to detect `isatty` properly.
|
||||
- [#1192](https://github.com/wasmerio/wasmer/pull/1192) Use `ExceptionCode` for error representation.
|
||||
- [#1191](https://github.com/wasmerio/wasmer/pull/1191) Fix singlepass miscompilation on `Operator::CallIndirect`.
|
||||
- [#1180](https://github.com/wasmerio/wasmer/pull/1180) Fix compilation for target `x86_64-unknown-linux-musl`.
|
||||
- [#1170](https://github.com/wasmerio/wasmer/pull/1170) Improve the WasiFs builder API with convenience methods for overriding stdin, stdout, and stderr as well as a new sub-builder for controlling the permissions and properties of preopened directories. Also breaks that implementations of `WasiFile` must be `Send` -- please file an issue if this change causes you any issues.
|
||||
- [#1161](https://github.com/wasmerio/wasmer/pull/1161) Require imported functions to be `Send`. This is a breaking change that fixes a soundness issue in the API.
|
||||
- [#1140](https://github.com/wasmerio/wasmer/pull/1140) Use [`blake3`](https://github.com/BLAKE3-team/BLAKE3) as default hashing algorithm for caching.
|
||||
- [#1129](https://github.com/wasmerio/wasmer/pull/1129) Standard exception types for singlepass backend.
|
||||
|
||||
## 0.13.1 - 2020-01-16
|
||||
- Fix bug in wapm related to the `package.wasmer_extra_flags` entry in the manifest
|
||||
|
||||
## 0.13.0 - 2020-01-15
|
||||
|
||||
Special thanks to [@repi](https://github.com/repi) and [@srenatus](https://github.com/srenatus) for their contributions!
|
||||
|
||||
- [#1153](https://github.com/wasmerio/wasmer/pull/1153) Added Wasmex, an Elixir language integration, to the README
|
||||
- [#1133](https://github.com/wasmerio/wasmer/pull/1133) New `wasmer_trap` function in the C API, to properly error from within a host function
|
||||
- [#1147](https://github.com/wasmerio/wasmer/pull/1147) Remove `log` and `trace` macros from `wasmer-runtime-core`, remove `debug` and `trace` features from `wasmer-*` crates, use the `log` crate for logging and use `fern` in the Wasmer CLI binary to output log messages. Colorized output will be enabled automatically if printing to a terminal, to force colorization on or off, set the `WASMER_COLOR` environment variable to `true` or `false`.
|
||||
- [#1128](https://github.com/wasmerio/wasmer/pull/1128) Fix a crash when a host function is missing and the `allow_missing_functions` flag is enabled
|
||||
- [#1099](https://github.com/wasmerio/wasmer/pull/1099) Remove `backend::Backend` from `wasmer_runtime_core`
|
||||
- [#1097](https://github.com/wasmerio/wasmer/pull/1097) Move inline breakpoint outside of runtime backend
|
||||
- [#1095](https://github.com/wasmerio/wasmer/pull/1095) Update to cranelift 0.52.
|
||||
- [#1092](https://github.com/wasmerio/wasmer/pull/1092) Add `get_utf8_string_with_nul` to `WasmPtr` to read nul-terminated strings from memory.
|
||||
- [#1071](https://github.com/wasmerio/wasmer/pull/1071) Add support for non-trapping float-to-int conversions, enabled by default.
|
||||
|
||||
## 0.12.0 - 2019-12-18
|
||||
|
||||
Special thanks to [@ethanfrey](https://github.com/ethanfrey), [@AdamSLevy](https://github.com/AdamSLevy), [@Jasper-Bekkers](https://github.com/Jasper-Bekkers), [@srenatus](https://github.com/srenatus) for their contributions!
|
||||
|
||||
- [#1078](https://github.com/wasmerio/wasmer/pull/1078) Increase the maximum number of parameters `Func` can take
|
||||
- [#1062](https://github.com/wasmerio/wasmer/pull/1062) Expose some opt-in Emscripten functions to the C API
|
||||
- [#1032](https://github.com/wasmerio/wasmer/pull/1032) Change the signature of the Emscripten `abort` function to work with Emscripten 1.38.30
|
||||
- [#1060](https://github.com/wasmerio/wasmer/pull/1060) Test the capi with all the backends
|
||||
- [#1069](https://github.com/wasmerio/wasmer/pull/1069) Add function `get_memory_and_data` to `Ctx` to help prevent undefined behavior and mutable aliasing. It allows accessing memory while borrowing data mutably for the `Ctx` lifetime. This new function is now being used in `wasmer-wasi`.
|
||||
- [#1058](https://github.com/wasmerio/wasmer/pull/1058) Fix minor panic issue when `wasmer::compile_with` called with llvm backend.
|
||||
- [#858](https://github.com/wasmerio/wasmer/pull/858) Minor panic fix when wasmer binary with `loader` option run a module without exported `_start` function.
|
||||
- [#1056](https://github.com/wasmerio/wasmer/pull/1056) Improved `--invoke` args parsing (supporting `i32`, `i64`, `f32` and `f32`) in Wasmer CLI
|
||||
- [#1054](https://github.com/wasmerio/wasmer/pull/1054) Improve `--invoke` output in Wasmer CLI
|
||||
- [#1053](https://github.com/wasmerio/wasmer/pull/1053) For RuntimeError and breakpoints, use Box<Any + Send> instead of Box<Any>.
|
||||
- [#1052](https://github.com/wasmerio/wasmer/pull/1052) Fix minor panic and improve Error handling in singlepass backend.
|
||||
- [#1050](https://github.com/wasmerio/wasmer/pull/1050) Attach C & C++ headers to releases.
|
||||
- [#1033](https://github.com/wasmerio/wasmer/pull/1033) Set cranelift backend as default compiler backend again, require at least one backend to be enabled for Wasmer CLI
|
||||
- [#1044](https://github.com/wasmerio/wasmer/pull/1044) Enable AArch64 support in the LLVM backend.
|
||||
- [#1030](https://github.com/wasmerio/wasmer/pull/1030) Ability to generate `ImportObject` for a specific version WASI version with the C API.
|
||||
- [#1028](https://github.com/wasmerio/wasmer/pull/1028) Introduce strict/non-strict modes for `get_wasi_version`
|
||||
- [#1029](https://github.com/wasmerio/wasmer/pull/1029) Add the “floating” `WasiVersion::Latest` version.
|
||||
- [#1006](https://github.com/wasmerio/wasmer/pull/1006) Fix minor panic issue when `wasmer::compile_with` called with llvm backend
|
||||
- [#1009](https://github.com/wasmerio/wasmer/pull/1009) Enable LLVM verifier for all tests, add new llvm-backend-tests crate.
|
||||
- [#1022](https://github.com/wasmerio/wasmer/pull/1022) Add caching support for Singlepass backend.
|
||||
- [#1004](https://github.com/wasmerio/wasmer/pull/1004) Add the Auto backend to enable to adapt backend usage depending on wasm file executed.
|
||||
- [#1068](https://github.com/wasmerio/wasmer/pull/1068) Various cleanups for the singlepass backend on AArch64.
|
||||
|
||||
## 0.11.0 - 2019-11-22
|
||||
|
||||
- [#713](https://github.com/wasmerio/wasmer/pull/713) Add AArch64 support for singlepass.
|
||||
- [#995](https://github.com/wasmerio/wasmer/pull/995) Detect when a global is read without being initialized (emit a proper error instead of panicking)
|
||||
- [#996](https://github.com/wasmerio/wasmer/pull/997) Refactored spectests, emtests and wasitests to use default compiler logic
|
||||
- [#992](https://github.com/wasmerio/wasmer/pull/992) Updates WAPM version to 0.4.1, fix arguments issue introduced in #990
|
||||
- [#990](https://github.com/wasmerio/wasmer/pull/990) Default wasmer CLI to `run`. Wasmer will now attempt to parse unrecognized command line options as if they were applied to the run command: `wasmer mywasm.wasm --dir=.` now works!
|
||||
- [#987](https://github.com/wasmerio/wasmer/pull/987) Fix `runtime-c-api` header files when compiled by gnuc.
|
||||
- [#957](https://github.com/wasmerio/wasmer/pull/957) Change the meaning of `wasmer_wasi::is_wasi_module` to detect any type of WASI module, add support for new wasi snapshot_preview1
|
||||
- [#934](https://github.com/wasmerio/wasmer/pull/934) Simplify float expressions in the LLVM backend.
|
||||
|
||||
## 0.10.2 - 2019-11-18
|
||||
|
||||
- [#968](https://github.com/wasmerio/wasmer/pull/968) Added `--invoke` option to the command
|
||||
- [#964](https://github.com/wasmerio/wasmer/pull/964) Enable cross-compilation for specific target
|
||||
- [#971](https://github.com/wasmerio/wasmer/pull/971) In LLVM backend, use unaligned loads and stores for non-atomic accesses to wasmer memory.
|
||||
- [#960](https://github.com/wasmerio/wasmer/pull/960) Fix `runtime-c-api` header files when compiled by clang.
|
||||
- [#925](https://github.com/wasmerio/wasmer/pull/925) Host functions can be closures with a captured environment.
|
||||
- [#917](https://github.com/wasmerio/wasmer/pull/917) Host functions (aka imported functions) may not have `&mut vm::Ctx` as first argument, i.e. the presence of the `&mut vm::Ctx` argument is optional.
|
||||
- [#915](https://github.com/wasmerio/wasmer/pull/915) All backends share the same definition of `Trampoline` (defined in `wasmer-runtime-core`).
|
||||
|
||||
## 0.10.1 - 2019-11-11
|
||||
|
||||
- [#952](https://github.com/wasmerio/wasmer/pull/952) Use C preprocessor to properly hide trampoline functions on Windows and non-x86_64 targets.
|
||||
|
||||
## 0.10.0 - 2019-11-11
|
||||
|
||||
Special thanks to [@newpavlov](https://github.com/newpavlov) and [@Maxgy](https://github.com/Maxgy) for their contributions!
|
||||
|
||||
- [#942](https://github.com/wasmerio/wasmer/pull/942) Deny missing docs in runtime core and add missing docs
|
||||
- [#939](https://github.com/wasmerio/wasmer/pull/939) Fix bug causing attempts to append to files with WASI to delete the contents of the file
|
||||
- [#940](https://github.com/wasmerio/wasmer/pull/940) Update supported Rust version to 1.38+
|
||||
- [#923](https://github.com/wasmerio/wasmer/pull/923) Fix memory leak in the C API caused by an incorrect cast in `wasmer_trampoline_buffer_destroy`
|
||||
- [#921](https://github.com/wasmerio/wasmer/pull/921) In LLVM backend, annotate all memory accesses with TBAA metadata.
|
||||
- [#883](https://github.com/wasmerio/wasmer/pull/883) Allow floating point operations to have arbitrary inputs, even including SNaNs.
|
||||
- [#856](https://github.com/wasmerio/wasmer/pull/856) Expose methods in the runtime C API to get a WASI import object
|
||||
|
||||
## 0.9.0 - 2019-10-23
|
||||
|
||||
Special thanks to @alocquet for their contributions!
|
||||
|
||||
- [#898](https://github.com/wasmerio/wasmer/pull/898) State tracking is now disabled by default in the LLVM backend. It can be enabled with `--track-state`.
|
||||
- [#861](https://github.com/wasmerio/wasmer/pull/861) Add descriptions to `unimplemented!` macro in various places
|
||||
- [#897](https://github.com/wasmerio/wasmer/pull/897) Removes special casing of stdin, stdout, and stderr in WASI. Closing these files now works. Removes `stdin`, `stdout`, and `stderr` from `WasiFS`, replaced by the methods `stdout`, `stdout_mut`, and so on.
|
||||
- [#863](https://github.com/wasmerio/wasmer/pull/863) Fix min and max for cases involving NaN and negative zero when using the LLVM backend.
|
||||
|
||||
## 0.8.0 - 2019-10-02
|
||||
|
||||
Special thanks to @jdanford for their contributions!
|
||||
|
||||
- [#850](https://github.com/wasmerio/wasmer/pull/850) New `WasiStateBuilder` API. small, add misc. breaking changes to existing API (for example, changing the preopen dirs arg on `wasi::generate_import_object` from `Vec<String>` to `Vec<Pathbuf>`)
|
||||
- [#852](https://github.com/wasmerio/wasmer/pull/852) Make minor grammar/capitalization fixes to README.md
|
||||
- [#841](https://github.com/wasmerio/wasmer/pull/841) Slightly improve rustdoc documentation and small updates to outdated info in readme files
|
||||
- [#836](https://github.com/wasmerio/wasmer/pull/836) Update Cranelift fork version to `0.44.0`
|
||||
- [#839](https://github.com/wasmerio/wasmer/pull/839) Change supported version to stable Rust 1.37+
|
||||
- [#834](https://github.com/wasmerio/wasmer/pull/834) Fix panic when unwraping `wasmer` arguments
|
||||
- [#835](https://github.com/wasmerio/wasmer/pull/835) Add parallel execution example (independent instances created from the same `ImportObject` and `Module` run with rayon)
|
||||
- [#834](https://github.com/wasmerio/wasmer/pull/834) Fix panic when parsing numerical arguments for no-ABI targets run with the wasmer binary
|
||||
- [#833](https://github.com/wasmerio/wasmer/pull/833) Add doc example of using ImportObject's new `maybe_with_namespace` method
|
||||
- [#832](https://github.com/wasmerio/wasmer/pull/832) Delete unused runtime ABI
|
||||
- [#809](https://github.com/wasmerio/wasmer/pull/809) Fix bugs leading to panics in `LocalBacking`.
|
||||
- [#831](https://github.com/wasmerio/wasmer/pull/831) Add support for atomic operations, excluding wait and notify, to singlepass.
|
||||
- [#822](https://github.com/wasmerio/wasmer/pull/822) Update Cranelift fork version to `0.43.1`
|
||||
- [#829](https://github.com/wasmerio/wasmer/pull/829) Fix deps on `make bench-*` commands; benchmarks don't compile other backends now
|
||||
- [#807](https://github.com/wasmerio/wasmer/pull/807) Implement Send for `Instance`, breaking change on `ImportObject`, remove method `get_namespace` replaced with `with_namespace` and `maybe_with_namespace`
|
||||
- [#817](https://github.com/wasmerio/wasmer/pull/817) Add document for tracking features across backends and language integrations, [docs/feature_matrix.md]
|
||||
- [#823](https://github.com/wasmerio/wasmer/issues/823) Improved Emscripten / WASI integration
|
||||
- [#821](https://github.com/wasmerio/wasmer/issues/821) Remove patch version on most deps Cargo manifests. This gives Wasmer library users more control over which versions of the deps they use.
|
||||
- [#820](https://github.com/wasmerio/wasmer/issues/820) Remove null-pointer checks in `WasmPtr` from runtime-core, re-add them in Emscripten
|
||||
- [#803](https://github.com/wasmerio/wasmer/issues/803) Add method to `Ctx` to invoke functions by their `TableIndex`
|
||||
- [#790](https://github.com/wasmerio/wasmer/pull/790) Fix flaky test failure with LLVM, switch to large code model.
|
||||
- [#788](https://github.com/wasmerio/wasmer/pull/788) Use union merge on the changelog file.
|
||||
- [#785](https://github.com/wasmerio/wasmer/pull/785) Include Apache license file for spectests.
|
||||
- [#786](https://github.com/wasmerio/wasmer/pull/786) In the LLVM backend, lower atomic wasm operations to atomic machine instructions.
|
||||
- [#784](https://github.com/wasmerio/wasmer/pull/784) Fix help string for wasmer run.
|
||||
|
||||
## 0.7.0 - 2019-09-12
|
||||
|
||||
Special thanks to @YaronWittenstein @penberg for their contributions.
|
||||
|
||||
- [#776](https://github.com/wasmerio/wasmer/issues/776) Allow WASI preopened fds to be closed
|
||||
- [#774](https://github.com/wasmerio/wasmer/issues/774) Add more methods to the `WasiFile` trait
|
||||
- [#772](https://github.com/wasmerio/wasmer/issues/772) [#770](https://github.com/wasmerio/wasmer/issues/770) Handle more internal failures by passing back errors
|
||||
- [#756](https://github.com/wasmerio/wasmer/issues/756) Allow NULL parameter and 0 arity in `wasmer_export_func_call` C API
|
||||
- [#747](https://github.com/wasmerio/wasmer/issues/747) Return error instead of panicking on traps when using the Wasmer binary
|
||||
- [#741](https://github.com/wasmerio/wasmer/issues/741) Add validate Wasm fuzz target
|
||||
- [#733](https://github.com/wasmerio/wasmer/issues/733) Remove dependency on compiler backends for `middleware-common`
|
||||
- [#732](https://github.com/wasmerio/wasmer/issues/732) [#731](https://github.com/wasmerio/wasmer/issues/731) WASI bug fixes and improvements
|
||||
- [#726](https://github.com/wasmerio/wasmer/issues/726) Add serialization and deserialization for Wasi State
|
||||
- [#716](https://github.com/wasmerio/wasmer/issues/716) Improve portability of install script
|
||||
- [#714](https://github.com/wasmerio/wasmer/issues/714) Add Code of Conduct
|
||||
- [#708](https://github.com/wasmerio/wasmer/issues/708) Remove unconditional dependency on Cranelift in the C API
|
||||
- [#703](https://github.com/wasmerio/wasmer/issues/703) Fix compilation on AArch64 Linux
|
||||
- [#702](https://github.com/wasmerio/wasmer/issues/702) Add SharedMemory to Wasmer. Add `--enable-threads` flag, add partial implementation of atomics to LLVM backend.
|
||||
- [#698](https://github.com/wasmerio/wasmer/issues/698) [#690](https://github.com/wasmerio/wasmer/issues/690) [#687](https://github.com/wasmerio/wasmer/issues/690) Fix panics in Emscripten
|
||||
- [#689](https://github.com/wasmerio/wasmer/issues/689) Replace `wasmer_runtime_code::memory::Atomic` with `std::sync::atomic` atomics, changing its interface
|
||||
- [#680](https://github.com/wasmerio/wasmer/issues/680) [#673](https://github.com/wasmerio/wasmer/issues/673) [#669](https://github.com/wasmerio/wasmer/issues/669) [#660](https://github.com/wasmerio/wasmer/issues/660) [#659](https://github.com/wasmerio/wasmer/issues/659) Misc. runtime and singlepass fixes
|
||||
- [#677](https://github.com/wasmerio/wasmer/issues/677) [#675](https://github.com/wasmerio/wasmer/issues/675) [#674](https://github.com/wasmerio/wasmer/issues/674) LLVM backend fixes and improvements
|
||||
- [#671](https://github.com/wasmerio/wasmer/issues/671) Implement fs polling in `wasi::poll_oneoff` for Unix-like platforms
|
||||
- [#656](https://github.com/wasmerio/wasmer/issues/656) Move CI to Azure Pipelines
|
||||
- [#650](https://github.com/wasmerio/wasmer/issues/650) Implement `wasi::path_rename`, improve WASI FS public api, and allow open files to exist even when the underlying file is deleted
|
||||
- [#643](https://github.com/wasmerio/wasmer/issues/643) Implement `wasi::path_symlink` and improve WASI FS public api IO error reporting
|
||||
- [#608](https://github.com/wasmerio/wasmer/issues/608) Implement wasi syscalls `fd_allocate`, `fd_sync`, `fd_pread`, `path_link`, `path_filestat_set_times`; update WASI fs API in a WIP way; reduce coupling of WASI code to host filesystem; make debug messages from WASI more readable; improve rights-checking when calling syscalls; implement reference counting on inodes; misc bug fixes and improvements
|
||||
- [#616](https://github.com/wasmerio/wasmer/issues/616) Create the import object separately from instance instantiation in `runtime-c-api`
|
||||
- [#620](https://github.com/wasmerio/wasmer/issues/620) Replace one `throw()` with `noexcept` in llvm backend
|
||||
- [#618](https://github.com/wasmerio/wasmer/issues/618) Implement `InternalEvent::Breakpoint` in the llvm backend to allow metering in llvm
|
||||
- [#615](https://github.com/wasmerio/wasmer/issues/615) Eliminate `FunctionEnvironment` construction in `feed_event()` speeding up to 70% of compilation in clif
|
||||
- [#609](https://github.com/wasmerio/wasmer/issues/609) Update dependencies
|
||||
- [#602](https://github.com/wasmerio/wasmer/issues/602) C api extract instance context from instance
|
||||
- [#590](https://github.com/wasmerio/wasmer/issues/590) Error visibility changes in wasmer-c-api
|
||||
- [#589](https://github.com/wasmerio/wasmer/issues/589) Make `wasmer_byte_array` fields `public` in wasmer-c-api
|
||||
|
||||
## 0.6.0 - 2019-07-31
|
||||
- [#603](https://github.com/wasmerio/wasmer/pull/603) Update Wapm-cli, bump version numbers
|
||||
- [#595](https://github.com/wasmerio/wasmer/pull/595) Add unstable public API for interfacing with the WASI file system in plugin-like usecases
|
||||
- [#598](https://github.com/wasmerio/wasmer/pull/598) LLVM Backend is now supported in Windows
|
||||
- [#599](https://github.com/wasmerio/wasmer/pull/599) Fix llvm backend failures in fat spec tests and simd_binaryen spec test.
|
||||
- [#579](https://github.com/wasmerio/wasmer/pull/579) Fix bug in caching with LLVM and Singlepass backends.
|
||||
Add `default-backend-singlepass`, `default-backend-llvm`, and `default-backend-cranelift` features to `wasmer-runtime`
|
||||
to control the `default_compiler()` function (this is a breaking change). Add `compiler_for_backend` function in `wasmer-runtime`
|
||||
- [#561](https://github.com/wasmerio/wasmer/pull/561) Call the `data_finalizer` field on the `Ctx`
|
||||
- [#576](https://github.com/wasmerio/wasmer/pull/576) fix `Drop` of uninit `Ctx`
|
||||
- [#542](https://github.com/wasmerio/wasmer/pull/542) Add SIMD support to Wasmer (LLVM backend only)
|
||||
- Updates LLVM to version 8.0
|
||||
|
||||
## 0.5.7 - 2019-07-23
|
||||
- [#575](https://github.com/wasmerio/wasmer/pull/575) Prepare for release; update wapm to 0.3.6
|
||||
- [#555](https://github.com/wasmerio/wasmer/pull/555) WASI filesystem rewrite. Major improvements
|
||||
- adds virtual root showing all preopened directories
|
||||
- improved sandboxing and code-reuse
|
||||
- symlinks work in a lot more situations
|
||||
- many misc. improvements to most syscalls touching the filesystem
|
||||
|
||||
## 0.5.6 - 2019-07-16
|
||||
- [#565](https://github.com/wasmerio/wasmer/pull/565) Update wapm and bump version to 0.5.6
|
||||
- [#563](https://github.com/wasmerio/wasmer/pull/563) Improve wasi testing infrastructure
|
||||
- fixes arg parsing from comments & fixes the mapdir test to have the native code doing the same thing as the WASI code
|
||||
- makes wasitests-generate output stdout/stderr by default & adds function to print stdout and stderr for a command if it fails
|
||||
- compiles wasm with size optimizations & strips generated wasm with wasm-strip
|
||||
- [#554](https://github.com/wasmerio/wasmer/pull/554) Finish implementation of `wasi::fd_seek`, fix bug in filestat
|
||||
- [#550](https://github.com/wasmerio/wasmer/pull/550) Fix singlepass compilation error with `imul` instruction
|
||||
|
||||
|
||||
## 0.5.5 - 2019-07-10
|
||||
- [#541](https://github.com/wasmerio/wasmer/pull/541) Fix dependency graph by making separate test crates; ABI implementations should not depend on compilers. Add Cranelift fork as git submodule of clif-backend
|
||||
- [#537](https://github.com/wasmerio/wasmer/pull/537) Add hidden flag (`--cache-key`) to use prehashed key into the compiled wasm cache and change compiler backend-specific caching to use directories
|
||||
- [#536](https://github.com/wasmerio/wasmer/pull/536) ~Update cache to use compiler backend name in cache key~
|
||||
|
||||
## 0.5.4 - 2019-07-06
|
||||
- [#529](https://github.com/wasmerio/wasmer/pull/529) Updates the Wasm Interface library, which is used by wapm, with bug fixes and error message improvements
|
||||
|
||||
## 0.5.3 - 2019-07-03
|
||||
- [#523](https://github.com/wasmerio/wasmer/pull/523) Update wapm version to fix bug related to signed packages in the global namespace and locally-stored public keys
|
||||
|
||||
## 0.5.2 - 2019-07-02
|
||||
- [#516](https://github.com/wasmerio/wasmer/pull/516) Add workaround for singlepass miscompilation on GetLocal
|
||||
- [#521](https://github.com/wasmerio/wasmer/pull/521) Update Wapm-cli, bump version numbers
|
||||
- [#518](https://github.com/wasmerio/wasmer/pull/518) Update Cranelift and WasmParser
|
||||
- [#514](https://github.com/wasmerio/wasmer/pull/514) [#519](https://github.com/wasmerio/wasmer/pull/519) Improved Emscripten network related calls, added a null check to `WasmPtr`
|
||||
- [#515](https://github.com/wasmerio/wasmer/pull/515) Improved Emscripten dyncalls
|
||||
- [#513](https://github.com/wasmerio/wasmer/pull/513) Fix emscripten lseek implementation.
|
||||
- [#510](https://github.com/wasmerio/wasmer/pull/510) Simplify construction of floating point constants in LLVM backend. Fix LLVM assertion failure due to definition of %ctx.
|
||||
|
||||
## 0.5.1 - 2019-06-24
|
||||
- [#508](https://github.com/wasmerio/wasmer/pull/508) Update wapm version, includes bug fixes
|
||||
|
||||
## 0.5.0 - 2019-06-17
|
||||
|
||||
- [#471](https://github.com/wasmerio/wasmer/pull/471) Added missing functions to run Python. Improved Emscripten bindings
|
||||
- [#494](https://github.com/wasmerio/wasmer/pull/494) Remove deprecated type aliases from libc in the runtime C API
|
||||
- [#493](https://github.com/wasmerio/wasmer/pull/493) `wasmer_module_instantiate` has better error messages in the runtime C API
|
||||
- [#474](https://github.com/wasmerio/wasmer/pull/474) Set the install name of the dylib to `@rpath`
|
||||
- [#490](https://github.com/wasmerio/wasmer/pull/490) Add MiddlewareChain and StreamingCompiler to runtime
|
||||
- [#487](https://github.com/wasmerio/wasmer/pull/487) Fix stack offset check in singlepass backend
|
||||
- [#450](https://github.com/wasmerio/wasmer/pull/450) Added Metering
|
||||
- [#481](https://github.com/wasmerio/wasmer/pull/481) Added context trampoline into runtime
|
||||
- [#484](https://github.com/wasmerio/wasmer/pull/484) Fix bugs in emscripten socket syscalls
|
||||
- [#476](https://github.com/wasmerio/wasmer/pull/476) Fix bug with wasi::environ_get, fix off by one error in wasi::environ_sizes_get
|
||||
- [#470](https://github.com/wasmerio/wasmer/pull/470) Add mapdir support to Emscripten, implement getdents for Unix
|
||||
- [#467](https://github.com/wasmerio/wasmer/pull/467) `wasmer_instantiate` returns better error messages in the runtime C API
|
||||
- [#463](https://github.com/wasmerio/wasmer/pull/463) Fix bug in WASI path_open allowing one level above preopened dir to be accessed
|
||||
- [#461](https://github.com/wasmerio/wasmer/pull/461) Prevent passing negative lengths in various places in the runtime C API
|
||||
- [#459](https://github.com/wasmerio/wasmer/pull/459) Add monotonic and real time clocks for wasi on windows
|
||||
- [#447](https://github.com/wasmerio/wasmer/pull/447) Add trace macro (`--features trace`) for more verbose debug statements
|
||||
- [#451](https://github.com/wasmerio/wasmer/pull/451) Add `--mapdir=src:dest` flag to rename host directories in the guest context
|
||||
- [#457](https://github.com/wasmerio/wasmer/pull/457) Implement file metadata for WASI, fix bugs in WASI clock code for Unix platforms
|
||||
|
||||
## 0.4.2 - 2019-05-16
|
||||
|
||||
- [#416](https://github.com/wasmerio/wasmer/pull/416) Remote code loading framework
|
||||
- [#449](https://github.com/wasmerio/wasmer/pull/449) Fix bugs: opening host files in filestat and opening with write permissions unconditionally in path_open
|
||||
- [#442](https://github.com/wasmerio/wasmer/pull/442) Misc. WASI FS fixes and implement readdir
|
||||
- [#440](https://github.com/wasmerio/wasmer/pull/440) Fix type mismatch between `wasmer_instance_call` and `wasmer_export_func_*_arity` functions in the runtime C API.
|
||||
- [#269](https://github.com/wasmerio/wasmer/pull/269) Add better runtime docs
|
||||
- [#432](https://github.com/wasmerio/wasmer/pull/432) Fix returned value of `wasmer_last_error_message` in the runtime C API
|
||||
- [#429](https://github.com/wasmerio/wasmer/pull/429) Get wasi::path_filestat_get working for some programs; misc. minor WASI FS improvements
|
||||
- [#413](https://github.com/wasmerio/wasmer/pull/413) Update LLVM backend to use new parser codegen traits
|
||||
|
||||
## 0.4.1 - 2019-05-06
|
||||
|
||||
- [#426](https://github.com/wasmerio/wasmer/pull/426) Update wapm-cli submodule, bump version to 0.4.1
|
||||
- [#422](https://github.com/wasmerio/wasmer/pull/422) Improved Emscripten functions to run optipng and pngquant compiled to wasm
|
||||
- [#409](https://github.com/wasmerio/wasmer/pull/409) Improved Emscripten functions to run JavascriptCore compiled to wasm
|
||||
- [#399](https://github.com/wasmerio/wasmer/pull/399) Add example of using a plugin extended from WASI
|
||||
- [#397](https://github.com/wasmerio/wasmer/pull/397) Fix WASI fs abstraction to work on Windows
|
||||
- [#390](https://github.com/wasmerio/wasmer/pull/390) Pin released wapm version and add it as a git submodule
|
||||
- [#408](https://github.com/wasmerio/wasmer/pull/408) Add images to windows installer and update installer to add wapm bin directory to path
|
||||
|
||||
## 0.4.0 - 2019-04-23
|
||||
|
||||
- [#383](https://github.com/wasmerio/wasmer/pull/383) Hook up wasi exit code to wasmer cli.
|
||||
- [#382](https://github.com/wasmerio/wasmer/pull/382) Improve error message on `--backend` flag to only suggest currently enabled backends
|
||||
- [#381](https://github.com/wasmerio/wasmer/pull/381) Allow retrieving propagated user errors.
|
||||
- [#379](https://github.com/wasmerio/wasmer/pull/379) Fix small return types from imported functions.
|
||||
- [#371](https://github.com/wasmerio/wasmer/pull/371) Add more Debug impl for WASI types
|
||||
- [#368](https://github.com/wasmerio/wasmer/pull/368) Fix issue with write buffering
|
||||
- [#343](https://github.com/wasmerio/wasmer/pull/343) Implement preopened files for WASI and fix aligment issue when accessing WASI memory
|
||||
- [#367](https://github.com/wasmerio/wasmer/pull/367) Add caching support to the LLVM backend.
|
||||
- [#366](https://github.com/wasmerio/wasmer/pull/366) Remove `UserTrapper` trait to fix [#365](https://github.com/wasmerio/wasmer/issues/365).
|
||||
- [#348](https://github.com/wasmerio/wasmer/pull/348) Refactor internal runtime ↔️ backend abstraction.
|
||||
- [#355](https://github.com/wasmerio/wasmer/pull/355) Misc changes to `Cargo.toml`s for publishing
|
||||
- [#352](https://github.com/wasmerio/wasmer/pull/352) Bump version numbers to 0.3.0
|
||||
- [#351](https://github.com/wasmerio/wasmer/pull/351) Add hidden option to specify wasm program name (can be used to improve error messages)
|
||||
- [#350](https://github.com/wasmerio/wasmer/pull/350) Enforce that CHANGELOG.md is updated through CI.
|
||||
- [#349](https://github.com/wasmerio/wasmer/pull/349) Add [CHANGELOG.md](https://github.com/wasmerio/wasmer/blob/master/CHANGELOG.md).
|
||||
|
||||
## 0.3.0 - 2019-04-12
|
||||
|
||||
- [#276](https://github.com/wasmerio/wasmer/pull/276) [#288](https://github.com/wasmerio/wasmer/pull/288) [#344](https://github.com/wasmerio/wasmer/pull/344) Use new singlepass backend (with the `--backend=singlepass` when running Wasmer)
|
||||
- [#338](https://github.com/wasmerio/wasmer/pull/338) Actually catch traps/panics/etc when using a typed func.
|
||||
- [#325](https://github.com/wasmerio/wasmer/pull/325) Fixed func_index in debug mode
|
||||
- [#323](https://github.com/wasmerio/wasmer/pull/323) Add validate subcommand to validate Wasm files
|
||||
- [#321](https://github.com/wasmerio/wasmer/pull/321) Upgrade to Cranelift 0.3.0
|
||||
- [#319](https://github.com/wasmerio/wasmer/pull/319) Add Export and GlobalDescriptor to Runtime API
|
||||
- [#310](https://github.com/wasmerio/wasmer/pull/310) Cleanup warnings
|
||||
- [#299](https://github.com/wasmerio/wasmer/pull/299) [#300](https://github.com/wasmerio/wasmer/pull/300) [#301](https://github.com/wasmerio/wasmer/pull/301) [#303](https://github.com/wasmerio/wasmer/pull/303) [#304](https://github.com/wasmerio/wasmer/pull/304) [#305](https://github.com/wasmerio/wasmer/pull/305) [#306](https://github.com/wasmerio/wasmer/pull/306) [#307](https://github.com/wasmerio/wasmer/pull/307) Add support for WASI 🎉
|
||||
- [#286](https://github.com/wasmerio/wasmer/pull/286) Add extend to imports
|
||||
- [#278](https://github.com/wasmerio/wasmer/pull/278) Add versioning to cache
|
||||
- [#250](https://github.com/wasmerio/wasmer/pull/250) Setup bors
|
33
CONTRIBUTING.md
Normal file
@ -0,0 +1,33 @@
|
||||
# How to Contribute to Wasmer
|
||||
|
||||
Thank you for your interest in contributing to Wasmer. This document outlines some recommendations on how to contribute.
|
||||
|
||||
## Issues & Feature Requests
|
||||
|
||||
Please use the issue template and provide a failing example if possible to help us recreate the issue.
|
||||
|
||||
## Pull Requests
|
||||
|
||||
For large changes, please try reaching communicating with the Wasmer maintainers via GitHub Issues or Spectrum Chat to ensure we can accept the change once it is ready.
|
||||
|
||||
We recommend trying the following commands before sending a pull request to ensure code quality:
|
||||
|
||||
- `cargo fmt --all` Ensures all code is correctly formatted.
|
||||
- Run `cargo test` in the crates that you are modifying.
|
||||
- Run `cargo build --all` (nightly) or `cargo build --all --exclude wasmer-singlepass-backend`
|
||||
|
||||
A comprehensive CI test suite will be run by a Wasmer team member after the PR has been created.
|
||||
|
||||
### Common Build Issues
|
||||
|
||||
#### LLVM Dependency
|
||||
|
||||
`Didn't find usable system-wide LLVM`
|
||||
|
||||
Building Wasmer with the LLVM backend requires LLVM to be installed
|
||||
|
||||
#### Singlepass Nightly Only
|
||||
|
||||
`error[E0554]: #![feature] may not be used on the stable release channel`
|
||||
|
||||
Building Wasmer with the singlepass backend requires the nightly version of Rust
|
1179
Cargo.lock
generated
Normal file
59
Cargo.toml
Normal file
@ -0,0 +1,59 @@
|
||||
[package]
|
||||
name = "wasmer-bin"
|
||||
version = "1.0.0"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
description = "High-Performance WebAssembly Framework"
|
||||
license = "MIT"
|
||||
include = [
|
||||
"src/**/*",
|
||||
"Cargo.lock",
|
||||
"Cargo.toml",
|
||||
"LICENSE",
|
||||
"Makefile",
|
||||
"/README.md"
|
||||
]
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
default-run = "wasmer"
|
||||
publish = false
|
||||
autoexamples = false
|
||||
|
||||
[dependencies]
|
||||
wasmer = { path = "lib/api" }
|
||||
wasmer-compiler = { path = "lib/compiler" }
|
||||
wasmer-compiler-cranelift = { path = "lib/compiler-cranelift", optional = true }
|
||||
wasmer-jit = { path = "lib/jit" }
|
||||
atty = "0.2"
|
||||
anyhow = "1.0.28"
|
||||
structopt = { version = "0.3.13", features = ["suggestions"] }
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
]
|
||||
|
||||
[build-dependencies]
|
||||
test-generator = { path = "tests/lib/test-generator" }
|
||||
anyhow = "1.0.28"
|
||||
glob = "0.3"
|
||||
rustc_version = "0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.28"
|
||||
test-utils = { path = "tests/lib/test-utils" }
|
||||
wasmer = { path = "lib/api" }
|
||||
wasmer-compiler = { path = "lib/compiler" }
|
||||
wasmer-compiler-cranelift = { path = "lib/compiler-cranelift" }
|
||||
wasmer-jit = { path = "lib/jit" }
|
||||
wasmer-wast = { path = "tests/lib/wast" }
|
||||
|
||||
[features]
|
||||
# Don't add the backend features in default, please add them on the Makefile
|
||||
# since we might want to autoconfigure them depending on the availability on the host.
|
||||
# default = ["wasi"]
|
||||
default = ["compiler-cranelift"]
|
||||
compiler-cranelift = [
|
||||
"wasmer-compiler-cranelift",
|
||||
]
|
||||
# wasi = ["wasmer-wasi"]
|
||||
# experimental-io-devices = ["wasmer-wasi-experimental-io-devices"]
|
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019-present Wasmer, Inc. and its affiliates.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
2
Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
tests-spec-update-testuite:
|
||||
git subtree pull --prefix tests/wast/spec https://github.com/WebAssembly/testsuite.git master --squash
|
232
README.md
@ -1,22 +1,27 @@
|
||||
|
||||
<div align="center">
|
||||
<a href="https://docs.wasmer.io/ecosystem/wasienv" target="_blank" rel="noopener noreferrer">
|
||||
<img width="240" src="https://raw.githubusercontent.com/wasienv/wasienv/master/logo.png" alt="Wasmer logo">
|
||||
<a href="https://wasmer.io" target="_blank" rel="noopener noreferrer">
|
||||
<img width="300" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/logo.png" alt="Wasmer logo">
|
||||
</a>
|
||||
|
||||
<p>
|
||||
<a href="https://github.com/wasienv/wasienv/actions?workflow=CI">
|
||||
<img src="https://github.com/wasienv/wasienv/workflows/CI/badge.svg?style=flat-square" alt="Tests">
|
||||
<a href="https://dev.azure.com/wasmerio/wasmer/_build/latest?definitionId=3&branchName=master">
|
||||
<img src="https://img.shields.io/azure-devops/build/wasmerio/wasmer/3.svg?style=flat-square" alt="Build Status">
|
||||
</a>
|
||||
<a href="https://slack.wasmer.io">
|
||||
<img src="https://img.shields.io/static/v1?label=Slack&message=join%20chat&color=brighgreen&style=flat-square" alt="Slack channel">
|
||||
</a>
|
||||
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/wasienv/wasienv.svg?style=flat-square" alt="License">
|
||||
<img src="https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square" alt="License">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h3>
|
||||
<a href="https://docs.wasmer.io/ecosystem/wasienv">Wasienv Docs</a>
|
||||
<a href="https://wasmer.io/">Website</a>
|
||||
<span> • </span>
|
||||
<a href="https://docs.wasmer.io">Docs</a>
|
||||
<span> • </span>
|
||||
<a href="https://medium.com/wasmer/">Blog</a>
|
||||
<span> • </span>
|
||||
<a href="https://slack.wasmer.io/">Slack</a>
|
||||
<span> • </span>
|
||||
@ -27,188 +32,97 @@
|
||||
|
||||
<br />
|
||||
|
||||
Wasienv is a tool that aims to bring all programming languages to [WebAssembly WASI](https://github.com/WebAssembly/WASI). With `wasienv` you can compile:
|
||||
[Wasmer](https://wasmer.io/) is a standalone [WebAssembly](https://webassembly.org/) runtime:
|
||||
* **Universal**: Wasmer is available in *Linux, macOS and Windows* (for both Desktop and [ARM](https://medium.com/wasmer/running-webassembly-on-arm-7d365ed0e50c))
|
||||
* **Fast**: Wasmer aims to run WebAssembly at near-native speed
|
||||
* **Pluggable**: Wasmer can be used from almost **any programming language**
|
||||
* **Safe**: supporting [WASI](https://github.com/WebAssembly/WASI) and [Emscripten](https://emscripten.org/)
|
||||
|
||||
* C/C++ projects to Wasm + WASI ([see usage example](https://docs.wasmer.io/ecosystem/wasienv/compile-c-c++-to-wasm-wasi))
|
||||
* Swift to Wasm + WASI ([see usage example](https://docs.wasmer.io/ecosystem/wasienv/compile-swift-to-wasm-wasi))
|
||||
It is used to run software fast, universally and safely: standalone applications and universal libraries.
|
||||
|
||||
So you can run them anywhere (with any [Standalone WASI WebAssembly runtime](https://wasmer.io), or [in the Browser](https://webassembly.sh)).
|
||||
## Contents
|
||||
|
||||
> Note: If you aim to use the WebAssembly files in the web directly (using graphics, audio or other tools that are not supported in WASI) then [Emscripten](https://emscripten.org/) is probably a much better choice.
|
||||
- [Quickstart](#quickstart)
|
||||
- [Language Integrations](#language-integrations)
|
||||
- [Contribute](#contribute)
|
||||
- [Community](#community)
|
||||
|
||||
## Install
|
||||
## Quickstart
|
||||
|
||||
You can install `wasienv` with:
|
||||
Get started with Wasmer:
|
||||
|
||||
#### 1. Install Wasmer
|
||||
|
||||
```sh
|
||||
curl https://raw.githubusercontent.com/wasienv/wasienv/master/install.sh | sh
|
||||
curl https://get.wasmer.io -sSfL | sh
|
||||
```
|
||||
> Note: *Wasmer is also [available on Windows](https://github.com/wasmerio/wasmer/releases)*
|
||||
|
||||
> Note: we also ship `wasienv` in a Docker image. You can check [how to use the Wasienv Docker image here](https://github.com/wasienv/wasienv/blob/master/docker/).
|
||||
|
||||
## Using wasienv for C projects
|
||||
|
||||
If you want to compile a C file to a WebAssembly WASI:
|
||||
<details>
|
||||
<summary><b>Alternative</b>: Install with Homebrew</summary>
|
||||
<p>
|
||||
|
||||
```sh
|
||||
# To compile to a WebAssembly WASI file
|
||||
# This command will generate:
|
||||
# • An executable: ./example
|
||||
# • A WebAssembly file: ./example.wasm
|
||||
wasicc examples/example.c -o example
|
||||
|
||||
# If you are using configure
|
||||
wasiconfigure ./configure
|
||||
|
||||
# If you are using cmake (or make)
|
||||
wasimake cmake .
|
||||
brew install wasmer
|
||||
```
|
||||
|
||||
If you want to compile a C file to plain WebAssembly:
|
||||
</p>
|
||||
</details>
|
||||
|
||||
```sh
|
||||
# To compile to a WebAssembly file
|
||||
# This command will generate:
|
||||
# • An executable: ./example
|
||||
# • A WebAssembly file: ./example.wasm
|
||||
wasmcc examples/example.c -o example
|
||||
#### 2. Use Wasmer
|
||||
|
||||
Download a WASM file, and use it universally! You can start with QuickJS: [qjs.wasm](https://registry-cdn.wapm.io/contents/_/quickjs/0.0.3/build/qjs.wasm)
|
||||
|
||||
```bash
|
||||
wasmer qjs.wasm
|
||||
```
|
||||
|
||||
## Commands
|
||||
#### 3. Next steps
|
||||
|
||||
When installing `wasienv`, the following commands will be automatically available:
|
||||
Here is what you can do next:
|
||||
|
||||
### `wasienv`
|
||||
- [Use Wasmer from your Rust application](https://docs.wasmer.io/integrations/rust)
|
||||
- [Publish a Wasm package on WAPM](https://docs.wasmer.io/ecosystem/wapm/publishing-your-package)
|
||||
- [Read more about Wasmer](https://medium.com/wasmer/)
|
||||
|
||||
This is the compiler toolchain. You have two commands available:
|
||||
|
||||
For installing a SDK (`wasienv install-sdk`):
|
||||
### Language Integrations
|
||||
|
||||
```sh
|
||||
wasienv install-sdk 7
|
||||
```
|
||||
Wasmer runtime can be used as a library embedded in different languages, so you can **use WebAssembly anywhere** 🎉
|
||||
|
||||
For setting a SDK as the default (`wasienv default-sdk`):
|
||||
| | Language | Docs | Author(s) | Maintenance | Release | Stars |
|
||||
|-|-|-|-|-|-|-|
|
||||
|  | [**Rust**](https://github.com/wasmerio/wasmer-rust-example) | [Docs](https://wasmerio.github.io/wasmer/crates/wasmer_runtime/) | Wasmer | actively developed | <a href="https://crates.io/crates/wasmer-runtime/" target="_blank"></a> |  |
|
||||
|  | [**C/C++**](https://github.com/wasmerio/wasmer-c-api) | [Docs](https://wasmerio.github.io/wasmer/c/runtime-c-api/) | Wasmer | actively developed | <a href="https://github.com/wasmerio/wasmer-c-api/" target="_blank"></a> |  |
|
||||
|  | [**Python**](https://github.com/wasmerio/python-ext-wasm) | [Docs](https://github.com/wasmerio/python-ext-wasm#api-of-the-wasmer-extensionmodule) | Wasmer | actively developed | <a href="https://pypi.org/project/wasmer/" target="_blank"></a> |  |
|
||||
|  | [**Go**](https://github.com/wasmerio/go-ext-wasm) | [Docs](https://github.com/wasmerio/go-ext-wasm#basic-example-exported-function) | Wasmer | actively developed | <a href="https://github.com/wasmerio/go-ext-wasm" target="_blank"></a> |  |
|
||||
|  | [**PHP**](https://github.com/wasmerio/php-ext-wasm) | [Docs](https://wasmerio.github.io/php-ext-wasm/wasm/) | Wasmer | actively developed | <a href="https://pecl.php.net/package/wasm" target="_blank"></a> |  |
|
||||
|  | [**Ruby**](https://github.com/wasmerio/ruby-ext-wasm) | [Docs](https://www.rubydoc.info/gems/wasmer/) | Wasmer | actively developed | <a href="https://rubygems.org/gems/wasmer" target="_blank"></a> |  |
|
||||
|  | [**Postgres**](https://github.com/wasmerio/postgres-ext-wasm) | | Wasmer | actively developed | <a href="https://github.com/wasmerio/postgres-ext-wasm" target="_blank"></a> |  |
|
||||
|  | [**JavaScript**](https://github.com/wasmerio/wasmer-js) | [Docs](https://docs.wasmer.io/wasmer-js/wasmer-js) | Wasmer | actively developed | <a href="https://www.npmjs.com/package/@wasmer/wasi" target="_blank"></a> |  |
|
||||
|  | [**C#/.Net**](https://github.com/migueldeicaza/WasmerSharp) | [Docs](https://migueldeicaza.github.io/WasmerSharp/) |[Miguel de Icaza](https://github.com/migueldeicaza) | actively developed | <a href="https://www.nuget.org/packages/WasmerSharp/" target="_blank"></a> |  |
|
||||
|  | [**R**](https://github.com/dirkschumacher/wasmr) | [Docs](https://github.com/dirkschumacher/wasmr#example) | [Dirk Schumacher](https://github.com/dirkschumacher) | actively developed | |  |
|
||||
|  | [**Elixir**](https://github.com/tessi/wasmex) | [Docs](https://hexdocs.pm/wasmex/api-reference.html) | [Philipp Tessenow](https://github.com/tessi) | actively developed | <a href="https://hex.pm/packages/wasmex" target="_blank"></a> |  |
|
||||
| ❓ | [your language is missing?](https://github.com/wasmerio/wasmer/issues/new?assignees=&labels=%F0%9F%8E%89+enhancement&template=---feature-request.md&title=) | | | | |
|
||||
|
||||
```sh
|
||||
wasienv default-sdk 7
|
||||
```
|
||||
|
||||
### `wasicc`
|
||||
## Contribute
|
||||
|
||||
It's a wrapper on top of `clang`, with additions for the stubs, sysroot and target.
|
||||
It also detects autoexecutables in the output and wraps to execute them with a WebAssembly WASI runtime via `wasirun`.
|
||||
**We welcome any form of contribution, especially from new members of our community** 💜
|
||||
|
||||
### `wasic++`
|
||||
You can check how to build the Wasmer runtime in [our awesome docs](https://docs.wasmer.io/ecosystem/wasmer/building-from-source)!
|
||||
|
||||
It's a wrapper on top of `clang++`, with additions for the stubs, sysroot and target.
|
||||
It also detects autoexecutables in the output and wraps to execute them with a WebAssembly WASI runtime via `wasirun`.
|
||||
### Testing
|
||||
|
||||
### `wasmcc`
|
||||
Test you want? The [Wasmer docs will show you how](https://docs.wasmer.io/ecosystem/wasmer/building-from-source/testing).
|
||||
|
||||
It's a wrapper on top of `clang`, with additions for preconfiguring the wasm linker, target, etc...
|
||||
## Community
|
||||
|
||||
### `wasmc++`
|
||||
Wasmer has an amazing community developers and contributors. Welcome, please join us! 👋
|
||||
|
||||
It's a wrapper on top of `clang++`, with additions for preconfiguring the wasm linker, target, etc...
|
||||
### Channels
|
||||
|
||||
### `wasiconfigure`
|
||||
|
||||
It's a helper that adds the wasienv environment vars (`CC`, `CXX`, `RUNLIB`, ...) to the following command (`./configure`).
|
||||
|
||||
Example:
|
||||
|
||||
```sh
|
||||
wasiconfigure ./configure
|
||||
```
|
||||
|
||||
### `wasimake`
|
||||
|
||||
It's a helper that adds the wasienv environment vars (`CC`, `CXX`, `RUNLIB`, ...) for the make (`make` or `cmake`).
|
||||
|
||||
Example:
|
||||
|
||||
```sh
|
||||
# With CMake
|
||||
wasimake cmake .
|
||||
|
||||
# With Make
|
||||
wasimake make
|
||||
```
|
||||
|
||||
### `wasirun`
|
||||
|
||||
It executes a given WebAssembly file with a standalone WebAssembly runtime.
|
||||
|
||||
```sh
|
||||
wasirun myfile.wasm
|
||||
```
|
||||
|
||||
## Using wasienv for Swift projects
|
||||
|
||||
If you want to compile a Swift file to a WebAssembly WASI, you
|
||||
will need to first install the Wasienv Swift integration:
|
||||
|
||||
```sh
|
||||
wasienv install-swift
|
||||
```
|
||||
|
||||
Once the integration is installed, you can start compiling Swift files
|
||||
to WebAssembly!
|
||||
|
||||
### `wasiswiftc`
|
||||
|
||||
It compiles your Swift files into WebAssembly.
|
||||
|
||||
```sh
|
||||
wasiswiftc example.swift -o example.wasm
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
After cloning this repo, ensure dependencies are installed by running:
|
||||
|
||||
```sh
|
||||
make install-dev
|
||||
```
|
||||
|
||||
After that, all the commands will be available on your shell and you should be able to start seeing the changes directly without re-installing wasienv.
|
||||
|
||||
## Testing
|
||||
|
||||
After running `make install-dev` you can run directly:
|
||||
|
||||
```sh
|
||||
make test
|
||||
```
|
||||
|
||||
## How wasienv compares to …?
|
||||
|
||||
### Emscripten
|
||||
|
||||
[Emscripten](https://emscripten.org/) is a great toolchain that let's you compile your C/C++ projects to WebAssembly so you can use them in the web easily.
|
||||
|
||||
However, Emscripten has a **non-stable ABI** (because constant and fast iteration is very useful for their usecase).
|
||||
This makes it a bit challening for standalone-runtimes to continually adapt.
|
||||
Because of that, adopting the WASI ABI is a much easier path for standalone server-side WebAssembly runtimes.
|
||||
|
||||
Right now Emscripten is [moving towards WASI adoption](https://github.com/emscripten-core/emscripten/issues/9479).
|
||||
However, Emscripten can only emit WASI WebAssembly files for some programs as Emscripten's filesystem layer supports POSIX features not yet present in WASI.
|
||||
|
||||
Emscripten has also some tools that are not needed (nor supported) in the case of server-side Standalone WebAssembly runtimes, such as [`EM_JS` and `EM_ASM`](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#calling-javascript-from-c-c).
|
||||
|
||||
Wasienv learns a lot from Emscripten, since they figured out the perfect ergonomics for having C/C++ projects to adopt WebAssembly. Alon, the creator of Emscripten, is without any doubt one of the brilliant minds behind WebAssembly and he inspired us with his work to keep improving the ergonomics of WASI.
|
||||
|
||||
### WASI-libc
|
||||
|
||||
WASI-libc is the "frontend ABI" for WASI. By itself, it only provides header files and implementations that make C compilers adopt WASI very easily via the `--sysroot` flag.
|
||||
|
||||
### WASI-SDK
|
||||
|
||||
We can see WASI-SDK as the union between `WASI-libc` and the compiler binaries `clang`, `wasm-ld`, ...
|
||||
|
||||
Wasienv is using WASI-SDK under the hood to compile to WebAssembly, however it differs from it in two major ways:
|
||||
1. `wasienv` is designed to work with **multiple SDKs** versions
|
||||
2. `wasienv` is completely focused on the **ergonomics**, exposing very simple to use CLI tools so that projects can adopt it easily.
|
||||
|
||||
We can think of `wasienv` as applying the ergonomic ideas from Emscripten to the WASI-SDK
|
||||
- [Slack](https://slack.wasmer.io/)
|
||||
- [Twitter](https://twitter.com/wasmerio)
|
||||
- [Facebook](https://www.facebook.com/wasmerio)
|
||||
- [Email](mailto:hello@wasmer.io)
|
||||
|
19
SECURITY.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
While in beta, the latest published version of `wasmer` (`0.x`) will be supported with security updates.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 0.x | :white_check_mark: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
The Wasmer team and community take security bugs in Wasmer seriously.
|
||||
We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
|
||||
|
||||
To report a security issue, email security@wasmer.io and include the word "SECURITY" in the subject line.
|
||||
|
||||
The Wasmer team will send a response indicating the next steps in handling your report.
|
||||
After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.
|
1
assets/languages/c.svg
Executable file
@ -0,0 +1 @@
|
||||
<svg width="20" height="22" id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 22"><defs><style>.cls-1{fill:#7f8b99;}.cls-2{fill:#a9b9cb;}.cls-3{fill:#fff;}</style></defs><title>c-logo-colored</title><path class="cls-1" d="M1,17.22l2.19,1.24L16.79,3.54,11,.26a2,2,0,0,0-2,0L1,4.78A2,2,0,0,0,0,6.52v9A2,2,0,0,0,1,17.22Z"/><path class="cls-2" d="M19,4.78,16.79,3.54,3.21,18.46,9,21.74a2,2,0,0,0,2,0l8-4.52a2,2,0,0,0,1-1.74v-9A2,2,0,0,0,19,4.78Z"/><path class="cls-3" d="M12.07,9.58l3.11,0A4.84,4.84,0,0,0,10.1,5.25,5.42,5.42,0,0,0,4.48,11a5.31,5.31,0,0,0,5.62,5.66c4,0,4.94-2.86,4.94-4.38h-3a1.73,1.73,0,0,1-2,1.75c-1.91,0-2.22-2.27-2.22-3,0-1.18.42-3,2.22-3A1.86,1.86,0,0,1,12.07,9.58Z"/></svg>
|
After Width: | Height: | Size: 726 B |
1
assets/languages/cpp.svg
Executable file
@ -0,0 +1 @@
|
||||
<svg width="20" height="22" id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 22"><defs><style>.cls-1{fill:#659ad2;}.cls-2{fill:#0e4580;}.cls-3{fill:#035a9d;}.cls-4{fill:#fff;}</style></defs><title>c++-logo-colored</title><path class="cls-1" d="M19.72,5.5A2,2,0,0,0,19,4.77L11,.25a2,2,0,0,0-2,0L1,4.77A2,2,0,0,0,0,6.52v9a2,2,0,0,0,.28,1Z"/><path class="cls-2" d="M.28,16.5a2,2,0,0,0,.74.73l8,4.52a2,2,0,0,0,2,0l8-4.52a2,2,0,0,0,.74-.73L10,11Z"/><path class="cls-3" d="M20,6.52a2,2,0,0,0-.28-1L10,11l9.72,5.5a2,2,0,0,0,.28-1Z"/><path class="cls-4" d="M16.67,10.69h-.74V10h-.75v.73h-.74v.73h.74v.72h.75v-.72h.74Z"/><path class="cls-4" d="M19.44,10.69H18.7V10H18v.73h-.74v.73H18v.72h.74v-.72h.74Z"/><path class="cls-4" d="M12.86,12.62a3.3,3.3,0,1,1,0-3.24l2.8-1.58a6.5,6.5,0,1,0,0,6.4Z"/></svg>
|
After Width: | Height: | Size: 825 B |
1
assets/languages/csharp.svg
Executable file
@ -0,0 +1 @@
|
||||
<svg width="20" height="20" id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><defs><style>.cls-1{fill:#0c9438;}.cls-2{fill:#219e38;opacity:0.85;}.cls-3{opacity:0.1;isolation:isolate;}.cls-4{fill:#fff;}</style></defs><title>csharp-logo-colored</title><path class="cls-1" d="M17.07,2.93a10,10,0,0,0-14.14,0L17.07,17.07A10,10,0,0,0,17.07,2.93Z"/><path class="cls-2" d="M2.93,2.93A10,10,0,0,0,17.07,17.07Z"/><path class="cls-3" d="M1,10.23a.76.76,0,0,1,0-.15c0,.15,0,.3,0,.45Z"/><path class="cls-4" d="M9.51,13.64a4.6,4.6,0,0,1-2.19.46,3.46,3.46,0,0,1-1.46-.25A3.66,3.66,0,0,1,4.63,13a4.05,4.05,0,0,1-1-2.84,4.34,4.34,0,0,1,.26-1.64,4.2,4.2,0,0,1,.88-1.41,4,4,0,0,1,1.32-.94A3.81,3.81,0,0,1,7.65,6a4.42,4.42,0,0,1,1.84.25v1A3.72,3.72,0,0,0,7.64,6.8,2.9,2.9,0,0,0,6.44,7a2.81,2.81,0,0,0-1,.69,3.4,3.4,0,0,0-.85,2.42,3.24,3.24,0,0,0,.79,2.29,2.68,2.68,0,0,0,.94.66,2.9,2.9,0,0,0,1.13.2,4,4,0,0,0,2.05-.53Z"/><path class="cls-4" d="M16.49,8.3l-.13.58H14.93l-.35,1.65h1.55l-.15.58H14.47L14,13.3h-.69l.47-2.19H12.39l-.46,2.19h-.68l.46-2.19H10.26l.11-.58h1.46l.33-1.65H10.64l.12-.58h1.52l.46-2.21h.69L13,8.3l1.38,0,.47-2.17h.66L15.05,8.3Zm-2.24.58H12.86l-.36,1.65h1.4Z"/></svg>
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/languages/elixir.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
1
assets/languages/go.svg
Executable file
@ -0,0 +1 @@
|
||||
<svg width="32" height="12" id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 12"><defs><style>.cls-1{fill:#38b6ac;}</style></defs><title>go-logo-colored</title><path class="cls-1" d="M2.4,3.62s-.07,0,0-.09l.33-.42A.25.25,0,0,1,2.86,3H8.43c.05,0,.08,0,0,.09l-.25.4a.24.24,0,0,1-.17.09Z"/><path class="cls-1" d="M.06,5.06C0,5.06,0,5,0,5l.32-.42a.3.3,0,0,1,.17-.08H7.62a.08.08,0,0,1,.08.1L7.57,5a.15.15,0,0,1-.14.1Z"/><path class="cls-1" d="M3.84,6.49c-.06,0-.09,0-.06-.09L4,6a.2.2,0,0,1,.16-.09H7.28A.09.09,0,0,1,7.37,6l0,.37a.13.13,0,0,1-.12.11Z"/><path class="cls-1" d="M31.94,4.55a4.86,4.86,0,0,0-2.2-3.49A5.72,5.72,0,0,0,25.31.25,6.56,6.56,0,0,0,21,2.87a6.74,6.74,0,0,0-1.08,2.05h-5a.43.43,0,0,0-.38.25c-.22.41-.6,1.24-.8,1.71-.11.27,0,.47.29.47h3c-.16.22-.28.41-.43.58a2.88,2.88,0,0,1-2.65,1A2.45,2.45,0,0,1,11.8,6.49a3.36,3.36,0,0,1,1.6-3,2.69,2.69,0,0,1,2.81-.23,2,2,0,0,1,.73.6c.2.23.22.21.45.15,1-.25,1.64-.45,2.62-.69.18,0,.24-.12.18-.25A5,5,0,0,0,18.39.87,5.48,5.48,0,0,0,14.26.11,6.88,6.88,0,0,0,9.9,2.82,6.12,6.12,0,0,0,8.58,7.41a4.93,4.93,0,0,0,2,3.47,5.49,5.49,0,0,0,4.18,1,6.45,6.45,0,0,0,4.44-2.66,6.88,6.88,0,0,0,.64-1.07,5,5,0,0,0,1.54,2.43A5.77,5.77,0,0,0,25.16,12c.41-.05.78-.06,1.19-.14a7.21,7.21,0,0,0,3.79-2A6.07,6.07,0,0,0,31.94,4.55Zm-5,4.1a2.83,2.83,0,0,1-2.55.09,2.58,2.58,0,0,1-1.42-2.95,3.37,3.37,0,0,1,2.72-2.73,2.58,2.58,0,0,1,3.18,2.09c0,.16,0,.32.05.52A3.44,3.44,0,0,1,26.9,8.65Z"/></svg>
|
After Width: | Height: | Size: 1.4 KiB |
8
assets/languages/js.svg
Executable file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<svg width="20" height="20" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||
<g>
|
||||
<path d="M0,0 L256,0 L256,256 L0,256 L0,0 Z" fill="#F7DF1E"></path>
|
||||
<path d="M67.311746,213.932292 L86.902654,202.076241 C90.6821079,208.777346 94.1202286,214.447137 102.367086,214.447137 C110.272203,214.447137 115.256076,211.354819 115.256076,199.326883 L115.256076,117.528787 L139.313575,117.528787 L139.313575,199.666997 C139.313575,224.58433 124.707759,235.925943 103.3984,235.925943 C84.1532952,235.925943 72.9819429,225.958603 67.3113397,213.93026" fill="#000000"></path>
|
||||
<path d="M152.380952,211.354413 L171.969422,200.0128 C177.125994,208.433981 183.827911,214.619835 195.684368,214.619835 C205.652521,214.619835 212.009041,209.635962 212.009041,202.762159 C212.009041,194.513676 205.479416,191.592025 194.481168,186.78207 L188.468419,184.202565 C171.111213,176.81473 159.597308,167.53534 159.597308,147.944838 C159.597308,129.901308 173.344508,116.153295 194.825752,116.153295 C210.119924,116.153295 221.117765,121.48094 229.021663,135.400432 L210.29059,147.428775 C206.166146,140.040127 201.699556,137.119289 194.826159,137.119289 C187.78047,137.119289 183.312254,141.587098 183.312254,147.428775 C183.312254,154.646349 187.78047,157.568406 198.089956,162.036622 L204.103924,164.614095 C224.553448,173.378641 236.067352,182.313448 236.067352,202.418387 C236.067352,224.071924 219.055137,235.927975 196.200432,235.927975 C173.860978,235.927975 159.425829,225.274311 152.381359,211.354413" fill="#000000"></path>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
1
assets/languages/php.svg
Executable file
@ -0,0 +1 @@
|
||||
<svg width="27" height="14" id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 14"><title>php-logo</title><path d="M2.14,2.94h4A3.05,3.05,0,0,1,8.69,4a3.59,3.59,0,0,1,.53,2.82,5.59,5.59,0,0,1-.47,1.61,4.6,4.6,0,0,1-1,1.42,3.31,3.31,0,0,1-1.6,1,6.9,6.9,0,0,1-1.76.22H2.64L2.08,14H0ZM3.88,4.7,3,9.33h.39a7.55,7.55,0,0,0,2.39-.29c.63-.22,1.06-1,1.28-2.25.18-1.08,0-1.7-.54-1.87a6.6,6.6,0,0,0-2-.23l-.35,0H3.87Z"/><path d="M11.59,0h2.06l-.59,2.94h1.86a3.72,3.72,0,0,1,2.27.65c.51.41.66,1.19.45,2.35l-1,5.13h-2.1l1-4.9a1.44,1.44,0,0,0-.09-1.1,1.26,1.26,0,0,0-1-.32l-1.66,0L11.5,11.07H9.43Z"/><path d="M19.85,2.94h4A3.08,3.08,0,0,1,26.41,4a3.59,3.59,0,0,1,.52,2.82,5.56,5.56,0,0,1-.46,1.61,4.81,4.81,0,0,1-1,1.42,3.31,3.31,0,0,1-1.6,1,6.79,6.79,0,0,1-1.76.22H20.36L19.79,14H17.71ZM21.6,4.7l-.9,4.63h.39a7.51,7.51,0,0,0,2.39-.29q1-.33,1.29-2.25c.17-1.08,0-1.7-.54-1.87a6.67,6.67,0,0,0-2-.23l-.34,0h-.32Z"/></svg>
|
After Width: | Height: | Size: 938 B |
22
assets/languages/postgres.svg
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<svg width="21px" height="22px" viewBox="0 0 432.071 445.383" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="orginal" style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||
</g>
|
||||
<g id="Layer_x0020_3" style="fill-rule:nonzero;clip-rule:nonzero;fill:none;stroke:#FFFFFF;stroke-width:12.4651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;">
|
||||
<path style="fill:#000000;stroke:#000000;stroke-width:37.3953;stroke-linecap:butt;stroke-linejoin:miter;" d="M323.205,324.227c2.833-23.601,1.984-27.062,19.563-23.239l4.463,0.392c13.517,0.615,31.199-2.174,41.587-7c22.362-10.376,35.622-27.7,13.572-23.148c-50.297,10.376-53.755-6.655-53.755-6.655c53.111-78.803,75.313-178.836,56.149-203.322 C352.514-5.534,262.036,26.049,260.522,26.869l-0.482,0.089c-9.938-2.062-21.06-3.294-33.554-3.496c-22.761-0.374-40.032,5.967-53.133,15.904c0,0-161.408-66.498-153.899,83.628c1.597,31.936,45.777,241.655,98.47,178.31 c19.259-23.163,37.871-42.748,37.871-42.748c9.242,6.14,20.307,9.272,31.912,8.147l0.897-0.765c-0.281,2.876-0.157,5.689,0.359,9.019c-13.572,15.167-9.584,17.83-36.723,23.416c-27.457,5.659-11.326,15.734-0.797,18.367c12.768,3.193,42.305,7.716,62.268-20.224 l-0.795,3.188c5.325,4.26,4.965,30.619,5.72,49.452c0.756,18.834,2.017,36.409,5.856,46.771c3.839,10.36,8.369,37.05,44.036,29.406c29.809-6.388,52.6-15.582,54.677-101.107"/>
|
||||
<path style="fill:#336791;stroke:none;" d="M402.395,271.23c-50.302,10.376-53.76-6.655-53.76-6.655c53.111-78.808,75.313-178.843,56.153-203.326c-52.27-66.785-142.752-35.2-144.262-34.38l-0.486,0.087c-9.938-2.063-21.06-3.292-33.56-3.496c-22.761-0.373-40.026,5.967-53.127,15.902 c0,0-161.411-66.495-153.904,83.63c1.597,31.938,45.776,241.657,98.471,178.312c19.26-23.163,37.869-42.748,37.869-42.748c9.243,6.14,20.308,9.272,31.908,8.147l0.901-0.765c-0.28,2.876-0.152,5.689,0.361,9.019c-13.575,15.167-9.586,17.83-36.723,23.416 c-27.459,5.659-11.328,15.734-0.796,18.367c12.768,3.193,42.307,7.716,62.266-20.224l-0.796,3.188c5.319,4.26,9.054,27.711,8.428,48.969c-0.626,21.259-1.044,35.854,3.147,47.254c4.191,11.4,8.368,37.05,44.042,29.406c29.809-6.388,45.256-22.942,47.405-50.555 c1.525-19.631,4.976-16.729,5.194-34.28l2.768-8.309c3.192-26.611,0.507-35.196,18.872-31.203l4.463,0.392c13.517,0.615,31.208-2.174,41.591-7c22.358-10.376,35.618-27.7,13.573-23.148z"/>
|
||||
<path d="M215.866,286.484c-1.385,49.516,0.348,99.377,5.193,111.495c4.848,12.118,15.223,35.688,50.9,28.045c29.806-6.39,40.651-18.756,45.357-46.051c3.466-20.082,10.148-75.854,11.005-87.281"/>
|
||||
<path d="M173.104,38.256c0,0-161.521-66.016-154.012,84.109c1.597,31.938,45.779,241.664,98.473,178.316c19.256-23.166,36.671-41.335,36.671-41.335"/>
|
||||
<path d="M260.349,26.207c-5.591,1.753,89.848-34.889,144.087,34.417c19.159,24.484-3.043,124.519-56.153,203.329"/>
|
||||
<path style="stroke-linejoin:bevel;" d="M348.282,263.953c0,0,3.461,17.036,53.764,6.653c22.04-4.552,8.776,12.774-13.577,23.155c-18.345,8.514-59.474,10.696-60.146-1.069c-1.729-30.355,21.647-21.133,19.96-28.739c-1.525-6.85-11.979-13.573-18.894-30.338 c-6.037-14.633-82.796-126.849,21.287-110.183c3.813-0.789-27.146-99.002-124.553-100.599c-97.385-1.597-94.19,119.762-94.19,119.762"/>
|
||||
<path d="M188.604,274.334c-13.577,15.166-9.584,17.829-36.723,23.417c-27.459,5.66-11.326,15.733-0.797,18.365c12.768,3.195,42.307,7.718,62.266-20.229c6.078-8.509-0.036-22.086-8.385-25.547c-4.034-1.671-9.428-3.765-16.361,3.994z"/>
|
||||
<path d="M187.715,274.069c-1.368-8.917,2.93-19.528,7.536-31.942c6.922-18.626,22.893-37.255,10.117-96.339c-9.523-44.029-73.396-9.163-73.436-3.193c-0.039,5.968,2.889,30.26-1.067,58.548c-5.162,36.913,23.488,68.132,56.479,64.938"/>
|
||||
<path style="fill:#FFFFFF;stroke-width:4.155;stroke-linecap:butt;stroke-linejoin:miter;" d="M172.517,141.7c-0.288,2.039,3.733,7.48,8.976,8.207c5.234,0.73,9.714-3.522,9.998-5.559c0.284-2.039-3.732-4.285-8.977-5.015c-5.237-0.731-9.719,0.333-9.996,2.367z"/>
|
||||
<path style="fill:#FFFFFF;stroke-width:2.0775;stroke-linecap:butt;stroke-linejoin:miter;" d="M331.941,137.543c0.284,2.039-3.732,7.48-8.976,8.207c-5.238,0.73-9.718-3.522-10.005-5.559c-0.277-2.039,3.74-4.285,8.979-5.015c5.239-0.73,9.718,0.333,10.002,2.368z"/>
|
||||
<path d="M350.676,123.432c0.863,15.994-3.445,26.888-3.988,43.914c-0.804,24.748,11.799,53.074-7.191,81.435"/>
|
||||
<path style="stroke-width:3;" d="M0,60.232"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.3 KiB |
1
assets/languages/python.svg
Executable file
@ -0,0 +1 @@
|
||||
<svg width="20" height="20" id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><defs><style>.cls-1{fill:#387ab0;}.cls-2{fill:#feca3d;}</style></defs><title>python-logo-colored</title><path class="cls-1" d="M9.93,0C4.87,0,5.18,2.21,5.18,2.21V4.5H10v.7H3.25S0,4.82,0,10s2.84,5,2.84,5H4.53V12.56A2.71,2.71,0,0,1,7.32,9.7h4.79A2.58,2.58,0,0,0,14.8,7.08V2.67S15.23,0,9.93,0ZM7.26,1.53a.88.88,0,1,1-.87.88A.88.88,0,0,1,7.26,1.53Z"/><path class="cls-2" d="M10.07,20c5.06,0,4.75-2.21,4.75-2.21V15.5H10v-.7h6.76S20,15.18,20,10s-2.84-5-2.84-5H15.47V7.44a2.71,2.71,0,0,1-2.79,2.86H7.89A2.58,2.58,0,0,0,5.2,12.92v4.41S4.77,20,10.07,20Zm2.67-1.53a.88.88,0,1,1,.87-.88A.88.88,0,0,1,12.74,18.47Z"/></svg>
|
After Width: | Height: | Size: 726 B |
14
assets/languages/r.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg width="24" height="18" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" viewBox="0 0 724 561">
|
||||
<defs>
|
||||
<linearGradient id="gradientFill-1" x1="0" x2="1" y1="0" y2="1" gradientUnits="objectBoundingBox" spreadMethod="pad">
|
||||
<stop offset="0" stop-color="rgb(203,206,208)" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="rgb(132,131,139)" stop-opacity="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="gradientFill-2" x1="0" x2="1" y1="0" y2="1" gradientUnits="objectBoundingBox" spreadMethod="pad">
|
||||
<stop offset="0" stop-color="rgb(39,109,195)" stop-opacity="1"/>
|
||||
<stop offset="1" stop-color="rgb(22,92,170)" stop-opacity="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path d="M361.453,485.937 C162.329,485.937 0.906,377.828 0.906,244.469 C0.906,111.109 162.329,3.000 361.453,3.000 C560.578,3.000 722.000,111.109 722.000,244.469 C722.000,377.828 560.578,485.937 361.453,485.937 ZM416.641,97.406 C265.289,97.406 142.594,171.314 142.594,262.484 C142.594,353.654 265.289,427.562 416.641,427.562 C567.992,427.562 679.687,377.033 679.687,262.484 C679.687,147.971 567.992,97.406 416.641,97.406 Z" fill="url(#gradientFill-1)" fill-rule="evenodd"/>
|
||||
<path d="M550.000,377.000 C550.000,377.000 571.822,383.585 584.500,390.000 C588.899,392.226 596.510,396.668 602.000,402.500 C607.378,408.212 610.000,414.000 610.000,414.000 L696.000,559.000 L557.000,559.062 L492.000,437.000 C492.000,437.000 478.690,414.131 470.500,407.500 C463.668,401.969 460.755,400.000 454.000,400.000 C449.298,400.000 420.974,400.000 420.974,400.000 L421.000,558.974 L298.000,559.026 L298.000,152.938 L545.000,152.938 C545.000,152.938 657.500,154.967 657.500,262.000 C657.500,369.033 550.000,377.000 550.000,377.000 ZM496.500,241.024 L422.037,240.976 L422.000,310.026 L496.500,310.002 C496.500,310.002 531.000,309.895 531.000,274.877 C531.000,239.155 496.500,241.024 496.500,241.024 Z" fill="url(#gradientFill-2)" fill-rule="evenodd"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
1
assets/languages/ruby.svg
Executable file
@ -0,0 +1 @@
|
||||
<svg width="20" height="20" id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><defs><style>.cls-1{fill:#b11917;}</style></defs><title>ruby-logo-colored</title><path class="cls-1" d="M2,12.84l-.42-1C1.21,11,.86,10.15.51,9.3a.08.08,0,0,1,0-.1C1.23,8,2,6.73,2.67,5.49a.61.61,0,0,1,.12-.16l3.9-3.65a.74.74,0,0,1,.21-.13L10.2.33a.11.11,0,0,1,.1,0l2.77,2s0,0,0,.09c-.47,1.48-.93,3-1.4,4.44a.41.41,0,0,1,0,.09L6.39,11.82l-.09,0L2,12.83Z"/><path class="cls-1" d="M5.12,19.94Q6,16.38,6.9,12.83l6.78,2.63-.07,0A22.89,22.89,0,0,1,11,17.58a14.7,14.7,0,0,1-4.29,2,13.17,13.17,0,0,1-1.54.33Z"/><path class="cls-1" d="M12.68,7.55l6.67-.15c-.08.19-.14.37-.22.55a18.59,18.59,0,0,1-2.54,4.3c-.54.7-1.11,1.35-1.69,2l0,0Z"/><path class="cls-1" d="M7.39,12.08l4.48-4.19,2.21,6.79Z"/><path class="cls-1" d="M15.29,15.19l0,0,.05-.05c.59-.64,1.17-1.29,1.71-2A21.07,21.07,0,0,0,19.43,9.4L19.64,9h0q-.37,4.79-.73,9.55"/><path class="cls-1" d="M19.73,6.08l-5.34-3.9,0,0L17.61.38a.06.06,0,0,1,.06,0,3.5,3.5,0,0,1,2,2A4.49,4.49,0,0,1,20,3.54a6.5,6.5,0,0,1-.12,1.92C19.81,5.66,19.77,5.86,19.73,6.08Z"/><path class="cls-1" d="M18.46,19.09,9,19.74a21.13,21.13,0,0,0,5.66-4l.42.38"/><path class="cls-1" d="M12.66,6.67c.4-1.27.8-2.53,1.19-3.8l5,3.65h0Z"/><path class="cls-1" d="M4.32,19.48c-.67-1.94-1.43-3.87-2.11-5.81L6,12.83c-.56,2.23-1.12,4.44-1.67,6.65Z"/><path class="cls-1" d="M1.64,14.4c.64,1.87,1.29,3.73,1.94,5.6l-.3,0A4.44,4.44,0,0,1,2,19.53,3.32,3.32,0,0,1,.54,18.1,4.57,4.57,0,0,1,.15,17a.19.19,0,0,1,0-.08l1.45-2.49Z"/><path class="cls-1" d="M11.33,0h5.15l0,0L13.67,1.58s-.06,0-.09,0L11.36,0Z"/><path class="cls-1" d="M0,15.47V10.35l0,.08c.39.94.79,1.89,1.18,2.84a.08.08,0,0,1,0,.1L0,15.42A.13.13,0,0,1,0,15.47Z"/></svg>
|
After Width: | Height: | Size: 1.7 KiB |
1
assets/languages/rust.svg
Executable file
@ -0,0 +1 @@
|
||||
<svg width="22" height="22" id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22"><title>rust-logo</title><path d="M21.85,10.73l-.92-.57,0-.27.79-.74a.34.34,0,0,0,.1-.3.33.33,0,0,0-.2-.23l-1-.38A2,2,0,0,0,20.49,8l.64-.88a.34.34,0,0,0,0-.31.31.31,0,0,0-.24-.19l-1.07-.18c0-.08-.08-.16-.13-.24l.45-1a.31.31,0,0,0-.3-.45l-1.08,0-.18-.21.25-1.06a.31.31,0,0,0-.38-.38l-1.06.25a1.73,1.73,0,0,0-.21-.17l0-1.09a.32.32,0,0,0-.14-.28.31.31,0,0,0-.31,0l-1,.45-.24-.13L15.4,1.08a.31.31,0,0,0-.19-.24.34.34,0,0,0-.31,0L14,1.51l-.26-.08-.38-1a.33.33,0,0,0-.23-.2.34.34,0,0,0-.3.1l-.74.79-.27,0L11.27.15a.32.32,0,0,0-.54,0l-.57.92-.27,0L9.15.31a.32.32,0,0,0-.53.1l-.38,1L8,1.51,7.1.87a.32.32,0,0,0-.5.21L6.42,2.15l-.24.13-1-.45a.31.31,0,0,0-.31,0,.32.32,0,0,0-.14.28l0,1.09-.21.17L3.52,3.14a.31.31,0,0,0-.38.38l.25,1.06-.17.21-1.09,0a.32.32,0,0,0-.28.14.31.31,0,0,0,0,.31l.45,1-.13.24L1.08,6.6a.31.31,0,0,0-.24.19.34.34,0,0,0,0,.31L1.51,8a2,2,0,0,0-.08.26l-1,.38a.33.33,0,0,0-.2.23.34.34,0,0,0,.1.3l.79.74,0,.27-.92.57a.32.32,0,0,0,0,.54l.92.57,0,.27-.79.74a.34.34,0,0,0-.1.3.33.33,0,0,0,.2.23l1,.38a2,2,0,0,0,.08.26l-.64.88a.34.34,0,0,0,0,.31.31.31,0,0,0,.24.19l1.07.18.13.24-.45,1a.31.31,0,0,0,0,.31.32.32,0,0,0,.28.14l1.09,0a1.73,1.73,0,0,0,.17.21l-.25,1.06a.31.31,0,0,0,.38.38l1.06-.25.21.17,0,1.09a.32.32,0,0,0,.14.28.31.31,0,0,0,.31,0l1-.45.24.13.18,1.07a.31.31,0,0,0,.19.24.34.34,0,0,0,.31,0L8,20.49l.26.08.38,1a.33.33,0,0,0,.23.2.34.34,0,0,0,.3-.1l.74-.79.27,0,.57.92a.32.32,0,0,0,.54,0l.57-.92.27,0,.74.79a.34.34,0,0,0,.3.1.33.33,0,0,0,.23-.2l.38-1,.26-.08.88.64a.34.34,0,0,0,.31,0,.31.31,0,0,0,.19-.24l.18-1.07.24-.13,1,.45a.31.31,0,0,0,.31,0,.32.32,0,0,0,.14-.28l0-1.09a1.73,1.73,0,0,0,.21-.17l1.06.25a.31.31,0,0,0,.38-.38l-.25-1.06.18-.21,1.08,0a.31.31,0,0,0,.3-.45l-.45-1c.05-.08.09-.16.13-.24l1.07-.18a.31.31,0,0,0,.24-.19.34.34,0,0,0,0-.31L20.49,14a2,2,0,0,0,.08-.26l1-.38a.33.33,0,0,0,.2-.23.34.34,0,0,0-.1-.3l-.79-.74,0-.27.92-.57a.32.32,0,0,0,0-.54Zm-6.18,7.66a.65.65,0,0,1,.27-1.28.66.66,0,0,1,.5.78A.64.64,0,0,1,15.67,18.39Zm-.32-2.12a.59.59,0,0,0-.7.46l-.33,1.53A8,8,0,0,1,11,19a8.16,8.16,0,0,1-3.39-.74L7.28,16.7a.58.58,0,0,0-.7-.46l-1.35.29a7.88,7.88,0,0,1-.7-.83H11.1c.08,0,.12,0,.12-.08V13.3c0-.07,0-.08-.12-.08H9.18V11.74h2.08a1.32,1.32,0,0,1,1.28,1.11c.08.33.26,1.38.38,1.72s.63,1.13,1.17,1.13h3.39a10.43,10.43,0,0,1-.74.88ZM6.26,18.36a.64.64,0,0,1-.77-.5.65.65,0,0,1,.5-.78.65.65,0,0,1,.27,1.28ZM3.77,8.25a.65.65,0,1,1-.86-.33A.64.64,0,0,1,3.77,8.25ZM3,10.07l1.41-.63a.59.59,0,0,0,.3-.79L4.42,8H5.56v5.14H3.26A8.54,8.54,0,0,1,3,10.07Zm6.18-.5V8.05h2.71c.14,0,1,.16,1,.8s-.65.72-1.18.72ZM19,10.93c0,.2,0,.4,0,.6H18.2c-.09,0-.12.05-.12.13V12c0,.89-.5,1.09-.94,1.14s-.89-.18-.95-.44a2.81,2.81,0,0,0-1.31-2.2,3,3,0,0,0,1.65-2.29,2.59,2.59,0,0,0-1.26-2.12,3.63,3.63,0,0,0-1.73-.57H5A8.07,8.07,0,0,1,9.51,3l1,1.06a.6.6,0,0,0,.85,0L12.48,3A8.07,8.07,0,0,1,18,6.94l-.77,1.74a.59.59,0,0,0,.3.79l1.48.66A7,7,0,0,1,19,10.93Zm-8.53-8.8a.66.66,0,0,1,.93,0,.65.65,0,0,1,0,.92.66.66,0,0,1-.93,0A.65.65,0,0,1,10.51,2.13Zm7.65,6.15A.65.65,0,0,1,19,8a.65.65,0,1,1-.86.33Z"/></svg>
|
After Width: | Height: | Size: 3.0 KiB |
10
assets/languages/swift.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="20px" height="18px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 36" version="1.1">
|
||||
<defs>
|
||||
<linearGradient id="a" x2="50%" x1="50%" y2="100%">
|
||||
<stop stop-color="#F88A36" offset="0"/>
|
||||
<stop stop-color="#FD2020" offset="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path d="m29.885 33.047c-4.667 2.696-11.084 2.973-17.54 0.206-5.2273-2.224-9.5646-6.117-12.345-10.565 1.3346 1.112 2.8916 2.002 4.5598 2.78 6.6672 3.125 13.333 2.911 18.024 0.008-0.003-0.003-0.005-0.005-0.007-0.008-6.673-5.116-12.345-11.789-16.571-17.238-0.8901-0.8898-1.5574-2.002-2.2247-3.0029 5.1159 4.671 13.235 10.565 16.126 12.234-6.116-6.451-11.566-14.458-11.344-14.236 9.676 9.787 18.685 15.348 18.685 15.348 0.298 0.168 0.528 0.308 0.713 0.433 0.195-0.496 0.366-1.011 0.51-1.545 1.557-5.672-0.222-12.123-4.115-17.461 9.008 5.4495 14.347 15.681 12.122 24.245-0.058 0.231-0.121 0.459-0.189 0.683 0.026 0.031 0.052 0.063 0.078 0.096 4.448 5.561 3.225 11.455 2.669 10.343-2.413-4.722-6.88-3.278-9.151-2.32z" fill="url(#a)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/logo.png
Normal file
After Width: | Height: | Size: 27 KiB |
59
build.rs
Normal file
@ -0,0 +1,59 @@
|
||||
//! The logic that gets executed before building the binary and tests.
|
||||
//! We use it to auto-generate the Wasm spectests for each of the
|
||||
//! available compilers.
|
||||
//!
|
||||
//! Please try to keep this file as clean as possible.
|
||||
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use test_generator::{
|
||||
build_ignores_from_textfile, test_directory, test_directory_module, wast_processor,
|
||||
with_backends, with_test_module, Testsuite,
|
||||
};
|
||||
|
||||
fn is_truthy_env(name: &str) -> bool {
|
||||
env::var(name).map(|n| n == "1").unwrap_or_default()
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=tests/ignores.txt");
|
||||
|
||||
let out_dir = PathBuf::from(
|
||||
env::var_os("OUT_DIR").expect("The OUT_DIR environment variable must be set"),
|
||||
);
|
||||
let ignores = build_ignores_from_textfile("tests/ignores.txt".into())?;
|
||||
|
||||
// Spectests test generation
|
||||
let mut spectests = Testsuite {
|
||||
buffer: String::new(),
|
||||
path: vec![],
|
||||
ignores: ignores.clone(),
|
||||
};
|
||||
|
||||
let backends = vec!["cranelift"];
|
||||
with_backends(&mut spectests, &backends, |mut spectests| {
|
||||
with_test_module(&mut spectests, "spec", |spectests| {
|
||||
let _spec_tests = test_directory(spectests, "tests/wast/spec", wast_processor)?;
|
||||
Ok(())
|
||||
})?;
|
||||
with_test_module(&mut spectests, "wasmer", |spectests| {
|
||||
let _spec_tests = test_directory(spectests, "tests/wast/wasmer", wast_processor)?;
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let spectests_output = out_dir.join("generated_spectests.rs");
|
||||
fs::write(&spectests_output, spectests.buffer)?;
|
||||
|
||||
// Write out our auto-generated tests and opportunistically format them with
|
||||
// `rustfmt` if it's installed.
|
||||
// Note: We need drop because we don't want to run `unwrap` or `expect` as
|
||||
// the command might fail, but we don't care about it's result.
|
||||
drop(Command::new("rustfmt").arg(&spectests_output).status());
|
||||
|
||||
Ok(())
|
||||
}
|
44
lib/api/Cargo.toml
Normal file
@ -0,0 +1,44 @@
|
||||
[package]
|
||||
name = "wasmer"
|
||||
version = "0.16.2"
|
||||
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
description = "Wasmer runtime API"
|
||||
license = "(Apache-2.0 WITH LLVM-exception) OR MIT"
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
|
||||
wasmer-compiler-cranelift = { path = "../compiler-cranelift", version = "0.16.2" }
|
||||
wasmer-compiler = { path = "../compiler", version = "0.16.2" }
|
||||
wasmer-jit = { path = "../jit", version = "0.16.2" }
|
||||
wasm-common = { path = "../wasm-common", version = "0.16.2" }
|
||||
indexmap = { version = "1.3.2", features = ["serde-1"] }
|
||||
cfg-if = "0.1.10"
|
||||
wat = { version = "1.0.14", optional = true }
|
||||
thiserror = "1.0.14"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = "0.3.8"
|
||||
|
||||
[dev-dependencies]
|
||||
# for the binary wasmer.rs
|
||||
libc = "0.2"
|
||||
pretty_env_logger = "0.4.0"
|
||||
rayon = "1.3.0"
|
||||
file-per-thread-logger = "0.1.2"
|
||||
wat = "1.0.15"
|
||||
tempfile = "3.1"
|
||||
# TODO: remove anyhow from testing
|
||||
anyhow = "1.0.28"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
[features]
|
||||
default = ['wat']
|
||||
|
||||
[[test]]
|
||||
name = "host-segfault"
|
||||
harness = false
|
8
lib/api/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
## Wasmer Embedding API
|
||||
|
||||
The `wasmer` crate is an embedding API of the `wasmer` WebAssembly runtime.
|
||||
This is intended to be used in Rust projects and provides a high-level API of
|
||||
working with WebAssembly modules.
|
||||
|
||||
If you're interested in embedding `wasmer` in other languages, you may wish to
|
||||
take a look a the [C embedding API](../c-api) instead!
|
155
lib/api/src/exports.rs
Normal file
@ -0,0 +1,155 @@
|
||||
use crate::externals::{Extern, Func, Global, Memory, Table};
|
||||
use crate::import_object::LikeNamespace;
|
||||
use indexmap::IndexMap;
|
||||
use std::iter::FromIterator;
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
use wasmer_runtime::Export;
|
||||
|
||||
/// The `ExportError` can happen when trying to get a specific
|
||||
/// export [`Extern`] from the [`Instance`] exports.
|
||||
///
|
||||
/// ```ignore
|
||||
/// # let my_instance = Instance::new(...);
|
||||
/// // This returns an Error since the imported is a funciton, not a global.
|
||||
/// let missing_import: Global = my_instance.exports.get("func")?;
|
||||
/// // This returns an Error since the import doesn't exist.
|
||||
/// let missing_import: Func = my_instance.exports.get("unknown")?;
|
||||
/// ```
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ExportError {
|
||||
/// An error than occurs when the exported type and the expected type
|
||||
/// are incompatible.
|
||||
#[error("Incompatible Export Type")]
|
||||
IncompatibleType,
|
||||
/// This error arises when an export is missing
|
||||
#[error("Missing export {0}")]
|
||||
Missing(String),
|
||||
}
|
||||
|
||||
/// Exports is a special kind of map that allows easily unwrapping
|
||||
/// the types of instances.
|
||||
#[derive(Clone)]
|
||||
pub struct Exports {
|
||||
map: Arc<IndexMap<String, Extern>>,
|
||||
}
|
||||
|
||||
impl Exports {
|
||||
/// Creates a new `Exports`.
|
||||
pub fn new() -> Self {
|
||||
Exports {
|
||||
map: Arc::new(IndexMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `Exports` with capacity `n`.
|
||||
pub fn with_capacity(n: usize) -> Self {
|
||||
Exports {
|
||||
map: Arc::new(IndexMap::with_capacity(n)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the number of exports in the `Exports` map.
|
||||
pub fn len(&self) -> usize {
|
||||
self.map.len()
|
||||
}
|
||||
|
||||
/// Insert a new export into this `Exports` map.
|
||||
pub fn insert<S, E>(&mut self, name: S, value: E)
|
||||
where
|
||||
S: Into<String>,
|
||||
E: Into<Extern>,
|
||||
{
|
||||
Arc::get_mut(&mut self.map)
|
||||
.unwrap()
|
||||
.insert(name.into(), value.into());
|
||||
}
|
||||
|
||||
/// Get an export given a `name`.
|
||||
///
|
||||
/// The `get` method is specifically made for usage inside of
|
||||
/// Rust APIs, as we can detect what's the desired type easily.
|
||||
///
|
||||
/// If you want to get an export dynamically with type checking
|
||||
/// please use the following functions: `get_func`, `get_memory`,
|
||||
/// `get_table` or `get_global` instead.
|
||||
///
|
||||
/// If you want to get an export dynamically handling manually
|
||||
/// type checking manually, please use `get_extern`.
|
||||
pub fn get<'a, T: Exportable<'a>>(&'a self, name: &str) -> Result<&T, ExportError> {
|
||||
match self.map.get(name) {
|
||||
None => return Err(ExportError::Missing(name.to_string())),
|
||||
Some(extern_) => T::get_self_from_extern(extern_),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an export as a `Global`.
|
||||
pub fn get_global(&self, name: &str) -> Result<&Global, ExportError> {
|
||||
self.get(name)
|
||||
}
|
||||
|
||||
/// Get an export as a `Memory`.
|
||||
pub fn get_memory(&self, name: &str) -> Result<&Memory, ExportError> {
|
||||
self.get(name)
|
||||
}
|
||||
|
||||
/// Get an export as a `Table`.
|
||||
pub fn get_table(&self, name: &str) -> Result<&Table, ExportError> {
|
||||
self.get(name)
|
||||
}
|
||||
|
||||
/// Get an export as a `Func`.
|
||||
pub fn get_func(&self, name: &str) -> Result<&Func, ExportError> {
|
||||
self.get(name)
|
||||
}
|
||||
|
||||
/// Get an export as an `Extern`.
|
||||
pub fn get_extern(&self, name: &str) -> Option<&Extern> {
|
||||
self.map.get(name)
|
||||
}
|
||||
|
||||
/// Returns true if the `Exports` contains the given name.
|
||||
pub fn contains<S>(&mut self, name: S) -> bool
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
Arc::get_mut(&mut self.map)
|
||||
.unwrap()
|
||||
.contains_key(&name.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<(String, Extern)> for Exports {
|
||||
fn from_iter<I: IntoIterator<Item = (String, Extern)>>(iter: I) -> Self {
|
||||
// TODO: Move into IndexMap collect
|
||||
let mut exports = Exports::new();
|
||||
for (name, extern_) in iter {
|
||||
exports.insert(name, extern_);
|
||||
}
|
||||
exports
|
||||
}
|
||||
}
|
||||
|
||||
impl LikeNamespace for Exports {
|
||||
fn get_namespace_export(&self, name: &str) -> Option<Export> {
|
||||
self.map.get(name).map(|is_export| is_export.to_export())
|
||||
}
|
||||
|
||||
fn get_namespace_exports(&self) -> Vec<(String, Export)> {
|
||||
self.map
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.to_export()))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// This trait is used to mark types as gettable from an [`Instance`].
|
||||
pub trait Exportable<'a>: Sized {
|
||||
/// This function is used when providedd the Extern as exportable, so it
|
||||
/// can be used while instantiating the Module.
|
||||
fn to_export(&self) -> Export;
|
||||
|
||||
/// Implementation of how to get the export corresponding to the implementing type
|
||||
/// from an [`Instance`] by name.
|
||||
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError>;
|
||||
}
|
656
lib/api/src/externals.rs
Normal file
@ -0,0 +1,656 @@
|
||||
use crate::exports::{ExportError, Exportable};
|
||||
use crate::store::{Store, StoreObject};
|
||||
use crate::types::{Val, ValAnyFunc};
|
||||
use crate::Mutability;
|
||||
use crate::RuntimeError;
|
||||
use crate::{ExternType, FuncType, GlobalType, MemoryType, TableType, ValType};
|
||||
use std::cmp::max;
|
||||
use std::slice;
|
||||
use wasm_common::{HostFunction, WasmTypeList, WithEnv, WithoutEnv, WASM_PAGE_SIZE};
|
||||
use wasmer_runtime::{
|
||||
wasmer_call_trampoline, Export, ExportFunction, ExportGlobal, ExportMemory, ExportTable,
|
||||
InstanceHandle, LinearMemory, Table as RuntimeTable, VMCallerCheckedAnyfunc, VMContext,
|
||||
VMFunctionBody, VMGlobalDefinition, VMMemoryDefinition, VMTrampoline,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Extern {
|
||||
Func(Func),
|
||||
Global(Global),
|
||||
Table(Table),
|
||||
Memory(Memory),
|
||||
}
|
||||
|
||||
impl Extern {
|
||||
pub fn ty(&self) -> ExternType {
|
||||
match self {
|
||||
Extern::Func(ft) => ExternType::Func(ft.ty().clone()),
|
||||
Extern::Memory(ft) => ExternType::Memory(ft.ty().clone()),
|
||||
Extern::Table(tt) => ExternType::Table(tt.ty().clone()),
|
||||
Extern::Global(gt) => ExternType::Global(gt.ty().clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_export(store: &Store, export: Export) -> Extern {
|
||||
match export {
|
||||
Export::Function(f) => Extern::Func(Func::from_export(store, f)),
|
||||
Export::Memory(m) => Extern::Memory(Memory::from_export(store, m)),
|
||||
Export::Global(g) => Extern::Global(Global::from_export(store, g)),
|
||||
Export::Table(t) => Extern::Table(Table::from_export(store, t)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Extern {
|
||||
fn to_export(&self) -> Export {
|
||||
match self {
|
||||
Extern::Func(f) => f.to_export(),
|
||||
Extern::Global(g) => g.to_export(),
|
||||
Extern::Memory(m) => m.to_export(),
|
||||
Extern::Table(t) => t.to_export(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
|
||||
// Since this is already an extern, we can just return it.
|
||||
Ok(_extern)
|
||||
}
|
||||
}
|
||||
|
||||
impl StoreObject for Extern {
|
||||
fn comes_from_same_store(&self, store: &Store) -> bool {
|
||||
let my_store = match self {
|
||||
Extern::Func(f) => f.store(),
|
||||
Extern::Global(g) => g.store(),
|
||||
Extern::Memory(m) => m.store(),
|
||||
Extern::Table(t) => t.store(),
|
||||
};
|
||||
Store::same(my_store, store)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Func> for Extern {
|
||||
fn from(r: Func) -> Self {
|
||||
Extern::Func(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Global> for Extern {
|
||||
fn from(r: Global) -> Self {
|
||||
Extern::Global(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Memory> for Extern {
|
||||
fn from(r: Memory) -> Self {
|
||||
Extern::Memory(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Table> for Extern {
|
||||
fn from(r: Table) -> Self {
|
||||
Extern::Table(r)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Global {
|
||||
store: Store,
|
||||
exported: ExportGlobal,
|
||||
}
|
||||
|
||||
impl Global {
|
||||
pub fn new(store: &Store, val: Val) -> Global {
|
||||
// Note: we unwrap because the provided type should always match
|
||||
// the value type, so it's safe to unwrap.
|
||||
Self::from_type(store, GlobalType::new(val.ty(), Mutability::Const), val).unwrap()
|
||||
}
|
||||
|
||||
pub fn new_mut(store: &Store, val: Val) -> Global {
|
||||
// Note: we unwrap because the provided type should always match
|
||||
// the value type, so it's safe to unwrap.
|
||||
Self::from_type(store, GlobalType::new(val.ty(), Mutability::Var), val).unwrap()
|
||||
}
|
||||
|
||||
pub fn from_type(store: &Store, ty: GlobalType, val: Val) -> Result<Global, RuntimeError> {
|
||||
if !val.comes_from_same_store(store) {
|
||||
return Err(RuntimeError::new("cross-`Store` globals are not supported"));
|
||||
}
|
||||
if val.ty() != ty.ty.clone() {
|
||||
return Err(RuntimeError::new(
|
||||
"value provided does not match the type of this global",
|
||||
));
|
||||
}
|
||||
let mut definition = VMGlobalDefinition::new();
|
||||
unsafe {
|
||||
match val {
|
||||
Val::I32(x) => *definition.as_i32_mut() = x,
|
||||
Val::I64(x) => *definition.as_i64_mut() = x,
|
||||
Val::F32(x) => *definition.as_f32_bits_mut() = x,
|
||||
Val::F64(x) => *definition.as_f64_bits_mut() = x,
|
||||
_ => return Err(RuntimeError::new(format!("create_global for {:?}", val))),
|
||||
// Val::V128(x) => *definition.as_u128_bits_mut() = x,
|
||||
}
|
||||
};
|
||||
let exported = ExportGlobal {
|
||||
definition: Box::leak(Box::new(definition)),
|
||||
global: ty,
|
||||
};
|
||||
Ok(Global {
|
||||
store: store.clone(),
|
||||
exported,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ty(&self) -> &GlobalType {
|
||||
&self.exported.global
|
||||
}
|
||||
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.store
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Val {
|
||||
unsafe {
|
||||
let definition = &mut *self.exported.definition;
|
||||
match self.ty().ty {
|
||||
ValType::I32 => Val::from(*definition.as_i32()),
|
||||
ValType::I64 => Val::from(*definition.as_i64()),
|
||||
ValType::F32 => Val::F32(*definition.as_u32()),
|
||||
ValType::F64 => Val::F64(*definition.as_u64()),
|
||||
_ => unimplemented!("Global::get for {:?}", self.ty().ty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&self, val: Val) -> Result<(), RuntimeError> {
|
||||
if self.ty().mutability != Mutability::Var {
|
||||
return Err(RuntimeError::new(format!("immutable global cannot be set")));
|
||||
}
|
||||
if val.ty() != self.ty().ty {
|
||||
return Err(RuntimeError::new(format!(
|
||||
"global of type {:?} cannot be set to {:?}",
|
||||
self.ty().ty,
|
||||
val.ty()
|
||||
)));
|
||||
}
|
||||
if !val.comes_from_same_store(&self.store) {
|
||||
return Err(RuntimeError::new("cross-`Store` values are not supported"));
|
||||
}
|
||||
unsafe {
|
||||
let definition = &mut *self.exported.definition;
|
||||
match val {
|
||||
Val::I32(i) => *definition.as_i32_mut() = i,
|
||||
Val::I64(i) => *definition.as_i64_mut() = i,
|
||||
Val::F32(f) => *definition.as_u32_mut() = f,
|
||||
Val::F64(f) => *definition.as_u64_mut() = f,
|
||||
_ => unimplemented!("Global::set for {:?}", val.ty()),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn from_export(store: &Store, wasmer_export: ExportGlobal) -> Global {
|
||||
Global {
|
||||
store: store.clone(),
|
||||
exported: wasmer_export,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Global {
|
||||
fn to_export(&self) -> Export {
|
||||
self.exported.clone().into()
|
||||
}
|
||||
|
||||
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
|
||||
match _extern {
|
||||
Extern::Global(global) => Ok(global),
|
||||
_ => Err(ExportError::IncompatibleType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Table {
|
||||
store: Store,
|
||||
// If the Table is owned by the Store, not the instance
|
||||
owned_by_store: bool,
|
||||
exported: ExportTable,
|
||||
}
|
||||
|
||||
fn set_table_item(
|
||||
table: &RuntimeTable,
|
||||
item_index: u32,
|
||||
item: VMCallerCheckedAnyfunc,
|
||||
) -> Result<(), RuntimeError> {
|
||||
table.set(item_index, item).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
impl Table {
|
||||
pub fn new(store: &Store, ty: TableType, init: Val) -> Result<Table, RuntimeError> {
|
||||
let item = init.into_checked_anyfunc(store)?;
|
||||
let table = store.engine().create_table(&ty);
|
||||
|
||||
let definition = table.vmtable();
|
||||
for i in 0..definition.current_elements {
|
||||
set_table_item(&table, i, item.clone())?;
|
||||
}
|
||||
|
||||
Ok(Table {
|
||||
store: store.clone(),
|
||||
owned_by_store: true,
|
||||
exported: ExportTable {
|
||||
from: Box::leak(Box::new(table)),
|
||||
definition: Box::leak(Box::new(definition)),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn table(&self) -> &RuntimeTable {
|
||||
unsafe { (&*self.exported.from) }
|
||||
}
|
||||
|
||||
pub fn ty(&self) -> &TableType {
|
||||
&self.exported.plan().table
|
||||
}
|
||||
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.store
|
||||
}
|
||||
|
||||
pub fn get(&self, index: u32) -> Option<Val> {
|
||||
let item = self.table().get(index)?;
|
||||
Some(ValAnyFunc::from_checked_anyfunc(item, &self.store))
|
||||
}
|
||||
|
||||
pub fn set(&self, index: u32, val: Val) -> Result<(), RuntimeError> {
|
||||
let item = val.into_checked_anyfunc(&self.store)?;
|
||||
set_table_item(self.table(), index, item)
|
||||
}
|
||||
|
||||
pub fn size(&self) -> u32 {
|
||||
self.table().size()
|
||||
}
|
||||
|
||||
pub fn grow(&self, delta: u32, init: Val) -> Result<u32, RuntimeError> {
|
||||
let item = init.into_checked_anyfunc(&self.store)?;
|
||||
let table = self.table();
|
||||
if let Some(len) = table.grow(delta) {
|
||||
for i in 0..delta {
|
||||
let i = len - (delta - i);
|
||||
set_table_item(table, i, item.clone())?;
|
||||
}
|
||||
Ok(len)
|
||||
} else {
|
||||
Err(RuntimeError::new(format!(
|
||||
"failed to grow table by `{}`",
|
||||
delta
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy(
|
||||
dst_table: &Table,
|
||||
dst_index: u32,
|
||||
src_table: &Table,
|
||||
src_index: u32,
|
||||
len: u32,
|
||||
) -> Result<(), RuntimeError> {
|
||||
if !Store::same(&dst_table.store, &src_table.store) {
|
||||
return Err(RuntimeError::new(
|
||||
"cross-`Store` table copies are not supported",
|
||||
));
|
||||
}
|
||||
RuntimeTable::copy(
|
||||
dst_table.table(),
|
||||
src_table.table(),
|
||||
dst_index,
|
||||
src_index,
|
||||
len,
|
||||
)
|
||||
.map_err(|e| RuntimeError::from_jit(e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn from_export(store: &Store, wasmer_export: ExportTable) -> Table {
|
||||
Table {
|
||||
store: store.clone(),
|
||||
owned_by_store: false,
|
||||
exported: wasmer_export,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Table {
|
||||
fn to_export(&self) -> Export {
|
||||
self.exported.clone().into()
|
||||
}
|
||||
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
|
||||
match _extern {
|
||||
Extern::Table(table) => Ok(table),
|
||||
_ => Err(ExportError::IncompatibleType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Memory {
|
||||
store: Store,
|
||||
// If the Memory is owned by the Store, not the instance
|
||||
owned_by_store: bool,
|
||||
exported: ExportMemory,
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
pub fn new(store: &Store, ty: MemoryType) -> Memory {
|
||||
let memory = store.engine().create_memory(&ty).unwrap();
|
||||
let definition = memory.vmmemory();
|
||||
|
||||
Memory {
|
||||
store: store.clone(),
|
||||
owned_by_store: true,
|
||||
exported: ExportMemory {
|
||||
from: Box::leak(Box::new(memory)),
|
||||
definition: Box::leak(Box::new(definition)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn definition(&self) -> &VMMemoryDefinition {
|
||||
unsafe { &*self.exported.definition }
|
||||
}
|
||||
|
||||
pub fn ty(&self) -> &MemoryType {
|
||||
&self.exported.plan().memory
|
||||
}
|
||||
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.store
|
||||
}
|
||||
|
||||
pub unsafe fn data_unchecked(&self) -> &[u8] {
|
||||
self.data_unchecked_mut()
|
||||
}
|
||||
|
||||
pub unsafe fn data_unchecked_mut(&self) -> &mut [u8] {
|
||||
let definition = self.definition();
|
||||
slice::from_raw_parts_mut(definition.base, definition.current_length)
|
||||
}
|
||||
|
||||
pub fn data_ptr(&self) -> *mut u8 {
|
||||
self.definition().base
|
||||
}
|
||||
|
||||
pub fn data_size(&self) -> usize {
|
||||
self.definition().current_length
|
||||
}
|
||||
|
||||
pub fn size(&self) -> u32 {
|
||||
(self.data_size() / WASM_PAGE_SIZE as usize) as u32
|
||||
}
|
||||
|
||||
pub fn grow(&self, delta: u32) -> Result<u32, RuntimeError> {
|
||||
Ok(unsafe { (&*self.exported.from) }.grow(delta).unwrap())
|
||||
}
|
||||
|
||||
pub(crate) fn from_export(store: &Store, wasmer_export: ExportMemory) -> Memory {
|
||||
Memory {
|
||||
store: store.clone(),
|
||||
owned_by_store: false,
|
||||
exported: wasmer_export.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Memory {
|
||||
fn to_export(&self) -> Export {
|
||||
self.exported.clone().into()
|
||||
}
|
||||
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
|
||||
match _extern {
|
||||
Extern::Memory(memory) => Ok(memory),
|
||||
_ => Err(ExportError::IncompatibleType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Memory {
|
||||
fn drop(&mut self) {
|
||||
if self.owned_by_store {
|
||||
// let r = unsafe { libc::munmap(self.ptr as *mut libc::c_void, self.len) };
|
||||
// assert_eq!(r, 0, "munmap failed: {}", std::io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A function defined in the Wasm module
|
||||
#[derive(Clone)]
|
||||
pub struct WasmFunc {
|
||||
// The trampoline to do the call
|
||||
trampoline: VMTrampoline,
|
||||
}
|
||||
|
||||
/// A function defined in the Host
|
||||
#[derive(Clone)]
|
||||
pub struct HostFunc {
|
||||
// func: wasm_common::Func<Args, Rets>,
|
||||
}
|
||||
|
||||
/// The inner helper
|
||||
#[derive(Clone)]
|
||||
pub enum InnerFunc {
|
||||
/// A function defined in the Wasm side
|
||||
Wasm(WasmFunc),
|
||||
/// A function defined in the Host side
|
||||
Host(HostFunc),
|
||||
}
|
||||
|
||||
/// A WebAssembly `function`.
|
||||
#[derive(Clone)]
|
||||
pub struct Func {
|
||||
store: Store,
|
||||
// If the Function is owned by the Store, not the instance
|
||||
owned_by_store: bool,
|
||||
inner: InnerFunc,
|
||||
exported: ExportFunction,
|
||||
}
|
||||
|
||||
impl Func {
|
||||
/// Creates a new `Func` with the given parameters.
|
||||
///
|
||||
/// * `store` - a global cache to store information in
|
||||
/// * `func` - the function.
|
||||
pub fn new<F, Args, Rets, Env>(store: &Store, func: F) -> Func
|
||||
where
|
||||
F: HostFunction<Args, Rets, WithoutEnv, Env>,
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
Env: Sized,
|
||||
{
|
||||
let func: wasm_common::Func<Args, Rets, Env> = wasm_common::Func::new(func);
|
||||
let address = func.address() as *const VMFunctionBody;
|
||||
let vmctx = (func.env().unwrap_or(std::ptr::null_mut()) as *mut _) as *mut VMContext;
|
||||
let func_type = func.ty();
|
||||
let signature = store.engine().register_signature(&func_type);
|
||||
Func {
|
||||
store: store.clone(),
|
||||
owned_by_store: true,
|
||||
inner: InnerFunc::Host(HostFunc {
|
||||
// func
|
||||
}),
|
||||
exported: ExportFunction {
|
||||
address,
|
||||
vmctx,
|
||||
signature,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `Func` with the given parameters.
|
||||
///
|
||||
/// * `store` - a global cache to store information in.
|
||||
/// * `env` - the function environment.
|
||||
/// * `func` - the function.
|
||||
pub fn new_env<F, Args, Rets, Env>(store: &Store, env: &mut Env, func: F) -> Func
|
||||
where
|
||||
F: HostFunction<Args, Rets, WithEnv, Env>,
|
||||
Args: WasmTypeList,
|
||||
Rets: WasmTypeList,
|
||||
Env: Sized,
|
||||
{
|
||||
let func: wasm_common::Func<Args, Rets, Env> = wasm_common::Func::new_env(env, func);
|
||||
let address = func.address() as *const VMFunctionBody;
|
||||
let vmctx = (func.env().unwrap_or(std::ptr::null_mut()) as *mut _) as *mut VMContext;
|
||||
let func_type = func.ty();
|
||||
let signature = store.engine().register_signature(&func_type);
|
||||
Func {
|
||||
store: store.clone(),
|
||||
owned_by_store: true,
|
||||
inner: InnerFunc::Host(HostFunc {
|
||||
// func
|
||||
}),
|
||||
exported: ExportFunction {
|
||||
address,
|
||||
vmctx,
|
||||
signature,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying type of this function.
|
||||
pub fn ty(&self) -> FuncType {
|
||||
self.store
|
||||
.engine()
|
||||
.lookup_signature(self.exported.signature)
|
||||
.expect("missing signature")
|
||||
// self.inner.unwrap().ty()
|
||||
}
|
||||
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.store
|
||||
}
|
||||
|
||||
fn call_wasm(
|
||||
&self,
|
||||
func: &WasmFunc,
|
||||
params: &[Val],
|
||||
results: &mut [Val],
|
||||
) -> Result<(), RuntimeError> {
|
||||
let signature = self.ty();
|
||||
if signature.params().len() != params.len() {
|
||||
return Err(RuntimeError::new(format!(
|
||||
"expected {} arguments, got {}",
|
||||
signature.params().len(),
|
||||
params.len()
|
||||
)));
|
||||
}
|
||||
if signature.results().len() != results.len() {
|
||||
return Err(RuntimeError::new(format!(
|
||||
"expected {} results, got {}",
|
||||
signature.results().len(),
|
||||
results.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let mut values_vec = vec![0; max(params.len(), results.len())];
|
||||
|
||||
// Store the argument values into `values_vec`.
|
||||
let param_tys = signature.params().iter();
|
||||
for ((arg, slot), ty) in params.iter().zip(&mut values_vec).zip(param_tys) {
|
||||
if arg.ty() != ty.clone() {
|
||||
return Err(RuntimeError::new("argument type mismatch"));
|
||||
}
|
||||
unsafe {
|
||||
arg.write_value_to(slot);
|
||||
}
|
||||
}
|
||||
|
||||
// Call the trampoline.
|
||||
if let Err(error) = unsafe {
|
||||
wasmer_call_trampoline(
|
||||
self.exported.vmctx,
|
||||
std::ptr::null_mut(),
|
||||
func.trampoline,
|
||||
self.exported.address,
|
||||
values_vec.as_mut_ptr() as *mut u8,
|
||||
)
|
||||
} {
|
||||
return Err(RuntimeError::from_jit(error));
|
||||
}
|
||||
|
||||
// Load the return values out of `values_vec`.
|
||||
for (index, value_type) in signature.results().iter().enumerate() {
|
||||
unsafe {
|
||||
let ptr = values_vec.as_ptr().add(index);
|
||||
results[index] = Val::read_value_from(ptr, value_type.clone());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the number of parameters that this function takes.
|
||||
pub fn param_arity(&self) -> usize {
|
||||
self.ty().params().len()
|
||||
}
|
||||
|
||||
/// Returns the number of results this function produces.
|
||||
pub fn result_arity(&self) -> usize {
|
||||
self.ty().results().len()
|
||||
}
|
||||
|
||||
/// Call the [`Func`] function.
|
||||
///
|
||||
/// Depending on where the Function is defined, it will call it.
|
||||
/// 1. If the function is defined inside a WebAssembly, it will call the trampoline
|
||||
/// for the function signature.
|
||||
/// 2. If the function is defined in the host (in a native way), it will
|
||||
/// call the trampoline.
|
||||
pub fn call(&self, params: &[Val]) -> Result<Box<[Val]>, RuntimeError> {
|
||||
let mut results = vec![Val::null(); self.result_arity()];
|
||||
match &self.inner {
|
||||
InnerFunc::Wasm(wasm) => {
|
||||
self.call_wasm(&wasm, params, &mut results)?;
|
||||
}
|
||||
_ => {} // _ => unimplemented!("The host is unimplemented"),
|
||||
}
|
||||
Ok(results.into_boxed_slice())
|
||||
}
|
||||
|
||||
pub(crate) fn from_export(store: &Store, wasmer_export: ExportFunction) -> Func {
|
||||
let trampoline = store.engine().trampoline(wasmer_export.signature).unwrap();
|
||||
Func {
|
||||
store: store.clone(),
|
||||
owned_by_store: false,
|
||||
inner: InnerFunc::Wasm(WasmFunc { trampoline }),
|
||||
exported: wasmer_export,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn checked_anyfunc(&self) -> VMCallerCheckedAnyfunc {
|
||||
VMCallerCheckedAnyfunc {
|
||||
func_ptr: self.exported.address,
|
||||
type_index: self.exported.signature,
|
||||
vmctx: self.exported.vmctx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Exportable<'a> for Func {
|
||||
fn to_export(&self) -> Export {
|
||||
self.exported.clone().into()
|
||||
}
|
||||
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> {
|
||||
match _extern {
|
||||
Extern::Func(func) => Ok(func),
|
||||
_ => Err(ExportError::IncompatibleType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Func {
|
||||
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
355
lib/api/src/import_object.rs
Normal file
@ -0,0 +1,355 @@
|
||||
//! The import module contains the implementation data structures and helper functions used to
|
||||
//! manipulate and access a wasm module's imports including memories, tables, globals, and
|
||||
//! functions.
|
||||
use crate::exports::{ExportError, Exportable, Exports};
|
||||
use crate::externals::Extern;
|
||||
use std::collections::VecDeque;
|
||||
use std::collections::{hash_map::Entry, HashMap};
|
||||
use std::{
|
||||
borrow::{Borrow, BorrowMut},
|
||||
ffi::c_void,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use wasm_common::GlobalType;
|
||||
use wasmer_jit::Resolver;
|
||||
use wasmer_runtime::{Export, ExportGlobal};
|
||||
|
||||
/// The `LikeNamespace` trait represents objects that act as a namespace for imports.
|
||||
/// For example, an `Instance` or `Namespace` could be
|
||||
/// considered namespaces that could provide imports to an instance.
|
||||
pub trait LikeNamespace {
|
||||
/// Gets an export by name.
|
||||
fn get_namespace_export(&self, name: &str) -> Option<Export>;
|
||||
/// Gets all exports in the namespace.
|
||||
fn get_namespace_exports(&self) -> Vec<(String, Export)>;
|
||||
}
|
||||
|
||||
/// All of the import data used when instantiating.
|
||||
///
|
||||
/// It's suggested that you use the [`imports!`] macro
|
||||
/// instead of creating an `ImportObject` by hand.
|
||||
///
|
||||
/// [`imports!`]: macro.imports.html
|
||||
///
|
||||
/// # Usage:
|
||||
/// ```ignore
|
||||
/// use wasmer::{ImportObject, Exports};
|
||||
///
|
||||
/// let mut import_object = ImportObject::new();
|
||||
/// let mut env = Exports::new();
|
||||
///
|
||||
/// env.insert("foo", func!(foo));
|
||||
/// import_object.register("env", env);
|
||||
///
|
||||
/// fn foo(n: i32) -> i32 {
|
||||
/// n
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct ImportObject {
|
||||
map: Arc<Mutex<HashMap<String, Box<dyn LikeNamespace>>>>,
|
||||
pub(crate) state_creator: Option<Arc<dyn Fn() -> (*mut c_void, fn(*mut c_void)) + 'static>>,
|
||||
/// Allow missing functions to be generated and instantiation to continue when required
|
||||
/// functions are not provided.
|
||||
pub allow_missing_functions: bool,
|
||||
}
|
||||
|
||||
impl ImportObject {
|
||||
/// Create a new `ImportObject`.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
map: Arc::new(Mutex::new(HashMap::new())),
|
||||
state_creator: None,
|
||||
allow_missing_functions: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets an export given a module and a name
|
||||
///
|
||||
/// # Usage
|
||||
/// ```ignore
|
||||
/// # use wasmer_runtime::{ImportObject, Instance, Namespace};
|
||||
/// let mut import_object = ImportObject::new();
|
||||
/// import_object.get_export("module", "name");
|
||||
/// ```
|
||||
pub fn get_export(&self, module: &str, name: &str) -> Option<Export> {
|
||||
let guard = self.map.lock().unwrap();
|
||||
let map_ref = guard.borrow();
|
||||
if map_ref.contains_key(module) {
|
||||
let namespace = map_ref[module].as_ref();
|
||||
return namespace.get_namespace_export(name);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns true if the ImportObject contains namespace with the provided name.
|
||||
pub fn contains_namespace(&self, name: &str) -> bool {
|
||||
self.map.lock().unwrap().borrow().contains_key(name)
|
||||
}
|
||||
|
||||
/// Create a new `ImportObject` which generates data from the provided state creator.
|
||||
pub fn new_with_data<F>(state_creator: F) -> Self
|
||||
where
|
||||
F: Fn() -> (*mut c_void, fn(*mut c_void)) + 'static,
|
||||
{
|
||||
Self {
|
||||
map: Arc::new(Mutex::new(HashMap::new())),
|
||||
state_creator: Some(Arc::new(state_creator)),
|
||||
allow_missing_functions: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls the state creator
|
||||
pub fn call_state_creator(&self) -> Option<(*mut c_void, fn(*mut c_void))> {
|
||||
self.state_creator.as_ref().map(|state_gen| state_gen())
|
||||
}
|
||||
|
||||
/// Register anything that implements `LikeNamespace` as a namespace.
|
||||
///
|
||||
/// # Usage:
|
||||
/// ```ignore
|
||||
/// # use wasmer_runtime::{ImportObject, Instance, Namespace};
|
||||
/// let mut import_object = ImportObject::new();
|
||||
///
|
||||
/// import_object.register("namespace0", instance);
|
||||
/// import_object.register("namespace1", namespace);
|
||||
/// // ...
|
||||
/// ```
|
||||
pub fn register<S, N>(&mut self, name: S, namespace: N) -> Option<Box<dyn LikeNamespace>>
|
||||
where
|
||||
S: Into<String>,
|
||||
N: LikeNamespace + 'static,
|
||||
{
|
||||
let mut guard = self.map.lock().unwrap();
|
||||
let map = guard.borrow_mut();
|
||||
|
||||
match map.entry(name.into()) {
|
||||
Entry::Vacant(empty) => {
|
||||
empty.insert(Box::new(namespace));
|
||||
None
|
||||
}
|
||||
Entry::Occupied(mut occupied) => Some(occupied.insert(Box::new(namespace))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a clone ref of this namespace.
|
||||
pub fn clone_ref(&self) -> Self {
|
||||
Self {
|
||||
map: Arc::clone(&self.map),
|
||||
state_creator: self.state_creator.clone(),
|
||||
allow_missing_functions: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_objects(&self) -> VecDeque<((String, String), Export)> {
|
||||
let mut out = VecDeque::new();
|
||||
let guard = self.map.lock().unwrap();
|
||||
let map = guard.borrow();
|
||||
for (name, ns) in map.iter() {
|
||||
for (id, exp) in ns.get_namespace_exports() {
|
||||
out.push_back(((name.clone(), id), exp));
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolver for ImportObject {
|
||||
fn resolve(&self, _idx: u32, module: &str, name: &str) -> Option<Export> {
|
||||
self.get_export(module, name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator for an `ImportObject`'s exports.
|
||||
pub struct ImportObjectIterator {
|
||||
elements: VecDeque<((String, String), Export)>,
|
||||
}
|
||||
|
||||
impl Iterator for ImportObjectIterator {
|
||||
type Item = ((String, String), Export);
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.elements.pop_front()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for ImportObject {
|
||||
type IntoIter = ImportObjectIterator;
|
||||
type Item = ((String, String), Export);
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
ImportObjectIterator {
|
||||
elements: self.get_objects(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<((String, String), Export)> for ImportObject {
|
||||
fn extend<T: IntoIterator<Item = ((String, String), Export)>>(&mut self, iter: T) {
|
||||
unimplemented!("Extend not yet implemented");
|
||||
}
|
||||
}
|
||||
|
||||
// The import! macro for ImportObject
|
||||
|
||||
/// Generate an [`ImportObject`] easily with the `imports!` macro.
|
||||
///
|
||||
/// [`ImportObject`]: struct.ImportObject.html
|
||||
///
|
||||
///
|
||||
/// # Usage:
|
||||
/// ```ignore
|
||||
/// use wasmer::{imports, func};
|
||||
///
|
||||
/// let import_object = imports! {
|
||||
/// "env" => {
|
||||
/// "foo" => func!(foo),
|
||||
/// },
|
||||
/// };
|
||||
///
|
||||
/// fn foo(n: i32) -> i32 {
|
||||
/// n
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! imports {
|
||||
( $( $ns_name:expr => $ns:tt, )* ) => {{
|
||||
use $crate::ImportObject;
|
||||
|
||||
let mut import_object = ImportObject::new();
|
||||
|
||||
$({
|
||||
let ns = $crate::import_namespace!($ns);
|
||||
|
||||
import_object.register($ns_name, ns);
|
||||
})*
|
||||
|
||||
import_object
|
||||
}};
|
||||
}
|
||||
|
||||
/// Generate an [`Namespace`] easily with the `namespace!` macro.
|
||||
///
|
||||
/// [`Namespace`]: struct.Namespace.html
|
||||
///
|
||||
///
|
||||
/// # Usage:
|
||||
/// ```ignore
|
||||
/// use wasmer::{namespace, func};
|
||||
///
|
||||
/// let env = namespace! {
|
||||
/// "foo" => func!(foo),
|
||||
/// };
|
||||
///
|
||||
/// fn foo(n: i32) -> i32 {
|
||||
/// n
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! namespace {
|
||||
($( $imp_name:expr => $import_item:expr, )*) => {
|
||||
$crate::import_namespace!({ $( $imp_name => $import_item, )* })
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! import_namespace {
|
||||
( { $( $imp_name:expr => $import_item:expr, )* } ) => {{
|
||||
let mut ns = $crate::Exports::new();
|
||||
$(
|
||||
ns.insert($imp_name, $import_item);
|
||||
)*
|
||||
ns
|
||||
}};
|
||||
($ns:ident) => {
|
||||
$ns
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{Global, Store, Val, ValType};
|
||||
use wasm_common::{GlobalType, Mutability, Type, Value};
|
||||
use wasmer_runtime::Export;
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn extending_works() {
|
||||
let store = Store::default();
|
||||
let g = Global::new(&store, Val::I32(0));
|
||||
|
||||
let mut imports1 = imports! {
|
||||
"dog" => {
|
||||
"happy" => g.clone(),
|
||||
},
|
||||
};
|
||||
|
||||
let imports2 = imports! {
|
||||
"dog" => {
|
||||
"small" => g.clone(),
|
||||
},
|
||||
"cat" => {
|
||||
"small" => g.clone(),
|
||||
},
|
||||
};
|
||||
|
||||
imports1.extend(imports2);
|
||||
|
||||
let small_cat_export = imports1.get_export("cat", "small");
|
||||
assert!(small_cat_export.is_some());
|
||||
|
||||
let happy = imports1.get_export("dog", "happy");
|
||||
let small = imports1.get_export("dog", "small");
|
||||
assert!(happy.is_some());
|
||||
assert!(small.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn extending_conflict_overwrites() {
|
||||
let store = Store::default();
|
||||
let g1 = Global::new(&store, Val::I32(0));
|
||||
let g2 = Global::new(&store, Val::I64(0));
|
||||
|
||||
let mut imports1 = imports! {
|
||||
"dog" => {
|
||||
"happy" => g1,
|
||||
},
|
||||
};
|
||||
|
||||
let imports2 = imports! {
|
||||
"dog" => {
|
||||
"happy" => g2,
|
||||
},
|
||||
};
|
||||
|
||||
imports1.extend(imports2);
|
||||
let happy_dog_entry = imports1.get_export("dog", "happy").unwrap();
|
||||
|
||||
assert!(if let Export::Global(happy_dog_global) = happy_dog_entry {
|
||||
happy_dog_global.global.ty == Type::I64
|
||||
} else {
|
||||
false
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn namespace() {
|
||||
let store = Store::default();
|
||||
let g1 = Global::new(&store, Val::I32(0));
|
||||
let namespace = namespace! {
|
||||
"happy" => g1,
|
||||
};
|
||||
let imports1 = imports! {
|
||||
"dog" => namespace,
|
||||
};
|
||||
|
||||
let happy_dog_entry = imports1.get_export("dog", "happy").unwrap();
|
||||
|
||||
assert!(if let Export::Global(happy_dog_global) = happy_dog_entry {
|
||||
happy_dog_global.global.ty == Type::I32
|
||||
} else {
|
||||
false
|
||||
});
|
||||
}
|
||||
}
|
87
lib/api/src/instance.rs
Normal file
@ -0,0 +1,87 @@
|
||||
use crate::exports::{Exportable, Exports};
|
||||
use crate::externals::Extern;
|
||||
use crate::import_object::{ImportObject, LikeNamespace};
|
||||
use crate::imports;
|
||||
use crate::module::Module;
|
||||
use crate::store::Store;
|
||||
use crate::RuntimeError;
|
||||
use crate::{InstantiationError, LinkError};
|
||||
use std::collections::HashMap;
|
||||
use wasm_common::Features;
|
||||
use wasmer_jit::{CompiledModule, CompilerConfig, Resolver};
|
||||
use wasmer_runtime::{Export, InstanceHandle, SignatureRegistry};
|
||||
|
||||
/// A WebAssembly Instance is a stateful, executable
|
||||
/// instance of a WebAssembly [`Module`].
|
||||
///
|
||||
/// Instance objects contain all the exported WebAssembly
|
||||
/// functions, memories, tables and globals that allow
|
||||
/// interacting with WebAssembly.
|
||||
#[derive(Clone)]
|
||||
pub struct Instance {
|
||||
handle: InstanceHandle,
|
||||
module: Module,
|
||||
/// The exports for an instance.
|
||||
pub exports: Exports,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
/// Creates a new `Instance` from a WebAssembly [`Module`] and a
|
||||
/// set of imports resolved by the [`Resolver`].
|
||||
///
|
||||
/// The resolver can be anything that implements the [`Resolver`] trait,
|
||||
/// so you can plug custom resolution for the imports.
|
||||
///
|
||||
/// The [`ImportObject`] is the easiest way to provide imports to the instance.
|
||||
///
|
||||
/// ```
|
||||
/// let store = Store::default();
|
||||
/// let module = Module::new(store, "(module)");
|
||||
/// let imports = imports!{
|
||||
/// "host" => {
|
||||
/// "var" => Global::new(Value::I32(2))
|
||||
/// }
|
||||
/// };
|
||||
/// let instance = Instance::new(&module, &imports);
|
||||
/// ```
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
/// The function can return [`InstantiationErrors`].
|
||||
///
|
||||
/// Those are, as defined by the spec:
|
||||
/// * Link errors that happen when plugging the imports into the instance
|
||||
/// * Runtime errors that happen when running the module `start` function.
|
||||
pub fn new(module: &Module, resolver: &dyn Resolver) -> Result<Instance, InstantiationError> {
|
||||
let store = module.store();
|
||||
|
||||
let handle = store
|
||||
.engine()
|
||||
.instantiate(module.compiled_module(), resolver)?;
|
||||
|
||||
let exports = module
|
||||
.exports()
|
||||
.map(|export| {
|
||||
let name = export.name().to_string();
|
||||
let export = handle.lookup(&name).expect("export");
|
||||
let extern_ = Extern::from_export(store, export.clone());
|
||||
(name.to_string(), extern_)
|
||||
})
|
||||
.collect::<Exports>();
|
||||
|
||||
Ok(Instance {
|
||||
handle,
|
||||
module: module.clone(),
|
||||
exports,
|
||||
})
|
||||
}
|
||||
|
||||
/// Gets the [`Module`] associated with this instance.
|
||||
pub fn module(&self) -> &Module {
|
||||
&self.module
|
||||
}
|
||||
|
||||
pub fn store(&self) -> &Store {
|
||||
self.module.store()
|
||||
}
|
||||
}
|
29
lib/api/src/lib.rs
Normal file
@ -0,0 +1,29 @@
|
||||
//! Wasmer API
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
|
||||
mod exports;
|
||||
mod externals;
|
||||
mod import_object;
|
||||
mod instance;
|
||||
mod module;
|
||||
mod store;
|
||||
mod types;
|
||||
|
||||
pub use crate::exports::{ExportError, Exportable, Exports};
|
||||
pub use crate::externals::{Extern, Func, Global, Memory, Table};
|
||||
#[macro_use]
|
||||
pub use crate::import_object::{ImportObject, ImportObjectIterator, LikeNamespace};
|
||||
pub use crate::instance::Instance;
|
||||
pub use crate::module::Module;
|
||||
pub use crate::store::{Engine, Store, StoreObject};
|
||||
pub use crate::types::{
|
||||
AnyRef, ExportType, ExternType, FuncType, GlobalType, HostInfo, HostRef, ImportType,
|
||||
MemoryType, Mutability, TableType, Val, ValType,
|
||||
};
|
||||
pub use wasmer_compiler_cranelift::CraneliftConfig as Config;
|
||||
pub use wasmer_jit::{
|
||||
CompilerConfig, DeserializeError, InstantiationError, LinkError, RuntimeError, SerializeError,
|
||||
};
|
||||
|
||||
/// Version number of this crate.
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
275
lib/api/src/module.rs
Normal file
@ -0,0 +1,275 @@
|
||||
use crate::store::Store;
|
||||
use crate::types::{ExportType, ImportType};
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
use wasmer_compiler::{CompileError, WasmError};
|
||||
use wasmer_jit::{CompiledModule, DeserializeError, SerializeError};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum IoCompileError {
|
||||
/// An IO error
|
||||
#[error(transparent)]
|
||||
Io(#[from] io::Error),
|
||||
/// A compilation error
|
||||
#[error(transparent)]
|
||||
Compile(#[from] CompileError),
|
||||
}
|
||||
|
||||
/// A WebAssembly Module contains stateless WebAssembly
|
||||
/// code that has already been compiled and can be instantiated
|
||||
/// multiple times.
|
||||
///
|
||||
/// ## Cloning a module
|
||||
///
|
||||
/// Cloning a moudle is cheap: it does a shallow copy of the compiled
|
||||
/// contents rather than a deep copy.
|
||||
#[derive(Clone)]
|
||||
pub struct Module {
|
||||
store: Store,
|
||||
compiled: Arc<CompiledModule>,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
/// Creates a new WebAssembly Module given the configuration
|
||||
/// in the store.
|
||||
///
|
||||
/// If the provided bytes are not WebAssembly-like (start with `b"\0asm"`),
|
||||
/// and the "wat" feature is enabled for this crate, this function will try to
|
||||
/// to convert the bytes assuming they correspond to the WebAssembly text
|
||||
/// format.
|
||||
///
|
||||
/// ## Security
|
||||
///
|
||||
/// Before the code is compiled, it will be validated using the store
|
||||
/// features.
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
/// Creating a WebAssembly module from bytecode can result in a
|
||||
/// [`CompileError`] since this operation requires to transorm the Wasm
|
||||
/// bytecode into code the machine can easily execute (normally through a JIT).
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// Reading from a WAT file.
|
||||
///
|
||||
/// ```
|
||||
/// let wat = "(module)";
|
||||
/// let module = Module::new(&store, wat)?;
|
||||
/// ```
|
||||
///
|
||||
/// Reading from bytes:
|
||||
///
|
||||
/// ```
|
||||
/// let bytes: Vec<u8> = vec![];
|
||||
/// let module = Module::new(&store, bytes)?;
|
||||
/// ```
|
||||
pub fn new(store: &Store, bytes: impl AsRef<[u8]>) -> Result<Module, CompileError> {
|
||||
// We try to parse it with WAT: it will be a no-op on
|
||||
// wasm files.
|
||||
if bytes.as_ref().starts_with(b"\0asm") {
|
||||
return Module::from_binary(store, bytes.as_ref());
|
||||
}
|
||||
|
||||
#[cfg(feature = "wat")]
|
||||
{
|
||||
let bytes = wat::parse_bytes(bytes.as_ref())
|
||||
.map_err(|e| CompileError::Wasm(WasmError::Generic(format!("{}", e))))?;
|
||||
// We can assume the binary is valid WebAssembly if returned
|
||||
// without errors from from wat. However, by skipping validation
|
||||
// we are not checking if it's using WebAssembly features not enabled
|
||||
// in the store.
|
||||
// This is a good tradeoff, as we can assume the "wat" feature is only
|
||||
// going to be used in development mode.
|
||||
return unsafe { Module::from_binary_unchecked(store, bytes.as_ref()) };
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_file(store: &Store, file: impl AsRef<Path>) -> Result<Module, IoCompileError> {
|
||||
let file_ref = file.as_ref();
|
||||
let canonical = file_ref.canonicalize()?;
|
||||
let wasm_bytes = std::fs::read(file_ref)?;
|
||||
let mut module = Module::new(store, &wasm_bytes)?;
|
||||
// Set the module name to the absolute path of the filename.
|
||||
// This is useful for debugging the stack traces.
|
||||
let filename = canonical.as_path().to_str().unwrap();
|
||||
module.set_name(filename);
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
/// Creates a new WebAssembly module from a binary.
|
||||
///
|
||||
/// Opposed to [`Module::new`], this function is not compatible with
|
||||
/// the WebAssembly text format (if the "wat" feature is enabled for
|
||||
/// this crate).
|
||||
pub fn from_binary(store: &Store, binary: &[u8]) -> Result<Module, CompileError> {
|
||||
Module::validate(store, binary)?;
|
||||
unsafe { Module::from_binary_unchecked(store, binary) }
|
||||
}
|
||||
|
||||
/// Creates a new WebAssembly module skipping any kind of validation.
|
||||
///
|
||||
/// This can speed up compilation time a bit, but it should be only used
|
||||
/// in environments where the WebAssembly modules are trusted and validated
|
||||
/// beforehand.
|
||||
pub unsafe fn from_binary_unchecked(
|
||||
store: &Store,
|
||||
binary: &[u8],
|
||||
) -> Result<Module, CompileError> {
|
||||
let module = Module::compile(store, binary)?;
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
/// Validates a new WebAssembly Module given the configuration
|
||||
/// in the Store.
|
||||
///
|
||||
/// This validation is normally pretty fast and checks the enabled
|
||||
/// WebAssembly features in the Store Engine to assure deterministic
|
||||
/// validation of the Module.
|
||||
pub fn validate(store: &Store, binary: &[u8]) -> Result<(), CompileError> {
|
||||
store.engine().validate(binary)
|
||||
}
|
||||
|
||||
unsafe fn compile(store: &Store, binary: &[u8]) -> Result<Self, CompileError> {
|
||||
let compiled = store.engine().compile(binary)?;
|
||||
Ok(Self::from_compiled_module(store, compiled))
|
||||
}
|
||||
|
||||
/// Serializes a module into it a propietary serializable format,
|
||||
/// so it can be used later by [`Module::deserialize`].
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// ```ignore
|
||||
/// # use wasmer::*;
|
||||
/// # let store = Store::default();
|
||||
/// # let module = Module::from_file(&store, "path/to/foo.wasm")?;
|
||||
/// let serialized = module.serialize()?;
|
||||
/// ```
|
||||
pub fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
|
||||
self.store.engine().serialize(self.compiled_module())
|
||||
}
|
||||
|
||||
/// Deserializes a a serialized Module binary into a `Module`.
|
||||
/// > Note: the module has to be serialized before with the `serialize` method.
|
||||
///
|
||||
/// # Unsafety
|
||||
///
|
||||
/// This function is inherently `unsafe` as the provided bytes:
|
||||
/// 1. Are going to be deserialized directly into Rust objects.
|
||||
/// 2. Contains the function assembly bodies and, if intercepted,
|
||||
/// a malicious actor could inject code into executable
|
||||
/// memory.
|
||||
///
|
||||
/// And as such, the `deserialize` method is unsafe.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// ```ignore
|
||||
/// # use wasmer::*;
|
||||
/// # let store = Store::default();
|
||||
/// let module = Module::deserialize(&store, serialized_data)?;
|
||||
/// ```
|
||||
pub unsafe fn deserialize(store: &Store, bytes: &[u8]) -> Result<Self, DeserializeError> {
|
||||
let compiled = store.engine().deserialize(bytes)?;
|
||||
Ok(Self::from_compiled_module(store, compiled))
|
||||
}
|
||||
|
||||
fn from_compiled_module(store: &Store, compiled: CompiledModule) -> Self {
|
||||
Module {
|
||||
store: store.clone(),
|
||||
compiled: Arc::new(compiled),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn compiled_module(&self) -> &CompiledModule {
|
||||
&self.compiled
|
||||
}
|
||||
|
||||
/// Returns the name of the current module.
|
||||
///
|
||||
/// This name is normally set in the WebAssembly bytecode by some
|
||||
/// compilers, but can be also overwritten using the [`Module::set_name`] method.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// let wat = "(module $moduleName)";
|
||||
/// let module = Module::new(&store, wat)?;
|
||||
/// assert_eq!(module.name(), Some("moduleName"));
|
||||
/// ```
|
||||
pub fn name(&self) -> Option<&str> {
|
||||
self.compiled.module().name.as_deref()
|
||||
}
|
||||
|
||||
/// Sets the name of the current module.
|
||||
///
|
||||
/// This is normally useful for stacktraces and debugging.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// let wat = "(module)";
|
||||
/// let module = Module::new(&store, wat)?;
|
||||
/// assert_eq!(module.name(), None);
|
||||
/// module.set_name("foo");
|
||||
/// assert_eq!(module.name(), Some("foo"));
|
||||
/// ```
|
||||
pub fn set_name(&mut self, name: &str) {
|
||||
let compiled = Arc::get_mut(&mut self.compiled).unwrap();
|
||||
Arc::get_mut(compiled.module_mut()).unwrap().name = Some(name.to_string());
|
||||
}
|
||||
|
||||
/// Returns an iterator over the imported types in the Module.
|
||||
///
|
||||
/// The order of the imports is guaranteed to be the same as in the
|
||||
/// WebAssembly bytecode.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # let store = Store::default();
|
||||
/// let wat = r#"(module
|
||||
/// (import "host" "func1" (func))
|
||||
/// (import "host" "func2" (func))
|
||||
/// )"#;
|
||||
/// let module = Module::new(&store, wat)?;
|
||||
/// for import in module.imports() {
|
||||
/// assert_eq!(import.module(), "host");
|
||||
/// assert!(import.name().contains("func"));
|
||||
/// import.ty();
|
||||
/// }
|
||||
/// ```
|
||||
pub fn imports<'a>(&'a self) -> impl Iterator<Item = ImportType> + 'a {
|
||||
self.compiled.module_ref().imports()
|
||||
}
|
||||
|
||||
/// Returns an iterator over the exported types in the Module.
|
||||
///
|
||||
/// The order of the exports is guaranteed to be the same as in the
|
||||
/// WebAssembly bytecode.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # let store = Store::default();
|
||||
/// let wat = r#"(module
|
||||
/// (func (export "namedfunc"))
|
||||
/// (memory (export "namedmemory") 1)
|
||||
/// )"#;
|
||||
/// let module = Module::new(&store, wat)?;
|
||||
/// for import in module.exports() {
|
||||
/// assert_eq!(export.name().contains("named"));
|
||||
/// export.ty();
|
||||
/// }
|
||||
/// ```
|
||||
pub fn exports<'a>(&'a self) -> impl Iterator<Item = ExportType> + 'a {
|
||||
self.compiled.module_ref().exports()
|
||||
}
|
||||
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.store
|
||||
}
|
||||
}
|
21
lib/api/src/resolver.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use wasmer_jit::Resolver;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
pub struct IndexResolver {
|
||||
externs: Vec<Extern>,
|
||||
}
|
||||
impl Resolver for IndexResolver {
|
||||
fn resolve(&self, index: u32, _module: &str, _name: &str) -> Option<Export> {
|
||||
self.externs.get(index as usize).map(|extern_| extern_.to_export())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Extern> for IndexResolver {
|
||||
fn from_iter<I: IntoIterator<Item = Extern>>(iter: I) -> Self {
|
||||
let mut externs = Vec::new();
|
||||
for extern_ in iter {
|
||||
externs.push(extern_);
|
||||
}
|
||||
IndexResolver { externs }
|
||||
}
|
||||
}
|
36
lib/api/src/store.rs
Normal file
@ -0,0 +1,36 @@
|
||||
use std::sync::Arc;
|
||||
use wasmer_compiler_cranelift::CraneliftConfig;
|
||||
use wasmer_jit::JITEngine;
|
||||
|
||||
pub type Engine = JITEngine;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Store {
|
||||
engine: Arc<Engine>,
|
||||
}
|
||||
|
||||
impl Store {
|
||||
pub fn new(engine: &Engine) -> Store {
|
||||
Store {
|
||||
engine: Arc::new(engine.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn engine(&self) -> &Engine {
|
||||
&self.engine
|
||||
}
|
||||
|
||||
pub fn same(a: &Store, b: &Store) -> bool {
|
||||
Arc::ptr_eq(&a.engine, &b.engine)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Store {
|
||||
fn default() -> Store {
|
||||
Store::new(&Engine::new(&CraneliftConfig::default()))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait StoreObject {
|
||||
fn comes_from_same_store(&self, store: &Store) -> bool;
|
||||
}
|
72
lib/api/src/types.rs
Normal file
@ -0,0 +1,72 @@
|
||||
use crate::externals::Func;
|
||||
use crate::store::{Store, StoreObject};
|
||||
use crate::RuntimeError;
|
||||
use std::ptr;
|
||||
use wasm_common::Value;
|
||||
pub use wasm_common::{
|
||||
AnyRef, ExportType, ExternType, FuncType, GlobalType, HostInfo, HostRef, ImportType,
|
||||
MemoryType, Mutability, TableType, Type as ValType,
|
||||
};
|
||||
|
||||
pub type Val = Value<Func>;
|
||||
|
||||
impl StoreObject for Val {
|
||||
fn comes_from_same_store(&self, store: &Store) -> bool {
|
||||
match self {
|
||||
Val::FuncRef(f) => Store::same(store, f.store()),
|
||||
Val::AnyRef(AnyRef::Ref(_)) | Val::AnyRef(AnyRef::Other(_)) => false,
|
||||
Val::AnyRef(AnyRef::Null) => true,
|
||||
Val::I32(_) | Val::I64(_) | Val::F32(_) | Val::F64(_) | Val::V128(_) => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Func> for Val {
|
||||
fn from(val: Func) -> Val {
|
||||
Val::FuncRef(val)
|
||||
}
|
||||
}
|
||||
|
||||
/// It provides useful functions for converting back and forth
|
||||
/// from [`Val`] into `AnyFunc`.
|
||||
pub trait ValAnyFunc {
|
||||
fn into_checked_anyfunc(
|
||||
&self,
|
||||
store: &Store,
|
||||
) -> Result<wasmer_runtime::VMCallerCheckedAnyfunc, RuntimeError>;
|
||||
|
||||
fn from_checked_anyfunc(item: wasmer_runtime::VMCallerCheckedAnyfunc, store: &Store) -> Self;
|
||||
}
|
||||
|
||||
impl ValAnyFunc for Val {
|
||||
fn into_checked_anyfunc(
|
||||
&self,
|
||||
store: &Store,
|
||||
) -> Result<wasmer_runtime::VMCallerCheckedAnyfunc, RuntimeError> {
|
||||
if !self.comes_from_same_store(store) {
|
||||
return Err(RuntimeError::new("cross-`Store` values are not supported"));
|
||||
}
|
||||
Ok(match self {
|
||||
Val::AnyRef(AnyRef::Null) => wasmer_runtime::VMCallerCheckedAnyfunc {
|
||||
func_ptr: ptr::null(),
|
||||
type_index: wasmer_runtime::VMSharedSignatureIndex::default(),
|
||||
vmctx: ptr::null_mut(),
|
||||
},
|
||||
Val::FuncRef(f) => f.checked_anyfunc(),
|
||||
_ => return Err(RuntimeError::new("val is not funcref")),
|
||||
})
|
||||
}
|
||||
|
||||
fn from_checked_anyfunc(item: wasmer_runtime::VMCallerCheckedAnyfunc, store: &Store) -> Val {
|
||||
if item.type_index == wasmer_runtime::VMSharedSignatureIndex::default() {
|
||||
Val::AnyRef(AnyRef::Null);
|
||||
}
|
||||
let export = wasmer_runtime::ExportFunction {
|
||||
address: item.func_ptr,
|
||||
signature: item.type_index,
|
||||
vmctx: item.vmctx,
|
||||
};
|
||||
let f = Func::from_export(store, export);
|
||||
Val::FuncRef(f)
|
||||
}
|
||||
}
|
3
lib/compiler-cranelift/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
target/
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
47
lib/compiler-cranelift/Cargo.toml
Normal file
@ -0,0 +1,47 @@
|
||||
[package]
|
||||
name = "wasmer-compiler-cranelift"
|
||||
version = "0.16.2"
|
||||
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
description = "Standalone environment support for WebAsssembly code in Cranelift"
|
||||
license = "MIT OR (Apache-2.0 WITH LLVM-exception)"
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
documentation = "https://docs.rs/wasmer-compiler-cranelift/"
|
||||
categories = ["wasm"]
|
||||
keywords = ["webassembly", "wasm"]
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
hashbrown = { version = "0.7.1", optional = true }
|
||||
log = { version = "0.4.8", default-features = false }
|
||||
cranelift-codegen = { version = "0.62.0", default-features = false }
|
||||
cranelift-frontend = { version = "0.62.0", default-features = false }
|
||||
wasmer-compiler = { path = "../compiler", version = "0.16.2", default-features = false }
|
||||
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
|
||||
wasm-common = { path = "../wasm-common", version = "0.16.2", default-features = false }
|
||||
wasmparser = "0.51.4"
|
||||
rayon = "1.3.0"
|
||||
serde = { version = "1.0.106", features = ["derive"] }
|
||||
more-asserts = "0.2.1"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = "0.3.8"
|
||||
|
||||
[target.'cfg(not(target_os = "windows"))'.dependencies]
|
||||
libc = "0.2.69"
|
||||
errno = "0.2.5"
|
||||
|
||||
[dev-dependencies]
|
||||
target-lexicon = { version = "0.10.0", default-features = false }
|
||||
cranelift-codegen = { version = "0.62.0", features = ["enable-serde", "all-arch"] }
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
[features]
|
||||
default = ["std", "enable-serde", "unwind"]
|
||||
unwind = ["cranelift-codegen/unwind"]
|
||||
enable-serde = ["wasmer-compiler/enable-serde", "cranelift-codegen/enable-serde", "wasm-common/enable-serde"]
|
||||
std = ["cranelift-codegen/std", "cranelift-frontend/std", "wasmer-compiler/std", "wasm-common/std"]
|
||||
core = ["hashbrown", "cranelift-codegen/core", "cranelift-frontend/core"]
|
2
lib/compiler-cranelift/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
This is the `wasmer-compiler-cranelift` crate, which contains the
|
||||
Cranelift compiler implementation.
|
10
lib/compiler-cranelift/build.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use std::process::Command;
|
||||
use std::str;
|
||||
|
||||
fn main() {
|
||||
let git_rev = match Command::new("git").args(&["rev-parse", "HEAD"]).output() {
|
||||
Ok(output) => str::from_utf8(&output.stdout).unwrap().trim().to_string(),
|
||||
Err(_) => env!("CARGO_PKG_VERSION").to_string(),
|
||||
};
|
||||
println!("cargo:rustc-env=GIT_REV={}", git_rev);
|
||||
}
|
42
lib/compiler-cranelift/src/address_map.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use cranelift_codegen::{isa, Context};
|
||||
use wasm_common::SourceLoc;
|
||||
use wasmer_compiler::{FunctionAddressMap, FunctionBodyData, InstructionAddressMap};
|
||||
|
||||
pub fn get_function_address_map<'data>(
|
||||
context: &Context,
|
||||
data: &FunctionBodyData<'data>,
|
||||
body_len: usize,
|
||||
isa: &dyn isa::TargetIsa,
|
||||
) -> FunctionAddressMap {
|
||||
let mut instructions = Vec::new();
|
||||
|
||||
let func = &context.func;
|
||||
let mut blocks = func.layout.blocks().collect::<Vec<_>>();
|
||||
blocks.sort_by_key(|block| func.offsets[*block]); // Ensure inst offsets always increase
|
||||
|
||||
let encinfo = isa.encoding_info();
|
||||
for block in blocks {
|
||||
for (offset, inst, size) in func.inst_offsets(block, &encinfo) {
|
||||
let srcloc = func.srclocs[inst];
|
||||
instructions.push(InstructionAddressMap {
|
||||
srcloc: SourceLoc::new(srcloc.bits()),
|
||||
code_offset: offset as usize,
|
||||
code_len: size as usize,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Generate artificial srcloc for function start/end to identify boundary
|
||||
// within module. Similar to FuncTranslator::cur_srcloc(): it will wrap around
|
||||
// if byte code is larger than 4 GB.
|
||||
let start_srcloc = SourceLoc::new(data.module_offset as u32);
|
||||
let end_srcloc = SourceLoc::new((data.module_offset + data.data.len()) as u32);
|
||||
|
||||
FunctionAddressMap {
|
||||
instructions,
|
||||
start_srcloc,
|
||||
end_srcloc,
|
||||
body_offset: 0,
|
||||
body_len,
|
||||
}
|
||||
}
|
294
lib/compiler-cranelift/src/compiler.rs
Normal file
@ -0,0 +1,294 @@
|
||||
//! Support for compiling with Cranelift.
|
||||
|
||||
use crate::address_map::get_function_address_map;
|
||||
use crate::config::CraneliftConfig;
|
||||
use crate::func_environ::{get_func_name, FuncEnvironment};
|
||||
use crate::trampoline::{make_wasm_trampoline, FunctionBuilderContext};
|
||||
use crate::translator::{
|
||||
irlibcall_to_libcall, irreloc_to_relocationkind, signature_to_cranelift_ir, FuncTranslator,
|
||||
};
|
||||
use crate::unwind::compiled_function_unwind_info;
|
||||
use cranelift_codegen::ir::{self, ExternalName};
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_codegen::{binemit, isa, Context};
|
||||
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
|
||||
use std::collections::HashMap;
|
||||
use wasm_common::entity::{EntityRef, PrimaryMap, SecondaryMap};
|
||||
use wasm_common::{
|
||||
DefinedFuncIndex, Features, FuncIndex, FuncType, MemoryIndex, SignatureIndex, SourceLoc,
|
||||
TableIndex,
|
||||
};
|
||||
use wasmer_compiler::CompileError;
|
||||
use wasmer_compiler::FunctionBodyData;
|
||||
use wasmer_compiler::{Compilation, CompiledFunction, Compiler, JumpTable};
|
||||
use wasmer_compiler::{CompilerConfig, ModuleTranslationState, Target};
|
||||
use wasmer_compiler::{Relocation, RelocationTarget};
|
||||
use wasmer_runtime::{MemoryPlan, Module, TablePlan};
|
||||
use wasmer_runtime::{TrapCode, TrapInformation};
|
||||
|
||||
/// Implementation of a relocation sink that just saves all the information for later
|
||||
pub struct RelocSink {
|
||||
/// Current function index.
|
||||
func_index: FuncIndex,
|
||||
|
||||
/// Relocations recorded for the function.
|
||||
pub func_relocs: Vec<Relocation>,
|
||||
}
|
||||
|
||||
impl binemit::RelocSink for RelocSink {
|
||||
fn reloc_block(
|
||||
&mut self,
|
||||
_offset: binemit::CodeOffset,
|
||||
_reloc: binemit::Reloc,
|
||||
_block_offset: binemit::CodeOffset,
|
||||
) {
|
||||
// This should use the `offsets` field of `ir::Function`.
|
||||
panic!("block headers not yet implemented");
|
||||
}
|
||||
fn reloc_external(
|
||||
&mut self,
|
||||
offset: binemit::CodeOffset,
|
||||
reloc: binemit::Reloc,
|
||||
name: &ExternalName,
|
||||
addend: binemit::Addend,
|
||||
) {
|
||||
let reloc_target = if let ExternalName::User { namespace, index } = *name {
|
||||
debug_assert_eq!(namespace, 0);
|
||||
RelocationTarget::UserFunc(FuncIndex::from_u32(index))
|
||||
} else if let ExternalName::LibCall(libcall) = *name {
|
||||
RelocationTarget::LibCall(irlibcall_to_libcall(libcall))
|
||||
} else {
|
||||
panic!("unrecognized external name")
|
||||
};
|
||||
self.func_relocs.push(Relocation {
|
||||
kind: irreloc_to_relocationkind(reloc),
|
||||
reloc_target,
|
||||
offset,
|
||||
addend,
|
||||
});
|
||||
}
|
||||
|
||||
fn reloc_constant(
|
||||
&mut self,
|
||||
_code_offset: binemit::CodeOffset,
|
||||
_reloc: binemit::Reloc,
|
||||
_constant_offset: ir::ConstantOffset,
|
||||
) {
|
||||
// Do nothing for now: cranelift emits constant data after the function code and also emits
|
||||
// function code with correct relative offsets to the constant data.
|
||||
}
|
||||
|
||||
fn reloc_jt(&mut self, offset: binemit::CodeOffset, reloc: binemit::Reloc, jt: ir::JumpTable) {
|
||||
self.func_relocs.push(Relocation {
|
||||
kind: irreloc_to_relocationkind(reloc),
|
||||
reloc_target: RelocationTarget::JumpTable(self.func_index, JumpTable::new(jt.index())),
|
||||
offset,
|
||||
addend: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl RelocSink {
|
||||
/// Return a new `RelocSink` instance.
|
||||
pub fn new(func_index: FuncIndex) -> Self {
|
||||
Self {
|
||||
func_index,
|
||||
func_relocs: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TrapSink {
|
||||
pub traps: Vec<TrapInformation>,
|
||||
}
|
||||
|
||||
impl TrapSink {
|
||||
fn new() -> Self {
|
||||
Self { traps: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
impl binemit::TrapSink for TrapSink {
|
||||
fn trap(
|
||||
&mut self,
|
||||
code_offset: binemit::CodeOffset,
|
||||
source_loc: ir::SourceLoc,
|
||||
trap_code: ir::TrapCode,
|
||||
) {
|
||||
self.traps.push(TrapInformation {
|
||||
code_offset,
|
||||
source_loc: SourceLoc::new(source_loc.bits()),
|
||||
// TODO: Translate properly environment Trapcode into cranelift IR
|
||||
trap_code: translate_ir_trapcode(trap_code),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Translates the Cranelift IR TrapCode into generic Trap Code
|
||||
fn translate_ir_trapcode(trap: ir::TrapCode) -> TrapCode {
|
||||
match trap {
|
||||
ir::TrapCode::StackOverflow => TrapCode::StackOverflow,
|
||||
ir::TrapCode::HeapOutOfBounds => TrapCode::HeapAccessOutOfBounds,
|
||||
ir::TrapCode::TableOutOfBounds => TrapCode::TableAccessOutOfBounds,
|
||||
ir::TrapCode::OutOfBounds => TrapCode::OutOfBounds,
|
||||
ir::TrapCode::IndirectCallToNull => TrapCode::IndirectCallToNull,
|
||||
ir::TrapCode::BadSignature => TrapCode::BadSignature,
|
||||
ir::TrapCode::IntegerOverflow => TrapCode::IntegerOverflow,
|
||||
ir::TrapCode::IntegerDivisionByZero => TrapCode::IntegerDivisionByZero,
|
||||
ir::TrapCode::BadConversionToInteger => TrapCode::BadConversionToInteger,
|
||||
ir::TrapCode::UnreachableCodeReached => TrapCode::UnreachableCodeReached,
|
||||
ir::TrapCode::Interrupt => TrapCode::Interrupt,
|
||||
ir::TrapCode::User(user_code) => TrapCode::User(user_code),
|
||||
}
|
||||
}
|
||||
|
||||
/// A compiler that compiles a WebAssembly module with Cranelift, translating the Wasm to Cranelift IR,
|
||||
/// optimizing it and then translating to assembly.
|
||||
pub struct CraneliftCompiler {
|
||||
isa: Box<dyn isa::TargetIsa>,
|
||||
config: CraneliftConfig,
|
||||
}
|
||||
|
||||
impl CraneliftCompiler {
|
||||
/// Creates a new Cranelift compiler
|
||||
pub fn new(config: &CraneliftConfig) -> CraneliftCompiler {
|
||||
let isa = config.isa();
|
||||
CraneliftCompiler {
|
||||
isa,
|
||||
config: config.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves the target ISA
|
||||
fn isa(&self) -> &dyn isa::TargetIsa {
|
||||
&*self.isa
|
||||
}
|
||||
|
||||
/// Gets the WebAssembly features for this Compiler
|
||||
fn config(&self) -> &CraneliftConfig {
|
||||
&self.config
|
||||
}
|
||||
}
|
||||
|
||||
impl Compiler for CraneliftCompiler {
|
||||
/// Gets the WebAssembly features for this Compiler
|
||||
fn features(&self) -> Features {
|
||||
self.config.features().clone()
|
||||
}
|
||||
|
||||
/// Gets the target associated to the Cranelift ISA.
|
||||
fn target(&self) -> Target {
|
||||
self.config.target().clone()
|
||||
}
|
||||
|
||||
/// Compile the module using Cranelift, producing a compilation result with
|
||||
/// associated relocations.
|
||||
fn compile_module(
|
||||
&self,
|
||||
module: &Module,
|
||||
module_translation: &ModuleTranslationState,
|
||||
function_body_inputs: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'_>>,
|
||||
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
|
||||
table_plans: PrimaryMap<TableIndex, TablePlan>,
|
||||
) -> Result<Compilation, CompileError> {
|
||||
let isa = self.isa();
|
||||
let frontend_config = isa.frontend_config();
|
||||
let signatures = module
|
||||
.signatures
|
||||
.iter()
|
||||
.map(|(_sig_index, func_type)| signature_to_cranelift_ir(func_type, &frontend_config))
|
||||
.collect::<PrimaryMap<SignatureIndex, ir::Signature>>();
|
||||
|
||||
let functions = function_body_inputs
|
||||
.into_iter()
|
||||
.collect::<Vec<(DefinedFuncIndex, &FunctionBodyData<'_>)>>()
|
||||
.par_iter()
|
||||
.map_init(FuncTranslator::new, |func_translator, (i, input)| {
|
||||
let func_index = module.func_index(*i);
|
||||
let mut context = Context::new();
|
||||
let mut func_env = FuncEnvironment::new(
|
||||
isa.frontend_config(),
|
||||
module,
|
||||
&signatures,
|
||||
&memory_plans,
|
||||
&table_plans,
|
||||
);
|
||||
context.func.name = get_func_name(func_index);
|
||||
context.func.signature = signatures[module.functions[func_index]].clone();
|
||||
context.func.collect_frame_layout_info();
|
||||
// if generate_debug_info {
|
||||
// context.func.collect_debug_info();
|
||||
// }
|
||||
|
||||
func_translator.translate(
|
||||
module_translation,
|
||||
input.data,
|
||||
input.module_offset,
|
||||
&mut context.func,
|
||||
&mut func_env,
|
||||
)?;
|
||||
|
||||
let mut code_buf: Vec<u8> = Vec::new();
|
||||
let mut reloc_sink = RelocSink::new(func_index);
|
||||
let mut trap_sink = TrapSink::new();
|
||||
let mut stackmap_sink = binemit::NullStackmapSink {};
|
||||
context
|
||||
.compile_and_emit(
|
||||
isa,
|
||||
&mut code_buf,
|
||||
&mut reloc_sink,
|
||||
&mut trap_sink,
|
||||
&mut stackmap_sink,
|
||||
)
|
||||
.map_err(|error| {
|
||||
CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
|
||||
})?;
|
||||
|
||||
let unwind_info = compiled_function_unwind_info(isa, &context);
|
||||
|
||||
let address_map = get_function_address_map(&context, input, code_buf.len(), isa);
|
||||
|
||||
// We transform the Cranelift JumpTable's into compiler JumpTables
|
||||
let func_jt_offsets = transform_jump_table(context.func.jt_offsets);
|
||||
|
||||
Ok(CompiledFunction {
|
||||
body: code_buf,
|
||||
jt_offsets: func_jt_offsets,
|
||||
unwind_info,
|
||||
address_map,
|
||||
relocations: reloc_sink.func_relocs,
|
||||
traps: trap_sink.traps,
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, CompileError>>()?
|
||||
.into_iter()
|
||||
.collect::<PrimaryMap<DefinedFuncIndex, _>>();
|
||||
|
||||
Ok(Compilation::new(functions))
|
||||
}
|
||||
|
||||
fn compile_wasm_trampolines(
|
||||
&self,
|
||||
signatures: &[FuncType],
|
||||
) -> Result<Vec<CompiledFunction>, CompileError> {
|
||||
signatures
|
||||
.par_iter()
|
||||
.map_init(FunctionBuilderContext::new, |mut cx, sig| {
|
||||
make_wasm_trampoline(&*self.isa, &mut cx, sig, std::mem::size_of::<u128>())
|
||||
})
|
||||
.collect::<Result<Vec<_>, CompileError>>()
|
||||
}
|
||||
}
|
||||
|
||||
/// Transforms Cranelift JumpTable's into runtime JumpTables
|
||||
pub fn transform_jump_table(
|
||||
jt_offsets: SecondaryMap<ir::JumpTable, u32>,
|
||||
) -> SecondaryMap<JumpTable, u32> {
|
||||
let mut func_jt_offsets = SecondaryMap::with_capacity(jt_offsets.capacity());
|
||||
|
||||
for (key, value) in jt_offsets.iter() {
|
||||
let new_key = JumpTable::new(key.index());
|
||||
func_jt_offsets[new_key] = *value;
|
||||
}
|
||||
func_jt_offsets
|
||||
}
|
200
lib/compiler-cranelift/src/config.rs
Normal file
@ -0,0 +1,200 @@
|
||||
use crate::compiler::CraneliftCompiler;
|
||||
use cranelift_codegen::isa::{lookup, TargetIsa};
|
||||
use cranelift_codegen::settings::{self, Configurable};
|
||||
use wasmer_compiler::{Compiler, CompilerConfig, CpuFeature, Features, Target};
|
||||
|
||||
// Runtime Environment
|
||||
|
||||
/// Possible optimization levels for the Cranelift codegen backend.
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum OptLevel {
|
||||
/// No optimizations performed, minimizes compilation time by disabling most
|
||||
/// optimizations.
|
||||
None,
|
||||
/// Generates the fastest possible code, but may take longer.
|
||||
Speed,
|
||||
/// Similar to `speed`, but also performs transformations aimed at reducing
|
||||
/// code size.
|
||||
SpeedAndSize,
|
||||
}
|
||||
|
||||
/// Global configuration options used to create an [`Engine`] and customize its
|
||||
/// behavior.
|
||||
///
|
||||
/// This structure exposed a builder-like interface and is primarily consumed by
|
||||
/// [`Engine::new()`]
|
||||
#[derive(Clone)]
|
||||
pub struct CraneliftConfig {
|
||||
/// Enable NaN canonicalization.
|
||||
///
|
||||
/// NaN canonicalization is useful when trying to run WebAssembly
|
||||
/// deterministically across different architectures.
|
||||
pub enable_nan_canonicalization: bool,
|
||||
|
||||
/// Should the Cranelift verifier be enabled.
|
||||
///
|
||||
/// The verifier assures that the generated Cranelift IR is valid.
|
||||
pub enable_verifier: bool,
|
||||
|
||||
/// The optimization levels when optimizing the IR.
|
||||
pub opt_level: OptLevel,
|
||||
|
||||
features: Features,
|
||||
target: Target,
|
||||
}
|
||||
|
||||
impl CraneliftConfig {
|
||||
/// Creates a new configuration object with the default configuration
|
||||
/// specified.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
enable_nan_canonicalization: false,
|
||||
enable_verifier: false,
|
||||
opt_level: OptLevel::Speed,
|
||||
features: Default::default(),
|
||||
target: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the ISA for the current target
|
||||
pub fn isa(&self) -> Box<dyn TargetIsa> {
|
||||
let target = self.target();
|
||||
let mut builder =
|
||||
lookup(target.triple().clone()).expect("construct Cranelift ISA for triple");
|
||||
// Cpu Features
|
||||
|
||||
let cpu_features = target.cpu_features();
|
||||
if !cpu_features.contains(CpuFeature::SSE2) {
|
||||
panic!("x86 support requires SSE2");
|
||||
}
|
||||
if cpu_features.contains(CpuFeature::SSE3) {
|
||||
builder.enable("has_sse3").expect("should be valid flag");
|
||||
}
|
||||
if cpu_features.contains(CpuFeature::SSSE3) {
|
||||
builder.enable("has_ssse3").expect("should be valid flag");
|
||||
}
|
||||
if cpu_features.contains(CpuFeature::SSE41) {
|
||||
builder.enable("has_sse41").expect("should be valid flag");
|
||||
}
|
||||
if cpu_features.contains(CpuFeature::SSE42) {
|
||||
builder.enable("has_sse42").expect("should be valid flag");
|
||||
}
|
||||
if cpu_features.contains(CpuFeature::POPCNT) {
|
||||
builder.enable("has_popcnt").expect("should be valid flag");
|
||||
}
|
||||
if cpu_features.contains(CpuFeature::AVX) {
|
||||
builder.enable("has_avx").expect("should be valid flag");
|
||||
}
|
||||
if cpu_features.contains(CpuFeature::BMI1) {
|
||||
builder.enable("has_bmi1").expect("should be valid flag");
|
||||
}
|
||||
if cpu_features.contains(CpuFeature::BMI2) {
|
||||
builder.enable("has_bmi2").expect("should be valid flag");
|
||||
}
|
||||
if cpu_features.contains(CpuFeature::AVX2) {
|
||||
builder.enable("has_avx2").expect("should be valid flag");
|
||||
}
|
||||
if cpu_features.contains(CpuFeature::AVX512DQ) {
|
||||
builder
|
||||
.enable("has_avx512dq")
|
||||
.expect("should be valid flag");
|
||||
}
|
||||
if cpu_features.contains(CpuFeature::AVX512VL) {
|
||||
builder
|
||||
.enable("has_avx512vl")
|
||||
.expect("should be valid flag");
|
||||
}
|
||||
if cpu_features.contains(CpuFeature::LZCNT) {
|
||||
builder.enable("has_lzcnt").expect("should be valid flag");
|
||||
}
|
||||
|
||||
builder.finish(self.flags())
|
||||
}
|
||||
|
||||
/// Generates the flags for the current target
|
||||
pub fn flags(&self) -> settings::Flags {
|
||||
let mut flags = settings::builder();
|
||||
|
||||
// There are two possible traps for division, and this way
|
||||
// we get the proper one if code traps.
|
||||
flags
|
||||
.enable("avoid_div_traps")
|
||||
.expect("should be valid flag");
|
||||
|
||||
// Invert cranelift's default-on verification to instead default off.
|
||||
let enable_verifier = if self.enable_verifier {
|
||||
"true"
|
||||
} else {
|
||||
"false"
|
||||
};
|
||||
flags
|
||||
.set("enable_verifier", enable_verifier)
|
||||
.expect("should be valid flag");
|
||||
|
||||
let opt_level = if self.features.simd {
|
||||
"none"
|
||||
} else {
|
||||
match self.opt_level {
|
||||
OptLevel::None => "none",
|
||||
OptLevel::Speed => "speed",
|
||||
OptLevel::SpeedAndSize => "speed_and_size",
|
||||
}
|
||||
};
|
||||
|
||||
flags
|
||||
.set("opt_level", opt_level)
|
||||
.expect("should be valid flag");
|
||||
|
||||
let enable_simd = if self.features.simd { "true" } else { "false" };
|
||||
flags
|
||||
.set("enable_simd", enable_simd)
|
||||
.expect("should be valid flag");
|
||||
|
||||
let enable_nan_canonicalization = if self.enable_nan_canonicalization {
|
||||
"true"
|
||||
} else {
|
||||
"false"
|
||||
};
|
||||
flags
|
||||
.set("enable_nan_canonicalization", enable_nan_canonicalization)
|
||||
.expect("should be valid flag");
|
||||
|
||||
settings::Flags::new(flags)
|
||||
}
|
||||
}
|
||||
|
||||
impl CompilerConfig for CraneliftConfig {
|
||||
/// Gets the WebAssembly features
|
||||
fn features(&self) -> &Features {
|
||||
&self.features
|
||||
}
|
||||
|
||||
/// Gets the WebAssembly features, mutable
|
||||
fn features_mut(&mut self) -> &mut Features {
|
||||
&mut self.features
|
||||
}
|
||||
|
||||
/// Gets the target that we will use for compiling
|
||||
/// the WebAssembly module
|
||||
fn target(&self) -> &Target {
|
||||
&self.target
|
||||
}
|
||||
|
||||
/// Gets the target that we will use for compiling
|
||||
/// the WebAssembly module, mutable
|
||||
fn target_mut(&mut self) -> &mut Target {
|
||||
&mut self.target
|
||||
}
|
||||
|
||||
/// Transform it into the compiler
|
||||
fn compiler(&self) -> Box<dyn Compiler> {
|
||||
Box::new(CraneliftCompiler::new(&self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CraneliftConfig {
|
||||
fn default() -> CraneliftConfig {
|
||||
CraneliftConfig::new()
|
||||
}
|
||||
}
|
70
lib/compiler-cranelift/src/debug/address_map.rs
Normal file
@ -0,0 +1,70 @@
|
||||
//! Data structures to provide transformation of the source
|
||||
// addresses of a WebAssembly module into the native code.
|
||||
|
||||
use cranelift_codegen::ir;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use wasm_common::entity::PrimaryMap;
|
||||
use wasm_common::{DefinedFuncIndex, SourceLoc};
|
||||
|
||||
/// Single source location to generated address mapping.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct InstructionAddressMap {
|
||||
/// Original source location.
|
||||
pub srcloc: SourceLoc,
|
||||
|
||||
/// Generated instructions offset.
|
||||
pub code_offset: usize,
|
||||
|
||||
/// Generated instructions length.
|
||||
pub code_len: usize,
|
||||
}
|
||||
|
||||
/// Function and its instructions addresses mappings.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FunctionAddressMap {
|
||||
/// Instructions maps.
|
||||
/// The array is sorted by the InstructionAddressMap::code_offset field.
|
||||
pub instructions: Vec<InstructionAddressMap>,
|
||||
|
||||
/// Function start source location (normally declaration).
|
||||
pub start_srcloc: SourceLoc,
|
||||
|
||||
/// Function end source location.
|
||||
pub end_srcloc: SourceLoc,
|
||||
|
||||
/// Generated function body offset if applicable, otherwise 0.
|
||||
pub body_offset: usize,
|
||||
|
||||
/// Generated function body length.
|
||||
pub body_len: usize,
|
||||
}
|
||||
|
||||
/// Module functions addresses mappings.
|
||||
pub type ModuleAddressMap = PrimaryMap<DefinedFuncIndex, FunctionAddressMap>;
|
||||
|
||||
/// Value ranges for functions.
|
||||
pub type ValueLabelsRanges = PrimaryMap<DefinedFuncIndex, cranelift_codegen::ValueLabelsRanges>;
|
||||
|
||||
/// Stack slots for functions.
|
||||
pub type StackSlots = PrimaryMap<DefinedFuncIndex, ir::StackSlots>;
|
||||
|
||||
/// Memory definition offset in the VMContext structure.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ModuleMemoryOffset {
|
||||
/// Not available.
|
||||
None,
|
||||
/// Offset to the defined memory.
|
||||
Defined(u32),
|
||||
/// Offset to the imported memory.
|
||||
Imported(u32),
|
||||
}
|
||||
|
||||
/// Module `vmctx` related info.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ModuleVmctxInfo {
|
||||
/// The memory definition offset in the VMContext structure.
|
||||
pub memory_offset: ModuleMemoryOffset,
|
||||
|
||||
/// The functions stack slots.
|
||||
pub stack_slots: StackSlots,
|
||||
}
|
21
lib/compiler-cranelift/src/debug/frame_layout.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use cranelift_codegen::isa::CallConv;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use wasm_common::entity::PrimaryMap;
|
||||
use wasm_common::DefinedFuncIndex;
|
||||
|
||||
pub use cranelift_codegen::ir::FrameLayoutChange;
|
||||
|
||||
/// Frame layout information: call convention and
|
||||
/// registers save/restore commands.
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||
pub struct FrameLayout {
|
||||
/// Call convention.
|
||||
pub call_conv: CallConv,
|
||||
/// Frame default/initial commands.
|
||||
pub initial_commands: Box<[FrameLayoutChange]>,
|
||||
/// Frame commands at specific offset.
|
||||
pub commands: Box<[(usize, FrameLayoutChange)]>,
|
||||
}
|
||||
|
||||
/// Functions frame layouts.
|
||||
pub type FrameLayouts = PrimaryMap<DefinedFuncIndex, FrameLayout>;
|
8
lib/compiler-cranelift/src/debug/mod.rs
Normal file
@ -0,0 +1,8 @@
|
||||
mod address_map;
|
||||
mod frame_layout;
|
||||
|
||||
pub use self::address_map::{
|
||||
FunctionAddressMap, InstructionAddressMap, ModuleAddressMap, ModuleMemoryOffset,
|
||||
ModuleVmctxInfo, ValueLabelsRanges,
|
||||
};
|
||||
pub use self::frame_layout::{FrameLayout, FrameLayoutChange, FrameLayouts};
|
1078
lib/compiler-cranelift/src/func_environ.rs
Normal file
68
lib/compiler-cranelift/src/lib.rs
Normal file
@ -0,0 +1,68 @@
|
||||
//! A WebAssembly `Compiler` implementation using Cranelift.
|
||||
//!
|
||||
//! Cranelift is a fast IR generator created by Mozilla for usage in
|
||||
//! Firefox as a next JS compiler generator.
|
||||
//!
|
||||
//! Compared to LLVM, Cranelit is a bit faster and made enterely in Rust.
|
||||
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||
#![cfg_attr(
|
||||
feature = "cargo-clippy",
|
||||
allow(clippy::new_without_default, clippy::new_without_default)
|
||||
)]
|
||||
#![cfg_attr(
|
||||
feature = "cargo-clippy",
|
||||
warn(
|
||||
clippy::float_arithmetic,
|
||||
clippy::mut_mut,
|
||||
clippy::nonminimal_bool,
|
||||
clippy::option_map_unwrap_or,
|
||||
clippy::option_map_unwrap_or_else,
|
||||
clippy::print_stdout,
|
||||
clippy::unicode_not_nfc,
|
||||
clippy::use_self
|
||||
)
|
||||
)]
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
#[macro_use]
|
||||
extern crate alloc as std;
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use hashbrown::{
|
||||
hash_map,
|
||||
hash_map::Entry::{Occupied, Vacant},
|
||||
HashMap,
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::{
|
||||
hash_map,
|
||||
hash_map::Entry::{Occupied, Vacant},
|
||||
HashMap,
|
||||
};
|
||||
|
||||
mod address_map;
|
||||
mod compiler;
|
||||
mod config;
|
||||
mod debug;
|
||||
mod func_environ;
|
||||
mod trampoline;
|
||||
mod translator;
|
||||
mod unwind;
|
||||
|
||||
pub use crate::compiler::{transform_jump_table, CraneliftCompiler};
|
||||
pub use crate::config::CraneliftConfig;
|
||||
pub use crate::debug::{FrameLayout, FrameLayoutChange, FrameLayouts};
|
||||
pub use crate::debug::{
|
||||
FunctionAddressMap, InstructionAddressMap, ModuleAddressMap, ModuleMemoryOffset,
|
||||
ModuleVmctxInfo, ValueLabelsRanges,
|
||||
};
|
||||
pub use crate::trampoline::make_wasm_trampoline;
|
||||
pub use crate::unwind::compiled_function_unwind_info;
|
||||
|
||||
/// Version number of this crate.
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
64
lib/compiler-cranelift/src/trampoline/mod.rs
Normal file
@ -0,0 +1,64 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
// mod host;
|
||||
mod wasm;
|
||||
|
||||
// pub use host::make_host_trampoline;
|
||||
pub use self::wasm::make_wasm_trampoline;
|
||||
|
||||
// TODO: Delete
|
||||
pub mod ir {
|
||||
pub use cranelift_codegen::ir::{
|
||||
ExternalName, Function, InstBuilder, MemFlags, StackSlotData, StackSlotKind,
|
||||
};
|
||||
}
|
||||
pub use cranelift_codegen::print_errors::pretty_error;
|
||||
pub use cranelift_codegen::Context;
|
||||
pub use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||
|
||||
pub mod binemit {
|
||||
pub use cranelift_codegen::binemit::NullTrapSink;
|
||||
pub use cranelift_codegen::binemit::{CodeOffset, NullStackmapSink, TrapSink};
|
||||
|
||||
use cranelift_codegen::{binemit, ir};
|
||||
|
||||
/// We don't expect trampoline compilation to produce any relocations, so
|
||||
/// this `RelocSink` just asserts that it doesn't recieve any.
|
||||
pub struct TrampolineRelocSink {}
|
||||
|
||||
impl binemit::RelocSink for TrampolineRelocSink {
|
||||
fn reloc_block(
|
||||
&mut self,
|
||||
_offset: binemit::CodeOffset,
|
||||
_reloc: binemit::Reloc,
|
||||
_block_offset: binemit::CodeOffset,
|
||||
) {
|
||||
panic!("trampoline compilation should not produce block relocs");
|
||||
}
|
||||
fn reloc_external(
|
||||
&mut self,
|
||||
_offset: binemit::CodeOffset,
|
||||
_reloc: binemit::Reloc,
|
||||
_name: &ir::ExternalName,
|
||||
_addend: binemit::Addend,
|
||||
) {
|
||||
panic!("trampoline compilation should not produce external symbol relocs");
|
||||
}
|
||||
fn reloc_constant(
|
||||
&mut self,
|
||||
_code_offset: binemit::CodeOffset,
|
||||
_reloc: binemit::Reloc,
|
||||
_constant_offset: ir::ConstantOffset,
|
||||
) {
|
||||
panic!("trampoline compilation should not produce constant relocs");
|
||||
}
|
||||
fn reloc_jt(
|
||||
&mut self,
|
||||
_offset: binemit::CodeOffset,
|
||||
_reloc: binemit::Reloc,
|
||||
_jt: ir::JumpTable,
|
||||
) {
|
||||
panic!("trampoline compilation should not produce jump table relocs");
|
||||
}
|
||||
}
|
||||
}
|
135
lib/compiler-cranelift/src/trampoline/wasm.rs
Normal file
@ -0,0 +1,135 @@
|
||||
//! A trampoline generator for calling Wasm functions easily.
|
||||
//!
|
||||
//! That way, you can start calling Wasm functions doing things like:
|
||||
//! ```ignore
|
||||
//! let my_func = instance.exports.get("func");
|
||||
//! my_func.call([1, 2])
|
||||
//! ```
|
||||
use super::binemit::TrampolineRelocSink;
|
||||
use crate::translator::signature_to_cranelift_ir;
|
||||
use crate::{compiled_function_unwind_info, transform_jump_table};
|
||||
use cranelift_codegen::ir::InstBuilder;
|
||||
use cranelift_codegen::isa::TargetIsa;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_codegen::Context;
|
||||
use cranelift_codegen::{binemit, ir};
|
||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||
use wasm_common::FuncType;
|
||||
use wasmer_compiler::FunctionAddressMap;
|
||||
use wasmer_compiler::{CompileError, CompiledFunction};
|
||||
|
||||
/// Create a trampoline for invoking a WebAssembly function.
|
||||
pub fn make_wasm_trampoline(
|
||||
isa: &dyn TargetIsa,
|
||||
fn_builder_ctx: &mut FunctionBuilderContext,
|
||||
func_type: &FuncType,
|
||||
value_size: usize,
|
||||
) -> Result<CompiledFunction, CompileError> {
|
||||
let pointer_type = isa.pointer_type();
|
||||
let frontend_config = isa.frontend_config();
|
||||
let signature = signature_to_cranelift_ir(func_type, &frontend_config);
|
||||
let mut wrapper_sig = ir::Signature::new(frontend_config.default_call_conv);
|
||||
|
||||
// Add the callee `vmctx` parameter.
|
||||
wrapper_sig.params.push(ir::AbiParam::special(
|
||||
pointer_type,
|
||||
ir::ArgumentPurpose::VMContext,
|
||||
));
|
||||
|
||||
// Add the caller `vmctx` parameter.
|
||||
wrapper_sig.params.push(ir::AbiParam::new(pointer_type));
|
||||
|
||||
// Add the `callee_address` parameter.
|
||||
wrapper_sig.params.push(ir::AbiParam::new(pointer_type));
|
||||
|
||||
// Add the `values_vec` parameter.
|
||||
wrapper_sig.params.push(ir::AbiParam::new(pointer_type));
|
||||
|
||||
let mut context = Context::new();
|
||||
context.func = ir::Function::with_name_signature(ir::ExternalName::user(0, 0), wrapper_sig);
|
||||
context.func.collect_frame_layout_info();
|
||||
|
||||
{
|
||||
let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx);
|
||||
let block0 = builder.create_block();
|
||||
|
||||
builder.append_block_params_for_function_params(block0);
|
||||
builder.switch_to_block(block0);
|
||||
builder.seal_block(block0);
|
||||
|
||||
let (vmctx_ptr_val, caller_vmctx_ptr_val, callee_value, values_vec_ptr_val) = {
|
||||
let params = builder.func.dfg.block_params(block0);
|
||||
(params[0], params[1], params[2], params[3])
|
||||
};
|
||||
|
||||
// Load the argument values out of `values_vec`.
|
||||
let mflags = ir::MemFlags::trusted();
|
||||
let callee_args = signature
|
||||
.params
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, r)| {
|
||||
match i {
|
||||
0 => vmctx_ptr_val,
|
||||
1 => caller_vmctx_ptr_val,
|
||||
_ =>
|
||||
// i - 2 because vmctx and caller vmctx aren't passed through `values_vec`.
|
||||
{
|
||||
builder.ins().load(
|
||||
r.value_type,
|
||||
mflags,
|
||||
values_vec_ptr_val,
|
||||
((i - 2) * value_size) as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let new_sig = builder.import_signature(signature.clone());
|
||||
|
||||
let call = builder
|
||||
.ins()
|
||||
.call_indirect(new_sig, callee_value, &callee_args);
|
||||
|
||||
let results = builder.func.dfg.inst_results(call).to_vec();
|
||||
|
||||
// Store the return values into `values_vec`.
|
||||
let mflags = ir::MemFlags::trusted();
|
||||
for (i, r) in results.iter().enumerate() {
|
||||
builder
|
||||
.ins()
|
||||
.store(mflags, *r, values_vec_ptr_val, (i * value_size) as i32);
|
||||
}
|
||||
|
||||
builder.ins().return_(&[]);
|
||||
builder.finalize()
|
||||
}
|
||||
|
||||
let mut code_buf = Vec::new();
|
||||
let mut reloc_sink = TrampolineRelocSink {};
|
||||
let mut trap_sink = binemit::NullTrapSink {};
|
||||
let mut stackmap_sink = binemit::NullStackmapSink {};
|
||||
context
|
||||
.compile_and_emit(
|
||||
isa,
|
||||
&mut code_buf,
|
||||
&mut reloc_sink,
|
||||
&mut trap_sink,
|
||||
&mut stackmap_sink,
|
||||
)
|
||||
.map_err(|error| CompileError::Codegen(pretty_error(&context.func, Some(isa), error)))?;
|
||||
|
||||
let unwind_info = compiled_function_unwind_info(isa, &context);
|
||||
// let address_map = get_function_address_map(&context, input, code_buf.len(), isa);
|
||||
let address_map = FunctionAddressMap::default(); // get_function_address_map(&context, input, code_buf.len(), isa);
|
||||
|
||||
Ok(CompiledFunction {
|
||||
body: code_buf,
|
||||
jt_offsets: transform_jump_table(context.func.jt_offsets),
|
||||
unwind_info,
|
||||
address_map,
|
||||
relocations: vec![],
|
||||
traps: vec![],
|
||||
})
|
||||
}
|
2132
lib/compiler-cranelift/src/translator/code_translator.rs
Normal file
391
lib/compiler-cranelift/src/translator/func_environ.rs
Normal file
@ -0,0 +1,391 @@
|
||||
//! All the runtime support necessary for the wasm to cranelift translation is formalized by the
|
||||
//! traits `FunctionEnvironment`.
|
||||
|
||||
use super::func_state::FuncTranslationState;
|
||||
use super::translation_utils::reference_type;
|
||||
use core::convert::From;
|
||||
use cranelift_codegen::cursor::FuncCursor;
|
||||
use cranelift_codegen::ir::immediates::Offset32;
|
||||
use cranelift_codegen::ir::{self, InstBuilder};
|
||||
use cranelift_codegen::isa::TargetFrontendConfig;
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
use wasm_common::{FuncIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
|
||||
use wasmer_compiler::WasmResult;
|
||||
use wasmparser::Operator;
|
||||
|
||||
/// The value of a WebAssembly global variable.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum GlobalVariable {
|
||||
/// This is a constant global with a value known at compile time.
|
||||
Const(ir::Value),
|
||||
|
||||
/// This is a variable in memory that should be referenced through a `GlobalValue`.
|
||||
Memory {
|
||||
/// The address of the global variable storage.
|
||||
gv: ir::GlobalValue,
|
||||
/// An offset to add to the address.
|
||||
offset: Offset32,
|
||||
/// The global variable's type.
|
||||
ty: ir::Type,
|
||||
},
|
||||
|
||||
/// This is a global variable that needs to be handled by the environment.
|
||||
Custom,
|
||||
}
|
||||
|
||||
/// How to return from functions.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum ReturnMode {
|
||||
/// Use normal return instructions as needed.
|
||||
NormalReturns,
|
||||
/// Use a single fallthrough return at the end of the function.
|
||||
FallthroughReturn,
|
||||
}
|
||||
|
||||
/// Environment affecting the translation of a WebAssembly.
|
||||
pub trait TargetEnvironment {
|
||||
/// Get the information needed to produce Cranelift IR for the given target.
|
||||
fn target_config(&self) -> TargetFrontendConfig;
|
||||
|
||||
/// Get the Cranelift integer type to use for native pointers.
|
||||
///
|
||||
/// This returns `I64` for 64-bit architectures and `I32` for 32-bit architectures.
|
||||
fn pointer_type(&self) -> ir::Type {
|
||||
ir::Type::int(u16::from(self.target_config().pointer_bits())).unwrap()
|
||||
}
|
||||
|
||||
/// Get the size of a native pointer, in bytes.
|
||||
fn pointer_bytes(&self) -> u8 {
|
||||
self.target_config().pointer_bytes()
|
||||
}
|
||||
|
||||
/// Get the Cranelift reference type to use for native references.
|
||||
///
|
||||
/// This returns `R64` for 64-bit architectures and `R32` for 32-bit architectures.
|
||||
fn reference_type(&self) -> ir::Type {
|
||||
reference_type(&self.target_config()).expect("expected reference type")
|
||||
}
|
||||
}
|
||||
|
||||
/// Environment affecting the translation of a single WebAssembly function.
|
||||
///
|
||||
/// A `FuncEnvironment` trait object is required to translate a WebAssembly function to Cranelift
|
||||
/// IR. The function environment provides information about the WebAssembly module as well as the
|
||||
/// runtime environment.
|
||||
pub trait FuncEnvironment: TargetEnvironment {
|
||||
/// Is the given parameter of the given function a wasm-level parameter, as opposed to a hidden
|
||||
/// parameter added for use by the implementation?
|
||||
fn is_wasm_parameter(&self, signature: &ir::Signature, index: usize) -> bool {
|
||||
signature.params[index].purpose == ir::ArgumentPurpose::Normal
|
||||
}
|
||||
|
||||
/// Is the given return of the given function a wasm-level parameter, as
|
||||
/// opposed to a hidden parameter added for use by the implementation?
|
||||
fn is_wasm_return(&self, signature: &ir::Signature, index: usize) -> bool {
|
||||
signature.returns[index].purpose == ir::ArgumentPurpose::Normal
|
||||
}
|
||||
|
||||
/// Should the code be structured to use a single `fallthrough_return` instruction at the end
|
||||
/// of the function body, rather than `return` instructions as needed? This is used by VMs
|
||||
/// to append custom epilogues.
|
||||
fn return_mode(&self) -> ReturnMode {
|
||||
ReturnMode::NormalReturns
|
||||
}
|
||||
|
||||
/// Set up the necessary preamble definitions in `func` to access the global variable
|
||||
/// identified by `index`.
|
||||
///
|
||||
/// The index space covers both imported globals and globals defined by the module.
|
||||
///
|
||||
/// Return the global variable reference that should be used to access the global and the
|
||||
/// WebAssembly type of the global.
|
||||
fn make_global(
|
||||
&mut self,
|
||||
func: &mut ir::Function,
|
||||
index: GlobalIndex,
|
||||
) -> WasmResult<GlobalVariable>;
|
||||
|
||||
/// Set up the necessary preamble definitions in `func` to access the linear memory identified
|
||||
/// by `index`.
|
||||
///
|
||||
/// The index space covers both imported and locally declared memories.
|
||||
fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> WasmResult<ir::Heap>;
|
||||
|
||||
/// Set up the necessary preamble definitions in `func` to access the table identified
|
||||
/// by `index`.
|
||||
///
|
||||
/// The index space covers both imported and locally declared tables.
|
||||
fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> WasmResult<ir::Table>;
|
||||
|
||||
/// Set up a signature definition in the preamble of `func` that can be used for an indirect
|
||||
/// call with signature `index`.
|
||||
///
|
||||
/// The signature may contain additional arguments needed for an indirect call, but the
|
||||
/// arguments marked as `ArgumentPurpose::Normal` must correspond to the WebAssembly signature
|
||||
/// arguments.
|
||||
///
|
||||
/// The signature will only be used for indirect calls, even if the module has direct function
|
||||
/// calls with the same WebAssembly type.
|
||||
fn make_indirect_sig(
|
||||
&mut self,
|
||||
func: &mut ir::Function,
|
||||
index: SignatureIndex,
|
||||
) -> WasmResult<ir::SigRef>;
|
||||
|
||||
/// Set up an external function definition in the preamble of `func` that can be used to
|
||||
/// directly call the function `index`.
|
||||
///
|
||||
/// The index space covers both imported functions and functions defined in the current module.
|
||||
///
|
||||
/// The function's signature may contain additional arguments needed for a direct call, but the
|
||||
/// arguments marked as `ArgumentPurpose::Normal` must correspond to the WebAssembly signature
|
||||
/// arguments.
|
||||
///
|
||||
/// The function's signature will only be used for direct calls, even if the module has
|
||||
/// indirect calls with the same WebAssembly type.
|
||||
fn make_direct_func(
|
||||
&mut self,
|
||||
func: &mut ir::Function,
|
||||
index: FuncIndex,
|
||||
) -> WasmResult<ir::FuncRef>;
|
||||
|
||||
/// Translate a `call_indirect` WebAssembly instruction at `pos`.
|
||||
///
|
||||
/// Insert instructions at `pos` for an indirect call to the function `callee` in the table
|
||||
/// `table_index` with WebAssembly signature `sig_index`. The `callee` value will have type
|
||||
/// `i32`.
|
||||
///
|
||||
/// The signature `sig_ref` was previously created by `make_indirect_sig()`.
|
||||
///
|
||||
/// Return the call instruction whose results are the WebAssembly return values.
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))]
|
||||
fn translate_call_indirect(
|
||||
&mut self,
|
||||
pos: FuncCursor,
|
||||
table_index: TableIndex,
|
||||
table: ir::Table,
|
||||
sig_index: SignatureIndex,
|
||||
sig_ref: ir::SigRef,
|
||||
callee: ir::Value,
|
||||
call_args: &[ir::Value],
|
||||
) -> WasmResult<ir::Inst>;
|
||||
|
||||
/// Translate a `call` WebAssembly instruction at `pos`.
|
||||
///
|
||||
/// Insert instructions at `pos` for a direct call to the function `callee_index`.
|
||||
///
|
||||
/// The function reference `callee` was previously created by `make_direct_func()`.
|
||||
///
|
||||
/// Return the call instruction whose results are the WebAssembly return values.
|
||||
fn translate_call(
|
||||
&mut self,
|
||||
mut pos: FuncCursor,
|
||||
_callee_index: FuncIndex,
|
||||
callee: ir::FuncRef,
|
||||
call_args: &[ir::Value],
|
||||
) -> WasmResult<ir::Inst> {
|
||||
Ok(pos.ins().call(callee, call_args))
|
||||
}
|
||||
|
||||
/// Translate a `memory.grow` WebAssembly instruction.
|
||||
///
|
||||
/// The `index` provided identifies the linear memory to grow, and `heap` is the heap reference
|
||||
/// returned by `make_heap` for the same index.
|
||||
///
|
||||
/// The `val` value is the requested memory size in pages.
|
||||
///
|
||||
/// Returns the old size (in pages) of the memory.
|
||||
fn translate_memory_grow(
|
||||
&mut self,
|
||||
pos: FuncCursor,
|
||||
index: MemoryIndex,
|
||||
heap: ir::Heap,
|
||||
val: ir::Value,
|
||||
) -> WasmResult<ir::Value>;
|
||||
|
||||
/// Translates a `memory.size` WebAssembly instruction.
|
||||
///
|
||||
/// The `index` provided identifies the linear memory to query, and `heap` is the heap reference
|
||||
/// returned by `make_heap` for the same index.
|
||||
///
|
||||
/// Returns the size in pages of the memory.
|
||||
fn translate_memory_size(
|
||||
&mut self,
|
||||
pos: FuncCursor,
|
||||
index: MemoryIndex,
|
||||
heap: ir::Heap,
|
||||
) -> WasmResult<ir::Value>;
|
||||
|
||||
/// Translate a `memory.copy` WebAssembly instruction.
|
||||
///
|
||||
/// The `index` provided identifies the linear memory to query, and `heap` is the heap reference
|
||||
/// returned by `make_heap` for the same index.
|
||||
fn translate_memory_copy(
|
||||
&mut self,
|
||||
pos: FuncCursor,
|
||||
index: MemoryIndex,
|
||||
heap: ir::Heap,
|
||||
dst: ir::Value,
|
||||
src: ir::Value,
|
||||
len: ir::Value,
|
||||
) -> WasmResult<()>;
|
||||
|
||||
/// Translate a `memory.fill` WebAssembly instruction.
|
||||
///
|
||||
/// The `index` provided identifies the linear memory to query, and `heap` is the heap reference
|
||||
/// returned by `make_heap` for the same index.
|
||||
fn translate_memory_fill(
|
||||
&mut self,
|
||||
pos: FuncCursor,
|
||||
index: MemoryIndex,
|
||||
heap: ir::Heap,
|
||||
dst: ir::Value,
|
||||
val: ir::Value,
|
||||
len: ir::Value,
|
||||
) -> WasmResult<()>;
|
||||
|
||||
/// Translate a `memory.init` WebAssembly instruction.
|
||||
///
|
||||
/// The `index` provided identifies the linear memory to query, and `heap` is the heap reference
|
||||
/// returned by `make_heap` for the same index. `seg_index` is the index of the segment to copy
|
||||
/// from.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn translate_memory_init(
|
||||
&mut self,
|
||||
pos: FuncCursor,
|
||||
index: MemoryIndex,
|
||||
heap: ir::Heap,
|
||||
seg_index: u32,
|
||||
dst: ir::Value,
|
||||
src: ir::Value,
|
||||
len: ir::Value,
|
||||
) -> WasmResult<()>;
|
||||
|
||||
/// Translate a `data.drop` WebAssembly instruction.
|
||||
fn translate_data_drop(&mut self, pos: FuncCursor, seg_index: u32) -> WasmResult<()>;
|
||||
|
||||
/// Translate a `table.size` WebAssembly instruction.
|
||||
fn translate_table_size(
|
||||
&mut self,
|
||||
pos: FuncCursor,
|
||||
index: TableIndex,
|
||||
table: ir::Table,
|
||||
) -> WasmResult<ir::Value>;
|
||||
|
||||
/// Translate a `table.grow` WebAssembly instruction.
|
||||
fn translate_table_grow(
|
||||
&mut self,
|
||||
pos: FuncCursor,
|
||||
table_index: u32,
|
||||
delta: ir::Value,
|
||||
init_value: ir::Value,
|
||||
) -> WasmResult<ir::Value>;
|
||||
|
||||
/// Translate a `table.get` WebAssembly instruction.
|
||||
fn translate_table_get(
|
||||
&mut self,
|
||||
pos: FuncCursor,
|
||||
table_index: u32,
|
||||
index: ir::Value,
|
||||
) -> WasmResult<ir::Value>;
|
||||
|
||||
/// Translate a `table.set` WebAssembly instruction.
|
||||
fn translate_table_set(
|
||||
&mut self,
|
||||
pos: FuncCursor,
|
||||
table_index: u32,
|
||||
value: ir::Value,
|
||||
index: ir::Value,
|
||||
) -> WasmResult<()>;
|
||||
|
||||
/// Translate a `table.copy` WebAssembly instruction.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn translate_table_copy(
|
||||
&mut self,
|
||||
pos: FuncCursor,
|
||||
dst_table_index: TableIndex,
|
||||
dst_table: ir::Table,
|
||||
src_table_index: TableIndex,
|
||||
src_table: ir::Table,
|
||||
dst: ir::Value,
|
||||
src: ir::Value,
|
||||
len: ir::Value,
|
||||
) -> WasmResult<()>;
|
||||
|
||||
/// Translate a `table.fill` WebAssembly instruction.
|
||||
fn translate_table_fill(
|
||||
&mut self,
|
||||
pos: FuncCursor,
|
||||
table_index: u32,
|
||||
dst: ir::Value,
|
||||
val: ir::Value,
|
||||
len: ir::Value,
|
||||
) -> WasmResult<()>;
|
||||
|
||||
/// Translate a `table.init` WebAssembly instruction.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn translate_table_init(
|
||||
&mut self,
|
||||
pos: FuncCursor,
|
||||
seg_index: u32,
|
||||
table_index: TableIndex,
|
||||
table: ir::Table,
|
||||
dst: ir::Value,
|
||||
src: ir::Value,
|
||||
len: ir::Value,
|
||||
) -> WasmResult<()>;
|
||||
|
||||
/// Translate a `elem.drop` WebAssembly instruction.
|
||||
fn translate_elem_drop(&mut self, pos: FuncCursor, seg_index: u32) -> WasmResult<()>;
|
||||
|
||||
/// Translate a `ref.func` WebAssembly instruction.
|
||||
fn translate_ref_func(&mut self, pos: FuncCursor, func_index: u32) -> WasmResult<ir::Value>;
|
||||
|
||||
/// Translate a `global.get` WebAssembly instruction at `pos` for a global
|
||||
/// that is custom.
|
||||
fn translate_custom_global_get(
|
||||
&mut self,
|
||||
pos: FuncCursor,
|
||||
global_index: GlobalIndex,
|
||||
) -> WasmResult<ir::Value>;
|
||||
|
||||
/// Translate a `global.set` WebAssembly instruction at `pos` for a global
|
||||
/// that is custom.
|
||||
fn translate_custom_global_set(
|
||||
&mut self,
|
||||
pos: FuncCursor,
|
||||
global_index: GlobalIndex,
|
||||
val: ir::Value,
|
||||
) -> WasmResult<()>;
|
||||
|
||||
/// Emit code at the beginning of every wasm loop.
|
||||
///
|
||||
/// This can be used to insert explicit interrupt or safepoint checking at
|
||||
/// the beginnings of loops.
|
||||
fn translate_loop_header(&mut self, _pos: FuncCursor) -> WasmResult<()> {
|
||||
// By default, don't emit anything.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Optional callback for the `FunctionEnvironment` performing this translation to maintain
|
||||
/// internal state or prepare custom state for the operator to translate
|
||||
fn before_translate_operator(
|
||||
&mut self,
|
||||
_op: &Operator,
|
||||
_builder: &mut FunctionBuilder,
|
||||
_state: &FuncTranslationState,
|
||||
) -> WasmResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Optional callback for the `FunctionEnvironment` performing this translation to maintain
|
||||
/// internal state or finalize custom state for the operator that was translated
|
||||
fn after_translate_operator(
|
||||
&mut self,
|
||||
_op: &Operator,
|
||||
_builder: &mut FunctionBuilder,
|
||||
_state: &FuncTranslationState,
|
||||
) -> WasmResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
515
lib/compiler-cranelift/src/translator/func_state.rs
Normal file
@ -0,0 +1,515 @@
|
||||
//! WebAssembly module and function translation state.
|
||||
//!
|
||||
//! The `ModuleTranslationState` struct defined in this module is used to keep track of data about
|
||||
//! the whole WebAssembly module, such as the decoded type signatures.
|
||||
//!
|
||||
//! The `FuncTranslationState` struct defined in this module is used to keep track of the WebAssembly
|
||||
//! value and control stacks during the translation of a single function.
|
||||
|
||||
use super::func_environ::{FuncEnvironment, GlobalVariable};
|
||||
use crate::{HashMap, Occupied, Vacant};
|
||||
use cranelift_codegen::ir::{self, Block, Inst, Value};
|
||||
use std::vec::Vec;
|
||||
use wasm_common::{FuncIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
|
||||
use wasmer_compiler::WasmResult;
|
||||
|
||||
/// Information about the presence of an associated `else` for an `if`, or the
|
||||
/// lack thereof.
|
||||
#[derive(Debug)]
|
||||
pub enum ElseData {
|
||||
/// The `if` does not already have an `else` block.
|
||||
///
|
||||
/// This doesn't mean that it will never have an `else`, just that we
|
||||
/// haven't seen it yet.
|
||||
NoElse {
|
||||
/// If we discover that we need an `else` block, this is the jump
|
||||
/// instruction that needs to be fixed up to point to the new `else`
|
||||
/// block rather than the destination block after the `if...end`.
|
||||
branch_inst: Inst,
|
||||
},
|
||||
|
||||
/// We have already allocated an `else` block.
|
||||
///
|
||||
/// Usually we don't know whether we will hit an `if .. end` or an `if
|
||||
/// .. else .. end`, but sometimes we can tell based on the block's type
|
||||
/// signature that the signature is not valid if there isn't an `else`. In
|
||||
/// these cases, we pre-allocate the `else` block.
|
||||
WithElse {
|
||||
/// This is the `else` block.
|
||||
else_block: Block,
|
||||
},
|
||||
}
|
||||
|
||||
/// A control stack frame can be an `if`, a `block` or a `loop`, each one having the following
|
||||
/// fields:
|
||||
///
|
||||
/// - `destination`: reference to the `Block` that will hold the code after the control block;
|
||||
/// - `num_return_values`: number of values returned by the control block;
|
||||
/// - `original_stack_size`: size of the value stack at the beginning of the control block.
|
||||
///
|
||||
/// Moreover, the `if` frame has the `branch_inst` field that points to the `brz` instruction
|
||||
/// separating the `true` and `false` branch. The `loop` frame has a `header` field that references
|
||||
/// the `Block` that contains the beginning of the body of the loop.
|
||||
#[derive(Debug)]
|
||||
pub enum ControlStackFrame {
|
||||
If {
|
||||
destination: Block,
|
||||
else_data: ElseData,
|
||||
num_param_values: usize,
|
||||
num_return_values: usize,
|
||||
original_stack_size: usize,
|
||||
exit_is_branched_to: bool,
|
||||
blocktype: wasmparser::TypeOrFuncType,
|
||||
/// Was the head of the `if` reachable?
|
||||
head_is_reachable: bool,
|
||||
/// What was the reachability at the end of the consequent?
|
||||
///
|
||||
/// This is `None` until we're finished translating the consequent, and
|
||||
/// is set to `Some` either by hitting an `else` when we will begin
|
||||
/// translating the alternative, or by hitting an `end` in which case
|
||||
/// there is no alternative.
|
||||
consequent_ends_reachable: Option<bool>,
|
||||
// Note: no need for `alternative_ends_reachable` because that is just
|
||||
// `state.reachable` when we hit the `end` in the `if .. else .. end`.
|
||||
},
|
||||
Block {
|
||||
destination: Block,
|
||||
num_param_values: usize,
|
||||
num_return_values: usize,
|
||||
original_stack_size: usize,
|
||||
exit_is_branched_to: bool,
|
||||
},
|
||||
Loop {
|
||||
destination: Block,
|
||||
header: Block,
|
||||
num_param_values: usize,
|
||||
num_return_values: usize,
|
||||
original_stack_size: usize,
|
||||
},
|
||||
}
|
||||
|
||||
/// Helper methods for the control stack objects.
|
||||
impl ControlStackFrame {
|
||||
pub fn num_return_values(&self) -> usize {
|
||||
match *self {
|
||||
Self::If {
|
||||
num_return_values, ..
|
||||
}
|
||||
| Self::Block {
|
||||
num_return_values, ..
|
||||
}
|
||||
| Self::Loop {
|
||||
num_return_values, ..
|
||||
} => num_return_values,
|
||||
}
|
||||
}
|
||||
pub fn num_param_values(&self) -> usize {
|
||||
match *self {
|
||||
Self::If {
|
||||
num_param_values, ..
|
||||
}
|
||||
| Self::Block {
|
||||
num_param_values, ..
|
||||
}
|
||||
| Self::Loop {
|
||||
num_param_values, ..
|
||||
} => num_param_values,
|
||||
}
|
||||
}
|
||||
pub fn following_code(&self) -> Block {
|
||||
match *self {
|
||||
Self::If { destination, .. }
|
||||
| Self::Block { destination, .. }
|
||||
| Self::Loop { destination, .. } => destination,
|
||||
}
|
||||
}
|
||||
pub fn br_destination(&self) -> Block {
|
||||
match *self {
|
||||
Self::If { destination, .. } | Self::Block { destination, .. } => destination,
|
||||
Self::Loop { header, .. } => header,
|
||||
}
|
||||
}
|
||||
pub fn original_stack_size(&self) -> usize {
|
||||
match *self {
|
||||
Self::If {
|
||||
original_stack_size,
|
||||
..
|
||||
}
|
||||
| Self::Block {
|
||||
original_stack_size,
|
||||
..
|
||||
}
|
||||
| Self::Loop {
|
||||
original_stack_size,
|
||||
..
|
||||
} => original_stack_size,
|
||||
}
|
||||
}
|
||||
pub fn is_loop(&self) -> bool {
|
||||
match *self {
|
||||
Self::If { .. } | Self::Block { .. } => false,
|
||||
Self::Loop { .. } => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exit_is_branched_to(&self) -> bool {
|
||||
match *self {
|
||||
Self::If {
|
||||
exit_is_branched_to,
|
||||
..
|
||||
}
|
||||
| Self::Block {
|
||||
exit_is_branched_to,
|
||||
..
|
||||
} => exit_is_branched_to,
|
||||
Self::Loop { .. } => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_branched_to_exit(&mut self) {
|
||||
match *self {
|
||||
Self::If {
|
||||
ref mut exit_is_branched_to,
|
||||
..
|
||||
}
|
||||
| Self::Block {
|
||||
ref mut exit_is_branched_to,
|
||||
..
|
||||
} => *exit_is_branched_to = true,
|
||||
Self::Loop { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains information passed along during a function's translation and that records:
|
||||
///
|
||||
/// - The current value and control stacks.
|
||||
/// - The depth of the two unreachable control blocks stacks, that are manipulated when translating
|
||||
/// unreachable code;
|
||||
pub struct FuncTranslationState {
|
||||
/// A stack of values corresponding to the active values in the input wasm function at this
|
||||
/// point.
|
||||
pub(crate) stack: Vec<Value>,
|
||||
/// A stack of active control flow operations at this point in the input wasm function.
|
||||
pub(crate) control_stack: Vec<ControlStackFrame>,
|
||||
/// Is the current translation state still reachable? This is false when translating operators
|
||||
/// like End, Return, or Unreachable.
|
||||
pub(crate) reachable: bool,
|
||||
|
||||
// Map of global variables that have already been created by `FuncEnvironment::make_global`.
|
||||
globals: HashMap<GlobalIndex, GlobalVariable>,
|
||||
|
||||
// Map of heaps that have been created by `FuncEnvironment::make_heap`.
|
||||
heaps: HashMap<MemoryIndex, ir::Heap>,
|
||||
|
||||
// Map of tables that have been created by `FuncEnvironment::make_table`.
|
||||
tables: HashMap<TableIndex, ir::Table>,
|
||||
|
||||
// Map of indirect call signatures that have been created by
|
||||
// `FuncEnvironment::make_indirect_sig()`.
|
||||
// Stores both the signature reference and the number of WebAssembly arguments
|
||||
signatures: HashMap<SignatureIndex, (ir::SigRef, usize)>,
|
||||
|
||||
// Imported and local functions that have been created by
|
||||
// `FuncEnvironment::make_direct_func()`.
|
||||
// Stores both the function reference and the number of WebAssembly arguments
|
||||
functions: HashMap<FuncIndex, (ir::FuncRef, usize)>,
|
||||
}
|
||||
|
||||
// Public methods that are exposed to non-`wasmer_compiler` API consumers.
|
||||
impl FuncTranslationState {
|
||||
/// True if the current translation state expresses reachable code, false if it is unreachable.
|
||||
#[inline]
|
||||
pub fn reachable(&self) -> bool {
|
||||
self.reachable
|
||||
}
|
||||
}
|
||||
|
||||
impl FuncTranslationState {
|
||||
/// Construct a new, empty, `FuncTranslationState`
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
stack: Vec::new(),
|
||||
control_stack: Vec::new(),
|
||||
reachable: true,
|
||||
globals: HashMap::new(),
|
||||
heaps: HashMap::new(),
|
||||
tables: HashMap::new(),
|
||||
signatures: HashMap::new(),
|
||||
functions: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
debug_assert!(self.stack.is_empty());
|
||||
debug_assert!(self.control_stack.is_empty());
|
||||
self.reachable = true;
|
||||
self.globals.clear();
|
||||
self.heaps.clear();
|
||||
self.tables.clear();
|
||||
self.signatures.clear();
|
||||
self.functions.clear();
|
||||
}
|
||||
|
||||
/// Initialize the state for compiling a function with the given signature.
|
||||
///
|
||||
/// This resets the state to containing only a single block representing the whole function.
|
||||
/// The exit block is the last block in the function which will contain the return instruction.
|
||||
pub(crate) fn initialize(&mut self, sig: &ir::Signature, exit_block: Block) {
|
||||
self.clear();
|
||||
self.push_block(
|
||||
exit_block,
|
||||
0,
|
||||
sig.returns
|
||||
.iter()
|
||||
.filter(|arg| arg.purpose == ir::ArgumentPurpose::Normal)
|
||||
.count(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Push a value.
|
||||
pub(crate) fn push1(&mut self, val: Value) {
|
||||
self.stack.push(val);
|
||||
}
|
||||
|
||||
/// Push multiple values.
|
||||
pub(crate) fn pushn(&mut self, vals: &[Value]) {
|
||||
self.stack.extend_from_slice(vals);
|
||||
}
|
||||
|
||||
/// Pop one value.
|
||||
pub(crate) fn pop1(&mut self) -> Value {
|
||||
self.stack
|
||||
.pop()
|
||||
.expect("attempted to pop a value from an empty stack")
|
||||
}
|
||||
|
||||
/// Peek at the top of the stack without popping it.
|
||||
pub(crate) fn peek1(&self) -> Value {
|
||||
*self
|
||||
.stack
|
||||
.last()
|
||||
.expect("attempted to peek at a value on an empty stack")
|
||||
}
|
||||
|
||||
/// Pop two values. Return them in the order they were pushed.
|
||||
pub(crate) fn pop2(&mut self) -> (Value, Value) {
|
||||
let v2 = self.stack.pop().unwrap();
|
||||
let v1 = self.stack.pop().unwrap();
|
||||
(v1, v2)
|
||||
}
|
||||
|
||||
/// Pop three values. Return them in the order they were pushed.
|
||||
pub(crate) fn pop3(&mut self) -> (Value, Value, Value) {
|
||||
let v3 = self.stack.pop().unwrap();
|
||||
let v2 = self.stack.pop().unwrap();
|
||||
let v1 = self.stack.pop().unwrap();
|
||||
(v1, v2, v3)
|
||||
}
|
||||
|
||||
/// Helper to ensure the the stack size is at least as big as `n`; note that due to
|
||||
/// `debug_assert` this will not execute in non-optimized builds.
|
||||
#[inline]
|
||||
fn ensure_length_is_at_least(&self, n: usize) {
|
||||
debug_assert!(
|
||||
n <= self.stack.len(),
|
||||
"attempted to access {} values but stack only has {} values",
|
||||
n,
|
||||
self.stack.len()
|
||||
)
|
||||
}
|
||||
|
||||
/// Pop the top `n` values on the stack.
|
||||
///
|
||||
/// The popped values are not returned. Use `peekn` to look at them before popping.
|
||||
pub(crate) fn popn(&mut self, n: usize) {
|
||||
self.ensure_length_is_at_least(n);
|
||||
let new_len = self.stack.len() - n;
|
||||
self.stack.truncate(new_len);
|
||||
}
|
||||
|
||||
/// Peek at the top `n` values on the stack in the order they were pushed.
|
||||
pub(crate) fn peekn(&self, n: usize) -> &[Value] {
|
||||
self.ensure_length_is_at_least(n);
|
||||
&self.stack[self.stack.len() - n..]
|
||||
}
|
||||
|
||||
/// Peek at the top `n` values on the stack in the order they were pushed.
|
||||
pub(crate) fn peekn_mut(&mut self, n: usize) -> &mut [Value] {
|
||||
self.ensure_length_is_at_least(n);
|
||||
let len = self.stack.len();
|
||||
&mut self.stack[len - n..]
|
||||
}
|
||||
|
||||
/// Push a block on the control stack.
|
||||
pub(crate) fn push_block(
|
||||
&mut self,
|
||||
following_code: Block,
|
||||
num_param_types: usize,
|
||||
num_result_types: usize,
|
||||
) {
|
||||
debug_assert!(num_param_types <= self.stack.len());
|
||||
self.control_stack.push(ControlStackFrame::Block {
|
||||
destination: following_code,
|
||||
original_stack_size: self.stack.len() - num_param_types,
|
||||
num_param_values: num_param_types,
|
||||
num_return_values: num_result_types,
|
||||
exit_is_branched_to: false,
|
||||
});
|
||||
}
|
||||
|
||||
/// Push a loop on the control stack.
|
||||
pub(crate) fn push_loop(
|
||||
&mut self,
|
||||
header: Block,
|
||||
following_code: Block,
|
||||
num_param_types: usize,
|
||||
num_result_types: usize,
|
||||
) {
|
||||
debug_assert!(num_param_types <= self.stack.len());
|
||||
self.control_stack.push(ControlStackFrame::Loop {
|
||||
header,
|
||||
destination: following_code,
|
||||
original_stack_size: self.stack.len() - num_param_types,
|
||||
num_param_values: num_param_types,
|
||||
num_return_values: num_result_types,
|
||||
});
|
||||
}
|
||||
|
||||
/// Push an if on the control stack.
|
||||
pub(crate) fn push_if(
|
||||
&mut self,
|
||||
destination: Block,
|
||||
else_data: ElseData,
|
||||
num_param_types: usize,
|
||||
num_result_types: usize,
|
||||
blocktype: wasmparser::TypeOrFuncType,
|
||||
) {
|
||||
debug_assert!(num_param_types <= self.stack.len());
|
||||
|
||||
// Push a second copy of our `if`'s parameters on the stack. This lets
|
||||
// us avoid saving them on the side in the `ControlStackFrame` for our
|
||||
// `else` block (if it exists), which would require a second heap
|
||||
// allocation. See also the comment in `translate_operator` for
|
||||
// `Operator::Else`.
|
||||
self.stack.reserve(num_param_types);
|
||||
for i in (self.stack.len() - num_param_types)..self.stack.len() {
|
||||
let val = self.stack[i];
|
||||
self.stack.push(val);
|
||||
}
|
||||
|
||||
self.control_stack.push(ControlStackFrame::If {
|
||||
destination,
|
||||
else_data,
|
||||
original_stack_size: self.stack.len() - num_param_types,
|
||||
num_param_values: num_param_types,
|
||||
num_return_values: num_result_types,
|
||||
exit_is_branched_to: false,
|
||||
head_is_reachable: self.reachable,
|
||||
consequent_ends_reachable: None,
|
||||
blocktype,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods for handling entity references.
|
||||
impl FuncTranslationState {
|
||||
/// Get the `GlobalVariable` reference that should be used to access the global variable
|
||||
/// `index`. Create the reference if necessary.
|
||||
/// Also return the WebAssembly type of the global.
|
||||
pub(crate) fn get_global<FE: FuncEnvironment + ?Sized>(
|
||||
&mut self,
|
||||
func: &mut ir::Function,
|
||||
index: u32,
|
||||
environ: &mut FE,
|
||||
) -> WasmResult<GlobalVariable> {
|
||||
let index = GlobalIndex::from_u32(index);
|
||||
match self.globals.entry(index) {
|
||||
Occupied(entry) => Ok(*entry.get()),
|
||||
Vacant(entry) => Ok(*entry.insert(environ.make_global(func, index)?)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the `Heap` reference that should be used to access linear memory `index`.
|
||||
/// Create the reference if necessary.
|
||||
pub(crate) fn get_heap<FE: FuncEnvironment + ?Sized>(
|
||||
&mut self,
|
||||
func: &mut ir::Function,
|
||||
index: u32,
|
||||
environ: &mut FE,
|
||||
) -> WasmResult<ir::Heap> {
|
||||
let index = MemoryIndex::from_u32(index);
|
||||
match self.heaps.entry(index) {
|
||||
Occupied(entry) => Ok(*entry.get()),
|
||||
Vacant(entry) => Ok(*entry.insert(environ.make_heap(func, index)?)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the `Table` reference that should be used to access table `index`.
|
||||
/// Create the reference if necessary.
|
||||
pub(crate) fn get_table<FE: FuncEnvironment + ?Sized>(
|
||||
&mut self,
|
||||
func: &mut ir::Function,
|
||||
index: u32,
|
||||
environ: &mut FE,
|
||||
) -> WasmResult<ir::Table> {
|
||||
let index = TableIndex::from_u32(index);
|
||||
match self.tables.entry(index) {
|
||||
Occupied(entry) => Ok(*entry.get()),
|
||||
Vacant(entry) => Ok(*entry.insert(environ.make_table(func, index)?)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the `SigRef` reference that should be used to make an indirect call with signature
|
||||
/// `index`. Also return the number of WebAssembly arguments in the signature.
|
||||
///
|
||||
/// Create the signature if necessary.
|
||||
pub(crate) fn get_indirect_sig<FE: FuncEnvironment + ?Sized>(
|
||||
&mut self,
|
||||
func: &mut ir::Function,
|
||||
index: u32,
|
||||
environ: &mut FE,
|
||||
) -> WasmResult<(ir::SigRef, usize)> {
|
||||
let index = SignatureIndex::from_u32(index);
|
||||
match self.signatures.entry(index) {
|
||||
Occupied(entry) => Ok(*entry.get()),
|
||||
Vacant(entry) => {
|
||||
let sig = environ.make_indirect_sig(func, index)?;
|
||||
Ok(*entry.insert((sig, num_wasm_parameters(environ, &func.dfg.signatures[sig]))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the `FuncRef` reference that should be used to make a direct call to function
|
||||
/// `index`. Also return the number of WebAssembly arguments in the signature.
|
||||
///
|
||||
/// Create the function reference if necessary.
|
||||
pub(crate) fn get_direct_func<FE: FuncEnvironment + ?Sized>(
|
||||
&mut self,
|
||||
func: &mut ir::Function,
|
||||
index: u32,
|
||||
environ: &mut FE,
|
||||
) -> WasmResult<(ir::FuncRef, usize)> {
|
||||
let index = FuncIndex::from_u32(index);
|
||||
match self.functions.entry(index) {
|
||||
Occupied(entry) => Ok(*entry.get()),
|
||||
Vacant(entry) => {
|
||||
let fref = environ.make_direct_func(func, index)?;
|
||||
let sig = func.dfg.ext_funcs[fref].signature;
|
||||
Ok(*entry.insert((
|
||||
fref,
|
||||
num_wasm_parameters(environ, &func.dfg.signatures[sig]),
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn num_wasm_parameters<FE: FuncEnvironment + ?Sized>(
|
||||
environ: &FE,
|
||||
signature: &ir::Signature,
|
||||
) -> usize {
|
||||
(0..signature.params.len())
|
||||
.filter(|index| environ.is_wasm_parameter(signature, *index))
|
||||
.count()
|
||||
}
|
276
lib/compiler-cranelift/src/translator/func_translator.rs
Normal file
@ -0,0 +1,276 @@
|
||||
//! Stand-alone WebAssembly to Cranelift IR translator.
|
||||
//!
|
||||
//! This module defines the `FuncTranslator` type which can translate a single WebAssembly
|
||||
//! function to Cranelift IR guided by a `FuncEnvironment` which provides information about the
|
||||
//! WebAssembly module and the runtime environment.
|
||||
|
||||
use super::code_translator::{bitcast_arguments, translate_operator, wasm_param_types};
|
||||
use super::func_environ::{FuncEnvironment, ReturnMode};
|
||||
use super::func_state::FuncTranslationState;
|
||||
use super::translation_utils::get_vmctx_value_label;
|
||||
use cranelift_codegen::entity::EntityRef;
|
||||
use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel};
|
||||
use cranelift_codegen::timing;
|
||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
|
||||
use log::info;
|
||||
use wasmer_compiler::{to_wasm_error, wasm_unsupported, ModuleTranslationState, WasmResult};
|
||||
use wasmparser::{self, BinaryReader};
|
||||
|
||||
/// WebAssembly to Cranelift IR function translator.
|
||||
///
|
||||
/// A `FuncTranslator` is used to translate a binary WebAssembly function into Cranelift IR guided
|
||||
/// by a `FuncEnvironment` object. A single translator instance can be reused to translate multiple
|
||||
/// functions which will reduce heap allocation traffic.
|
||||
pub struct FuncTranslator {
|
||||
func_ctx: FunctionBuilderContext,
|
||||
state: FuncTranslationState,
|
||||
}
|
||||
|
||||
impl FuncTranslator {
|
||||
/// Create a new translator.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
func_ctx: FunctionBuilderContext::new(),
|
||||
state: FuncTranslationState::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Translate a binary WebAssembly function.
|
||||
///
|
||||
/// The `code` slice contains the binary WebAssembly *function code* as it appears in the code
|
||||
/// section of a WebAssembly module, not including the initial size of the function code. The
|
||||
/// slice is expected to contain two parts:
|
||||
///
|
||||
/// - The declaration of *locals*, and
|
||||
/// - The function *body* as an expression.
|
||||
///
|
||||
/// See [the WebAssembly specification][wasm].
|
||||
///
|
||||
/// [wasm]: https://webassembly.github.io/spec/core/binary/modules.html#code-section
|
||||
///
|
||||
/// The Cranelift IR function `func` should be completely empty except for the `func.signature`
|
||||
/// and `func.name` fields. The signature may contain special-purpose arguments which are not
|
||||
/// regarded as WebAssembly local variables. Any signature arguments marked as
|
||||
/// `ArgumentPurpose::Normal` are made accessible as WebAssembly local variables.
|
||||
///
|
||||
pub fn translate<FE: FuncEnvironment + ?Sized>(
|
||||
&mut self,
|
||||
module_translation_state: &ModuleTranslationState,
|
||||
code: &[u8],
|
||||
code_offset: usize,
|
||||
func: &mut ir::Function,
|
||||
environ: &mut FE,
|
||||
) -> WasmResult<()> {
|
||||
self.translate_from_reader(
|
||||
module_translation_state,
|
||||
BinaryReader::new_with_offset(code, code_offset),
|
||||
func,
|
||||
environ,
|
||||
)
|
||||
}
|
||||
|
||||
/// Translate a binary WebAssembly function from a `BinaryReader`.
|
||||
pub fn translate_from_reader<FE: FuncEnvironment + ?Sized>(
|
||||
&mut self,
|
||||
module_translation_state: &ModuleTranslationState,
|
||||
mut reader: BinaryReader,
|
||||
func: &mut ir::Function,
|
||||
environ: &mut FE,
|
||||
) -> WasmResult<()> {
|
||||
let _tt = timing::wasm_translate_function();
|
||||
info!(
|
||||
"translate({} bytes, {}{})",
|
||||
reader.bytes_remaining(),
|
||||
func.name,
|
||||
func.signature
|
||||
);
|
||||
debug_assert_eq!(func.dfg.num_blocks(), 0, "Function must be empty");
|
||||
debug_assert_eq!(func.dfg.num_insts(), 0, "Function must be empty");
|
||||
|
||||
// This clears the `FunctionBuilderContext`.
|
||||
let mut builder = FunctionBuilder::new(func, &mut self.func_ctx);
|
||||
builder.set_srcloc(cur_srcloc(&reader));
|
||||
let entry_block = builder.create_block();
|
||||
builder.append_block_params_for_function_params(entry_block);
|
||||
builder.switch_to_block(entry_block); // This also creates values for the arguments.
|
||||
builder.seal_block(entry_block); // Declare all predecessors known.
|
||||
|
||||
// Make sure the entry block is inserted in the layout before we make any callbacks to
|
||||
// `environ`. The callback functions may need to insert things in the entry block.
|
||||
builder.ensure_inserted_block();
|
||||
|
||||
let num_params = declare_wasm_parameters(&mut builder, entry_block, environ);
|
||||
|
||||
// Set up the translation state with a single pushed control block representing the whole
|
||||
// function and its return values.
|
||||
let exit_block = builder.create_block();
|
||||
builder.append_block_params_for_function_returns(exit_block);
|
||||
self.state.initialize(&builder.func.signature, exit_block);
|
||||
|
||||
parse_local_decls(&mut reader, &mut builder, num_params, environ)?;
|
||||
parse_function_body(
|
||||
module_translation_state,
|
||||
reader,
|
||||
&mut builder,
|
||||
&mut self.state,
|
||||
environ,
|
||||
)?;
|
||||
|
||||
builder.finalize();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Declare local variables for the signature parameters that correspond to WebAssembly locals.
|
||||
///
|
||||
/// Return the number of local variables declared.
|
||||
fn declare_wasm_parameters<FE: FuncEnvironment + ?Sized>(
|
||||
builder: &mut FunctionBuilder,
|
||||
entry_block: Block,
|
||||
environ: &FE,
|
||||
) -> usize {
|
||||
let sig_len = builder.func.signature.params.len();
|
||||
let mut next_local = 0;
|
||||
for i in 0..sig_len {
|
||||
let param_type = builder.func.signature.params[i];
|
||||
// There may be additional special-purpose parameters in addition to the normal WebAssembly
|
||||
// signature parameters. For example, a `vmctx` pointer.
|
||||
if environ.is_wasm_parameter(&builder.func.signature, i) {
|
||||
// This is a normal WebAssembly signature parameter, so create a local for it.
|
||||
let local = Variable::new(next_local);
|
||||
builder.declare_var(local, param_type.value_type);
|
||||
next_local += 1;
|
||||
|
||||
let param_value = builder.block_params(entry_block)[i];
|
||||
builder.def_var(local, param_value);
|
||||
}
|
||||
if param_type.purpose == ir::ArgumentPurpose::VMContext {
|
||||
let param_value = builder.block_params(entry_block)[i];
|
||||
builder.set_val_label(param_value, get_vmctx_value_label());
|
||||
}
|
||||
}
|
||||
|
||||
next_local
|
||||
}
|
||||
|
||||
/// Parse the local variable declarations that precede the function body.
|
||||
///
|
||||
/// Declare local variables, starting from `num_params`.
|
||||
fn parse_local_decls<FE: FuncEnvironment + ?Sized>(
|
||||
reader: &mut BinaryReader,
|
||||
builder: &mut FunctionBuilder,
|
||||
num_params: usize,
|
||||
environ: &mut FE,
|
||||
) -> WasmResult<()> {
|
||||
let mut next_local = num_params;
|
||||
let local_count = reader.read_local_count().map_err(to_wasm_error)?;
|
||||
|
||||
let mut locals_total = 0;
|
||||
for _ in 0..local_count {
|
||||
builder.set_srcloc(cur_srcloc(reader));
|
||||
let (count, ty) = reader
|
||||
.read_local_decl(&mut locals_total)
|
||||
.map_err(to_wasm_error)?;
|
||||
declare_locals(builder, count, ty, &mut next_local, environ)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Declare `count` local variables of the same type, starting from `next_local`.
|
||||
///
|
||||
/// Fail of too many locals are declared in the function, or if the type is not valid for a local.
|
||||
fn declare_locals<FE: FuncEnvironment + ?Sized>(
|
||||
builder: &mut FunctionBuilder,
|
||||
count: u32,
|
||||
wasm_type: wasmparser::Type,
|
||||
next_local: &mut usize,
|
||||
environ: &mut FE,
|
||||
) -> WasmResult<()> {
|
||||
// All locals are initialized to 0.
|
||||
use wasmparser::Type::*;
|
||||
let zeroval = match wasm_type {
|
||||
I32 => builder.ins().iconst(ir::types::I32, 0),
|
||||
I64 => builder.ins().iconst(ir::types::I64, 0),
|
||||
F32 => builder.ins().f32const(ir::immediates::Ieee32::with_bits(0)),
|
||||
F64 => builder.ins().f64const(ir::immediates::Ieee64::with_bits(0)),
|
||||
V128 => {
|
||||
let constant_handle = builder.func.dfg.constants.insert([0; 16].to_vec().into());
|
||||
builder.ins().vconst(ir::types::I8X16, constant_handle)
|
||||
}
|
||||
NullRef => builder.ins().null(environ.reference_type()),
|
||||
AnyRef => builder.ins().null(environ.reference_type()),
|
||||
AnyFunc => builder.ins().null(environ.reference_type()),
|
||||
ty => return Err(wasm_unsupported!("unsupported local type {:?}", ty)),
|
||||
};
|
||||
|
||||
let ty = builder.func.dfg.value_type(zeroval);
|
||||
for _ in 0..count {
|
||||
let local = Variable::new(*next_local);
|
||||
builder.declare_var(local, ty);
|
||||
builder.def_var(local, zeroval);
|
||||
builder.set_val_label(zeroval, ValueLabel::new(*next_local));
|
||||
*next_local += 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parse the function body in `reader`.
|
||||
///
|
||||
/// This assumes that the local variable declarations have already been parsed and function
|
||||
/// arguments and locals are declared in the builder.
|
||||
fn parse_function_body<FE: FuncEnvironment + ?Sized>(
|
||||
module_translation_state: &ModuleTranslationState,
|
||||
mut reader: BinaryReader,
|
||||
builder: &mut FunctionBuilder,
|
||||
state: &mut FuncTranslationState,
|
||||
environ: &mut FE,
|
||||
) -> WasmResult<()> {
|
||||
// The control stack is initialized with a single block representing the whole function.
|
||||
debug_assert_eq!(state.control_stack.len(), 1, "State not initialized");
|
||||
|
||||
// Keep going until the final `End` operator which pops the outermost block.
|
||||
while !state.control_stack.is_empty() {
|
||||
builder.set_srcloc(cur_srcloc(&reader));
|
||||
let op = reader.read_operator().map_err(to_wasm_error)?;
|
||||
environ.before_translate_operator(&op, builder, state)?;
|
||||
translate_operator(module_translation_state, &op, builder, state, environ)?;
|
||||
environ.after_translate_operator(&op, builder, state)?;
|
||||
}
|
||||
|
||||
// The final `End` operator left us in the exit block where we need to manually add a return
|
||||
// instruction.
|
||||
//
|
||||
// If the exit block is unreachable, it may not have the correct arguments, so we would
|
||||
// generate a return instruction that doesn't match the signature.
|
||||
if state.reachable {
|
||||
debug_assert!(builder.is_pristine());
|
||||
if !builder.is_unreachable() {
|
||||
match environ.return_mode() {
|
||||
ReturnMode::NormalReturns => {
|
||||
let return_types = wasm_param_types(&builder.func.signature.returns, |i| {
|
||||
environ.is_wasm_return(&builder.func.signature, i)
|
||||
});
|
||||
bitcast_arguments(&mut state.stack, &return_types, builder);
|
||||
builder.ins().return_(&state.stack)
|
||||
}
|
||||
ReturnMode::FallthroughReturn => builder.ins().fallthrough_return(&state.stack),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Discard any remaining values on the stack. Either we just returned them,
|
||||
// or the end of the function is unreachable.
|
||||
state.stack.clear();
|
||||
|
||||
debug_assert!(reader.eof());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the current source location from a reader.
|
||||
fn cur_srcloc(reader: &BinaryReader) -> ir::SourceLoc {
|
||||
// We record source locations as byte code offsets relative to the beginning of the file.
|
||||
// This will wrap around if byte code is larger than 4 GB.
|
||||
ir::SourceLoc::new(reader.original_position() as u32)
|
||||
}
|
15
lib/compiler-cranelift/src/translator/mod.rs
Normal file
@ -0,0 +1,15 @@
|
||||
//! Tools for translating wasm function bytecode to Cranelift IR.
|
||||
|
||||
mod code_translator;
|
||||
mod func_environ;
|
||||
mod func_state;
|
||||
mod func_translator;
|
||||
mod translation_utils;
|
||||
|
||||
pub use self::func_environ::{FuncEnvironment, GlobalVariable, ReturnMode, TargetEnvironment};
|
||||
pub use self::func_state::FuncTranslationState;
|
||||
pub use self::func_translator::FuncTranslator;
|
||||
pub use self::translation_utils::{
|
||||
get_vmctx_value_label, irlibcall_to_libcall, irreloc_to_relocationkind,
|
||||
signature_to_cranelift_ir, type_to_irtype,
|
||||
};
|
149
lib/compiler-cranelift/src/translator/translation_utils.rs
Normal file
@ -0,0 +1,149 @@
|
||||
//! Helper functions and structures for the translation.
|
||||
|
||||
use super::func_environ::TargetEnvironment;
|
||||
use crate::std::string::ToString;
|
||||
use core::u32;
|
||||
use cranelift_codegen::binemit::Reloc;
|
||||
use cranelift_codegen::ir::{self, AbiParam};
|
||||
use cranelift_codegen::isa::TargetFrontendConfig;
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
use wasm_common::{FuncType, Type};
|
||||
use wasmer_compiler::wasm_unsupported;
|
||||
use wasmer_compiler::RelocationKind;
|
||||
use wasmer_compiler::{WasmError, WasmResult};
|
||||
use wasmer_runtime::libcalls::LibCall;
|
||||
use wasmparser;
|
||||
|
||||
/// Helper function translate a Funciton signature into Cranelift Ir
|
||||
pub fn signature_to_cranelift_ir(
|
||||
signature: &FuncType,
|
||||
target_config: &TargetFrontendConfig,
|
||||
) -> ir::Signature {
|
||||
let mut sig = ir::Signature::new(target_config.default_call_conv);
|
||||
sig.params.extend(signature.params().iter().map(|ty| {
|
||||
let cret_arg: ir::Type = type_to_irtype(ty.clone(), target_config)
|
||||
.expect("only numeric types are supported in function signatures");
|
||||
AbiParam::new(cret_arg)
|
||||
}));
|
||||
sig.returns.extend(signature.results().iter().map(|ty| {
|
||||
let cret_arg: ir::Type = type_to_irtype(ty.clone(), target_config)
|
||||
.expect("only numeric types are supported in function signatures");
|
||||
AbiParam::new(cret_arg)
|
||||
}));
|
||||
// The Vmctx signature
|
||||
sig.params.insert(
|
||||
0,
|
||||
AbiParam::special(target_config.pointer_type(), ir::ArgumentPurpose::VMContext),
|
||||
);
|
||||
// Prepend the caller vmctx argument.
|
||||
sig.params
|
||||
.insert(1, AbiParam::new(target_config.pointer_type()));
|
||||
|
||||
sig
|
||||
}
|
||||
|
||||
/// Helper function translating wasmparser types to Cranelift types when possible.
|
||||
pub fn reference_type(target_config: &TargetFrontendConfig) -> WasmResult<ir::Type> {
|
||||
match target_config.pointer_type() {
|
||||
ir::types::I32 => Ok(ir::types::R32),
|
||||
ir::types::I64 => Ok(ir::types::R64),
|
||||
_ => Err(WasmError::Unsupported(
|
||||
"unsupported pointer type".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function translating wasmparser types to Cranelift types when possible.
|
||||
pub fn type_to_irtype(ty: Type, target_config: &TargetFrontendConfig) -> WasmResult<ir::Type> {
|
||||
match ty {
|
||||
Type::I32 => Ok(ir::types::I32),
|
||||
Type::I64 => Ok(ir::types::I64),
|
||||
Type::F32 => Ok(ir::types::F32),
|
||||
Type::F64 => Ok(ir::types::F64),
|
||||
Type::V128 => Ok(ir::types::I8X16),
|
||||
Type::AnyRef | Type::FuncRef => reference_type(target_config),
|
||||
// ty => Err(wasm_unsupported!("type_to_type: wasm type {:?}", ty)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Transform Cranelift LibCall into runtime LibCall
|
||||
pub fn irlibcall_to_libcall(libcall: ir::LibCall) -> LibCall {
|
||||
match libcall {
|
||||
ir::LibCall::Probestack => LibCall::Probestack,
|
||||
ir::LibCall::CeilF32 => LibCall::CeilF32,
|
||||
ir::LibCall::CeilF64 => LibCall::CeilF64,
|
||||
ir::LibCall::FloorF32 => LibCall::FloorF32,
|
||||
ir::LibCall::FloorF64 => LibCall::FloorF64,
|
||||
ir::LibCall::TruncF32 => LibCall::TruncF32,
|
||||
ir::LibCall::TruncF64 => LibCall::TruncF64,
|
||||
ir::LibCall::NearestF32 => LibCall::NearestF32,
|
||||
ir::LibCall::NearestF64 => LibCall::NearestF64,
|
||||
_ => panic!("Unsupported libcall"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Transform Cranelift Reloc to compiler Relocation
|
||||
pub fn irreloc_to_relocationkind(reloc: Reloc) -> RelocationKind {
|
||||
match reloc {
|
||||
Reloc::Abs4 => RelocationKind::Abs4,
|
||||
Reloc::Abs8 => RelocationKind::Abs8,
|
||||
Reloc::X86PCRel4 => RelocationKind::X86PCRel4,
|
||||
Reloc::X86PCRelRodata4 => RelocationKind::X86PCRelRodata4,
|
||||
Reloc::X86CallPCRel4 => RelocationKind::X86CallPCRel4,
|
||||
_ => panic!("The relocation {} is not yet supported.", reloc),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `Block` with the given Wasm parameters.
|
||||
pub fn block_with_params<PE: TargetEnvironment + ?Sized>(
|
||||
builder: &mut FunctionBuilder,
|
||||
params: &[wasmparser::Type],
|
||||
environ: &PE,
|
||||
) -> WasmResult<ir::Block> {
|
||||
let block = builder.create_block();
|
||||
for ty in params.iter() {
|
||||
match ty {
|
||||
wasmparser::Type::I32 => {
|
||||
builder.append_block_param(block, ir::types::I32);
|
||||
}
|
||||
wasmparser::Type::I64 => {
|
||||
builder.append_block_param(block, ir::types::I64);
|
||||
}
|
||||
wasmparser::Type::F32 => {
|
||||
builder.append_block_param(block, ir::types::F32);
|
||||
}
|
||||
wasmparser::Type::F64 => {
|
||||
builder.append_block_param(block, ir::types::F64);
|
||||
}
|
||||
wasmparser::Type::AnyRef | wasmparser::Type::AnyFunc | wasmparser::Type::NullRef => {
|
||||
builder.append_block_param(block, environ.reference_type());
|
||||
}
|
||||
wasmparser::Type::V128 => {
|
||||
builder.append_block_param(block, ir::types::I8X16);
|
||||
}
|
||||
ty => {
|
||||
return Err(wasm_unsupported!(
|
||||
"block_with_params: type {:?} in multi-value block's signature",
|
||||
ty
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(block)
|
||||
}
|
||||
|
||||
/// Turns a `wasmparser` `f32` into a `Cranelift` one.
|
||||
pub fn f32_translation(x: wasmparser::Ieee32) -> ir::immediates::Ieee32 {
|
||||
ir::immediates::Ieee32::with_bits(x.bits())
|
||||
}
|
||||
|
||||
/// Turns a `wasmparser` `f64` into a `Cranelift` one.
|
||||
pub fn f64_translation(x: wasmparser::Ieee64) -> ir::immediates::Ieee64 {
|
||||
ir::immediates::Ieee64::with_bits(x.bits())
|
||||
}
|
||||
|
||||
/// Special VMContext value label. It is tracked as 0xffff_fffe label.
|
||||
pub fn get_vmctx_value_label() -> ir::ValueLabel {
|
||||
const VMCTX_LABEL: u32 = 0xffff_fffe;
|
||||
ir::ValueLabel::from_u32(VMCTX_LABEL)
|
||||
}
|
64
lib/compiler-cranelift/src/unwind.rs
Normal file
@ -0,0 +1,64 @@
|
||||
//! A `Compilation` contains the compiled function bodies for a WebAssembly
|
||||
//! module.
|
||||
|
||||
use cranelift_codegen::{isa, Context};
|
||||
use wasmer_compiler::{CompiledFunctionUnwindInfo, FDERelocEntry};
|
||||
|
||||
/// Constructs unwind info object from Cranelift IR
|
||||
pub fn compiled_function_unwind_info(
|
||||
isa: &dyn isa::TargetIsa,
|
||||
context: &Context,
|
||||
) -> CompiledFunctionUnwindInfo {
|
||||
use cranelift_codegen::binemit::{FrameUnwindKind, FrameUnwindOffset, FrameUnwindSink, Reloc};
|
||||
use cranelift_codegen::isa::CallConv;
|
||||
|
||||
struct Sink(Vec<u8>, usize, Vec<FDERelocEntry>);
|
||||
impl FrameUnwindSink for Sink {
|
||||
fn len(&self) -> FrameUnwindOffset {
|
||||
self.0.len()
|
||||
}
|
||||
fn bytes(&mut self, b: &[u8]) {
|
||||
self.0.extend_from_slice(b);
|
||||
}
|
||||
fn reserve(&mut self, len: usize) {
|
||||
self.0.reserve(len)
|
||||
}
|
||||
fn reloc(&mut self, r: Reloc, off: FrameUnwindOffset) {
|
||||
self.2.push(FDERelocEntry(
|
||||
0,
|
||||
off,
|
||||
match r {
|
||||
Reloc::Abs4 => 4,
|
||||
Reloc::Abs8 => 8,
|
||||
_ => {
|
||||
panic!("unexpected reloc type");
|
||||
}
|
||||
},
|
||||
))
|
||||
}
|
||||
fn set_entry_offset(&mut self, off: FrameUnwindOffset) {
|
||||
self.1 = off;
|
||||
}
|
||||
}
|
||||
|
||||
let kind = match context.func.signature.call_conv {
|
||||
CallConv::SystemV | CallConv::Fast | CallConv::Cold => FrameUnwindKind::Libunwind,
|
||||
CallConv::WindowsFastcall => FrameUnwindKind::Fastcall,
|
||||
_ => {
|
||||
return CompiledFunctionUnwindInfo::None;
|
||||
}
|
||||
};
|
||||
|
||||
let mut sink = Sink(Vec::new(), 0, Vec::new());
|
||||
context.emit_unwind_info(isa, kind, &mut sink);
|
||||
|
||||
let Sink(data, offset, relocs) = sink;
|
||||
if data.is_empty() {
|
||||
return CompiledFunctionUnwindInfo::None;
|
||||
}
|
||||
|
||||
match kind {
|
||||
FrameUnwindKind::Fastcall => CompiledFunctionUnwindInfo::Windows(data),
|
||||
FrameUnwindKind::Libunwind => CompiledFunctionUnwindInfo::FrameLayout(data, offset, relocs),
|
||||
}
|
||||
}
|
48
lib/compiler-llvm/Cargo.toml
Normal file
@ -0,0 +1,48 @@
|
||||
[package]
|
||||
name = "wasmer-compiler-llvm"
|
||||
version = "0.16.2"
|
||||
license = "MIT"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
keywords = ["wasm", "webassembly", "compiler", "JIT", "llvm"]
|
||||
categories = ["wasm"]
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
wasmer-compiler = { path = "../compiler", version = "0.16.2" }
|
||||
wasm-common = { path = "../wasm-common", version = "0.16.2" }
|
||||
wasmparser = "0.51.4"
|
||||
target-lexicon = { version = "0.10.0", default-features = false }
|
||||
smallvec = "1"
|
||||
goblin = "0.2"
|
||||
libc = "0.2.69"
|
||||
byteorder = "1"
|
||||
itertools = "0.9.0"
|
||||
rayon = "1.3.0"
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies.inkwell]
|
||||
version = "0.1.0-llvm8sample"
|
||||
default-features = false
|
||||
features = ["llvm8-0", "target-x86"]
|
||||
|
||||
[target.'cfg(target_arch = "aarch64")'.dependencies.inkwell]
|
||||
version = "0.1.0-llvm8sample"
|
||||
default-features = false
|
||||
features = ["llvm8-0", "target-aarch64"]
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix = "0.17"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["memoryapi"] }
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
lazy_static = "1.4"
|
||||
regex = "1.2"
|
||||
semver = "0.9"
|
||||
rustc_version = "0.2"
|
||||
|
||||
[features]
|
||||
test = []
|
2
lib/compiler-llvm/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
This is the `wasmer-compiler-llvm` crate, which contains the
|
||||
LLVM compiler implementation.
|
300
lib/compiler-llvm/build.rs
Normal file
@ -0,0 +1,300 @@
|
||||
//! This file was mostly taken from the llvm-sys crate.
|
||||
//! (https://bitbucket.org/tari/llvm-sys.rs/raw/94361c1083a88f439b9d24c59b2d2831517413d7/build.rs)
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use semver::Version;
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::io::{self, ErrorKind};
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
// Version of the llvm-sys crate that we (through inkwell) depend on.
|
||||
const LLVM_SYS_MAJOR_VERSION: &str = "80";
|
||||
const LLVM_SYS_MINOR_VERSION: &str = "0";
|
||||
|
||||
// Environment variables that can guide compilation
|
||||
//
|
||||
// When adding new ones, they should also be added to main() to force a
|
||||
// rebuild if they are changed.
|
||||
lazy_static! {
|
||||
|
||||
/// A single path to search for LLVM in (containing bin/llvm-config)
|
||||
static ref ENV_LLVM_PREFIX: String =
|
||||
format!("LLVM_SYS_{}_PREFIX", LLVM_SYS_MAJOR_VERSION);
|
||||
|
||||
/// If exactly "YES", ignore the version blacklist
|
||||
static ref ENV_IGNORE_BLACKLIST: String =
|
||||
format!("LLVM_SYS_{}_IGNORE_BLACKLIST", LLVM_SYS_MAJOR_VERSION);
|
||||
|
||||
/// If set, enforce precise correspondence between crate and binary versions.
|
||||
static ref ENV_STRICT_VERSIONING: String =
|
||||
format!("LLVM_SYS_{}_STRICT_VERSIONING", LLVM_SYS_MAJOR_VERSION);
|
||||
|
||||
/// If set, do not attempt to strip irrelevant options for llvm-config --cflags
|
||||
static ref ENV_NO_CLEAN_CXXFLAGS: String =
|
||||
format!("LLVM_SYS_{}_NO_CLEAN_CXXFLAGS", LLVM_SYS_MAJOR_VERSION);
|
||||
|
||||
/// If set and targeting MSVC, force the debug runtime library
|
||||
static ref ENV_USE_DEBUG_MSVCRT: String =
|
||||
format!("LLVM_SYS_{}_USE_DEBUG_MSVCRT", LLVM_SYS_MAJOR_VERSION);
|
||||
|
||||
/// If set, always link against libffi
|
||||
static ref ENV_FORCE_FFI: String =
|
||||
format!("LLVM_SYS_{}_FFI_WORKAROUND", LLVM_SYS_MAJOR_VERSION);
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// LLVM version used by this version of the crate.
|
||||
static ref CRATE_VERSION: Version = {
|
||||
Version::new(LLVM_SYS_MAJOR_VERSION.parse::<u64>().unwrap() / 10,
|
||||
LLVM_SYS_MINOR_VERSION.parse::<u64>().unwrap() % 10,
|
||||
0)
|
||||
};
|
||||
|
||||
static ref LLVM_CONFIG_BINARY_NAMES: Vec<String> = {
|
||||
vec![
|
||||
"llvm-config".into(),
|
||||
format!("llvm-config-{}", CRATE_VERSION.major),
|
||||
format!("llvm-config-{}.{}", CRATE_VERSION.major, CRATE_VERSION.minor),
|
||||
format!("llvm-config{}{}", CRATE_VERSION.major, CRATE_VERSION.minor),
|
||||
]
|
||||
};
|
||||
|
||||
/// Filesystem path to an llvm-config binary for the correct version.
|
||||
static ref LLVM_CONFIG_PATH: PathBuf = {
|
||||
// Try llvm-config via PATH first.
|
||||
if let Some(name) = locate_system_llvm_config() {
|
||||
return name.into();
|
||||
} else {
|
||||
println!("Didn't find usable system-wide LLVM.");
|
||||
}
|
||||
|
||||
// Did the user give us a binary path to use? If yes, try
|
||||
// to use that and fail if it doesn't work.
|
||||
if let Some(path) = env::var_os(&*ENV_LLVM_PREFIX) {
|
||||
for binary_name in LLVM_CONFIG_BINARY_NAMES.iter() {
|
||||
let mut pb: PathBuf = path.clone().into();
|
||||
pb.push("bin");
|
||||
pb.push(binary_name);
|
||||
|
||||
let ver = llvm_version(&pb)
|
||||
.expect(&format!("Failed to execute {:?}", &pb));
|
||||
if is_compatible_llvm(&ver) {
|
||||
return pb;
|
||||
} else {
|
||||
println!("LLVM binaries specified by {} are the wrong version.
|
||||
(Found {}, need {}.)", *ENV_LLVM_PREFIX, ver, *CRATE_VERSION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("No suitable version of LLVM was found system-wide or pointed
|
||||
to by {}.
|
||||
|
||||
Consider using `llvmenv` to compile an appropriate copy of LLVM, and
|
||||
refer to the llvm-sys documentation for more information.
|
||||
|
||||
llvm-sys: https://crates.io/crates/llvm-sys
|
||||
llvmenv: https://crates.io/crates/llvmenv", *ENV_LLVM_PREFIX);
|
||||
panic!("Could not find a compatible version of LLVM");
|
||||
};
|
||||
}
|
||||
|
||||
/// Try to find a system-wide version of llvm-config that is compatible with
|
||||
/// this crate.
|
||||
///
|
||||
/// Returns None on failure.
|
||||
fn locate_system_llvm_config() -> Option<&'static str> {
|
||||
for binary_name in LLVM_CONFIG_BINARY_NAMES.iter() {
|
||||
match llvm_version(binary_name) {
|
||||
Ok(ref version) if is_compatible_llvm(version) => {
|
||||
// Compatible version found. Nice.
|
||||
return Some(binary_name);
|
||||
}
|
||||
Ok(version) => {
|
||||
// Version mismatch. Will try further searches, but warn that
|
||||
// we're not using the system one.
|
||||
println!(
|
||||
"Found LLVM version {} on PATH, but need {}.",
|
||||
version, *CRATE_VERSION
|
||||
);
|
||||
}
|
||||
Err(ref e) if e.kind() == ErrorKind::NotFound => {
|
||||
// Looks like we failed to execute any llvm-config. Keep
|
||||
// searching.
|
||||
}
|
||||
// Some other error, probably a weird failure. Give up.
|
||||
Err(e) => panic!("Failed to search PATH for llvm-config: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Check whether the given version of LLVM is blacklisted,
|
||||
/// returning `Some(reason)` if it is.
|
||||
fn is_blacklisted_llvm(llvm_version: &Version) -> Option<&'static str> {
|
||||
static BLACKLIST: &'static [(u64, u64, u64, &'static str)] = &[];
|
||||
|
||||
if let Some(x) = env::var_os(&*ENV_IGNORE_BLACKLIST) {
|
||||
if &x == "YES" {
|
||||
println!(
|
||||
"cargo:warning=Ignoring blacklist entry for LLVM {}",
|
||||
llvm_version
|
||||
);
|
||||
return None;
|
||||
} else {
|
||||
println!(
|
||||
"cargo:warning={} is set but not exactly \"YES\"; blacklist is still honored.",
|
||||
*ENV_IGNORE_BLACKLIST
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for &(major, minor, patch, reason) in BLACKLIST.iter() {
|
||||
let bad_version = Version {
|
||||
major: major,
|
||||
minor: minor,
|
||||
patch: patch,
|
||||
pre: vec![],
|
||||
build: vec![],
|
||||
};
|
||||
|
||||
if &bad_version == llvm_version {
|
||||
return Some(reason);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Check whether the given LLVM version is compatible with this version of
|
||||
/// the crate.
|
||||
fn is_compatible_llvm(llvm_version: &Version) -> bool {
|
||||
if let Some(reason) = is_blacklisted_llvm(llvm_version) {
|
||||
println!(
|
||||
"Found LLVM {}, which is blacklisted: {}",
|
||||
llvm_version, reason
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
let strict =
|
||||
env::var_os(&*ENV_STRICT_VERSIONING).is_some() || cfg!(feature = "strict-versioning");
|
||||
if strict {
|
||||
llvm_version.major == CRATE_VERSION.major && llvm_version.minor == CRATE_VERSION.minor
|
||||
} else {
|
||||
llvm_version.major >= CRATE_VERSION.major
|
||||
|| (llvm_version.major == CRATE_VERSION.major
|
||||
&& llvm_version.minor >= CRATE_VERSION.minor)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the output from running `llvm-config` with the given argument.
|
||||
///
|
||||
/// Lazily searches for or compiles LLVM as configured by the environment
|
||||
/// variables.
|
||||
fn llvm_config(arg: &str) -> String {
|
||||
llvm_config_ex(&*LLVM_CONFIG_PATH, arg).expect("Surprising failure from llvm-config")
|
||||
}
|
||||
|
||||
/// Invoke the specified binary as llvm-config.
|
||||
///
|
||||
/// Explicit version of the `llvm_config` function that bubbles errors
|
||||
/// up.
|
||||
fn llvm_config_ex<S: AsRef<OsStr>>(binary: S, arg: &str) -> io::Result<String> {
|
||||
Command::new(binary)
|
||||
.arg(arg)
|
||||
.arg("--link-static") // Don't use dylib for >= 3.9
|
||||
.output()
|
||||
.map(|output| {
|
||||
String::from_utf8(output.stdout).expect("Output from llvm-config was not valid UTF-8")
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the LLVM version using llvm-config.
|
||||
fn llvm_version<S: AsRef<OsStr>>(binary: S) -> io::Result<Version> {
|
||||
let version_str = llvm_config_ex(binary.as_ref(), "--version")?;
|
||||
|
||||
// LLVM isn't really semver and uses version suffixes to build
|
||||
// version strings like '3.8.0svn', so limit what we try to parse
|
||||
// to only the numeric bits.
|
||||
let re = Regex::new(r"^(?P<major>\d+)\.(?P<minor>\d+)(?:\.(?P<patch>\d+))??").unwrap();
|
||||
let c = re
|
||||
.captures(&version_str)
|
||||
.expect("Could not determine LLVM version from llvm-config.");
|
||||
|
||||
// some systems don't have a patch number but Version wants it so we just append .0 if it isn't
|
||||
// there
|
||||
let s = match c.name("patch") {
|
||||
None => format!("{}.0", &c[0]),
|
||||
Some(_) => c[0].to_string(),
|
||||
};
|
||||
Ok(Version::parse(&s).unwrap())
|
||||
}
|
||||
|
||||
fn get_llvm_cxxflags() -> String {
|
||||
let output = llvm_config("--cxxflags");
|
||||
|
||||
// llvm-config includes cflags from its own compilation with --cflags that
|
||||
// may not be relevant to us. In particularly annoying cases, these might
|
||||
// include flags that aren't understood by the default compiler we're
|
||||
// using. Unless requested otherwise, clean CFLAGS of options that are
|
||||
// known to be possibly-harmful.
|
||||
let no_clean = env::var_os(&*ENV_NO_CLEAN_CXXFLAGS).is_some();
|
||||
if no_clean || cfg!(target_env = "msvc") {
|
||||
// MSVC doesn't accept -W... options, so don't try to strip them and
|
||||
// possibly strip something that should be retained. Also do nothing if
|
||||
// the user requests it.
|
||||
return output;
|
||||
}
|
||||
|
||||
output
|
||||
.split(&[' ', '\n'][..])
|
||||
.filter(|word| !word.starts_with("-W"))
|
||||
.filter(|word| word != &"-fno-exceptions")
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
}
|
||||
|
||||
fn is_llvm_debug() -> bool {
|
||||
// Has to be either Debug or Release
|
||||
llvm_config("--build-mode").contains("Debug")
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rustc-link-lib=static=llvm-backend");
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=cpp/object_loader.cpp");
|
||||
println!("cargo:rerun-if-changed=cpp/object_loader.hh");
|
||||
println!("cargo:rerun-if-env-changed={}", &*ENV_LLVM_PREFIX);
|
||||
println!("cargo:rerun-if-env-changed={}", &*ENV_IGNORE_BLACKLIST);
|
||||
println!("cargo:rerun-if-env-changed={}", &*ENV_STRICT_VERSIONING);
|
||||
println!("cargo:rerun-if-env-changed={}", &*ENV_NO_CLEAN_CXXFLAGS);
|
||||
println!("cargo:rerun-if-env-changed={}", &*ENV_USE_DEBUG_MSVCRT);
|
||||
println!("cargo:rerun-if-env-changed={}", &*ENV_FORCE_FFI);
|
||||
|
||||
std::env::set_var("CXXFLAGS", get_llvm_cxxflags());
|
||||
cc::Build::new()
|
||||
.cpp(true)
|
||||
.file("cpp/object_loader.cpp")
|
||||
.compile("llvm-backend");
|
||||
|
||||
// Enable "nightly" cfg if the current compiler is nightly.
|
||||
if rustc_version::version_meta().unwrap().channel == rustc_version::Channel::Nightly {
|
||||
println!("cargo:rustc-cfg=nightly");
|
||||
}
|
||||
|
||||
let use_debug_msvcrt = env::var_os(&*ENV_USE_DEBUG_MSVCRT).is_some();
|
||||
if cfg!(target_env = "msvc") && (use_debug_msvcrt || is_llvm_debug()) {
|
||||
println!("cargo:rustc-link-lib={}", "msvcrtd");
|
||||
}
|
||||
|
||||
// Link libffi if the user requested this workaround.
|
||||
// See https://bitbucket.org/tari/llvm-sys.rs/issues/12/
|
||||
let force_ffi = env::var_os(&*ENV_FORCE_FFI).is_some();
|
||||
if force_ffi {
|
||||
println!("cargo:rustc-link-lib=dylib={}", "ffi");
|
||||
}
|
||||
}
|
271
lib/compiler-llvm/cpp/object_loader.cpp
Normal file
@ -0,0 +1,271 @@
|
||||
#include "object_loader.hh"
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <setjmp.h>
|
||||
|
||||
extern "C" void __register_frame(uint8_t *);
|
||||
extern "C" void __deregister_frame(uint8_t *);
|
||||
|
||||
MemoryManager::~MemoryManager() {
|
||||
deregisterEHFrames();
|
||||
// Deallocate all of the allocated memory.
|
||||
callbacks.dealloc_memory(code_section.base, code_section.size);
|
||||
callbacks.dealloc_memory(read_section.base, read_section.size);
|
||||
callbacks.dealloc_memory(readwrite_section.base, readwrite_section.size);
|
||||
}
|
||||
void unwinding_setjmp(jmp_buf stack_out, void (*func)(void *), void *userdata) {
|
||||
if (setjmp(stack_out)) {
|
||||
|
||||
} else {
|
||||
func(userdata);
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]] void unwinding_longjmp(jmp_buf stack_in) { longjmp(stack_in, 42); }
|
||||
|
||||
struct UnwindPoint {
|
||||
UnwindPoint *prev;
|
||||
jmp_buf stack;
|
||||
std::function<void()> *f;
|
||||
std::unique_ptr<WasmException> exception;
|
||||
};
|
||||
|
||||
static thread_local UnwindPoint *unwind_state = nullptr;
|
||||
|
||||
static void unwind_payload(void *_point) {
|
||||
UnwindPoint *point = (UnwindPoint *)_point;
|
||||
(*point->f)();
|
||||
}
|
||||
|
||||
void catch_unwind(std::function<void()> &&f) {
|
||||
UnwindPoint current;
|
||||
current.prev = unwind_state;
|
||||
current.f = &f;
|
||||
unwind_state = ¤t;
|
||||
|
||||
unwinding_setjmp(current.stack, unwind_payload, (void *)¤t);
|
||||
|
||||
unwind_state = current.prev;
|
||||
if (current.exception) {
|
||||
throw std::move(current.exception);
|
||||
}
|
||||
}
|
||||
|
||||
void unsafe_unwind(WasmException *exception) {
|
||||
UnwindPoint *state = unwind_state;
|
||||
if (state) {
|
||||
state->exception.reset(exception);
|
||||
unwinding_longjmp(state->stack);
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *MemoryManager::allocateCodeSection(uintptr_t size, unsigned alignment,
|
||||
unsigned section_id,
|
||||
llvm::StringRef section_name) {
|
||||
return allocate_bump(code_section, code_bump_ptr, size, alignment);
|
||||
}
|
||||
|
||||
uint8_t *MemoryManager::allocateDataSection(uintptr_t size, unsigned alignment,
|
||||
unsigned section_id,
|
||||
llvm::StringRef section_name,
|
||||
bool read_only) {
|
||||
// Allocate from the read-only section or the read-write section, depending
|
||||
// on if this allocation should be read-only or not.
|
||||
uint8_t *ret;
|
||||
if (read_only) {
|
||||
ret = allocate_bump(read_section, read_bump_ptr, size, alignment);
|
||||
} else {
|
||||
ret = allocate_bump(readwrite_section, readwrite_bump_ptr, size, alignment);
|
||||
}
|
||||
if (section_name.equals(llvm::StringRef("__llvm_stackmaps")) ||
|
||||
section_name.equals(llvm::StringRef(".llvm_stackmaps"))) {
|
||||
stack_map_ptr = ret;
|
||||
stack_map_size = size;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MemoryManager::reserveAllocationSpace(uintptr_t code_size,
|
||||
uint32_t code_align,
|
||||
uintptr_t read_data_size,
|
||||
uint32_t read_data_align,
|
||||
uintptr_t read_write_data_size,
|
||||
uint32_t read_write_data_align) {
|
||||
auto aligner = [](uintptr_t ptr, size_t align) {
|
||||
if (ptr == 0) {
|
||||
return align;
|
||||
}
|
||||
return (ptr + align - 1) & ~(align - 1);
|
||||
};
|
||||
uint8_t *code_ptr_out = nullptr;
|
||||
size_t code_size_out = 0;
|
||||
auto code_result =
|
||||
callbacks.alloc_memory(aligner(code_size, 4096), PROTECT_READ_WRITE,
|
||||
&code_ptr_out, &code_size_out);
|
||||
assert(code_result == RESULT_OK);
|
||||
code_section = Section{code_ptr_out, code_size_out};
|
||||
code_bump_ptr = (uintptr_t)code_ptr_out;
|
||||
code_start_ptr = (uintptr_t)code_ptr_out;
|
||||
this->code_size = code_size;
|
||||
|
||||
uint8_t *read_ptr_out = nullptr;
|
||||
size_t read_size_out = 0;
|
||||
auto read_result =
|
||||
callbacks.alloc_memory(aligner(read_data_size, 4096), PROTECT_READ_WRITE,
|
||||
&read_ptr_out, &read_size_out);
|
||||
assert(read_result == RESULT_OK);
|
||||
read_section = Section{read_ptr_out, read_size_out};
|
||||
read_bump_ptr = (uintptr_t)read_ptr_out;
|
||||
|
||||
uint8_t *readwrite_ptr_out = nullptr;
|
||||
size_t readwrite_size_out = 0;
|
||||
auto readwrite_result = callbacks.alloc_memory(
|
||||
aligner(read_write_data_size, 4096), PROTECT_READ_WRITE,
|
||||
&readwrite_ptr_out, &readwrite_size_out);
|
||||
assert(readwrite_result == RESULT_OK);
|
||||
readwrite_section = Section{readwrite_ptr_out, readwrite_size_out};
|
||||
readwrite_bump_ptr = (uintptr_t)readwrite_ptr_out;
|
||||
}
|
||||
|
||||
bool MemoryManager::needsToReserveAllocationSpace() { return true; }
|
||||
|
||||
void MemoryManager::registerEHFrames(uint8_t *addr, uint64_t LoadAddr,
|
||||
size_t size) {
|
||||
// We don't know yet how to do this on Windows, so we hide this on compilation
|
||||
// so we can compile and pass spectests on unix systems
|
||||
#ifndef _WIN32
|
||||
eh_frame_ptr = addr;
|
||||
eh_frame_size = size;
|
||||
eh_frames_registered = true;
|
||||
callbacks.visit_fde(addr, size, __register_frame);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MemoryManager::deregisterEHFrames() {
|
||||
// We don't know yet how to do this on Windows, so we hide this on compilation
|
||||
// so we can compile and pass spectests on unix systems
|
||||
#ifndef _WIN32
|
||||
if (eh_frames_registered) {
|
||||
callbacks.visit_fde(eh_frame_ptr, eh_frame_size, __deregister_frame);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MemoryManager::finalizeMemory(std::string *ErrMsg) {
|
||||
auto code_result =
|
||||
callbacks.protect_memory(code_section.base, code_section.size,
|
||||
mem_protect_t::PROTECT_READ_EXECUTE);
|
||||
if (code_result != RESULT_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto read_result = callbacks.protect_memory(
|
||||
read_section.base, read_section.size, mem_protect_t::PROTECT_READ);
|
||||
if (read_result != RESULT_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The readwrite section is already mapped as read-write.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MemoryManager::notifyObjectLoaded(llvm::RuntimeDyld &RTDyld,
|
||||
const llvm::object::ObjectFile &Obj) {}
|
||||
|
||||
uint8_t *MemoryManager::allocate_bump(Section §ion, uintptr_t &bump_ptr,
|
||||
size_t size, size_t align) {
|
||||
auto aligner = [](uintptr_t &ptr, size_t align) {
|
||||
ptr = (ptr + align - 1) & ~(align - 1);
|
||||
};
|
||||
|
||||
// Align the bump pointer to the requires alignment.
|
||||
aligner(bump_ptr, align);
|
||||
|
||||
auto ret_ptr = bump_ptr;
|
||||
bump_ptr += size;
|
||||
|
||||
assert(bump_ptr <= (uintptr_t)section.base + section.size);
|
||||
|
||||
return (uint8_t *)ret_ptr;
|
||||
}
|
||||
|
||||
struct SymbolLookup : llvm::JITSymbolResolver {
|
||||
public:
|
||||
SymbolLookup(callbacks_t callbacks) : callbacks(callbacks) {}
|
||||
|
||||
void lookup(const LookupSet &symbols, OnResolvedFunction OnResolved) {
|
||||
LookupResult result;
|
||||
|
||||
for (auto symbol : symbols) {
|
||||
result.emplace(symbol, symbol_lookup(symbol));
|
||||
}
|
||||
|
||||
OnResolved(result);
|
||||
}
|
||||
|
||||
llvm::Expected<LookupSet> getResponsibilitySet(const LookupSet &Symbols) {
|
||||
const std::set<llvm::StringRef> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
private:
|
||||
llvm::JITEvaluatedSymbol symbol_lookup(llvm::StringRef name) {
|
||||
uint64_t addr = callbacks.lookup_vm_symbol(name.data(), name.size());
|
||||
|
||||
return llvm::JITEvaluatedSymbol(addr, llvm::JITSymbolFlags::None);
|
||||
}
|
||||
|
||||
callbacks_t callbacks;
|
||||
};
|
||||
|
||||
WasmModule::WasmModule(const uint8_t *object_start, size_t object_size,
|
||||
callbacks_t callbacks)
|
||||
: memory_manager(
|
||||
std::unique_ptr<MemoryManager>(new MemoryManager(callbacks))) {
|
||||
|
||||
if (auto created_object_file =
|
||||
llvm::object::ObjectFile::createObjectFile(llvm::MemoryBufferRef(
|
||||
llvm::StringRef((const char *)object_start, object_size),
|
||||
"object"))) {
|
||||
object_file = cantFail(std::move(created_object_file));
|
||||
SymbolLookup symbol_resolver(callbacks);
|
||||
runtime_dyld = std::unique_ptr<llvm::RuntimeDyld>(
|
||||
new llvm::RuntimeDyld(*memory_manager, symbol_resolver));
|
||||
|
||||
runtime_dyld->setProcessAllSections(true);
|
||||
|
||||
runtime_dyld->loadObject(*object_file);
|
||||
runtime_dyld->finalizeWithMemoryManagerLocking();
|
||||
|
||||
if (runtime_dyld->hasError()) {
|
||||
_init_failed = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
_init_failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void *WasmModule::get_func(llvm::StringRef name) const {
|
||||
auto symbol = runtime_dyld->getSymbol(name);
|
||||
return (void *)symbol.getAddress();
|
||||
}
|
||||
|
||||
uint8_t *WasmModule::get_stack_map_ptr() const {
|
||||
return memory_manager->get_stack_map_ptr();
|
||||
}
|
||||
|
||||
size_t WasmModule::get_stack_map_size() const {
|
||||
return memory_manager->get_stack_map_size();
|
||||
}
|
||||
|
||||
uint8_t *WasmModule::get_code_ptr() const {
|
||||
return memory_manager->get_code_ptr();
|
||||
}
|
||||
|
||||
size_t WasmModule::get_code_size() const {
|
||||
return memory_manager->get_code_size();
|
||||
}
|
328
lib/compiler-llvm/cpp/object_loader.hh
Normal file
@ -0,0 +1,328 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <setjmp.h>
|
||||
#include <sstream>
|
||||
|
||||
#include <llvm/ExecutionEngine/RuntimeDyld.h>
|
||||
|
||||
typedef enum {
|
||||
PROTECT_NONE,
|
||||
PROTECT_READ,
|
||||
PROTECT_READ_WRITE,
|
||||
PROTECT_READ_EXECUTE,
|
||||
} mem_protect_t;
|
||||
|
||||
typedef enum {
|
||||
RESULT_OK,
|
||||
RESULT_ALLOCATE_FAILURE,
|
||||
RESULT_PROTECT_FAILURE,
|
||||
RESULT_DEALLOC_FAILURE,
|
||||
RESULT_OBJECT_LOAD_FAILURE,
|
||||
} result_t;
|
||||
|
||||
typedef result_t (*alloc_memory_t)(size_t size, mem_protect_t protect,
|
||||
uint8_t **ptr_out, size_t *size_out);
|
||||
typedef result_t (*protect_memory_t)(uint8_t *ptr, size_t size,
|
||||
mem_protect_t protect);
|
||||
typedef result_t (*dealloc_memory_t)(uint8_t *ptr, size_t size);
|
||||
typedef uintptr_t (*lookup_vm_symbol_t)(const char *name_ptr, size_t length);
|
||||
typedef void (*fde_visitor_t)(uint8_t *fde);
|
||||
typedef result_t (*visit_fde_t)(uint8_t *fde, size_t size,
|
||||
fde_visitor_t visitor);
|
||||
|
||||
typedef void (*trampoline_t)(void *, void *, void *, void *);
|
||||
|
||||
typedef struct {
|
||||
/* Memory management. */
|
||||
alloc_memory_t alloc_memory;
|
||||
protect_memory_t protect_memory;
|
||||
dealloc_memory_t dealloc_memory;
|
||||
|
||||
lookup_vm_symbol_t lookup_vm_symbol;
|
||||
|
||||
visit_fde_t visit_fde;
|
||||
} callbacks_t;
|
||||
|
||||
typedef struct {
|
||||
size_t data, vtable;
|
||||
} box_any_t;
|
||||
|
||||
enum WasmTrapType {
|
||||
Unreachable = 0,
|
||||
IncorrectCallIndirectSignature = 1,
|
||||
MemoryOutOfBounds = 2,
|
||||
CallIndirectOOB = 3,
|
||||
IllegalArithmetic = 4,
|
||||
MisalignedAtomicAccess = 5,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
extern "C" void callback_trampoline(void *, void *);
|
||||
|
||||
struct MemoryManager : llvm::RuntimeDyld::MemoryManager {
|
||||
public:
|
||||
MemoryManager(callbacks_t callbacks) : callbacks(callbacks) {}
|
||||
virtual ~MemoryManager() override;
|
||||
|
||||
inline uint8_t *get_stack_map_ptr() const { return stack_map_ptr; }
|
||||
inline size_t get_stack_map_size() const { return stack_map_size; }
|
||||
inline uint8_t *get_code_ptr() const { return (uint8_t *)code_start_ptr; }
|
||||
inline size_t get_code_size() const { return code_size; }
|
||||
|
||||
virtual uint8_t *allocateCodeSection(uintptr_t size, unsigned alignment,
|
||||
unsigned section_id,
|
||||
llvm::StringRef section_name) override;
|
||||
virtual uint8_t *allocateDataSection(uintptr_t size, unsigned alignment,
|
||||
unsigned section_id,
|
||||
llvm::StringRef section_name,
|
||||
bool read_only) override;
|
||||
virtual void reserveAllocationSpace(uintptr_t code_size, uint32_t code_align,
|
||||
uintptr_t read_data_size,
|
||||
uint32_t read_data_align,
|
||||
uintptr_t read_write_data_size,
|
||||
uint32_t read_write_data_align) override;
|
||||
/* Turn on the `reserveAllocationSpace` callback. */
|
||||
virtual bool needsToReserveAllocationSpace() override;
|
||||
virtual void registerEHFrames(uint8_t *addr, uint64_t LoadAddr,
|
||||
size_t size) override;
|
||||
virtual void deregisterEHFrames() override;
|
||||
virtual bool finalizeMemory(std::string *ErrMsg = nullptr) override;
|
||||
virtual void notifyObjectLoaded(llvm::RuntimeDyld &RTDyld,
|
||||
const llvm::object::ObjectFile &Obj) override;
|
||||
|
||||
private:
|
||||
struct Section {
|
||||
uint8_t *base;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
uint8_t *allocate_bump(Section §ion, uintptr_t &bump_ptr, size_t size,
|
||||
size_t align);
|
||||
|
||||
Section code_section, read_section, readwrite_section;
|
||||
uintptr_t code_start_ptr;
|
||||
size_t code_size;
|
||||
uintptr_t code_bump_ptr, read_bump_ptr, readwrite_bump_ptr;
|
||||
uint8_t *eh_frame_ptr;
|
||||
size_t eh_frame_size;
|
||||
bool eh_frames_registered = false;
|
||||
|
||||
callbacks_t callbacks;
|
||||
|
||||
uint8_t *stack_map_ptr = nullptr;
|
||||
size_t stack_map_size = 0;
|
||||
};
|
||||
|
||||
struct WasmErrorSink {
|
||||
WasmTrapType *trap_out;
|
||||
box_any_t *user_error;
|
||||
};
|
||||
|
||||
struct WasmException : std::exception {
|
||||
public:
|
||||
virtual std::string description() const noexcept { return "unknown"; }
|
||||
|
||||
virtual const char *what() const noexcept override {
|
||||
return "wasm exception";
|
||||
}
|
||||
|
||||
virtual void write_error(WasmErrorSink &out) const noexcept {
|
||||
*out.trap_out = WasmTrapType::Unknown;
|
||||
}
|
||||
};
|
||||
|
||||
void catch_unwind(std::function<void()> &&f);
|
||||
[[noreturn]] void unsafe_unwind(WasmException *exception);
|
||||
|
||||
struct UncatchableException : WasmException {
|
||||
public:
|
||||
virtual std::string description() const noexcept override {
|
||||
return "Uncatchable exception";
|
||||
}
|
||||
};
|
||||
|
||||
struct UserException : UncatchableException {
|
||||
public:
|
||||
UserException(size_t data, size_t vtable) : error_data({data, vtable}) {}
|
||||
|
||||
virtual std::string description() const noexcept override {
|
||||
return "user exception";
|
||||
}
|
||||
|
||||
// The parts of a `Box<dyn Any>`.
|
||||
box_any_t error_data;
|
||||
|
||||
virtual void write_error(WasmErrorSink &out) const noexcept override {
|
||||
*out.user_error = error_data;
|
||||
}
|
||||
};
|
||||
|
||||
struct BreakpointException : UncatchableException {
|
||||
public:
|
||||
BreakpointException(uintptr_t callback) : callback(callback) {}
|
||||
|
||||
virtual std::string description() const noexcept override {
|
||||
return "breakpoint exception";
|
||||
}
|
||||
|
||||
uintptr_t callback;
|
||||
|
||||
virtual void write_error(WasmErrorSink &out) const noexcept override {
|
||||
puts("CB TRAMPOLINE");
|
||||
callback_trampoline(out.user_error, (void *)callback);
|
||||
}
|
||||
};
|
||||
|
||||
struct WasmModule {
|
||||
public:
|
||||
WasmModule(const uint8_t *object_start, size_t object_size,
|
||||
callbacks_t callbacks);
|
||||
|
||||
void *get_func(llvm::StringRef name) const;
|
||||
uint8_t *get_stack_map_ptr() const;
|
||||
size_t get_stack_map_size() const;
|
||||
uint8_t *get_code_ptr() const;
|
||||
size_t get_code_size() const;
|
||||
|
||||
bool _init_failed = false;
|
||||
|
||||
private:
|
||||
std::unique_ptr<MemoryManager> memory_manager;
|
||||
std::unique_ptr<llvm::object::ObjectFile> object_file;
|
||||
std::unique_ptr<llvm::RuntimeDyld> runtime_dyld;
|
||||
};
|
||||
|
||||
struct WasmTrap : UncatchableException {
|
||||
public:
|
||||
WasmTrap(WasmTrapType type) : type(type) {}
|
||||
|
||||
virtual std::string description() const noexcept override {
|
||||
std::ostringstream ss;
|
||||
ss << "WebAssembly trap:" << '\n' << " - type: " << type << '\n';
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
WasmTrapType type;
|
||||
|
||||
virtual void write_error(WasmErrorSink &out) const noexcept override {
|
||||
*out.trap_out = type;
|
||||
}
|
||||
|
||||
private:
|
||||
friend std::ostream &operator<<(std::ostream &out, const WasmTrapType &ty) {
|
||||
switch (ty) {
|
||||
case WasmTrapType::Unreachable:
|
||||
out << "unreachable";
|
||||
break;
|
||||
case WasmTrapType::IncorrectCallIndirectSignature:
|
||||
out << "incorrect call_indirect signature";
|
||||
break;
|
||||
case WasmTrapType::MemoryOutOfBounds:
|
||||
out << "memory access out-of-bounds";
|
||||
break;
|
||||
case WasmTrapType::CallIndirectOOB:
|
||||
out << "call_indirect out-of-bounds";
|
||||
break;
|
||||
case WasmTrapType::IllegalArithmetic:
|
||||
out << "illegal arithmetic operation";
|
||||
break;
|
||||
case WasmTrapType::Unknown:
|
||||
default:
|
||||
out << "unknown";
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
struct CatchableException : WasmException {
|
||||
public:
|
||||
CatchableException(uint32_t type_id, uint32_t value_num)
|
||||
: type_id(type_id), value_num(value_num) {}
|
||||
|
||||
virtual std::string description() const noexcept override {
|
||||
return "catchable exception";
|
||||
}
|
||||
|
||||
uint32_t type_id, value_num;
|
||||
uint64_t values[1];
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
result_t module_load(const uint8_t *mem_ptr, size_t mem_size,
|
||||
callbacks_t callbacks, WasmModule **module_out) {
|
||||
*module_out = new WasmModule(mem_ptr, mem_size, callbacks);
|
||||
|
||||
if ((*module_out)->_init_failed) {
|
||||
return RESULT_OBJECT_LOAD_FAILURE;
|
||||
}
|
||||
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
[[noreturn]] void throw_trap(WasmTrapType ty) {
|
||||
unsafe_unwind(new WasmTrap(ty));
|
||||
}
|
||||
|
||||
void module_delete(WasmModule *module) { delete module; }
|
||||
|
||||
// Throw a fat pointer that's assumed to be `*mut dyn Any` on the rust
|
||||
// side.
|
||||
[[noreturn]] void throw_any(size_t data, size_t vtable) {
|
||||
unsafe_unwind(new UserException(data, vtable));
|
||||
}
|
||||
|
||||
// Throw a pointer that's assumed to be codegen::BreakpointHandler on the
|
||||
// rust side.
|
||||
[[noreturn]] void throw_breakpoint(uintptr_t callback) {
|
||||
unsafe_unwind(new BreakpointException(callback));
|
||||
}
|
||||
|
||||
bool cxx_invoke_trampoline(trampoline_t trampoline, void *ctx, void *func,
|
||||
void *params, void *results, WasmTrapType *trap_out,
|
||||
box_any_t *user_error, void *invoke_env) noexcept {
|
||||
try {
|
||||
catch_unwind([trampoline, ctx, func, params, results]() {
|
||||
trampoline(ctx, func, params, results);
|
||||
});
|
||||
return true;
|
||||
} catch (std::unique_ptr<WasmException> &e) {
|
||||
WasmErrorSink sink;
|
||||
sink.trap_out = trap_out;
|
||||
sink.user_error = user_error;
|
||||
e->write_error(sink);
|
||||
return false;
|
||||
} catch (...) {
|
||||
*trap_out = WasmTrapType::Unknown;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void *get_func_symbol(WasmModule *module, const char *name) {
|
||||
return module->get_func(llvm::StringRef(name));
|
||||
}
|
||||
|
||||
const uint8_t *llvm_backend_get_stack_map_ptr(const WasmModule *module) {
|
||||
return module->get_stack_map_ptr();
|
||||
}
|
||||
|
||||
size_t llvm_backend_get_stack_map_size(const WasmModule *module) {
|
||||
return module->get_stack_map_size();
|
||||
}
|
||||
|
||||
const uint8_t *llvm_backend_get_code_ptr(const WasmModule *module) {
|
||||
return module->get_code_ptr();
|
||||
}
|
||||
|
||||
size_t llvm_backend_get_code_size(const WasmModule *module) {
|
||||
return module->get_code_size();
|
||||
}
|
||||
}
|
79
lib/compiler-llvm/src/compiler.rs
Normal file
@ -0,0 +1,79 @@
|
||||
//! Support for compiling with LLVM.
|
||||
// Allow unused imports while developing.
|
||||
#![allow(unused_imports, dead_code)]
|
||||
|
||||
use crate::code::FuncTranslator;
|
||||
use crate::config::LLVMConfig;
|
||||
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
|
||||
use wasm_common::entity::{EntityRef, PrimaryMap};
|
||||
use wasm_common::Features;
|
||||
use wasm_common::{DefinedFuncIndex, FuncIndex, FuncType};
|
||||
use wasmer_compiler::FunctionBodyData;
|
||||
use wasmer_compiler::Module;
|
||||
use wasmer_compiler::{Compilation, CompileError, CompiledFunction, Compiler};
|
||||
use wasmer_compiler::{CompilerConfig, ModuleTranslationState, Target};
|
||||
use wasmer_compiler::{TrapCode, TrapInformation};
|
||||
|
||||
use inkwell::targets::{InitializationConfig, Target as InkwellTarget};
|
||||
|
||||
/// A compiler that compiles a WebAssembly module with LLVM, translating the Wasm to LLVM IR,
|
||||
/// optimizing it and then translating to assembly.
|
||||
pub struct LLVMCompiler {
|
||||
config: LLVMConfig,
|
||||
}
|
||||
|
||||
impl LLVMCompiler {
|
||||
/// Creates a new LLVM compiler
|
||||
pub fn new(config: &LLVMConfig) -> LLVMCompiler {
|
||||
LLVMCompiler {
|
||||
config: config.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the WebAssembly features for this Compiler
|
||||
fn config(&self) -> &LLVMConfig {
|
||||
&self.config
|
||||
}
|
||||
}
|
||||
|
||||
impl Compiler for LLVMCompiler {
|
||||
/// Gets the WebAssembly features for this Compiler
|
||||
fn features(&self) -> Features {
|
||||
self.config.features().clone()
|
||||
}
|
||||
|
||||
/// Gets the target associated to this Compiler.
|
||||
fn target(&self) -> Target {
|
||||
self.config.target().clone()
|
||||
}
|
||||
|
||||
/// Compile the module using LLVM, producing a compilation result with
|
||||
/// associated relocations.
|
||||
fn compile_module(
|
||||
&self,
|
||||
module: &Module,
|
||||
_module_translation: &ModuleTranslationState,
|
||||
function_body_inputs: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'_>>,
|
||||
) -> Result<Compilation, CompileError> {
|
||||
let functions = function_body_inputs
|
||||
.into_iter()
|
||||
.collect::<Vec<(DefinedFuncIndex, &FunctionBodyData<'_>)>>()
|
||||
.par_iter()
|
||||
.map_init(FuncTranslator::new, |func_translator, (i, input)| {
|
||||
func_translator.translate(module, i, input, self.config())
|
||||
})
|
||||
.collect::<Result<Vec<_>, CompileError>>()?
|
||||
.into_iter()
|
||||
.collect::<PrimaryMap<DefinedFuncIndex, _>>();
|
||||
|
||||
Ok(Compilation::new(functions))
|
||||
}
|
||||
|
||||
fn compile_wasm_trampolines(
|
||||
&self,
|
||||
_signatures: &[FuncType],
|
||||
) -> Result<Vec<CompiledFunction>, CompileError> {
|
||||
// Note: do not implement this yet
|
||||
unimplemented!("Trampoline compilation not yet implemented.")
|
||||
}
|
||||
}
|
166
lib/compiler-llvm/src/config.rs
Normal file
@ -0,0 +1,166 @@
|
||||
// Allow unused imports while developing
|
||||
#![allow(unused_imports, dead_code)]
|
||||
|
||||
use crate::compiler::LLVMCompiler;
|
||||
use inkwell::targets::{
|
||||
CodeModel, InitializationConfig, RelocMode, Target as LLVMTarget, TargetMachine, TargetTriple,
|
||||
};
|
||||
use inkwell::OptimizationLevel;
|
||||
use itertools::Itertools;
|
||||
use target_lexicon::Architecture;
|
||||
use wasmer_compiler::{Compiler, CompilerConfig, CpuFeature, Features, Target};
|
||||
|
||||
|
||||
/// The InkWell Module type
|
||||
pub type InkwellModule<'ctx> = inkwell::module::Module<'ctx>;
|
||||
|
||||
/// The InkWell MemoryBuffer type
|
||||
pub type InkwellMemoryBuffer = inkwell::memory_buffer::MemoryBuffer;
|
||||
|
||||
/// Callbacks to
|
||||
pub trait LLVMCallbacks: std::any::Any + 'static {
|
||||
fn preopt_ir_callback(&mut self, module: &InkwellModule);
|
||||
fn postopt_ir_callback(&mut self, module: &InkwellModule);
|
||||
fn obj_memory_buffer_callback(&mut self, memory_buffer: &InkwellMemoryBuffer);
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LLVMConfig {
|
||||
/// Enable NaN canonicalization.
|
||||
///
|
||||
/// NaN canonicalization is useful when trying to run WebAssembly
|
||||
/// deterministically across different architectures.
|
||||
pub enable_nan_canonicalization: bool,
|
||||
|
||||
/// Should the LLVM IR verifier be enabled.
|
||||
///
|
||||
/// The verifier assures that the generated LLVM IR is valid.
|
||||
pub enable_verifier: bool,
|
||||
|
||||
/// The optimization levels when optimizing the IR.
|
||||
pub opt_level: OptimizationLevel,
|
||||
|
||||
features: Features,
|
||||
target: Target,
|
||||
}
|
||||
|
||||
impl LLVMConfig {
|
||||
/// Creates a new configuration object with the default configuration
|
||||
/// specified.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
enable_nan_canonicalization: true,
|
||||
enable_verifier: false,
|
||||
opt_level: OptimizationLevel::Aggressive,
|
||||
features: Default::default(),
|
||||
target: Default::default(),
|
||||
}
|
||||
}
|
||||
fn reloc_mode(&self) -> RelocMode {
|
||||
RelocMode::Static
|
||||
}
|
||||
|
||||
fn code_model(&self) -> CodeModel {
|
||||
CodeModel::Large
|
||||
}
|
||||
|
||||
pub fn target_triple(&self) -> TargetTriple {
|
||||
TargetTriple::create(&self.target().triple().to_string())
|
||||
}
|
||||
|
||||
/// Generates the target machine for the current target
|
||||
pub fn target_machine(&self) -> TargetMachine {
|
||||
let target = self.target();
|
||||
let triple = target.triple();
|
||||
let cpu_features = &target.cpu_features();
|
||||
|
||||
match triple.architecture {
|
||||
Architecture::X86_64 => LLVMTarget::initialize_x86(&InitializationConfig {
|
||||
asm_parser: true,
|
||||
asm_printer: true,
|
||||
base: true,
|
||||
disassembler: true,
|
||||
info: true,
|
||||
machine_code: true,
|
||||
}),
|
||||
Architecture::Arm(_) => LLVMTarget::initialize_aarch64(&InitializationConfig {
|
||||
asm_parser: true,
|
||||
asm_printer: true,
|
||||
base: true,
|
||||
disassembler: true,
|
||||
info: true,
|
||||
machine_code: true,
|
||||
}),
|
||||
_ => unimplemented!("target {} not supported", triple),
|
||||
}
|
||||
|
||||
// The CPU features formatted as LLVM strings
|
||||
let llvm_cpu_features = cpu_features
|
||||
.iter()
|
||||
.filter_map(|feature| match feature {
|
||||
CpuFeature::SSE2 => Some("+sse2"),
|
||||
CpuFeature::SSE3 => Some("+sse3"),
|
||||
CpuFeature::SSSE3 => Some("+ssse3"),
|
||||
CpuFeature::SSE41 => Some("+sse41"),
|
||||
CpuFeature::SSE42 => Some("+sse42"),
|
||||
CpuFeature::POPCNT => Some("+popcnt"),
|
||||
CpuFeature::AVX => Some("+avx"),
|
||||
CpuFeature::BMI1 => Some("+bmi"),
|
||||
CpuFeature::BMI2 => Some("+bmi2"),
|
||||
CpuFeature::AVX2 => Some("+avx2"),
|
||||
CpuFeature::AVX512DQ => Some("+avx512dq"),
|
||||
CpuFeature::AVX512VL => Some("+avx512vl"),
|
||||
CpuFeature::LZCNT => Some("+lzcnt"),
|
||||
})
|
||||
.join(",");
|
||||
|
||||
let arch_string = triple.architecture.to_string();
|
||||
let llvm_target = LLVMTarget::from_name(&arch_string).unwrap();
|
||||
let target_machine = llvm_target
|
||||
.create_target_machine(
|
||||
&self.target_triple(),
|
||||
&arch_string,
|
||||
&llvm_cpu_features,
|
||||
self.opt_level.clone(),
|
||||
self.reloc_mode(),
|
||||
self.code_model(),
|
||||
)
|
||||
.unwrap();
|
||||
target_machine
|
||||
}
|
||||
}
|
||||
|
||||
impl CompilerConfig for LLVMConfig {
|
||||
/// Gets the WebAssembly features
|
||||
fn features(&self) -> &Features {
|
||||
&self.features
|
||||
}
|
||||
|
||||
/// Gets the WebAssembly features, mutable
|
||||
fn features_mut(&mut self) -> &mut Features {
|
||||
&mut self.features
|
||||
}
|
||||
|
||||
/// Gets the target that we will use for compiling
|
||||
/// the WebAssembly module
|
||||
fn target(&self) -> &Target {
|
||||
&self.target
|
||||
}
|
||||
|
||||
/// Gets the target that we will use for compiling
|
||||
/// the WebAssembly module, mutable
|
||||
fn target_mut(&mut self) -> &mut Target {
|
||||
&mut self.target
|
||||
}
|
||||
|
||||
/// Transform it into the compiler
|
||||
fn compiler(&self) -> Box<dyn Compiler> {
|
||||
Box::new(LLVMCompiler::new(&self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LLVMConfig {
|
||||
fn default() -> LLVMConfig {
|
||||
LLVMConfig::new()
|
||||
}
|
||||
}
|
26
lib/compiler-llvm/src/lib.rs
Normal file
@ -0,0 +1,26 @@
|
||||
#![deny(
|
||||
nonstandard_style,
|
||||
/*
|
||||
unused_imports,
|
||||
unused_mut,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
*/
|
||||
unreachable_patterns
|
||||
)]
|
||||
#![cfg_attr(
|
||||
all(not(target_os = "windows"), not(target_arch = "aarch64")),
|
||||
//deny(dead_code)
|
||||
)]
|
||||
#![cfg_attr(nightly, feature(unwind_attributes))]
|
||||
#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")]
|
||||
#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")]
|
||||
|
||||
mod compiler;
|
||||
mod translator;
|
||||
mod config;
|
||||
mod structs;
|
||||
// mod trampoline;
|
||||
|
||||
pub use crate::compiler::LLVMCompiler;
|
||||
pub use crate::config::{LLVMConfig, LLVMCallbacks, InkwellModule, InkwellMemoryBuffer};
|
0
lib/compiler-llvm/src/trampoline/mod.rs
Normal file
119
lib/compiler-llvm/src/trampoline/wasm.rs
Normal file
@ -0,0 +1,119 @@
|
||||
use crate::intrinsics::Intrinsics;
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
module::{Linkage, Module},
|
||||
types::{BasicType, FunctionType},
|
||||
values::FunctionValue,
|
||||
AddressSpace,
|
||||
};
|
||||
use wasmer_runtime_core::{
|
||||
module::ModuleInfo,
|
||||
structures::{SliceMap, TypedIndex},
|
||||
types::{FuncSig, SigIndex, Type},
|
||||
};
|
||||
|
||||
pub fn generate_trampolines<'ctx>(
|
||||
info: &ModuleInfo,
|
||||
signatures: &SliceMap<SigIndex, FunctionType<'ctx>>,
|
||||
module: &Module<'ctx>,
|
||||
context: &'ctx Context,
|
||||
intrinsics: &Intrinsics<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
for (sig_index, sig) in info.signatures.iter() {
|
||||
let func_type = signatures[sig_index];
|
||||
|
||||
let trampoline_sig = intrinsics.void_ty.fn_type(
|
||||
&[
|
||||
intrinsics.ctx_ptr_ty.as_basic_type_enum(), // vmctx ptr
|
||||
func_type
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.as_basic_type_enum(), // func ptr
|
||||
intrinsics.i64_ptr_ty.as_basic_type_enum(), // args ptr
|
||||
intrinsics.i64_ptr_ty.as_basic_type_enum(), // returns ptr
|
||||
],
|
||||
false,
|
||||
);
|
||||
|
||||
let trampoline_func = module.add_function(
|
||||
&format!("trmp{}", sig_index.index()),
|
||||
trampoline_sig,
|
||||
Some(Linkage::External),
|
||||
);
|
||||
|
||||
generate_trampoline(trampoline_func, sig, context, intrinsics)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_trampoline<'ctx>(
|
||||
trampoline_func: FunctionValue,
|
||||
func_sig: &FuncSig,
|
||||
context: &'ctx Context,
|
||||
intrinsics: &Intrinsics<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
let entry_block = context.append_basic_block(trampoline_func, "entry");
|
||||
let builder = context.create_builder();
|
||||
builder.position_at_end(entry_block);
|
||||
|
||||
let (vmctx_ptr, func_ptr, args_ptr, returns_ptr) = match trampoline_func.get_params().as_slice()
|
||||
{
|
||||
&[vmctx_ptr, func_ptr, args_ptr, returns_ptr] => (
|
||||
vmctx_ptr,
|
||||
func_ptr.into_pointer_value(),
|
||||
args_ptr.into_pointer_value(),
|
||||
returns_ptr.into_pointer_value(),
|
||||
),
|
||||
_ => return Err("trampoline function unimplemented".to_string()),
|
||||
};
|
||||
|
||||
let cast_ptr_ty = |wasmer_ty| match wasmer_ty {
|
||||
Type::I32 => intrinsics.i32_ptr_ty,
|
||||
Type::F32 => intrinsics.f32_ptr_ty,
|
||||
Type::I64 => intrinsics.i64_ptr_ty,
|
||||
Type::F64 => intrinsics.f64_ptr_ty,
|
||||
Type::V128 => intrinsics.i128_ptr_ty,
|
||||
};
|
||||
|
||||
let mut args_vec = Vec::with_capacity(func_sig.params().len() + 1);
|
||||
args_vec.push(vmctx_ptr);
|
||||
|
||||
let mut i = 0;
|
||||
for param_ty in func_sig.params().iter() {
|
||||
let index = intrinsics.i32_ty.const_int(i as _, false);
|
||||
let item_pointer = unsafe { builder.build_in_bounds_gep(args_ptr, &[index], "arg_ptr") };
|
||||
|
||||
let casted_pointer_type = cast_ptr_ty(*param_ty);
|
||||
|
||||
let typed_item_pointer =
|
||||
builder.build_pointer_cast(item_pointer, casted_pointer_type, "typed_arg_pointer");
|
||||
|
||||
let arg = builder.build_load(typed_item_pointer, "arg");
|
||||
args_vec.push(arg);
|
||||
i = i + 1;
|
||||
if *param_ty == Type::V128 {
|
||||
i = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
let call_site = builder.build_call(func_ptr, &args_vec, "call");
|
||||
|
||||
match func_sig.returns() {
|
||||
&[] => {}
|
||||
&[one_ret] => {
|
||||
let ret_ptr_type = cast_ptr_ty(one_ret);
|
||||
|
||||
let typed_ret_ptr =
|
||||
builder.build_pointer_cast(returns_ptr, ret_ptr_type, "typed_ret_ptr");
|
||||
builder.build_store(
|
||||
typed_ret_ptr,
|
||||
call_site.try_as_basic_value().left().unwrap(),
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
return Err("trampoline function multi-value returns unimplemented".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
builder.build_return(None);
|
||||
Ok(())
|
||||
}
|
9171
lib/compiler-llvm/src/translator/code.rs
Normal file
1236
lib/compiler-llvm/src/translator/intrinsics.rs
Normal file
5
lib/compiler-llvm/src/translator/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
mod code;
|
||||
mod intrinsics;
|
||||
mod read_info;
|
||||
//mod stackmap;
|
||||
mod state;
|
35
lib/compiler-llvm/src/translator/read_info.rs
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
use wasmer_runtime_core::parse::{wp_type_to_type, LoadError};
|
||||
use wasmer_runtime_core::types::Type;
|
||||
*/
|
||||
use wasm_common::Type;
|
||||
use wasmer_compiler::CompileError;
|
||||
use wasmparser::Type as WpType;
|
||||
use wasmparser::TypeOrFuncType as WpTypeOrFuncType;
|
||||
|
||||
fn wp_type_to_type(ty: WpType) -> Result<Type, CompileError> {
|
||||
match ty {
|
||||
WpType::I32 => Ok(Type::I32),
|
||||
WpType::I64 => Ok(Type::I64),
|
||||
WpType::F32 => Ok(Type::F32),
|
||||
WpType::F64 => Ok(Type::F64),
|
||||
WpType::V128 => Ok(Type::V128),
|
||||
_ => {
|
||||
return Err(CompileError::Codegen(
|
||||
"broken invariant, invalid type".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn blocktype_to_type(ty: WpTypeOrFuncType) -> Result<Type, CompileError> {
|
||||
match ty {
|
||||
WpTypeOrFuncType::Type(inner_ty) => Ok(wp_type_to_type(inner_ty)?),
|
||||
_ => {
|
||||
return Err(CompileError::Codegen(
|
||||
"the wasmer llvm backend does not yet support the multi-value return extension"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
571
lib/compiler-llvm/src/translator/stackmap.rs
Normal file
@ -0,0 +1,571 @@
|
||||
// https://llvm.org/docs/StackMaps.html#stackmap-section
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use std::io::{self, Cursor};
|
||||
use wasmer_runtime_core::vm::Ctx;
|
||||
use wasmer_runtime_core::{
|
||||
module::ModuleInfo,
|
||||
structures::TypedIndex,
|
||||
types::{GlobalIndex, LocalOrImport, TableIndex},
|
||||
};
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct StackmapRegistry {
|
||||
pub entries: Vec<StackmapEntry>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StackmapEntry {
|
||||
pub kind: StackmapEntryKind,
|
||||
pub local_function_id: usize,
|
||||
pub opcode_offset: usize,
|
||||
pub value_semantics: Vec<ValueSemantic>,
|
||||
pub local_count: usize,
|
||||
pub stack_count: usize,
|
||||
pub is_start: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ValueSemantic {
|
||||
WasmLocal(usize),
|
||||
WasmStack(usize),
|
||||
Ctx,
|
||||
SignalMem,
|
||||
PointerToMemoryBase,
|
||||
PointerToMemoryBound, // 64-bit
|
||||
MemoryBase,
|
||||
MemoryBound, // 64-bit
|
||||
PointerToGlobal(usize),
|
||||
Global(usize),
|
||||
PointerToTableBase,
|
||||
PointerToTableBound,
|
||||
ImportedFuncPointer(usize),
|
||||
ImportedFuncCtx(usize),
|
||||
DynamicSigindice(usize),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum StackmapEntryKind {
|
||||
FunctionHeader,
|
||||
Loop,
|
||||
Call,
|
||||
Trappable,
|
||||
}
|
||||
|
||||
impl StackmapEntry {
|
||||
#[cfg(all(
|
||||
any(target_os = "freebsd", target_os = "linux", target_os = "macos"),
|
||||
target_arch = "x86_64"
|
||||
))]
|
||||
pub fn populate_msm(
|
||||
&self,
|
||||
module_info: &ModuleInfo,
|
||||
code_addr: usize,
|
||||
llvm_map: &StackMap,
|
||||
size_record: &StkSizeRecord,
|
||||
map_record: &StkMapRecord,
|
||||
end: Option<(&StackmapEntry, &StkMapRecord)>,
|
||||
msm: &mut wasmer_runtime_core::state::ModuleStateMap,
|
||||
) {
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use wasmer_runtime_core::state::{
|
||||
x64::{new_machine_state, X64Register, GPR},
|
||||
FunctionStateMap, MachineStateDiff, MachineValue, OffsetInfo, RegisterIndex,
|
||||
SuspendOffset, WasmAbstractValue,
|
||||
};
|
||||
use wasmer_runtime_core::vm;
|
||||
|
||||
let func_base_addr = (size_record.function_address as usize)
|
||||
.checked_sub(code_addr)
|
||||
.unwrap();
|
||||
let target_offset = func_base_addr + map_record.instruction_offset as usize;
|
||||
assert!(self.is_start);
|
||||
|
||||
if msm.local_functions.len() == self.local_function_id {
|
||||
assert_eq!(self.kind, StackmapEntryKind::FunctionHeader);
|
||||
msm.local_functions.insert(
|
||||
target_offset,
|
||||
FunctionStateMap::new(new_machine_state(), self.local_function_id, 0, vec![]),
|
||||
);
|
||||
} else if msm.local_functions.len() == self.local_function_id + 1 {
|
||||
} else {
|
||||
panic!("unordered local functions");
|
||||
}
|
||||
|
||||
let (_, fsm) = msm.local_functions.iter_mut().last().unwrap();
|
||||
|
||||
assert_eq!(self.value_semantics.len(), map_record.locations.len());
|
||||
|
||||
// System V requires 16-byte alignment before each call instruction.
|
||||
// Considering the saved rbp we need to ensure the stack size % 16 always equals to 8.
|
||||
assert!(size_record.stack_size % 16 == 8);
|
||||
|
||||
// Layout begins just below saved rbp. (push rbp; mov rbp, rsp)
|
||||
let mut machine_stack_half_layout: Vec<MachineValue> =
|
||||
vec![MachineValue::Undefined; (size_record.stack_size - 8) as usize / 4];
|
||||
let mut regs: Vec<(RegisterIndex, MachineValue)> = vec![];
|
||||
let mut stack_constants: HashMap<usize, u64> = HashMap::new();
|
||||
|
||||
let mut prev_frame_diff: BTreeMap<usize, Option<MachineValue>> = BTreeMap::new();
|
||||
|
||||
let mut wasm_locals: Vec<WasmAbstractValue> = vec![];
|
||||
let mut wasm_stack: Vec<WasmAbstractValue> = vec![];
|
||||
|
||||
for (i, loc) in map_record.locations.iter().enumerate() {
|
||||
let mv = match self.value_semantics[i] {
|
||||
ValueSemantic::WasmLocal(x) => {
|
||||
if x != wasm_locals.len() {
|
||||
panic!("unordered local values");
|
||||
}
|
||||
wasm_locals.push(WasmAbstractValue::Runtime);
|
||||
MachineValue::WasmLocal(x)
|
||||
}
|
||||
ValueSemantic::WasmStack(x) => {
|
||||
if x != wasm_stack.len() {
|
||||
panic!("unordered stack values");
|
||||
}
|
||||
wasm_stack.push(WasmAbstractValue::Runtime);
|
||||
MachineValue::WasmStack(x)
|
||||
}
|
||||
ValueSemantic::Ctx => MachineValue::Vmctx,
|
||||
ValueSemantic::SignalMem => {
|
||||
MachineValue::VmctxDeref(vec![Ctx::offset_interrupt_signal_mem() as usize, 0])
|
||||
}
|
||||
ValueSemantic::PointerToMemoryBase => {
|
||||
MachineValue::VmctxDeref(vec![Ctx::offset_memory_base() as usize])
|
||||
}
|
||||
ValueSemantic::PointerToMemoryBound => {
|
||||
MachineValue::VmctxDeref(vec![Ctx::offset_memory_bound() as usize])
|
||||
}
|
||||
ValueSemantic::MemoryBase => {
|
||||
MachineValue::VmctxDeref(vec![Ctx::offset_memory_base() as usize, 0])
|
||||
}
|
||||
ValueSemantic::MemoryBound => {
|
||||
MachineValue::VmctxDeref(vec![Ctx::offset_memory_bound() as usize, 0])
|
||||
}
|
||||
ValueSemantic::PointerToGlobal(idx) => {
|
||||
MachineValue::VmctxDeref(deref_global(module_info, idx, false))
|
||||
}
|
||||
ValueSemantic::Global(idx) => {
|
||||
MachineValue::VmctxDeref(deref_global(module_info, idx, true))
|
||||
}
|
||||
ValueSemantic::PointerToTableBase => {
|
||||
MachineValue::VmctxDeref(deref_table_base(module_info, 0, false))
|
||||
}
|
||||
ValueSemantic::PointerToTableBound => {
|
||||
MachineValue::VmctxDeref(deref_table_bound(module_info, 0, false))
|
||||
}
|
||||
ValueSemantic::ImportedFuncPointer(idx) => MachineValue::VmctxDeref(vec![
|
||||
Ctx::offset_imported_funcs() as usize,
|
||||
vm::ImportedFunc::size() as usize * idx
|
||||
+ vm::ImportedFunc::offset_func() as usize,
|
||||
0,
|
||||
]),
|
||||
ValueSemantic::ImportedFuncCtx(idx) => MachineValue::VmctxDeref(vec![
|
||||
Ctx::offset_imported_funcs() as usize,
|
||||
vm::ImportedFunc::size() as usize * idx
|
||||
+ vm::ImportedFunc::offset_func_ctx() as usize,
|
||||
0,
|
||||
]),
|
||||
ValueSemantic::DynamicSigindice(idx) => {
|
||||
MachineValue::VmctxDeref(vec![Ctx::offset_signatures() as usize, idx * 4, 0])
|
||||
}
|
||||
};
|
||||
match loc.ty {
|
||||
LocationType::Register => {
|
||||
let index = X64Register::from_dwarf_regnum(loc.dwarf_regnum)
|
||||
.expect("invalid regnum")
|
||||
.to_index();
|
||||
regs.push((index, mv));
|
||||
}
|
||||
LocationType::Constant => {
|
||||
let v = loc.offset_or_small_constant as u32 as u64;
|
||||
match mv {
|
||||
MachineValue::WasmStack(x) => {
|
||||
stack_constants.insert(x, v);
|
||||
*wasm_stack.last_mut().unwrap() = WasmAbstractValue::Const(v);
|
||||
}
|
||||
_ => {} // TODO
|
||||
}
|
||||
}
|
||||
LocationType::ConstantIndex => {
|
||||
let v =
|
||||
llvm_map.constants[loc.offset_or_small_constant as usize].large_constant;
|
||||
match mv {
|
||||
MachineValue::WasmStack(x) => {
|
||||
stack_constants.insert(x, v);
|
||||
*wasm_stack.last_mut().unwrap() = WasmAbstractValue::Const(v);
|
||||
}
|
||||
_ => {} // TODO
|
||||
}
|
||||
}
|
||||
LocationType::Direct => match mv {
|
||||
MachineValue::WasmLocal(_) => {
|
||||
assert_eq!(loc.location_size, 8); // the pointer itself
|
||||
assert!(
|
||||
X64Register::from_dwarf_regnum(loc.dwarf_regnum).unwrap()
|
||||
== X64Register::GPR(GPR::RBP)
|
||||
);
|
||||
if loc.offset_or_small_constant >= 0 {
|
||||
assert!(loc.offset_or_small_constant >= 16); // (saved_rbp, return_address)
|
||||
assert!(loc.offset_or_small_constant % 8 == 0);
|
||||
prev_frame_diff
|
||||
.insert((loc.offset_or_small_constant as usize - 16) / 8, Some(mv));
|
||||
} else {
|
||||
let stack_offset = ((-loc.offset_or_small_constant) / 4) as usize;
|
||||
assert!(
|
||||
stack_offset > 0 && stack_offset <= machine_stack_half_layout.len()
|
||||
);
|
||||
machine_stack_half_layout[stack_offset - 1] = mv;
|
||||
}
|
||||
}
|
||||
_ => unreachable!(
|
||||
"Direct location type is not expected for values other than local"
|
||||
),
|
||||
},
|
||||
LocationType::Indirect => {
|
||||
assert!(loc.offset_or_small_constant < 0);
|
||||
assert!(
|
||||
X64Register::from_dwarf_regnum(loc.dwarf_regnum).unwrap()
|
||||
== X64Register::GPR(GPR::RBP)
|
||||
);
|
||||
let stack_offset = ((-loc.offset_or_small_constant) / 4) as usize;
|
||||
assert!(stack_offset > 0 && stack_offset <= machine_stack_half_layout.len());
|
||||
machine_stack_half_layout[stack_offset - 1] = mv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(wasm_stack.len(), self.stack_count);
|
||||
assert_eq!(wasm_locals.len(), self.local_count);
|
||||
|
||||
let mut machine_stack_layout: Vec<MachineValue> =
|
||||
Vec::with_capacity(machine_stack_half_layout.len() / 2);
|
||||
|
||||
for i in 0..machine_stack_half_layout.len() / 2 {
|
||||
let major = &machine_stack_half_layout[i * 2 + 1]; // mod 8 == 0
|
||||
let minor = &machine_stack_half_layout[i * 2]; // mod 8 == 4
|
||||
let only_major = match *minor {
|
||||
MachineValue::Undefined => true,
|
||||
_ => false,
|
||||
};
|
||||
if only_major {
|
||||
machine_stack_layout.push(major.clone());
|
||||
} else {
|
||||
machine_stack_layout.push(MachineValue::TwoHalves(Box::new((
|
||||
major.clone(),
|
||||
minor.clone(),
|
||||
))));
|
||||
}
|
||||
}
|
||||
|
||||
let diff = MachineStateDiff {
|
||||
last: None,
|
||||
stack_push: machine_stack_layout,
|
||||
stack_pop: 0,
|
||||
prev_frame_diff,
|
||||
reg_diff: regs,
|
||||
wasm_stack_push: wasm_stack,
|
||||
wasm_stack_pop: 0,
|
||||
wasm_stack_private_depth: 0,
|
||||
wasm_inst_offset: self.opcode_offset,
|
||||
};
|
||||
let diff_id = fsm.diffs.len();
|
||||
fsm.diffs.push(diff);
|
||||
|
||||
match self.kind {
|
||||
StackmapEntryKind::FunctionHeader => {
|
||||
fsm.locals = wasm_locals;
|
||||
}
|
||||
_ => {
|
||||
assert_eq!(fsm.locals, wasm_locals);
|
||||
}
|
||||
}
|
||||
|
||||
let end_offset = {
|
||||
if let Some(end) = end {
|
||||
let (end_entry, end_record) = end;
|
||||
assert_eq!(end_entry.is_start, false);
|
||||
assert_eq!(self.opcode_offset, end_entry.opcode_offset);
|
||||
let end_offset = func_base_addr + end_record.instruction_offset as usize;
|
||||
assert!(end_offset >= target_offset);
|
||||
end_offset
|
||||
} else {
|
||||
target_offset + 1
|
||||
}
|
||||
};
|
||||
|
||||
match self.kind {
|
||||
StackmapEntryKind::Loop => {
|
||||
fsm.wasm_offset_to_target_offset
|
||||
.insert(self.opcode_offset, SuspendOffset::Loop(target_offset));
|
||||
fsm.loop_offsets.insert(
|
||||
target_offset,
|
||||
OffsetInfo {
|
||||
end_offset,
|
||||
diff_id,
|
||||
activate_offset: target_offset,
|
||||
},
|
||||
);
|
||||
}
|
||||
StackmapEntryKind::Call => {
|
||||
fsm.wasm_offset_to_target_offset
|
||||
.insert(self.opcode_offset, SuspendOffset::Call(target_offset));
|
||||
fsm.call_offsets.insert(
|
||||
target_offset,
|
||||
OffsetInfo {
|
||||
end_offset: end_offset + 1, // The return address is just after 'call' instruction. Offset by one here.
|
||||
diff_id,
|
||||
activate_offset: target_offset,
|
||||
},
|
||||
);
|
||||
}
|
||||
StackmapEntryKind::Trappable => {
|
||||
fsm.wasm_offset_to_target_offset
|
||||
.insert(self.opcode_offset, SuspendOffset::Trappable(target_offset));
|
||||
fsm.trappable_offsets.insert(
|
||||
target_offset,
|
||||
OffsetInfo {
|
||||
end_offset,
|
||||
diff_id,
|
||||
activate_offset: target_offset,
|
||||
},
|
||||
);
|
||||
}
|
||||
StackmapEntryKind::FunctionHeader => {
|
||||
fsm.wasm_function_header_target_offset = Some(SuspendOffset::Loop(target_offset));
|
||||
fsm.loop_offsets.insert(
|
||||
target_offset,
|
||||
OffsetInfo {
|
||||
end_offset,
|
||||
diff_id,
|
||||
activate_offset: target_offset,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct StackMap {
|
||||
pub version: u8,
|
||||
pub stk_size_records: Vec<StkSizeRecord>,
|
||||
pub constants: Vec<Constant>,
|
||||
pub stk_map_records: Vec<StkMapRecord>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct StkSizeRecord {
|
||||
pub function_address: u64,
|
||||
pub stack_size: u64,
|
||||
pub record_count: u64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct Constant {
|
||||
pub large_constant: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct StkMapRecord {
|
||||
pub patchpoint_id: u64,
|
||||
pub instruction_offset: u32,
|
||||
pub locations: Vec<Location>,
|
||||
pub live_outs: Vec<LiveOut>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Location {
|
||||
pub ty: LocationType,
|
||||
pub location_size: u16,
|
||||
pub dwarf_regnum: u16,
|
||||
pub offset_or_small_constant: i32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct LiveOut {
|
||||
pub dwarf_regnum: u16,
|
||||
pub size_in_bytes: u8,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum LocationType {
|
||||
Register,
|
||||
Direct,
|
||||
Indirect,
|
||||
Constant,
|
||||
ConstantIndex,
|
||||
}
|
||||
|
||||
impl StackMap {
|
||||
pub fn parse(raw: &[u8]) -> io::Result<StackMap> {
|
||||
let mut reader = Cursor::new(raw);
|
||||
let mut map = StackMap::default();
|
||||
|
||||
let version = reader.read_u8()?;
|
||||
if version != 3 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "version is not 3"));
|
||||
}
|
||||
map.version = version;
|
||||
if reader.read_u8()? != 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"reserved field is not zero (1)",
|
||||
));
|
||||
}
|
||||
if reader.read_u16::<LittleEndian>()? != 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"reserved field is not zero (2)",
|
||||
));
|
||||
}
|
||||
let num_functions = reader.read_u32::<LittleEndian>()?;
|
||||
let num_constants = reader.read_u32::<LittleEndian>()?;
|
||||
let num_records = reader.read_u32::<LittleEndian>()?;
|
||||
for _ in 0..num_functions {
|
||||
let mut record = StkSizeRecord::default();
|
||||
record.function_address = reader.read_u64::<LittleEndian>()?;
|
||||
record.stack_size = reader.read_u64::<LittleEndian>()?;
|
||||
record.record_count = reader.read_u64::<LittleEndian>()?;
|
||||
map.stk_size_records.push(record);
|
||||
}
|
||||
for _ in 0..num_constants {
|
||||
map.constants.push(Constant {
|
||||
large_constant: reader.read_u64::<LittleEndian>()?,
|
||||
});
|
||||
}
|
||||
for _ in 0..num_records {
|
||||
let mut record = StkMapRecord::default();
|
||||
|
||||
record.patchpoint_id = reader.read_u64::<LittleEndian>()?;
|
||||
record.instruction_offset = reader.read_u32::<LittleEndian>()?;
|
||||
if reader.read_u16::<LittleEndian>()? != 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"reserved field is not zero (3)",
|
||||
));
|
||||
}
|
||||
let num_locations = reader.read_u16::<LittleEndian>()?;
|
||||
for _ in 0..num_locations {
|
||||
let ty = reader.read_u8()?;
|
||||
|
||||
let mut location = Location {
|
||||
ty: match ty {
|
||||
1 => LocationType::Register,
|
||||
2 => LocationType::Direct,
|
||||
3 => LocationType::Indirect,
|
||||
4 => LocationType::Constant,
|
||||
5 => LocationType::ConstantIndex,
|
||||
_ => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"unknown location type",
|
||||
))
|
||||
}
|
||||
},
|
||||
location_size: 0,
|
||||
dwarf_regnum: 0,
|
||||
offset_or_small_constant: 0,
|
||||
};
|
||||
|
||||
if reader.read_u8()? != 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"reserved field is not zero (4)",
|
||||
));
|
||||
}
|
||||
location.location_size = reader.read_u16::<LittleEndian>()?;
|
||||
location.dwarf_regnum = reader.read_u16::<LittleEndian>()?;
|
||||
if reader.read_u16::<LittleEndian>()? != 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"reserved field is not zero (5)",
|
||||
));
|
||||
}
|
||||
location.offset_or_small_constant = reader.read_i32::<LittleEndian>()?;
|
||||
|
||||
record.locations.push(location);
|
||||
}
|
||||
if reader.position() % 8 != 0 {
|
||||
if reader.read_u32::<LittleEndian>()? != 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"reserved field is not zero (6)",
|
||||
));
|
||||
}
|
||||
}
|
||||
if reader.read_u16::<LittleEndian>()? != 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"reserved field is not zero (7)",
|
||||
));
|
||||
}
|
||||
let num_live_outs = reader.read_u16::<LittleEndian>()?;
|
||||
for _ in 0..num_live_outs {
|
||||
let mut liveout = LiveOut::default();
|
||||
|
||||
liveout.dwarf_regnum = reader.read_u16::<LittleEndian>()?;
|
||||
if reader.read_u8()? != 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"reserved field is not zero (8)",
|
||||
));
|
||||
}
|
||||
liveout.size_in_bytes = reader.read_u8()?;
|
||||
|
||||
record.live_outs.push(liveout);
|
||||
}
|
||||
if reader.position() % 8 != 0 {
|
||||
if reader.read_u32::<LittleEndian>()? != 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"reserved field is not zero (9)",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
map.stk_map_records.push(record);
|
||||
}
|
||||
Ok(map)
|
||||
}
|
||||
}
|
||||
|
||||
fn deref_global(info: &ModuleInfo, idx: usize, deref_into_value: bool) -> Vec<usize> {
|
||||
let mut x: Vec<usize> = match GlobalIndex::new(idx).local_or_import(info) {
|
||||
LocalOrImport::Local(idx) => vec![Ctx::offset_globals() as usize, idx.index() * 8, 0],
|
||||
LocalOrImport::Import(idx) => {
|
||||
vec![Ctx::offset_imported_globals() as usize, idx.index() * 8, 0]
|
||||
}
|
||||
};
|
||||
if deref_into_value {
|
||||
x.push(0);
|
||||
}
|
||||
x
|
||||
}
|
||||
|
||||
fn deref_table_base(info: &ModuleInfo, idx: usize, deref_into_value: bool) -> Vec<usize> {
|
||||
let mut x: Vec<usize> = match TableIndex::new(idx).local_or_import(info) {
|
||||
LocalOrImport::Local(idx) => vec![Ctx::offset_tables() as usize, idx.index() * 8, 0],
|
||||
LocalOrImport::Import(idx) => {
|
||||
vec![Ctx::offset_imported_tables() as usize, idx.index() * 8, 0]
|
||||
}
|
||||
};
|
||||
if deref_into_value {
|
||||
x.push(0);
|
||||
}
|
||||
x
|
||||
}
|
||||
|
||||
fn deref_table_bound(info: &ModuleInfo, idx: usize, deref_into_value: bool) -> Vec<usize> {
|
||||
let mut x: Vec<usize> = match TableIndex::new(idx).local_or_import(info) {
|
||||
LocalOrImport::Local(idx) => vec![Ctx::offset_tables() as usize, idx.index() * 8, 8],
|
||||
LocalOrImport::Import(idx) => {
|
||||
vec![Ctx::offset_imported_tables() as usize, idx.index() * 8, 8]
|
||||
}
|
||||
};
|
||||
if deref_into_value {
|
||||
x.push(0);
|
||||
}
|
||||
x
|
||||
}
|
423
lib/compiler-llvm/src/translator/state.rs
Normal file
@ -0,0 +1,423 @@
|
||||
use inkwell::{
|
||||
basic_block::BasicBlock,
|
||||
values::{BasicValue, BasicValueEnum, PhiValue},
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::cell::Cell;
|
||||
use std::ops::{BitAnd, BitOr, BitOrAssign};
|
||||
use wasmer_compiler::CompileError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ControlFrame<'ctx> {
|
||||
Block {
|
||||
next: BasicBlock<'ctx>,
|
||||
phis: SmallVec<[PhiValue<'ctx>; 1]>,
|
||||
stack_size_snapshot: usize,
|
||||
},
|
||||
Loop {
|
||||
body: BasicBlock<'ctx>,
|
||||
next: BasicBlock<'ctx>,
|
||||
phis: SmallVec<[PhiValue<'ctx>; 1]>,
|
||||
stack_size_snapshot: usize,
|
||||
},
|
||||
IfElse {
|
||||
if_then: BasicBlock<'ctx>,
|
||||
if_else: BasicBlock<'ctx>,
|
||||
next: BasicBlock<'ctx>,
|
||||
phis: SmallVec<[PhiValue<'ctx>; 1]>,
|
||||
stack_size_snapshot: usize,
|
||||
if_else_state: IfElseState,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum IfElseState {
|
||||
If,
|
||||
Else,
|
||||
}
|
||||
|
||||
impl<'ctx> ControlFrame<'ctx> {
|
||||
pub fn code_after(&self) -> &BasicBlock<'ctx> {
|
||||
match self {
|
||||
ControlFrame::Block { ref next, .. }
|
||||
| ControlFrame::Loop { ref next, .. }
|
||||
| ControlFrame::IfElse { ref next, .. } => next,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn br_dest(&self) -> &BasicBlock<'ctx> {
|
||||
match self {
|
||||
ControlFrame::Block { ref next, .. } | ControlFrame::IfElse { ref next, .. } => next,
|
||||
ControlFrame::Loop { ref body, .. } => body,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn phis(&self) -> &[PhiValue<'ctx>] {
|
||||
match self {
|
||||
ControlFrame::Block { ref phis, .. }
|
||||
| ControlFrame::Loop { ref phis, .. }
|
||||
| ControlFrame::IfElse { ref phis, .. } => phis.as_slice(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_loop(&self) -> bool {
|
||||
match self {
|
||||
ControlFrame::Loop { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Eq, PartialEq, Copy, Clone, Hash)]
|
||||
pub struct ExtraInfo {
|
||||
state: u8,
|
||||
}
|
||||
impl ExtraInfo {
|
||||
// This value is required to be arithmetic 32-bit NaN (or 32x4) by the WAsm
|
||||
// machine, but which might not be in the LLVM value. The conversion to
|
||||
// arithmetic NaN is pending. It is required for correctness.
|
||||
//
|
||||
// When applied to a 64-bit value, this flag has no meaning and must be
|
||||
// ignored. It may be set in such cases to allow for common handling of
|
||||
// 32 and 64-bit operations.
|
||||
pub const fn pending_f32_nan() -> ExtraInfo {
|
||||
ExtraInfo { state: 1 }
|
||||
}
|
||||
|
||||
// This value is required to be arithmetic 64-bit NaN (or 64x2) by the WAsm
|
||||
// machine, but which might not be in the LLVM value. The conversion to
|
||||
// arithmetic NaN is pending. It is required for correctness.
|
||||
//
|
||||
// When applied to a 32-bit value, this flag has no meaning and must be
|
||||
// ignored. It may be set in such cases to allow for common handling of
|
||||
// 32 and 64-bit operations.
|
||||
pub const fn pending_f64_nan() -> ExtraInfo {
|
||||
ExtraInfo { state: 2 }
|
||||
}
|
||||
|
||||
// This value either does not contain a 32-bit NaN, or it contains an
|
||||
// arithmetic NaN. In SIMD, applies to all 4 lanes.
|
||||
pub const fn arithmetic_f32() -> ExtraInfo {
|
||||
ExtraInfo { state: 4 }
|
||||
}
|
||||
|
||||
// This value either does not contain a 64-bit NaN, or it contains an
|
||||
// arithmetic NaN. In SIMD, applies to both lanes.
|
||||
pub const fn arithmetic_f64() -> ExtraInfo {
|
||||
ExtraInfo { state: 8 }
|
||||
}
|
||||
|
||||
pub const fn has_pending_f32_nan(&self) -> bool {
|
||||
self.state & ExtraInfo::pending_f32_nan().state != 0
|
||||
}
|
||||
pub const fn has_pending_f64_nan(&self) -> bool {
|
||||
self.state & ExtraInfo::pending_f64_nan().state != 0
|
||||
}
|
||||
pub const fn is_arithmetic_f32(&self) -> bool {
|
||||
self.state & ExtraInfo::arithmetic_f32().state != 0
|
||||
}
|
||||
pub const fn is_arithmetic_f64(&self) -> bool {
|
||||
self.state & ExtraInfo::arithmetic_f64().state != 0
|
||||
}
|
||||
|
||||
pub const fn strip_pending(&self) -> ExtraInfo {
|
||||
ExtraInfo {
|
||||
state: self.state
|
||||
& !(ExtraInfo::pending_f32_nan().state | ExtraInfo::pending_f64_nan().state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Union two ExtraInfos.
|
||||
impl BitOr for ExtraInfo {
|
||||
type Output = Self;
|
||||
|
||||
fn bitor(self, other: Self) -> Self {
|
||||
debug_assert!(!(self.has_pending_f32_nan() && other.has_pending_f64_nan()));
|
||||
debug_assert!(!(self.has_pending_f64_nan() && other.has_pending_f32_nan()));
|
||||
ExtraInfo {
|
||||
state: if self.is_arithmetic_f32() || other.is_arithmetic_f32() {
|
||||
ExtraInfo::arithmetic_f32().state
|
||||
} else if self.has_pending_f32_nan() || other.has_pending_f32_nan() {
|
||||
ExtraInfo::pending_f32_nan().state
|
||||
} else {
|
||||
0
|
||||
} + if self.is_arithmetic_f64() || other.is_arithmetic_f64() {
|
||||
ExtraInfo::arithmetic_f64().state
|
||||
} else if self.has_pending_f64_nan() || other.has_pending_f64_nan() {
|
||||
ExtraInfo::pending_f64_nan().state
|
||||
} else {
|
||||
0
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
impl BitOrAssign for ExtraInfo {
|
||||
fn bitor_assign(&mut self, other: Self) {
|
||||
*self = *self | other;
|
||||
}
|
||||
}
|
||||
|
||||
// Intersection for ExtraInfo.
|
||||
impl BitAnd for ExtraInfo {
|
||||
type Output = Self;
|
||||
fn bitand(self, other: Self) -> Self {
|
||||
// Pending canonicalizations are not safe to discard, or even reorder.
|
||||
debug_assert!(
|
||||
self.has_pending_f32_nan() == other.has_pending_f32_nan()
|
||||
|| self.is_arithmetic_f32()
|
||||
|| other.is_arithmetic_f32()
|
||||
);
|
||||
debug_assert!(
|
||||
self.has_pending_f64_nan() == other.has_pending_f64_nan()
|
||||
|| self.is_arithmetic_f64()
|
||||
|| other.is_arithmetic_f64()
|
||||
);
|
||||
let info = match (
|
||||
self.is_arithmetic_f32() && other.is_arithmetic_f32(),
|
||||
self.is_arithmetic_f64() && other.is_arithmetic_f64(),
|
||||
) {
|
||||
(false, false) => Default::default(),
|
||||
(true, false) => ExtraInfo::arithmetic_f32(),
|
||||
(false, true) => ExtraInfo::arithmetic_f64(),
|
||||
(true, true) => ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64(),
|
||||
};
|
||||
let info = match (self.has_pending_f32_nan(), self.has_pending_f64_nan()) {
|
||||
(false, false) => info,
|
||||
(true, false) => info | ExtraInfo::pending_f32_nan(),
|
||||
(false, true) => info | ExtraInfo::pending_f64_nan(),
|
||||
(true, true) => unreachable!("Can't form ExtraInfo with two pending canonicalizations"),
|
||||
};
|
||||
info
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct State<'ctx> {
|
||||
pub stack: Vec<(BasicValueEnum<'ctx>, ExtraInfo)>,
|
||||
control_stack: Vec<ControlFrame<'ctx>>,
|
||||
value_counter: Cell<usize>,
|
||||
|
||||
pub reachable: bool,
|
||||
}
|
||||
|
||||
impl<'ctx> State<'ctx> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
stack: vec![],
|
||||
control_stack: vec![],
|
||||
value_counter: Cell::new(0),
|
||||
reachable: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_control_frames(&self) -> bool {
|
||||
return !self.control_stack.is_empty();
|
||||
}
|
||||
|
||||
pub fn reset_stack(&mut self, frame: &ControlFrame<'ctx>) {
|
||||
let stack_size_snapshot = match frame {
|
||||
ControlFrame::Block {
|
||||
stack_size_snapshot,
|
||||
..
|
||||
}
|
||||
| ControlFrame::Loop {
|
||||
stack_size_snapshot,
|
||||
..
|
||||
}
|
||||
| ControlFrame::IfElse {
|
||||
stack_size_snapshot,
|
||||
..
|
||||
} => *stack_size_snapshot,
|
||||
};
|
||||
self.stack.truncate(stack_size_snapshot);
|
||||
}
|
||||
|
||||
pub fn outermost_frame(&self) -> Result<&ControlFrame<'ctx>, CompileError> {
|
||||
self.control_stack.get(0).ok_or(CompileError::Codegen(
|
||||
"outermost_frame: invalid control stack depth".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn frame_at_depth(&self, depth: u32) -> Result<&ControlFrame<'ctx>, CompileError> {
|
||||
let index = self
|
||||
.control_stack
|
||||
.len()
|
||||
.checked_sub(1 + (depth as usize))
|
||||
.ok_or(CompileError::Codegen(
|
||||
"frame_at_depth: invalid control stack depth".to_string(),
|
||||
))?;
|
||||
Ok(&self.control_stack[index])
|
||||
}
|
||||
|
||||
pub fn frame_at_depth_mut(
|
||||
&mut self,
|
||||
depth: u32,
|
||||
) -> Result<&mut ControlFrame<'ctx>, CompileError> {
|
||||
let index = self
|
||||
.control_stack
|
||||
.len()
|
||||
.checked_sub(1 + (depth as usize))
|
||||
.ok_or(CompileError::Codegen(
|
||||
"frame_at_depth_mut: invalid control stack depth".to_string(),
|
||||
))?;
|
||||
Ok(&mut self.control_stack[index])
|
||||
}
|
||||
|
||||
pub fn pop_frame(&mut self) -> Result<ControlFrame<'ctx>, CompileError> {
|
||||
self.control_stack.pop().ok_or(CompileError::Codegen(
|
||||
"pop_frame: cannot pop from control stack".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn var_name(&self) -> String {
|
||||
let counter = self.value_counter.get();
|
||||
let s = format!("s{}", counter);
|
||||
self.value_counter.set(counter + 1);
|
||||
s
|
||||
}
|
||||
|
||||
pub fn push1<T: BasicValue<'ctx>>(&mut self, value: T) {
|
||||
self.push1_extra(value, Default::default());
|
||||
}
|
||||
|
||||
pub fn push1_extra<T: BasicValue<'ctx>>(&mut self, value: T, info: ExtraInfo) {
|
||||
self.stack.push((value.as_basic_value_enum(), info));
|
||||
}
|
||||
|
||||
pub fn pop1(&mut self) -> Result<BasicValueEnum<'ctx>, CompileError> {
|
||||
Ok(self.pop1_extra()?.0)
|
||||
}
|
||||
|
||||
pub fn pop1_extra(&mut self) -> Result<(BasicValueEnum<'ctx>, ExtraInfo), CompileError> {
|
||||
self.stack.pop().ok_or(CompileError::Codegen(
|
||||
"pop1_extra: invalid value stack".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn pop2(&mut self) -> Result<(BasicValueEnum<'ctx>, BasicValueEnum<'ctx>), CompileError> {
|
||||
let v2 = self.pop1()?;
|
||||
let v1 = self.pop1()?;
|
||||
Ok((v1, v2))
|
||||
}
|
||||
|
||||
pub fn pop2_extra(
|
||||
&mut self,
|
||||
) -> Result<
|
||||
(
|
||||
(BasicValueEnum<'ctx>, ExtraInfo),
|
||||
(BasicValueEnum<'ctx>, ExtraInfo),
|
||||
),
|
||||
CompileError,
|
||||
> {
|
||||
let v2 = self.pop1_extra()?;
|
||||
let v1 = self.pop1_extra()?;
|
||||
Ok((v1, v2))
|
||||
}
|
||||
|
||||
pub fn pop3_extra(
|
||||
&mut self,
|
||||
) -> Result<
|
||||
(
|
||||
(BasicValueEnum<'ctx>, ExtraInfo),
|
||||
(BasicValueEnum<'ctx>, ExtraInfo),
|
||||
(BasicValueEnum<'ctx>, ExtraInfo),
|
||||
),
|
||||
CompileError,
|
||||
> {
|
||||
let v3 = self.pop1_extra()?;
|
||||
let v2 = self.pop1_extra()?;
|
||||
let v1 = self.pop1_extra()?;
|
||||
Ok((v1, v2, v3))
|
||||
}
|
||||
|
||||
pub fn peek1_extra(&self) -> Result<(BasicValueEnum<'ctx>, ExtraInfo), CompileError> {
|
||||
let index = self
|
||||
.stack
|
||||
.len()
|
||||
.checked_sub(1)
|
||||
.ok_or(CompileError::Codegen(
|
||||
"peek1_extra: invalid value stack".to_string(),
|
||||
))?;
|
||||
Ok(self.stack[index])
|
||||
}
|
||||
|
||||
pub fn peekn(&self, n: usize) -> Result<Vec<BasicValueEnum<'ctx>>, CompileError> {
|
||||
Ok(self.peekn_extra(n)?.iter().map(|x| x.0).collect())
|
||||
}
|
||||
|
||||
pub fn peekn_extra(
|
||||
&self,
|
||||
n: usize,
|
||||
) -> Result<&[(BasicValueEnum<'ctx>, ExtraInfo)], CompileError> {
|
||||
let index = self
|
||||
.stack
|
||||
.len()
|
||||
.checked_sub(n)
|
||||
.ok_or(CompileError::Codegen(
|
||||
"peekn_extra: invalid value stack".to_string(),
|
||||
))?;
|
||||
Ok(&self.stack[index..])
|
||||
}
|
||||
|
||||
pub fn popn_save_extra(
|
||||
&mut self,
|
||||
n: usize,
|
||||
) -> Result<Vec<(BasicValueEnum<'ctx>, ExtraInfo)>, CompileError> {
|
||||
let v = self.peekn_extra(n)?.to_vec();
|
||||
self.popn(n)?;
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
pub fn popn(&mut self, n: usize) -> Result<(), CompileError> {
|
||||
let index = self
|
||||
.stack
|
||||
.len()
|
||||
.checked_sub(n)
|
||||
.ok_or(CompileError::Codegen(
|
||||
"popn: invalid value stack".to_string(),
|
||||
))?;
|
||||
|
||||
self.stack.truncate(index);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn push_block(&mut self, next: BasicBlock<'ctx>, phis: SmallVec<[PhiValue<'ctx>; 1]>) {
|
||||
self.control_stack.push(ControlFrame::Block {
|
||||
next,
|
||||
phis,
|
||||
stack_size_snapshot: self.stack.len(),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn push_loop(
|
||||
&mut self,
|
||||
body: BasicBlock<'ctx>,
|
||||
next: BasicBlock<'ctx>,
|
||||
phis: SmallVec<[PhiValue<'ctx>; 1]>,
|
||||
) {
|
||||
self.control_stack.push(ControlFrame::Loop {
|
||||
body,
|
||||
next,
|
||||
phis,
|
||||
stack_size_snapshot: self.stack.len(),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn push_if(
|
||||
&mut self,
|
||||
if_then: BasicBlock<'ctx>,
|
||||
if_else: BasicBlock<'ctx>,
|
||||
next: BasicBlock<'ctx>,
|
||||
phis: SmallVec<[PhiValue<'ctx>; 1]>,
|
||||
) {
|
||||
self.control_stack.push(ControlFrame::IfElse {
|
||||
if_then,
|
||||
if_else,
|
||||
next,
|
||||
phis,
|
||||
stack_size_snapshot: self.stack.len(),
|
||||
if_else_state: IfElseState::If,
|
||||
});
|
||||
}
|
||||
}
|
34
lib/compiler/Cargo.toml
Normal file
@ -0,0 +1,34 @@
|
||||
[package]
|
||||
name = "wasmer-compiler"
|
||||
version = "0.16.2"
|
||||
description = "Base compiler abstraction for WebAssembly"
|
||||
license = "MIT OR (Apache-2.0 WITH LLVM-exception)"
|
||||
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
categories = ["no-std", "wasm"]
|
||||
readme = "README.md"
|
||||
keywords = ["webassembly", "wasm", "compiler"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
enumset = "1.0.0"
|
||||
target-lexicon = { version = "0.10.0", default-features = false }
|
||||
wasmparser = { version = "0.51.4", default-features = false }
|
||||
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
|
||||
wasm-common = { path = "../wasm-common", version = "0.16.2" }
|
||||
hashbrown = { version = "0.7.1", optional = true }
|
||||
serde = { version = "1.0.106", features = ["derive"], optional = true }
|
||||
thiserror = "1.0.14"
|
||||
serde_bytes = { version = "0.11.3", optional = true }
|
||||
|
||||
[target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies]
|
||||
raw-cpuid = "7.0.3"
|
||||
|
||||
[features]
|
||||
default = ["std", "enable-serde"]
|
||||
std = []
|
||||
core = ["hashbrown"]
|
||||
enable-serde = ["serde", "serde_bytes"]
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "experimental" }
|
4
lib/compiler/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
This crate is the base for Compiler implementations.
|
||||
|
||||
It performs the translation from a Wasm module into a basic Module,
|
||||
but leaves the Wasm function bytecode translation to the compiler implementor.
|
39
lib/compiler/src/address_map.rs
Normal file
@ -0,0 +1,39 @@
|
||||
//! Data structures to provide transformation of the source
|
||||
// addresses of a WebAssembly module into the native code.
|
||||
|
||||
use crate::std::vec::Vec;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use wasm_common::SourceLoc;
|
||||
|
||||
/// Single source location to generated address mapping.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct InstructionAddressMap {
|
||||
/// Original source location.
|
||||
pub srcloc: SourceLoc,
|
||||
|
||||
/// Generated instructions offset.
|
||||
pub code_offset: usize,
|
||||
|
||||
/// Generated instructions length.
|
||||
pub code_len: usize,
|
||||
}
|
||||
|
||||
/// Function and its instructions addresses mappings.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct FunctionAddressMap {
|
||||
/// Instructions maps.
|
||||
/// The array is sorted by the InstructionAddressMap::code_offset field.
|
||||
pub instructions: Vec<InstructionAddressMap>,
|
||||
|
||||
/// Function start source location (normally declaration).
|
||||
pub start_srcloc: SourceLoc,
|
||||
|
||||
/// Function end source location.
|
||||
pub end_srcloc: SourceLoc,
|
||||
|
||||
/// Generated function body offset if applicable, otherwise 0.
|
||||
pub body_offset: usize,
|
||||
|
||||
/// Generated function body length.
|
||||
pub body_len: usize,
|
||||
}
|
70
lib/compiler/src/compiler.rs
Normal file
@ -0,0 +1,70 @@
|
||||
//! This module mainly outputs the `Compiler` trait that custom
|
||||
//! compilers will need to implement.
|
||||
|
||||
use crate::config::Target;
|
||||
use crate::errors::CompileError;
|
||||
use crate::function::{Compilation, CompiledFunction};
|
||||
use crate::std::vec::Vec;
|
||||
use crate::FunctionBodyData;
|
||||
use crate::ModuleTranslationState;
|
||||
use wasm_common::entity::PrimaryMap;
|
||||
use wasm_common::{DefinedFuncIndex, Features, FuncType, MemoryIndex, TableIndex};
|
||||
use wasmer_runtime::Module;
|
||||
use wasmer_runtime::{MemoryPlan, TablePlan};
|
||||
use wasmparser::{validate, OperatorValidatorConfig, ValidatingParserConfig};
|
||||
|
||||
/// An implementation of a Compiler from parsed WebAssembly module to Compiled native code.
|
||||
pub trait Compiler {
|
||||
/// Gets the target associated with this compiler
|
||||
fn target(&self) -> Target;
|
||||
|
||||
/// Gets the WebAssembly features for this Compiler
|
||||
fn features(&self) -> Features;
|
||||
|
||||
/// Validates a module.
|
||||
///
|
||||
/// It returns the a succesful Result in case is valid, `CompileError` in case is not.
|
||||
fn validate_module<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
|
||||
let features = self.features();
|
||||
let config = ValidatingParserConfig {
|
||||
operator_config: OperatorValidatorConfig {
|
||||
enable_threads: features.threads,
|
||||
enable_reference_types: features.reference_types,
|
||||
enable_bulk_memory: features.bulk_memory,
|
||||
enable_simd: features.simd,
|
||||
enable_multi_value: features.multi_value,
|
||||
},
|
||||
};
|
||||
validate(data, Some(config)).map_err(|e| CompileError::Validate(format!("{}", e)))
|
||||
}
|
||||
|
||||
/// Compiles a parsed module.
|
||||
///
|
||||
/// It returns the `Compilation` result (with a list of `CompiledFunction`)
|
||||
/// or a `CompileError`.
|
||||
fn compile_module<'data, 'module>(
|
||||
&self,
|
||||
module: &'module Module,
|
||||
module_translation: &ModuleTranslationState,
|
||||
// The list of function bodies
|
||||
function_body_inputs: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
|
||||
// The plans for the module memories (imported and local)
|
||||
memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
|
||||
// The plans for the module tables (imported and local)
|
||||
table_plans: PrimaryMap<TableIndex, TablePlan>,
|
||||
) -> Result<Compilation, CompileError>;
|
||||
|
||||
/// Compile the trampolines to call a function defined in
|
||||
/// a Wasm module.
|
||||
///
|
||||
/// This allows us to call easily Wasm functions, such as:
|
||||
///
|
||||
/// ```ignore
|
||||
/// let func = instance.exports.func("my_func");
|
||||
/// func.call(&[Value::I32(1)]);
|
||||
/// ```
|
||||
fn compile_wasm_trampolines(
|
||||
&self,
|
||||
signatures: &[FuncType],
|
||||
) -> Result<Vec<CompiledFunction>, CompileError>;
|
||||
}
|
165
lib/compiler/src/config.rs
Normal file
@ -0,0 +1,165 @@
|
||||
//! The configuration for the
|
||||
use crate::compiler::Compiler;
|
||||
use crate::std::boxed::Box;
|
||||
use enumset::{EnumSet, EnumSetType};
|
||||
use target_lexicon::Triple;
|
||||
pub use wasm_common::Features;
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
use raw_cpuid::CpuId;
|
||||
|
||||
/// The nomenclature is inspired by the [raw-cpuid crate].
|
||||
/// The list of supported features was initially retrieved from
|
||||
/// [cranelift-native].
|
||||
///
|
||||
/// The `CpuFeature` enum vaues are likely to grow closer to the
|
||||
/// original cpuid. However, we prefer to start small and grow from there.
|
||||
///
|
||||
/// If you would like to use a flag that doesn't exist yet here, please
|
||||
/// open a PR.
|
||||
///
|
||||
/// [cpuid crate]: https://docs.rs/cpuid/0.1.1/cpuid/enum.CpuFeature.html
|
||||
/// [cranelift-native]: https://github.com/bytecodealliance/cranelift/blob/6988545fd20249b084c53f4761b8c861266f5d31/cranelift-native/src/lib.rs#L51-L92
|
||||
#[allow(missing_docs)]
|
||||
#[derive(EnumSetType, Debug, Hash)]
|
||||
pub enum CpuFeature {
|
||||
// X86 features
|
||||
SSE2,
|
||||
SSE3,
|
||||
SSSE3,
|
||||
SSE41,
|
||||
SSE42,
|
||||
POPCNT,
|
||||
AVX,
|
||||
BMI1,
|
||||
BMI2,
|
||||
AVX2,
|
||||
AVX512DQ,
|
||||
AVX512VL,
|
||||
LZCNT,
|
||||
// ARM features
|
||||
// Risc-V features
|
||||
}
|
||||
|
||||
impl CpuFeature {
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
/// Retrieves the features for the current Host
|
||||
pub fn for_host() -> EnumSet<Self> {
|
||||
let mut features = EnumSet::new();
|
||||
let cpuid = CpuId::new();
|
||||
|
||||
if let Some(info) = cpuid.get_feature_info() {
|
||||
if info.has_sse2() {
|
||||
features.insert(CpuFeature::SSE2);
|
||||
}
|
||||
if info.has_sse3() {
|
||||
features.insert(CpuFeature::SSE3);
|
||||
}
|
||||
if info.has_ssse3() {
|
||||
features.insert(CpuFeature::SSSE3);
|
||||
}
|
||||
if info.has_sse41() {
|
||||
features.insert(CpuFeature::SSE41);
|
||||
}
|
||||
if info.has_sse42() {
|
||||
features.insert(CpuFeature::SSE42);
|
||||
}
|
||||
if info.has_popcnt() {
|
||||
features.insert(CpuFeature::POPCNT);
|
||||
}
|
||||
if info.has_avx() {
|
||||
features.insert(CpuFeature::AVX);
|
||||
}
|
||||
}
|
||||
if let Some(info) = cpuid.get_extended_feature_info() {
|
||||
if info.has_bmi1() {
|
||||
features.insert(CpuFeature::BMI1);
|
||||
}
|
||||
if info.has_bmi2() {
|
||||
features.insert(CpuFeature::BMI2);
|
||||
}
|
||||
if info.has_avx2() {
|
||||
features.insert(CpuFeature::AVX2);
|
||||
}
|
||||
if info.has_avx512dq() {
|
||||
features.insert(CpuFeature::AVX512DQ);
|
||||
}
|
||||
if info.has_avx512vl() {
|
||||
features.insert(CpuFeature::AVX512VL);
|
||||
}
|
||||
}
|
||||
if let Some(info) = cpuid.get_extended_function_info() {
|
||||
if info.has_lzcnt() {
|
||||
features.insert(CpuFeature::LZCNT);
|
||||
}
|
||||
}
|
||||
features
|
||||
}
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
/// Retrieves the features for the current Host
|
||||
pub fn for_host() -> EnumSet<Self> {
|
||||
// We default to an empty hash set
|
||||
EnumSet::new();
|
||||
}
|
||||
}
|
||||
|
||||
/// This is the target that we will use for compiling
|
||||
/// the WebAssembly Module, and then run it.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Target {
|
||||
triple: Triple,
|
||||
cpu_features: EnumSet<CpuFeature>,
|
||||
}
|
||||
|
||||
impl Target {
|
||||
/// Creates a new target given a triple
|
||||
pub fn new(triple: Triple, cpu_features: EnumSet<CpuFeature>) -> Target {
|
||||
Target {
|
||||
triple,
|
||||
cpu_features,
|
||||
}
|
||||
}
|
||||
|
||||
/// The triple associated for the target.
|
||||
pub fn triple(&self) -> &Triple {
|
||||
&self.triple
|
||||
}
|
||||
|
||||
/// The triple associated for the target.
|
||||
pub fn cpu_features(&self) -> &EnumSet<CpuFeature> {
|
||||
&self.cpu_features
|
||||
}
|
||||
}
|
||||
|
||||
/// The default for the Target will use the HOST as the triple
|
||||
impl Default for Target {
|
||||
fn default() -> Target {
|
||||
Target {
|
||||
triple: Triple::host(),
|
||||
cpu_features: CpuFeature::for_host(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The compiler configuration options.
|
||||
///
|
||||
/// This options must have WebAssembly `Features` and a specific
|
||||
/// `Target` to compile to.
|
||||
pub trait CompilerConfig: Clone {
|
||||
/// Gets the WebAssembly features
|
||||
fn features(&self) -> &Features;
|
||||
|
||||
/// Gets the WebAssembly features, mutable
|
||||
fn features_mut(&mut self) -> &mut Features;
|
||||
|
||||
/// Gets the target that we will use for compiling
|
||||
/// the WebAssembly module
|
||||
fn target(&self) -> &Target;
|
||||
|
||||
/// Gets the target that we will use for compiling
|
||||
/// the WebAssembly module, mutable
|
||||
fn target_mut(&mut self) -> &mut Target;
|
||||
|
||||
/// Gets the custom compiler config
|
||||
fn compiler(&self) -> Box<dyn Compiler>;
|
||||
}
|
30
lib/compiler/src/errors.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use crate::std::string::String;
|
||||
use crate::translator::WasmError;
|
||||
use thiserror::Error;
|
||||
|
||||
// Compilation Errors
|
||||
|
||||
/// The WebAssembly.CompileError object indicates an error during
|
||||
/// WebAssembly decoding or validation.
|
||||
///
|
||||
/// This is based on the [Wasm Compile Error][compile-error] API.
|
||||
///
|
||||
/// [compiler-error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/CompileError
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CompileError {
|
||||
/// A wasm translation error occured.
|
||||
#[error("WebAssembly translation error: {0}")]
|
||||
Wasm(#[from] WasmError),
|
||||
|
||||
/// A compilation error occured.
|
||||
#[error("Compilation error: {0}")]
|
||||
Codegen(String),
|
||||
|
||||
/// The module did not pass validation.
|
||||
#[error("Validation error: {0}")]
|
||||
Validate(String),
|
||||
|
||||
/// Insufficient resources available for execution.
|
||||
#[error("Insufficient resources: {0}")]
|
||||
Resource(String),
|
||||
}
|
168
lib/compiler/src/function.rs
Normal file
@ -0,0 +1,168 @@
|
||||
//! A `Compilation` contains the compiled function bodies for a WebAssembly
|
||||
//! module (`CompiledFunction`).
|
||||
//!
|
||||
//! The `CompiledFunction` will be used mainly by different frontends:
|
||||
//! * `jit`: to generate a JIT
|
||||
//! * `obj`: to generate a native object
|
||||
|
||||
use crate::std::ops::Range;
|
||||
use crate::std::vec::Vec;
|
||||
use crate::{CompiledFunctionUnwindInfo, FunctionAddressMap, JumpTableOffsets, Relocation};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use wasmer_runtime::TrapInformation;
|
||||
|
||||
use wasm_common::entity::PrimaryMap;
|
||||
use wasm_common::DefinedFuncIndex;
|
||||
|
||||
type FunctionBody = Vec<u8>;
|
||||
|
||||
/// The result of compiling a WebAssembly function.
|
||||
///
|
||||
/// This structure only have the compiled information data
|
||||
/// (function bytecode body, relocations, traps, jump tables
|
||||
/// and unwind information).
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CompiledFunction {
|
||||
/// The function body.
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub body: FunctionBody,
|
||||
|
||||
/// The relocations (in the body)
|
||||
pub relocations: Vec<Relocation>,
|
||||
|
||||
/// The traps (in the body)
|
||||
pub traps: Vec<TrapInformation>,
|
||||
|
||||
/// The jump tables offsets (in the body).
|
||||
pub jt_offsets: JumpTableOffsets,
|
||||
|
||||
/// The unwind information.
|
||||
pub unwind_info: CompiledFunctionUnwindInfo,
|
||||
|
||||
/// The address map.
|
||||
///
|
||||
/// TODO: Make it optional as it's not required for trampoline generation.
|
||||
pub address_map: FunctionAddressMap,
|
||||
}
|
||||
|
||||
/// The compiled functions map (index in the Wasm -> function)
|
||||
pub type Functions = PrimaryMap<DefinedFuncIndex, CompiledFunction>;
|
||||
|
||||
/// The result of compiling a WebAssembly module's functions.
|
||||
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)]
|
||||
pub struct Compilation {
|
||||
/// Compiled code for the function bodies.
|
||||
functions: Functions,
|
||||
}
|
||||
|
||||
impl Compilation {
|
||||
/// Creates a compilation artifact from a contiguous function buffer and a set of ranges
|
||||
pub fn new(functions: Functions) -> Self {
|
||||
Self { functions }
|
||||
}
|
||||
|
||||
/// Allocates the compilation result with the given function bodies.
|
||||
pub fn from_buffer(
|
||||
buffer: Vec<u8>,
|
||||
functions: impl IntoIterator<
|
||||
Item = (
|
||||
Range<usize>,
|
||||
JumpTableOffsets,
|
||||
Range<usize>,
|
||||
Vec<Relocation>,
|
||||
Vec<TrapInformation>,
|
||||
FunctionAddressMap,
|
||||
),
|
||||
>,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
functions
|
||||
.into_iter()
|
||||
.map(
|
||||
|(body_range, jt_offsets, unwind_range, relocations, traps, address_map)| {
|
||||
CompiledFunction {
|
||||
body: buffer[body_range].to_vec(),
|
||||
jt_offsets,
|
||||
unwind_info: CompiledFunctionUnwindInfo::Windows(
|
||||
buffer[unwind_range].to_vec(),
|
||||
),
|
||||
address_map,
|
||||
relocations,
|
||||
traps,
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Gets the bytes of a single function
|
||||
pub fn get(&self, func: DefinedFuncIndex) -> &CompiledFunction {
|
||||
&self.functions[func]
|
||||
}
|
||||
|
||||
/// Gets the number of functions defined.
|
||||
pub fn len(&self) -> usize {
|
||||
self.functions.len()
|
||||
}
|
||||
|
||||
/// Returns whether there are no functions defined.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.functions.is_empty()
|
||||
}
|
||||
|
||||
/// Gets functions jump table offsets.
|
||||
pub fn get_jt_offsets(&self) -> PrimaryMap<DefinedFuncIndex, JumpTableOffsets> {
|
||||
self.functions
|
||||
.iter()
|
||||
.map(|(_, func)| func.jt_offsets.clone())
|
||||
.collect::<PrimaryMap<DefinedFuncIndex, _>>()
|
||||
}
|
||||
|
||||
/// Gets functions jump table offsets.
|
||||
pub fn get_relocations(&self) -> PrimaryMap<DefinedFuncIndex, Vec<Relocation>> {
|
||||
self.functions
|
||||
.iter()
|
||||
.map(|(_, func)| func.relocations.clone())
|
||||
.collect::<PrimaryMap<DefinedFuncIndex, _>>()
|
||||
}
|
||||
|
||||
/// Gets functions address maps.
|
||||
pub fn get_address_maps(&self) -> PrimaryMap<DefinedFuncIndex, FunctionAddressMap> {
|
||||
self.functions
|
||||
.iter()
|
||||
.map(|(_, func)| func.address_map.clone())
|
||||
.collect::<PrimaryMap<DefinedFuncIndex, _>>()
|
||||
}
|
||||
|
||||
/// Gets functions jump table offsets.
|
||||
pub fn get_traps(&self) -> PrimaryMap<DefinedFuncIndex, Vec<TrapInformation>> {
|
||||
self.functions
|
||||
.iter()
|
||||
.map(|(_, func)| func.traps.clone())
|
||||
.collect::<PrimaryMap<DefinedFuncIndex, _>>()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a Compilation {
|
||||
type IntoIter = Iter<'a>;
|
||||
type Item = <Self::IntoIter as Iterator>::Item;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
Iter {
|
||||
iterator: self.functions.iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Iter<'a> {
|
||||
iterator: <&'a Functions as IntoIterator>::IntoIter,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Iter<'a> {
|
||||
type Item = &'a CompiledFunction;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iterator.next().map(|(_, b)| b)
|
||||
}
|
||||
}
|
33
lib/compiler/src/jump_table.rs
Normal file
@ -0,0 +1,33 @@
|
||||
//! A jump table is a method of transferring program control (branching)
|
||||
//! to another part of a program (or a different program that may have
|
||||
//! been dynamically loaded) using a table of branch or jump instructions.
|
||||
//!
|
||||
//! Source: https://en.wikipedia.org/wiki/Branch_table
|
||||
|
||||
use super::CodeOffset;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use wasm_common::entity::{entity_impl, SecondaryMap};
|
||||
|
||||
/// An opaque reference to a [jump table](https://en.wikipedia.org/wiki/Branch_table).
|
||||
///
|
||||
/// `JumpTable`s are used for indirect branching and are specialized for dense,
|
||||
/// 0-based jump offsets.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct JumpTable(u32);
|
||||
entity_impl!(JumpTable, "jt");
|
||||
|
||||
impl JumpTable {
|
||||
/// Create a new jump table reference from its number.
|
||||
///
|
||||
/// This method is for use by the parser.
|
||||
pub fn with_number(n: u32) -> Option<Self> {
|
||||
if n < u32::max_value() {
|
||||
Some(Self(n))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Code offsets for Jump Tables.
|
||||
pub type JumpTableOffsets = SecondaryMap<JumpTable, CodeOffset>;
|
71
lib/compiler/src/lib.rs
Normal file
@ -0,0 +1,71 @@
|
||||
//! The `wasmer-compiler` crate provides the necessary abstractions
|
||||
//! to create a compiler.
|
||||
//!
|
||||
//! It provides an universal way of parsing a module via `wasmparser`,
|
||||
//! while giving the responsibility of compiling specific function
|
||||
//! WebAssembly bodies to the `Compiler` implementation.
|
||||
|
||||
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![cfg_attr(feature = "std", deny(unstable_features))]
|
||||
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
|
||||
#![cfg_attr(
|
||||
feature = "cargo-clippy",
|
||||
warn(
|
||||
clippy::float_arithmetic,
|
||||
clippy::mut_mut,
|
||||
clippy::nonminimal_bool,
|
||||
clippy::option_map_unwrap_or,
|
||||
clippy::option_map_unwrap_or_else,
|
||||
clippy::print_stdout,
|
||||
clippy::unicode_not_nfc,
|
||||
clippy::use_self
|
||||
)
|
||||
)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
#[macro_use]
|
||||
extern crate alloc as std;
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use hashbrown::HashMap;
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashMap;
|
||||
|
||||
mod address_map;
|
||||
mod compiler;
|
||||
mod config;
|
||||
mod errors;
|
||||
mod function;
|
||||
mod jump_table;
|
||||
mod relocation;
|
||||
mod unwind;
|
||||
#[macro_use]
|
||||
mod translator;
|
||||
|
||||
pub use crate::address_map::{FunctionAddressMap, InstructionAddressMap};
|
||||
pub use crate::compiler::Compiler;
|
||||
pub use crate::config::{CompilerConfig, CpuFeature, Features, Target};
|
||||
pub use crate::errors::CompileError;
|
||||
pub use crate::function::{Compilation, CompiledFunction, Functions};
|
||||
pub use crate::jump_table::{JumpTable, JumpTableOffsets};
|
||||
pub use crate::relocation::{Relocation, RelocationKind, RelocationTarget, Relocations};
|
||||
pub use crate::translator::{
|
||||
to_wasm_error, translate_module, FunctionBodyData, ModuleEnvironment, ModuleTranslation,
|
||||
ModuleTranslationState, WasmError, WasmResult,
|
||||
};
|
||||
pub use crate::unwind::{CompiledFunctionUnwindInfo, FDERelocEntry, FunctionTableReloc};
|
||||
|
||||
/// Offset in bytes from the beginning of the function.
|
||||
pub type CodeOffset = u32;
|
||||
|
||||
/// Addend to add to the symbol value.
|
||||
pub type Addend = i64;
|
||||
|
||||
/// Version number of this crate.
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
100
lib/compiler/src/relocation.rs
Normal file
@ -0,0 +1,100 @@
|
||||
//! Relocation is the process of assigning load addresses for position-dependent
|
||||
//! code and data of a program and adjusting the code and data to reflect the
|
||||
//! assigned addresses.
|
||||
//!
|
||||
//! Source: https://en.wikipedia.org/wiki/Relocation_(computing)
|
||||
//!
|
||||
//! Each time a `Compiler` compiles a WebAssembly function (into machine code),
|
||||
//! it also attaches if there are any relocations that need to be patched into
|
||||
//! the generated machine code, so a given frontend (JIT or native) can
|
||||
//! do the corresponding work to run it.
|
||||
|
||||
use crate::std::vec::Vec;
|
||||
use crate::{Addend, CodeOffset, JumpTable};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use wasm_common::entity::PrimaryMap;
|
||||
use wasm_common::{DefinedFuncIndex, FuncIndex};
|
||||
use wasmer_runtime::libcalls::LibCall;
|
||||
|
||||
/// Relocation kinds for every ISA.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum RelocationKind {
|
||||
/// absolute 4-byte
|
||||
Abs4,
|
||||
/// absolute 8-byte
|
||||
Abs8,
|
||||
/// x86 PC-relative 4-byte
|
||||
X86PCRel4,
|
||||
/// x86 PC-relative 4-byte offset to trailing rodata
|
||||
X86PCRelRodata4,
|
||||
/// x86 call to PC-relative 4-byte
|
||||
X86CallPCRel4,
|
||||
// /// x86 call to PLT-relative 4-byte
|
||||
// X86CallPLTRel4,
|
||||
|
||||
// /// x86 GOT PC-relative 4-byte
|
||||
// X86GOTPCRel4,
|
||||
|
||||
// /// Arm32 call target
|
||||
// Arm32Call,
|
||||
|
||||
// /// Arm64 call target
|
||||
// Arm64Call,
|
||||
|
||||
// /// RISC-V call target
|
||||
// RiscvCall,
|
||||
|
||||
// /// Elf x86_64 32 bit signed PC relative offset to two GOT entries for GD symbol.
|
||||
// ElfX86_64TlsGd,
|
||||
|
||||
// /// Mach-O x86_64 32 bit signed PC relative offset to a `__thread_vars` entry.
|
||||
// MachOX86_64Tlv,
|
||||
}
|
||||
|
||||
impl fmt::Display for RelocationKind {
|
||||
/// Display trait implementation drops the arch, since its used in contexts where the arch is
|
||||
/// already unambiguous, e.g. clif syntax with isa specified. In other contexts, use Debug.
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Self::Abs4 => write!(f, "Abs4"),
|
||||
Self::Abs8 => write!(f, "Abs8"),
|
||||
Self::X86PCRel4 => write!(f, "PCRel4"),
|
||||
Self::X86PCRelRodata4 => write!(f, "PCRelRodata4"),
|
||||
Self::X86CallPCRel4 => write!(f, "CallPCRel4"),
|
||||
// Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
|
||||
// Self::X86GOTPCRel4 => write!(f, "GOTPCRel4"),
|
||||
// Self::Arm32Call | Self::Arm64Call | Self::RiscvCall => write!(f, "Call"),
|
||||
|
||||
// Self::ElfX86_64TlsGd => write!(f, "ElfX86_64TlsGd"),
|
||||
// Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A record of a relocation to perform.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Relocation {
|
||||
/// The relocation kind.
|
||||
pub kind: RelocationKind,
|
||||
/// Relocation target.
|
||||
pub reloc_target: RelocationTarget,
|
||||
/// The offset where to apply the relocation.
|
||||
pub offset: CodeOffset,
|
||||
/// The addend to add to the relocation value.
|
||||
pub addend: Addend,
|
||||
}
|
||||
|
||||
/// Destination function. Can be either user function or some special one, like `memory.grow`.
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum RelocationTarget {
|
||||
/// The user function index.
|
||||
UserFunc(FuncIndex),
|
||||
/// A compiler-generated libcall.
|
||||
LibCall(LibCall),
|
||||
/// Jump table index.
|
||||
JumpTable(FuncIndex, JumpTable),
|
||||
}
|
||||
|
||||
/// Relocations to apply to function bodies.
|
||||
pub type Relocations = PrimaryMap<DefinedFuncIndex, Vec<Relocation>>;
|
455
lib/compiler/src/translator/environ.rs
Normal file
@ -0,0 +1,455 @@
|
||||
use super::errors::{WasmError, WasmResult};
|
||||
use super::module::translate_module;
|
||||
use super::state::ModuleTranslationState;
|
||||
use crate::std::borrow::ToOwned;
|
||||
use crate::std::string::ToString;
|
||||
use crate::std::{boxed::Box, string::String, vec::Vec};
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::Arc;
|
||||
use wasm_common::entity::PrimaryMap;
|
||||
use wasm_common::FuncType;
|
||||
use wasm_common::{
|
||||
DataIndex, DataInitializer, DataInitializerLocation, DefinedFuncIndex, ElemIndex, ExportIndex,
|
||||
FuncIndex, GlobalIndex, GlobalType, ImportIndex, MemoryIndex, MemoryType, SignatureIndex,
|
||||
TableIndex, TableType,
|
||||
};
|
||||
use wasmer_runtime::{Module, TableElements};
|
||||
|
||||
/// Contains function data: bytecode and its offset in the module.
|
||||
#[derive(Hash)]
|
||||
pub struct FunctionBodyData<'a> {
|
||||
/// Function body bytecode.
|
||||
pub data: &'a [u8],
|
||||
|
||||
/// Body offset relative to the module file.
|
||||
pub module_offset: usize,
|
||||
}
|
||||
|
||||
/// The result of translating via `ModuleEnvironment`. Function bodies are not
|
||||
/// yet translated, and data initializers have not yet been copied out of the
|
||||
/// original buffer.
|
||||
/// The function bodies will be translated by a specific compiler backend.
|
||||
pub struct ModuleTranslation<'data> {
|
||||
/// Module information.
|
||||
pub module: Module,
|
||||
|
||||
/// References to the function bodies.
|
||||
pub function_body_inputs: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
|
||||
|
||||
/// References to the data initializers.
|
||||
pub data_initializers: Vec<DataInitializer<'data>>,
|
||||
|
||||
/// The decoded Wasm types for the module.
|
||||
pub module_translation: Option<ModuleTranslationState>,
|
||||
}
|
||||
|
||||
/// Object containing the standalone environment information.
|
||||
pub struct ModuleEnvironment<'data> {
|
||||
/// The result to be filled in.
|
||||
pub result: ModuleTranslation<'data>,
|
||||
imports: u32,
|
||||
}
|
||||
|
||||
impl<'data> ModuleEnvironment<'data> {
|
||||
/// Allocates the environment data structures.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
result: ModuleTranslation {
|
||||
module: Module::new(),
|
||||
function_body_inputs: PrimaryMap::new(),
|
||||
data_initializers: Vec::new(),
|
||||
module_translation: None,
|
||||
},
|
||||
imports: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Translate a wasm module using this environment. This consumes the
|
||||
/// `ModuleEnvironment` and produces a `ModuleTranslation`.
|
||||
pub fn translate(mut self, data: &'data [u8]) -> WasmResult<ModuleTranslation<'data>> {
|
||||
assert!(self.result.module_translation.is_none());
|
||||
let module_translation = translate_module(data, &mut self)?;
|
||||
self.result.module_translation = Some(module_translation);
|
||||
Ok(self.result)
|
||||
}
|
||||
|
||||
pub(crate) fn declare_export(&mut self, export: ExportIndex, name: &str) -> WasmResult<()> {
|
||||
self.result
|
||||
.module
|
||||
.exports
|
||||
.insert(String::from(name), export);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn declare_import(
|
||||
&mut self,
|
||||
import: ImportIndex,
|
||||
module: &str,
|
||||
field: &str,
|
||||
) -> WasmResult<()> {
|
||||
self.result.module.imports.insert(
|
||||
(String::from(module), String::from(field), self.imports),
|
||||
import,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn reserve_signatures(&mut self, num: u32) -> WasmResult<()> {
|
||||
self.result
|
||||
.module
|
||||
.signatures
|
||||
.reserve_exact(usize::try_from(num).unwrap());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn declare_signature(&mut self, sig: FuncType) -> WasmResult<()> {
|
||||
// TODO: Deduplicate signatures.
|
||||
self.result.module.signatures.push(sig);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn declare_func_import(
|
||||
&mut self,
|
||||
sig_index: SignatureIndex,
|
||||
module: &str,
|
||||
field: &str,
|
||||
) -> WasmResult<()> {
|
||||
debug_assert_eq!(
|
||||
self.result.module.functions.len(),
|
||||
self.result.module.num_imported_funcs,
|
||||
"Imported functions must be declared first"
|
||||
);
|
||||
self.declare_import(
|
||||
ImportIndex::Function(FuncIndex::from_u32(
|
||||
self.result.module.num_imported_funcs as _,
|
||||
)),
|
||||
module,
|
||||
field,
|
||||
)?;
|
||||
self.result.module.functions.push(sig_index);
|
||||
self.result.module.num_imported_funcs += 1;
|
||||
self.imports += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn declare_table_import(
|
||||
&mut self,
|
||||
table: TableType,
|
||||
module: &str,
|
||||
field: &str,
|
||||
) -> WasmResult<()> {
|
||||
debug_assert_eq!(
|
||||
self.result.module.tables.len(),
|
||||
self.result.module.num_imported_tables,
|
||||
"Imported tables must be declared first"
|
||||
);
|
||||
self.declare_import(
|
||||
ImportIndex::Table(TableIndex::from_u32(
|
||||
self.result.module.num_imported_tables as _,
|
||||
)),
|
||||
module,
|
||||
field,
|
||||
)?;
|
||||
self.result.module.tables.push(table);
|
||||
self.result.module.num_imported_tables += 1;
|
||||
self.imports += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn declare_memory_import(
|
||||
&mut self,
|
||||
memory: MemoryType,
|
||||
module: &str,
|
||||
field: &str,
|
||||
) -> WasmResult<()> {
|
||||
debug_assert_eq!(
|
||||
self.result.module.memories.len(),
|
||||
self.result.module.num_imported_memories,
|
||||
"Imported memories must be declared first"
|
||||
);
|
||||
self.declare_import(
|
||||
ImportIndex::Memory(MemoryIndex::from_u32(
|
||||
self.result.module.num_imported_memories as _,
|
||||
)),
|
||||
module,
|
||||
field,
|
||||
)?;
|
||||
self.result.module.memories.push(memory);
|
||||
self.result.module.num_imported_memories += 1;
|
||||
self.imports += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn declare_global_import(
|
||||
&mut self,
|
||||
global: GlobalType,
|
||||
module: &str,
|
||||
field: &str,
|
||||
) -> WasmResult<()> {
|
||||
debug_assert_eq!(
|
||||
self.result.module.globals.len(),
|
||||
self.result.module.num_imported_globals,
|
||||
"Imported globals must be declared first"
|
||||
);
|
||||
self.declare_import(
|
||||
ImportIndex::Global(GlobalIndex::from_u32(
|
||||
self.result.module.num_imported_globals as _,
|
||||
)),
|
||||
module,
|
||||
field,
|
||||
)?;
|
||||
self.result.module.globals.push(global);
|
||||
self.result.module.num_imported_globals += 1;
|
||||
self.imports += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn finish_imports(&mut self) -> WasmResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn reserve_func_types(&mut self, num: u32) -> WasmResult<()> {
|
||||
self.result
|
||||
.module
|
||||
.functions
|
||||
.reserve_exact(usize::try_from(num).unwrap());
|
||||
self.result
|
||||
.function_body_inputs
|
||||
.reserve_exact(usize::try_from(num).unwrap());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn declare_func_type(&mut self, sig_index: SignatureIndex) -> WasmResult<()> {
|
||||
self.result.module.functions.push(sig_index);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn reserve_tables(&mut self, num: u32) -> WasmResult<()> {
|
||||
self.result
|
||||
.module
|
||||
.tables
|
||||
.reserve_exact(usize::try_from(num).unwrap());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn declare_table(&mut self, table: TableType) -> WasmResult<()> {
|
||||
self.result.module.tables.push(table);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn reserve_memories(&mut self, num: u32) -> WasmResult<()> {
|
||||
self.result
|
||||
.module
|
||||
.memories
|
||||
.reserve_exact(usize::try_from(num).unwrap());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn declare_memory(&mut self, memory: MemoryType) -> WasmResult<()> {
|
||||
if memory.shared {
|
||||
return Err(WasmError::Unsupported(
|
||||
"shared memories are not supported yet".to_owned(),
|
||||
));
|
||||
}
|
||||
self.result.module.memories.push(memory);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn reserve_globals(&mut self, num: u32) -> WasmResult<()> {
|
||||
self.result
|
||||
.module
|
||||
.globals
|
||||
.reserve_exact(usize::try_from(num).unwrap());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn declare_global(&mut self, global: GlobalType) -> WasmResult<()> {
|
||||
self.result.module.globals.push(global);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn reserve_exports(&mut self, num: u32) -> WasmResult<()> {
|
||||
self.result
|
||||
.module
|
||||
.exports
|
||||
.reserve(usize::try_from(num).unwrap());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn declare_func_export(
|
||||
&mut self,
|
||||
func_index: FuncIndex,
|
||||
name: &str,
|
||||
) -> WasmResult<()> {
|
||||
self.declare_export(ExportIndex::Function(func_index), name)
|
||||
}
|
||||
|
||||
pub(crate) fn declare_table_export(
|
||||
&mut self,
|
||||
table_index: TableIndex,
|
||||
name: &str,
|
||||
) -> WasmResult<()> {
|
||||
self.declare_export(ExportIndex::Table(table_index), name)
|
||||
}
|
||||
|
||||
pub(crate) fn declare_memory_export(
|
||||
&mut self,
|
||||
memory_index: MemoryIndex,
|
||||
name: &str,
|
||||
) -> WasmResult<()> {
|
||||
self.declare_export(ExportIndex::Memory(memory_index), name)
|
||||
}
|
||||
|
||||
pub(crate) fn declare_global_export(
|
||||
&mut self,
|
||||
global_index: GlobalIndex,
|
||||
name: &str,
|
||||
) -> WasmResult<()> {
|
||||
self.declare_export(ExportIndex::Global(global_index), name)
|
||||
}
|
||||
|
||||
pub(crate) fn declare_start_func(&mut self, func_index: FuncIndex) -> WasmResult<()> {
|
||||
debug_assert!(self.result.module.start_func.is_none());
|
||||
self.result.module.start_func = Some(func_index);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn reserve_table_elements(&mut self, num: u32) -> WasmResult<()> {
|
||||
self.result
|
||||
.module
|
||||
.table_elements
|
||||
.reserve_exact(usize::try_from(num).unwrap());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn declare_table_elements(
|
||||
&mut self,
|
||||
table_index: TableIndex,
|
||||
base: Option<GlobalIndex>,
|
||||
offset: usize,
|
||||
elements: Box<[FuncIndex]>,
|
||||
) -> WasmResult<()> {
|
||||
self.result.module.table_elements.push(TableElements {
|
||||
table_index,
|
||||
base,
|
||||
offset,
|
||||
elements,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn declare_passive_element(
|
||||
&mut self,
|
||||
elem_index: ElemIndex,
|
||||
segments: Box<[FuncIndex]>,
|
||||
) -> WasmResult<()> {
|
||||
let old = self
|
||||
.result
|
||||
.module
|
||||
.passive_elements
|
||||
.insert(elem_index, segments);
|
||||
debug_assert!(
|
||||
old.is_none(),
|
||||
"should never get duplicate element indices, that would be a bug in `wasmer_compiler`'s \
|
||||
translation"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn define_function_body(
|
||||
&mut self,
|
||||
_module_translation: &ModuleTranslationState,
|
||||
body_bytes: &'data [u8],
|
||||
body_offset: usize,
|
||||
) -> WasmResult<()> {
|
||||
self.result.function_body_inputs.push(FunctionBodyData {
|
||||
data: body_bytes,
|
||||
module_offset: body_offset,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn reserve_data_initializers(&mut self, num: u32) -> WasmResult<()> {
|
||||
self.result
|
||||
.data_initializers
|
||||
.reserve_exact(usize::try_from(num).unwrap());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn declare_data_initialization(
|
||||
&mut self,
|
||||
memory_index: MemoryIndex,
|
||||
base: Option<GlobalIndex>,
|
||||
offset: usize,
|
||||
data: &'data [u8],
|
||||
) -> WasmResult<()> {
|
||||
self.result.data_initializers.push(DataInitializer {
|
||||
location: DataInitializerLocation {
|
||||
memory_index,
|
||||
base,
|
||||
offset,
|
||||
},
|
||||
data,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn reserve_passive_data(&mut self, count: u32) -> WasmResult<()> {
|
||||
self.result.module.passive_data.reserve(count as usize);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn declare_passive_data(
|
||||
&mut self,
|
||||
data_index: DataIndex,
|
||||
data: &'data [u8],
|
||||
) -> WasmResult<()> {
|
||||
let old = self
|
||||
.result
|
||||
.module
|
||||
.passive_data
|
||||
.insert(data_index, Arc::from(data));
|
||||
debug_assert!(
|
||||
old.is_none(),
|
||||
"a module can't have duplicate indices, this would be a wasmer-compiler bug"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn declare_module_name(&mut self, name: &'data str) -> WasmResult<()> {
|
||||
self.result.module.name = Some(name.to_string());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn declare_func_name(
|
||||
&mut self,
|
||||
func_index: FuncIndex,
|
||||
name: &'data str,
|
||||
) -> WasmResult<()> {
|
||||
self.result
|
||||
.module
|
||||
.func_names
|
||||
.insert(func_index, name.to_string());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Provides the number of imports up front. By default this does nothing, but
|
||||
/// implementations can use this to preallocate memory if desired.
|
||||
pub(crate) fn reserve_imports(&mut self, _num: u32) -> WasmResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Notifies the implementation that all exports have been declared.
|
||||
pub(crate) fn finish_exports(&mut self) -> WasmResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Indicates that a custom section has been found in the wasm file
|
||||
pub(crate) fn custom_section(
|
||||
&mut self,
|
||||
_name: &'data str,
|
||||
_data: &'data [u8],
|
||||
) -> WasmResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
53
lib/compiler/src/translator/errors.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use crate::std::string::String;
|
||||
use thiserror::Error;
|
||||
use wasmparser::BinaryReaderError;
|
||||
|
||||
/// A WebAssembly translation error.
|
||||
///
|
||||
/// When a WebAssembly function can't be translated, one of these error codes will be returned
|
||||
/// to describe the failure.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum WasmError {
|
||||
/// The input WebAssembly code is invalid.
|
||||
///
|
||||
/// This error code is used by a WebAssembly translator when it encounters invalid WebAssembly
|
||||
/// code. This should never happen for validated WebAssembly code.
|
||||
#[error("Invalid input WebAssembly code at offset {offset}: {message}")]
|
||||
InvalidWebAssembly {
|
||||
/// A string describing the validation error.
|
||||
message: String,
|
||||
/// The bytecode offset where the error occurred.
|
||||
offset: usize,
|
||||
},
|
||||
|
||||
/// A feature used by the WebAssembly code is not supported by the embedding environment.
|
||||
///
|
||||
/// Embedding environments may have their own limitations and feature restrictions.
|
||||
#[error("Unsupported feature: {0}")]
|
||||
Unsupported(String),
|
||||
|
||||
/// An implementation limit was exceeded.
|
||||
#[error("Implementation limit exceeded")]
|
||||
ImplLimitExceeded,
|
||||
|
||||
/// A generic error.
|
||||
#[error("{0}")]
|
||||
Generic(String),
|
||||
}
|
||||
/// Return an `Err(WasmError::Unsupported(msg))` where `msg` the string built by calling `format!`
|
||||
/// on the arguments to this macro.
|
||||
#[macro_export]
|
||||
macro_rules! wasm_unsupported {
|
||||
($($arg:tt)*) => { $crate::WasmError::Unsupported(format!($($arg)*)) }
|
||||
}
|
||||
|
||||
/// Converts a Wasm binary reading error to a runtime Wasm error
|
||||
pub fn to_wasm_error(e: BinaryReaderError) -> WasmError {
|
||||
WasmError::InvalidWebAssembly {
|
||||
message: e.message().into(),
|
||||
offset: e.offset(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenient alias for a `Result` that uses `WasmError` as the error type.
|
||||
pub type WasmResult<T> = Result<T, WasmError>;
|
18
lib/compiler/src/translator/mod.rs
Normal file
@ -0,0 +1,18 @@
|
||||
//! This module defines the parser and translator from wasmparser
|
||||
//! to a common structure `Module`.
|
||||
//!
|
||||
//! It's derived from [cranelift-wasm] but architected for multiple
|
||||
//! compilers rather than just Cranelift.
|
||||
//!
|
||||
//! [cranelift-wasm]: https://crates.io/crates/cranelift-wasm/
|
||||
mod environ;
|
||||
mod module;
|
||||
mod state;
|
||||
#[macro_use]
|
||||
mod errors;
|
||||
mod sections;
|
||||
|
||||
pub use self::environ::{FunctionBodyData, ModuleEnvironment, ModuleTranslation};
|
||||
pub use self::errors::{to_wasm_error, WasmError, WasmResult};
|
||||
pub use self::module::translate_module;
|
||||
pub use self::state::ModuleTranslationState;
|
92
lib/compiler/src/translator/module.rs
Normal file
@ -0,0 +1,92 @@
|
||||
//! Translation skeleton that traverses the whole WebAssembly module and call helper functions
|
||||
//! to deal with each part of it.
|
||||
use super::environ::ModuleEnvironment;
|
||||
use super::errors::{to_wasm_error, WasmResult};
|
||||
use super::sections::{
|
||||
parse_code_section, parse_data_section, parse_element_section, parse_export_section,
|
||||
parse_function_section, parse_global_section, parse_import_section, parse_memory_section,
|
||||
parse_name_section, parse_start_section, parse_table_section, parse_type_section,
|
||||
};
|
||||
use super::state::ModuleTranslationState;
|
||||
use wasmparser::{CustomSectionContent, ModuleReader, SectionContent};
|
||||
|
||||
/// Translate a sequence of bytes forming a valid Wasm binary into a
|
||||
/// parsed Module `ModuleTranslationState`.
|
||||
pub fn translate_module<'data>(
|
||||
data: &'data [u8],
|
||||
environ: &mut ModuleEnvironment<'data>,
|
||||
) -> WasmResult<ModuleTranslationState> {
|
||||
let mut reader = ModuleReader::new(data).map_err(to_wasm_error)?;
|
||||
let mut module_translation_state = ModuleTranslationState::new();
|
||||
|
||||
while !reader.eof() {
|
||||
let section = reader.read().map_err(to_wasm_error)?;
|
||||
match section.content().map_err(to_wasm_error)? {
|
||||
SectionContent::Type(types) => {
|
||||
parse_type_section(types, &mut module_translation_state, environ)?;
|
||||
}
|
||||
|
||||
SectionContent::Import(imports) => {
|
||||
parse_import_section(imports, environ)?;
|
||||
}
|
||||
|
||||
SectionContent::Function(functions) => {
|
||||
parse_function_section(functions, environ)?;
|
||||
}
|
||||
|
||||
SectionContent::Table(tables) => {
|
||||
parse_table_section(tables, environ)?;
|
||||
}
|
||||
|
||||
SectionContent::Memory(memories) => {
|
||||
parse_memory_section(memories, environ)?;
|
||||
}
|
||||
|
||||
SectionContent::Global(globals) => {
|
||||
parse_global_section(globals, environ)?;
|
||||
}
|
||||
|
||||
SectionContent::Export(exports) => {
|
||||
parse_export_section(exports, environ)?;
|
||||
}
|
||||
|
||||
SectionContent::Start(start) => {
|
||||
parse_start_section(start, environ)?;
|
||||
}
|
||||
|
||||
SectionContent::Element(elements) => {
|
||||
parse_element_section(elements, environ)?;
|
||||
}
|
||||
|
||||
SectionContent::Code(code) => {
|
||||
parse_code_section(code, &module_translation_state, environ)?;
|
||||
}
|
||||
|
||||
SectionContent::Data(data) => {
|
||||
parse_data_section(data, environ)?;
|
||||
}
|
||||
|
||||
SectionContent::DataCount(count) => {
|
||||
environ.reserve_passive_data(count)?;
|
||||
}
|
||||
|
||||
SectionContent::Custom {
|
||||
name,
|
||||
binary,
|
||||
content,
|
||||
} => match content {
|
||||
Some(CustomSectionContent::Name(names)) => {
|
||||
parse_name_section(names, environ)?;
|
||||
}
|
||||
_ => {
|
||||
let mut reader = binary.clone();
|
||||
let len = reader.bytes_remaining();
|
||||
let payload = reader.read_bytes(len).map_err(to_wasm_error)?;
|
||||
environ.custom_section(name, payload)?;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(module_translation_state)
|
||||
}
|
484
lib/compiler/src/translator/sections.rs
Normal file
@ -0,0 +1,484 @@
|
||||
//! Helper functions to gather information for each of the non-function sections of a
|
||||
//! WebAssembly module.
|
||||
//!
|
||||
//! The code of these helper functions is straightforward since they only read metadata
|
||||
//! about linear memories, tables, globals, etc. and store them for later use.
|
||||
//!
|
||||
//! The special case of the initialize expressions for table elements offsets or global variables
|
||||
//! is handled, according to the semantics of WebAssembly, to only specific expressions that are
|
||||
//! interpreted on the fly.
|
||||
use super::environ::ModuleEnvironment;
|
||||
use super::errors::{to_wasm_error, WasmError, WasmResult};
|
||||
use super::state::ModuleTranslationState;
|
||||
use crate::{wasm_unsupported, HashMap};
|
||||
use core::convert::TryFrom;
|
||||
use std::boxed::Box;
|
||||
use std::vec::Vec;
|
||||
use wasm_common::entity::packed_option::ReservedValue;
|
||||
use wasm_common::entity::EntityRef;
|
||||
use wasm_common::{
|
||||
DataIndex, ElemIndex, FuncIndex, FuncType, GlobalIndex, GlobalInit, GlobalType, MemoryIndex,
|
||||
MemoryType, SignatureIndex, TableIndex, TableType, Type, V128,
|
||||
};
|
||||
use wasmparser::{
|
||||
self, CodeSectionReader, Data, DataKind, DataSectionReader, Element, ElementItem, ElementItems,
|
||||
ElementKind, ElementSectionReader, Export, ExportSectionReader, ExternalKind,
|
||||
FuncType as WPFuncType, FunctionSectionReader, GlobalSectionReader, GlobalType as WPGlobalType,
|
||||
ImportSectionEntryType, ImportSectionReader, MemorySectionReader, MemoryType as WPMemoryType,
|
||||
NameSectionReader, Naming, NamingReader, Operator, TableSectionReader, TypeSectionReader,
|
||||
};
|
||||
|
||||
/// Helper function translating wasmparser types to Wasm Type.
|
||||
pub fn wptype_to_type(ty: wasmparser::Type) -> WasmResult<Type> {
|
||||
match ty {
|
||||
wasmparser::Type::I32 => Ok(Type::I32),
|
||||
wasmparser::Type::I64 => Ok(Type::I64),
|
||||
wasmparser::Type::F32 => Ok(Type::F32),
|
||||
wasmparser::Type::F64 => Ok(Type::F64),
|
||||
wasmparser::Type::V128 => Ok(Type::V128),
|
||||
wasmparser::Type::AnyRef => Ok(Type::AnyRef),
|
||||
wasmparser::Type::AnyFunc => Ok(Type::FuncRef),
|
||||
ty => Err(wasm_unsupported!(
|
||||
"wptype_to_irtype: parser wasm type {:?}",
|
||||
ty
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses the Type section of the wasm module.
|
||||
pub fn parse_type_section(
|
||||
types: TypeSectionReader,
|
||||
module_translation_state: &mut ModuleTranslationState,
|
||||
environ: &mut ModuleEnvironment,
|
||||
) -> WasmResult<()> {
|
||||
let count = types.get_count();
|
||||
environ.reserve_signatures(count)?;
|
||||
|
||||
for entry in types {
|
||||
match entry.map_err(to_wasm_error)? {
|
||||
WPFuncType {
|
||||
form: wasmparser::Type::Func,
|
||||
params,
|
||||
returns,
|
||||
} => {
|
||||
let sig_params: Vec<Type> = params
|
||||
.iter()
|
||||
.map(|ty| {
|
||||
wptype_to_type(*ty)
|
||||
.expect("only numeric types are supported in function signatures")
|
||||
})
|
||||
.collect();
|
||||
let sig_returns: Vec<Type> = returns
|
||||
.iter()
|
||||
.map(|ty| {
|
||||
wptype_to_type(*ty)
|
||||
.expect("only numeric types are supported in function signatures")
|
||||
})
|
||||
.collect();
|
||||
let sig = FuncType::new(sig_params, sig_returns);
|
||||
environ.declare_signature(sig)?;
|
||||
module_translation_state.wasm_types.push((params, returns));
|
||||
}
|
||||
ty => {
|
||||
return Err(wasm_unsupported!(
|
||||
"unsupported type in type section: {:?}",
|
||||
ty
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Import section of the wasm module.
|
||||
pub fn parse_import_section<'data>(
|
||||
imports: ImportSectionReader<'data>,
|
||||
environ: &mut ModuleEnvironment<'data>,
|
||||
) -> WasmResult<()> {
|
||||
environ.reserve_imports(imports.get_count())?;
|
||||
|
||||
for entry in imports {
|
||||
let import = entry.map_err(to_wasm_error)?;
|
||||
let module_name = import.module;
|
||||
let field_name = import.field;
|
||||
|
||||
match import.ty {
|
||||
ImportSectionEntryType::Function(sig) => {
|
||||
environ.declare_func_import(
|
||||
SignatureIndex::from_u32(sig),
|
||||
module_name,
|
||||
field_name,
|
||||
)?;
|
||||
}
|
||||
ImportSectionEntryType::Memory(WPMemoryType {
|
||||
limits: ref memlimits,
|
||||
shared,
|
||||
}) => {
|
||||
environ.declare_memory_import(
|
||||
MemoryType {
|
||||
minimum: memlimits.initial,
|
||||
maximum: memlimits.maximum,
|
||||
shared,
|
||||
},
|
||||
module_name,
|
||||
field_name,
|
||||
)?;
|
||||
}
|
||||
ImportSectionEntryType::Global(ref ty) => {
|
||||
environ.declare_global_import(
|
||||
GlobalType {
|
||||
ty: wptype_to_type(ty.content_type).unwrap(),
|
||||
mutability: ty.mutable.into(),
|
||||
initializer: GlobalInit::Import,
|
||||
},
|
||||
module_name,
|
||||
field_name,
|
||||
)?;
|
||||
}
|
||||
ImportSectionEntryType::Table(ref tab) => {
|
||||
environ.declare_table_import(
|
||||
TableType {
|
||||
ty: wptype_to_type(tab.element_type).unwrap(),
|
||||
minimum: tab.limits.initial,
|
||||
maximum: tab.limits.maximum,
|
||||
},
|
||||
module_name,
|
||||
field_name,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
environ.finish_imports()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Function section of the wasm module.
|
||||
pub fn parse_function_section(
|
||||
functions: FunctionSectionReader,
|
||||
environ: &mut ModuleEnvironment,
|
||||
) -> WasmResult<()> {
|
||||
let num_functions = functions.get_count();
|
||||
if num_functions == std::u32::MAX {
|
||||
// We reserve `u32::MAX` for our own use.
|
||||
return Err(WasmError::ImplLimitExceeded);
|
||||
}
|
||||
|
||||
environ.reserve_func_types(num_functions)?;
|
||||
|
||||
for entry in functions {
|
||||
let sigindex = entry.map_err(to_wasm_error)?;
|
||||
environ.declare_func_type(SignatureIndex::from_u32(sigindex))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Table section of the wasm module.
|
||||
pub fn parse_table_section(
|
||||
tables: TableSectionReader,
|
||||
environ: &mut ModuleEnvironment,
|
||||
) -> WasmResult<()> {
|
||||
environ.reserve_tables(tables.get_count())?;
|
||||
|
||||
for entry in tables {
|
||||
let table = entry.map_err(to_wasm_error)?;
|
||||
environ.declare_table(TableType {
|
||||
ty: wptype_to_type(table.element_type).unwrap(),
|
||||
minimum: table.limits.initial,
|
||||
maximum: table.limits.maximum,
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Memory section of the wasm module.
|
||||
pub fn parse_memory_section(
|
||||
memories: MemorySectionReader,
|
||||
environ: &mut ModuleEnvironment,
|
||||
) -> WasmResult<()> {
|
||||
environ.reserve_memories(memories.get_count())?;
|
||||
|
||||
for entry in memories {
|
||||
let memory = entry.map_err(to_wasm_error)?;
|
||||
environ.declare_memory(MemoryType {
|
||||
minimum: memory.limits.initial,
|
||||
maximum: memory.limits.maximum,
|
||||
shared: memory.shared,
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Global section of the wasm module.
|
||||
pub fn parse_global_section(
|
||||
globals: GlobalSectionReader,
|
||||
environ: &mut ModuleEnvironment,
|
||||
) -> WasmResult<()> {
|
||||
environ.reserve_globals(globals.get_count())?;
|
||||
|
||||
for entry in globals {
|
||||
let wasmparser::Global {
|
||||
ty: WPGlobalType {
|
||||
content_type,
|
||||
mutable,
|
||||
},
|
||||
init_expr,
|
||||
} = entry.map_err(to_wasm_error)?;
|
||||
let mut init_expr_reader = init_expr.get_binary_reader();
|
||||
let initializer = match init_expr_reader.read_operator().map_err(to_wasm_error)? {
|
||||
Operator::I32Const { value } => GlobalInit::I32Const(value),
|
||||
Operator::I64Const { value } => GlobalInit::I64Const(value),
|
||||
Operator::F32Const { value } => GlobalInit::F32Const(value.bits()),
|
||||
Operator::F64Const { value } => GlobalInit::F64Const(value.bits()),
|
||||
Operator::V128Const { value } => {
|
||||
GlobalInit::V128Const(V128::from(value.bytes().to_vec().as_slice()))
|
||||
}
|
||||
Operator::RefNull => GlobalInit::RefNullConst,
|
||||
Operator::RefFunc { function_index } => {
|
||||
GlobalInit::RefFunc(FuncIndex::from_u32(function_index))
|
||||
}
|
||||
Operator::GlobalGet { global_index } => {
|
||||
GlobalInit::GetGlobal(GlobalIndex::from_u32(global_index))
|
||||
}
|
||||
ref s => {
|
||||
return Err(wasm_unsupported!(
|
||||
"unsupported init expr in global section: {:?}",
|
||||
s
|
||||
));
|
||||
}
|
||||
};
|
||||
let global = GlobalType {
|
||||
ty: wptype_to_type(content_type).unwrap(),
|
||||
mutability: mutable.into(),
|
||||
initializer,
|
||||
};
|
||||
environ.declare_global(global)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Export section of the wasm module.
|
||||
pub fn parse_export_section<'data>(
|
||||
exports: ExportSectionReader<'data>,
|
||||
environ: &mut ModuleEnvironment<'data>,
|
||||
) -> WasmResult<()> {
|
||||
environ.reserve_exports(exports.get_count())?;
|
||||
|
||||
for entry in exports {
|
||||
let Export {
|
||||
field,
|
||||
ref kind,
|
||||
index,
|
||||
} = entry.map_err(to_wasm_error)?;
|
||||
|
||||
// The input has already been validated, so we should be able to
|
||||
// assume valid UTF-8 and use `from_utf8_unchecked` if performance
|
||||
// becomes a concern here.
|
||||
let index = index as usize;
|
||||
match *kind {
|
||||
ExternalKind::Function => environ.declare_func_export(FuncIndex::new(index), field)?,
|
||||
ExternalKind::Table => environ.declare_table_export(TableIndex::new(index), field)?,
|
||||
ExternalKind::Memory => {
|
||||
environ.declare_memory_export(MemoryIndex::new(index), field)?
|
||||
}
|
||||
ExternalKind::Global => {
|
||||
environ.declare_global_export(GlobalIndex::new(index), field)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
environ.finish_exports()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Start section of the wasm module.
|
||||
pub fn parse_start_section(index: u32, environ: &mut ModuleEnvironment) -> WasmResult<()> {
|
||||
environ.declare_start_func(FuncIndex::from_u32(index))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_elems(items: &ElementItems) -> WasmResult<Box<[FuncIndex]>> {
|
||||
let items_reader = items.get_items_reader().map_err(to_wasm_error)?;
|
||||
let mut elems = Vec::with_capacity(usize::try_from(items_reader.get_count()).unwrap());
|
||||
for item in items_reader {
|
||||
let elem = match item.map_err(to_wasm_error)? {
|
||||
ElementItem::Null => FuncIndex::reserved_value(),
|
||||
ElementItem::Func(index) => FuncIndex::from_u32(index),
|
||||
};
|
||||
elems.push(elem);
|
||||
}
|
||||
Ok(elems.into_boxed_slice())
|
||||
}
|
||||
|
||||
/// Parses the Element section of the wasm module.
|
||||
pub fn parse_element_section<'data>(
|
||||
elements: ElementSectionReader<'data>,
|
||||
environ: &mut ModuleEnvironment,
|
||||
) -> WasmResult<()> {
|
||||
environ.reserve_table_elements(elements.get_count())?;
|
||||
|
||||
for (index, entry) in elements.into_iter().enumerate() {
|
||||
let Element { kind, items, ty } = entry.map_err(to_wasm_error)?;
|
||||
if ty != wasmparser::Type::AnyFunc {
|
||||
return Err(wasm_unsupported!(
|
||||
"unsupported table element type: {:?}",
|
||||
ty
|
||||
));
|
||||
}
|
||||
let segments = read_elems(&items)?;
|
||||
match kind {
|
||||
ElementKind::Active {
|
||||
table_index,
|
||||
init_expr,
|
||||
} => {
|
||||
let mut init_expr_reader = init_expr.get_binary_reader();
|
||||
let (base, offset) =
|
||||
match init_expr_reader.read_operator().map_err(to_wasm_error)? {
|
||||
Operator::I32Const { value } => (None, value as u32 as usize),
|
||||
Operator::GlobalGet { global_index } => {
|
||||
(Some(GlobalIndex::from_u32(global_index)), 0)
|
||||
}
|
||||
ref s => {
|
||||
return Err(wasm_unsupported!(
|
||||
"unsupported init expr in element section: {:?}",
|
||||
s
|
||||
));
|
||||
}
|
||||
};
|
||||
environ.declare_table_elements(
|
||||
TableIndex::from_u32(table_index),
|
||||
base,
|
||||
offset,
|
||||
segments,
|
||||
)?
|
||||
}
|
||||
ElementKind::Passive => {
|
||||
let index = ElemIndex::from_u32(index as u32);
|
||||
environ.declare_passive_element(index, segments)?;
|
||||
}
|
||||
ElementKind::Declared => return Err(wasm_unsupported!("element kind declared")),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Code section of the wasm module.
|
||||
pub fn parse_code_section<'data>(
|
||||
code: CodeSectionReader<'data>,
|
||||
module_translation_state: &ModuleTranslationState,
|
||||
environ: &mut ModuleEnvironment<'data>,
|
||||
) -> WasmResult<()> {
|
||||
for body in code {
|
||||
let mut reader = body.map_err(to_wasm_error)?.get_binary_reader();
|
||||
let size = reader.bytes_remaining();
|
||||
let offset = reader.original_position();
|
||||
environ.define_function_body(
|
||||
module_translation_state,
|
||||
reader.read_bytes(size).map_err(to_wasm_error)?,
|
||||
offset,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Data section of the wasm module.
|
||||
pub fn parse_data_section<'data>(
|
||||
data: DataSectionReader<'data>,
|
||||
environ: &mut ModuleEnvironment<'data>,
|
||||
) -> WasmResult<()> {
|
||||
environ.reserve_data_initializers(data.get_count())?;
|
||||
|
||||
for (index, entry) in data.into_iter().enumerate() {
|
||||
let Data { kind, data } = entry.map_err(to_wasm_error)?;
|
||||
match kind {
|
||||
DataKind::Active {
|
||||
memory_index,
|
||||
init_expr,
|
||||
} => {
|
||||
let mut init_expr_reader = init_expr.get_binary_reader();
|
||||
let (base, offset) =
|
||||
match init_expr_reader.read_operator().map_err(to_wasm_error)? {
|
||||
Operator::I32Const { value } => (None, value as u32 as usize),
|
||||
Operator::GlobalGet { global_index } => {
|
||||
(Some(GlobalIndex::from_u32(global_index)), 0)
|
||||
}
|
||||
ref s => {
|
||||
return Err(wasm_unsupported!(
|
||||
"unsupported init expr in data section: {:?}",
|
||||
s
|
||||
))
|
||||
}
|
||||
};
|
||||
environ.declare_data_initialization(
|
||||
MemoryIndex::from_u32(memory_index),
|
||||
base,
|
||||
offset,
|
||||
data,
|
||||
)?;
|
||||
}
|
||||
DataKind::Passive => {
|
||||
let index = DataIndex::from_u32(index as u32);
|
||||
environ.declare_passive_data(index, data)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Name section of the wasm module.
|
||||
pub fn parse_name_section<'data>(
|
||||
mut names: NameSectionReader<'data>,
|
||||
environ: &mut ModuleEnvironment<'data>,
|
||||
) -> WasmResult<()> {
|
||||
while let Ok(subsection) = names.read() {
|
||||
match subsection {
|
||||
wasmparser::Name::Function(function_subsection) => {
|
||||
if let Some(function_names) = function_subsection
|
||||
.get_map()
|
||||
.ok()
|
||||
.and_then(parse_function_name_subsection)
|
||||
{
|
||||
for (index, name) in function_names {
|
||||
environ.declare_func_name(index, name)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
wasmparser::Name::Module(module) => {
|
||||
if let Ok(name) = module.get_name() {
|
||||
environ.declare_module_name(name)?;
|
||||
}
|
||||
}
|
||||
wasmparser::Name::Local(_) => {}
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_function_name_subsection(
|
||||
mut naming_reader: NamingReader<'_>,
|
||||
) -> Option<HashMap<FuncIndex, &str>> {
|
||||
let mut function_names = HashMap::new();
|
||||
for _ in 0..naming_reader.get_count() {
|
||||
let Naming { index, name } = naming_reader.read().ok()?;
|
||||
if index == std::u32::MAX {
|
||||
// We reserve `u32::MAX` for our own use.
|
||||
return None;
|
||||
}
|
||||
|
||||
if function_names
|
||||
.insert(FuncIndex::from_u32(index), name)
|
||||
.is_some()
|
||||
{
|
||||
// If the function index has been previously seen, then we
|
||||
// break out of the loop and early return `None`, because these
|
||||
// should be unique.
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(function_names)
|
||||
}
|
59
lib/compiler/src/translator/state.rs
Normal file
@ -0,0 +1,59 @@
|
||||
use crate::{wasm_unsupported, WasmResult};
|
||||
use std::boxed::Box;
|
||||
use wasm_common::entity::PrimaryMap;
|
||||
use wasm_common::SignatureIndex;
|
||||
use wasmparser;
|
||||
|
||||
/// Map of signatures to a function's parameter and return types.
|
||||
pub(crate) type WasmTypes =
|
||||
PrimaryMap<SignatureIndex, (Box<[wasmparser::Type]>, Box<[wasmparser::Type]>)>;
|
||||
|
||||
/// Contains information decoded from the Wasm module that must be referenced
|
||||
/// during each Wasm function's translation.
|
||||
///
|
||||
/// This is only for data that is maintained by `wasmer-compiler` itself, as
|
||||
/// opposed to being maintained by the embedder. Data that is maintained by the
|
||||
/// embedder is represented with `ModuleEnvironment`.
|
||||
#[derive(Debug)]
|
||||
pub struct ModuleTranslationState {
|
||||
/// A map containing a Wasm module's original, raw signatures.
|
||||
///
|
||||
/// This is used for translating multi-value Wasm blocks inside functions,
|
||||
/// which are encoded to refer to their type signature via index.
|
||||
pub(crate) wasm_types: WasmTypes,
|
||||
}
|
||||
|
||||
impl ModuleTranslationState {
|
||||
/// Creates a new empty ModuleTranslationState.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
wasm_types: PrimaryMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the parameter and result types for the given Wasm blocktype.
|
||||
pub fn blocktype_params_results(
|
||||
&self,
|
||||
ty_or_ft: wasmparser::TypeOrFuncType,
|
||||
) -> WasmResult<(&[wasmparser::Type], &[wasmparser::Type])> {
|
||||
Ok(match ty_or_ft {
|
||||
wasmparser::TypeOrFuncType::Type(ty) => match ty {
|
||||
wasmparser::Type::I32 => (&[], &[wasmparser::Type::I32]),
|
||||
wasmparser::Type::I64 => (&[], &[wasmparser::Type::I64]),
|
||||
wasmparser::Type::F32 => (&[], &[wasmparser::Type::F32]),
|
||||
wasmparser::Type::F64 => (&[], &[wasmparser::Type::F64]),
|
||||
wasmparser::Type::V128 => (&[], &[wasmparser::Type::V128]),
|
||||
wasmparser::Type::AnyRef => (&[], &[wasmparser::Type::AnyRef]),
|
||||
wasmparser::Type::AnyFunc => (&[], &[wasmparser::Type::AnyFunc]),
|
||||
wasmparser::Type::NullRef => (&[], &[wasmparser::Type::NullRef]),
|
||||
wasmparser::Type::EmptyBlockType => (&[], &[]),
|
||||
ty => return Err(wasm_unsupported!("blocktype_params_results: type {:?}", ty)),
|
||||
},
|
||||
wasmparser::TypeOrFuncType::FuncType(ty_index) => {
|
||||
let sig_idx = SignatureIndex::from_u32(ty_index);
|
||||
let (ref params, ref results) = self.wasm_types[sig_idx];
|
||||
(&*params, &*results)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
82
lib/compiler/src/unwind.rs
Normal file
@ -0,0 +1,82 @@
|
||||
//! A `CompiledFunctionUnwindInfo` contains the function unwind information.
|
||||
//!
|
||||
//! The unwind information is used to determine which function
|
||||
//! called the function that threw the exception, and which
|
||||
//! function called that one, and so forth.
|
||||
//!
|
||||
//! More info: https://en.wikipedia.org/wiki/Call_stack
|
||||
use crate::std::vec::Vec;
|
||||
use crate::{Addend, CodeOffset};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Relocation Entry data
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FDERelocEntry(pub i64, pub usize, pub u8);
|
||||
|
||||
/// Relocation entry for unwind info.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FunctionTableReloc {
|
||||
/// Entry offest in the code block.
|
||||
pub offset: CodeOffset,
|
||||
/// Entry addend relative to the code block.
|
||||
pub addend: Addend,
|
||||
}
|
||||
|
||||
/// Compiled function unwind information.
|
||||
///
|
||||
/// > Note: Windows OS have a different way of representing the [unwind info],
|
||||
/// > That's why we keep the Windows data and the Unix frame layout in different
|
||||
/// > fields.
|
||||
///
|
||||
/// [unwind info]: https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64?view=vs-2019
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub enum CompiledFunctionUnwindInfo {
|
||||
/// No unwind information.
|
||||
None,
|
||||
|
||||
/// Windows UNWIND_INFO.
|
||||
Windows(Vec<u8>),
|
||||
|
||||
/// Unix frame layout info.
|
||||
FrameLayout(Vec<u8>, usize, Vec<FDERelocEntry>),
|
||||
}
|
||||
|
||||
impl CompiledFunctionUnwindInfo {
|
||||
/// Retuns true is no unwind info data.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
CompiledFunctionUnwindInfo::None => true,
|
||||
CompiledFunctionUnwindInfo::Windows(d) => d.is_empty(),
|
||||
CompiledFunctionUnwindInfo::FrameLayout(c, _, _) => c.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns size of serilized unwind info.
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
CompiledFunctionUnwindInfo::None => 0,
|
||||
CompiledFunctionUnwindInfo::Windows(d) => d.len(),
|
||||
CompiledFunctionUnwindInfo::FrameLayout(c, _, _) => c.len(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializes data into byte array.
|
||||
pub fn serialize(&self, dest: &mut [u8], relocs: &mut Vec<FunctionTableReloc>) {
|
||||
match self {
|
||||
CompiledFunctionUnwindInfo::None => (),
|
||||
CompiledFunctionUnwindInfo::Windows(d) => {
|
||||
dest.copy_from_slice(d);
|
||||
}
|
||||
CompiledFunctionUnwindInfo::FrameLayout(code, _fde_offset, r) => {
|
||||
dest.copy_from_slice(code);
|
||||
r.iter().for_each(move |r| {
|
||||
assert_eq!(r.2, 8);
|
||||
relocs.push(FunctionTableReloc {
|
||||
offset: r.1 as _,
|
||||
addend: r.0,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
lib/deprecated/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Wasmer deprecated packages
|
||||
|
||||
We create "wrapping" crates to ease the adodption of new
|
||||
versions of Wasmer, with minimal or no breaking changes.
|
||||
|
31
lib/jit/Cargo.toml
Normal file
@ -0,0 +1,31 @@
|
||||
[package]
|
||||
name = "wasmer-jit"
|
||||
version = "0.16.2"
|
||||
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
description = "Wasmer JIT frontend"
|
||||
license = "(Apache-2.0 WITH LLVM-exception) or MIT"
|
||||
categories = ["wasm"]
|
||||
keywords = ["webassembly", "wasm"]
|
||||
repository = "https://github.com/wasmerio/wasmer"
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasm-common = { path = "../wasm-common", version = "0.16.2" }
|
||||
wasmer-compiler = { path = "../compiler", version = "0.16.2", default-features = false }
|
||||
wasmer-runtime = { path = "../runtime", version = "0.16.2" }
|
||||
target-lexicon = { version = "0.10.0", default-features = false }
|
||||
backtrace = "0.3.46"
|
||||
rustc-demangle = "0.1.16"
|
||||
more-asserts = "0.2.1"
|
||||
thiserror = "1.0.15"
|
||||
region = "2.1.2"
|
||||
serde = { version = "1.0.106", sfeatures = ["derive", "rc"] }
|
||||
bincode = "1.2.1"
|
||||
lazy_static = "1.4"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = { version = "0.3.8", features = ["winnt", "impl-default"] }
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
4
lib/jit/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# Wasmer JIT
|
||||
|
||||
> Note: this project started as a subfork of [this crate](https://crates.io/crates/wasmtime-jit).
|
||||
|
260
lib/jit/src/code_memory.rs
Normal file
@ -0,0 +1,260 @@
|
||||
//! Memory management for executable code.
|
||||
use crate::function_table::FunctionTable;
|
||||
use region;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::{cmp, mem};
|
||||
use wasm_common::entity::PrimaryMap;
|
||||
use wasm_common::DefinedFuncIndex;
|
||||
use wasmer_compiler::{Compilation, CompiledFunction};
|
||||
use wasmer_runtime::{Mmap, VMFunctionBody};
|
||||
|
||||
struct CodeMemoryEntry {
|
||||
mmap: ManuallyDrop<Mmap>,
|
||||
table: ManuallyDrop<FunctionTable>,
|
||||
}
|
||||
|
||||
impl CodeMemoryEntry {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
mmap: ManuallyDrop::new(Mmap::new()),
|
||||
table: ManuallyDrop::new(FunctionTable::new()),
|
||||
}
|
||||
}
|
||||
fn with_capacity(cap: usize) -> Result<Self, String> {
|
||||
Ok(Self {
|
||||
mmap: ManuallyDrop::new(Mmap::with_at_least(cap)?),
|
||||
table: ManuallyDrop::new(FunctionTable::new()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for CodeMemoryEntry {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// Table needs to be freed before mmap.
|
||||
ManuallyDrop::drop(&mut self.table);
|
||||
ManuallyDrop::drop(&mut self.mmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Memory manager for executable code.
|
||||
pub struct CodeMemory {
|
||||
current: CodeMemoryEntry,
|
||||
entries: Vec<CodeMemoryEntry>,
|
||||
position: usize,
|
||||
published: usize,
|
||||
}
|
||||
|
||||
impl CodeMemory {
|
||||
/// Create a new `CodeMemory` instance.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
current: CodeMemoryEntry::new(),
|
||||
entries: Vec::new(),
|
||||
position: 0,
|
||||
published: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocate a continuous memory blocks for a single compiled function.
|
||||
pub fn allocate_functions(
|
||||
&mut self,
|
||||
compilation: &Compilation,
|
||||
) -> Result<PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>, String> {
|
||||
let fat_ptrs = self.allocate_for_compilation(compilation)?;
|
||||
|
||||
// Second, create a PrimaryMap from result vector of pointers.
|
||||
let mut result = PrimaryMap::with_capacity(compilation.len());
|
||||
for i in 0..fat_ptrs.len() {
|
||||
let fat_ptr: *mut [VMFunctionBody] = fat_ptrs[i];
|
||||
result.push(fat_ptr);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Allocate a continuous memory block for a single compiled function.
|
||||
/// TODO: Reorganize the code that calls this to emit code directly into the
|
||||
/// mmap region rather than into a Vec that we need to copy in.
|
||||
pub fn allocate_for_function(
|
||||
&mut self,
|
||||
func: &CompiledFunction,
|
||||
) -> Result<&mut [VMFunctionBody], String> {
|
||||
let size = Self::function_allocation_size(func);
|
||||
|
||||
let (buf, table, start) = self.allocate(size)?;
|
||||
|
||||
let (_, _, _, vmfunc) = Self::copy_function(func, start as u32, buf, table);
|
||||
|
||||
Ok(vmfunc)
|
||||
}
|
||||
|
||||
/// Allocate a continuous memory block for a compilation.
|
||||
///
|
||||
/// Allocates memory for both the function bodies as well as function unwind data.
|
||||
pub fn allocate_for_compilation(
|
||||
&mut self,
|
||||
compilation: &Compilation,
|
||||
) -> Result<Box<[&mut [VMFunctionBody]]>, String> {
|
||||
let total_len = compilation
|
||||
.into_iter()
|
||||
.fold(0, |acc, func| acc + Self::function_allocation_size(func));
|
||||
|
||||
let (mut buf, mut table, start) = self.allocate(total_len)?;
|
||||
let mut result = Vec::with_capacity(compilation.len());
|
||||
let mut start = start as u32;
|
||||
|
||||
for func in compilation.into_iter() {
|
||||
let (next_start, next_buf, next_table, vmfunc) =
|
||||
Self::copy_function(func, start, buf, table);
|
||||
|
||||
result.push(vmfunc);
|
||||
|
||||
start = next_start;
|
||||
buf = next_buf;
|
||||
table = next_table;
|
||||
}
|
||||
|
||||
Ok(result.into_boxed_slice())
|
||||
}
|
||||
|
||||
/// Make all allocated memory executable.
|
||||
pub fn publish(&mut self) {
|
||||
self.push_current(0)
|
||||
.expect("failed to push current memory map");
|
||||
|
||||
for CodeMemoryEntry { mmap: m, table: t } in &mut self.entries[self.published..] {
|
||||
// Remove write access to the pages due to the relocation fixups.
|
||||
t.publish(m.as_ptr() as u64)
|
||||
.expect("failed to publish function table");
|
||||
|
||||
if !m.is_empty() {
|
||||
unsafe {
|
||||
region::protect(m.as_mut_ptr(), m.len(), region::Protection::ReadExecute)
|
||||
}
|
||||
.expect("unable to make memory readonly and executable");
|
||||
}
|
||||
}
|
||||
|
||||
self.published = self.entries.len();
|
||||
}
|
||||
|
||||
/// Allocate `size` bytes of memory which can be made executable later by
|
||||
/// calling `publish()`. Note that we allocate the memory as writeable so
|
||||
/// that it can be written to and patched, though we make it readonly before
|
||||
/// actually executing from it.
|
||||
///
|
||||
/// A few values are returned:
|
||||
///
|
||||
/// * A mutable slice which references the allocated memory
|
||||
/// * A function table instance where unwind information is registered
|
||||
/// * The offset within the current mmap that the slice starts at
|
||||
///
|
||||
/// TODO: Add an alignment flag.
|
||||
fn allocate(&mut self, size: usize) -> Result<(&mut [u8], &mut FunctionTable, usize), String> {
|
||||
if self.current.mmap.len() - self.position < size {
|
||||
self.push_current(cmp::max(0x10000, size))?;
|
||||
}
|
||||
|
||||
let old_position = self.position;
|
||||
self.position += size;
|
||||
|
||||
Ok((
|
||||
&mut self.current.mmap.as_mut_slice()[old_position..self.position],
|
||||
&mut self.current.table,
|
||||
old_position,
|
||||
))
|
||||
}
|
||||
|
||||
/// Calculates the allocation size of the given compiled function.
|
||||
fn function_allocation_size(func: &CompiledFunction) -> usize {
|
||||
if func.unwind_info.is_empty() {
|
||||
func.body.len()
|
||||
} else {
|
||||
// Account for necessary unwind information alignment padding (32-bit)
|
||||
((func.body.len() + 3) & !3) + func.unwind_info.len()
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies the data of the compiled function to the given buffer.
|
||||
///
|
||||
/// This will also add the function to the current function table.
|
||||
fn copy_function<'a>(
|
||||
func: &CompiledFunction,
|
||||
func_start: u32,
|
||||
buf: &'a mut [u8],
|
||||
table: &'a mut FunctionTable,
|
||||
) -> (
|
||||
u32,
|
||||
&'a mut [u8],
|
||||
&'a mut FunctionTable,
|
||||
&'a mut [VMFunctionBody],
|
||||
) {
|
||||
let func_end = func_start + (func.body.len() as u32);
|
||||
|
||||
let (body, remainder) = buf.split_at_mut(func.body.len());
|
||||
body.copy_from_slice(&func.body);
|
||||
let vmfunc = Self::view_as_mut_vmfunc_slice(body);
|
||||
|
||||
if func.unwind_info.is_empty() {
|
||||
return (func_end, remainder, table, vmfunc);
|
||||
}
|
||||
|
||||
// Keep unwind information 32-bit aligned (round up to the nearest 4 byte boundary)
|
||||
let padding = ((func.body.len() + 3) & !3) - func.body.len();
|
||||
let (unwind, remainder) = remainder.split_at_mut(padding + func.unwind_info.len());
|
||||
let mut relocs = Vec::new();
|
||||
func.unwind_info
|
||||
.serialize(&mut unwind[padding..], &mut relocs);
|
||||
|
||||
let unwind_start = func_end + (padding as u32);
|
||||
let unwind_end = unwind_start + (func.unwind_info.len() as u32);
|
||||
|
||||
relocs.iter_mut().for_each(move |r| {
|
||||
r.offset += unwind_start;
|
||||
r.addend += func_start as i64;
|
||||
});
|
||||
|
||||
table.add_function(func_start, func_end, unwind_start, &relocs);
|
||||
|
||||
(unwind_end, remainder, table, vmfunc)
|
||||
}
|
||||
|
||||
/// Convert mut a slice from u8 to VMFunctionBody.
|
||||
fn view_as_mut_vmfunc_slice(slice: &mut [u8]) -> &mut [VMFunctionBody] {
|
||||
let byte_ptr: *mut [u8] = slice;
|
||||
let body_ptr = byte_ptr as *mut [VMFunctionBody];
|
||||
unsafe { &mut *body_ptr }
|
||||
}
|
||||
|
||||
/// Pushes the current Mmap (and function table) and allocates a new Mmap of the given size.
|
||||
fn push_current(&mut self, new_size: usize) -> Result<(), String> {
|
||||
let previous = mem::replace(
|
||||
&mut self.current,
|
||||
if new_size == 0 {
|
||||
CodeMemoryEntry::new()
|
||||
} else {
|
||||
CodeMemoryEntry::with_capacity(cmp::max(0x10000, new_size))?
|
||||
},
|
||||
);
|
||||
|
||||
if !previous.mmap.is_empty() {
|
||||
self.entries.push(previous);
|
||||
} else {
|
||||
assert_eq!(previous.table.len(), 0);
|
||||
}
|
||||
|
||||
self.position = 0;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::CodeMemory;
|
||||
fn _assert() {
|
||||
fn _assert_send_sync<T: Send + Sync>() {}
|
||||
_assert_send_sync::<CodeMemory>();
|
||||
}
|
||||
}
|