Merge remote-tracking branch 'origin/main' into pch/wasi_common_cap_std
This commit is contained in:
@@ -41,7 +41,6 @@ if (process.env.CENTOS !== undefined) {
|
||||
let path = process.env.PATH;
|
||||
path = `${path}:/rust/bin`;
|
||||
path = `/opt/rh/devtoolset-8/root/usr/bin:${path}`;
|
||||
path = `/opt/rh/rh-python36/root/usr/bin:${path}`;
|
||||
|
||||
// Spawn a container daemonized in the background which we'll connect to via
|
||||
// `docker exec`. This'll have access to the current directory.
|
||||
@@ -52,7 +51,7 @@ child_process.execFileSync('docker', [
|
||||
'-v', `${process.cwd()}:${process.cwd()}`,
|
||||
'-v', `${child_process.execSync('rustc --print sysroot').toString().trim()}:/rust:ro`,
|
||||
'--env', `PATH=${path}`,
|
||||
'centos:6',
|
||||
'centos:7',
|
||||
], stdio);
|
||||
|
||||
// Use ourselves to run future commands
|
||||
@@ -63,7 +62,7 @@ const exec = s => {
|
||||
child_process.execSync(`docker exec centos ${s}`, stdio);
|
||||
};
|
||||
exec('yum install -y centos-release-scl cmake xz epel-release');
|
||||
exec('yum install -y rh-python36 patchelf unzip');
|
||||
exec('yum install -y python3 patchelf unzip');
|
||||
exec('yum install -y devtoolset-8-gcc devtoolset-8-binutils devtoolset-8-gcc-c++');
|
||||
exec('yum install -y git');
|
||||
|
||||
|
||||
13
.github/workflows/main.yml
vendored
13
.github/workflows/main.yml
vendored
@@ -60,7 +60,10 @@ jobs:
|
||||
# nightly-only feature right now.
|
||||
- uses: ./.github/actions/install-rust
|
||||
with:
|
||||
toolchain: nightly
|
||||
# TODO (rust-lang/rust#79661): We are seeing an internal compiler error when
|
||||
# building with the latest (2020-12-06) nightly; pin on a slightly older
|
||||
# version for now.
|
||||
toolchain: nightly-2020-11-29
|
||||
- run: cargo doc --no-deps --all --exclude wasmtime-cli --exclude test-programs --exclude cranelift-codegen-meta
|
||||
- run: cargo doc --package cranelift-codegen-meta --document-private-items
|
||||
- uses: actions/upload-artifact@v1
|
||||
@@ -145,7 +148,7 @@ jobs:
|
||||
# flags to rustc.
|
||||
- uses: ./.github/actions/install-rust
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: nightly-2020-11-29
|
||||
- run: cargo install cargo-fuzz --vers "^0.8"
|
||||
- run: cargo fetch
|
||||
working-directory: ./fuzz
|
||||
@@ -202,7 +205,7 @@ jobs:
|
||||
rust: beta
|
||||
- build: nightly
|
||||
os: ubuntu-latest
|
||||
rust: nightly
|
||||
rust: nightly-2020-11-29
|
||||
- build: macos
|
||||
os: macos-latest
|
||||
rust: stable
|
||||
@@ -292,7 +295,7 @@ jobs:
|
||||
submodules: true
|
||||
- uses: ./.github/actions/install-rust
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: nightly-2020-11-29
|
||||
- uses: ./.github/actions/define-llvm-env
|
||||
|
||||
# Install wasm32 targets in order to build various tests throughout the
|
||||
@@ -303,7 +306,7 @@ jobs:
|
||||
# Run the x64 CI script.
|
||||
- run: ./ci/run-experimental-x64-ci.sh
|
||||
env:
|
||||
CARGO_VERSION: "+nightly"
|
||||
CARGO_VERSION: "+nightly-2020-11-29"
|
||||
RUST_BACKTRACE: 1
|
||||
|
||||
# Build and test the wasi-nn module.
|
||||
|
||||
192
Cargo.lock
generated
192
Cargo.lock
generated
@@ -391,6 +391,16 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||
|
||||
[[package]]
|
||||
name = "cpp_demangle"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44919ecaf6f99e8e737bc239408931c9a01e9a6c74814fee8242dd2506b65390"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"glob",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpu-time"
|
||||
version = "1.0.0"
|
||||
@@ -444,7 +454,7 @@ dependencies = [
|
||||
"souper-ir",
|
||||
"target-lexicon",
|
||||
"thiserror",
|
||||
"wast 27.0.0",
|
||||
"wast 29.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -512,6 +522,26 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-jit"
|
||||
version = "0.68.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cranelift",
|
||||
"cranelift-codegen",
|
||||
"cranelift-entity",
|
||||
"cranelift-frontend",
|
||||
"cranelift-module",
|
||||
"cranelift-native",
|
||||
"errno",
|
||||
"libc",
|
||||
"log",
|
||||
"memmap",
|
||||
"region",
|
||||
"target-lexicon",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-module"
|
||||
version = "0.68.0"
|
||||
@@ -577,25 +607,6 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-simplejit"
|
||||
version = "0.68.0"
|
||||
dependencies = [
|
||||
"cranelift",
|
||||
"cranelift-codegen",
|
||||
"cranelift-entity",
|
||||
"cranelift-frontend",
|
||||
"cranelift-module",
|
||||
"cranelift-native",
|
||||
"errno",
|
||||
"libc",
|
||||
"log",
|
||||
"memmap",
|
||||
"region",
|
||||
"target-lexicon",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-tools"
|
||||
version = "0.66.0"
|
||||
@@ -609,13 +620,13 @@ dependencies = [
|
||||
"cranelift-filetests",
|
||||
"cranelift-frontend",
|
||||
"cranelift-interpreter",
|
||||
"cranelift-jit",
|
||||
"cranelift-module",
|
||||
"cranelift-native",
|
||||
"cranelift-object",
|
||||
"cranelift-preopt",
|
||||
"cranelift-reader",
|
||||
"cranelift-serde",
|
||||
"cranelift-simplejit",
|
||||
"cranelift-wasm",
|
||||
"file-per-thread-logger",
|
||||
"filecheck",
|
||||
@@ -646,7 +657,7 @@ dependencies = [
|
||||
"smallvec",
|
||||
"target-lexicon",
|
||||
"thiserror",
|
||||
"wasmparser 0.68.0",
|
||||
"wasmparser 0.70.0",
|
||||
"wat",
|
||||
]
|
||||
|
||||
@@ -816,6 +827,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "downcast-rs"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||
|
||||
[[package]]
|
||||
name = "dynasm"
|
||||
version = "1.0.0"
|
||||
@@ -1226,7 +1243,7 @@ dependencies = [
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"typemap",
|
||||
"wasmparser 0.68.0",
|
||||
"wasmparser 0.70.0",
|
||||
"wat",
|
||||
]
|
||||
|
||||
@@ -1304,6 +1321,12 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memory_units"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.4.3"
|
||||
@@ -1330,6 +1353,17 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
@@ -1340,6 +1374,18 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
@@ -1418,6 +1464,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parity-wasm"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865"
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
@@ -1435,7 +1487,7 @@ dependencies = [
|
||||
"peepmatic-test-operator",
|
||||
"peepmatic-traits",
|
||||
"serde",
|
||||
"wast 27.0.0",
|
||||
"wast 29.0.0",
|
||||
"z3",
|
||||
]
|
||||
|
||||
@@ -1463,7 +1515,7 @@ dependencies = [
|
||||
"peepmatic-traits",
|
||||
"rand",
|
||||
"serde",
|
||||
"wast 27.0.0",
|
||||
"wast 29.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1488,7 +1540,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_test",
|
||||
"thiserror",
|
||||
"wast 27.0.0",
|
||||
"wast 29.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1500,7 +1552,7 @@ dependencies = [
|
||||
"peepmatic",
|
||||
"peepmatic-test-operator",
|
||||
"souper-ir",
|
||||
"wast 27.0.0",
|
||||
"wast 29.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1521,7 +1573,7 @@ version = "0.68.0"
|
||||
dependencies = [
|
||||
"peepmatic-traits",
|
||||
"serde",
|
||||
"wast 27.0.0",
|
||||
"wast 29.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2027,9 +2079,9 @@ checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252"
|
||||
|
||||
[[package]]
|
||||
name = "souper-ir"
|
||||
version = "1.0.0"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "163cc2bdd8a66cbaccdf06a6b476689a97e928883e09bffbe06fd5945842a83f"
|
||||
checksum = "a50c18ce33988e1973003afbaa66e6a465ad7a614dc33f246879ccc209c2c044"
|
||||
dependencies = [
|
||||
"id-arena",
|
||||
]
|
||||
@@ -2434,13 +2486,47 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-smith"
|
||||
version = "0.1.10"
|
||||
name = "wasm-encoder"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd5e4720bb44dc5e46a917139dc9dfa4bb6fee023bf9f77d2c55ec12eeaf9930"
|
||||
checksum = "ed89eaf99e08b84f96e477a16588a07dd3b51dc5f07291c3706782f62a10a5e1"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-smith"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "509904d9c4c4659ac238a3f27c3656dd6d3931697eddd4b0f32e335769c298d0"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"leb128",
|
||||
"wasm-encoder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmi"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bb6825d9b2147105789adb4c2d84b9b568719713f3ac39618b637b4dafc86c4"
|
||||
dependencies = [
|
||||
"downcast-rs",
|
||||
"libc",
|
||||
"memory_units",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
"parity-wasm",
|
||||
"wasmi-validation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmi-validation"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea78c597064ba73596099281e2f4cfc019075122a65cdda3205af94f0b264d93"
|
||||
dependencies = [
|
||||
"parity-wasm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2451,18 +2537,18 @@ checksum = "32fddd575d477c6e9702484139cf9f23dcd554b06d185ed0f56c857dd3a47aa6"
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.68.0"
|
||||
version = "0.70.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29a00e14eed9c2ecbbdbdd4fb284f49d21b6808965de24769a6379a13ec47d4c"
|
||||
checksum = "ed1b3f9e9cf01a580b9f3281214dfdb1922b5dfb8494ee312ca03ae10036c2a2"
|
||||
|
||||
[[package]]
|
||||
name = "wasmprinter"
|
||||
version = "0.2.15"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f39a73b5f09cfcb1b568b61968d39b19e4ddec9b49040cfc091adf3b0788bca6"
|
||||
checksum = "f89b2b24dce17e27fe9c09c28331cbd77067fcde5c6ea2508ac84bcbd5d3e018"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"wasmparser 0.68.0",
|
||||
"wasmparser 0.70.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2473,6 +2559,8 @@ dependencies = [
|
||||
"backtrace",
|
||||
"bincode",
|
||||
"cfg-if 1.0.0",
|
||||
"cpp_demangle",
|
||||
"indexmap",
|
||||
"libc",
|
||||
"log",
|
||||
"region",
|
||||
@@ -2481,7 +2569,7 @@ dependencies = [
|
||||
"smallvec",
|
||||
"target-lexicon",
|
||||
"tempfile",
|
||||
"wasmparser 0.68.0",
|
||||
"wasmparser 0.70.0",
|
||||
"wasmtime-cache",
|
||||
"wasmtime-environ",
|
||||
"wasmtime-jit",
|
||||
@@ -2559,7 +2647,7 @@ dependencies = [
|
||||
"test-programs",
|
||||
"tracing-subscriber",
|
||||
"wasi-common",
|
||||
"wasmparser 0.68.0",
|
||||
"wasmparser 0.70.0",
|
||||
"wasmtime",
|
||||
"wasmtime-cache",
|
||||
"wasmtime-debug",
|
||||
@@ -2595,7 +2683,7 @@ dependencies = [
|
||||
"object",
|
||||
"target-lexicon",
|
||||
"thiserror",
|
||||
"wasmparser 0.68.0",
|
||||
"wasmparser 0.70.0",
|
||||
"wasmtime-environ",
|
||||
]
|
||||
|
||||
@@ -2614,7 +2702,7 @@ dependencies = [
|
||||
"more-asserts",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"wasmparser 0.68.0",
|
||||
"wasmparser 0.70.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2642,7 +2730,8 @@ dependencies = [
|
||||
"log",
|
||||
"rayon",
|
||||
"wasm-smith",
|
||||
"wasmparser 0.68.0",
|
||||
"wasmi",
|
||||
"wasmparser 0.70.0",
|
||||
"wasmprinter",
|
||||
"wasmtime",
|
||||
"wasmtime-wast",
|
||||
@@ -2653,6 +2742,7 @@ dependencies = [
|
||||
name = "wasmtime-jit"
|
||||
version = "0.21.0"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"anyhow",
|
||||
"cfg-if 1.0.0",
|
||||
"cranelift-codegen",
|
||||
@@ -2669,7 +2759,7 @@ dependencies = [
|
||||
"serde",
|
||||
"target-lexicon",
|
||||
"thiserror",
|
||||
"wasmparser 0.68.0",
|
||||
"wasmparser 0.70.0",
|
||||
"wasmtime-cranelift",
|
||||
"wasmtime-debug",
|
||||
"wasmtime-environ",
|
||||
@@ -2686,7 +2776,7 @@ version = "0.21.0"
|
||||
dependencies = [
|
||||
"cranelift-codegen",
|
||||
"lightbeam",
|
||||
"wasmparser 0.68.0",
|
||||
"wasmparser 0.70.0",
|
||||
"wasmtime-environ",
|
||||
]
|
||||
|
||||
@@ -2793,7 +2883,7 @@ version = "0.21.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"wasmtime",
|
||||
"wast 27.0.0",
|
||||
"wast 29.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2829,20 +2919,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wast"
|
||||
version = "27.0.0"
|
||||
version = "29.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2c3ef5f6a72dffa44c24d5811123f704e18a1dbc83637d347b1852b41d3835c"
|
||||
checksum = "dcf2268937131d63c3d833242bf5e075406f9ed868b4265f3280e15dac29ac18"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wat"
|
||||
version = "1.0.28"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "835cf59c907f67e2bbc20f50157e08f35006fe2a8444d8ec9f5683e22f937045"
|
||||
checksum = "0d11a88d953b298172d218d18f22853f4e6e12873b62755d05617b864d312c68"
|
||||
dependencies = [
|
||||
"wast 27.0.0",
|
||||
"wast 29.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -38,12 +38,12 @@ anyhow = "1.0.19"
|
||||
target-lexicon = { version = "0.11.0", default-features = false }
|
||||
pretty_env_logger = "0.4.0"
|
||||
file-per-thread-logger = "0.1.1"
|
||||
wat = "1.0.27"
|
||||
wat = "1.0.30"
|
||||
libc = "0.2.60"
|
||||
log = "0.4.8"
|
||||
rayon = "1.2.1"
|
||||
humantime = "2.0.0"
|
||||
wasmparser = "0.68"
|
||||
wasmparser = "0.70.0"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.8.1"
|
||||
|
||||
62
build.rs
62
build.rs
@@ -31,6 +31,7 @@ fn main() -> anyhow::Result<()> {
|
||||
test_directory_module(out, "tests/misc_testsuite/bulk-memory-operations", strategy)?;
|
||||
test_directory_module(out, "tests/misc_testsuite/reference-types", strategy)?;
|
||||
test_directory_module(out, "tests/misc_testsuite/multi-memory", strategy)?;
|
||||
test_directory_module(out, "tests/misc_testsuite/module-linking", strategy)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
@@ -174,46 +175,10 @@ fn write_testsuite_tests(
|
||||
|
||||
/// For experimental_x64 backend features that are not supported yet, mark tests as panicking, so
|
||||
/// they stop "passing" once the features are properly implemented.
|
||||
fn experimental_x64_should_panic(testsuite: &str, testname: &str, strategy: &str) -> bool {
|
||||
if !cfg!(feature = "experimental_x64") || strategy != "Cranelift" {
|
||||
return false;
|
||||
}
|
||||
|
||||
match (testsuite, testname) {
|
||||
("simd", "simd_address") => return false,
|
||||
("simd", "simd_bitwise") => return false,
|
||||
("simd", "simd_bit_shift") => return false,
|
||||
("simd", "simd_boolean") => return false,
|
||||
("simd", "simd_const") => return false,
|
||||
("simd", "simd_i8x16_arith") => return false,
|
||||
("simd", "simd_i8x16_arith2") => return false,
|
||||
("simd", "simd_i8x16_cmp") => return false,
|
||||
("simd", "simd_i8x16_sat_arith") => return false,
|
||||
("simd", "simd_i16x8_arith") => return false,
|
||||
("simd", "simd_i16x8_arith2") => return false,
|
||||
("simd", "simd_i16x8_cmp") => return false,
|
||||
("simd", "simd_i16x8_sat_arith") => return false,
|
||||
("simd", "simd_i32x4_arith") => return false,
|
||||
("simd", "simd_i32x4_arith2") => return false,
|
||||
("simd", "simd_i32x4_cmp") => return false,
|
||||
("simd", "simd_i64x2_arith") => return false,
|
||||
("simd", "simd_f32x4") => return false,
|
||||
("simd", "simd_f32x4_arith") => return false,
|
||||
("simd", "simd_f32x4_cmp") => return false,
|
||||
("simd", "simd_f32x4_pmin_pmax") => return false,
|
||||
("simd", "simd_f64x2") => return false,
|
||||
("simd", "simd_f64x2_arith") => return false,
|
||||
("simd", "simd_f64x2_cmp") => return false,
|
||||
("simd", "simd_f64x2_pmin_pmax") => return false,
|
||||
("simd", "simd_lane") => return false,
|
||||
("simd", "simd_load") => return false,
|
||||
("simd", "simd_load_splat") => return false,
|
||||
("simd", "simd_splat") => return false,
|
||||
("simd", "simd_store") => return false,
|
||||
("simd", _) => return true,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
///
|
||||
/// TODO(#2470): removed all tests from this set as we are disabling x64 SIMD tests unconditionally
|
||||
/// instead until we resolve a nondeterminism bug. Restore when fixed.
|
||||
fn experimental_x64_should_panic(_testsuite: &str, _testname: &str, _strategy: &str) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
@@ -235,21 +200,22 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
|
||||
return env::var("CARGO_CFG_TARGET_ARCH").unwrap() != "x86_64";
|
||||
}
|
||||
|
||||
// Ignore all x64 SIMD tests for now (#2470).
|
||||
("simd", _) if cfg!(feature = "experimental_x64") => {
|
||||
return env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "x86_64";
|
||||
}
|
||||
|
||||
// These are only implemented on aarch64 and x64.
|
||||
("simd", "simd_boolean")
|
||||
| ("simd", "simd_f32x4_pmin_pmax")
|
||||
| ("simd", "simd_f64x2_pmin_pmax") => {
|
||||
| ("simd", "simd_f64x2_pmin_pmax")
|
||||
| ("simd", "simd_f32x4_rounding")
|
||||
| ("simd", "simd_f64x2_rounding")
|
||||
| ("simd", "simd_i32x4_dot_i16x8") => {
|
||||
return !(cfg!(feature = "experimental_x64")
|
||||
|| env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "aarch64")
|
||||
}
|
||||
|
||||
// These are only implemented on aarch64.
|
||||
("simd", "simd_f32x4_rounding") | ("simd", "simd_f64x2_rounding") => {
|
||||
return env::var("CARGO_CFG_TARGET_ARCH").unwrap() != "aarch64";
|
||||
}
|
||||
|
||||
// These tests have simd operators which aren't implemented yet.
|
||||
// (currently none)
|
||||
_ => {}
|
||||
},
|
||||
_ => panic!("unrecognized strategy"),
|
||||
|
||||
@@ -7,7 +7,16 @@
|
||||
# executed with the Wasmtime CLI.
|
||||
set -e
|
||||
WASMTIME_DIR=$(dirname "$0" | xargs dirname)
|
||||
FIXTURE=https://gist.github.com/abrown/c7847bf3701f9efbb2070da1878542c1/raw/07a9f163994b0ff8f0d7c5a5c9645ec3d8b24024
|
||||
FIXTURE=https://github.com/intel/openvino-rs/raw/main/crates/openvino/tests/fixtures/alexnet
|
||||
if [ -z "${1+x}" ]; then
|
||||
# If no temporary directory is specified, create one.
|
||||
TMP_DIR=$(mktemp -d -t ci-XXXXXXXXXX)
|
||||
REMOVE_TMP_DIR=1
|
||||
else
|
||||
# If a directory was specified, use it and avoid removing it.
|
||||
TMP_DIR=$(realpath $1)
|
||||
REMOVE_TMP_DIR=0
|
||||
fi
|
||||
|
||||
# Inform the environment of OpenVINO library locations. Then we use OPENVINO_INSTALL_DIR below to avoid building all of
|
||||
# OpenVINO from source (quite slow).
|
||||
@@ -17,10 +26,9 @@ source /opt/intel/openvino/bin/setupvars.sh
|
||||
OPENVINO_INSTALL_DIR=/opt/intel/openvino cargo build -p wasmtime-cli --features wasi-nn
|
||||
|
||||
# Download all necessary test fixtures to the temporary directory.
|
||||
TMP_DIR=${1:-$(mktemp -d -t ci-XXXXXXXXXX)}
|
||||
wget --no-clobber --directory-prefix=$TMP_DIR $FIXTURE/frozen_inference_graph.bin
|
||||
wget --no-clobber --directory-prefix=$TMP_DIR $FIXTURE/frozen_inference_graph.xml
|
||||
wget --no-clobber --directory-prefix=$TMP_DIR $FIXTURE/tensor-1x3x300x300-f32.bgr
|
||||
wget --no-clobber --directory-prefix=$TMP_DIR $FIXTURE/alexnet.bin
|
||||
wget --no-clobber --directory-prefix=$TMP_DIR $FIXTURE/alexnet.xml
|
||||
wget --no-clobber --directory-prefix=$TMP_DIR $FIXTURE/tensor-1x3x227x227-f32.bgr
|
||||
|
||||
# Now build an example that uses the wasi-nn API.
|
||||
pushd $WASMTIME_DIR/crates/wasi-nn/examples/classification-example
|
||||
@@ -31,5 +39,7 @@ popd
|
||||
# Run the example in Wasmtime (note that the example uses `fixture` as the expected location of the model/tensor files).
|
||||
OPENVINO_INSTALL_DIR=/opt/intel/openvino cargo run --features wasi-nn -- run --mapdir fixture::$TMP_DIR $TMP_DIR/wasi-nn-example.wasm
|
||||
|
||||
# Clean up.
|
||||
rm -rf $TMP_DIR
|
||||
# Clean up the temporary directory only if it was not specified (users may want to keep the directory around).
|
||||
if [[ $REMOVE_TMP_DIR -eq 1 ]]; then
|
||||
rm -rf $TMP_DIR
|
||||
fi
|
||||
@@ -26,7 +26,7 @@ cranelift-native = { path = "native", version = "0.68.0" }
|
||||
cranelift-filetests = { path = "filetests", version = "0.66.0" }
|
||||
cranelift-module = { path = "module", version = "0.68.0" }
|
||||
cranelift-object = { path = "object", version = "0.68.0" }
|
||||
cranelift-simplejit = { path = "simplejit", version = "0.68.0" }
|
||||
cranelift-jit = { path = "jit", version = "0.68.0" }
|
||||
cranelift-preopt = { path = "preopt", version = "0.68.0" }
|
||||
cranelift = { path = "umbrella", version = "0.68.0" }
|
||||
filecheck = "0.5.0"
|
||||
|
||||
@@ -16,10 +16,10 @@ into executable machine code.
|
||||
|
||||
For more information, see [the documentation](docs/index.md).
|
||||
|
||||
For an example of how to use the JIT, see the [SimpleJIT Demo], which
|
||||
For an example of how to use the JIT, see the [JIT Demo], which
|
||||
implements a toy language.
|
||||
|
||||
[SimpleJIT Demo]: https://github.com/bytecodealliance/simplejit-demo
|
||||
[JIT Demo]: https://github.com/bytecodealliance/simplejit-demo
|
||||
|
||||
For an example of how to use Cranelift to run WebAssembly code, see
|
||||
[Wasmtime], which implements a standalone, embeddable, VM using Cranelift.
|
||||
|
||||
@@ -29,8 +29,8 @@ peepmatic = { path = "../peepmatic", optional = true, version = "0.68.0" }
|
||||
peepmatic-traits = { path = "../peepmatic/crates/traits", optional = true, version = "0.68.0" }
|
||||
peepmatic-runtime = { path = "../peepmatic/crates/runtime", optional = true, version = "0.68.0" }
|
||||
regalloc = { version = "0.0.31" }
|
||||
souper-ir = { version = "1", optional = true }
|
||||
wast = { version = "27.0.0", optional = true }
|
||||
souper-ir = { version = "2.1.0", optional = true }
|
||||
wast = { version = "29.0.0", optional = true }
|
||||
# It is a goal of the cranelift-codegen crate to have minimal external dependencies.
|
||||
# Please don't add any unless they are essential to the task of creating binary
|
||||
# machine code. Integration tests that need external dependencies can be
|
||||
|
||||
@@ -549,10 +549,13 @@ fn define_moves(e: &mut PerCpuModeEncodings, shared_defs: &SharedDefinitions, r:
|
||||
}
|
||||
e.enc64(bconst.bind(B64), rec_pu_id_bool.opcodes(&MOV_IMM).rex());
|
||||
|
||||
// You may expect that i8 encodings would use 0x30 (XORB) to indicate that encodings should be
|
||||
// on 8-bit operands (f.ex "xor %al, %al"). Cranelift currently does not know when it can
|
||||
// safely drop the 0x66 prefix, so we explicitly select a wider but permissible opcode.
|
||||
let is_zero_int = InstructionPredicate::new_is_zero_int(&formats.unary_imm, "imm");
|
||||
e.enc_both_instp(
|
||||
iconst.bind(I8),
|
||||
rec_u_id_z.opcodes(&XORB),
|
||||
rec_u_id_z.opcodes(&XOR),
|
||||
is_zero_int.clone(),
|
||||
);
|
||||
|
||||
|
||||
@@ -711,9 +711,6 @@ pub static XOR_IMM8_SIGN_EXTEND: [u8; 1] = [0x83];
|
||||
/// r/m{16,32,64} XOR register of the same size.
|
||||
pub static XOR: [u8; 1] = [0x31];
|
||||
|
||||
/// r/m8 XOR r8.
|
||||
pub static XORB: [u8; 1] = [0x30];
|
||||
|
||||
/// Bitwise logical XOR of packed double-precision floating-point values.
|
||||
pub static XORPD: [u8; 3] = [0x66, 0x0f, 0x57];
|
||||
|
||||
|
||||
@@ -7,8 +7,9 @@ use crate::binemit::CodeOffset;
|
||||
use crate::entity::{PrimaryMap, SecondaryMap};
|
||||
use crate::ir;
|
||||
use crate::ir::{
|
||||
Block, ExtFuncData, FuncRef, GlobalValue, GlobalValueData, Heap, HeapData, Inst, JumpTable,
|
||||
JumpTableData, Opcode, SigRef, StackSlot, StackSlotData, Table, TableData,
|
||||
instructions::BranchInfo, Block, ExtFuncData, FuncRef, GlobalValue, GlobalValueData, Heap,
|
||||
HeapData, Inst, InstructionData, JumpTable, JumpTableData, Opcode, SigRef, StackSlot,
|
||||
StackSlotData, Table, TableData,
|
||||
};
|
||||
use crate::ir::{BlockOffsets, InstEncodings, SourceLocs, StackSlots, ValueLocations};
|
||||
use crate::ir::{DataFlowGraph, ExternalName, Layout, Signature};
|
||||
@@ -270,6 +271,8 @@ impl Function {
|
||||
|
||||
/// Changes the destination of a jump or branch instruction.
|
||||
/// Does nothing if called with a non-jump or non-branch instruction.
|
||||
///
|
||||
/// Note that this method ignores multi-destination branches like `br_table`.
|
||||
pub fn change_branch_destination(&mut self, inst: Inst, new_dest: Block) {
|
||||
match self.dfg[inst].branch_destination_mut() {
|
||||
None => (),
|
||||
@@ -277,6 +280,43 @@ impl Function {
|
||||
}
|
||||
}
|
||||
|
||||
/// Rewrite the branch destination to `new_dest` if the destination matches `old_dest`.
|
||||
/// Does nothing if called with a non-jump or non-branch instruction.
|
||||
///
|
||||
/// Unlike [change_branch_destination](Function::change_branch_destination), this method rewrite the destinations of
|
||||
/// multi-destination branches like `br_table`.
|
||||
pub fn rewrite_branch_destination(&mut self, inst: Inst, old_dest: Block, new_dest: Block) {
|
||||
match self.dfg.analyze_branch(inst) {
|
||||
BranchInfo::SingleDest(dest, ..) => {
|
||||
if dest == old_dest {
|
||||
self.change_branch_destination(inst, new_dest);
|
||||
}
|
||||
}
|
||||
|
||||
BranchInfo::Table(table, default_dest) => {
|
||||
self.jump_tables[table].iter_mut().for_each(|entry| {
|
||||
if *entry == old_dest {
|
||||
*entry = new_dest;
|
||||
}
|
||||
});
|
||||
|
||||
if default_dest == Some(old_dest) {
|
||||
match &mut self.dfg[inst] {
|
||||
InstructionData::BranchTable { destination, .. } => {
|
||||
*destination = new_dest;
|
||||
}
|
||||
_ => panic!(
|
||||
"Unexpected instruction {} having default destination",
|
||||
self.dfg.display_inst(inst, None)
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BranchInfo::NotABranch => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that the specified block can be encoded as a basic block.
|
||||
///
|
||||
/// On error, returns the first invalid instruction and an error message.
|
||||
|
||||
@@ -277,7 +277,7 @@ impl InstructionData {
|
||||
ref mut destination,
|
||||
..
|
||||
} => Some(destination),
|
||||
Self::BranchTable { .. } => None,
|
||||
Self::BranchTable { .. } | Self::IndirectJump { .. } => None,
|
||||
_ => {
|
||||
debug_assert!(!self.opcode().is_branch());
|
||||
None
|
||||
|
||||
@@ -63,6 +63,7 @@ pub enum LibCall {
|
||||
|
||||
/// Elf __tls_get_addr
|
||||
ElfTlsGetAddr,
|
||||
// When adding a new variant make sure to add it to `all_libcalls` too.
|
||||
}
|
||||
|
||||
impl fmt::Display for LibCall {
|
||||
@@ -136,6 +137,33 @@ impl LibCall {
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a list of all known `LibCall`'s.
|
||||
pub fn all_libcalls() -> &'static [LibCall] {
|
||||
use LibCall::*;
|
||||
&[
|
||||
Probestack,
|
||||
UdivI64,
|
||||
SdivI64,
|
||||
UremI64,
|
||||
SremI64,
|
||||
IshlI64,
|
||||
UshrI64,
|
||||
SshrI64,
|
||||
CeilF32,
|
||||
CeilF64,
|
||||
FloorF32,
|
||||
FloorF64,
|
||||
TruncF32,
|
||||
TruncF64,
|
||||
NearestF32,
|
||||
NearestF64,
|
||||
Memcpy,
|
||||
Memset,
|
||||
Memmove,
|
||||
ElfTlsGetAddr,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a function reference for `libcall` in `func`, following the signature
|
||||
|
||||
@@ -6,15 +6,31 @@ enum FlagBit {
|
||||
Notrap,
|
||||
Aligned,
|
||||
Readonly,
|
||||
LittleEndian,
|
||||
BigEndian,
|
||||
}
|
||||
|
||||
const NAMES: [&str; 3] = ["notrap", "aligned", "readonly"];
|
||||
const NAMES: [&str; 5] = ["notrap", "aligned", "readonly", "little", "big"];
|
||||
|
||||
/// Endianness of a memory access.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum Endianness {
|
||||
/// Little-endian
|
||||
Little,
|
||||
/// Big-endian
|
||||
Big,
|
||||
}
|
||||
|
||||
/// Flags for memory operations like load/store.
|
||||
///
|
||||
/// Each of these flags introduce a limited form of undefined behavior. The flags each enable
|
||||
/// certain optimizations that need to make additional assumptions. Generally, the semantics of a
|
||||
/// program does not change when a flag is removed, but adding a flag will.
|
||||
///
|
||||
/// In addition, the flags determine the endianness of the memory access. By default,
|
||||
/// any memory access uses the native endianness determined by the target ISA. This can
|
||||
/// be overridden for individual accesses by explicitly specifying little- or big-endian
|
||||
/// semantics via the flags.
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct MemFlags {
|
||||
bits: u8,
|
||||
@@ -48,16 +64,48 @@ impl MemFlags {
|
||||
/// Set a flag bit by name.
|
||||
///
|
||||
/// Returns true if the flag was found and set, false for an unknown flag name.
|
||||
/// Will also return false when trying to set inconsistent endianness flags.
|
||||
pub fn set_by_name(&mut self, name: &str) -> bool {
|
||||
match NAMES.iter().position(|&s| s == name) {
|
||||
Some(bit) => {
|
||||
self.bits |= 1 << bit;
|
||||
true
|
||||
let bits = self.bits | 1 << bit;
|
||||
if (bits & (1 << FlagBit::LittleEndian as usize)) != 0
|
||||
&& (bits & (1 << FlagBit::BigEndian as usize)) != 0
|
||||
{
|
||||
false
|
||||
} else {
|
||||
self.bits = bits;
|
||||
true
|
||||
}
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return endianness of the memory access. This will return the endianness
|
||||
/// explicitly specified by the flags if any, and will default to the native
|
||||
/// endianness otherwise. The native endianness has to be provided by the
|
||||
/// caller since it is not explicitly encoded in CLIF IR -- this allows a
|
||||
/// front end to create IR without having to know the target endianness.
|
||||
pub fn endianness(self, native_endianness: Endianness) -> Endianness {
|
||||
if self.read(FlagBit::LittleEndian) {
|
||||
Endianness::Little
|
||||
} else if self.read(FlagBit::BigEndian) {
|
||||
Endianness::Big
|
||||
} else {
|
||||
native_endianness
|
||||
}
|
||||
}
|
||||
|
||||
/// Set endianness of the memory access.
|
||||
pub fn set_endianness(&mut self, endianness: Endianness) {
|
||||
match endianness {
|
||||
Endianness::Little => self.set(FlagBit::LittleEndian),
|
||||
Endianness::Big => self.set(FlagBit::BigEndian),
|
||||
};
|
||||
assert!(!(self.read(FlagBit::LittleEndian) && self.read(FlagBit::BigEndian)));
|
||||
}
|
||||
|
||||
/// Test if the `notrap` flag is set.
|
||||
///
|
||||
/// Normally, trapping is part of the semantics of a load/store operation. If the platform
|
||||
|
||||
@@ -50,7 +50,7 @@ pub use crate::ir::instructions::{
|
||||
pub use crate::ir::jumptable::JumpTableData;
|
||||
pub use crate::ir::layout::Layout;
|
||||
pub use crate::ir::libcall::{get_probestack_funcref, LibCall};
|
||||
pub use crate::ir::memflags::MemFlags;
|
||||
pub use crate::ir::memflags::{Endianness, MemFlags};
|
||||
pub use crate::ir::progpoint::{ExpandedProgramPoint, ProgramOrder, ProgramPoint};
|
||||
pub use crate::ir::sourceloc::SourceLoc;
|
||||
pub use crate::ir::stackslot::{StackLayoutInfo, StackSlotData, StackSlotKind, StackSlots};
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::{CodegenError, CodegenResult};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use regalloc::{RealReg, Reg, RegClass, Set, Writable};
|
||||
use smallvec::SmallVec;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
// We use a generic implementation that factors out AArch64 and x64 ABI commonalities, because
|
||||
// these ABIs are very similar.
|
||||
@@ -508,6 +508,12 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
||||
insts
|
||||
}
|
||||
|
||||
fn gen_probestack(_: u32) -> SmallVec<[Self::I; 2]> {
|
||||
// TODO: implement if we ever require stack probes on an AArch64 host
|
||||
// (unlikely unless Lucet is ported)
|
||||
smallvec![]
|
||||
}
|
||||
|
||||
// Returns stack bytes used as well as instructions. Does not adjust
|
||||
// nominal SP offset; abi_impl generic code will do that.
|
||||
fn gen_clobber_save(
|
||||
|
||||
@@ -1312,6 +1312,13 @@ impl MachInstEmit for Inst {
|
||||
| machreg_to_vec(rd.to_reg()),
|
||||
);
|
||||
}
|
||||
&Inst::FpuExtend { rd, rn, size } => {
|
||||
sink.put4(enc_fpurr(
|
||||
0b000_11110_00_1_000000_10000 | (size.ftype() << 13),
|
||||
rd,
|
||||
rn,
|
||||
));
|
||||
}
|
||||
&Inst::FpuRR { fpu_op, rd, rn } => {
|
||||
let top22 = match fpu_op {
|
||||
FPUOp1::Abs32 => 0b000_11110_00_1_000001_10000,
|
||||
@@ -1746,6 +1753,17 @@ impl MachInstEmit for Inst {
|
||||
| machreg_to_vec(rd.to_reg()),
|
||||
);
|
||||
}
|
||||
&Inst::VecDupFPImm { rd, imm, size } => {
|
||||
let imm = imm.enc_bits();
|
||||
let op = match size.lane_size() {
|
||||
ScalarSize::Size32 => 0,
|
||||
ScalarSize::Size64 => 1,
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
let q_op = op | ((size.is_128bits() as u32) << 1);
|
||||
|
||||
sink.put4(enc_asimd_mod_imm(rd, q_op, 0b1111, imm));
|
||||
}
|
||||
&Inst::VecDupImm {
|
||||
rd,
|
||||
imm,
|
||||
|
||||
@@ -2072,6 +2072,24 @@ fn test_aarch64_binemit() {
|
||||
"5205084E",
|
||||
"dup v18.2d, v10.d[0]",
|
||||
));
|
||||
insns.push((
|
||||
Inst::VecDupFPImm {
|
||||
rd: writable_vreg(31),
|
||||
imm: ASIMDFPModImm::maybe_from_u64(1_f32.to_bits() as u64, ScalarSize::Size32).unwrap(),
|
||||
size: VectorSize::Size32x2,
|
||||
},
|
||||
"1FF6030F",
|
||||
"fmov v31.2s, #1",
|
||||
));
|
||||
insns.push((
|
||||
Inst::VecDupFPImm {
|
||||
rd: writable_vreg(0),
|
||||
imm: ASIMDFPModImm::maybe_from_u64(2_f64.to_bits(), ScalarSize::Size64).unwrap(),
|
||||
size: VectorSize::Size64x2,
|
||||
},
|
||||
"00F4006F",
|
||||
"fmov v0.2d, #2",
|
||||
));
|
||||
insns.push((
|
||||
Inst::VecDupImm {
|
||||
rd: writable_vreg(31),
|
||||
@@ -2082,16 +2100,96 @@ fn test_aarch64_binemit() {
|
||||
"FFE7074F",
|
||||
"movi v31.16b, #255",
|
||||
));
|
||||
insns.push((
|
||||
Inst::VecDupImm {
|
||||
rd: writable_vreg(30),
|
||||
imm: ASIMDMovModImm::maybe_from_u64(0, ScalarSize::Size16).unwrap(),
|
||||
invert: false,
|
||||
size: VectorSize::Size16x8,
|
||||
},
|
||||
"1E84004F",
|
||||
"movi v30.8h, #0",
|
||||
));
|
||||
insns.push((
|
||||
Inst::VecDupImm {
|
||||
rd: writable_vreg(0),
|
||||
imm: ASIMDMovModImm::zero(),
|
||||
imm: ASIMDMovModImm::zero(ScalarSize::Size16),
|
||||
invert: true,
|
||||
size: VectorSize::Size16x4,
|
||||
},
|
||||
"0084002F",
|
||||
"mvni v0.4h, #0",
|
||||
));
|
||||
insns.push((
|
||||
Inst::VecDupImm {
|
||||
rd: writable_vreg(0),
|
||||
imm: ASIMDMovModImm::maybe_from_u64(256, ScalarSize::Size16).unwrap(),
|
||||
invert: false,
|
||||
size: VectorSize::Size16x8,
|
||||
},
|
||||
"20A4004F",
|
||||
"movi v0.8h, #1, LSL #8",
|
||||
));
|
||||
insns.push((
|
||||
Inst::VecDupImm {
|
||||
rd: writable_vreg(8),
|
||||
imm: ASIMDMovModImm::maybe_from_u64(2228223, ScalarSize::Size32).unwrap(),
|
||||
invert: false,
|
||||
size: VectorSize::Size32x4,
|
||||
},
|
||||
"28D4014F",
|
||||
"movi v8.4s, #33, MSL #16",
|
||||
));
|
||||
insns.push((
|
||||
Inst::VecDupImm {
|
||||
rd: writable_vreg(16),
|
||||
imm: ASIMDMovModImm::maybe_from_u64(35071, ScalarSize::Size32).unwrap(),
|
||||
invert: true,
|
||||
size: VectorSize::Size32x2,
|
||||
},
|
||||
"10C5042F",
|
||||
"mvni v16.2s, #136, MSL #8",
|
||||
));
|
||||
insns.push((
|
||||
Inst::VecDupImm {
|
||||
rd: writable_vreg(1),
|
||||
imm: ASIMDMovModImm::maybe_from_u64(0, ScalarSize::Size32).unwrap(),
|
||||
invert: false,
|
||||
size: VectorSize::Size32x2,
|
||||
},
|
||||
"0104000F",
|
||||
"movi v1.2s, #0",
|
||||
));
|
||||
insns.push((
|
||||
Inst::VecDupImm {
|
||||
rd: writable_vreg(24),
|
||||
imm: ASIMDMovModImm::maybe_from_u64(1107296256, ScalarSize::Size32).unwrap(),
|
||||
invert: false,
|
||||
size: VectorSize::Size32x4,
|
||||
},
|
||||
"5864024F",
|
||||
"movi v24.4s, #66, LSL #24",
|
||||
));
|
||||
insns.push((
|
||||
Inst::VecDupImm {
|
||||
rd: writable_vreg(8),
|
||||
imm: ASIMDMovModImm::zero(ScalarSize::Size64),
|
||||
invert: false,
|
||||
size: VectorSize::Size64x2,
|
||||
},
|
||||
"08E4006F",
|
||||
"movi v8.2d, #0",
|
||||
));
|
||||
insns.push((
|
||||
Inst::VecDupImm {
|
||||
rd: writable_vreg(7),
|
||||
imm: ASIMDMovModImm::maybe_from_u64(18374687574904995840, ScalarSize::Size64).unwrap(),
|
||||
invert: false,
|
||||
size: VectorSize::Size64x2,
|
||||
},
|
||||
"87E6046F",
|
||||
"movi v7.2d, #18374687574904995840",
|
||||
));
|
||||
insns.push((
|
||||
Inst::VecExtend {
|
||||
t: VecExtendOp::Sxtl8,
|
||||
@@ -4376,6 +4474,16 @@ fn test_aarch64_binemit() {
|
||||
"mov d23, v11.d[0]",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::FpuExtend {
|
||||
rd: writable_vreg(31),
|
||||
rn: vreg(0),
|
||||
size: ScalarSize::Size32,
|
||||
},
|
||||
"1F40201E",
|
||||
"fmov s31, s0",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::FpuRR {
|
||||
fpu_op: FPUOp1::Abs32,
|
||||
|
||||
@@ -668,39 +668,208 @@ impl MoveWideConst {
|
||||
}
|
||||
|
||||
/// Advanced SIMD modified immediate as used by MOVI/MVNI.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct ASIMDMovModImm {
|
||||
imm: u8,
|
||||
shift: u8,
|
||||
is_64bit: bool,
|
||||
shift_ones: bool,
|
||||
}
|
||||
|
||||
impl ASIMDMovModImm {
|
||||
/// Construct an ASIMDMovModImm from an arbitrary 64-bit constant, if possible.
|
||||
/// Note that the bits in `value` outside of the range specified by `size` are
|
||||
/// ignored; for example, in the case of `ScalarSize::Size8` all bits above the
|
||||
/// lowest 8 are ignored.
|
||||
pub fn maybe_from_u64(value: u64, size: ScalarSize) -> Option<ASIMDMovModImm> {
|
||||
match size {
|
||||
ScalarSize::Size8 => Some(ASIMDMovModImm {
|
||||
imm: value as u8,
|
||||
shift: 0,
|
||||
is_64bit: false,
|
||||
shift_ones: false,
|
||||
}),
|
||||
ScalarSize::Size16 => {
|
||||
let value = value as u16;
|
||||
|
||||
if value >> 8 == 0 {
|
||||
Some(ASIMDMovModImm {
|
||||
imm: value as u8,
|
||||
shift: 0,
|
||||
is_64bit: false,
|
||||
shift_ones: false,
|
||||
})
|
||||
} else if value as u8 == 0 {
|
||||
Some(ASIMDMovModImm {
|
||||
imm: (value >> 8) as u8,
|
||||
shift: 8,
|
||||
is_64bit: false,
|
||||
shift_ones: false,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
ScalarSize::Size32 => {
|
||||
let value = value as u32;
|
||||
|
||||
// Value is of the form 0x00MMFFFF.
|
||||
if value & 0xFF00FFFF == 0x0000FFFF {
|
||||
let imm = (value >> 16) as u8;
|
||||
|
||||
Some(ASIMDMovModImm {
|
||||
imm,
|
||||
shift: 16,
|
||||
is_64bit: false,
|
||||
shift_ones: true,
|
||||
})
|
||||
// Value is of the form 0x0000MMFF.
|
||||
} else if value & 0xFFFF00FF == 0x000000FF {
|
||||
let imm = (value >> 8) as u8;
|
||||
|
||||
Some(ASIMDMovModImm {
|
||||
imm,
|
||||
shift: 8,
|
||||
is_64bit: false,
|
||||
shift_ones: true,
|
||||
})
|
||||
} else {
|
||||
// Of the 4 bytes, at most one is non-zero.
|
||||
for shift in (0..32).step_by(8) {
|
||||
if value & (0xFF << shift) == value {
|
||||
return Some(ASIMDMovModImm {
|
||||
imm: (value >> shift) as u8,
|
||||
shift,
|
||||
is_64bit: false,
|
||||
shift_ones: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
ScalarSize::Size64 => {
|
||||
let mut imm = 0u8;
|
||||
|
||||
// Check if all bytes are either 0 or 0xFF.
|
||||
for i in 0..8 {
|
||||
let b = (value >> (i * 8)) as u8;
|
||||
|
||||
if b == 0 || b == 0xFF {
|
||||
imm |= (b & 1) << i;
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some(ASIMDMovModImm {
|
||||
imm,
|
||||
shift: 0,
|
||||
is_64bit: true,
|
||||
shift_ones: false,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a zero immediate of this format.
|
||||
pub fn zero() -> Self {
|
||||
pub fn zero(size: ScalarSize) -> Self {
|
||||
ASIMDMovModImm {
|
||||
imm: 0,
|
||||
shift: 0,
|
||||
is_64bit: size == ScalarSize::Size64,
|
||||
shift_ones: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the value that this immediate represents.
|
||||
pub fn value(&self) -> (u8, u32, bool) {
|
||||
(self.imm, self.shift as u32, self.shift_ones)
|
||||
}
|
||||
}
|
||||
|
||||
/// Advanced SIMD modified immediate as used by the vector variant of FMOV.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct ASIMDFPModImm {
|
||||
imm: u8,
|
||||
is_64bit: bool,
|
||||
}
|
||||
|
||||
impl ASIMDFPModImm {
|
||||
/// Construct an ASIMDFPModImm from an arbitrary 64-bit constant, if possible.
|
||||
pub fn maybe_from_u64(value: u64, size: ScalarSize) -> Option<ASIMDFPModImm> {
|
||||
// In all cases immediates are encoded as an 8-bit number 0b_abcdefgh;
|
||||
// let `D` be the inverse of the digit `d`.
|
||||
match size {
|
||||
ScalarSize::Size32 => {
|
||||
// In this case the representable immediates are 32-bit numbers of the form
|
||||
// 0b_aBbb_bbbc_defg_h000 shifted to the left by 16.
|
||||
let value = value as u32;
|
||||
let b0_5 = (value >> 19) & 0b111111;
|
||||
let b6 = (value >> 19) & (1 << 6);
|
||||
let b7 = (value >> 24) & (1 << 7);
|
||||
let imm = (b0_5 | b6 | b7) as u8;
|
||||
|
||||
if value == Self::value32(imm) {
|
||||
Some(ASIMDFPModImm {
|
||||
imm,
|
||||
is_64bit: false,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
ScalarSize::Size64 => {
|
||||
// In this case the representable immediates are 64-bit numbers of the form
|
||||
// 0b_aBbb_bbbb_bbcd_efgh shifted to the left by 48.
|
||||
let b0_5 = (value >> 48) & 0b111111;
|
||||
let b6 = (value >> 48) & (1 << 6);
|
||||
let b7 = (value >> 56) & (1 << 7);
|
||||
let imm = (b0_5 | b6 | b7) as u8;
|
||||
|
||||
if value == Self::value64(imm) {
|
||||
Some(ASIMDFPModImm {
|
||||
imm,
|
||||
is_64bit: true,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns bits ready for encoding.
|
||||
pub fn enc_bits(&self) -> u8 {
|
||||
self.imm
|
||||
}
|
||||
|
||||
/// Returns the 32-bit value that corresponds to an 8-bit encoding.
|
||||
fn value32(imm: u8) -> u32 {
|
||||
let imm = imm as u32;
|
||||
let b0_5 = imm & 0b111111;
|
||||
let b6 = (imm >> 6) & 1;
|
||||
let b6_inv = b6 ^ 1;
|
||||
let b7 = (imm >> 7) & 1;
|
||||
|
||||
b0_5 << 19 | (b6 * 0b11111) << 25 | b6_inv << 30 | b7 << 31
|
||||
}
|
||||
|
||||
/// Returns the 64-bit value that corresponds to an 8-bit encoding.
|
||||
fn value64(imm: u8) -> u64 {
|
||||
let imm = imm as u64;
|
||||
let b0_5 = imm & 0b111111;
|
||||
let b6 = (imm >> 6) & 1;
|
||||
let b6_inv = b6 ^ 1;
|
||||
let b7 = (imm >> 7) & 1;
|
||||
|
||||
b0_5 << 48 | (b6 * 0b11111111) << 54 | b6_inv << 62 | b7 << 63
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyPrint for NZCV {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
let fmt = |c: char, v| if v { c.to_ascii_uppercase() } else { c };
|
||||
@@ -782,7 +951,20 @@ impl PrettyPrint for MoveWideConst {
|
||||
|
||||
impl PrettyPrint for ASIMDMovModImm {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
if self.shift == 0 {
|
||||
if self.is_64bit {
|
||||
debug_assert_eq!(self.shift, 0);
|
||||
|
||||
let enc_imm = self.imm as i8;
|
||||
let mut imm = 0u64;
|
||||
|
||||
for i in 0..8 {
|
||||
let b = (enc_imm >> i) & 1;
|
||||
|
||||
imm |= (-b as u8 as u64) << (i * 8);
|
||||
}
|
||||
|
||||
format!("#{}", imm)
|
||||
} else if self.shift == 0 {
|
||||
format!("#{}", self.imm)
|
||||
} else {
|
||||
let shift_type = if self.shift_ones { "MSL" } else { "LSL" };
|
||||
@@ -791,6 +973,16 @@ impl PrettyPrint for ASIMDMovModImm {
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyPrint for ASIMDFPModImm {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
if self.is_64bit {
|
||||
format!("#{}", f64::from_bits(Self::value64(self.imm)))
|
||||
} else {
|
||||
format!("#{}", f32::from_bits(Self::value32(self.imm)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
@@ -1022,4 +1214,44 @@ mod test {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asimd_fp_mod_imm_test() {
|
||||
assert_eq!(None, ASIMDFPModImm::maybe_from_u64(0, ScalarSize::Size32));
|
||||
assert_eq!(
|
||||
None,
|
||||
ASIMDFPModImm::maybe_from_u64(0.013671875_f32.to_bits() as u64, ScalarSize::Size32)
|
||||
);
|
||||
assert_eq!(None, ASIMDFPModImm::maybe_from_u64(0, ScalarSize::Size64));
|
||||
assert_eq!(
|
||||
None,
|
||||
ASIMDFPModImm::maybe_from_u64(10000_f64.to_bits(), ScalarSize::Size64)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asimd_mov_mod_imm_test() {
|
||||
assert_eq!(
|
||||
None,
|
||||
ASIMDMovModImm::maybe_from_u64(513, ScalarSize::Size16)
|
||||
);
|
||||
assert_eq!(
|
||||
None,
|
||||
ASIMDMovModImm::maybe_from_u64(4278190335, ScalarSize::Size32)
|
||||
);
|
||||
assert_eq!(
|
||||
None,
|
||||
ASIMDMovModImm::maybe_from_u64(8388608, ScalarSize::Size64)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Some(ASIMDMovModImm {
|
||||
imm: 66,
|
||||
shift: 16,
|
||||
is_64bit: false,
|
||||
shift_ones: true,
|
||||
}),
|
||||
ASIMDMovModImm::maybe_from_u64(4390911, ScalarSize::Size32)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -755,6 +755,13 @@ pub enum Inst {
|
||||
size: VectorSize,
|
||||
},
|
||||
|
||||
/// Zero-extend a SIMD & FP scalar to the full width of a vector register.
|
||||
FpuExtend {
|
||||
rd: Writable<Reg>,
|
||||
rn: Reg,
|
||||
size: ScalarSize,
|
||||
},
|
||||
|
||||
/// 1-op FPU instruction.
|
||||
FpuRR {
|
||||
fpu_op: FPUOp1,
|
||||
@@ -928,6 +935,13 @@ pub enum Inst {
|
||||
size: VectorSize,
|
||||
},
|
||||
|
||||
/// Duplicate FP immediate to vector.
|
||||
VecDupFPImm {
|
||||
rd: Writable<Reg>,
|
||||
imm: ASIMDFPModImm,
|
||||
size: VectorSize,
|
||||
},
|
||||
|
||||
/// Duplicate immediate to vector.
|
||||
VecDupImm {
|
||||
rd: Writable<Reg>,
|
||||
@@ -1295,12 +1309,15 @@ impl Inst {
|
||||
value: u32,
|
||||
mut alloc_tmp: F,
|
||||
) -> SmallVec<[Inst; 4]> {
|
||||
// Note that we must make sure that all bits outside the lowest 32 are set to 0
|
||||
// because this function is also used to load wider constants (that have zeros
|
||||
// in their most significant bits).
|
||||
if value == 0 {
|
||||
smallvec![Inst::VecDupImm {
|
||||
rd,
|
||||
imm: ASIMDMovModImm::zero(),
|
||||
imm: ASIMDMovModImm::zero(ScalarSize::Size32),
|
||||
invert: false,
|
||||
size: VectorSize::Size8x8
|
||||
size: VectorSize::Size32x2
|
||||
}]
|
||||
} else {
|
||||
// TODO: use FMOV immediate form when `value` has sufficiently few mantissa/exponent
|
||||
@@ -1324,6 +1341,9 @@ impl Inst {
|
||||
const_data: u64,
|
||||
mut alloc_tmp: F,
|
||||
) -> SmallVec<[Inst; 4]> {
|
||||
// Note that we must make sure that all bits outside the lowest 64 are set to 0
|
||||
// because this function is also used to load wider constants (that have zeros
|
||||
// in their most significant bits).
|
||||
if let Ok(const_data) = u32::try_from(const_data) {
|
||||
Inst::load_fp_constant32(rd, const_data, alloc_tmp)
|
||||
// TODO: use FMOV immediate form when `const_data` has sufficiently few mantissa/exponent
|
||||
@@ -1394,7 +1414,7 @@ impl Inst {
|
||||
r
|
||||
}
|
||||
|
||||
/// Create instructions that load a 128-bit vector constant consisting of elements with
|
||||
/// Create instructions that load a vector constant consisting of elements with
|
||||
/// the same value.
|
||||
pub fn load_replicated_vector_pattern<F: FnMut(RegClass, Type) -> Writable<Reg>>(
|
||||
rd: Writable<Reg>,
|
||||
@@ -1403,6 +1423,15 @@ impl Inst {
|
||||
mut alloc_tmp: F,
|
||||
) -> SmallVec<[Inst; 5]> {
|
||||
let lane_size = size.lane_size();
|
||||
let widen_32_bit_pattern = |pattern, lane_size| {
|
||||
if lane_size == ScalarSize::Size32 {
|
||||
let pattern = pattern as u32 as u64;
|
||||
|
||||
ASIMDMovModImm::maybe_from_u64(pattern | (pattern << 32), ScalarSize::Size64)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(imm) = ASIMDMovModImm::maybe_from_u64(pattern, lane_size) {
|
||||
smallvec![Inst::VecDupImm {
|
||||
@@ -1421,6 +1450,27 @@ impl Inst {
|
||||
invert: true,
|
||||
size
|
||||
}]
|
||||
} else if let Some(imm) = widen_32_bit_pattern(pattern, lane_size) {
|
||||
let mut insts = smallvec![Inst::VecDupImm {
|
||||
rd,
|
||||
imm,
|
||||
invert: false,
|
||||
size: VectorSize::Size64x2,
|
||||
}];
|
||||
|
||||
// TODO: Implement support for 64-bit scalar MOVI; we zero-extend the
|
||||
// lower 64 bits instead.
|
||||
if !size.is_128bits() {
|
||||
insts.push(Inst::FpuExtend {
|
||||
rd,
|
||||
rn: rd.to_reg(),
|
||||
size: ScalarSize::Size64,
|
||||
});
|
||||
}
|
||||
|
||||
insts
|
||||
} else if let Some(imm) = ASIMDFPModImm::maybe_from_u64(pattern, lane_size) {
|
||||
smallvec![Inst::VecDupFPImm { rd, imm, size }]
|
||||
} else {
|
||||
let tmp = alloc_tmp(RegClass::I64, I64);
|
||||
let mut insts = SmallVec::from(&Inst::load_constant(tmp, pattern)[..]);
|
||||
@@ -1721,6 +1771,10 @@ fn aarch64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
|
||||
collector.add_def(rd);
|
||||
collector.add_use(rn);
|
||||
}
|
||||
&Inst::FpuExtend { rd, rn, .. } => {
|
||||
collector.add_def(rd);
|
||||
collector.add_use(rn);
|
||||
}
|
||||
&Inst::FpuRR { rd, rn, .. } => {
|
||||
collector.add_def(rd);
|
||||
collector.add_use(rn);
|
||||
@@ -1870,6 +1924,9 @@ fn aarch64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
|
||||
collector.add_def(rd);
|
||||
collector.add_use(rn);
|
||||
}
|
||||
&Inst::VecDupFPImm { rd, .. } => {
|
||||
collector.add_def(rd);
|
||||
}
|
||||
&Inst::VecDupImm { rd, .. } => {
|
||||
collector.add_def(rd);
|
||||
}
|
||||
@@ -2299,6 +2356,14 @@ fn aarch64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
|
||||
map_def(mapper, rd);
|
||||
map_use(mapper, rn);
|
||||
}
|
||||
&mut Inst::FpuExtend {
|
||||
ref mut rd,
|
||||
ref mut rn,
|
||||
..
|
||||
} => {
|
||||
map_def(mapper, rd);
|
||||
map_use(mapper, rn);
|
||||
}
|
||||
&mut Inst::FpuRR {
|
||||
ref mut rd,
|
||||
ref mut rn,
|
||||
@@ -2582,6 +2647,9 @@ fn aarch64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
|
||||
map_def(mapper, rd);
|
||||
map_use(mapper, rn);
|
||||
}
|
||||
&mut Inst::VecDupFPImm { ref mut rd, .. } => {
|
||||
map_def(mapper, rd);
|
||||
}
|
||||
&mut Inst::VecDupImm { ref mut rd, .. } => {
|
||||
map_def(mapper, rd);
|
||||
}
|
||||
@@ -3229,6 +3297,12 @@ impl Inst {
|
||||
let rn = show_vreg_element(rn, mb_rru, idx, size);
|
||||
format!("mov {}, {}", rd, rn)
|
||||
}
|
||||
&Inst::FpuExtend { rd, rn, size } => {
|
||||
let rd = show_vreg_scalar(rd.to_reg(), mb_rru, size);
|
||||
let rn = show_vreg_scalar(rn, mb_rru, size);
|
||||
|
||||
format!("fmov {}, {}", rd, rn)
|
||||
}
|
||||
&Inst::FpuRR { fpu_op, rd, rn } => {
|
||||
let (op, sizesrc, sizedest) = match fpu_op {
|
||||
FPUOp1::Abs32 => ("fabs", ScalarSize::Size32, ScalarSize::Size32),
|
||||
@@ -3465,6 +3539,12 @@ impl Inst {
|
||||
let rn = show_vreg_element(rn, mb_rru, 0, size);
|
||||
format!("dup {}, {}", rd, rn)
|
||||
}
|
||||
&Inst::VecDupFPImm { rd, imm, size } => {
|
||||
let imm = imm.show_rru(mb_rru);
|
||||
let rd = show_vreg_vector(rd.to_reg(), mb_rru, size);
|
||||
|
||||
format!("fmov {}, {}", rd, imm)
|
||||
}
|
||||
&Inst::VecDupImm {
|
||||
rd,
|
||||
imm,
|
||||
|
||||
@@ -616,7 +616,7 @@ fn collect_address_addends<C: LowerCtx<I = Inst>>(
|
||||
maybe_input_insn(ctx, extendee_input, Opcode::Iconst),
|
||||
extendop,
|
||||
) {
|
||||
let value = ctx.get_constant(insn).unwrap() as i64;
|
||||
let value = (ctx.get_constant(insn).unwrap() & 0xFFFF_FFFF_u64) as i64;
|
||||
offset += value;
|
||||
} else {
|
||||
let reg = put_input_in_reg(ctx, extendee_input, NarrowValueMode::None);
|
||||
@@ -853,7 +853,7 @@ pub(crate) fn lower_constant_f128<C: LowerCtx<I = Inst>>(
|
||||
// is potentially expensive.
|
||||
ctx.emit(Inst::VecDupImm {
|
||||
rd,
|
||||
imm: ASIMDMovModImm::zero(),
|
||||
imm: ASIMDMovModImm::zero(ScalarSize::Size8),
|
||||
invert: false,
|
||||
size: VectorSize::Size8x16,
|
||||
});
|
||||
|
||||
@@ -2075,8 +2075,6 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
// derivation of these sequences. Alternative sequences are discussed in
|
||||
// https://github.com/bytecodealliance/wasmtime/issues/2296, although they are not
|
||||
// used here.
|
||||
// Also .. FIXME: when https://github.com/bytecodealliance/wasmtime/pull/2310 is
|
||||
// merged, use `lower_splat_constant` instead to generate the constants.
|
||||
let tmp_r0 = ctx.alloc_tmp(RegClass::I64, I64);
|
||||
let tmp_v0 = ctx.alloc_tmp(RegClass::V128, I8X16);
|
||||
let tmp_v1 = ctx.alloc_tmp(RegClass::V128, I8X16);
|
||||
@@ -2100,12 +2098,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
size: VectorSize::Size8x16,
|
||||
imm: 7,
|
||||
});
|
||||
lower_constant_u64(ctx, tmp_r0, 0x8040201008040201u64);
|
||||
ctx.emit(Inst::VecDup {
|
||||
rd: tmp_v0,
|
||||
rn: tmp_r0.to_reg(),
|
||||
size: VectorSize::Size64x2,
|
||||
});
|
||||
lower_splat_const(ctx, tmp_v0, 0x8040201008040201u64, VectorSize::Size64x2);
|
||||
ctx.emit(Inst::VecRRR {
|
||||
alu_op: VecALUOp::And,
|
||||
rd: tmp_v1,
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::{CodegenError, CodegenResult};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use regalloc::{RealReg, Reg, RegClass, Set, Writable};
|
||||
use smallvec::SmallVec;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
/// Support for the ARM ABI from the callee side (within a function body).
|
||||
pub(crate) type Arm32ABICallee = ABICalleeImpl<Arm32MachineDeps>;
|
||||
@@ -305,6 +305,12 @@ impl ABIMachineSpec for Arm32MachineDeps {
|
||||
ret
|
||||
}
|
||||
|
||||
fn gen_probestack(_: u32) -> SmallVec<[Self::I; 2]> {
|
||||
// TODO: implement if we ever require stack probes on ARM32 (unlikely
|
||||
// unless Lucet is ported)
|
||||
smallvec![]
|
||||
}
|
||||
|
||||
/// Returns stack bytes used as well as instructions. Does not adjust
|
||||
/// nominal SP offset; caller will do that.
|
||||
fn gen_clobber_save(
|
||||
|
||||
@@ -235,6 +235,14 @@ pub trait TargetIsa: fmt::Display + Send + Sync {
|
||||
CallConv::triple_default(self.triple())
|
||||
}
|
||||
|
||||
/// Get the endianness of this ISA.
|
||||
fn endianness(&self) -> ir::Endianness {
|
||||
match self.triple().endianness().unwrap() {
|
||||
target_lexicon::Endianness::Little => ir::Endianness::Little,
|
||||
target_lexicon::Endianness::Big => ir::Endianness::Big,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the pointer type of this ISA.
|
||||
fn pointer_type(&self) -> ir::Type {
|
||||
ir::Type::int(u16::from(self.pointer_bits())).unwrap()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Implementation of the standard x64 ABI.
|
||||
|
||||
use crate::ir::types::*;
|
||||
use crate::ir::{self, types, MemFlags, TrapCode, Type};
|
||||
use crate::ir::{self, types, ExternalName, LibCall, MemFlags, Opcode, TrapCode, Type};
|
||||
use crate::isa;
|
||||
use crate::isa::{x64::inst::*, CallConv};
|
||||
use crate::machinst::abi_impl::*;
|
||||
@@ -389,6 +389,22 @@ impl ABIMachineSpec for X64ABIMachineSpec {
|
||||
insts
|
||||
}
|
||||
|
||||
fn gen_probestack(frame_size: u32) -> SmallVec<[Self::I; 2]> {
|
||||
let mut insts = SmallVec::new();
|
||||
insts.push(Inst::imm(
|
||||
OperandSize::Size32,
|
||||
frame_size as u64,
|
||||
Writable::from_reg(regs::rax()),
|
||||
));
|
||||
insts.push(Inst::CallKnown {
|
||||
dest: ExternalName::LibCall(LibCall::Probestack),
|
||||
uses: vec![regs::rax()],
|
||||
defs: vec![],
|
||||
opcode: Opcode::Call,
|
||||
});
|
||||
insts
|
||||
}
|
||||
|
||||
fn gen_clobber_save(
|
||||
call_conv: isa::CallConv,
|
||||
_: &settings::Flags,
|
||||
|
||||
@@ -467,7 +467,10 @@ pub enum SseOpcode {
|
||||
Pabsb,
|
||||
Pabsw,
|
||||
Pabsd,
|
||||
Packssdw,
|
||||
Packsswb,
|
||||
Packusdw,
|
||||
Packuswb,
|
||||
Paddb,
|
||||
Paddd,
|
||||
Paddq,
|
||||
@@ -476,6 +479,7 @@ pub enum SseOpcode {
|
||||
Paddsw,
|
||||
Paddusb,
|
||||
Paddusw,
|
||||
Palignr,
|
||||
Pand,
|
||||
Pandn,
|
||||
Pavgb,
|
||||
@@ -507,6 +511,18 @@ pub enum SseOpcode {
|
||||
Pminuw,
|
||||
Pminud,
|
||||
Pmovmskb,
|
||||
Pmovsxbd,
|
||||
Pmovsxbw,
|
||||
Pmovsxbq,
|
||||
Pmovsxwd,
|
||||
Pmovsxwq,
|
||||
Pmovsxdq,
|
||||
Pmovzxbd,
|
||||
Pmovzxbw,
|
||||
Pmovzxbq,
|
||||
Pmovzxwd,
|
||||
Pmovzxwq,
|
||||
Pmovzxdq,
|
||||
Pmulld,
|
||||
Pmullw,
|
||||
Pmuludq,
|
||||
@@ -534,6 +550,8 @@ pub enum SseOpcode {
|
||||
Punpcklbw,
|
||||
Pxor,
|
||||
Rcpss,
|
||||
Roundps,
|
||||
Roundpd,
|
||||
Roundss,
|
||||
Roundsd,
|
||||
Rsqrtss,
|
||||
@@ -620,7 +638,9 @@ impl SseOpcode {
|
||||
| SseOpcode::Mulpd
|
||||
| SseOpcode::Mulsd
|
||||
| SseOpcode::Orpd
|
||||
| SseOpcode::Packssdw
|
||||
| SseOpcode::Packsswb
|
||||
| SseOpcode::Packuswb
|
||||
| SseOpcode::Paddb
|
||||
| SseOpcode::Paddd
|
||||
| SseOpcode::Paddq
|
||||
@@ -676,9 +696,14 @@ impl SseOpcode {
|
||||
| SseOpcode::Ucomisd
|
||||
| SseOpcode::Xorpd => SSE2,
|
||||
|
||||
SseOpcode::Pabsb | SseOpcode::Pabsw | SseOpcode::Pabsd | SseOpcode::Pshufb => SSSE3,
|
||||
SseOpcode::Pabsb
|
||||
| SseOpcode::Pabsw
|
||||
| SseOpcode::Pabsd
|
||||
| SseOpcode::Palignr
|
||||
| SseOpcode::Pshufb => SSSE3,
|
||||
|
||||
SseOpcode::Insertps
|
||||
| SseOpcode::Packusdw
|
||||
| SseOpcode::Pcmpeqq
|
||||
| SseOpcode::Pextrb
|
||||
| SseOpcode::Pextrd
|
||||
@@ -692,8 +717,22 @@ impl SseOpcode {
|
||||
| SseOpcode::Pminsd
|
||||
| SseOpcode::Pminuw
|
||||
| SseOpcode::Pminud
|
||||
| SseOpcode::Pmovsxbd
|
||||
| SseOpcode::Pmovsxbw
|
||||
| SseOpcode::Pmovsxbq
|
||||
| SseOpcode::Pmovsxwd
|
||||
| SseOpcode::Pmovsxwq
|
||||
| SseOpcode::Pmovsxdq
|
||||
| SseOpcode::Pmovzxbd
|
||||
| SseOpcode::Pmovzxbw
|
||||
| SseOpcode::Pmovzxbq
|
||||
| SseOpcode::Pmovzxwd
|
||||
| SseOpcode::Pmovzxwq
|
||||
| SseOpcode::Pmovzxdq
|
||||
| SseOpcode::Pmulld
|
||||
| SseOpcode::Ptest
|
||||
| SseOpcode::Roundps
|
||||
| SseOpcode::Roundpd
|
||||
| SseOpcode::Roundss
|
||||
| SseOpcode::Roundsd => SSE41,
|
||||
|
||||
@@ -772,7 +811,10 @@ impl fmt::Debug for SseOpcode {
|
||||
SseOpcode::Pabsb => "pabsb",
|
||||
SseOpcode::Pabsw => "pabsw",
|
||||
SseOpcode::Pabsd => "pabsd",
|
||||
SseOpcode::Packssdw => "packssdw",
|
||||
SseOpcode::Packsswb => "packsswb",
|
||||
SseOpcode::Packusdw => "packusdw",
|
||||
SseOpcode::Packuswb => "packuswb",
|
||||
SseOpcode::Paddb => "paddb",
|
||||
SseOpcode::Paddd => "paddd",
|
||||
SseOpcode::Paddq => "paddq",
|
||||
@@ -781,6 +823,7 @@ impl fmt::Debug for SseOpcode {
|
||||
SseOpcode::Paddsw => "paddsw",
|
||||
SseOpcode::Paddusb => "paddusb",
|
||||
SseOpcode::Paddusw => "paddusw",
|
||||
SseOpcode::Palignr => "palignr",
|
||||
SseOpcode::Pand => "pand",
|
||||
SseOpcode::Pandn => "pandn",
|
||||
SseOpcode::Pavgb => "pavgb",
|
||||
@@ -812,6 +855,18 @@ impl fmt::Debug for SseOpcode {
|
||||
SseOpcode::Pminuw => "pminuw",
|
||||
SseOpcode::Pminud => "pminud",
|
||||
SseOpcode::Pmovmskb => "pmovmskb",
|
||||
SseOpcode::Pmovsxbd => "pmovsxbd",
|
||||
SseOpcode::Pmovsxbw => "pmovsxbw",
|
||||
SseOpcode::Pmovsxbq => "pmovsxbq",
|
||||
SseOpcode::Pmovsxwd => "pmovsxwd",
|
||||
SseOpcode::Pmovsxwq => "pmovsxwq",
|
||||
SseOpcode::Pmovsxdq => "pmovsxdq",
|
||||
SseOpcode::Pmovzxbd => "pmovzxbd",
|
||||
SseOpcode::Pmovzxbw => "pmovzxbw",
|
||||
SseOpcode::Pmovzxbq => "pmovzxbq",
|
||||
SseOpcode::Pmovzxwd => "pmovzxwd",
|
||||
SseOpcode::Pmovzxwq => "pmovzxwq",
|
||||
SseOpcode::Pmovzxdq => "pmovzxdq",
|
||||
SseOpcode::Pmulld => "pmulld",
|
||||
SseOpcode::Pmullw => "pmullw",
|
||||
SseOpcode::Pmuludq => "pmuludq",
|
||||
@@ -839,6 +894,8 @@ impl fmt::Debug for SseOpcode {
|
||||
SseOpcode::Punpcklbw => "punpcklbw",
|
||||
SseOpcode::Pxor => "pxor",
|
||||
SseOpcode::Rcpss => "rcpss",
|
||||
SseOpcode::Roundps => "roundps",
|
||||
SseOpcode::Roundpd => "roundpd",
|
||||
SseOpcode::Roundss => "roundss",
|
||||
SseOpcode::Roundsd => "roundsd",
|
||||
SseOpcode::Rsqrtss => "rsqrtss",
|
||||
@@ -1187,6 +1244,25 @@ impl From<FloatCC> for FcmpImm {
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode the rounding modes used as part of the Rounding Control field.
|
||||
/// Note, these rounding immediates only consider the rounding control field
|
||||
/// (i.e. the rounding mode) which only take up the first two bits when encoded.
|
||||
/// However the rounding immediate which this field helps make up, also includes
|
||||
/// bits 3 and 4 which define the rounding select and precision mask respectively.
|
||||
/// These two bits are not defined here and are implictly set to zero when encoded.
|
||||
pub(crate) enum RoundImm {
|
||||
RoundNearest = 0x00,
|
||||
RoundDown = 0x01,
|
||||
RoundUp = 0x02,
|
||||
RoundZero = 0x03,
|
||||
}
|
||||
|
||||
impl RoundImm {
|
||||
pub(crate) fn encode(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
}
|
||||
|
||||
/// An operand's size in bits.
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum OperandSize {
|
||||
|
||||
@@ -182,6 +182,7 @@ impl LegacyPrefixes {
|
||||
fn emit_std_enc_mem(
|
||||
sink: &mut MachBuffer<Inst>,
|
||||
state: &EmitState,
|
||||
info: &EmitInfo,
|
||||
prefixes: LegacyPrefixes,
|
||||
opcodes: u32,
|
||||
mut num_opcodes: usize,
|
||||
@@ -194,7 +195,8 @@ fn emit_std_enc_mem(
|
||||
// expression. But `enc_g` can be derived from a register of any class.
|
||||
|
||||
let srcloc = state.cur_srcloc();
|
||||
if srcloc != SourceLoc::default() && mem_e.can_trap() {
|
||||
let can_trap = mem_e.can_trap();
|
||||
if srcloc != SourceLoc::default() && can_trap {
|
||||
sink.add_trap(srcloc, TrapCode::HeapOutOfBounds);
|
||||
}
|
||||
|
||||
@@ -202,6 +204,12 @@ fn emit_std_enc_mem(
|
||||
|
||||
match mem_e {
|
||||
Amode::ImmReg { simm32, base, .. } => {
|
||||
// If this is an access based off of RSP, it may trap with a stack overflow if it's the
|
||||
// first touch of a new stack page.
|
||||
if *base == regs::rsp() && !can_trap && info.flags().enable_probestack() {
|
||||
sink.add_trap(srcloc, TrapCode::StackOverflow);
|
||||
}
|
||||
|
||||
// First, the REX byte.
|
||||
let enc_e = int_reg_enc(*base);
|
||||
rex.emit_two_op(sink, enc_g, enc_e);
|
||||
@@ -262,6 +270,12 @@ fn emit_std_enc_mem(
|
||||
shift,
|
||||
..
|
||||
} => {
|
||||
// If this is an access based off of RSP, it may trap with a stack overflow if it's the
|
||||
// first touch of a new stack page.
|
||||
if *reg_base == regs::rsp() && !can_trap && info.flags().enable_probestack() {
|
||||
sink.add_trap(srcloc, TrapCode::StackOverflow);
|
||||
}
|
||||
|
||||
let enc_base = int_reg_enc(*reg_base);
|
||||
let enc_index = int_reg_enc(*reg_index);
|
||||
|
||||
@@ -350,6 +364,7 @@ fn emit_std_enc_enc(
|
||||
fn emit_std_reg_mem(
|
||||
sink: &mut MachBuffer<Inst>,
|
||||
state: &EmitState,
|
||||
info: &EmitInfo,
|
||||
prefixes: LegacyPrefixes,
|
||||
opcodes: u32,
|
||||
num_opcodes: usize,
|
||||
@@ -361,6 +376,7 @@ fn emit_std_reg_mem(
|
||||
emit_std_enc_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
prefixes,
|
||||
opcodes,
|
||||
num_opcodes,
|
||||
@@ -538,6 +554,7 @@ pub(crate) fn emit(
|
||||
emit_std_reg_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
LegacyPrefixes::None,
|
||||
0x0FAF,
|
||||
2,
|
||||
@@ -597,6 +614,7 @@ pub(crate) fn emit(
|
||||
emit_std_reg_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
LegacyPrefixes::None,
|
||||
opcode_m,
|
||||
1,
|
||||
@@ -654,6 +672,7 @@ pub(crate) fn emit(
|
||||
emit_std_reg_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
prefix,
|
||||
opcode,
|
||||
num_opcodes,
|
||||
@@ -717,7 +736,9 @@ pub(crate) fn emit(
|
||||
}
|
||||
RegMem::Mem { addr: src } => {
|
||||
let amode = src.finalize(state, sink);
|
||||
emit_std_enc_mem(sink, state, prefix, opcode, 1, subopcode, &amode, rex_flags);
|
||||
emit_std_enc_mem(
|
||||
sink, state, info, prefix, opcode, 1, subopcode, &amode, rex_flags,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -738,7 +759,9 @@ pub(crate) fn emit(
|
||||
}
|
||||
RegMem::Mem { addr: src } => {
|
||||
let amode = src.finalize(state, sink);
|
||||
emit_std_enc_mem(sink, state, prefix, 0xF7, 1, subopcode, &amode, rex_flags);
|
||||
emit_std_enc_mem(
|
||||
sink, state, info, prefix, 0xF7, 1, subopcode, &amode, rex_flags,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -987,6 +1010,7 @@ pub(crate) fn emit(
|
||||
emit_std_reg_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
LegacyPrefixes::None,
|
||||
opcodes,
|
||||
num_opcodes,
|
||||
@@ -1004,6 +1028,7 @@ pub(crate) fn emit(
|
||||
emit_std_reg_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
LegacyPrefixes::None,
|
||||
0x8B,
|
||||
1,
|
||||
@@ -1019,6 +1044,7 @@ pub(crate) fn emit(
|
||||
emit_std_reg_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
LegacyPrefixes::None,
|
||||
0x8D,
|
||||
1,
|
||||
@@ -1081,6 +1107,7 @@ pub(crate) fn emit(
|
||||
emit_std_reg_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
LegacyPrefixes::None,
|
||||
opcodes,
|
||||
num_opcodes,
|
||||
@@ -1108,7 +1135,17 @@ pub(crate) fn emit(
|
||||
};
|
||||
|
||||
// MOV r8, r/m8 is (REX.W==0) 88 /r
|
||||
emit_std_reg_mem(sink, state, LegacyPrefixes::None, 0x88, 1, *src, dst, rex)
|
||||
emit_std_reg_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
LegacyPrefixes::None,
|
||||
0x88,
|
||||
1,
|
||||
*src,
|
||||
dst,
|
||||
rex,
|
||||
)
|
||||
}
|
||||
|
||||
2 => {
|
||||
@@ -1116,6 +1153,7 @@ pub(crate) fn emit(
|
||||
emit_std_reg_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
LegacyPrefixes::_66,
|
||||
0x89,
|
||||
1,
|
||||
@@ -1130,6 +1168,7 @@ pub(crate) fn emit(
|
||||
emit_std_reg_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
LegacyPrefixes::None,
|
||||
0x89,
|
||||
1,
|
||||
@@ -1144,6 +1183,7 @@ pub(crate) fn emit(
|
||||
emit_std_reg_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
LegacyPrefixes::None,
|
||||
0x89,
|
||||
1,
|
||||
@@ -1253,6 +1293,7 @@ pub(crate) fn emit(
|
||||
emit_std_reg_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
prefix,
|
||||
opcode_bytes,
|
||||
2,
|
||||
@@ -1311,7 +1352,7 @@ pub(crate) fn emit(
|
||||
let addr = &addr.finalize(state, sink);
|
||||
// Whereas here we revert to the "normal" G-E ordering.
|
||||
let opcode = if *size == 1 { 0x3A } else { 0x3B };
|
||||
emit_std_reg_mem(sink, state, prefix, opcode, 1, *reg_g, addr, rex);
|
||||
emit_std_reg_mem(sink, state, info, prefix, opcode, 1, *reg_g, addr, rex);
|
||||
}
|
||||
|
||||
RegMemImm::Imm { simm32 } => {
|
||||
@@ -1372,6 +1413,7 @@ pub(crate) fn emit(
|
||||
emit_std_reg_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
prefix,
|
||||
opcode,
|
||||
2,
|
||||
@@ -1408,6 +1450,10 @@ pub(crate) fn emit(
|
||||
}
|
||||
|
||||
Inst::Push64 { src } => {
|
||||
if info.flags().enable_probestack() {
|
||||
sink.add_trap(state.cur_srcloc(), TrapCode::StackOverflow);
|
||||
}
|
||||
|
||||
match src {
|
||||
RegMemImm::Reg { reg } => {
|
||||
let enc_reg = int_reg_enc(*reg);
|
||||
@@ -1423,6 +1469,7 @@ pub(crate) fn emit(
|
||||
emit_std_enc_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
LegacyPrefixes::None,
|
||||
0xFF,
|
||||
1,
|
||||
@@ -1454,6 +1501,9 @@ pub(crate) fn emit(
|
||||
}
|
||||
|
||||
Inst::CallKnown { dest, opcode, .. } => {
|
||||
if info.flags().enable_probestack() {
|
||||
sink.add_trap(state.cur_srcloc(), TrapCode::StackOverflow);
|
||||
}
|
||||
if let Some(s) = state.take_stack_map() {
|
||||
sink.add_stack_map(StackMapExtent::UpcomingBytes(5), s);
|
||||
}
|
||||
@@ -1469,6 +1519,9 @@ pub(crate) fn emit(
|
||||
}
|
||||
|
||||
Inst::CallUnknown { dest, opcode, .. } => {
|
||||
if info.flags().enable_probestack() {
|
||||
sink.add_trap(state.cur_srcloc(), TrapCode::StackOverflow);
|
||||
}
|
||||
let start_offset = sink.cur_offset();
|
||||
match dest {
|
||||
RegMem::Reg { reg } => {
|
||||
@@ -1489,6 +1542,7 @@ pub(crate) fn emit(
|
||||
emit_std_enc_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
LegacyPrefixes::None,
|
||||
0xFF,
|
||||
1,
|
||||
@@ -1587,6 +1641,7 @@ pub(crate) fn emit(
|
||||
emit_std_enc_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
LegacyPrefixes::None,
|
||||
0xFF,
|
||||
1,
|
||||
@@ -1733,6 +1788,7 @@ pub(crate) fn emit(
|
||||
emit_std_reg_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
prefix,
|
||||
opcode,
|
||||
num_opcodes,
|
||||
@@ -1781,7 +1837,10 @@ pub(crate) fn emit(
|
||||
SseOpcode::Mulsd => (LegacyPrefixes::_F2, 0x0F59, 2),
|
||||
SseOpcode::Orpd => (LegacyPrefixes::_66, 0x0F56, 2),
|
||||
SseOpcode::Orps => (LegacyPrefixes::None, 0x0F56, 2),
|
||||
SseOpcode::Packssdw => (LegacyPrefixes::_66, 0x0F6B, 2),
|
||||
SseOpcode::Packsswb => (LegacyPrefixes::_66, 0x0F63, 2),
|
||||
SseOpcode::Packusdw => (LegacyPrefixes::_66, 0x0F382B, 3),
|
||||
SseOpcode::Packuswb => (LegacyPrefixes::_66, 0x0F67, 2),
|
||||
SseOpcode::Paddb => (LegacyPrefixes::_66, 0x0FFC, 2),
|
||||
SseOpcode::Paddd => (LegacyPrefixes::_66, 0x0FFE, 2),
|
||||
SseOpcode::Paddq => (LegacyPrefixes::_66, 0x0FD4, 2),
|
||||
@@ -1802,6 +1861,18 @@ pub(crate) fn emit(
|
||||
SseOpcode::Pcmpgtw => (LegacyPrefixes::_66, 0x0F65, 2),
|
||||
SseOpcode::Pcmpgtd => (LegacyPrefixes::_66, 0x0F66, 2),
|
||||
SseOpcode::Pcmpgtq => (LegacyPrefixes::_66, 0x0F3837, 3),
|
||||
SseOpcode::Pmovsxbd => (LegacyPrefixes::_66, 0x0F3821, 3),
|
||||
SseOpcode::Pmovsxbw => (LegacyPrefixes::_66, 0x0F3820, 3),
|
||||
SseOpcode::Pmovsxbq => (LegacyPrefixes::_66, 0x0F3822, 3),
|
||||
SseOpcode::Pmovsxwd => (LegacyPrefixes::_66, 0x0F3823, 3),
|
||||
SseOpcode::Pmovsxwq => (LegacyPrefixes::_66, 0x0F3824, 3),
|
||||
SseOpcode::Pmovsxdq => (LegacyPrefixes::_66, 0x0F3825, 3),
|
||||
SseOpcode::Pmovzxbd => (LegacyPrefixes::_66, 0x0F3831, 3),
|
||||
SseOpcode::Pmovzxbw => (LegacyPrefixes::_66, 0x0F3830, 3),
|
||||
SseOpcode::Pmovzxbq => (LegacyPrefixes::_66, 0x0F3832, 3),
|
||||
SseOpcode::Pmovzxwd => (LegacyPrefixes::_66, 0x0F3833, 3),
|
||||
SseOpcode::Pmovzxwq => (LegacyPrefixes::_66, 0x0F3834, 3),
|
||||
SseOpcode::Pmovzxdq => (LegacyPrefixes::_66, 0x0F3835, 3),
|
||||
SseOpcode::Pmaxsb => (LegacyPrefixes::_66, 0x0F383C, 3),
|
||||
SseOpcode::Pmaxsw => (LegacyPrefixes::_66, 0x0FEE, 2),
|
||||
SseOpcode::Pmaxsd => (LegacyPrefixes::_66, 0x0F383D, 3),
|
||||
@@ -1848,6 +1919,7 @@ pub(crate) fn emit(
|
||||
emit_std_reg_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
prefix,
|
||||
opcode,
|
||||
length,
|
||||
@@ -1958,6 +2030,7 @@ pub(crate) fn emit(
|
||||
SseOpcode::Cmpss => (LegacyPrefixes::_F3, 0x0FC2, 2),
|
||||
SseOpcode::Cmpsd => (LegacyPrefixes::_F2, 0x0FC2, 2),
|
||||
SseOpcode::Insertps => (LegacyPrefixes::_66, 0x0F3A21, 3),
|
||||
SseOpcode::Palignr => (LegacyPrefixes::_66, 0x0F3A0F, 3),
|
||||
SseOpcode::Pinsrb => (LegacyPrefixes::_66, 0x0F3A20, 3),
|
||||
SseOpcode::Pinsrw => (LegacyPrefixes::_66, 0x0FC4, 2),
|
||||
SseOpcode::Pinsrd => (LegacyPrefixes::_66, 0x0F3A22, 3),
|
||||
@@ -1965,6 +2038,8 @@ pub(crate) fn emit(
|
||||
SseOpcode::Pextrw => (LegacyPrefixes::_66, 0x0FC5, 2),
|
||||
SseOpcode::Pextrd => (LegacyPrefixes::_66, 0x0F3A16, 3),
|
||||
SseOpcode::Pshufd => (LegacyPrefixes::_66, 0x0F70, 2),
|
||||
SseOpcode::Roundps => (LegacyPrefixes::_66, 0x0F3A08, 3),
|
||||
SseOpcode::Roundpd => (LegacyPrefixes::_66, 0x0F3A09, 3),
|
||||
_ => unimplemented!("Opcode {:?} not implemented", op),
|
||||
};
|
||||
let rex = if *is64 {
|
||||
@@ -1994,7 +2069,17 @@ pub(crate) fn emit(
|
||||
!regs_swapped,
|
||||
"No existing way to encode a mem argument in the ModRM r/m field."
|
||||
);
|
||||
emit_std_reg_mem(sink, state, prefix, opcode, len, dst.to_reg(), addr, rex);
|
||||
emit_std_reg_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
prefix,
|
||||
opcode,
|
||||
len,
|
||||
dst.to_reg(),
|
||||
addr,
|
||||
rex,
|
||||
);
|
||||
}
|
||||
}
|
||||
sink.put1(*imm);
|
||||
@@ -2027,6 +2112,7 @@ pub(crate) fn emit(
|
||||
emit_std_reg_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
prefix,
|
||||
opcode,
|
||||
2,
|
||||
@@ -2091,7 +2177,17 @@ pub(crate) fn emit(
|
||||
}
|
||||
RegMem::Mem { addr } => {
|
||||
let addr = &addr.finalize(state, sink);
|
||||
emit_std_reg_mem(sink, state, prefix, opcode, 2, reg_g.to_reg(), addr, rex);
|
||||
emit_std_reg_mem(
|
||||
sink,
|
||||
state,
|
||||
info,
|
||||
prefix,
|
||||
opcode,
|
||||
2,
|
||||
reg_g.to_reg(),
|
||||
addr,
|
||||
rex,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2111,7 +2207,7 @@ pub(crate) fn emit(
|
||||
}
|
||||
RegMem::Mem { addr } => {
|
||||
let addr = &addr.finalize(state, sink);
|
||||
emit_std_reg_mem(sink, state, prefix, opcode, len, *dst, addr, rex);
|
||||
emit_std_reg_mem(sink, state, info, prefix, opcode, len, *dst, addr, rex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2625,7 +2721,7 @@ pub(crate) fn emit(
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let amode = dst.finalize(state, sink);
|
||||
emit_std_reg_mem(sink, state, prefix, opcodes, 2, *src, &amode, rex);
|
||||
emit_std_reg_mem(sink, state, info, prefix, opcodes, 2, *src, &amode, rex);
|
||||
}
|
||||
|
||||
Inst::AtomicRmwSeq { ty, op } => {
|
||||
|
||||
@@ -3151,12 +3151,30 @@ fn test_x64_emit() {
|
||||
"pshufb %xmm11, %xmm2",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::xmm_rm_r(SseOpcode::Packssdw, RegMem::reg(xmm11), w_xmm12),
|
||||
"66450F6BE3",
|
||||
"packssdw %xmm11, %xmm12",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::xmm_rm_r(SseOpcode::Packsswb, RegMem::reg(xmm11), w_xmm2),
|
||||
"66410F63D3",
|
||||
"packsswb %xmm11, %xmm2",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::xmm_rm_r(SseOpcode::Packusdw, RegMem::reg(xmm13), w_xmm6),
|
||||
"66410F382BF5",
|
||||
"packusdw %xmm13, %xmm6",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::xmm_rm_r(SseOpcode::Packuswb, RegMem::reg(xmm9), w_xmm4),
|
||||
"66410F67E1",
|
||||
"packuswb %xmm9, %xmm4",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::xmm_rm_r(SseOpcode::Punpckhbw, RegMem::reg(xmm3), w_xmm2),
|
||||
"660F68D3",
|
||||
@@ -3183,6 +3201,81 @@ fn test_x64_emit() {
|
||||
"cvttps2dq %xmm9, %xmm8",
|
||||
));
|
||||
|
||||
// ========================================================
|
||||
// XMM_RM_R: Packed Move
|
||||
|
||||
insns.push((
|
||||
Inst::xmm_rm_r(SseOpcode::Pmovsxbd, RegMem::reg(xmm6), w_xmm8),
|
||||
"66440F3821C6",
|
||||
"pmovsxbd %xmm6, %xmm8",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::xmm_rm_r(SseOpcode::Pmovsxbw, RegMem::reg(xmm9), w_xmm10),
|
||||
"66450F3820D1",
|
||||
"pmovsxbw %xmm9, %xmm10",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::xmm_rm_r(SseOpcode::Pmovsxbq, RegMem::reg(xmm1), w_xmm1),
|
||||
"660F3822C9",
|
||||
"pmovsxbq %xmm1, %xmm1",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::xmm_rm_r(SseOpcode::Pmovsxwd, RegMem::reg(xmm13), w_xmm10),
|
||||
"66450F3823D5",
|
||||
"pmovsxwd %xmm13, %xmm10",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::xmm_rm_r(SseOpcode::Pmovsxwq, RegMem::reg(xmm12), w_xmm12),
|
||||
"66450F3824E4",
|
||||
"pmovsxwq %xmm12, %xmm12",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::xmm_rm_r(SseOpcode::Pmovsxdq, RegMem::reg(xmm10), w_xmm8),
|
||||
"66450F3825C2",
|
||||
"pmovsxdq %xmm10, %xmm8",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::xmm_rm_r(SseOpcode::Pmovzxbd, RegMem::reg(xmm5), w_xmm6),
|
||||
"660F3831F5",
|
||||
"pmovzxbd %xmm5, %xmm6",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::xmm_rm_r(SseOpcode::Pmovzxbw, RegMem::reg(xmm5), w_xmm13),
|
||||
"66440F3830ED",
|
||||
"pmovzxbw %xmm5, %xmm13",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::xmm_rm_r(SseOpcode::Pmovzxbq, RegMem::reg(xmm10), w_xmm11),
|
||||
"66450F3832DA",
|
||||
"pmovzxbq %xmm10, %xmm11",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::xmm_rm_r(SseOpcode::Pmovzxwd, RegMem::reg(xmm2), w_xmm10),
|
||||
"66440F3833D2",
|
||||
"pmovzxwd %xmm2, %xmm10",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::xmm_rm_r(SseOpcode::Pmovzxwq, RegMem::reg(xmm7), w_xmm4),
|
||||
"660F3834E7",
|
||||
"pmovzxwq %xmm7, %xmm4",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::xmm_rm_r(SseOpcode::Pmovzxdq, RegMem::reg(xmm3), w_xmm4),
|
||||
"660F3835E3",
|
||||
"pmovzxdq %xmm3, %xmm4",
|
||||
));
|
||||
|
||||
// XMM_Mov_R_M: float stores
|
||||
insns.push((
|
||||
Inst::xmm_mov_r_m(SseOpcode::Movss, xmm15, Amode::imm_reg(128, r12)),
|
||||
@@ -3406,6 +3499,32 @@ fn test_x64_emit() {
|
||||
"410FC2FF00",
|
||||
"cmpps $0, %xmm15, %xmm7",
|
||||
));
|
||||
insns.push((
|
||||
Inst::xmm_rm_r_imm(SseOpcode::Palignr, RegMem::reg(xmm1), w_xmm9, 3, false),
|
||||
"66440F3A0FC903",
|
||||
"palignr $3, %xmm1, %xmm9",
|
||||
));
|
||||
|
||||
insns.push((
|
||||
Inst::xmm_rm_r_imm(SseOpcode::Roundps, RegMem::reg(xmm7), w_xmm8, 3, false),
|
||||
"66440F3A08C703",
|
||||
"roundps $3, %xmm7, %xmm8",
|
||||
));
|
||||
insns.push((
|
||||
Inst::xmm_rm_r_imm(SseOpcode::Roundpd, RegMem::reg(xmm10), w_xmm7, 2, false),
|
||||
"66410F3A09FA02",
|
||||
"roundpd $2, %xmm10, %xmm7",
|
||||
));
|
||||
insns.push((
|
||||
Inst::xmm_rm_r_imm(SseOpcode::Roundps, RegMem::reg(xmm4), w_xmm8, 1, false),
|
||||
"66440F3A08C401",
|
||||
"roundps $1, %xmm4, %xmm8",
|
||||
));
|
||||
insns.push((
|
||||
Inst::xmm_rm_r_imm(SseOpcode::Roundpd, RegMem::reg(xmm15), w_xmm15, 0, false),
|
||||
"66450F3A09FF00",
|
||||
"roundpd $0, %xmm15, %xmm15",
|
||||
));
|
||||
|
||||
// ========================================================
|
||||
// Pertaining to atomics.
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
//! Registers, the Universe thereof, and printing.
|
||||
//!
|
||||
//! These are ordered by sequence number, as required in the Universe. The strange ordering is
|
||||
//! intended to make callee-save registers available before caller-saved ones. This is a net win
|
||||
//! provided that each function makes at least one onward call. It'll be a net loss for leaf
|
||||
//! functions, and we should change the ordering in that case, so as to make caller-save regs
|
||||
//! available first.
|
||||
//! These are ordered by sequence number, as required in the Universe.
|
||||
//!
|
||||
//! TODO Maybe have two different universes, one for leaf functions and one for non-leaf functions?
|
||||
//! Also, they will have to be ABI dependent. Need to find a way to avoid constructing a universe
|
||||
//! for each function we compile.
|
||||
//! The caller-saved registers are placed first in order to prefer not to clobber (requiring
|
||||
//! saves/restores in prologue/epilogue code) when possible. Note that there is no other heuristic
|
||||
//! in the backend that will apply such pressure; the register allocator's cost heuristics are not
|
||||
//! aware of the cost of clobber-save/restore code.
|
||||
//!
|
||||
//! One might worry that this pessimizes code with many callsites, where using caller-saves causes
|
||||
//! us to have to save them (as we are the caller) frequently. However, the register allocator
|
||||
//! *should be* aware of *this* cost, because it sees that the call instruction modifies all of the
|
||||
//! caller-saved (i.e., callee-clobbered) registers.
|
||||
//!
|
||||
//! Hence, this ordering encodes pressure in one direction (prefer not to clobber registers that we
|
||||
//! ourselves have to save) and this is balanaced against the RA's pressure in the other direction
|
||||
//! at callsites.
|
||||
|
||||
use crate::settings;
|
||||
use alloc::vec::Vec;
|
||||
@@ -31,44 +37,44 @@ fn gpr(enc: u8, index: u8) -> Reg {
|
||||
Reg::new_real(RegClass::I64, enc, index)
|
||||
}
|
||||
|
||||
pub(crate) fn r12() -> Reg {
|
||||
gpr(ENC_R12, 16)
|
||||
}
|
||||
pub(crate) fn r13() -> Reg {
|
||||
gpr(ENC_R13, 17)
|
||||
}
|
||||
pub(crate) fn r14() -> Reg {
|
||||
gpr(ENC_R14, 18)
|
||||
}
|
||||
pub(crate) fn rbx() -> Reg {
|
||||
gpr(ENC_RBX, 19)
|
||||
}
|
||||
pub(crate) fn rsi() -> Reg {
|
||||
gpr(6, 20)
|
||||
gpr(6, 16)
|
||||
}
|
||||
pub(crate) fn rdi() -> Reg {
|
||||
gpr(7, 21)
|
||||
gpr(7, 17)
|
||||
}
|
||||
pub(crate) fn rax() -> Reg {
|
||||
gpr(0, 22)
|
||||
gpr(0, 18)
|
||||
}
|
||||
pub(crate) fn rcx() -> Reg {
|
||||
gpr(1, 23)
|
||||
gpr(1, 19)
|
||||
}
|
||||
pub(crate) fn rdx() -> Reg {
|
||||
gpr(2, 24)
|
||||
gpr(2, 20)
|
||||
}
|
||||
pub(crate) fn r8() -> Reg {
|
||||
gpr(8, 25)
|
||||
gpr(8, 21)
|
||||
}
|
||||
pub(crate) fn r9() -> Reg {
|
||||
gpr(9, 26)
|
||||
gpr(9, 22)
|
||||
}
|
||||
pub(crate) fn r10() -> Reg {
|
||||
gpr(10, 27)
|
||||
gpr(10, 23)
|
||||
}
|
||||
pub(crate) fn r11() -> Reg {
|
||||
gpr(11, 28)
|
||||
gpr(11, 24)
|
||||
}
|
||||
pub(crate) fn r12() -> Reg {
|
||||
gpr(ENC_R12, 25)
|
||||
}
|
||||
pub(crate) fn r13() -> Reg {
|
||||
gpr(ENC_R13, 26)
|
||||
}
|
||||
pub(crate) fn r14() -> Reg {
|
||||
gpr(ENC_R14, 27)
|
||||
}
|
||||
pub(crate) fn rbx() -> Reg {
|
||||
gpr(ENC_RBX, 28)
|
||||
}
|
||||
|
||||
pub(crate) fn r15() -> Reg {
|
||||
@@ -176,13 +182,6 @@ pub(crate) fn create_reg_universe_systemv(flags: &settings::Flags) -> RealRegUni
|
||||
// Integer regs.
|
||||
let first_gpr = regs.len();
|
||||
|
||||
// Callee-saved, in the SystemV x86_64 ABI.
|
||||
regs.push((r12().to_real_reg(), "%r12".into()));
|
||||
regs.push((r13().to_real_reg(), "%r13".into()));
|
||||
regs.push((r14().to_real_reg(), "%r14".into()));
|
||||
|
||||
regs.push((rbx().to_real_reg(), "%rbx".into()));
|
||||
|
||||
// Caller-saved, in the SystemV x86_64 ABI.
|
||||
regs.push((rsi().to_real_reg(), "%rsi".into()));
|
||||
regs.push((rdi().to_real_reg(), "%rdi".into()));
|
||||
@@ -194,6 +193,13 @@ pub(crate) fn create_reg_universe_systemv(flags: &settings::Flags) -> RealRegUni
|
||||
regs.push((r10().to_real_reg(), "%r10".into()));
|
||||
regs.push((r11().to_real_reg(), "%r11".into()));
|
||||
|
||||
// Callee-saved, in the SystemV x86_64 ABI.
|
||||
regs.push((r12().to_real_reg(), "%r12".into()));
|
||||
regs.push((r13().to_real_reg(), "%r13".into()));
|
||||
regs.push((r14().to_real_reg(), "%r14".into()));
|
||||
|
||||
regs.push((rbx().to_real_reg(), "%rbx".into()));
|
||||
|
||||
// Other regs, not available to the allocator.
|
||||
debug_assert_eq!(r15(), pinned_reg());
|
||||
let allocable = if use_pinned_reg {
|
||||
|
||||
@@ -1530,7 +1530,10 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
let src = if let Some(ext_spec) = ext_spec {
|
||||
RegMem::reg(extend_input_to_reg(ctx, inputs[0], ext_spec))
|
||||
} else {
|
||||
input_to_reg_mem(ctx, inputs[0])
|
||||
// N.B.: explicitly put input in a reg here because the width of the instruction
|
||||
// into which this RM op goes may not match the width of the input type (in fact,
|
||||
// it won't for i32.popcnt), and we don't want a larger than necessary load.
|
||||
RegMem::reg(put_input_in_reg(ctx, inputs[0]))
|
||||
};
|
||||
let dst = get_output_reg(ctx, outputs[0]);
|
||||
|
||||
@@ -2722,6 +2725,7 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
} else {
|
||||
if op == Opcode::FcvtToSintSat {
|
||||
// Sets destination to zero if float is NaN
|
||||
assert_eq!(types::F32X4, ctx.input_ty(insn, 0));
|
||||
let tmp = ctx.alloc_tmp(RegClass::V128, types::I32X4);
|
||||
ctx.emit(Inst::xmm_unary_rm_r(
|
||||
SseOpcode::Movapd,
|
||||
@@ -2776,7 +2780,118 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
dst,
|
||||
));
|
||||
} else if op == Opcode::FcvtToUintSat {
|
||||
unimplemented!("f32x4.convert_i32x4_u");
|
||||
// The algorithm for converting floats to unsigned ints is a little tricky. The
|
||||
// complication arises because we are converting from a signed 64-bit int with a positive
|
||||
// integer range from 1..INT_MAX (0x1..0x7FFFFFFF) to an unsigned integer with an extended
|
||||
// range from (INT_MAX+1)..UINT_MAX. It's this range from (INT_MAX+1)..UINT_MAX
|
||||
// (0x80000000..0xFFFFFFFF) that needs to be accounted for as a special case since our
|
||||
// conversion instruction (cvttps2dq) only converts as high as INT_MAX (0x7FFFFFFF), but
|
||||
// which conveniently setting underflows and overflows (smaller than MIN_INT or larger than
|
||||
// MAX_INT) to be INT_MAX+1 (0x80000000). Nothing that the range (INT_MAX+1)..UINT_MAX includes
|
||||
// precisely INT_MAX values we can correctly account for and convert every value in this range
|
||||
// if we simply subtract INT_MAX+1 before doing the cvttps2dq conversion. After the subtraction
|
||||
// every value originally (INT_MAX+1)..UINT_MAX is now the range (0..INT_MAX).
|
||||
// After the conversion we add INT_MAX+1 back to this converted value, noting again that
|
||||
// values we are trying to account for were already set to INT_MAX+1 during the original conversion.
|
||||
// We simply have to create a mask and make sure we are adding together only the lanes that need
|
||||
// to be accounted for. Digesting it all the steps then are:
|
||||
//
|
||||
// Step 1 - Account for NaN and negative floats by setting these src values to zero.
|
||||
// Step 2 - Make a copy (tmp1) of the src value since we need to convert twice for
|
||||
// reasons described above.
|
||||
// Step 3 - Convert the original src values. This will convert properly all floats up to INT_MAX
|
||||
// Step 4 - Subtract INT_MAX from the copy set (tmp1). Note, all zero and negative values are those
|
||||
// values that were originally in the range (0..INT_MAX). This will come in handy during
|
||||
// step 7 when we zero negative lanes.
|
||||
// Step 5 - Create a bit mask for tmp1 that will correspond to all lanes originally less than
|
||||
// UINT_MAX that are now less than INT_MAX thanks to the subtraction.
|
||||
// Step 6 - Convert the second set of values (tmp1)
|
||||
// Step 7 - Prep the converted second set by zeroing out negative lanes (these have already been
|
||||
// converted correctly with the first set) and by setting overflow lanes to 0x7FFFFFFF
|
||||
// as this will allow us to properly saturate overflow lanes when adding to 0x80000000
|
||||
// Step 8 - Add the orginal converted src and the converted tmp1 where float values originally less
|
||||
// than and equal to INT_MAX will be unchanged, float values originally between INT_MAX+1 and
|
||||
// UINT_MAX will add together (INT_MAX) + (SRC - INT_MAX), and float values originally
|
||||
// greater than UINT_MAX will be saturated to UINT_MAX (0xFFFFFFFF) after adding (0x8000000 + 0x7FFFFFFF).
|
||||
//
|
||||
//
|
||||
// The table below illustrates the result after each step where it matters for the converted set.
|
||||
// Note the original value range (original src set) is the final dst in Step 8:
|
||||
//
|
||||
// Original src set:
|
||||
// | Original Value Range | Step 1 | Step 3 | Step 8 |
|
||||
// | -FLT_MIN..FLT_MAX | 0.0..FLT_MAX | 0..INT_MAX(w/overflow) | 0..UINT_MAX(w/saturation) |
|
||||
//
|
||||
// Copied src set (tmp1):
|
||||
// | Step 2 | Step 4 |
|
||||
// | 0.0..FLT_MAX | (0.0-(INT_MAX+1))..(FLT_MAX-(INT_MAX+1)) |
|
||||
//
|
||||
// | Step 6 | Step 7 |
|
||||
// | (0-(INT_MAX+1))..(UINT_MAX-(INT_MAX+1))(w/overflow) | ((INT_MAX+1)-(INT_MAX+1))..(INT_MAX+1) |
|
||||
|
||||
// Create temporaries
|
||||
assert_eq!(types::F32X4, ctx.input_ty(insn, 0));
|
||||
let tmp1 = ctx.alloc_tmp(RegClass::V128, types::I32X4);
|
||||
let tmp2 = ctx.alloc_tmp(RegClass::V128, types::I32X4);
|
||||
|
||||
// Converting to unsigned int so if float src is negative or NaN
|
||||
// will first set to zero.
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Pxor, RegMem::from(tmp2), tmp2));
|
||||
ctx.emit(Inst::gen_move(dst, src, input_ty));
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Maxps, RegMem::from(tmp2), dst));
|
||||
|
||||
// Set tmp2 to INT_MAX+1. It is important to note here that after it looks
|
||||
// like we are only converting INT_MAX (0x7FFFFFFF) but in fact because
|
||||
// single precision IEEE-754 floats can only accurately represent contingous
|
||||
// integers up to 2^23 and outside of this range it rounds to the closest
|
||||
// integer that it can represent. In the case of INT_MAX, this value gets
|
||||
// represented as 0x4f000000 which is the integer value (INT_MAX+1).
|
||||
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Pcmpeqd, RegMem::from(tmp2), tmp2));
|
||||
ctx.emit(Inst::xmm_rmi_reg(SseOpcode::Psrld, RegMemImm::imm(1), tmp2));
|
||||
ctx.emit(Inst::xmm_rm_r(
|
||||
SseOpcode::Cvtdq2ps,
|
||||
RegMem::from(tmp2),
|
||||
tmp2,
|
||||
));
|
||||
|
||||
// Make a copy of these lanes and then do the first conversion.
|
||||
// Overflow lanes greater than the maximum allowed signed value will
|
||||
// set to 0x80000000. Negative and NaN lanes will be 0x0
|
||||
ctx.emit(Inst::xmm_mov(SseOpcode::Movaps, RegMem::from(dst), tmp1));
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Cvttps2dq, RegMem::from(dst), dst));
|
||||
|
||||
// Set lanes to src - max_signed_int
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Subps, RegMem::from(tmp2), tmp1));
|
||||
|
||||
// Create mask for all positive lanes to saturate (i.e. greater than
|
||||
// or equal to the maxmimum allowable unsigned int).
|
||||
let cond = FcmpImm::from(FloatCC::LessThanOrEqual);
|
||||
ctx.emit(Inst::xmm_rm_r_imm(
|
||||
SseOpcode::Cmpps,
|
||||
RegMem::from(tmp1),
|
||||
tmp2,
|
||||
cond.encode(),
|
||||
false,
|
||||
));
|
||||
|
||||
// Convert those set of lanes that have the max_signed_int factored out.
|
||||
ctx.emit(Inst::xmm_rm_r(
|
||||
SseOpcode::Cvttps2dq,
|
||||
RegMem::from(tmp1),
|
||||
tmp1,
|
||||
));
|
||||
|
||||
// Prepare converted lanes by zeroing negative lanes and prepping lanes
|
||||
// that have positive overflow (based on the mask) by setting these lanes
|
||||
// to 0x7FFFFFFF
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Pxor, RegMem::from(tmp2), tmp1));
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Pxor, RegMem::from(tmp2), tmp2));
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Pmaxsd, RegMem::from(tmp2), tmp1));
|
||||
|
||||
// Add this second set of converted lanes to the original to properly handle
|
||||
// values greater than max signed int.
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Paddd, RegMem::from(tmp1), dst));
|
||||
} else {
|
||||
// Since this branch is also guarded by a check for vector types
|
||||
// neither Opcode::FcvtToUint nor Opcode::FcvtToSint can reach here
|
||||
@@ -2786,7 +2901,127 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Opcode::UwidenHigh | Opcode::UwidenLow | Opcode::SwidenHigh | Opcode::SwidenLow => {
|
||||
let input_ty = ctx.input_ty(insn, 0);
|
||||
let output_ty = ctx.output_ty(insn, 0);
|
||||
let src = put_input_in_reg(ctx, inputs[0]);
|
||||
let dst = get_output_reg(ctx, outputs[0]);
|
||||
if output_ty.is_vector() {
|
||||
match op {
|
||||
Opcode::SwidenLow => match (input_ty, output_ty) {
|
||||
(types::I8X16, types::I16X8) => {
|
||||
ctx.emit(Inst::gen_move(dst, src, output_ty));
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Pmovsxbw, RegMem::from(dst), dst));
|
||||
}
|
||||
(types::I16X8, types::I32X4) => {
|
||||
ctx.emit(Inst::gen_move(dst, src, output_ty));
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Pmovsxwd, RegMem::from(dst), dst));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Opcode::SwidenHigh => match (input_ty, output_ty) {
|
||||
(types::I8X16, types::I16X8) => {
|
||||
ctx.emit(Inst::gen_move(dst, src, output_ty));
|
||||
ctx.emit(Inst::xmm_rm_r_imm(
|
||||
SseOpcode::Palignr,
|
||||
RegMem::reg(src),
|
||||
dst,
|
||||
8,
|
||||
false,
|
||||
));
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Pmovsxbw, RegMem::from(dst), dst));
|
||||
}
|
||||
(types::I16X8, types::I32X4) => {
|
||||
ctx.emit(Inst::gen_move(dst, src, output_ty));
|
||||
ctx.emit(Inst::xmm_rm_r_imm(
|
||||
SseOpcode::Palignr,
|
||||
RegMem::reg(src),
|
||||
dst,
|
||||
8,
|
||||
false,
|
||||
));
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Pmovsxwd, RegMem::from(dst), dst));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Opcode::UwidenLow => match (input_ty, output_ty) {
|
||||
(types::I8X16, types::I16X8) => {
|
||||
ctx.emit(Inst::gen_move(dst, src, output_ty));
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Pmovzxbw, RegMem::from(dst), dst));
|
||||
}
|
||||
(types::I16X8, types::I32X4) => {
|
||||
ctx.emit(Inst::gen_move(dst, src, output_ty));
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Pmovzxwd, RegMem::from(dst), dst));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Opcode::UwidenHigh => match (input_ty, output_ty) {
|
||||
(types::I8X16, types::I16X8) => {
|
||||
ctx.emit(Inst::gen_move(dst, src, output_ty));
|
||||
ctx.emit(Inst::xmm_rm_r_imm(
|
||||
SseOpcode::Palignr,
|
||||
RegMem::reg(src),
|
||||
dst,
|
||||
8,
|
||||
false,
|
||||
));
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Pmovzxbw, RegMem::from(dst), dst));
|
||||
}
|
||||
(types::I16X8, types::I32X4) => {
|
||||
ctx.emit(Inst::gen_move(dst, src, output_ty));
|
||||
ctx.emit(Inst::xmm_rm_r_imm(
|
||||
SseOpcode::Palignr,
|
||||
RegMem::reg(src),
|
||||
dst,
|
||||
8,
|
||||
false,
|
||||
));
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Pmovzxwd, RegMem::from(dst), dst));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
panic!("Unsupported non-vector type for widen instruction {:?}", ty);
|
||||
}
|
||||
}
|
||||
Opcode::Snarrow | Opcode::Unarrow => {
|
||||
let input_ty = ctx.input_ty(insn, 0);
|
||||
let output_ty = ctx.output_ty(insn, 0);
|
||||
let src1 = put_input_in_reg(ctx, inputs[0]);
|
||||
let src2 = put_input_in_reg(ctx, inputs[1]);
|
||||
let dst = get_output_reg(ctx, outputs[0]);
|
||||
if output_ty.is_vector() {
|
||||
match op {
|
||||
Opcode::Snarrow => match (input_ty, output_ty) {
|
||||
(types::I16X8, types::I8X16) => {
|
||||
ctx.emit(Inst::gen_move(dst, src1, input_ty));
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Packsswb, RegMem::reg(src2), dst));
|
||||
}
|
||||
(types::I32X4, types::I16X8) => {
|
||||
ctx.emit(Inst::gen_move(dst, src1, input_ty));
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Packssdw, RegMem::reg(src2), dst));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Opcode::Unarrow => match (input_ty, output_ty) {
|
||||
(types::I16X8, types::I8X16) => {
|
||||
ctx.emit(Inst::gen_move(dst, src1, input_ty));
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Packuswb, RegMem::reg(src2), dst));
|
||||
}
|
||||
(types::I32X4, types::I16X8) => {
|
||||
ctx.emit(Inst::gen_move(dst, src1, input_ty));
|
||||
ctx.emit(Inst::xmm_rm_r(SseOpcode::Packusdw, RegMem::reg(src2), dst));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
panic!("Unsupported non-vector type for widen instruction {:?}", ty);
|
||||
}
|
||||
}
|
||||
Opcode::Bitcast => {
|
||||
let input_ty = ctx.input_ty(insn, 0);
|
||||
let output_ty = ctx.output_ty(insn, 0);
|
||||
@@ -2975,22 +3210,45 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
|
||||
// Lower to VM calls when there's no access to SSE4.1.
|
||||
let ty = ty.unwrap();
|
||||
let libcall = match (ty, op) {
|
||||
(types::F32, Opcode::Ceil) => LibCall::CeilF32,
|
||||
(types::F64, Opcode::Ceil) => LibCall::CeilF64,
|
||||
(types::F32, Opcode::Floor) => LibCall::FloorF32,
|
||||
(types::F64, Opcode::Floor) => LibCall::FloorF64,
|
||||
(types::F32, Opcode::Nearest) => LibCall::NearestF32,
|
||||
(types::F64, Opcode::Nearest) => LibCall::NearestF64,
|
||||
(types::F32, Opcode::Trunc) => LibCall::TruncF32,
|
||||
(types::F64, Opcode::Trunc) => LibCall::TruncF64,
|
||||
_ => panic!(
|
||||
"unexpected type/opcode {:?}/{:?} in Ceil/Floor/Nearest/Trunc",
|
||||
ty, op
|
||||
),
|
||||
};
|
||||
|
||||
emit_vm_call(ctx, flags, triple, libcall, insn, inputs, outputs)?;
|
||||
if !ty.is_vector() {
|
||||
let libcall = match (op, ty) {
|
||||
(Opcode::Ceil, types::F32) => LibCall::CeilF32,
|
||||
(Opcode::Ceil, types::F64) => LibCall::CeilF64,
|
||||
(Opcode::Floor, types::F32) => LibCall::FloorF32,
|
||||
(Opcode::Floor, types::F64) => LibCall::FloorF64,
|
||||
(Opcode::Nearest, types::F32) => LibCall::NearestF32,
|
||||
(Opcode::Nearest, types::F64) => LibCall::NearestF64,
|
||||
(Opcode::Trunc, types::F32) => LibCall::TruncF32,
|
||||
(Opcode::Trunc, types::F64) => LibCall::TruncF64,
|
||||
_ => panic!(
|
||||
"unexpected type/opcode {:?}/{:?} in Ceil/Floor/Nearest/Trunc",
|
||||
ty, op
|
||||
),
|
||||
};
|
||||
emit_vm_call(ctx, flags, triple, libcall, insn, inputs, outputs)?;
|
||||
} else {
|
||||
let (op, mode) = match (op, ty) {
|
||||
(Opcode::Ceil, types::F32X4) => (SseOpcode::Roundps, RoundImm::RoundUp),
|
||||
(Opcode::Ceil, types::F64X2) => (SseOpcode::Roundpd, RoundImm::RoundUp),
|
||||
(Opcode::Floor, types::F32X4) => (SseOpcode::Roundps, RoundImm::RoundDown),
|
||||
(Opcode::Floor, types::F64X2) => (SseOpcode::Roundpd, RoundImm::RoundDown),
|
||||
(Opcode::Trunc, types::F32X4) => (SseOpcode::Roundps, RoundImm::RoundZero),
|
||||
(Opcode::Trunc, types::F64X2) => (SseOpcode::Roundpd, RoundImm::RoundZero),
|
||||
(Opcode::Nearest, types::F32X4) => (SseOpcode::Roundps, RoundImm::RoundNearest),
|
||||
(Opcode::Nearest, types::F64X2) => (SseOpcode::Roundpd, RoundImm::RoundNearest),
|
||||
_ => panic!("Unknown op/ty combination (vector){:?}", ty),
|
||||
};
|
||||
let src = put_input_in_reg(ctx, inputs[0]);
|
||||
let dst = get_output_reg(ctx, outputs[0]);
|
||||
ctx.emit(Inst::gen_move(dst, src, ty));
|
||||
ctx.emit(Inst::xmm_rm_r_imm(
|
||||
op,
|
||||
RegMem::from(dst),
|
||||
dst,
|
||||
mode.encode(),
|
||||
false,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Opcode::Load
|
||||
@@ -4079,8 +4337,8 @@ impl LowerBackend for X64Backend {
|
||||
let ty = ctx.input_ty(ifcmp_sp, 0);
|
||||
ctx.emit(Inst::cmp_rmi_r(
|
||||
ty.bytes() as u8,
|
||||
RegMemImm::reg(operand),
|
||||
regs::rsp(),
|
||||
RegMemImm::reg(regs::rsp()),
|
||||
operand,
|
||||
));
|
||||
let cond_code = ctx.data(branches[0]).cond_code().unwrap();
|
||||
let cc = CC::from_intcc(cond_code);
|
||||
|
||||
@@ -92,15 +92,15 @@ impl MachBackend for X64Backend {
|
||||
}
|
||||
|
||||
fn unsigned_add_overflow_condition(&self) -> IntCC {
|
||||
// Unsigned `>=`; this corresponds to the carry flag set on x86, which happens on
|
||||
// overflow of an add.
|
||||
IntCC::UnsignedGreaterThanOrEqual
|
||||
// Unsigned `<`; this corresponds to the carry flag set on x86, which
|
||||
// indicates an add has overflowed.
|
||||
IntCC::UnsignedLessThan
|
||||
}
|
||||
|
||||
fn unsigned_sub_overflow_condition(&self) -> IntCC {
|
||||
// unsigned `>=`; this corresponds to the carry flag set on x86, which happens on
|
||||
// underflow of a subtract (carry is borrow for subtract).
|
||||
IntCC::UnsignedGreaterThanOrEqual
|
||||
// unsigned `<`; this corresponds to the carry flag set on x86, which
|
||||
// indicates a sub has underflowed (carry is borrow for subtract).
|
||||
IntCC::UnsignedLessThan
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
|
||||
@@ -659,7 +659,7 @@ fn narrow_load(
|
||||
inst: ir::Inst,
|
||||
func: &mut ir::Function,
|
||||
_cfg: &mut ControlFlowGraph,
|
||||
_isa: &dyn TargetIsa,
|
||||
isa: &dyn TargetIsa,
|
||||
) {
|
||||
let mut pos = FuncCursor::new(func).at_inst(inst);
|
||||
pos.use_srcloc(inst);
|
||||
@@ -684,6 +684,10 @@ fn narrow_load(
|
||||
ptr,
|
||||
offset.try_add_i64(8).expect("load offset overflow"),
|
||||
);
|
||||
let (al, ah) = match flags.endianness(isa.endianness()) {
|
||||
ir::Endianness::Little => (al, ah),
|
||||
ir::Endianness::Big => (ah, al),
|
||||
};
|
||||
pos.func.dfg.replace(inst).iconcat(al, ah);
|
||||
}
|
||||
|
||||
@@ -692,7 +696,7 @@ fn narrow_store(
|
||||
inst: ir::Inst,
|
||||
func: &mut ir::Function,
|
||||
_cfg: &mut ControlFlowGraph,
|
||||
_isa: &dyn TargetIsa,
|
||||
isa: &dyn TargetIsa,
|
||||
) {
|
||||
let mut pos = FuncCursor::new(func).at_inst(inst);
|
||||
pos.use_srcloc(inst);
|
||||
@@ -708,6 +712,10 @@ fn narrow_store(
|
||||
};
|
||||
|
||||
let (al, ah) = pos.ins().isplit(val);
|
||||
let (al, ah) = match flags.endianness(isa.endianness()) {
|
||||
ir::Endianness::Little => (al, ah),
|
||||
ir::Endianness::Big => (ah, al),
|
||||
};
|
||||
pos.ins().store(flags, al, ptr, offset);
|
||||
pos.ins().store(
|
||||
flags,
|
||||
|
||||
@@ -61,8 +61,8 @@ pub fn do_licm(
|
||||
domtree.compute(func, cfg);
|
||||
}
|
||||
|
||||
// Insert a pre-header before the header, modifying the function layout and CFG to reflect it.
|
||||
// A jump instruction to the header is placed at the end of the pre-header.
|
||||
/// Insert a pre-header before the header, modifying the function layout and CFG to reflect it.
|
||||
/// A jump instruction to the header is placed at the end of the pre-header.
|
||||
fn create_pre_header(
|
||||
isa: &dyn TargetIsa,
|
||||
header: Block,
|
||||
@@ -81,30 +81,31 @@ fn create_pre_header(
|
||||
for typ in header_args_types {
|
||||
pre_header_args_value.push(func.dfg.append_block_param(pre_header, typ), pool);
|
||||
}
|
||||
|
||||
for BlockPredecessor {
|
||||
inst: last_inst, ..
|
||||
} in cfg.pred_iter(header)
|
||||
{
|
||||
// We only follow normal edges (not the back edges)
|
||||
if !domtree.dominates(header, last_inst, &func.layout) {
|
||||
func.change_branch_destination(last_inst, pre_header);
|
||||
func.rewrite_branch_destination(last_inst, header, pre_header);
|
||||
}
|
||||
}
|
||||
{
|
||||
let mut pos = EncCursor::new(func, isa).at_top(header);
|
||||
// Inserts the pre-header at the right place in the layout.
|
||||
pos.insert_block(pre_header);
|
||||
pos.next_inst();
|
||||
pos.ins().jump(header, pre_header_args_value.as_slice(pool));
|
||||
}
|
||||
|
||||
// Inserts the pre-header at the right place in the layout.
|
||||
let mut pos = EncCursor::new(func, isa).at_top(header);
|
||||
pos.insert_block(pre_header);
|
||||
pos.next_inst();
|
||||
pos.ins().jump(header, pre_header_args_value.as_slice(pool));
|
||||
|
||||
pre_header
|
||||
}
|
||||
|
||||
// Detects if a loop header has a natural pre-header.
|
||||
//
|
||||
// A loop header has a pre-header if there is only one predecessor that the header doesn't
|
||||
// dominate.
|
||||
// Returns the pre-header Block and the instruction jumping to the header.
|
||||
/// Detects if a loop header has a natural pre-header.
|
||||
///
|
||||
/// A loop header has a pre-header if there is only one predecessor that the header doesn't
|
||||
/// dominate.
|
||||
/// Returns the pre-header Block and the instruction jumping to the header.
|
||||
fn has_pre_header(
|
||||
layout: &Layout,
|
||||
cfg: &ControlFlowGraph,
|
||||
@@ -176,9 +177,9 @@ fn is_loop_invariant(inst: Inst, dfg: &DataFlowGraph, loop_values: &FxHashSet<Va
|
||||
true
|
||||
}
|
||||
|
||||
// Traverses a loop in reverse post-order from a header block and identify loop-invariant
|
||||
// instructions. These loop-invariant instructions are then removed from the code and returned
|
||||
// (in reverse post-order) for later use.
|
||||
/// Traverses a loop in reverse post-order from a header block and identify loop-invariant
|
||||
/// instructions. These loop-invariant instructions are then removed from the code and returned
|
||||
/// (in reverse post-order) for later use.
|
||||
fn remove_loop_invariant_instructions(
|
||||
lp: Loop,
|
||||
func: &mut Function,
|
||||
|
||||
@@ -314,6 +314,9 @@ pub trait ABIMachineSpec {
|
||||
/// Generate the usual frame-restore sequence for this architecture.
|
||||
fn gen_epilogue_frame_restore() -> SmallVec<[Self::I; 2]>;
|
||||
|
||||
/// Generate a probestack call.
|
||||
fn gen_probestack(_frame_size: u32) -> SmallVec<[Self::I; 2]>;
|
||||
|
||||
/// Generate a clobber-save sequence. This takes the list of *all* registers
|
||||
/// written/modified by the function body. The implementation here is
|
||||
/// responsible for determining which of these are callee-saved according to
|
||||
@@ -481,6 +484,9 @@ pub struct ABICalleeImpl<M: ABIMachineSpec> {
|
||||
/// manually register-allocated and carefully only use caller-saved
|
||||
/// registers and keep nothing live after this sequence of instructions.
|
||||
stack_limit: Option<(Reg, Vec<M::I>)>,
|
||||
/// Are we to invoke the probestack function in the prologue? If so,
|
||||
/// what is the minimum size at which we must invoke it?
|
||||
probestack_min_frame: Option<u32>,
|
||||
|
||||
_mach: PhantomData<M>,
|
||||
}
|
||||
@@ -536,6 +542,18 @@ impl<M: ABIMachineSpec> ABICalleeImpl<M> {
|
||||
.map(|reg| (reg, Vec::new()))
|
||||
.or_else(|| f.stack_limit.map(|gv| gen_stack_limit::<M>(f, &sig, gv)));
|
||||
|
||||
// Determine whether a probestack call is required for large enough
|
||||
// frames (and the minimum frame size if so).
|
||||
let probestack_min_frame = if flags.enable_probestack() {
|
||||
assert!(
|
||||
!flags.probestack_func_adjusts_sp(),
|
||||
"SP-adjusting probestack not supported in new backends"
|
||||
);
|
||||
Some(1 << flags.probestack_size_log2())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
sig,
|
||||
stackslots,
|
||||
@@ -550,6 +568,7 @@ impl<M: ABIMachineSpec> ABICalleeImpl<M> {
|
||||
flags,
|
||||
is_leaf: f.is_leaf(),
|
||||
stack_limit,
|
||||
probestack_min_frame,
|
||||
_mach: PhantomData,
|
||||
})
|
||||
}
|
||||
@@ -978,6 +997,11 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
||||
insts.extend_from_slice(stack_limit_load);
|
||||
self.insert_stack_check(*reg, total_stacksize, &mut insts);
|
||||
}
|
||||
if let Some(min_frame) = &self.probestack_min_frame {
|
||||
if total_stacksize >= *min_frame {
|
||||
insts.extend(M::gen_probestack(total_stacksize));
|
||||
}
|
||||
}
|
||||
}
|
||||
if total_stacksize > 0 {
|
||||
self.fixed_frame_storage_size += total_stacksize;
|
||||
|
||||
@@ -674,6 +674,12 @@ impl<I: VCodeInst> MachBuffer<I> {
|
||||
// (end of buffer)
|
||||
self.data.truncate(b.start as usize);
|
||||
self.fixup_records.truncate(b.fixup);
|
||||
while let Some(last_srcloc) = self.srclocs.last() {
|
||||
if last_srcloc.end <= b.start {
|
||||
break;
|
||||
}
|
||||
self.srclocs.pop();
|
||||
}
|
||||
// State:
|
||||
// [PRE CODE]
|
||||
// cur_off, Offset b.start, b.labels_at_this_branch:
|
||||
@@ -1184,12 +1190,15 @@ impl<I: VCodeInst> MachBuffer<I> {
|
||||
// incorrect.
|
||||
assert!(self.fixup_records.is_empty());
|
||||
|
||||
let mut srclocs = self.srclocs;
|
||||
srclocs.sort_by_key(|entry| entry.start);
|
||||
|
||||
MachBufferFinalized {
|
||||
data: self.data,
|
||||
relocs: self.relocs,
|
||||
traps: self.traps,
|
||||
call_sites: self.call_sites,
|
||||
srclocs: self.srclocs,
|
||||
srclocs,
|
||||
stack_maps: self.stack_maps,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -913,6 +913,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
||||
// the code-motion.
|
||||
if self.cur_scan_entry_color.is_some()
|
||||
&& self.value_uses[val] == 1
|
||||
&& self.value_lowered_uses[val] == 0
|
||||
&& self.num_outputs(src_inst) == 1
|
||||
&& self
|
||||
.side_effect_inst_entry_colors
|
||||
|
||||
@@ -1312,19 +1312,31 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "x86")]
|
||||
#[cfg(any(feature = "x64", feature = "x86", feature = "arm64"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::isa::lookup;
|
||||
use crate::isa::{lookup, TargetIsa};
|
||||
use crate::settings::{builder, Flags};
|
||||
use std::str::FromStr;
|
||||
use target_lexicon::triple;
|
||||
|
||||
fn isa() -> Box<dyn TargetIsa> {
|
||||
// We need a triple to instantiate and run the peephole optimizer, but we
|
||||
// don't care which one when we're just trying to trigger a rebuild of the
|
||||
// peephole optimizer (it doesn't affect the serialized bytes at all).
|
||||
let triple = if cfg!(any(feature = "x64", feature = "x86")) {
|
||||
triple!("x86_64")
|
||||
} else if cfg!(feature = "arm64") {
|
||||
triple!("aarch64")
|
||||
} else {
|
||||
panic!("unknown arch")
|
||||
};
|
||||
lookup(triple).unwrap().finish(Flags::new(builder()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_peepmatic_preopt() {
|
||||
let isa = lookup(triple!("x86_64"))
|
||||
.expect("expect x86 ISA")
|
||||
.finish(Flags::new(builder()));
|
||||
let isa = isa();
|
||||
let _ = preopt(&*isa);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +142,49 @@ fn harvest_candidate_lhs(
|
||||
let souper_assignment_rhs = match func.dfg.value_def(val) {
|
||||
ir::ValueDef::Result(inst, 0) => {
|
||||
let args = func.dfg.inst_args(inst);
|
||||
let arg = |allocs: &mut Allocs, n| allocs.ir_to_souper_val[&args[n]].into();
|
||||
|
||||
// Get the n^th argument as a souper operand.
|
||||
let arg = |allocs: &mut Allocs, n| {
|
||||
let arg = args[n];
|
||||
if let Some(a) = allocs.ir_to_souper_val.get(&arg).copied() {
|
||||
a.into()
|
||||
} else {
|
||||
// The only arguments we get that we haven't already
|
||||
// converted into a souper instruction are `iconst`s and
|
||||
// `bconst`s. This is because souper only allows
|
||||
// constants as operands, and it doesn't allow assigning
|
||||
// constants to a variable name. So we lazily convert
|
||||
// `iconst`s and `bconst`s into souper operands here,
|
||||
// when they are actually used.
|
||||
match func.dfg.value_def(arg) {
|
||||
ir::ValueDef::Result(inst, 0) => match func.dfg[inst] {
|
||||
ir::InstructionData::UnaryImm { opcode, imm } => {
|
||||
debug_assert_eq!(opcode, ir::Opcode::Iconst);
|
||||
let imm: i64 = imm.into();
|
||||
ast::Operand::Constant(ast::Constant {
|
||||
value: imm.into(),
|
||||
r#type: souper_type_of(&func.dfg, arg),
|
||||
})
|
||||
}
|
||||
ir::InstructionData::UnaryBool { opcode, imm } => {
|
||||
debug_assert_eq!(opcode, ir::Opcode::Iconst);
|
||||
ast::Operand::Constant(ast::Constant {
|
||||
value: imm.into(),
|
||||
r#type: souper_type_of(&func.dfg, arg),
|
||||
})
|
||||
}
|
||||
_ => unreachable!(
|
||||
"only iconst and bconst instructions \
|
||||
aren't in `ir_to_souper_val`"
|
||||
),
|
||||
},
|
||||
_ => unreachable!(
|
||||
"only iconst and bconst instructions \
|
||||
aren't in `ir_to_souper_val`"
|
||||
),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match (func.dfg[inst].opcode(), &func.dfg[inst]) {
|
||||
(ir::Opcode::Iadd, _) => {
|
||||
@@ -350,6 +392,28 @@ fn harvest_candidate_lhs(
|
||||
}
|
||||
(ir::Opcode::Select, _) => {
|
||||
let a = arg(allocs, 0);
|
||||
|
||||
// While Cranelift allows any width condition for
|
||||
// `select`, Souper requires an `i1`.
|
||||
let a = match a {
|
||||
ast::Operand::Value(id) => match lhs.get_value(id).r#type {
|
||||
Some(ast::Type { width: 1 }) => a,
|
||||
_ => lhs
|
||||
.assignment(
|
||||
None,
|
||||
Some(ast::Type { width: 1 }),
|
||||
ast::Instruction::Trunc { a },
|
||||
vec![],
|
||||
)
|
||||
.into(),
|
||||
},
|
||||
ast::Operand::Constant(ast::Constant { value, .. }) => ast::Constant {
|
||||
value: (value != 0) as _,
|
||||
r#type: Some(ast::Type { width: 1 }),
|
||||
}
|
||||
.into(),
|
||||
};
|
||||
|
||||
let b = arg(allocs, 1);
|
||||
let c = arg(allocs, 2);
|
||||
ast::Instruction::Select { a, b, c }.into()
|
||||
@@ -421,23 +485,13 @@ fn harvest_candidate_lhs(
|
||||
let b = arg(allocs, 1);
|
||||
ast::Instruction::UsubSat { a, b }.into()
|
||||
}
|
||||
(ir::Opcode::Iconst, ir::InstructionData::UnaryImm { imm, .. }) => {
|
||||
let value: i64 = (*imm).into();
|
||||
let value: i128 = value.into();
|
||||
ast::Constant {
|
||||
value,
|
||||
r#type: souper_type_of(&func.dfg, val),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
(ir::Opcode::Bconst, ir::InstructionData::UnaryBool { imm, .. }) => {
|
||||
let value = *imm as i128;
|
||||
ast::Constant {
|
||||
value,
|
||||
r#type: souper_type_of(&func.dfg, val),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
// Because Souper doesn't allow constants to be on the right
|
||||
// hand side of an assignment (i.e. `%0:i32 = 1234` is
|
||||
// disallowed) we have to ignore `iconst` and `bconst`
|
||||
// instructions until we process them as operands for some
|
||||
// other instruction. See the `arg` closure above for
|
||||
// details.
|
||||
(ir::Opcode::Iconst, _) | (ir::Opcode::Bconst, _) => return,
|
||||
_ => ast::AssignmentRhs::Var,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,6 @@
|
||||
emits native object files using the
|
||||
`object <https://github.com/gimli-rs/object>`_ library.
|
||||
|
||||
- [cranelift-simplejit](https://docs.rs/cranelift-simplejit)
|
||||
This crate provides a simple JIT backend for `cranelift-module`, which
|
||||
- [cranelift-jit](https://docs.rs/cranelift-jit)
|
||||
This crate provides a JIT backend for `cranelift-module`, which
|
||||
emits code and data into memory.
|
||||
|
||||
@@ -344,3 +344,69 @@ block0(v0: i64, v1: i32):
|
||||
; nextln: mov sp, fp
|
||||
; nextln: ldp fp, lr, [sp], #16
|
||||
; nextln: ret
|
||||
|
||||
function %f18(i64, i64, i64) -> i32 {
|
||||
block0(v0: i64, v1: i64, v2: i64):
|
||||
v3 = iconst.i32 -4098
|
||||
v6 = uextend.i64 v3
|
||||
v5 = sload16.i32 v6+0
|
||||
return v5
|
||||
}
|
||||
|
||||
; check: stp fp, lr, [sp, #-16]!
|
||||
; nextln: mov fp, sp
|
||||
; nextln: movn w0, #4097
|
||||
; nextln: ldrsh x0, [x0]
|
||||
; nextln: mov sp, fp
|
||||
; nextln: ldp fp, lr, [sp], #16
|
||||
; nextln: ret
|
||||
|
||||
function %f19(i64, i64, i64) -> i32 {
|
||||
block0(v0: i64, v1: i64, v2: i64):
|
||||
v3 = iconst.i32 4098
|
||||
v6 = uextend.i64 v3
|
||||
v5 = sload16.i32 v6+0
|
||||
return v5
|
||||
}
|
||||
|
||||
; check: stp fp, lr, [sp, #-16]!
|
||||
; nextln: mov fp, sp
|
||||
; nextln: movz x0, #4098
|
||||
; nextln: ldrsh x0, [x0]
|
||||
; nextln: mov sp, fp
|
||||
; nextln: ldp fp, lr, [sp], #16
|
||||
; nextln: ret
|
||||
|
||||
function %f20(i64, i64, i64) -> i32 {
|
||||
block0(v0: i64, v1: i64, v2: i64):
|
||||
v3 = iconst.i32 -4098
|
||||
v6 = sextend.i64 v3
|
||||
v5 = sload16.i32 v6+0
|
||||
return v5
|
||||
}
|
||||
|
||||
; check: stp fp, lr, [sp, #-16]!
|
||||
; nextln: mov fp, sp
|
||||
; nextln: movn w0, #4097
|
||||
; nextln: sxtw x0, w0
|
||||
; nextln: ldrsh x0, [x0]
|
||||
; nextln: mov sp, fp
|
||||
; nextln: ldp fp, lr, [sp], #16
|
||||
; nextln: ret
|
||||
|
||||
function %f21(i64, i64, i64) -> i32 {
|
||||
block0(v0: i64, v1: i64, v2: i64):
|
||||
v3 = iconst.i32 4098
|
||||
v6 = sextend.i64 v3
|
||||
v5 = sload16.i32 v6+0
|
||||
return v5
|
||||
}
|
||||
|
||||
; check: stp fp, lr, [sp, #-16]!
|
||||
; nextln: mov fp, sp
|
||||
; nextln: movz x0, #4098
|
||||
; nextln: sxtw x0, w0
|
||||
; nextln: ldrsh x0, [x0]
|
||||
; nextln: mov sp, fp
|
||||
; nextln: ldp fp, lr, [sp], #16
|
||||
; nextln: ret
|
||||
|
||||
@@ -715,7 +715,7 @@ block0(v0: f32):
|
||||
; nextln: movz x0, #20352, LSL #16
|
||||
; nextln: fmov d1, x0
|
||||
; nextln: fmin s2, s0, s1
|
||||
; nextln: movi v1.8b, #0
|
||||
; nextln: movi v1.2s, #0
|
||||
; nextln: fmax s2, s2, s1
|
||||
; nextln: fcmp s0, s0
|
||||
; nextln: fcsel s0, s1, s2, ne
|
||||
@@ -738,7 +738,7 @@ block0(v0: f32):
|
||||
; nextln: movz x0, #52992, LSL #16
|
||||
; nextln: fmov d2, x0
|
||||
; nextln: fmax s1, s1, s2
|
||||
; nextln: movi v2.8b, #0
|
||||
; nextln: movi v2.2s, #0
|
||||
; nextln: fcmp s0, s0
|
||||
; nextln: fcsel s0, s2, s1, ne
|
||||
; nextln: fcvtzs w0, s0
|
||||
@@ -757,7 +757,7 @@ block0(v0: f32):
|
||||
; nextln: movz x0, #24448, LSL #16
|
||||
; nextln: fmov d1, x0
|
||||
; nextln: fmin s2, s0, s1
|
||||
; nextln: movi v1.8b, #0
|
||||
; nextln: movi v1.2s, #0
|
||||
; nextln: fmax s2, s2, s1
|
||||
; nextln: fcmp s0, s0
|
||||
; nextln: fcsel s0, s1, s2, ne
|
||||
@@ -780,7 +780,7 @@ block0(v0: f32):
|
||||
; nextln: movz x0, #57088, LSL #16
|
||||
; nextln: fmov d2, x0
|
||||
; nextln: fmax s1, s1, s2
|
||||
; nextln: movi v2.8b, #0
|
||||
; nextln: movi v2.2s, #0
|
||||
; nextln: fcmp s0, s0
|
||||
; nextln: fcsel s0, s2, s1, ne
|
||||
; nextln: fcvtzs x0, s0
|
||||
@@ -798,7 +798,7 @@ block0(v0: f64):
|
||||
; nextln: mov fp, sp
|
||||
; nextln: ldr d1, pc+8 ; b 12 ; data.f64 4294967295
|
||||
; nextln: fmin d2, d0, d1
|
||||
; nextln: movi v1.8b, #0
|
||||
; nextln: movi v1.2s, #0
|
||||
; nextln: fmax d2, d2, d1
|
||||
; nextln: fcmp d0, d0
|
||||
; nextln: fcsel d0, d1, d2, ne
|
||||
@@ -820,7 +820,7 @@ block0(v0: f64):
|
||||
; nextln: movz x0, #49632, LSL #48
|
||||
; nextln: fmov d2, x0
|
||||
; nextln: fmax d1, d1, d2
|
||||
; nextln: movi v2.8b, #0
|
||||
; nextln: movi v2.2s, #0
|
||||
; nextln: fcmp d0, d0
|
||||
; nextln: fcsel d0, d2, d1, ne
|
||||
; nextln: fcvtzs w0, d0
|
||||
@@ -839,7 +839,7 @@ block0(v0: f64):
|
||||
; nextln: movz x0, #17392, LSL #48
|
||||
; nextln: fmov d1, x0
|
||||
; nextln: fmin d2, d0, d1
|
||||
; nextln: movi v1.8b, #0
|
||||
; nextln: movi v1.2s, #0
|
||||
; nextln: fmax d2, d2, d1
|
||||
; nextln: fcmp d0, d0
|
||||
; nextln: fcsel d0, d1, d2, ne
|
||||
@@ -862,7 +862,7 @@ block0(v0: f64):
|
||||
; nextln: movz x0, #50144, LSL #48
|
||||
; nextln: fmov d2, x0
|
||||
; nextln: fmax d1, d1, d2
|
||||
; nextln: movi v2.8b, #0
|
||||
; nextln: movi v2.2s, #0
|
||||
; nextln: fcmp d0, d0
|
||||
; nextln: fcsel d0, d2, d1, ne
|
||||
; nextln: fcvtzs x0, d0
|
||||
|
||||
@@ -127,3 +127,46 @@ block0(v0: i64, v1: i64):
|
||||
; nextln: mov sp, fp
|
||||
; nextln: ldp fp, lr, [sp], #16
|
||||
; nextln: ret
|
||||
|
||||
function %f9() -> i32x2 {
|
||||
block0:
|
||||
v0 = iconst.i32 4278190335
|
||||
v1 = splat.i32x2 v0
|
||||
return v1
|
||||
}
|
||||
|
||||
; check: stp fp, lr, [sp, #-16]!
|
||||
; nextln: mov fp, sp
|
||||
; nextln: movi v0.2d, #18374687579166474495
|
||||
; nextln: fmov d0, d0
|
||||
; nextln: mov sp, fp
|
||||
; nextln: ldp fp, lr, [sp], #16
|
||||
; nextln: ret
|
||||
|
||||
function %f10() -> i32x4 {
|
||||
block0:
|
||||
v0 = iconst.i32 4293918720
|
||||
v1 = splat.i32x4 v0
|
||||
return v1
|
||||
}
|
||||
|
||||
; check: stp fp, lr, [sp, #-16]!
|
||||
; nextln: mov fp, sp
|
||||
; nextln: mvni v0.4s, #15, MSL #16
|
||||
; nextln: mov sp, fp
|
||||
; nextln: ldp fp, lr, [sp], #16
|
||||
; nextln: ret
|
||||
|
||||
function %f11() -> f32x4 {
|
||||
block0:
|
||||
v0 = f32const 0x1.5
|
||||
v1 = splat.f32x4 v0
|
||||
return v1
|
||||
}
|
||||
|
||||
; check: stp fp, lr, [sp, #-16]!
|
||||
; nextln: mov fp, sp
|
||||
; nextln: fmov v0.4s, #1.3125
|
||||
; nextln: mov sp, fp
|
||||
; nextln: ldp fp, lr, [sp], #16
|
||||
; nextln: ret
|
||||
|
||||
@@ -7,7 +7,7 @@ block0(v0: i64, v1: i64):
|
||||
v2 = iadd v0, v1
|
||||
v3 = load.i64 v2
|
||||
return v3
|
||||
; check: movq 0(%rdi,%rsi,1), %r12
|
||||
; check: movq 0(%rdi,%rsi,1), %rsi
|
||||
}
|
||||
|
||||
function %amode_add_imm(i64) -> i64 {
|
||||
@@ -16,7 +16,7 @@ block0(v0: i64):
|
||||
v2 = iadd v0, v1
|
||||
v3 = load.i64 v2
|
||||
return v3
|
||||
; check: movq 42(%rdi), %r12
|
||||
; check: movq 42(%rdi), %rsi
|
||||
}
|
||||
|
||||
;; Same as above, but add operands have been reversed.
|
||||
@@ -26,7 +26,7 @@ block0(v0: i64):
|
||||
v2 = iadd v1, v0
|
||||
v3 = load.i64 v2
|
||||
return v3
|
||||
; check: movq 42(%rdi), %r12
|
||||
; check: movq 42(%rdi), %rsi
|
||||
}
|
||||
|
||||
;; Make sure that uextend(cst) are ignored when the cst will naturally sign-extend.
|
||||
@@ -37,5 +37,5 @@ block0(v0: i64):
|
||||
v3 = iadd v2, v0
|
||||
v4 = load.i64 v3
|
||||
return v4
|
||||
; check: movq 42(%rdi), %r12
|
||||
; check: movq 42(%rdi), %rsi
|
||||
}
|
||||
|
||||
23
cranelift/filetests/filetests/isa/x64/heap.clif
Normal file
23
cranelift/filetests/filetests/isa/x64/heap.clif
Normal file
@@ -0,0 +1,23 @@
|
||||
test compile
|
||||
target x86_64
|
||||
feature "experimental_x64"
|
||||
|
||||
function %f(i32, i64 vmctx) -> i64 {
|
||||
gv0 = vmctx
|
||||
gv1 = load.i64 notrap aligned gv0+0
|
||||
gv2 = load.i32 notrap aligned gv0+8
|
||||
heap0 = dynamic gv1, bound gv2, offset_guard 0x1000, index_type i32
|
||||
|
||||
block0(v0: i32, v1: i64):
|
||||
|
||||
v2 = heap_addr.i64 heap0, v0, 0x8000
|
||||
; check: movl 8(%rsi), %ecx
|
||||
; nextln: movq %rdi, %rax
|
||||
; nextln: addl $$32768, %eax
|
||||
; nextln: jnb ; ud2 heap_oob ;
|
||||
; nextln: cmpl %ecx, %eax
|
||||
; nextln: jbe label1; j label2
|
||||
; check: Block 1:
|
||||
|
||||
return v2
|
||||
}
|
||||
@@ -6,7 +6,7 @@ function %add_from_mem_u32_1(i64, i32) -> i32 {
|
||||
block0(v0: i64, v1: i32):
|
||||
v2 = load.i32 v0
|
||||
v3 = iadd.i32 v2, v1
|
||||
; check: addl 0(%rdi), %r12d
|
||||
; check: addl 0(%rdi), %esi
|
||||
return v3
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ function %add_from_mem_u32_2(i64, i32) -> i32 {
|
||||
block0(v0: i64, v1: i32):
|
||||
v2 = load.i32 v0
|
||||
v3 = iadd.i32 v1, v2
|
||||
; check: addl 0(%rdi), %r12d
|
||||
; check: addl 0(%rdi), %esi
|
||||
return v3
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ function %add_from_mem_u64_1(i64, i64) -> i64 {
|
||||
block0(v0: i64, v1: i64):
|
||||
v2 = load.i64 v0
|
||||
v3 = iadd.i64 v2, v1
|
||||
; check: addq 0(%rdi), %r12
|
||||
; check: addq 0(%rdi), %rsi
|
||||
return v3
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ function %add_from_mem_u64_2(i64, i64) -> i64 {
|
||||
block0(v0: i64, v1: i64):
|
||||
v2 = load.i64 v0
|
||||
v3 = iadd.i64 v1, v2
|
||||
; check: addq 0(%rdi), %r12
|
||||
; check: addq 0(%rdi), %rsi
|
||||
return v3
|
||||
}
|
||||
|
||||
@@ -40,7 +40,22 @@ function %add_from_mem_not_narrow(i64, i8) -> i8 {
|
||||
block0(v0: i64, v1: i8):
|
||||
v2 = load.i8 v0
|
||||
v3 = iadd.i8 v2, v1
|
||||
; check: movzbq 0(%rdi), %r12
|
||||
; nextln: addl %esi, %r12d
|
||||
; check: movzbq 0(%rdi), %rdi
|
||||
; nextln: addl %esi, %edi
|
||||
return v3
|
||||
}
|
||||
|
||||
function %no_merge_if_lookback_use(i64, i64) -> i64 {
|
||||
block0(v0: i64, v1: i64):
|
||||
v2 = load.i64 v0
|
||||
v3 = iadd.i64 v2, v0
|
||||
store.i64 v3, v1
|
||||
v4 = load.i64 v3
|
||||
return v4
|
||||
; check: movq 0(%rdi), %rax
|
||||
; nextln: movq %rax, %rcx
|
||||
; nextln: addq %rdi, %rcx
|
||||
; nextln: movq %rcx, 0(%rsi)
|
||||
; nextln: movq 0(%rax,%rdi,1), %rsi
|
||||
; nextln: movq %rsi, %rax
|
||||
}
|
||||
|
||||
113
cranelift/filetests/filetests/isa/x64/popcnt.clif
Normal file
113
cranelift/filetests/filetests/isa/x64/popcnt.clif
Normal file
@@ -0,0 +1,113 @@
|
||||
test compile
|
||||
target x86_64
|
||||
feature "experimental_x64"
|
||||
|
||||
; TODO: test with popcnt feature available too, once new backend supports that.
|
||||
|
||||
function %popcnt64(i64) -> i64 {
|
||||
block0(v0: i64):
|
||||
v1 = popcnt v0
|
||||
; check: movq %rdi, %rsi
|
||||
; nextln: shrq $$1, %rsi
|
||||
; nextln: movabsq $$8608480567731124087, %rax
|
||||
; nextln: andq %rax, %rsi
|
||||
; nextln: subq %rsi, %rdi
|
||||
; nextln: shrq $$1, %rsi
|
||||
; nextln: andq %rax, %rsi
|
||||
; nextln: subq %rsi, %rdi
|
||||
; nextln: shrq $$1, %rsi
|
||||
; nextln: andq %rax, %rsi
|
||||
; nextln: subq %rsi, %rdi
|
||||
; nextln: movq %rdi, %rsi
|
||||
; nextln: shrq $$4, %rsi
|
||||
; nextln: addq %rdi, %rsi
|
||||
; nextln: movabsq $$1085102592571150095, %rdi
|
||||
; nextln: andq %rdi, %rsi
|
||||
; nextln: movabsq $$72340172838076673, %rdi
|
||||
; nextln: imulq %rdi, %rsi
|
||||
; nextln: shrq $$56, %rsi
|
||||
; nextln: movq %rsi, %rax
|
||||
return v1
|
||||
}
|
||||
|
||||
function %popcnt64load(i64) -> i64 {
|
||||
block0(v0: i64):
|
||||
v1 = load.i64 v0
|
||||
v2 = popcnt v1
|
||||
return v2
|
||||
; check: movq 0(%rdi), %rdi
|
||||
; nextln: movq %rdi, %rsi
|
||||
; nextln: shrq $$1, %rsi
|
||||
; nextln: movabsq $$8608480567731124087, %rax
|
||||
; nextln: andq %rax, %rsi
|
||||
; nextln: subq %rsi, %rdi
|
||||
; nextln: shrq $$1, %rsi
|
||||
; nextln: andq %rax, %rsi
|
||||
; nextln: subq %rsi, %rdi
|
||||
; nextln: shrq $$1, %rsi
|
||||
; nextln: andq %rax, %rsi
|
||||
; nextln: subq %rsi, %rdi
|
||||
; nextln: movq %rdi, %rsi
|
||||
; nextln: shrq $$4, %rsi
|
||||
; nextln: addq %rdi, %rsi
|
||||
; nextln: movabsq $$1085102592571150095, %rdi
|
||||
; nextln: andq %rdi, %rsi
|
||||
; nextln: movabsq $$72340172838076673, %rdi
|
||||
; nextln: imulq %rdi, %rsi
|
||||
; nextln: shrq $$56, %rsi
|
||||
; nextln: movq %rsi, %rax
|
||||
}
|
||||
|
||||
function %popcnt32(i32) -> i32 {
|
||||
block0(v0: i32):
|
||||
v1 = popcnt v0
|
||||
return v1
|
||||
; check: movq %rdi, %rsi
|
||||
; nextln: shrl $$1, %esi
|
||||
; nextln: andl $$2004318071, %esi
|
||||
; nextln: subl %esi, %edi
|
||||
; nextln: shrl $$1, %esi
|
||||
; nextln: andl $$2004318071, %esi
|
||||
; nextln: subl %esi, %edi
|
||||
; nextln: shrl $$1, %esi
|
||||
; nextln: andl $$2004318071, %esi
|
||||
; nextln: subl %esi, %edi
|
||||
; nextln: movq %rdi, %rsi
|
||||
; nextln: shrl $$4, %esi
|
||||
; nextln: addl %edi, %esi
|
||||
; nextln: andl $$252645135, %esi
|
||||
; nextln: imull $$16843009, %esi
|
||||
; nextln: shrl $$24, %esi
|
||||
; nextln: movq %rsi, %rax
|
||||
; nextln: movq %rbp, %rsp
|
||||
; nextln: popq %rbp
|
||||
; nextln: ret
|
||||
}
|
||||
|
||||
function %popcnt32load(i64) -> i32 {
|
||||
block0(v0: i64):
|
||||
v1 = load.i32 v0
|
||||
v2 = popcnt v1
|
||||
return v2
|
||||
; check: movl 0(%rdi), %edi
|
||||
; nextln: movq %rdi, %rsi
|
||||
; nextln: shrl $$1, %esi
|
||||
; nextln: andl $$2004318071, %esi
|
||||
; nextln: subl %esi, %edi
|
||||
; nextln: shrl $$1, %esi
|
||||
; nextln: andl $$2004318071, %esi
|
||||
; nextln: subl %esi, %edi
|
||||
; nextln: shrl $$1, %esi
|
||||
; nextln: andl $$2004318071, %esi
|
||||
; nextln: subl %esi, %edi
|
||||
; nextln: movq %rdi, %rsi
|
||||
; nextln: shrl $$4, %esi
|
||||
; nextln: addl %edi, %esi
|
||||
; nextln: andl $$252645135, %esi
|
||||
; nextln: imull $$16843009, %esi
|
||||
; nextln: shrl $$24, %esi
|
||||
; nextln: movq %rsi, %rax
|
||||
; nextln: movq %rbp, %rsp
|
||||
; nextln: popq %rbp
|
||||
; nextln: ret
|
||||
}
|
||||
17
cranelift/filetests/filetests/isa/x64/probestack.clif
Normal file
17
cranelift/filetests/filetests/isa/x64/probestack.clif
Normal file
@@ -0,0 +1,17 @@
|
||||
test compile
|
||||
set enable_probestack=true
|
||||
target x86_64
|
||||
feature "experimental_x64"
|
||||
|
||||
function %f1() -> i64 {
|
||||
ss0 = explicit_slot 100000
|
||||
|
||||
block0:
|
||||
v1 = stack_addr.i64 ss0
|
||||
return v1
|
||||
}
|
||||
|
||||
; check: pushq %rbp
|
||||
; nextln: movq %rsp, %rbp
|
||||
; nextln: movl $$100000, %eax
|
||||
; nextln: call LibCall(Probestack)
|
||||
@@ -28,9 +28,9 @@ block0(v0: i32):
|
||||
}
|
||||
; check: movd %edi, %xmm1
|
||||
; nextln: psllw %xmm1, %xmm0
|
||||
; nextln: lea const(VCodeConstant(0)), %r12
|
||||
; nextln: lea const(VCodeConstant(0)), %rsi
|
||||
; nextln: shlq $$4, %rdi
|
||||
; nextln: movdqu 0(%r12,%rdi,1), %xmm1
|
||||
; nextln: movdqu 0(%rsi,%rdi,1), %xmm1
|
||||
; nextln: pand %xmm1, %xmm0
|
||||
|
||||
function %ushr_i8x16_imm() -> i8x16 {
|
||||
@@ -81,12 +81,12 @@ block0(v0: i64x2, v1: i32):
|
||||
v2 = sshr v0, v1
|
||||
return v2
|
||||
}
|
||||
; check: pextrd.w $$0, %xmm0, %r12
|
||||
; nextln: pextrd.w $$1, %xmm0, %r13
|
||||
; check: pextrd.w $$0, %xmm0, %rsi
|
||||
; nextln: pextrd.w $$1, %xmm0, %rax
|
||||
; nextln: movq %rdi, %rcx
|
||||
; nextln: sarq %cl, %r12
|
||||
; nextln: sarq %cl, %rsi
|
||||
; nextln: movq %rdi, %rcx
|
||||
; nextln: sarq %cl, %r13
|
||||
; nextln: pinsrd.w $$0, %r12, %xmm1
|
||||
; nextln: pinsrd.w $$1, %r13, %xmm1
|
||||
; nextln: sarq %cl, %rax
|
||||
; nextln: pinsrd.w $$0, %rsi, %xmm1
|
||||
; nextln: pinsrd.w $$1, %rax, %xmm1
|
||||
; nextln: movdqa %xmm1, %xmm0
|
||||
|
||||
@@ -70,8 +70,8 @@ block0:
|
||||
return v1
|
||||
}
|
||||
; check: uninit %xmm0
|
||||
; nextln: pinsrw $$0, %r12, %xmm0
|
||||
; nextln: pinsrw $$1, %r12, %xmm0
|
||||
; nextln: pinsrw $$0, %rsi, %xmm0
|
||||
; nextln: pinsrw $$1, %rsi, %xmm0
|
||||
; nextln: pshufd $$0, %xmm0, %xmm0
|
||||
|
||||
function %splat_i32(i32) -> i32x4 {
|
||||
|
||||
@@ -17,7 +17,7 @@ block0(v0: b32x4):
|
||||
return v1
|
||||
}
|
||||
; check: ptest %xmm0, %xmm0
|
||||
; nextln: setnz %r12b
|
||||
; nextln: setnz %sil
|
||||
|
||||
function %vall_true_i64x2(i64x2) -> b1 {
|
||||
block0(v0: i64x2):
|
||||
@@ -27,4 +27,4 @@ block0(v0: i64x2):
|
||||
; check: pxor %xmm1, %xmm1
|
||||
; nextln: pcmpeqq %xmm0, %xmm1
|
||||
; nextln: ptest %xmm1, %xmm1
|
||||
; nextln: setz %r12b
|
||||
; nextln: setz %sil
|
||||
|
||||
@@ -44,9 +44,9 @@ block0:
|
||||
|
||||
function %zero_byte() -> i8 fast {
|
||||
block0:
|
||||
; asm: xor %al, %al
|
||||
[-,%rax] v0 = iconst.i8 0 ; bin: 30 c0
|
||||
; asm: xor %dh, %dh
|
||||
[-,%rdi] v1 = iconst.i8 0 ; bin: 30 ff
|
||||
; asm: xor %eax, %eax
|
||||
[-,%rax] v0 = iconst.i8 0 ; bin: 31 c0
|
||||
; asm: xor %edi, %edi
|
||||
[-,%rdi] v1 = iconst.i8 0 ; bin: 31 ff
|
||||
return v0
|
||||
}
|
||||
|
||||
@@ -62,11 +62,11 @@ block0:
|
||||
|
||||
function %zero_byte() -> i8 fast {
|
||||
block0:
|
||||
; asm: xor %r8b, %r8b
|
||||
[-,%r15] v0 = iconst.i8 0 ; bin: 45 30 ff
|
||||
; asm: xor %al, %al
|
||||
[-,%rax] v1 = iconst.i8 0 ; bin: 30 c0
|
||||
; asm: xor %dh, %dh
|
||||
[-,%rdi] v2 = iconst.i8 0 ; bin: 30 ff
|
||||
; asm: xor %r8d, %r8d
|
||||
[-,%r15] v0 = iconst.i8 0 ; bin: 45 31 ff
|
||||
; asm: xor %eax, eax
|
||||
[-,%rax] v1 = iconst.i8 0 ; bin: 31 c0
|
||||
; asm: xor %edi, %edi
|
||||
[-,%rdi] v2 = iconst.i8 0 ; bin: 31 ff
|
||||
return v0
|
||||
}
|
||||
|
||||
19
cranelift/filetests/filetests/licm/br-table.clif
Normal file
19
cranelift/filetests/filetests/licm/br-table.clif
Normal file
@@ -0,0 +1,19 @@
|
||||
test compile
|
||||
set opt_level=speed_and_size
|
||||
target x86_64
|
||||
|
||||
function %br_table_opt() {
|
||||
jt0 = jump_table [block1, block2]
|
||||
|
||||
block0:
|
||||
v0 = iconst.i32 1
|
||||
br_table v0, block2, jt0
|
||||
|
||||
block1:
|
||||
return
|
||||
|
||||
block2:
|
||||
v1 = iconst.i32 1
|
||||
jump block2
|
||||
|
||||
}
|
||||
28
cranelift/filetests/filetests/licm/rewrite-jump-table.clif
Normal file
28
cranelift/filetests/filetests/licm/rewrite-jump-table.clif
Normal file
@@ -0,0 +1,28 @@
|
||||
test licm
|
||||
target aarch64
|
||||
|
||||
function %rewrite_jump_table() {
|
||||
jt0 = jump_table [block1, block2]
|
||||
|
||||
block0:
|
||||
v0 = iconst.i64 1
|
||||
v1 = jump_table_base.i64 jt0
|
||||
v2 = jump_table_entry.i64 v0, v1, 4, jt0
|
||||
v3 = iadd v1, v2
|
||||
indirect_jump_table_br v3, jt0
|
||||
|
||||
block1:
|
||||
return
|
||||
|
||||
block2:
|
||||
v4 = bconst.b1 false
|
||||
jump block2
|
||||
}
|
||||
|
||||
; sameln: function
|
||||
; nextln: jt0 = jump_table [block1, block3]
|
||||
; check: block3:
|
||||
; nextln: v4 = bconst.b1 false
|
||||
; nextln: jump block2
|
||||
; check: block2:
|
||||
; nextln: jump block2
|
||||
@@ -21,7 +21,7 @@ use thiserror::Error;
|
||||
/// `CompiledFunction`s and subsequently calling them through the use of a `Trampoline`. As its
|
||||
/// name indicates, this compiler is limited: any functionality that requires knowledge of things
|
||||
/// outside the [Function] will likely not work (e.g. global values, calls). For an example of this
|
||||
/// "outside-of-function" functionality, see `cranelift_simplejit::backend::SimpleJITBackend`.
|
||||
/// "outside-of-function" functionality, see `cranelift_jit::backend::JITBackend`.
|
||||
///
|
||||
/// ```
|
||||
/// use cranelift_filetests::SingleFunctionCompiler;
|
||||
|
||||
@@ -1,52 +1,74 @@
|
||||
//! Implements the function environment (e.g. a name-to-function mapping) for interpretation.
|
||||
|
||||
use cranelift_codegen::ir::{FuncRef, Function};
|
||||
use cranelift_entity::{entity_impl, PrimaryMap};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// A function store contains all of the functions that are accessible to an interpreter.
|
||||
#[derive(Default, Clone)]
|
||||
pub struct FunctionStore<'a> {
|
||||
functions: HashMap<FuncRef, &'a Function>,
|
||||
function_name_to_func_ref: HashMap<String, FuncRef>,
|
||||
functions: PrimaryMap<FuncIndex, &'a Function>,
|
||||
function_names: HashMap<String, FuncIndex>,
|
||||
}
|
||||
|
||||
/// An opaque reference to a [`Function`](Function) stored in the [FunctionStore].
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct FuncIndex(u32);
|
||||
entity_impl!(FuncIndex, "fn");
|
||||
|
||||
/// This is a helpful conversion for instantiating a store from a single [Function].
|
||||
impl<'a> From<&'a Function> for FunctionStore<'a> {
|
||||
fn from(f: &'a Function) -> Self {
|
||||
let func_ref = FuncRef::from_u32(0);
|
||||
let mut function_name_to_func_ref = HashMap::new();
|
||||
function_name_to_func_ref.insert(f.name.to_string(), func_ref);
|
||||
let mut functions = HashMap::new();
|
||||
functions.insert(func_ref, f);
|
||||
Self {
|
||||
functions,
|
||||
function_name_to_func_ref,
|
||||
}
|
||||
fn from(function: &'a Function) -> Self {
|
||||
let mut store = FunctionStore::default();
|
||||
store.add(function.name.to_string(), function);
|
||||
store
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FunctionStore<'a> {
|
||||
/// Add a function by name.
|
||||
pub fn add(&mut self, name: String, function: &'a Function) {
|
||||
let func_ref = FuncRef::with_number(self.function_name_to_func_ref.len() as u32)
|
||||
.expect("a valid function reference");
|
||||
self.function_name_to_func_ref.insert(name, func_ref);
|
||||
self.functions.insert(func_ref, function);
|
||||
assert!(!self.function_names.contains_key(&name));
|
||||
let index = self.functions.push(function);
|
||||
self.function_names.insert(name, index);
|
||||
}
|
||||
|
||||
/// Retrieve a reference to a function in the environment by its name.
|
||||
pub fn index_of(&self, name: &str) -> Option<FuncRef> {
|
||||
self.function_name_to_func_ref.get(name).cloned()
|
||||
/// Retrieve the index of a function in the function store by its `name`.
|
||||
pub fn index_of(&self, name: &str) -> Option<FuncIndex> {
|
||||
self.function_names.get(name).cloned()
|
||||
}
|
||||
|
||||
/// Retrieve a function by its function reference.
|
||||
pub fn get_by_func_ref(&self, func_ref: FuncRef) -> Option<&'a Function> {
|
||||
self.functions.get(&func_ref).cloned()
|
||||
/// Retrieve a function by its index in the function store.
|
||||
pub fn get_by_index(&self, index: FuncIndex) -> Option<&'a Function> {
|
||||
self.functions.get(index).cloned()
|
||||
}
|
||||
|
||||
/// Retrieve a function by its name.
|
||||
pub fn get_by_name(&self, name: &str) -> Option<&'a Function> {
|
||||
let func_ref = self.index_of(name)?;
|
||||
self.get_by_func_ref(func_ref)
|
||||
let index = self.index_of(name)?;
|
||||
self.get_by_index(index)
|
||||
}
|
||||
|
||||
/// Retrieve a function from a [FuncRef] within a [Function]. TODO this should be optimized, if possible, as
|
||||
/// currently it retrieves the function name as a string and performs string matching.
|
||||
pub fn get_from_func_ref(
|
||||
&self,
|
||||
func_ref: FuncRef,
|
||||
function: &Function,
|
||||
) -> Option<&'a Function> {
|
||||
self.get_by_name(&get_function_name(func_ref, function))
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve a function name from a [FuncRef] within a [Function]. TODO this should be optimized, if possible, as
|
||||
/// currently it retrieves the function name as a string and performs string matching.
|
||||
fn get_function_name(func_ref: FuncRef, function: &Function) -> String {
|
||||
function
|
||||
.dfg
|
||||
.ext_funcs
|
||||
.get(func_ref)
|
||||
.expect("function to exist")
|
||||
.name
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -77,6 +99,6 @@ mod tests {
|
||||
let signature = Signature::new(CallConv::Fast);
|
||||
let func = &Function::with_name_signature(name, signature);
|
||||
let env: FunctionStore = func.into();
|
||||
assert_eq!(env.index_of("%test"), FuncRef::with_number(0));
|
||||
assert_eq!(env.index_of("%test"), Some(FuncIndex::from_u32(0)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,41 +2,45 @@
|
||||
|
||||
use cranelift_codegen::data_value::DataValue;
|
||||
use cranelift_codegen::ir::{Function, Value as ValueRef};
|
||||
use cranelift_entity::EntityRef;
|
||||
use log::trace;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Holds the mutable elements of an interpretation. At some point I thought about using
|
||||
/// Cell/RefCell to do field-level mutability, thinking that otherwise I would have to
|
||||
/// pass around a mutable object (for inst and registers) and an immutable one (for function,
|
||||
/// could be self)--in the end I decided to do exactly that but perhaps one day that will become
|
||||
/// untenable.
|
||||
/// The type used for ensuring [Frame](crate::frame::Frame) entries conform to the expected memory layout.
|
||||
pub(crate) type Entries = Vec<Option<DataValue>>;
|
||||
|
||||
/// Holds the mutable elements of an interpreted function call.
|
||||
#[derive(Debug)]
|
||||
pub struct Frame<'a> {
|
||||
/// The currently executing function.
|
||||
pub function: &'a Function,
|
||||
/// The current mapping of SSA value-references to their actual values.
|
||||
registers: HashMap<ValueRef, DataValue>,
|
||||
pub(crate) function: &'a Function,
|
||||
/// The current mapping of SSA value-references to their actual values. For efficiency, each SSA value is used as an
|
||||
/// index into the Vec, meaning some slots may be unused.
|
||||
registers: Entries,
|
||||
}
|
||||
|
||||
impl<'a> Frame<'a> {
|
||||
/// Construct a new [Frame] for a function. This allocates a slot in the hash map for each SSA
|
||||
/// `Value` (renamed to `ValueRef` here) which should mean that no additional allocations are
|
||||
/// needed while interpreting the frame.
|
||||
/// Construct a new [Frame] for a function. This allocates a slot in the hash map for each SSA `Value` (renamed to
|
||||
/// `ValueRef` here) which should mean that no additional allocations are needed while interpreting the frame.
|
||||
pub fn new(function: &'a Function) -> Self {
|
||||
let num_slots = function.dfg.num_values();
|
||||
trace!("Create new frame for function: {}", function.signature);
|
||||
Self {
|
||||
function,
|
||||
registers: HashMap::with_capacity(function.dfg.num_values()),
|
||||
registers: vec![None; num_slots],
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the actual value associated with an SSA reference.
|
||||
#[inline]
|
||||
pub fn get(&self, name: ValueRef) -> &DataValue {
|
||||
assert!(name.index() < self.registers.len());
|
||||
trace!("Get {}", name);
|
||||
self.registers
|
||||
.get(&name)
|
||||
&self
|
||||
.registers
|
||||
.get(name.index())
|
||||
.unwrap_or_else(|| panic!("unknown value: {}", name))
|
||||
.as_ref()
|
||||
.unwrap_or_else(|| panic!("empty slot: {}", name))
|
||||
}
|
||||
|
||||
/// Retrieve multiple SSA references; see `get`.
|
||||
@@ -47,8 +51,9 @@ impl<'a> Frame<'a> {
|
||||
/// Assign `value` to the SSA reference `name`.
|
||||
#[inline]
|
||||
pub fn set(&mut self, name: ValueRef, value: DataValue) -> Option<DataValue> {
|
||||
assert!(name.index() < self.registers.len());
|
||||
trace!("Set {} -> {}", name, value);
|
||||
self.registers.insert(name, value)
|
||||
std::mem::replace(&mut self.registers[name.index()], Some(value))
|
||||
}
|
||||
|
||||
/// Assign to multiple SSA references; see `set`.
|
||||
@@ -66,12 +71,18 @@ impl<'a> Frame<'a> {
|
||||
pub fn rename(&mut self, old_names: &[ValueRef], new_names: &[ValueRef]) {
|
||||
trace!("Renaming {:?} -> {:?}", old_names, new_names);
|
||||
assert_eq!(old_names.len(), new_names.len());
|
||||
let mut registers = HashMap::with_capacity(self.registers.len());
|
||||
for (on, nn) in old_names.iter().zip(new_names) {
|
||||
let v = self.registers.get(on).unwrap().clone();
|
||||
registers.insert(*nn, v);
|
||||
let new_registers = vec![None; self.registers.len()];
|
||||
let mut old_registers = std::mem::replace(&mut self.registers, new_registers);
|
||||
self.registers = vec![None; self.registers.len()];
|
||||
for (&on, &nn) in old_names.iter().zip(new_names) {
|
||||
let value = std::mem::replace(&mut old_registers[on.index()], None);
|
||||
self.registers[nn.index()] = value;
|
||||
}
|
||||
self.registers = registers;
|
||||
}
|
||||
|
||||
/// Accessor for the current entries in the frame.
|
||||
pub fn entries_mut(&mut self) -> &mut [Option<DataValue>] {
|
||||
&mut self.registers
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,8 +90,15 @@ impl<'a> Frame<'a> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use cranelift_codegen::data_value::DataValue;
|
||||
use cranelift_codegen::ir::immediates::{Ieee32, Ieee64};
|
||||
use cranelift_codegen::ir::InstBuilder;
|
||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||
use cranelift_reader::parse_functions;
|
||||
|
||||
/// Helper to create a function from CLIF IR.
|
||||
fn function(code: &str) -> Function {
|
||||
parse_functions(code).unwrap().into_iter().next().unwrap()
|
||||
}
|
||||
|
||||
/// Build an empty function with a single return.
|
||||
fn empty_function() -> Function {
|
||||
@@ -101,23 +119,112 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assignment() {
|
||||
let func = empty_function();
|
||||
fn assignment_and_retrieval() {
|
||||
let func = function("function %test(i32) -> i32 { block0(v0:i32): return v0 }");
|
||||
let mut frame = Frame::new(&func);
|
||||
|
||||
let a = ValueRef::with_number(1).unwrap();
|
||||
let ssa_value_ref = ValueRef::from_u32(0);
|
||||
let fortytwo = DataValue::I32(42);
|
||||
frame.set(a, fortytwo.clone());
|
||||
assert_eq!(frame.get(a), &fortytwo);
|
||||
|
||||
// Verify that setting a valid SSA ref will make the value retrievable.
|
||||
frame.set(ssa_value_ref, fortytwo.clone());
|
||||
assert_eq!(frame.get(ssa_value_ref), &fortytwo);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn no_existing_value() {
|
||||
fn assignment_to_extra_slots() {
|
||||
let func = function("function %test(i32) -> i32 { block0(v10:i32): return v10 }");
|
||||
let mut frame = Frame::new(&func);
|
||||
let ssa_value_ref = ValueRef::from_u32(5);
|
||||
let fortytwo = DataValue::I32(42);
|
||||
|
||||
// Due to how Cranelift organizes its SSA values, the use of v10 defines 11 slots for values
|
||||
// to fit in--the following should work.
|
||||
frame.set(ssa_value_ref, fortytwo.clone());
|
||||
assert_eq!(frame.get(ssa_value_ref), &fortytwo);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "assertion failed: name.index() < self.registers.len()")]
|
||||
fn invalid_assignment() {
|
||||
let func = function("function %test(i32) -> i32 { block0(v10:i32): return v10 }");
|
||||
let mut frame = Frame::new(&func);
|
||||
let fortytwo = DataValue::I32(42);
|
||||
|
||||
// Since the SSA value ref points to 42 and the function only has 11 slots, this should
|
||||
// fail. TODO currently this is a panic under the assumption we will not set indexes outside
|
||||
// of the valid SSA value range but it might be better as a result.
|
||||
frame.set(ValueRef::from_u32(11), fortytwo.clone());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "assertion failed: name.index() < self.registers.len()")]
|
||||
fn retrieve_nonexistent_value() {
|
||||
let func = empty_function();
|
||||
let frame = Frame::new(&func);
|
||||
let ssa_value_ref = ValueRef::from_u32(1);
|
||||
|
||||
let a = ValueRef::with_number(1).unwrap();
|
||||
frame.get(a);
|
||||
// Retrieving a non-existent value should return an error.
|
||||
frame.get(ssa_value_ref);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "empty slot: v5")]
|
||||
fn retrieve_and_assign_multiple_values() {
|
||||
let func = function("function %test(i32) -> i32 { block0(v10:i32): return v10 }");
|
||||
let mut frame = Frame::new(&func);
|
||||
let ssa_value_refs = [
|
||||
ValueRef::from_u32(2),
|
||||
ValueRef::from_u32(4),
|
||||
ValueRef::from_u32(6),
|
||||
];
|
||||
let values = vec![
|
||||
DataValue::B(true),
|
||||
DataValue::I8(42),
|
||||
DataValue::F32(Ieee32::from(0.42)),
|
||||
];
|
||||
|
||||
// We can assign and retrieve multiple (cloned) values.
|
||||
frame.set_all(&ssa_value_refs, values.clone());
|
||||
let retrieved_values = frame.get_all(&ssa_value_refs);
|
||||
assert_eq!(values, retrieved_values);
|
||||
|
||||
// But if we attempt to retrieve an invalid value we should get an error:
|
||||
frame.get_all(&[ValueRef::from_u32(2), ValueRef::from_u32(5)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "empty slot: v10")]
|
||||
fn rename() {
|
||||
let func = function("function %test(i32) -> i32 { block0(v10:i32): return v10 }");
|
||||
let mut frame = Frame::new(&func);
|
||||
let old_ssa_value_refs = [ValueRef::from_u32(9), ValueRef::from_u32(10)];
|
||||
let values = vec![DataValue::B(true), DataValue::F64(Ieee64::from(0.0))];
|
||||
frame.set_all(&old_ssa_value_refs, values.clone());
|
||||
|
||||
// Rename the old SSA values to the new values.
|
||||
let new_ssa_value_refs = [ValueRef::from_u32(4), ValueRef::from_u32(2)];
|
||||
frame.rename(&old_ssa_value_refs, &new_ssa_value_refs);
|
||||
|
||||
// Now we should be able to retrieve new values and the old ones should fail.
|
||||
assert_eq!(frame.get_all(&new_ssa_value_refs), values);
|
||||
frame.get(ValueRef::from_u32(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "empty slot: v2")]
|
||||
fn rename_duplicates_causes_inconsistency() {
|
||||
let func = function("function %test(i32) -> i32 { block0(v10:i32): return v10 }");
|
||||
let mut frame = Frame::new(&func);
|
||||
let old_ssa_value_refs = [ValueRef::from_u32(1), ValueRef::from_u32(9)];
|
||||
let values = vec![DataValue::B(true), DataValue::F64(Ieee64::from(f64::NAN))];
|
||||
frame.set_all(&old_ssa_value_refs, values.clone());
|
||||
|
||||
// Rename the old SSA values to the new values.
|
||||
let old_duplicated_ssa_value_refs = [ValueRef::from_u32(1), ValueRef::from_u32(1)];
|
||||
let new_ssa_value_refs = [ValueRef::from_u32(4), ValueRef::from_u32(2)];
|
||||
frame.rename(&old_duplicated_ssa_value_refs, &new_ssa_value_refs);
|
||||
|
||||
// If we use duplicates then subsequent renamings (v1 -> v2) will be empty.
|
||||
frame.get(ValueRef::from_u32(2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! This module partially contains the logic for interpreting Cranelift IR.
|
||||
|
||||
use crate::environment::FunctionStore;
|
||||
use crate::environment::{FuncIndex, FunctionStore};
|
||||
use crate::frame::Frame;
|
||||
use crate::instruction::DfgInstructionContext;
|
||||
use crate::state::{MemoryError, State};
|
||||
@@ -34,22 +34,22 @@ impl<'a> Interpreter<'a> {
|
||||
func_name: &str,
|
||||
arguments: &[DataValue],
|
||||
) -> Result<ControlFlow<'a, DataValue>, InterpreterError> {
|
||||
let func_ref = self
|
||||
let index = self
|
||||
.state
|
||||
.functions
|
||||
.index_of(func_name)
|
||||
.ok_or_else(|| InterpreterError::UnknownFunctionName(func_name.to_string()))?;
|
||||
self.call_by_index(func_ref, arguments)
|
||||
self.call_by_index(index, arguments)
|
||||
}
|
||||
|
||||
/// Call a function by its index in the [FunctionStore]; this is a proxy for [Interpreter::call].
|
||||
pub fn call_by_index(
|
||||
&mut self,
|
||||
func_ref: FuncRef,
|
||||
index: FuncIndex,
|
||||
arguments: &[DataValue],
|
||||
) -> Result<ControlFlow<'a, DataValue>, InterpreterError> {
|
||||
match self.state.get_function(func_ref) {
|
||||
None => Err(InterpreterError::UnknownFunctionReference(func_ref)),
|
||||
match self.state.functions.get_by_index(index) {
|
||||
None => Err(InterpreterError::UnknownFunctionIndex(index)),
|
||||
Some(func) => self.call(func, arguments),
|
||||
}
|
||||
}
|
||||
@@ -98,8 +98,9 @@ impl<'a> Interpreter<'a> {
|
||||
.set_all(function.dfg.block_params(block), block_arguments.to_vec());
|
||||
maybe_inst = layout.first_inst(block)
|
||||
}
|
||||
ControlFlow::Call(function, arguments) => {
|
||||
let returned_arguments = self.call(function, &arguments)?.unwrap_return();
|
||||
ControlFlow::Call(called_function, arguments) => {
|
||||
let returned_arguments =
|
||||
self.call(called_function, &arguments)?.unwrap_return();
|
||||
self.state
|
||||
.current_frame_mut()
|
||||
.set_all(function.dfg.inst_results(inst), returned_arguments);
|
||||
@@ -123,8 +124,8 @@ pub enum InterpreterError {
|
||||
StepError(#[from] StepError),
|
||||
#[error("reached an unreachable statement")]
|
||||
Unreachable,
|
||||
#[error("unknown function reference (has it been added to the function store?): {0}")]
|
||||
UnknownFunctionReference(FuncRef),
|
||||
#[error("unknown function index (has it been added to the function store?): {0}")]
|
||||
UnknownFunctionIndex(FuncIndex),
|
||||
#[error("unknown function with name (has it been added to the function store?): {0}")]
|
||||
UnknownFunctionName(String),
|
||||
#[error("value error")]
|
||||
@@ -176,7 +177,8 @@ impl<'a> InterpreterState<'a> {
|
||||
|
||||
impl<'a> State<'a, DataValue> for InterpreterState<'a> {
|
||||
fn get_function(&self, func_ref: FuncRef) -> Option<&'a Function> {
|
||||
self.functions.get_by_func_ref(func_ref)
|
||||
self.functions
|
||||
.get_from_func_ref(func_ref, self.frame_stack.last().unwrap().function)
|
||||
}
|
||||
fn push_frame(&mut self, function: &'a Function) {
|
||||
self.frame_stack.push(Frame::new(function));
|
||||
@@ -273,6 +275,40 @@ mod tests {
|
||||
assert_eq!(result, vec![DataValue::B(true)])
|
||||
}
|
||||
|
||||
// This test verifies that functions can refer to each other using the function store. A double indirection is
|
||||
// required, which is tricky to get right: a referenced function is a FuncRef when called but a FuncIndex inside the
|
||||
// function store. This test would preferably be a CLIF filetest but the filetest infrastructure only looks at a
|
||||
// single function at a time--we need more than one function in the store for this test.
|
||||
#[test]
|
||||
fn function_references() {
|
||||
let code = "
|
||||
function %child(i32) -> i32 {
|
||||
block0(v0: i32):
|
||||
v1 = iadd_imm v0, -1
|
||||
return v1
|
||||
}
|
||||
|
||||
function %parent(i32) -> i32 {
|
||||
fn42 = %child(i32) -> i32
|
||||
block0(v0: i32):
|
||||
v1 = iadd_imm v0, 1
|
||||
v2 = call fn42(v1)
|
||||
return v2
|
||||
}";
|
||||
|
||||
let mut env = FunctionStore::default();
|
||||
let funcs = parse_functions(code).unwrap().to_vec();
|
||||
funcs.iter().for_each(|f| env.add(f.name.to_string(), f));
|
||||
|
||||
let state = InterpreterState::default().with_function_store(env);
|
||||
let result = Interpreter::new(state)
|
||||
.call_by_name("%parent", &[DataValue::I32(0)])
|
||||
.unwrap()
|
||||
.unwrap_return();
|
||||
|
||||
assert_eq!(result, vec![DataValue::I32(0)])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn state_heap_roundtrip() -> Result<(), MemoryError> {
|
||||
let mut state = InterpreterState::default();
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[package]
|
||||
name = "cranelift-simplejit"
|
||||
name = "cranelift-jit"
|
||||
version = "0.68.0"
|
||||
authors = ["The Cranelift Project Developers"]
|
||||
description = "A simple JIT library backed by Cranelift"
|
||||
description = "A JIT library backed by Cranelift"
|
||||
repository = "https://github.com/bytecodealliance/wasmtime"
|
||||
documentation = "https://docs.rs/cranelift-simplejit"
|
||||
documentation = "https://docs.rs/cranelift-jit"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
@@ -14,6 +14,7 @@ cranelift-module = { path = "../module", version = "0.68.0" }
|
||||
cranelift-native = { path = "../native", version = "0.68.0" }
|
||||
cranelift-codegen = { path = "../codegen", version = "0.68.0", default-features = false, features = ["std"] }
|
||||
cranelift-entity = { path = "../entity", version = "0.68.0" }
|
||||
anyhow = "1.0"
|
||||
region = "2.2.0"
|
||||
libc = { version = "0.2.42" }
|
||||
errno = "0.2.4"
|
||||
@@ -1,8 +1,8 @@
|
||||
This crate provides a simple JIT library that uses
|
||||
This crate provides a JIT library that uses
|
||||
[Cranelift](https://crates.io/crates/cranelift).
|
||||
|
||||
This crate is extremely experimental.
|
||||
|
||||
See the [example program] for a brief overview of how to use this.
|
||||
|
||||
[example program]: https://github.com/bytecodealliance/wasmtime/blob/main/cranelift/simplejit/examples/simplejit-minimal.rs
|
||||
[example program]: https://github.com/bytecodealliance/wasmtime/blob/main/cranelift/jit/examples/jit-minimal.rs
|
||||
@@ -1,12 +1,21 @@
|
||||
use cranelift::prelude::*;
|
||||
use cranelift_codegen::binemit::NullTrapSink;
|
||||
use cranelift_codegen::settings::{self, Configurable};
|
||||
use cranelift_jit::{JITBuilder, JITModule};
|
||||
use cranelift_module::{default_libcall_names, Linkage, Module};
|
||||
use cranelift_simplejit::{SimpleJITBuilder, SimpleJITModule};
|
||||
use std::mem;
|
||||
|
||||
fn main() {
|
||||
let mut module: SimpleJITModule =
|
||||
SimpleJITModule::new(SimpleJITBuilder::new(default_libcall_names()));
|
||||
let mut flag_builder = settings::builder();
|
||||
flag_builder.set("use_colocated_libcalls", "false").unwrap();
|
||||
// FIXME set back to true once the x64 backend supports it.
|
||||
flag_builder.set("is_pic", "false").unwrap();
|
||||
let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| {
|
||||
panic!("host machine is not supported: {}", msg);
|
||||
});
|
||||
let isa = isa_builder.finish(settings::Flags::new(flag_builder));
|
||||
let mut module = JITModule::new(JITBuilder::with_isa(isa, default_libcall_names()));
|
||||
|
||||
let mut ctx = module.make_context();
|
||||
let mut func_ctx = FunctionBuilderContext::new();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Defines `SimpleJITModule`.
|
||||
//! Defines `JITModule`.
|
||||
|
||||
use crate::{compiled_blob::CompiledBlob, memory::Memory};
|
||||
use cranelift_codegen::isa::TargetIsa;
|
||||
@@ -18,10 +18,11 @@ use cranelift_native;
|
||||
use libc;
|
||||
use log::info;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::ffi::CString;
|
||||
use std::io::Write;
|
||||
use std::ptr;
|
||||
use std::ptr::NonNull;
|
||||
use target_lexicon::PointerWidth;
|
||||
#[cfg(windows)]
|
||||
use winapi;
|
||||
@@ -30,15 +31,16 @@ const EXECUTABLE_DATA_ALIGNMENT: u64 = 0x10;
|
||||
const WRITABLE_DATA_ALIGNMENT: u64 = 0x8;
|
||||
const READONLY_DATA_ALIGNMENT: u64 = 0x1;
|
||||
|
||||
/// A builder for `SimpleJITModule`.
|
||||
pub struct SimpleJITBuilder {
|
||||
/// A builder for `JITModule`.
|
||||
pub struct JITBuilder {
|
||||
isa: Box<dyn TargetIsa>,
|
||||
symbols: HashMap<String, *const u8>,
|
||||
libcall_names: Box<dyn Fn(ir::LibCall) -> String + Send + Sync>,
|
||||
hotswap_enabled: bool,
|
||||
}
|
||||
|
||||
impl SimpleJITBuilder {
|
||||
/// Create a new `SimpleJITBuilder`.
|
||||
impl JITBuilder {
|
||||
/// Create a new `JITBuilder`.
|
||||
///
|
||||
/// The `libcall_names` function provides a way to translate `cranelift_codegen`'s `ir::LibCall`
|
||||
/// enum to symbols. LibCalls are inserted in the IR as part of the legalization for certain
|
||||
@@ -50,6 +52,7 @@ impl SimpleJITBuilder {
|
||||
// which might not reach all definitions; we can't handle that here, so
|
||||
// we require long-range relocation types.
|
||||
flag_builder.set("use_colocated_libcalls", "false").unwrap();
|
||||
flag_builder.set("is_pic", "true").unwrap();
|
||||
let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| {
|
||||
panic!("host machine is not supported: {}", msg);
|
||||
});
|
||||
@@ -57,12 +60,10 @@ impl SimpleJITBuilder {
|
||||
Self::with_isa(isa, libcall_names)
|
||||
}
|
||||
|
||||
/// Create a new `SimpleJITBuilder` with an arbitrary target. This is mainly
|
||||
/// Create a new `JITBuilder` with an arbitrary target. This is mainly
|
||||
/// useful for testing.
|
||||
///
|
||||
/// SimpleJIT requires a `TargetIsa` configured for non-PIC.
|
||||
///
|
||||
/// To create a `SimpleJITBuilder` for native use, use the `new` constructor
|
||||
/// To create a `JITBuilder` for native use, use the `new` constructor
|
||||
/// instead.
|
||||
///
|
||||
/// The `libcall_names` function provides a way to translate `cranelift_codegen`'s `ir::LibCall`
|
||||
@@ -73,12 +74,12 @@ impl SimpleJITBuilder {
|
||||
isa: Box<dyn TargetIsa>,
|
||||
libcall_names: Box<dyn Fn(ir::LibCall) -> String + Send + Sync>,
|
||||
) -> Self {
|
||||
debug_assert!(!isa.flags().is_pic(), "SimpleJIT requires non-PIC code");
|
||||
let symbols = HashMap::new();
|
||||
Self {
|
||||
isa,
|
||||
symbols,
|
||||
libcall_names,
|
||||
hotswap_enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,18 +118,33 @@ impl SimpleJITBuilder {
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable or disable hotswap support. See [`JITModule::prepare_for_function_redefine`]
|
||||
/// for more information.
|
||||
///
|
||||
/// Enabling hotswap support requires PIC code.
|
||||
pub fn hotswap(&mut self, enabled: bool) -> &mut Self {
|
||||
self.hotswap_enabled = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A `SimpleJITModule` implements `Module` and emits code and data into memory where it can be
|
||||
/// A `JITModule` implements `Module` and emits code and data into memory where it can be
|
||||
/// directly called and accessed.
|
||||
///
|
||||
/// See the `SimpleJITBuilder` for a convenient way to construct `SimpleJITModule` instances.
|
||||
pub struct SimpleJITModule {
|
||||
/// See the `JITBuilder` for a convenient way to construct `JITModule` instances.
|
||||
pub struct JITModule {
|
||||
isa: Box<dyn TargetIsa>,
|
||||
hotswap_enabled: bool,
|
||||
symbols: HashMap<String, *const u8>,
|
||||
libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
|
||||
memory: MemoryHandle,
|
||||
declarations: ModuleDeclarations,
|
||||
function_got_entries: SecondaryMap<FuncId, Option<NonNull<*const u8>>>,
|
||||
function_plt_entries: SecondaryMap<FuncId, Option<NonNull<[u8; 16]>>>,
|
||||
data_object_got_entries: SecondaryMap<DataId, Option<NonNull<*const u8>>>,
|
||||
libcall_got_entries: HashMap<ir::LibCall, NonNull<*const u8>>,
|
||||
libcall_plt_entries: HashMap<ir::LibCall, NonNull<[u8; 16]>>,
|
||||
compiled_functions: SecondaryMap<FuncId, Option<CompiledBlob>>,
|
||||
compiled_data_objects: SecondaryMap<DataId, Option<CompiledBlob>>,
|
||||
functions_to_finalize: Vec<FuncId>,
|
||||
@@ -142,7 +158,7 @@ struct MemoryHandle {
|
||||
writable: Memory,
|
||||
}
|
||||
|
||||
impl SimpleJITModule {
|
||||
impl JITModule {
|
||||
/// Free memory allocated for code and data segments of compiled functions.
|
||||
///
|
||||
/// # Safety
|
||||
@@ -164,16 +180,35 @@ impl SimpleJITModule {
|
||||
.or_else(|| lookup_with_dlsym(name))
|
||||
}
|
||||
|
||||
fn get_definition(&self, name: &ir::ExternalName) -> *const u8 {
|
||||
unsafe fn write_plt_entry_bytes(plt_ptr: *mut [u8; 16], got_ptr: *mut *const u8) {
|
||||
assert!(
|
||||
cfg!(target_arch = "x86_64"),
|
||||
"PLT is currently only supported on x86_64"
|
||||
);
|
||||
// jmp *got_ptr; ud2; ud2; ud2; ud2; ud2
|
||||
let mut plt_val = [
|
||||
0xff, 0x25, 0, 0, 0, 0, 0x0f, 0x0b, 0x0f, 0x0b, 0x0f, 0x0b, 0x0f, 0x0b, 0x0f, 0x0b,
|
||||
];
|
||||
let what = got_ptr as isize - 4;
|
||||
let at = plt_ptr as isize + 2;
|
||||
plt_val[2..6].copy_from_slice(&i32::to_ne_bytes(i32::try_from(what - at).unwrap()));
|
||||
std::ptr::write(plt_ptr, plt_val);
|
||||
}
|
||||
|
||||
fn get_address(&self, name: &ir::ExternalName) -> *const u8 {
|
||||
match *name {
|
||||
ir::ExternalName::User { .. } => {
|
||||
let (name, linkage) = if ModuleDeclarations::is_function(name) {
|
||||
let func_id = FuncId::from_name(name);
|
||||
match &self.compiled_functions[func_id] {
|
||||
Some(compiled) => return compiled.ptr,
|
||||
None => {
|
||||
let decl = self.declarations.get_function_decl(func_id);
|
||||
(&decl.name, decl.linkage)
|
||||
if self.hotswap_enabled {
|
||||
return self.get_plt_address(name);
|
||||
} else {
|
||||
let func_id = FuncId::from_name(name);
|
||||
match &self.compiled_functions[func_id] {
|
||||
Some(compiled) => return compiled.ptr,
|
||||
None => {
|
||||
let decl = self.declarations.get_function_decl(func_id);
|
||||
(&decl.name, decl.linkage)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -203,10 +238,60 @@ impl SimpleJITModule {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_got_address(&self, name: &ir::ExternalName) -> *const u8 {
|
||||
match *name {
|
||||
ir::ExternalName::User { .. } => {
|
||||
if ModuleDeclarations::is_function(name) {
|
||||
let func_id = FuncId::from_name(name);
|
||||
self.function_got_entries[func_id]
|
||||
.unwrap()
|
||||
.as_ptr()
|
||||
.cast::<u8>()
|
||||
} else {
|
||||
let data_id = DataId::from_name(name);
|
||||
self.data_object_got_entries[data_id]
|
||||
.unwrap()
|
||||
.as_ptr()
|
||||
.cast::<u8>()
|
||||
}
|
||||
}
|
||||
ir::ExternalName::LibCall(ref libcall) => self
|
||||
.libcall_got_entries
|
||||
.get(libcall)
|
||||
.unwrap_or_else(|| panic!("can't resolve libcall {}", libcall))
|
||||
.as_ptr()
|
||||
.cast::<u8>(),
|
||||
_ => panic!("invalid ExternalName {}", name),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_plt_address(&self, name: &ir::ExternalName) -> *const u8 {
|
||||
match *name {
|
||||
ir::ExternalName::User { .. } => {
|
||||
if ModuleDeclarations::is_function(name) {
|
||||
let func_id = FuncId::from_name(name);
|
||||
self.function_plt_entries[func_id]
|
||||
.unwrap()
|
||||
.as_ptr()
|
||||
.cast::<u8>()
|
||||
} else {
|
||||
unreachable!("PLT relocations can only have functions as target");
|
||||
}
|
||||
}
|
||||
ir::ExternalName::LibCall(ref libcall) => self
|
||||
.libcall_plt_entries
|
||||
.get(libcall)
|
||||
.unwrap_or_else(|| panic!("can't resolve libcall {}", libcall))
|
||||
.as_ptr()
|
||||
.cast::<u8>(),
|
||||
_ => panic!("invalid ExternalName {}", name),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the address of a finalized function.
|
||||
pub fn get_finalized_function(&self, func_id: FuncId) -> *const u8 {
|
||||
let info = &self.compiled_functions[func_id];
|
||||
debug_assert!(
|
||||
assert!(
|
||||
!self.functions_to_finalize.iter().any(|x| *x == func_id),
|
||||
"function not yet finalized"
|
||||
);
|
||||
@@ -218,7 +303,7 @@ impl SimpleJITModule {
|
||||
/// Returns the address and size of a finalized data object.
|
||||
pub fn get_finalized_data(&self, data_id: DataId) -> (*const u8, usize) {
|
||||
let info = &self.compiled_data_objects[data_id];
|
||||
debug_assert!(
|
||||
assert!(
|
||||
!self.data_objects_to_finalize.iter().any(|x| *x == data_id),
|
||||
"data object not yet finalized"
|
||||
);
|
||||
@@ -255,19 +340,28 @@ impl SimpleJITModule {
|
||||
pub fn finalize_definitions(&mut self) {
|
||||
for func in std::mem::take(&mut self.functions_to_finalize) {
|
||||
let decl = self.declarations.get_function_decl(func);
|
||||
debug_assert!(decl.linkage.is_definable());
|
||||
assert!(decl.linkage.is_definable());
|
||||
let func = self.compiled_functions[func]
|
||||
.as_ref()
|
||||
.expect("function must be compiled before it can be finalized");
|
||||
func.perform_relocations(|name| self.get_definition(name));
|
||||
func.perform_relocations(
|
||||
|name| self.get_address(name),
|
||||
|name| self.get_got_address(name),
|
||||
|name| self.get_plt_address(name),
|
||||
);
|
||||
}
|
||||
|
||||
for data in std::mem::take(&mut self.data_objects_to_finalize) {
|
||||
let decl = self.declarations.get_data_decl(data);
|
||||
debug_assert!(decl.linkage.is_definable());
|
||||
assert!(decl.linkage.is_definable());
|
||||
let data = self.compiled_data_objects[data]
|
||||
.as_ref()
|
||||
.expect("data object must be compiled before it can be finalized");
|
||||
data.perform_relocations(|name| self.get_definition(name));
|
||||
data.perform_relocations(
|
||||
|name| self.get_address(name),
|
||||
|name| self.get_got_address(name),
|
||||
|name| self.get_plt_address(name),
|
||||
);
|
||||
}
|
||||
|
||||
// Now that we're done patching, prepare the memory for execution!
|
||||
@@ -275,29 +369,111 @@ impl SimpleJITModule {
|
||||
self.memory.code.set_readable_and_executable();
|
||||
}
|
||||
|
||||
/// Create a new `SimpleJITModule`.
|
||||
pub fn new(builder: SimpleJITBuilder) -> Self {
|
||||
let memory = MemoryHandle {
|
||||
/// Create a new `JITModule`.
|
||||
pub fn new(builder: JITBuilder) -> Self {
|
||||
if builder.hotswap_enabled {
|
||||
assert!(
|
||||
builder.isa.flags().is_pic(),
|
||||
"Hotswapping requires PIC code"
|
||||
);
|
||||
}
|
||||
|
||||
let mut memory = MemoryHandle {
|
||||
code: Memory::new(),
|
||||
readonly: Memory::new(),
|
||||
writable: Memory::new(),
|
||||
};
|
||||
|
||||
let mut libcall_got_entries = HashMap::new();
|
||||
let mut libcall_plt_entries = HashMap::new();
|
||||
|
||||
// Pre-create a GOT and PLT entry for each libcall.
|
||||
let all_libcalls = if builder.isa.flags().is_pic() {
|
||||
ir::LibCall::all_libcalls()
|
||||
} else {
|
||||
&[] // Not PIC, so no GOT and PLT entries necessary
|
||||
};
|
||||
for &libcall in all_libcalls {
|
||||
let got_entry = memory
|
||||
.writable
|
||||
.allocate(
|
||||
std::mem::size_of::<*const u8>(),
|
||||
std::mem::align_of::<*const u8>().try_into().unwrap(),
|
||||
)
|
||||
.unwrap()
|
||||
.cast::<*const u8>();
|
||||
libcall_got_entries.insert(libcall, NonNull::new(got_entry).unwrap());
|
||||
let sym = (builder.libcall_names)(libcall);
|
||||
let addr = if let Some(addr) = builder
|
||||
.symbols
|
||||
.get(&sym)
|
||||
.copied()
|
||||
.or_else(|| lookup_with_dlsym(&sym))
|
||||
{
|
||||
addr
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
unsafe {
|
||||
std::ptr::write(got_entry, addr);
|
||||
}
|
||||
let plt_entry = memory
|
||||
.code
|
||||
.allocate(std::mem::size_of::<[u8; 16]>(), EXECUTABLE_DATA_ALIGNMENT)
|
||||
.unwrap()
|
||||
.cast::<[u8; 16]>();
|
||||
libcall_plt_entries.insert(libcall, NonNull::new(plt_entry).unwrap());
|
||||
unsafe {
|
||||
Self::write_plt_entry_bytes(plt_entry, got_entry);
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
isa: builder.isa,
|
||||
hotswap_enabled: builder.hotswap_enabled,
|
||||
symbols: builder.symbols,
|
||||
libcall_names: builder.libcall_names,
|
||||
memory,
|
||||
declarations: ModuleDeclarations::default(),
|
||||
function_got_entries: SecondaryMap::new(),
|
||||
function_plt_entries: SecondaryMap::new(),
|
||||
data_object_got_entries: SecondaryMap::new(),
|
||||
libcall_got_entries,
|
||||
libcall_plt_entries,
|
||||
compiled_functions: SecondaryMap::new(),
|
||||
compiled_data_objects: SecondaryMap::new(),
|
||||
functions_to_finalize: Vec::new(),
|
||||
data_objects_to_finalize: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Allow a single future `define_function` on a previously defined function. This allows for
|
||||
/// hot code swapping and lazy compilation of functions.
|
||||
///
|
||||
/// This requires hotswap support to be enabled first using [`JITBuilder::hotswap`].
|
||||
pub fn prepare_for_function_redefine(&mut self, func_id: FuncId) -> ModuleResult<()> {
|
||||
assert!(self.hotswap_enabled, "Hotswap support is not enabled");
|
||||
let decl = self.declarations.get_function_decl(func_id);
|
||||
if !decl.linkage.is_definable() {
|
||||
return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
|
||||
}
|
||||
|
||||
if self.compiled_functions[func_id].is_none() {
|
||||
return Err(ModuleError::Backend(anyhow::anyhow!(
|
||||
"Tried to redefine not yet defined function {}",
|
||||
decl.name
|
||||
)));
|
||||
}
|
||||
|
||||
self.compiled_functions[func_id] = None;
|
||||
|
||||
// FIXME return some kind of handle that allows for deallocating the function
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'simple_jit_backend> Module for SimpleJITModule {
|
||||
impl Module for JITModule {
|
||||
fn isa(&self) -> &dyn TargetIsa {
|
||||
&*self.isa
|
||||
}
|
||||
@@ -315,6 +491,38 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
|
||||
let (id, _decl) = self
|
||||
.declarations
|
||||
.declare_function(name, linkage, signature)?;
|
||||
if self.function_got_entries[id].is_none() && self.isa.flags().is_pic() {
|
||||
let got_entry = self
|
||||
.memory
|
||||
.writable
|
||||
.allocate(
|
||||
std::mem::size_of::<*const u8>(),
|
||||
std::mem::align_of::<*const u8>().try_into().unwrap(),
|
||||
)
|
||||
.unwrap()
|
||||
.cast::<*const u8>();
|
||||
self.function_got_entries[id] = Some(NonNull::new(got_entry).unwrap());
|
||||
// FIXME populate got entries with a null pointer when defined
|
||||
let val = self.lookup_symbol(name).unwrap_or(std::ptr::null());
|
||||
unsafe {
|
||||
std::ptr::write(got_entry, val);
|
||||
}
|
||||
let plt_entry = self
|
||||
.memory
|
||||
.code
|
||||
.allocate(std::mem::size_of::<[u8; 16]>(), EXECUTABLE_DATA_ALIGNMENT)
|
||||
.unwrap()
|
||||
.cast::<[u8; 16]>();
|
||||
self.record_function_for_perf(
|
||||
plt_entry as *mut _,
|
||||
std::mem::size_of::<[u8; 16]>(),
|
||||
&format!("{}@plt", name),
|
||||
);
|
||||
self.function_plt_entries[id] = Some(NonNull::new(plt_entry).unwrap());
|
||||
unsafe {
|
||||
Self::write_plt_entry_bytes(plt_entry, got_entry);
|
||||
}
|
||||
}
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
@@ -325,13 +533,69 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
|
||||
writable: bool,
|
||||
tls: bool,
|
||||
) -> ModuleResult<DataId> {
|
||||
assert!(!tls, "SimpleJIT doesn't yet support TLS");
|
||||
assert!(!tls, "JIT doesn't yet support TLS");
|
||||
let (id, _decl) = self
|
||||
.declarations
|
||||
.declare_data(name, linkage, writable, tls)?;
|
||||
if self.data_object_got_entries[id].is_none() && self.isa.flags().is_pic() {
|
||||
let got_entry = self
|
||||
.memory
|
||||
.writable
|
||||
.allocate(
|
||||
std::mem::size_of::<*const u8>(),
|
||||
std::mem::align_of::<*const u8>().try_into().unwrap(),
|
||||
)
|
||||
.unwrap()
|
||||
.cast::<*const u8>();
|
||||
self.data_object_got_entries[id] = Some(NonNull::new(got_entry).unwrap());
|
||||
// FIXME populate got entries with a null pointer when defined
|
||||
let val = self.lookup_symbol(name).unwrap_or(std::ptr::null());
|
||||
unsafe {
|
||||
std::ptr::write(got_entry, val);
|
||||
}
|
||||
}
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
/// Use this when you're building the IR of a function to reference a function.
|
||||
///
|
||||
/// TODO: Coalesce redundant decls and signatures.
|
||||
/// TODO: Look into ways to reduce the risk of using a FuncRef in the wrong function.
|
||||
fn declare_func_in_func(&self, func: FuncId, in_func: &mut ir::Function) -> ir::FuncRef {
|
||||
let decl = self.declarations.get_function_decl(func);
|
||||
let signature = in_func.import_signature(decl.signature.clone());
|
||||
let colocated = !self.hotswap_enabled && decl.linkage.is_final();
|
||||
in_func.import_function(ir::ExtFuncData {
|
||||
name: ir::ExternalName::user(0, func.as_u32()),
|
||||
signature,
|
||||
colocated,
|
||||
})
|
||||
}
|
||||
|
||||
/// Use this when you're building the IR of a function to reference a data object.
|
||||
///
|
||||
/// TODO: Same as above.
|
||||
fn declare_data_in_func(&self, data: DataId, func: &mut ir::Function) -> ir::GlobalValue {
|
||||
let decl = self.declarations.get_data_decl(data);
|
||||
let colocated = !self.hotswap_enabled && decl.linkage.is_final();
|
||||
func.create_global_value(ir::GlobalValueData::Symbol {
|
||||
name: ir::ExternalName::user(1, data.as_u32()),
|
||||
offset: ir::immediates::Imm64::new(0),
|
||||
colocated,
|
||||
tls: decl.tls,
|
||||
})
|
||||
}
|
||||
|
||||
/// TODO: Same as above.
|
||||
fn declare_func_in_data(&self, func: FuncId, ctx: &mut DataContext) -> ir::FuncRef {
|
||||
ctx.import_function(ir::ExternalName::user(0, func.as_u32()))
|
||||
}
|
||||
|
||||
/// TODO: Same as above.
|
||||
fn declare_data_in_data(&self, data: DataId, ctx: &mut DataContext) -> ir::GlobalValue {
|
||||
ctx.import_global_value(ir::ExternalName::user(1, data.as_u32()))
|
||||
}
|
||||
|
||||
fn define_function<TS>(
|
||||
&mut self,
|
||||
id: FuncId,
|
||||
@@ -363,7 +627,7 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
|
||||
.allocate(size, EXECUTABLE_DATA_ALIGNMENT)
|
||||
.expect("TODO: handle OOM etc.");
|
||||
|
||||
let mut reloc_sink = SimpleJITRelocSink::default();
|
||||
let mut reloc_sink = JITRelocSink::default();
|
||||
let mut stack_map_sink = binemit::NullStackMapSink {};
|
||||
unsafe {
|
||||
ctx.emit_to_memory(
|
||||
@@ -381,7 +645,36 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
|
||||
size,
|
||||
relocs: reloc_sink.relocs,
|
||||
});
|
||||
self.functions_to_finalize.push(id);
|
||||
|
||||
if self.isa.flags().is_pic() {
|
||||
unsafe {
|
||||
std::ptr::write(self.function_got_entries[id].unwrap().as_ptr(), ptr);
|
||||
}
|
||||
}
|
||||
|
||||
if self.hotswap_enabled {
|
||||
self.compiled_functions[id]
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.perform_relocations(
|
||||
|name| match *name {
|
||||
ir::ExternalName::User { .. } => {
|
||||
unreachable!("non GOT or PLT relocation in function {} to {}", id, name)
|
||||
}
|
||||
ir::ExternalName::LibCall(ref libcall) => self
|
||||
.libcall_plt_entries
|
||||
.get(libcall)
|
||||
.unwrap_or_else(|| panic!("can't resolve libcall {}", libcall))
|
||||
.as_ptr()
|
||||
.cast::<u8>(),
|
||||
_ => panic!("invalid ExternalName {}", name),
|
||||
},
|
||||
|name| self.get_got_address(name),
|
||||
|name| self.get_plt_address(name),
|
||||
);
|
||||
} else {
|
||||
self.functions_to_finalize.push(id);
|
||||
}
|
||||
|
||||
Ok(ModuleCompiledFunction { size: code_size })
|
||||
}
|
||||
@@ -424,7 +717,25 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
|
||||
size,
|
||||
relocs: relocs.to_vec(),
|
||||
});
|
||||
self.functions_to_finalize.push(id);
|
||||
|
||||
if self.isa.flags().is_pic() {
|
||||
unsafe {
|
||||
std::ptr::write(self.function_got_entries[id].unwrap().as_ptr(), ptr);
|
||||
}
|
||||
}
|
||||
|
||||
if self.hotswap_enabled {
|
||||
self.compiled_functions[id]
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.perform_relocations(
|
||||
|name| unreachable!("non GOT or PLT relocation in function {} to {}", id, name),
|
||||
|name| self.get_got_address(name),
|
||||
|name| self.get_plt_address(name),
|
||||
);
|
||||
} else {
|
||||
self.functions_to_finalize.push(id);
|
||||
}
|
||||
|
||||
Ok(ModuleCompiledFunction { size: total_size })
|
||||
}
|
||||
@@ -439,7 +750,7 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
|
||||
return Err(ModuleError::DuplicateDefinition(decl.name.to_owned()));
|
||||
}
|
||||
|
||||
assert!(!decl.tls, "SimpleJIT doesn't yet support TLS");
|
||||
assert!(!decl.tls, "JIT doesn't yet support TLS");
|
||||
|
||||
let &DataDescription {
|
||||
ref init,
|
||||
@@ -489,6 +800,11 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
|
||||
|
||||
self.compiled_data_objects[id] = Some(CompiledBlob { ptr, size, relocs });
|
||||
self.data_objects_to_finalize.push(id);
|
||||
if self.isa.flags().is_pic() {
|
||||
unsafe {
|
||||
std::ptr::write(self.data_object_got_entries[id].unwrap().as_ptr(), ptr);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -534,11 +850,11 @@ fn lookup_with_dlsym(name: &str) -> Option<*const u8> {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct SimpleJITRelocSink {
|
||||
struct JITRelocSink {
|
||||
relocs: Vec<RelocRecord>,
|
||||
}
|
||||
|
||||
impl RelocSink for SimpleJITRelocSink {
|
||||
impl RelocSink for JITRelocSink {
|
||||
fn reloc_external(
|
||||
&mut self,
|
||||
offset: CodeOffset,
|
||||
79
cranelift/jit/src/compiled_blob.rs
Normal file
79
cranelift/jit/src/compiled_blob.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use cranelift_codegen::binemit::Reloc;
|
||||
use cranelift_codegen::ir::ExternalName;
|
||||
use cranelift_module::RelocRecord;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct CompiledBlob {
|
||||
pub(crate) ptr: *mut u8,
|
||||
pub(crate) size: usize,
|
||||
pub(crate) relocs: Vec<RelocRecord>,
|
||||
}
|
||||
|
||||
impl CompiledBlob {
|
||||
pub(crate) fn perform_relocations(
|
||||
&self,
|
||||
get_address: impl Fn(&ExternalName) -> *const u8,
|
||||
get_got_entry: impl Fn(&ExternalName) -> *const u8,
|
||||
get_plt_entry: impl Fn(&ExternalName) -> *const u8,
|
||||
) {
|
||||
use std::ptr::write_unaligned;
|
||||
|
||||
for &RelocRecord {
|
||||
reloc,
|
||||
offset,
|
||||
ref name,
|
||||
addend,
|
||||
} in &self.relocs
|
||||
{
|
||||
debug_assert!((offset as usize) < self.size);
|
||||
let at = unsafe { self.ptr.offset(isize::try_from(offset).unwrap()) };
|
||||
match reloc {
|
||||
Reloc::Abs4 => {
|
||||
let base = get_address(name);
|
||||
let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
|
||||
unsafe {
|
||||
write_unaligned(at as *mut u32, u32::try_from(what as usize).unwrap())
|
||||
};
|
||||
}
|
||||
Reloc::Abs8 => {
|
||||
let base = get_address(name);
|
||||
let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
|
||||
unsafe {
|
||||
write_unaligned(at as *mut u64, u64::try_from(what as usize).unwrap())
|
||||
};
|
||||
}
|
||||
Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => {
|
||||
let base = get_address(name);
|
||||
let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };
|
||||
let pcrel = i32::try_from((what as isize) - (at as isize)).unwrap();
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
|
||||
unsafe {
|
||||
write_unaligned(at as *mut i32, pcrel)
|
||||
};
|
||||
}
|
||||
Reloc::X86GOTPCRel4 => {
|
||||
let base = get_got_entry(name);
|
||||
let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };
|
||||
let pcrel = i32::try_from((what as isize) - (at as isize)).unwrap();
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
|
||||
unsafe {
|
||||
write_unaligned(at as *mut i32, pcrel)
|
||||
};
|
||||
}
|
||||
Reloc::X86CallPLTRel4 => {
|
||||
let base = get_plt_entry(name);
|
||||
let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };
|
||||
let pcrel = i32::try_from((what as isize) - (at as isize)).unwrap();
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
|
||||
unsafe {
|
||||
write_unaligned(at as *mut i32, pcrel)
|
||||
};
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Top-level lib.rs for `cranelift_simplejit`.
|
||||
//! Top-level lib.rs for `cranelift_jit`.
|
||||
|
||||
#![deny(
|
||||
missing_docs,
|
||||
@@ -27,7 +27,7 @@ mod backend;
|
||||
mod compiled_blob;
|
||||
mod memory;
|
||||
|
||||
pub use crate::backend::{SimpleJITBuilder, SimpleJITModule};
|
||||
pub use crate::backend::{JITBuilder, JITModule};
|
||||
|
||||
/// Version number of this crate.
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
@@ -1,16 +1,25 @@
|
||||
use cranelift_codegen::binemit::NullTrapSink;
|
||||
use cranelift_codegen::ir::*;
|
||||
use cranelift_codegen::isa::CallConv;
|
||||
use cranelift_codegen::settings::{self, Configurable};
|
||||
use cranelift_codegen::{ir::types::I16, Context};
|
||||
use cranelift_entity::EntityRef;
|
||||
use cranelift_frontend::*;
|
||||
use cranelift_jit::*;
|
||||
use cranelift_module::*;
|
||||
use cranelift_simplejit::*;
|
||||
|
||||
#[test]
|
||||
fn error_on_incompatible_sig_in_declare_function() {
|
||||
let mut module: SimpleJITModule =
|
||||
SimpleJITModule::new(SimpleJITBuilder::new(default_libcall_names()));
|
||||
let mut flag_builder = settings::builder();
|
||||
flag_builder.set("use_colocated_libcalls", "false").unwrap();
|
||||
// FIXME set back to true once the x64 backend supports it.
|
||||
flag_builder.set("is_pic", "false").unwrap();
|
||||
let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| {
|
||||
panic!("host machine is not supported: {}", msg);
|
||||
});
|
||||
let isa = isa_builder.finish(settings::Flags::new(flag_builder));
|
||||
let mut module = JITModule::new(JITBuilder::with_isa(isa, default_libcall_names()));
|
||||
|
||||
let mut sig = Signature {
|
||||
params: vec![AbiParam::new(types::I64)],
|
||||
returns: vec![],
|
||||
@@ -26,7 +35,7 @@ fn error_on_incompatible_sig_in_declare_function() {
|
||||
.unwrap(); // Make sure this is an error
|
||||
}
|
||||
|
||||
fn define_simple_function(module: &mut SimpleJITModule) -> FuncId {
|
||||
fn define_simple_function(module: &mut JITModule) -> FuncId {
|
||||
let sig = Signature {
|
||||
params: vec![],
|
||||
returns: vec![],
|
||||
@@ -58,8 +67,15 @@ fn define_simple_function(module: &mut SimpleJITModule) -> FuncId {
|
||||
#[test]
|
||||
#[should_panic(expected = "Result::unwrap()` on an `Err` value: DuplicateDefinition(\"abc\")")]
|
||||
fn panic_on_define_after_finalize() {
|
||||
let mut module: SimpleJITModule =
|
||||
SimpleJITModule::new(SimpleJITBuilder::new(default_libcall_names()));
|
||||
let mut flag_builder = settings::builder();
|
||||
flag_builder.set("use_colocated_libcalls", "false").unwrap();
|
||||
// FIXME set back to true once the x64 backend supports it.
|
||||
flag_builder.set("is_pic", "false").unwrap();
|
||||
let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| {
|
||||
panic!("host machine is not supported: {}", msg);
|
||||
});
|
||||
let isa = isa_builder.finish(settings::Flags::new(flag_builder));
|
||||
let mut module = JITModule::new(JITBuilder::with_isa(isa, default_libcall_names()));
|
||||
|
||||
define_simple_function(&mut module);
|
||||
define_simple_function(&mut module);
|
||||
@@ -140,8 +156,15 @@ fn switch_error() {
|
||||
|
||||
#[test]
|
||||
fn libcall_function() {
|
||||
let mut module: SimpleJITModule =
|
||||
SimpleJITModule::new(SimpleJITBuilder::new(default_libcall_names()));
|
||||
let mut flag_builder = settings::builder();
|
||||
flag_builder.set("use_colocated_libcalls", "false").unwrap();
|
||||
// FIXME set back to true once the x64 backend supports it.
|
||||
flag_builder.set("is_pic", "false").unwrap();
|
||||
let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| {
|
||||
panic!("host machine is not supported: {}", msg);
|
||||
});
|
||||
let isa = isa_builder.finish(settings::Flags::new(flag_builder));
|
||||
let mut module = JITModule::new(JITBuilder::with_isa(isa, default_libcall_names()));
|
||||
|
||||
let sig = Signature {
|
||||
params: vec![],
|
||||
@@ -10,10 +10,8 @@ A module is a collection of functions and data objects that are linked
|
||||
together. The `Module` trait that defines a common interface for various kinds
|
||||
of modules. Most users will use one of the following `Module` implementations:
|
||||
|
||||
- `SimpleJITModule`, provided by [cranelift-simplejit], which JITs
|
||||
code to memory for direct execution.
|
||||
- `ObjectModule`, provided by [cranelift-object], which emits native
|
||||
object files.
|
||||
- `JITModule`, provided by [cranelift-jit], which JITs code to memory for direct execution.
|
||||
- `ObjectModule`, provided by [cranelift-object], which emits native object files.
|
||||
|
||||
[cranelift-simplejit]: https://crates.io/crates/cranelift-simplejit
|
||||
[cranelift-jit]: https://crates.io/crates/cranelift-jit
|
||||
[cranelift-object]: https://crates.io/crates/cranelift-object
|
||||
|
||||
@@ -490,3 +490,97 @@ pub trait Module {
|
||||
/// Define a data object, producing the data contents from the given `DataContext`.
|
||||
fn define_data(&mut self, data: DataId, data_ctx: &DataContext) -> ModuleResult<()>;
|
||||
}
|
||||
|
||||
impl<M: Module> Module for &mut M {
|
||||
fn isa(&self) -> &dyn isa::TargetIsa {
|
||||
(**self).isa()
|
||||
}
|
||||
|
||||
fn declarations(&self) -> &ModuleDeclarations {
|
||||
(**self).declarations()
|
||||
}
|
||||
|
||||
fn get_name(&self, name: &str) -> Option<FuncOrDataId> {
|
||||
(**self).get_name(name)
|
||||
}
|
||||
|
||||
fn target_config(&self) -> isa::TargetFrontendConfig {
|
||||
(**self).target_config()
|
||||
}
|
||||
|
||||
fn make_context(&self) -> Context {
|
||||
(**self).make_context()
|
||||
}
|
||||
|
||||
fn clear_context(&self, ctx: &mut Context) {
|
||||
(**self).clear_context(ctx)
|
||||
}
|
||||
|
||||
fn make_signature(&self) -> ir::Signature {
|
||||
(**self).make_signature()
|
||||
}
|
||||
|
||||
fn clear_signature(&self, sig: &mut ir::Signature) {
|
||||
(**self).clear_signature(sig)
|
||||
}
|
||||
|
||||
fn declare_function(
|
||||
&mut self,
|
||||
name: &str,
|
||||
linkage: Linkage,
|
||||
signature: &ir::Signature,
|
||||
) -> ModuleResult<FuncId> {
|
||||
(**self).declare_function(name, linkage, signature)
|
||||
}
|
||||
|
||||
fn declare_data(
|
||||
&mut self,
|
||||
name: &str,
|
||||
linkage: Linkage,
|
||||
writable: bool,
|
||||
tls: bool,
|
||||
) -> ModuleResult<DataId> {
|
||||
(**self).declare_data(name, linkage, writable, tls)
|
||||
}
|
||||
|
||||
fn declare_func_in_func(&self, func: FuncId, in_func: &mut ir::Function) -> ir::FuncRef {
|
||||
(**self).declare_func_in_func(func, in_func)
|
||||
}
|
||||
|
||||
fn declare_data_in_func(&self, data: DataId, func: &mut ir::Function) -> ir::GlobalValue {
|
||||
(**self).declare_data_in_func(data, func)
|
||||
}
|
||||
|
||||
fn declare_func_in_data(&self, func: FuncId, ctx: &mut DataContext) -> ir::FuncRef {
|
||||
(**self).declare_func_in_data(func, ctx)
|
||||
}
|
||||
|
||||
fn declare_data_in_data(&self, data: DataId, ctx: &mut DataContext) -> ir::GlobalValue {
|
||||
(**self).declare_data_in_data(data, ctx)
|
||||
}
|
||||
|
||||
fn define_function<TS>(
|
||||
&mut self,
|
||||
func: FuncId,
|
||||
ctx: &mut Context,
|
||||
trap_sink: &mut TS,
|
||||
) -> ModuleResult<ModuleCompiledFunction>
|
||||
where
|
||||
TS: binemit::TrapSink,
|
||||
{
|
||||
(**self).define_function(func, ctx, trap_sink)
|
||||
}
|
||||
|
||||
fn define_function_bytes(
|
||||
&mut self,
|
||||
func: FuncId,
|
||||
bytes: &[u8],
|
||||
relocs: &[RelocRecord],
|
||||
) -> ModuleResult<ModuleCompiledFunction> {
|
||||
(**self).define_function_bytes(func, bytes, relocs)
|
||||
}
|
||||
|
||||
fn define_data(&mut self, data: DataId, data_ctx: &DataContext) -> ModuleResult<()> {
|
||||
(**self).define_data(data, data_ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ peepmatic-macro = { version = "0.68.0", path = "crates/macro" }
|
||||
peepmatic-runtime = { version = "0.68.0", path = "crates/runtime", features = ["construct"] }
|
||||
peepmatic-traits = { version = "0.68.0", path = "crates/traits" }
|
||||
serde = { version = "1.0.105", features = ["derive"] }
|
||||
wast = "27.0.0"
|
||||
wast = "29.0.0"
|
||||
z3 = { version = "0.7.1", features = ["static-link-z3"] }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -21,4 +21,4 @@ peepmatic-test-operator = { path = "../test-operator" }
|
||||
peepmatic-traits = { path = "../traits" }
|
||||
rand = { version = "0.7.3", features = ["small_rng"] }
|
||||
serde = "1.0.106"
|
||||
wast = "27.0.0"
|
||||
wast = "29.0.0"
|
||||
|
||||
@@ -16,7 +16,7 @@ peepmatic-automata = { version = "0.68.0", path = "../automata", features = ["se
|
||||
peepmatic-traits = { version = "0.68.0", path = "../traits" }
|
||||
serde = { version = "1.0.105", features = ["derive"] }
|
||||
thiserror = "1.0.15"
|
||||
wast = { version = "27.0.0", optional = true }
|
||||
wast = { version = "29.0.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
peepmatic-test-operator = { version = "0.68.0", path = "../test-operator" }
|
||||
|
||||
@@ -10,10 +10,10 @@ description = "Converting Souper optimizations into Peepmatic DSL"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
souper-ir = { version = "1", features = ["parse"] }
|
||||
souper-ir = { version = "2.1.0", features = ["parse"] }
|
||||
log = "0.4.8"
|
||||
|
||||
[dev-dependencies]
|
||||
peepmatic = { path = "../..", version = "0.68.0" }
|
||||
peepmatic-test-operator = { version = "0.68.0", path = "../test-operator" }
|
||||
wast = "27.0.0"
|
||||
wast = "29.0.0"
|
||||
|
||||
@@ -174,7 +174,6 @@ fn convert_operand(
|
||||
}
|
||||
Some(format!("{}", convert_name(&assn.name)))
|
||||
}
|
||||
ast::AssignmentRhs::Constant(c) => Some(format!("{}", c.value)),
|
||||
ast::AssignmentRhs::Instruction(inst) => match inst {
|
||||
// Unsupported instructions.
|
||||
ast::Instruction::Bswap { .. }
|
||||
@@ -619,8 +618,7 @@ mod tests {
|
||||
"
|
||||
%0:i64 = var
|
||||
%1:i32 = trunc %0
|
||||
%2:i32 = 0
|
||||
cand %1 %2
|
||||
cand %1 0
|
||||
",
|
||||
"\
|
||||
(=> (when (ireduce {i32} $v0)
|
||||
@@ -631,8 +629,7 @@ mod tests {
|
||||
"
|
||||
%0:i32 = var
|
||||
%1:i64 = sext %0
|
||||
%2:i64 = 0
|
||||
cand %1 %2
|
||||
cand %1 0
|
||||
",
|
||||
"\
|
||||
(=> (when (sextend {i64} $v0)
|
||||
@@ -643,8 +640,7 @@ mod tests {
|
||||
"
|
||||
%0:i32 = var
|
||||
%1:i64 = zext %0
|
||||
%2:i64 = 0
|
||||
cand %1 %2
|
||||
cand %1 0
|
||||
",
|
||||
"\
|
||||
(=> (when (uextend {i64} $v0)
|
||||
@@ -677,8 +673,7 @@ mod tests {
|
||||
%1:i32 = var
|
||||
%2:i1 = eq %0, %1
|
||||
%3:i32 = zext %2
|
||||
%4:i32 = 0
|
||||
cand %3 %4
|
||||
cand %3 0
|
||||
",
|
||||
"\
|
||||
(=> (when (bint (icmp eq $v0 $v1))
|
||||
@@ -693,8 +688,7 @@ mod tests {
|
||||
"
|
||||
%0:i32 = var
|
||||
%1:i32 = add %0, 1
|
||||
%2:i32 = 0
|
||||
cand %1 %2
|
||||
cand %1 0
|
||||
",
|
||||
"\
|
||||
(=> (when (iadd_imm 1 $v0)
|
||||
@@ -705,8 +699,7 @@ mod tests {
|
||||
"
|
||||
%0:i32 = var
|
||||
%1:i32 = add 1, %0
|
||||
%2:i32 = 0
|
||||
cand %1 %2
|
||||
cand %1 0
|
||||
",
|
||||
"\
|
||||
(=> (when (iadd_imm 1 $v0)
|
||||
|
||||
@@ -9,4 +9,4 @@ edition = "2018"
|
||||
[dependencies]
|
||||
peepmatic-traits = { version = "0.68.0", path = "../traits" }
|
||||
serde = { version = "1.0.105", features = ["derive"] }
|
||||
wast = "27.0.0"
|
||||
wast = "29.0.0"
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
use cranelift_codegen::binemit::Reloc;
|
||||
use cranelift_codegen::ir::ExternalName;
|
||||
use cranelift_module::RelocRecord;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct CompiledBlob {
|
||||
pub(crate) ptr: *mut u8,
|
||||
pub(crate) size: usize,
|
||||
pub(crate) relocs: Vec<RelocRecord>,
|
||||
}
|
||||
|
||||
impl CompiledBlob {
|
||||
pub(crate) fn perform_relocations(&self, get_definition: impl Fn(&ExternalName) -> *const u8) {
|
||||
use std::ptr::write_unaligned;
|
||||
|
||||
for &RelocRecord {
|
||||
reloc,
|
||||
offset,
|
||||
ref name,
|
||||
addend,
|
||||
} in &self.relocs
|
||||
{
|
||||
debug_assert!((offset as usize) < self.size);
|
||||
let at = unsafe { self.ptr.offset(offset as isize) };
|
||||
let base = get_definition(name);
|
||||
// TODO: Handle overflow.
|
||||
let what = unsafe { base.offset(addend as isize) };
|
||||
match reloc {
|
||||
Reloc::Abs4 => {
|
||||
// TODO: Handle overflow.
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
|
||||
unsafe {
|
||||
write_unaligned(at as *mut u32, what as u32)
|
||||
};
|
||||
}
|
||||
Reloc::Abs8 => {
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
|
||||
unsafe {
|
||||
write_unaligned(at as *mut u64, what as u64)
|
||||
};
|
||||
}
|
||||
Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => {
|
||||
// TODO: Handle overflow.
|
||||
let pcrel = ((what as isize) - (at as isize)) as i32;
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
|
||||
unsafe {
|
||||
write_unaligned(at as *mut i32, pcrel)
|
||||
};
|
||||
}
|
||||
Reloc::X86GOTPCRel4 | Reloc::X86CallPLTRel4 => panic!("unexpected PIC relocation"),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ keywords = ["webassembly", "wasm"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmparser = { version = "0.68.0", default-features = false }
|
||||
wasmparser = { version = "0.70", default-features = false }
|
||||
cranelift-codegen = { path = "../codegen", version = "0.68.0", default-features = false }
|
||||
cranelift-entity = { path = "../entity", version = "0.68.0" }
|
||||
cranelift-frontend = { path = "../frontend", version = "0.68.0", default-features = false }
|
||||
|
||||
@@ -538,10 +538,10 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
}
|
||||
/********************************** Exception handing **********************************/
|
||||
Operator::Try { .. }
|
||||
| Operator::Catch
|
||||
| Operator::BrOnExn { .. }
|
||||
| Operator::Catch { .. }
|
||||
| Operator::Throw { .. }
|
||||
| Operator::Rethrow => {
|
||||
| Operator::Unwind
|
||||
| Operator::Rethrow { .. } => {
|
||||
return Err(wasm_unsupported!(
|
||||
"proposed exception handling operator {:?}",
|
||||
op
|
||||
@@ -2056,7 +2056,9 @@ fn prepare_load<FE: FuncEnvironment + ?Sized>(
|
||||
// Note that we don't set `is_aligned` here, even if the load instruction's
|
||||
// alignment immediate says it's aligned, because WebAssembly's immediate
|
||||
// field is just a hint, while Cranelift's aligned flag needs a guarantee.
|
||||
let flags = MemFlags::new();
|
||||
// WebAssembly memory accesses are always little-endian.
|
||||
let mut flags = MemFlags::new();
|
||||
flags.set_endianness(ir::Endianness::Little);
|
||||
|
||||
Ok((flags, base, offset.into()))
|
||||
}
|
||||
@@ -2103,7 +2105,8 @@ fn translate_store<FE: FuncEnvironment + ?Sized>(
|
||||
builder,
|
||||
);
|
||||
// See the comments in `prepare_load` about the flags.
|
||||
let flags = MemFlags::new();
|
||||
let mut flags = MemFlags::new();
|
||||
flags.set_endianness(ir::Endianness::Little);
|
||||
builder
|
||||
.ins()
|
||||
.Store(opcode, val_ty, flags, offset.into(), val, base);
|
||||
@@ -2207,7 +2210,8 @@ fn translate_atomic_rmw<FE: FuncEnvironment + ?Sized>(
|
||||
finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?;
|
||||
|
||||
// See the comments in `prepare_load` about the flags.
|
||||
let flags = MemFlags::new();
|
||||
let mut flags = MemFlags::new();
|
||||
flags.set_endianness(ir::Endianness::Little);
|
||||
let mut res = builder
|
||||
.ins()
|
||||
.atomic_rmw(access_ty, flags, op, final_effective_address, arg2);
|
||||
@@ -2260,7 +2264,8 @@ fn translate_atomic_cas<FE: FuncEnvironment + ?Sized>(
|
||||
finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?;
|
||||
|
||||
// See the comments in `prepare_load` about the flags.
|
||||
let flags = MemFlags::new();
|
||||
let mut flags = MemFlags::new();
|
||||
flags.set_endianness(ir::Endianness::Little);
|
||||
let mut res = builder
|
||||
.ins()
|
||||
.atomic_cas(flags, final_effective_address, expected, replacement);
|
||||
@@ -2302,7 +2307,8 @@ fn translate_atomic_load<FE: FuncEnvironment + ?Sized>(
|
||||
finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?;
|
||||
|
||||
// See the comments in `prepare_load` about the flags.
|
||||
let flags = MemFlags::new();
|
||||
let mut flags = MemFlags::new();
|
||||
flags.set_endianness(ir::Endianness::Little);
|
||||
let mut res = builder
|
||||
.ins()
|
||||
.atomic_load(access_ty, flags, final_effective_address);
|
||||
@@ -2348,7 +2354,8 @@ fn translate_atomic_store<FE: FuncEnvironment + ?Sized>(
|
||||
finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?;
|
||||
|
||||
// See the comments in `prepare_load` about the flags.
|
||||
let flags = MemFlags::new();
|
||||
let mut flags = MemFlags::new();
|
||||
flags.set_endianness(ir::Endianness::Little);
|
||||
builder
|
||||
.ins()
|
||||
.atomic_store(flags, data, final_effective_address);
|
||||
|
||||
@@ -6,6 +6,6 @@ mod spec;
|
||||
|
||||
pub use crate::environ::dummy::DummyEnvironment;
|
||||
pub use crate::environ::spec::{
|
||||
FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, TargetEnvironment, WasmError,
|
||||
WasmFuncType, WasmResult, WasmType,
|
||||
Alias, FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, TargetEnvironment,
|
||||
WasmError, WasmFuncType, WasmResult, WasmType,
|
||||
};
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
|
||||
use crate::state::FuncTranslationState;
|
||||
use crate::translation_utils::{
|
||||
DataIndex, ElemIndex, EntityType, Event, EventIndex, FuncIndex, Global, GlobalIndex, Memory,
|
||||
MemoryIndex, Table, TableIndex, TypeIndex,
|
||||
DataIndex, ElemIndex, EntityIndex, EntityType, Event, EventIndex, FuncIndex, Global,
|
||||
GlobalIndex, InstanceIndex, InstanceTypeIndex, Memory, MemoryIndex, ModuleIndex,
|
||||
ModuleTypeIndex, SignatureIndex, Table, TableIndex, TypeIndex,
|
||||
};
|
||||
use core::convert::From;
|
||||
use core::convert::TryFrom;
|
||||
@@ -22,6 +23,7 @@ use cranelift_frontend::FunctionBuilder;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::boxed::Box;
|
||||
use std::string::ToString;
|
||||
use std::vec::Vec;
|
||||
use thiserror::Error;
|
||||
use wasmparser::ValidatorResources;
|
||||
use wasmparser::{BinaryReaderError, FuncValidator, FunctionBody, Operator, WasmFeatures};
|
||||
@@ -201,6 +203,30 @@ pub enum ReturnMode {
|
||||
FallthroughReturn,
|
||||
}
|
||||
|
||||
/// An entry in the alias section of a wasm module (from the module linking
|
||||
/// proposal)
|
||||
pub enum Alias {
|
||||
/// A parent's module is being aliased into our own index space.
|
||||
///
|
||||
/// Note that the index here is in the parent's index space, not our own.
|
||||
ParentModule(ModuleIndex),
|
||||
|
||||
/// A parent's type is being aliased into our own index space
|
||||
///
|
||||
/// Note that the index here is in the parent's index space, not our own.
|
||||
ParentType(TypeIndex),
|
||||
|
||||
/// A previously created instance is having one of its exports aliased into
|
||||
/// our index space.
|
||||
Child {
|
||||
/// The index we're aliasing.
|
||||
instance: InstanceIndex,
|
||||
/// The nth export that we're inserting into our own index space
|
||||
/// locally.
|
||||
export: usize,
|
||||
},
|
||||
}
|
||||
|
||||
/// Environment affecting the translation of a WebAssembly.
|
||||
pub trait TargetEnvironment {
|
||||
/// Get the information needed to produce Cranelift IR for the given target.
|
||||
@@ -683,6 +709,27 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
|
||||
Err(WasmError::Unsupported("module linking".to_string()))
|
||||
}
|
||||
|
||||
/// Translates a type index to its signature index, only called for type
|
||||
/// indices which point to functions.
|
||||
fn type_to_signature(&self, index: TypeIndex) -> WasmResult<SignatureIndex> {
|
||||
drop(index);
|
||||
Err(WasmError::Unsupported("module linking".to_string()))
|
||||
}
|
||||
|
||||
/// Translates a type index to its module type index, only called for type
|
||||
/// indices which point to modules.
|
||||
fn type_to_module_type(&self, index: TypeIndex) -> WasmResult<ModuleTypeIndex> {
|
||||
drop(index);
|
||||
Err(WasmError::Unsupported("module linking".to_string()))
|
||||
}
|
||||
|
||||
/// Translates a type index to its instance type index, only called for type
|
||||
/// indices which point to instances.
|
||||
fn type_to_instance_type(&self, index: TypeIndex) -> WasmResult<InstanceTypeIndex> {
|
||||
drop(index);
|
||||
Err(WasmError::Unsupported("module linking".to_string()))
|
||||
}
|
||||
|
||||
/// Provides the number of imports up front. By default this does nothing, but
|
||||
/// implementations can use this to preallocate memory if desired.
|
||||
fn reserve_imports(&mut self, _num: u32) -> WasmResult<()> {
|
||||
@@ -844,6 +891,22 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
|
||||
name: &'data str,
|
||||
) -> WasmResult<()>;
|
||||
|
||||
/// Declares an instance export to the environment.
|
||||
fn declare_instance_export(
|
||||
&mut self,
|
||||
index: InstanceIndex,
|
||||
name: &'data str,
|
||||
) -> WasmResult<()> {
|
||||
drop((index, name));
|
||||
Err(WasmError::Unsupported("module linking".to_string()))
|
||||
}
|
||||
|
||||
/// Declares an instance export to the environment.
|
||||
fn declare_module_export(&mut self, index: ModuleIndex, name: &'data str) -> WasmResult<()> {
|
||||
drop((index, name));
|
||||
Err(WasmError::Unsupported("module linking".to_string()))
|
||||
}
|
||||
|
||||
/// Notifies the implementation that all exports have been declared.
|
||||
fn finish_exports(&mut self) -> WasmResult<()> {
|
||||
Ok(())
|
||||
@@ -951,6 +1014,12 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
|
||||
drop(amount);
|
||||
}
|
||||
|
||||
/// Declares that a module will come later with the type signature provided.
|
||||
fn declare_module(&mut self, ty: TypeIndex) -> WasmResult<()> {
|
||||
drop(ty);
|
||||
Err(WasmError::Unsupported("module linking".to_string()))
|
||||
}
|
||||
|
||||
/// Called at the beginning of translating a module.
|
||||
///
|
||||
/// The `index` argument is a monotonically increasing index which
|
||||
@@ -969,4 +1038,26 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
|
||||
fn module_end(&mut self, index: usize) {
|
||||
drop(index);
|
||||
}
|
||||
|
||||
/// Indicates that this module will have `amount` instances.
|
||||
fn reserve_instances(&mut self, amount: u32) {
|
||||
drop(amount);
|
||||
}
|
||||
|
||||
/// Declares a new instance which this module will instantiate before it's
|
||||
/// instantiated.
|
||||
fn declare_instance(&mut self, module: ModuleIndex, args: Vec<EntityIndex>) -> WasmResult<()> {
|
||||
drop((module, args));
|
||||
Err(WasmError::Unsupported("wasm instance".to_string()))
|
||||
}
|
||||
|
||||
/// Declares a new alias being added to this module.
|
||||
///
|
||||
/// The alias comes from the `instance` specified (or the parent if `None`
|
||||
/// is supplied) and the index is either in the module's own index spaces
|
||||
/// for the parent or an index into the exports for nested instances.
|
||||
fn declare_alias(&mut self, alias: Alias) -> WasmResult<()> {
|
||||
drop(alias);
|
||||
Err(WasmError::Unsupported("wasm alias".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ mod state;
|
||||
mod translation_utils;
|
||||
|
||||
pub use crate::environ::{
|
||||
DummyEnvironment, FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode,
|
||||
Alias, DummyEnvironment, FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode,
|
||||
TargetEnvironment, WasmError, WasmFuncType, WasmResult, WasmType,
|
||||
};
|
||||
pub use crate::func_translator::FuncTranslator;
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
//! to deal with each part of it.
|
||||
use crate::environ::{ModuleEnvironment, WasmResult};
|
||||
use crate::sections_translator::{
|
||||
parse_data_section, parse_element_section, parse_event_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,
|
||||
parse_alias_section, parse_data_section, parse_element_section, parse_event_section,
|
||||
parse_export_section, parse_function_section, parse_global_section, parse_import_section,
|
||||
parse_instance_section, parse_memory_section, parse_module_section, parse_name_section,
|
||||
parse_start_section, parse_table_section, parse_type_section,
|
||||
};
|
||||
use crate::state::ModuleTranslationState;
|
||||
use cranelift_codegen::timing;
|
||||
@@ -112,15 +113,15 @@ pub fn translate_module<'data>(
|
||||
|
||||
Payload::ModuleSection(s) => {
|
||||
validator.module_section(&s)?;
|
||||
environ.reserve_modules(s.get_count());
|
||||
parse_module_section(s, environ)?;
|
||||
}
|
||||
Payload::InstanceSection(s) => {
|
||||
validator.instance_section(&s)?;
|
||||
unimplemented!("module linking not implemented yet")
|
||||
parse_instance_section(s, environ)?;
|
||||
}
|
||||
Payload::AliasSection(s) => {
|
||||
validator.alias_section(&s)?;
|
||||
unimplemented!("module linking not implemented yet")
|
||||
parse_alias_section(s, environ)?;
|
||||
}
|
||||
Payload::ModuleCodeSectionStart {
|
||||
count,
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
//! 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 crate::environ::{ModuleEnvironment, WasmError, WasmResult};
|
||||
use crate::environ::{Alias, ModuleEnvironment, WasmError, WasmResult};
|
||||
use crate::state::ModuleTranslationState;
|
||||
use crate::translation_utils::{
|
||||
tabletype_to_type, type_to_type, DataIndex, ElemIndex, EntityType, Event, EventIndex,
|
||||
FuncIndex, Global, GlobalIndex, GlobalInit, Memory, MemoryIndex, Table, TableElementType,
|
||||
TableIndex, TypeIndex,
|
||||
tabletype_to_type, type_to_type, DataIndex, ElemIndex, EntityIndex, EntityType, Event,
|
||||
EventIndex, FuncIndex, Global, GlobalIndex, GlobalInit, InstanceIndex, Memory, MemoryIndex,
|
||||
ModuleIndex, Table, TableElementType, TableIndex, TypeIndex,
|
||||
};
|
||||
use crate::wasm_unsupported;
|
||||
use core::convert::TryFrom;
|
||||
@@ -36,9 +36,15 @@ fn entity_type(
|
||||
environ: &mut dyn ModuleEnvironment<'_>,
|
||||
) -> WasmResult<EntityType> {
|
||||
Ok(match ty {
|
||||
ImportSectionEntryType::Function(sig) => EntityType::Function(TypeIndex::from_u32(sig)),
|
||||
ImportSectionEntryType::Module(sig) => EntityType::Module(TypeIndex::from_u32(sig)),
|
||||
ImportSectionEntryType::Instance(sig) => EntityType::Instance(TypeIndex::from_u32(sig)),
|
||||
ImportSectionEntryType::Function(sig) => {
|
||||
EntityType::Function(environ.type_to_signature(TypeIndex::from_u32(sig))?)
|
||||
}
|
||||
ImportSectionEntryType::Module(sig) => {
|
||||
EntityType::Module(environ.type_to_module_type(TypeIndex::from_u32(sig))?)
|
||||
}
|
||||
ImportSectionEntryType::Instance(sig) => {
|
||||
EntityType::Instance(environ.type_to_instance_type(TypeIndex::from_u32(sig))?)
|
||||
}
|
||||
ImportSectionEntryType::Memory(ty) => EntityType::Memory(memory(ty)),
|
||||
ImportSectionEntryType::Event(evt) => EntityType::Event(event(evt)),
|
||||
ImportSectionEntryType::Global(ty) => {
|
||||
@@ -156,24 +162,40 @@ pub fn parse_import_section<'data>(
|
||||
|
||||
for entry in imports {
|
||||
let import = entry?;
|
||||
match entity_type(import.ty, environ)? {
|
||||
EntityType::Function(idx) => {
|
||||
environ.declare_func_import(idx, import.module, import.field)?;
|
||||
match import.ty {
|
||||
ImportSectionEntryType::Function(sig) => {
|
||||
environ.declare_func_import(
|
||||
TypeIndex::from_u32(sig),
|
||||
import.module,
|
||||
import.field,
|
||||
)?;
|
||||
}
|
||||
EntityType::Module(idx) => {
|
||||
environ.declare_module_import(idx, import.module, import.field)?;
|
||||
ImportSectionEntryType::Module(sig) => {
|
||||
environ.declare_module_import(
|
||||
TypeIndex::from_u32(sig),
|
||||
import.module,
|
||||
import.field,
|
||||
)?;
|
||||
}
|
||||
EntityType::Instance(idx) => {
|
||||
environ.declare_instance_import(idx, import.module, import.field)?;
|
||||
ImportSectionEntryType::Instance(sig) => {
|
||||
environ.declare_instance_import(
|
||||
TypeIndex::from_u32(sig),
|
||||
import.module,
|
||||
import.field,
|
||||
)?;
|
||||
}
|
||||
EntityType::Memory(ty) => {
|
||||
environ.declare_memory_import(ty, import.module, import.field)?;
|
||||
ImportSectionEntryType::Memory(ty) => {
|
||||
environ.declare_memory_import(memory(ty), import.module, import.field)?;
|
||||
}
|
||||
EntityType::Event(e) => environ.declare_event_import(e, import.module, import.field)?,
|
||||
EntityType::Global(ty) => {
|
||||
ImportSectionEntryType::Event(e) => {
|
||||
environ.declare_event_import(event(e), import.module, import.field)?;
|
||||
}
|
||||
ImportSectionEntryType::Global(ty) => {
|
||||
let ty = global(ty, environ, GlobalInit::Import)?;
|
||||
environ.declare_global_import(ty, import.module, import.field)?;
|
||||
}
|
||||
EntityType::Table(ty) => {
|
||||
ImportSectionEntryType::Table(ty) => {
|
||||
let ty = table(ty, environ)?;
|
||||
environ.declare_table_import(ty, import.module, import.field)?;
|
||||
}
|
||||
}
|
||||
@@ -316,9 +338,15 @@ pub fn parse_export_section<'data>(
|
||||
ExternalKind::Global => {
|
||||
environ.declare_global_export(GlobalIndex::new(index), field)?
|
||||
}
|
||||
ExternalKind::Type | ExternalKind::Module | ExternalKind::Instance => {
|
||||
unimplemented!("module linking not implemented yet")
|
||||
ExternalKind::Module => {
|
||||
environ.declare_module_export(ModuleIndex::new(index), field)?
|
||||
}
|
||||
ExternalKind::Instance => {
|
||||
environ.declare_instance_export(InstanceIndex::new(index), field)?
|
||||
}
|
||||
|
||||
// this never gets past validation
|
||||
ExternalKind::Type => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,3 +503,76 @@ pub fn parse_name_section<'data>(
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Module section of the wasm module.
|
||||
pub fn parse_module_section<'data>(
|
||||
section: wasmparser::ModuleSectionReader<'data>,
|
||||
environ: &mut dyn ModuleEnvironment<'data>,
|
||||
) -> WasmResult<()> {
|
||||
environ.reserve_modules(section.get_count());
|
||||
|
||||
for module_ty in section {
|
||||
environ.declare_module(TypeIndex::from_u32(module_ty?))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Instance section of the wasm module.
|
||||
pub fn parse_instance_section<'data>(
|
||||
section: wasmparser::InstanceSectionReader<'data>,
|
||||
environ: &mut dyn ModuleEnvironment<'data>,
|
||||
) -> WasmResult<()> {
|
||||
environ.reserve_instances(section.get_count());
|
||||
|
||||
for instance in section {
|
||||
let instance = instance?;
|
||||
let module = ModuleIndex::from_u32(instance.module());
|
||||
let args = instance
|
||||
.args()?
|
||||
.into_iter()
|
||||
.map(|result| {
|
||||
let (kind, idx) = result?;
|
||||
Ok(match kind {
|
||||
ExternalKind::Function => EntityIndex::Function(FuncIndex::from_u32(idx)),
|
||||
ExternalKind::Table => EntityIndex::Table(TableIndex::from_u32(idx)),
|
||||
ExternalKind::Memory => EntityIndex::Memory(MemoryIndex::from_u32(idx)),
|
||||
ExternalKind::Global => EntityIndex::Global(GlobalIndex::from_u32(idx)),
|
||||
ExternalKind::Module => EntityIndex::Module(ModuleIndex::from_u32(idx)),
|
||||
ExternalKind::Instance => EntityIndex::Instance(InstanceIndex::from_u32(idx)),
|
||||
ExternalKind::Event => unimplemented!(),
|
||||
|
||||
// this won't pass validation
|
||||
ExternalKind::Type => unreachable!(),
|
||||
})
|
||||
})
|
||||
.collect::<WasmResult<Vec<_>>>()?;
|
||||
environ.declare_instance(module, args)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses the Alias section of the wasm module.
|
||||
pub fn parse_alias_section<'data>(
|
||||
section: wasmparser::AliasSectionReader<'data>,
|
||||
environ: &mut dyn ModuleEnvironment<'data>,
|
||||
) -> WasmResult<()> {
|
||||
for alias in section {
|
||||
let alias = alias?;
|
||||
let alias = match alias.instance {
|
||||
wasmparser::AliasedInstance::Parent => {
|
||||
match alias.kind {
|
||||
ExternalKind::Module => Alias::ParentModule(ModuleIndex::from_u32(alias.index)),
|
||||
ExternalKind::Type => Alias::ParentType(TypeIndex::from_u32(alias.index)),
|
||||
// shouldn't get past validation
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
wasmparser::AliasedInstance::Child(i) => Alias::Child {
|
||||
instance: InstanceIndex::from_u32(i),
|
||||
export: alias.index as usize,
|
||||
},
|
||||
};
|
||||
environ.declare_alias(alias)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -97,8 +97,20 @@ entity_impl!(InstanceIndex);
|
||||
pub struct EventIndex(u32);
|
||||
entity_impl!(EventIndex);
|
||||
|
||||
/// Specialized index for just module types.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
pub struct ModuleTypeIndex(u32);
|
||||
entity_impl!(ModuleTypeIndex);
|
||||
|
||||
/// Specialized index for just instance types.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
pub struct InstanceTypeIndex(u32);
|
||||
entity_impl!(InstanceTypeIndex);
|
||||
|
||||
/// An index of an entity.
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
pub enum EntityIndex {
|
||||
/// Function index.
|
||||
@@ -131,13 +143,13 @@ pub enum EntityType {
|
||||
Table(Table),
|
||||
/// A function type where the index points to the type section and records a
|
||||
/// function signature.
|
||||
Function(TypeIndex),
|
||||
Function(SignatureIndex),
|
||||
/// An instance where the index points to the type section and records a
|
||||
/// instance's exports.
|
||||
Instance(TypeIndex),
|
||||
Instance(InstanceTypeIndex),
|
||||
/// A module where the index points to the type section and records a
|
||||
/// module's imports and exports.
|
||||
Module(TypeIndex),
|
||||
Module(ModuleTypeIndex),
|
||||
}
|
||||
|
||||
/// A WebAssembly global.
|
||||
|
||||
@@ -793,8 +793,15 @@
|
||||
* \typedef wasm_externkind_t
|
||||
* \brief Classifier for #wasm_externtype_t, defined by #wasm_externkind_enum
|
||||
*
|
||||
* This is returned from #wasm_extern_kind and #wasm_externtype_kind to
|
||||
* determine what kind of type is wrapped.
|
||||
*
|
||||
* \enum wasm_externkind_enum
|
||||
* \brief Kinds of external items for a wasm module.
|
||||
*
|
||||
* Note that this also includes #WASM_EXTERN_INSTANCE as well as
|
||||
* #WASM_EXTERN_MODULE and is intended to be used when #wasm_externkind_t is
|
||||
* used.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -208,6 +208,14 @@ WASMTIME_CONFIG_PROP(void, wasm_bulk_memory, bool)
|
||||
*/
|
||||
WASMTIME_CONFIG_PROP(void, wasm_multi_value, bool)
|
||||
|
||||
/**
|
||||
* \brief Configures whether the WebAssembly module linking proposal is
|
||||
* enabled.
|
||||
*
|
||||
* This setting is `false` by default.
|
||||
*/
|
||||
WASMTIME_CONFIG_PROP(void, wasm_module_linking, bool)
|
||||
|
||||
/**
|
||||
* \brief Configures how JIT code will be compiled.
|
||||
*
|
||||
@@ -961,6 +969,291 @@ WASM_API_EXTERN own wasmtime_error_t *wasmtime_module_deserialize(
|
||||
own wasm_module_t **ret
|
||||
);
|
||||
|
||||
/**
|
||||
* \struct wasm_instancetype_t
|
||||
* \brief An opaque object representing the type of a function.
|
||||
*
|
||||
* \typedef wasm_instancetype_t
|
||||
* \brief Convenience alias for #wasm_instancetype_t
|
||||
*
|
||||
* \struct wasm_instancetype_vec_t
|
||||
* \brief A list of #wasm_instancetype_t values.
|
||||
*
|
||||
* \var wasm_instancetype_vec_t::size
|
||||
* \brief Length of this vector.
|
||||
*
|
||||
* \var wasm_instancetype_vec_t::data
|
||||
* \brief Pointer to the base of this vector
|
||||
*
|
||||
* \typedef wasm_instancetype_vec_t
|
||||
* \brief Convenience alias for #wasm_instancetype_vec_t
|
||||
*
|
||||
* \fn void wasm_instancetype_delete(own wasm_instancetype_t *);
|
||||
* \brief Deletes a type.
|
||||
*
|
||||
* \fn void wasm_instancetype_vec_new_empty(own wasm_instancetype_vec_t *out);
|
||||
* \brief Creates an empty vector.
|
||||
*
|
||||
* See #wasm_byte_vec_new_empty for more information.
|
||||
*
|
||||
* \fn void wasm_instancetype_vec_new_uninitialized(own wasm_instancetype_vec_t *out, size_t);
|
||||
* \brief Creates a vector with the given capacity.
|
||||
*
|
||||
* See #wasm_byte_vec_new_uninitialized for more information.
|
||||
*
|
||||
* \fn void wasm_instancetype_vec_new(own wasm_instancetype_vec_t *out, size_t, own wasm_instancetype_t *const[]);
|
||||
* \brief Creates a vector with the provided contents.
|
||||
*
|
||||
* See #wasm_byte_vec_new for more information.
|
||||
*
|
||||
* \fn void wasm_instancetype_vec_copy(own wasm_instancetype_vec_t *out, const wasm_instancetype_vec_t *)
|
||||
* \brief Copies one vector to another
|
||||
*
|
||||
* See #wasm_byte_vec_copy for more information.
|
||||
*
|
||||
* \fn void wasm_instancetype_vec_delete(own wasm_instancetype_vec_t *out)
|
||||
* \brief Deallocates memory for a vector.
|
||||
*
|
||||
* See #wasm_byte_vec_delete for more information.
|
||||
*
|
||||
* \fn own wasm_instancetype_t* wasm_instancetype_copy(wasm_instancetype_t *)
|
||||
* \brief Creates a new value which matches the provided one.
|
||||
*
|
||||
* The caller is responsible for deleting the returned value.
|
||||
*/
|
||||
WASM_DECLARE_TYPE(instancetype)
|
||||
|
||||
/**
|
||||
* \brief Returns the list of exports that this instance type provides.
|
||||
*
|
||||
* This function does not take ownership of the provided instance type but
|
||||
* ownership of `out` is passed to the caller. Note that `out` is treated as
|
||||
* uninitialized when passed to this function.
|
||||
*/
|
||||
WASM_API_EXTERN void wasm_instancetype_exports(const wasm_instancetype_t*, own wasm_exporttype_vec_t* out);
|
||||
|
||||
/**
|
||||
* \brief Converts a #wasm_instancetype_t to a #wasm_externtype_t
|
||||
*
|
||||
* The returned value is owned by the #wasm_instancetype_t argument and should not
|
||||
* be deleted.
|
||||
*/
|
||||
WASM_API_EXTERN wasm_externtype_t* wasm_instancetype_as_externtype(wasm_instancetype_t*);
|
||||
|
||||
/**
|
||||
* \brief Attempts to convert a #wasm_externtype_t to a #wasm_instancetype_t
|
||||
*
|
||||
* The returned value is owned by the #wasm_instancetype_t argument and should not
|
||||
* be deleted. Returns `NULL` if the provided argument is not a
|
||||
* #wasm_instancetype_t.
|
||||
*/
|
||||
WASM_API_EXTERN wasm_instancetype_t* wasm_externtype_as_instancetype(wasm_externtype_t*);
|
||||
|
||||
/**
|
||||
* \brief Converts a #wasm_instancetype_t to a #wasm_externtype_t
|
||||
*
|
||||
* The returned value is owned by the #wasm_instancetype_t argument and should not
|
||||
* be deleted.
|
||||
*/
|
||||
WASM_API_EXTERN const wasm_externtype_t* wasm_instancetype_as_externtype_const(const wasm_instancetype_t*);
|
||||
|
||||
/**
|
||||
* \brief Attempts to convert a #wasm_externtype_t to a #wasm_instancetype_t
|
||||
*
|
||||
* The returned value is owned by the #wasm_instancetype_t argument and should not
|
||||
* be deleted. Returns `NULL` if the provided argument is not a
|
||||
* #wasm_instancetype_t.
|
||||
*/
|
||||
WASM_API_EXTERN const wasm_instancetype_t* wasm_externtype_as_instancetype_const(const wasm_externtype_t*);
|
||||
|
||||
/**
|
||||
* \struct wasm_moduletype_t
|
||||
* \brief An opaque object representing the type of a function.
|
||||
*
|
||||
* \typedef wasm_moduletype_t
|
||||
* \brief Convenience alias for #wasm_moduletype_t
|
||||
*
|
||||
* \struct wasm_moduletype_vec_t
|
||||
* \brief A list of #wasm_moduletype_t values.
|
||||
*
|
||||
* \var wasm_moduletype_vec_t::size
|
||||
* \brief Length of this vector.
|
||||
*
|
||||
* \var wasm_moduletype_vec_t::data
|
||||
* \brief Pointer to the base of this vector
|
||||
*
|
||||
* \typedef wasm_moduletype_vec_t
|
||||
* \brief Convenience alias for #wasm_moduletype_vec_t
|
||||
*
|
||||
* \fn void wasm_moduletype_delete(own wasm_moduletype_t *);
|
||||
* \brief Deletes a type.
|
||||
*
|
||||
* \fn void wasm_moduletype_vec_new_empty(own wasm_moduletype_vec_t *out);
|
||||
* \brief Creates an empty vector.
|
||||
*
|
||||
* See #wasm_byte_vec_new_empty for more information.
|
||||
*
|
||||
* \fn void wasm_moduletype_vec_new_uninitialized(own wasm_moduletype_vec_t *out, size_t);
|
||||
* \brief Creates a vector with the given capacity.
|
||||
*
|
||||
* See #wasm_byte_vec_new_uninitialized for more information.
|
||||
*
|
||||
* \fn void wasm_moduletype_vec_new(own wasm_moduletype_vec_t *out, size_t, own wasm_moduletype_t *const[]);
|
||||
* \brief Creates a vector with the provided contents.
|
||||
*
|
||||
* See #wasm_byte_vec_new for more information.
|
||||
*
|
||||
* \fn void wasm_moduletype_vec_copy(own wasm_moduletype_vec_t *out, const wasm_moduletype_vec_t *)
|
||||
* \brief Copies one vector to another
|
||||
*
|
||||
* See #wasm_byte_vec_copy for more information.
|
||||
*
|
||||
* \fn void wasm_moduletype_vec_delete(own wasm_moduletype_vec_t *out)
|
||||
* \brief Deallocates memory for a vector.
|
||||
*
|
||||
* See #wasm_byte_vec_delete for more information.
|
||||
*
|
||||
* \fn own wasm_moduletype_t* wasm_moduletype_copy(wasm_moduletype_t *)
|
||||
* \brief Creates a new value which matches the provided one.
|
||||
*
|
||||
* The caller is responsible for deleting the returned value.
|
||||
*/
|
||||
WASM_DECLARE_TYPE(moduletype)
|
||||
|
||||
/**
|
||||
* \brief Returns the list of imports that this module type requires.
|
||||
*
|
||||
* This function does not take ownership of the provided module type but
|
||||
* ownership of `out` is passed to the caller. Note that `out` is treated as
|
||||
* uninitialized when passed to this function.
|
||||
*/
|
||||
WASM_API_EXTERN void wasm_moduletype_imports(const wasm_moduletype_t*, own wasm_importtype_vec_t* out);
|
||||
|
||||
/**
|
||||
* \brief Returns the list of exports that this module type provides.
|
||||
*
|
||||
* This function does not take ownership of the provided module type but
|
||||
* ownership of `out` is passed to the caller. Note that `out` is treated as
|
||||
* uninitialized when passed to this function.
|
||||
*/
|
||||
WASM_API_EXTERN void wasm_moduletype_exports(const wasm_moduletype_t*, own wasm_exporttype_vec_t* out);
|
||||
|
||||
/**
|
||||
* \brief Converts a #wasm_moduletype_t to a #wasm_externtype_t
|
||||
*
|
||||
* The returned value is owned by the #wasm_moduletype_t argument and should not
|
||||
* be deleted.
|
||||
*/
|
||||
WASM_API_EXTERN wasm_externtype_t* wasm_moduletype_as_externtype(wasm_moduletype_t*);
|
||||
|
||||
/**
|
||||
* \brief Attempts to convert a #wasm_externtype_t to a #wasm_moduletype_t
|
||||
*
|
||||
* The returned value is owned by the #wasm_moduletype_t argument and should not
|
||||
* be deleted. Returns `NULL` if the provided argument is not a
|
||||
* #wasm_moduletype_t.
|
||||
*/
|
||||
WASM_API_EXTERN wasm_moduletype_t* wasm_externtype_as_moduletype(wasm_externtype_t*);
|
||||
|
||||
/**
|
||||
* \brief Converts a #wasm_moduletype_t to a #wasm_externtype_t
|
||||
*
|
||||
* The returned value is owned by the #wasm_moduletype_t argument and should not
|
||||
* be deleted.
|
||||
*/
|
||||
WASM_API_EXTERN const wasm_externtype_t* wasm_moduletype_as_externtype_const(const wasm_moduletype_t*);
|
||||
|
||||
/**
|
||||
* \brief Attempts to convert a #wasm_externtype_t to a #wasm_moduletype_t
|
||||
*
|
||||
* The returned value is owned by the #wasm_moduletype_t argument and should not
|
||||
* be deleted. Returns `NULL` if the provided argument is not a
|
||||
* #wasm_moduletype_t.
|
||||
*/
|
||||
WASM_API_EXTERN const wasm_moduletype_t* wasm_externtype_as_moduletype_const(const wasm_externtype_t*);
|
||||
|
||||
/**
|
||||
* \brief Converts a #wasm_module_t to #wasm_extern_t.
|
||||
*
|
||||
* The returned #wasm_extern_t is owned by the #wasm_module_t argument. Callers
|
||||
* should not delete the returned value, and it only lives as long as the
|
||||
* #wasm_module_t argument.
|
||||
*/
|
||||
WASM_API_EXTERN wasm_extern_t* wasm_module_as_extern(wasm_module_t*);
|
||||
|
||||
/**
|
||||
* \brief Converts a #wasm_extern_t to #wasm_module_t.
|
||||
*
|
||||
* The returned #wasm_module_t is owned by the #wasm_extern_t argument. Callers
|
||||
* should not delete the returned value, and it only lives as long as the
|
||||
* #wasm_extern_t argument.
|
||||
*
|
||||
* If the #wasm_extern_t argument isn't a #wasm_module_t then `NULL` is returned.
|
||||
*/
|
||||
WASM_API_EXTERN wasm_module_t* wasm_extern_as_module(wasm_extern_t*);
|
||||
|
||||
/**
|
||||
* \brief Converts a #wasm_extern_t to #wasm_instance_t.
|
||||
*
|
||||
* The returned #wasm_instance_t is owned by the #wasm_extern_t argument. Callers
|
||||
* should not delete the returned value, and it only lives as long as the
|
||||
* #wasm_extern_t argument.
|
||||
*/
|
||||
WASM_API_EXTERN const wasm_module_t* wasm_extern_as_module_const(const wasm_extern_t*);
|
||||
|
||||
/**
|
||||
* \brief Converts a #wasm_instance_t to #wasm_extern_t.
|
||||
*
|
||||
* The returned #wasm_extern_t is owned by the #wasm_instance_t argument. Callers
|
||||
* should not delete the returned value, and it only lives as long as the
|
||||
* #wasm_instance_t argument.
|
||||
*/
|
||||
WASM_API_EXTERN wasm_extern_t* wasm_instance_as_extern(wasm_instance_t*);
|
||||
|
||||
/**
|
||||
* \brief Converts a #wasm_extern_t to #wasm_instance_t.
|
||||
*
|
||||
* The returned #wasm_instance_t is owned by the #wasm_extern_t argument. Callers
|
||||
* should not delete the returned value, and it only lives as long as the
|
||||
* #wasm_extern_t argument.
|
||||
*
|
||||
* If the #wasm_extern_t argument isn't a #wasm_instance_t then `NULL` is returned.
|
||||
*/
|
||||
WASM_API_EXTERN wasm_instance_t* wasm_extern_as_instance(wasm_extern_t*);
|
||||
|
||||
/**
|
||||
* \brief Converts a #wasm_extern_t to #wasm_instance_t.
|
||||
*
|
||||
* The returned #wasm_instance_t is owned by the #wasm_extern_t argument. Callers
|
||||
* should not delete the returned value, and it only lives as long as the
|
||||
* #wasm_extern_t argument.
|
||||
*/
|
||||
WASM_API_EXTERN const wasm_instance_t* wasm_extern_as_instance_const(const wasm_extern_t*);
|
||||
|
||||
/**
|
||||
* \brief Returns the type of this instance.
|
||||
*
|
||||
* The returned #wasm_instancetype_t is expected to be deallocated by the caller.
|
||||
*/
|
||||
WASM_API_EXTERN own wasm_instancetype_t* wasm_instance_type(const wasm_instance_t*);
|
||||
|
||||
/**
|
||||
* \brief Returns the type of this module.
|
||||
*
|
||||
* The returned #wasm_moduletype_t is expected to be deallocated by the caller.
|
||||
*/
|
||||
WASM_API_EXTERN own wasm_moduletype_t* wasm_module_type(const wasm_module_t*);
|
||||
|
||||
/**
|
||||
* \brief Value of #wasm_externkind_enum corresponding to a wasm module.
|
||||
*/
|
||||
#define WASM_EXTERN_MODULE 4
|
||||
|
||||
/**
|
||||
* \brief Value of #wasm_externkind_enum corresponding to a wasm instance.
|
||||
*/
|
||||
#define WASM_EXTERN_INSTANCE 5
|
||||
|
||||
#undef own
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -85,6 +85,11 @@ pub extern "C" fn wasmtime_config_wasm_multi_value_set(c: &mut wasm_config_t, en
|
||||
c.config.wasm_multi_value(enable);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmtime_config_wasm_module_linking_set(c: &mut wasm_config_t, enable: bool) {
|
||||
c.config.wasm_module_linking(enable);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasmtime_config_strategy_set(
|
||||
c: &mut wasm_config_t,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::wasm_externkind_t;
|
||||
use crate::{wasm_externtype_t, wasm_func_t, wasm_global_t, wasm_memory_t, wasm_table_t};
|
||||
use crate::{
|
||||
wasm_externkind_t, wasm_externtype_t, wasm_func_t, wasm_global_t, wasm_instance_t,
|
||||
wasm_memory_t, wasm_module_t, wasm_table_t,
|
||||
};
|
||||
use wasmtime::Extern;
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -16,6 +18,8 @@ pub extern "C" fn wasm_extern_kind(e: &wasm_extern_t) -> wasm_externkind_t {
|
||||
Extern::Global(_) => crate::WASM_EXTERN_GLOBAL,
|
||||
Extern::Table(_) => crate::WASM_EXTERN_TABLE,
|
||||
Extern::Memory(_) => crate::WASM_EXTERN_MEMORY,
|
||||
Extern::Instance(_) => crate::WASM_EXTERN_INSTANCE,
|
||||
Extern::Module(_) => crate::WASM_EXTERN_MODULE,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,3 +67,23 @@ pub extern "C" fn wasm_extern_as_memory(e: &wasm_extern_t) -> Option<&wasm_memor
|
||||
pub extern "C" fn wasm_extern_as_memory_const(e: &wasm_extern_t) -> Option<&wasm_memory_t> {
|
||||
wasm_extern_as_memory(e)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_extern_as_module(e: &wasm_extern_t) -> Option<&wasm_module_t> {
|
||||
wasm_module_t::try_from(e)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_extern_as_module_const(e: &wasm_extern_t) -> Option<&wasm_module_t> {
|
||||
wasm_extern_as_module(e)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_extern_as_instance(e: &wasm_extern_t) -> Option<&wasm_instance_t> {
|
||||
wasm_instance_t::try_from(e)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_extern_as_instance_const(e: &wasm_extern_t) -> Option<&wasm_instance_t> {
|
||||
wasm_extern_as_instance(e)
|
||||
}
|
||||
|
||||
@@ -1,20 +1,38 @@
|
||||
use crate::{wasm_extern_t, wasm_extern_vec_t, wasm_module_t, wasm_trap_t};
|
||||
use crate::{wasm_store_t, wasmtime_error_t};
|
||||
use crate::{wasm_instancetype_t, wasm_store_t, wasmtime_error_t};
|
||||
use anyhow::Result;
|
||||
use std::ptr;
|
||||
use wasmtime::{Instance, Trap};
|
||||
use wasmtime::{Extern, Instance, Trap};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct wasm_instance_t {
|
||||
pub(crate) instance: Instance,
|
||||
ext: wasm_extern_t,
|
||||
}
|
||||
|
||||
wasmtime_c_api_macros::declare_ref!(wasm_instance_t);
|
||||
|
||||
impl wasm_instance_t {
|
||||
pub(crate) fn new(instance: Instance) -> wasm_instance_t {
|
||||
wasm_instance_t { instance: instance }
|
||||
wasm_instance_t {
|
||||
ext: wasm_extern_t {
|
||||
which: instance.into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_from(e: &wasm_extern_t) -> Option<&wasm_instance_t> {
|
||||
match &e.which {
|
||||
Extern::Instance(_) => Some(unsafe { &*(e as *const _ as *const _) }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn instance(&self) -> &Instance {
|
||||
match &self.ext.which {
|
||||
Extern::Instance(i) => i,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +49,7 @@ pub unsafe extern "C" fn wasm_instance_new(
|
||||
store,
|
||||
wasm_module,
|
||||
imports,
|
||||
wasm_module.imports.len(),
|
||||
wasm_module.module().imports().len(),
|
||||
&mut instance,
|
||||
&mut trap,
|
||||
);
|
||||
@@ -92,7 +110,7 @@ fn _wasmtime_instance_new(
|
||||
.map(|import| import.which.clone())
|
||||
.collect::<Vec<_>>();
|
||||
handle_instantiate(
|
||||
Instance::new(store, &module.module, &imports),
|
||||
Instance::new(store, module.module(), &imports),
|
||||
instance_ptr,
|
||||
trap_ptr,
|
||||
)
|
||||
@@ -122,11 +140,16 @@ pub fn handle_instantiate(
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_instance_as_extern(m: &wasm_instance_t) -> &wasm_extern_t {
|
||||
&m.ext
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_instance_exports(instance: &wasm_instance_t, out: &mut wasm_extern_vec_t) {
|
||||
out.set_buffer(
|
||||
instance
|
||||
.instance
|
||||
.instance()
|
||||
.exports()
|
||||
.map(|e| {
|
||||
Some(Box::new(wasm_extern_t {
|
||||
@@ -136,3 +159,8 @@ pub extern "C" fn wasm_instance_exports(instance: &wasm_instance_t, out: &mut wa
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_instance_type(f: &wasm_instance_t) -> Box<wasm_instancetype_t> {
|
||||
Box::new(wasm_instancetype_t::new(f.instance().ty()))
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ pub extern "C" fn wasmtime_linker_define_instance(
|
||||
Ok(s) => s,
|
||||
Err(_) => return bad_utf8(),
|
||||
};
|
||||
handle_result(linker.instance(name, &instance.instance), |_linker| ())
|
||||
handle_result(linker.instance(name, instance.instance()), |_linker| ())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -78,7 +78,7 @@ pub extern "C" fn wasmtime_linker_instantiate(
|
||||
instance_ptr: &mut *mut wasm_instance_t,
|
||||
trap_ptr: &mut *mut wasm_trap_t,
|
||||
) -> Option<Box<wasmtime_error_t>> {
|
||||
let result = linker.linker.instantiate(&module.module);
|
||||
let result = linker.linker.instantiate(module.module());
|
||||
super::instance::handle_instantiate(result, instance_ptr, trap_ptr)
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ pub extern "C" fn wasmtime_linker_module(
|
||||
Ok(s) => s,
|
||||
Err(_) => return bad_utf8(),
|
||||
};
|
||||
handle_result(linker.module(name, &module.module), |_linker| ())
|
||||
handle_result(linker.module(name, module.module()), |_linker| ())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
||||
@@ -1,20 +1,43 @@
|
||||
use crate::{
|
||||
handle_result, wasm_byte_vec_t, wasm_engine_t, wasm_exporttype_t, wasm_exporttype_vec_t,
|
||||
wasm_importtype_t, wasm_importtype_vec_t, wasm_store_t, wasmtime_error_t,
|
||||
wasm_extern_t, wasm_importtype_t, wasm_importtype_vec_t, wasm_moduletype_t, wasm_store_t,
|
||||
wasmtime_error_t,
|
||||
};
|
||||
use std::ptr;
|
||||
use wasmtime::{Engine, Module};
|
||||
use wasmtime::{Engine, Extern, Module};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct wasm_module_t {
|
||||
pub(crate) module: Module,
|
||||
pub(crate) imports: Vec<wasm_importtype_t>,
|
||||
pub(crate) exports: Vec<wasm_exporttype_t>,
|
||||
ext: wasm_extern_t,
|
||||
}
|
||||
|
||||
wasmtime_c_api_macros::declare_ref!(wasm_module_t);
|
||||
|
||||
impl wasm_module_t {
|
||||
pub(crate) fn new(module: Module) -> wasm_module_t {
|
||||
wasm_module_t {
|
||||
ext: wasm_extern_t {
|
||||
which: module.into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_from(e: &wasm_extern_t) -> Option<&wasm_module_t> {
|
||||
match &e.which {
|
||||
Extern::Module(_) => Some(unsafe { &*(e as *const _ as *const _) }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn module(&self) -> &Module {
|
||||
match &self.ext.which {
|
||||
Extern::Module(i) => i,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
pub struct wasm_shared_module_t {
|
||||
@@ -49,25 +72,7 @@ pub extern "C" fn wasmtime_module_new(
|
||||
) -> Option<Box<wasmtime_error_t>> {
|
||||
let binary = binary.as_slice();
|
||||
handle_result(Module::from_binary(&engine.engine, binary), |module| {
|
||||
let imports = module
|
||||
.imports()
|
||||
.map(|i| {
|
||||
wasm_importtype_t::new(
|
||||
i.module().to_owned(),
|
||||
i.name().map(|s| s.to_owned()),
|
||||
i.ty(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let exports = module
|
||||
.exports()
|
||||
.map(|e| wasm_exporttype_t::new(e.name().to_owned(), e.ty()))
|
||||
.collect::<Vec<_>>();
|
||||
let module = Box::new(wasm_module_t {
|
||||
module: module,
|
||||
imports,
|
||||
exports,
|
||||
});
|
||||
let module = Box::new(wasm_module_t::new(module));
|
||||
*ret = Box::into_raw(module);
|
||||
})
|
||||
}
|
||||
@@ -86,30 +91,46 @@ pub extern "C" fn wasmtime_module_validate(
|
||||
handle_result(Module::validate(store.store.engine(), binary), |()| {})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_module_as_extern(m: &wasm_module_t) -> &wasm_extern_t {
|
||||
&m.ext
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_module_exports(module: &wasm_module_t, out: &mut wasm_exporttype_vec_t) {
|
||||
let buffer = module
|
||||
.exports
|
||||
.iter()
|
||||
.map(|et| Some(Box::new(et.clone())))
|
||||
let exports = module
|
||||
.module()
|
||||
.exports()
|
||||
.map(|e| {
|
||||
Some(Box::new(wasm_exporttype_t::new(
|
||||
e.name().to_owned(),
|
||||
e.ty(),
|
||||
)))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
out.set_buffer(buffer);
|
||||
out.set_buffer(exports);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_module_imports(module: &wasm_module_t, out: &mut wasm_importtype_vec_t) {
|
||||
let buffer = module
|
||||
.imports
|
||||
.iter()
|
||||
.map(|it| Some(Box::new(it.clone())))
|
||||
let imports = module
|
||||
.module()
|
||||
.imports()
|
||||
.map(|i| {
|
||||
Some(Box::new(wasm_importtype_t::new(
|
||||
i.module().to_owned(),
|
||||
i.name().map(|s| s.to_owned()),
|
||||
i.ty(),
|
||||
)))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
out.set_buffer(buffer);
|
||||
out.set_buffer(imports);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_module_share(module: &wasm_module_t) -> Box<wasm_shared_module_t> {
|
||||
Box::new(wasm_shared_module_t {
|
||||
module: module.module.clone(),
|
||||
module: module.module().clone(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -122,25 +143,7 @@ pub extern "C" fn wasm_module_obtain(
|
||||
if !Engine::same(store.store.engine(), module.engine()) {
|
||||
return None;
|
||||
}
|
||||
let imports = module
|
||||
.imports()
|
||||
.map(|i| {
|
||||
wasm_importtype_t::new(
|
||||
i.module().to_owned(),
|
||||
i.name().map(|s| s.to_owned()),
|
||||
i.ty(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let exports = module
|
||||
.exports()
|
||||
.map(|e| wasm_exporttype_t::new(e.name().to_owned(), e.ty()))
|
||||
.collect::<Vec<_>>();
|
||||
Some(Box::new(wasm_module_t {
|
||||
module: module,
|
||||
imports,
|
||||
exports,
|
||||
}))
|
||||
Some(Box::new(wasm_module_t::new(module)))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -171,7 +174,7 @@ pub extern "C" fn wasmtime_module_serialize(
|
||||
module: &wasm_module_t,
|
||||
ret: &mut wasm_byte_vec_t,
|
||||
) -> Option<Box<wasmtime_error_t>> {
|
||||
handle_result(module.module.serialize(), |buf| {
|
||||
handle_result(module.module().serialize(), |buf| {
|
||||
ret.set_buffer(buf);
|
||||
})
|
||||
}
|
||||
@@ -185,26 +188,13 @@ pub extern "C" fn wasmtime_module_deserialize(
|
||||
handle_result(
|
||||
Module::deserialize(&engine.engine, binary.as_slice()),
|
||||
|module| {
|
||||
let imports = module
|
||||
.imports()
|
||||
.map(|i| {
|
||||
wasm_importtype_t::new(
|
||||
i.module().to_owned(),
|
||||
i.name().map(|s| s.to_owned()),
|
||||
i.ty(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let exports = module
|
||||
.exports()
|
||||
.map(|e| wasm_exporttype_t::new(e.name().to_owned(), e.ty()))
|
||||
.collect::<Vec<_>>();
|
||||
let module = Box::new(wasm_module_t {
|
||||
module: module,
|
||||
imports,
|
||||
exports,
|
||||
});
|
||||
let module = Box::new(wasm_module_t::new(module));
|
||||
*ret = Box::into_raw(module);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_module_type(f: &wasm_module_t) -> Box<wasm_moduletype_t> {
|
||||
Box::new(wasm_moduletype_t::new(f.module().ty()))
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ pub const WASM_EXTERN_FUNC: wasm_externkind_t = 0;
|
||||
pub const WASM_EXTERN_GLOBAL: wasm_externkind_t = 1;
|
||||
pub const WASM_EXTERN_TABLE: wasm_externkind_t = 2;
|
||||
pub const WASM_EXTERN_MEMORY: wasm_externkind_t = 3;
|
||||
pub const WASMTIME_EXTERN_MODULE: wasm_externkind_t = 4;
|
||||
pub const WASMTIME_EXTERN_INSTANCE: wasm_externkind_t = 5;
|
||||
pub const WASM_EXTERN_MODULE: wasm_externkind_t = 4;
|
||||
pub const WASM_EXTERN_INSTANCE: wasm_externkind_t = 5;
|
||||
|
||||
impl wasm_externtype_t {
|
||||
pub(crate) fn new(ty: ExternType) -> wasm_externtype_t {
|
||||
@@ -63,8 +63,8 @@ pub extern "C" fn wasm_externtype_kind(et: &wasm_externtype_t) -> wasm_externkin
|
||||
CExternType::Table(_) => WASM_EXTERN_TABLE,
|
||||
CExternType::Global(_) => WASM_EXTERN_GLOBAL,
|
||||
CExternType::Memory(_) => WASM_EXTERN_MEMORY,
|
||||
CExternType::Instance(_) => WASMTIME_EXTERN_INSTANCE,
|
||||
CExternType::Module(_) => WASMTIME_EXTERN_MODULE,
|
||||
CExternType::Instance(_) => WASM_EXTERN_INSTANCE,
|
||||
CExternType::Module(_) => WASM_EXTERN_MODULE,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::{wasm_externtype_t, wasm_limits_t, CExternType};
|
||||
use once_cell::unsync::OnceCell;
|
||||
use crate::{wasm_exporttype_t, wasm_exporttype_vec_t, wasm_externtype_t, CExternType};
|
||||
use wasmtime::InstanceType;
|
||||
|
||||
#[repr(transparent)]
|
||||
@@ -13,24 +12,33 @@ wasmtime_c_api_macros::declare_ty!(wasm_instancetype_t);
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct CInstanceType {
|
||||
pub(crate) ty: InstanceType,
|
||||
limits_cache: OnceCell<wasm_limits_t>,
|
||||
}
|
||||
|
||||
impl wasm_instancetype_t {
|
||||
pub(crate) fn new(ty: InstanceType) -> wasm_instancetype_t {
|
||||
wasm_instancetype_t {
|
||||
ext: wasm_externtype_t::new(ty.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_from(e: &wasm_externtype_t) -> Option<&wasm_instancetype_t> {
|
||||
match &e.which {
|
||||
CExternType::Instance(_) => Some(unsafe { &*(e as *const _ as *const _) }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn ty(&self) -> &CInstanceType {
|
||||
match &self.ext.which {
|
||||
CExternType::Instance(f) => &f,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CInstanceType {
|
||||
pub(crate) fn new(ty: InstanceType) -> CInstanceType {
|
||||
CInstanceType {
|
||||
ty,
|
||||
limits_cache: OnceCell::new(),
|
||||
}
|
||||
CInstanceType { ty }
|
||||
}
|
||||
}
|
||||
#[no_mangle]
|
||||
@@ -44,3 +52,22 @@ pub extern "C" fn wasm_instancetype_as_externtype_const(
|
||||
) -> &wasm_externtype_t {
|
||||
&ty.ext
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_instancetype_exports(
|
||||
instance: &wasm_instancetype_t,
|
||||
out: &mut wasm_exporttype_vec_t,
|
||||
) {
|
||||
let exports = instance
|
||||
.ty()
|
||||
.ty
|
||||
.exports()
|
||||
.map(|e| {
|
||||
Some(Box::new(wasm_exporttype_t::new(
|
||||
e.name().to_owned(),
|
||||
e.ty(),
|
||||
)))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
out.set_buffer(exports);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::{wasm_externtype_t, wasm_limits_t, CExternType};
|
||||
use once_cell::unsync::OnceCell;
|
||||
use crate::{
|
||||
wasm_exporttype_t, wasm_exporttype_vec_t, wasm_externtype_t, wasm_importtype_t,
|
||||
wasm_importtype_vec_t, CExternType,
|
||||
};
|
||||
use wasmtime::ModuleType;
|
||||
|
||||
#[repr(transparent)]
|
||||
@@ -13,24 +15,33 @@ wasmtime_c_api_macros::declare_ty!(wasm_moduletype_t);
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct CModuleType {
|
||||
pub(crate) ty: ModuleType,
|
||||
limits_cache: OnceCell<wasm_limits_t>,
|
||||
}
|
||||
|
||||
impl wasm_moduletype_t {
|
||||
pub(crate) fn new(ty: ModuleType) -> wasm_moduletype_t {
|
||||
wasm_moduletype_t {
|
||||
ext: wasm_externtype_t::new(ty.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_from(e: &wasm_externtype_t) -> Option<&wasm_moduletype_t> {
|
||||
match &e.which {
|
||||
CExternType::Module(_) => Some(unsafe { &*(e as *const _ as *const _) }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn ty(&self) -> &CModuleType {
|
||||
match &self.ext.which {
|
||||
CExternType::Module(f) => &f,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CModuleType {
|
||||
pub(crate) fn new(ty: ModuleType) -> CModuleType {
|
||||
CModuleType {
|
||||
ty,
|
||||
limits_cache: OnceCell::new(),
|
||||
}
|
||||
CModuleType { ty }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,3 +56,42 @@ pub extern "C" fn wasm_moduletype_as_externtype_const(
|
||||
) -> &wasm_externtype_t {
|
||||
&ty.ext
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_moduletype_exports(
|
||||
module: &wasm_moduletype_t,
|
||||
out: &mut wasm_exporttype_vec_t,
|
||||
) {
|
||||
let exports = module
|
||||
.ty()
|
||||
.ty
|
||||
.exports()
|
||||
.map(|e| {
|
||||
Some(Box::new(wasm_exporttype_t::new(
|
||||
e.name().to_owned(),
|
||||
e.ty(),
|
||||
)))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
out.set_buffer(exports);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_moduletype_imports(
|
||||
module: &wasm_moduletype_t,
|
||||
out: &mut wasm_importtype_vec_t,
|
||||
) {
|
||||
let imports = module
|
||||
.ty()
|
||||
.ty
|
||||
.imports()
|
||||
.map(|i| {
|
||||
Some(Box::new(wasm_importtype_t::new(
|
||||
i.module().to_owned(),
|
||||
i.name().map(|s| s.to_owned()),
|
||||
i.ty(),
|
||||
)))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
out.set_buffer(imports);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::wasm_valtype_t;
|
||||
use crate::{wasm_exporttype_t, wasm_extern_t, wasm_frame_t, wasm_val_t};
|
||||
use crate::{wasm_externtype_t, wasm_importtype_t, wasm_memorytype_t};
|
||||
use crate::{wasm_functype_t, wasm_globaltype_t, wasm_tabletype_t};
|
||||
use crate::{
|
||||
wasm_exporttype_t, wasm_extern_t, wasm_externtype_t, wasm_frame_t, wasm_functype_t,
|
||||
wasm_globaltype_t, wasm_importtype_t, wasm_instancetype_t, wasm_memorytype_t,
|
||||
wasm_moduletype_t, wasm_tabletype_t, wasm_val_t, wasm_valtype_t,
|
||||
};
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
@@ -172,6 +173,24 @@ declare_vecs! {
|
||||
copy: wasm_memorytype_vec_copy,
|
||||
delete: wasm_memorytype_vec_delete,
|
||||
)
|
||||
(
|
||||
name: wasm_instancetype_vec_t,
|
||||
ty: Option<Box<wasm_instancetype_t>>,
|
||||
new: wasm_instancetype_vec_new,
|
||||
empty: wasm_instancetype_vec_new_empty,
|
||||
uninit: wasm_instancetype_vec_new_uninitialized,
|
||||
copy: wasm_instancetype_vec_copy,
|
||||
delete: wasm_instancetype_vec_delete,
|
||||
)
|
||||
(
|
||||
name: wasm_moduletype_vec_t,
|
||||
ty: Option<Box<wasm_moduletype_t>>,
|
||||
new: wasm_moduletype_vec_new,
|
||||
empty: wasm_moduletype_vec_new_empty,
|
||||
uninit: wasm_moduletype_vec_new_uninitialized,
|
||||
copy: wasm_moduletype_vec_copy,
|
||||
delete: wasm_moduletype_vec_delete,
|
||||
)
|
||||
(
|
||||
name: wasm_externtype_vec_t,
|
||||
ty: Option<Box<wasm_externtype_t>>,
|
||||
|
||||
@@ -1039,7 +1039,6 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
callee: ir::Value,
|
||||
call_args: &[ir::Value],
|
||||
) -> WasmResult<ir::Inst> {
|
||||
let sig_index = self.module.types[ty_index].unwrap_function();
|
||||
let pointer_type = self.pointer_type();
|
||||
|
||||
let table_entry_addr = pos.ins().table_addr(pointer_type, table, callee, 0);
|
||||
@@ -1071,7 +1070,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
let vmctx = self.vmctx(pos.func);
|
||||
let base = pos.ins().global_value(pointer_type, vmctx);
|
||||
let offset =
|
||||
i32::try_from(self.offsets.vmctx_vmshared_signature_id(sig_index)).unwrap();
|
||||
i32::try_from(self.offsets.vmctx_vmshared_signature_id(ty_index)).unwrap();
|
||||
|
||||
// Load the caller ID.
|
||||
let mut mem_flags = ir::MemFlags::trusted();
|
||||
|
||||
@@ -99,7 +99,7 @@ use std::sync::Mutex;
|
||||
use wasmtime_environ::{
|
||||
CompileError, CompiledFunction, Compiler, FunctionAddressMap, FunctionBodyData,
|
||||
InstructionAddressMap, ModuleTranslation, Relocation, RelocationTarget, StackMapInformation,
|
||||
TrapInformation, Tunables,
|
||||
TrapInformation, Tunables, TypeTables,
|
||||
};
|
||||
|
||||
mod func_environ;
|
||||
@@ -348,21 +348,22 @@ impl Compiler for Cranelift {
|
||||
mut input: FunctionBodyData<'_>,
|
||||
isa: &dyn isa::TargetIsa,
|
||||
tunables: &Tunables,
|
||||
types: &TypeTables,
|
||||
) -> Result<CompiledFunction, CompileError> {
|
||||
let module = &translation.module;
|
||||
let func_index = module.func_index(func_index);
|
||||
let mut context = Context::new();
|
||||
context.func.name = get_func_name(func_index);
|
||||
let sig_index = module.functions[func_index];
|
||||
context.func.signature = translation.native_signatures[sig_index].clone();
|
||||
if tunables.debug_info {
|
||||
context.func.signature = types.native_signatures[sig_index].clone();
|
||||
if tunables.generate_native_debuginfo {
|
||||
context.func.collect_debug_info();
|
||||
}
|
||||
|
||||
let mut func_env = FuncEnvironment::new(
|
||||
isa.frontend_config(),
|
||||
module,
|
||||
&translation.native_signatures,
|
||||
&types.native_signatures,
|
||||
tunables,
|
||||
);
|
||||
|
||||
@@ -434,7 +435,7 @@ impl Compiler for Cranelift {
|
||||
let address_transform =
|
||||
get_function_address_map(&context, &input, code_buf.len() as u32, isa);
|
||||
|
||||
let ranges = if tunables.debug_info {
|
||||
let ranges = if tunables.generate_native_debuginfo {
|
||||
let ranges = context.build_value_labels_ranges(isa).map_err(|error| {
|
||||
CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
|
||||
})?;
|
||||
|
||||
@@ -13,7 +13,7 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
gimli = "0.23.0"
|
||||
wasmparser = "0.68.0"
|
||||
wasmparser = "0.70"
|
||||
object = { version = "0.22.0", default-features = false, features = ["read", "write"] }
|
||||
wasmtime-environ = { path = "../environ", version = "0.21.0" }
|
||||
target-lexicon = { version = "0.11.0", default-features = false }
|
||||
|
||||
@@ -16,7 +16,7 @@ anyhow = "1.0"
|
||||
cranelift-codegen = { path = "../../cranelift/codegen", version = "0.68.0", features = ["enable-serde"] }
|
||||
cranelift-entity = { path = "../../cranelift/entity", version = "0.68.0", features = ["enable-serde"] }
|
||||
cranelift-wasm = { path = "../../cranelift/wasm", version = "0.68.0", features = ["enable-serde"] }
|
||||
wasmparser = "0.68.0"
|
||||
wasmparser = "0.70"
|
||||
indexmap = { version = "1.0.2", features = ["serde-1"] }
|
||||
thiserror = "1.0.4"
|
||||
serde = { version = "1.0.94", features = ["derive"] }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user