зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1677452 - Update Cranelift to firefox85 / dcc52ba3f69d3de7cdbd787b936825d9c61e3c27 and wasmparser to 0.67: Part 2 - results of "mach vendor rust". r=lth.
Differential Revision: https://phabricator.services.mozilla.com/D97588
This commit is contained in:
Родитель
f28cf588c0
Коммит
3455a0f670
|
@ -22,6 +22,11 @@ git = "https://github.com/mozilla/application-services"
|
|||
replace-with = "vendored-sources"
|
||||
rev = "8a576fbe79199fa8664f64285524017f74ebcc5f"
|
||||
|
||||
[source."https://github.com/mozilla-spidermonkey/wasmtime"]
|
||||
branch = "firefox85"
|
||||
git = "https://github.com/mozilla-spidermonkey/wasmtime"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."https://github.com/mozilla-spidermonkey/jsparagus"]
|
||||
git = "https://github.com/mozilla-spidermonkey/jsparagus"
|
||||
replace-with = "vendored-sources"
|
||||
|
@ -57,11 +62,6 @@ git = "https://github.com/djg/cubeb-pulse-rs"
|
|||
replace-with = "vendored-sources"
|
||||
rev = "bf31534d08b2c16fb5e4c5834944ae3271efbd63"
|
||||
|
||||
[source."https://github.com/bytecodealliance/wasmtime"]
|
||||
git = "https://github.com/bytecodealliance/wasmtime"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "e22e2c3722f2fbccd3c8d3230119fa04c332c69c"
|
||||
|
||||
[source."https://github.com/badboy/failure"]
|
||||
git = "https://github.com/badboy/failure"
|
||||
replace-with = "vendored-sources"
|
||||
|
|
|
@ -220,6 +220,7 @@ dependencies = [
|
|||
"env_logger",
|
||||
"log",
|
||||
"smallvec",
|
||||
"wasmparser 0.67.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -774,22 +775,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-bforest"
|
||||
version = "0.67.0"
|
||||
source = "git+https://github.com/bytecodealliance/wasmtime?rev=e22e2c3722f2fbccd3c8d3230119fa04c332c69c#e22e2c3722f2fbccd3c8d3230119fa04c332c69c"
|
||||
version = "0.68.0"
|
||||
source = "git+https://github.com/mozilla-spidermonkey/wasmtime?branch=firefox85#dcc52ba3f69d3de7cdbd787b936825d9c61e3c27"
|
||||
dependencies = [
|
||||
"cranelift-entity 0.67.0",
|
||||
"cranelift-entity 0.68.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-codegen"
|
||||
version = "0.67.0"
|
||||
source = "git+https://github.com/bytecodealliance/wasmtime?rev=e22e2c3722f2fbccd3c8d3230119fa04c332c69c#e22e2c3722f2fbccd3c8d3230119fa04c332c69c"
|
||||
version = "0.68.0"
|
||||
source = "git+https://github.com/mozilla-spidermonkey/wasmtime?branch=firefox85#dcc52ba3f69d3de7cdbd787b936825d9c61e3c27"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"cranelift-bforest",
|
||||
"cranelift-codegen-meta",
|
||||
"cranelift-codegen-shared",
|
||||
"cranelift-entity 0.67.0",
|
||||
"cranelift-entity 0.68.0",
|
||||
"log",
|
||||
"regalloc",
|
||||
"smallvec",
|
||||
|
@ -799,17 +800,17 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-codegen-meta"
|
||||
version = "0.67.0"
|
||||
source = "git+https://github.com/bytecodealliance/wasmtime?rev=e22e2c3722f2fbccd3c8d3230119fa04c332c69c#e22e2c3722f2fbccd3c8d3230119fa04c332c69c"
|
||||
version = "0.68.0"
|
||||
source = "git+https://github.com/mozilla-spidermonkey/wasmtime?branch=firefox85#dcc52ba3f69d3de7cdbd787b936825d9c61e3c27"
|
||||
dependencies = [
|
||||
"cranelift-codegen-shared",
|
||||
"cranelift-entity 0.67.0",
|
||||
"cranelift-entity 0.68.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-codegen-shared"
|
||||
version = "0.67.0"
|
||||
source = "git+https://github.com/bytecodealliance/wasmtime?rev=e22e2c3722f2fbccd3c8d3230119fa04c332c69c#e22e2c3722f2fbccd3c8d3230119fa04c332c69c"
|
||||
version = "0.68.0"
|
||||
source = "git+https://github.com/mozilla-spidermonkey/wasmtime?branch=firefox85#dcc52ba3f69d3de7cdbd787b936825d9c61e3c27"
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-entity"
|
||||
|
@ -818,13 +819,13 @@ source = "git+https://github.com/PLSysSec/lucet_sandbox_compiler?rev=477d8fc53a6
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-entity"
|
||||
version = "0.67.0"
|
||||
source = "git+https://github.com/bytecodealliance/wasmtime?rev=e22e2c3722f2fbccd3c8d3230119fa04c332c69c#e22e2c3722f2fbccd3c8d3230119fa04c332c69c"
|
||||
version = "0.68.0"
|
||||
source = "git+https://github.com/mozilla-spidermonkey/wasmtime?branch=firefox85#dcc52ba3f69d3de7cdbd787b936825d9c61e3c27"
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-frontend"
|
||||
version = "0.67.0"
|
||||
source = "git+https://github.com/bytecodealliance/wasmtime?rev=e22e2c3722f2fbccd3c8d3230119fa04c332c69c#e22e2c3722f2fbccd3c8d3230119fa04c332c69c"
|
||||
version = "0.68.0"
|
||||
source = "git+https://github.com/mozilla-spidermonkey/wasmtime?branch=firefox85#dcc52ba3f69d3de7cdbd787b936825d9c61e3c27"
|
||||
dependencies = [
|
||||
"cranelift-codegen",
|
||||
"log",
|
||||
|
@ -834,16 +835,17 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-wasm"
|
||||
version = "0.67.0"
|
||||
source = "git+https://github.com/bytecodealliance/wasmtime?rev=e22e2c3722f2fbccd3c8d3230119fa04c332c69c#e22e2c3722f2fbccd3c8d3230119fa04c332c69c"
|
||||
version = "0.68.0"
|
||||
source = "git+https://github.com/mozilla-spidermonkey/wasmtime?branch=firefox85#dcc52ba3f69d3de7cdbd787b936825d9c61e3c27"
|
||||
dependencies = [
|
||||
"cranelift-codegen",
|
||||
"cranelift-entity 0.67.0",
|
||||
"cranelift-entity 0.68.0",
|
||||
"cranelift-frontend",
|
||||
"itertools 0.9.0",
|
||||
"log",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"wasmparser 0.63.0",
|
||||
"wasmparser 0.67.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4194,9 +4196,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regalloc"
|
||||
version = "0.0.30"
|
||||
version = "0.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2041c2d34f6ff346d6f428974f03d8bf12679b0c816bb640dc5eb1d48848d8d1"
|
||||
checksum = "571f7f397d61c4755285cd37853fe8e03271c243424a907415909379659381c5"
|
||||
dependencies = [
|
||||
"log",
|
||||
"rustc-hash",
|
||||
|
@ -5678,9 +5680,9 @@ checksum = "073da89bf1c84db000dd68ce660c1b4a08e3a2d28fd1e3394ab9e7abdde4a0f8"
|
|||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.63.0"
|
||||
version = "0.67.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57da5d7300428d75d8b3cdfb736e41ee6af8926d69c1de2f201a1a22f234b7b5"
|
||||
checksum = "9f091cf3849e5fe76a60255bff169277459f2201435bc583b6656880553f0ad0"
|
||||
|
||||
[[package]]
|
||||
name = "wast"
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"d0317b98ce3e0ec774584aba422be0a19c1f5ce8b309fda2f4c3d5889fce3e12","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"af367c67340fa7f6fb9a35b0aa637dcf303957f7ae7427a5f4f6356801c8bb04","src/lib.rs":"23a5c42d477197a947122e662068e681bb9ed31041c0b668c3267c3fce15d39e","src/map.rs":"a3b7f64cae7ec9c2a8038def315bcf90e8751552b1bc1c20b62fbb8c763866c4","src/node.rs":"28f7edd979f7b9712bc4ab30b0d2a1b8ad5485a4b1e8c09f3dcaf501b9b5ccd1","src/path.rs":"a86ee1c882c173e8af96fd53a416a0fb485dd3f045ac590ef313a9d9ecf90f56","src/pool.rs":"f6337b5417f7772e6878a160c1a40629199ff09997bdff18eb2a0ba770158600","src/set.rs":"281eb8b5ead1ffd395946464d881f9bb0e7fb61092aed701d72d2314b5f80994"},"package":null}
|
||||
{"files":{"Cargo.toml":"b0ed8fc54833fd48846644e3f59fbead46e7a2ff456194e03d04ce8b95404522","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"af367c67340fa7f6fb9a35b0aa637dcf303957f7ae7427a5f4f6356801c8bb04","src/lib.rs":"4204f6bd3dd43dc307a57dc1b3543fc3d31feb4c5c8e64035578a45d88c725b3","src/map.rs":"a3b7f64cae7ec9c2a8038def315bcf90e8751552b1bc1c20b62fbb8c763866c4","src/node.rs":"28f7edd979f7b9712bc4ab30b0d2a1b8ad5485a4b1e8c09f3dcaf501b9b5ccd1","src/path.rs":"a86ee1c882c173e8af96fd53a416a0fb485dd3f045ac590ef313a9d9ecf90f56","src/pool.rs":"f6337b5417f7772e6878a160c1a40629199ff09997bdff18eb2a0ba770158600","src/set.rs":"281eb8b5ead1ffd395946464d881f9bb0e7fb61092aed701d72d2314b5f80994"},"package":null}
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
authors = ["The Cranelift Project Developers"]
|
||||
name = "cranelift-bforest"
|
||||
version = "0.67.0"
|
||||
version = "0.68.0"
|
||||
description = "A forest of B+-trees"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
documentation = "https://docs.rs/cranelift-bforest"
|
||||
|
@ -12,7 +12,7 @@ keywords = ["btree", "forest", "set", "map"]
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
cranelift-entity = { path = "../entity", version = "0.67.0", default-features = false }
|
||||
cranelift-entity = { path = "../entity", version = "0.68.0", default-features = false }
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "experimental" }
|
||||
|
|
|
@ -23,8 +23,7 @@
|
|||
clippy::float_arithmetic,
|
||||
clippy::mut_mut,
|
||||
clippy::nonminimal_bool,
|
||||
clippy::option_map_unwrap_or,
|
||||
clippy::option_map_unwrap_or_else,
|
||||
clippy::map_unwrap_or,
|
||||
clippy::print_stdout,
|
||||
clippy::unicode_not_nfc,
|
||||
clippy::use_self
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"7c40668950dc4e8edbacfc6d7e8cc2c66b8b92ae3f8a65d90c8c4d356dd1f69a","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"b123f056d0d458396679c5f7f2a16d2762af0258fcda4ac14b6655a95e5a0022","src/cdsl/ast.rs":"84a4b7e3301e3249716958a7aa4ea5ba8c6172e3c02f57ee3880504c4433ff19","src/cdsl/cpu_modes.rs":"996e45b374cfe85ac47c8c86c4459fe4c04b3158102b4c63b6ee434d5eed6a9e","src/cdsl/encodings.rs":"d884a564815a03c23369bcf31d13b122ae5ba84d0c80eda9312f0c0a829bf794","src/cdsl/formats.rs":"63e638305aa3ca6dd409ddf0e5e9605eeac1cc2631103e42fc6cbc87703d9b63","src/cdsl/instructions.rs":"a0f5212fa593caf66371f5ee4b15e501939a9407c4663bff6b3ba356b11ca1b4","src/cdsl/isa.rs":"ccabd6848b69eb069c10db61c7e7f86080777495714bb53d03e663c40541be94","src/cdsl/mod.rs":"0aa827923bf4c45e5ee2359573bd863e00f474acd532739f49dcd74a27553882","src/cdsl/operands.rs":"1c3411504de9c83112ff48e0ff1cfbb2e4ba5a9a15c1716f411ef31a4df59899","src/cdsl/recipes.rs":"80b7cd87332229b569e38086ceee8d557e679b9a32ad2e50bdb15c33337c3418","src/cdsl/regs.rs":"466a42a43355fc7623fe5d8e8d330622207a3af6a80cb9367bc0f06e224c9ee0","src/cdsl/settings.rs":"e6fd9a31925743b93b11f09c9c8271bab6aa2430aa053a2601957b4487df7d77","src/cdsl/type_inference.rs":"1efca8a095ffc899b7527bda6b9d9378c73d7283f8dceaa4819e8af599f8be21","src/cdsl/types.rs":"50620fb2a6271a7c9126dc30c433a1bf25646a4d84511f5745650aaaec700f42","src/cdsl/typevar.rs":"3cbe83a09d2402511b20415a8356f848fb82536926386bb42eaaa7740fb2457e","src/cdsl/xform.rs":"55da0c3f2403147b535ab6ae5d69c623fbe839edecf2a3af1de84420cd58402d","src/default_map.rs":"101bb0282a124f9c921f6bd095f529e8753621450d783c3273b0b0394c2c5c03","src/error.rs":"e9b11b2feb2d867b94c8810fdc5a6c4e0d9131604a0bfa5340ff2639a55100b4","src/gen_binemit.rs":"515e243420b30d1e01f8ea630282d9b6d78a715e1951f3f20392e19a48164442","src/gen_encodings.rs":"f00cded6b68a9b48c9e3cd39a8b6f0ba136f4062c8f8666109158a72c62c3ed1","src/gen_inst.rs":"1ff123ab481b48d82e13363043dfc98eaef837bbf6af485b8259c3863550e29c","src/gen_legalizer.rs":"a5e507eb46649a28252582cfc1907c77c9266fec7f92e959a03258bed7d124e9","src/gen_registers.rs":"a904119ed803c9de24dedd15149a65337ffc168bb1d63df53d7fdebfb5f4b158","src/gen_settings.rs":"f3cc3d31f6cc898f30606caf084f0de220db2d3b1b5e5e4145fa7c9a9a1597e2","src/gen_types.rs":"f6c090e1646a43bf2fe81ae0a7029cc6f7dc6d43285368f56d86c35a21c469a6","src/isa/arm32/mod.rs":"da18cb40c1a0a6b613ddefcc38a5d01d02c95de6f233ebd4ad84fefb992c008b","src/isa/arm64/mod.rs":"3a815eaa478d82b7f8b536b83f9debb6b79ec860f99fea6485f209a836c6939a","src/isa/mod.rs":"be483f9a406f603e69603f9489a41a53ee02aa0ece07f7ca396956dfe3815f71","src/isa/riscv/encodings.rs":"8abb1968d917588bc5fc5f5be6dd66bdec23ac456ba65f8138237c8e891e843c","src/isa/riscv/mod.rs":"a7b461a30bbfbc1e3b33645422ff40d5b1761c30cb5d4a8aa12e9a3b7f7aee51","src/isa/riscv/recipes.rs":"5be3bf7c9ba3c51ece384b7eee75a8f7fa0cbacc6a5babc9d0e1d92a2e54a4c2","src/isa/x86/encodings.rs":"e9f1645fec6e4b5cfba9b08cfff70f9d1a5ad3b392f5ee9f40cb1a8669a7c689","src/isa/x86/instructions.rs":"d4d581448f8f7bd5afb033650af0026468eecc6f4184b3bb7c06232bf08c456b","src/isa/x86/legalize.rs":"186c688dd8ac773f2b2c4c1f1cbdb7a66ca13a8ed90c03f87dfe7fdaa12c15b3","src/isa/x86/mod.rs":"31571c281318e6f9bf17680feb96830983f5c1f9811aa4a89736f99f3d9a1831","src/isa/x86/opcodes.rs":"745ef09f4927b5334d68155fa047910ef96311feef7ec20964bb033c3419cd3c","src/isa/x86/recipes.rs":"744292109344363b2210ac1b42cb4704b4b692aa8bf5583e4230557cf3749298","src/isa/x86/registers.rs":"4be0a45d8acd465c31746b7976124025b06b453e3f6d587f93efb5af0e12b1a8","src/isa/x86/settings.rs":"47a5e9fb3b7917cfe817d56dcc77c0470545e451e0f38a875af0531fbd9b6a58","src/lib.rs":"23259ba28aa8f0b3586e9c60f4e67ae50660369f146f2a94249e8cff7d07b27b","src/shared/entities.rs":"90f774a70e1c2a2e9a553c07a5e80e0fe54cf127434bd83e67274bba4e1a19ba","src/shared/formats.rs":"14b668244b2afd71197c2dd8469af0e0602d590fcb14252c2b0b40cb9905a4ae","src/shared/immediates.rs":"563fa33accb992eb11a43f0f63259c62a2c44db59801431cc67ceec4b94f2ca3","src/shared/instructions.rs":"25d321a9ee48c7a40058c27aef85f4b7af3f18ece88841e3ee6335c06f3bded0","src/shared/legalize.rs":"eb5f07fa107cadd67483881ccce29cc8fb9b698a0cd4f1d89853aac275cf7bcf","src/shared/mod.rs":"c219625990bf15507ac1077b349ce20e5312d4e4707426183676d469e78792b7","src/shared/settings.rs":"e7406ce17fb313fa05397dd8103f74eed67d35170d70b6e546e08954aef2ed87","src/shared/types.rs":"4702df132f4b5d70cc9411ec5221ba0b1bd4479252274e0223ae57b6d0331247","src/srcgen.rs":"dcfc159c8599270f17e6a978c4be255abca51556b5ef0da497faec4a4a1e62ce","src/unique_table.rs":"31aa54330ca4786af772d32e8cb6158b6504b88fa93fe177bf0c6cbe545a8d35"},"package":null}
|
||||
{"files":{"Cargo.toml":"561ee9a55739ac9716bc2f024e2673d69aefa6edbc4ff8b61a221a1741ed862a","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"b123f056d0d458396679c5f7f2a16d2762af0258fcda4ac14b6655a95e5a0022","src/cdsl/ast.rs":"84a4b7e3301e3249716958a7aa4ea5ba8c6172e3c02f57ee3880504c4433ff19","src/cdsl/cpu_modes.rs":"996e45b374cfe85ac47c8c86c4459fe4c04b3158102b4c63b6ee434d5eed6a9e","src/cdsl/encodings.rs":"d884a564815a03c23369bcf31d13b122ae5ba84d0c80eda9312f0c0a829bf794","src/cdsl/formats.rs":"63e638305aa3ca6dd409ddf0e5e9605eeac1cc2631103e42fc6cbc87703d9b63","src/cdsl/instructions.rs":"a0f5212fa593caf66371f5ee4b15e501939a9407c4663bff6b3ba356b11ca1b4","src/cdsl/isa.rs":"ccabd6848b69eb069c10db61c7e7f86080777495714bb53d03e663c40541be94","src/cdsl/mod.rs":"0aa827923bf4c45e5ee2359573bd863e00f474acd532739f49dcd74a27553882","src/cdsl/operands.rs":"1c3411504de9c83112ff48e0ff1cfbb2e4ba5a9a15c1716f411ef31a4df59899","src/cdsl/recipes.rs":"80b7cd87332229b569e38086ceee8d557e679b9a32ad2e50bdb15c33337c3418","src/cdsl/regs.rs":"466a42a43355fc7623fe5d8e8d330622207a3af6a80cb9367bc0f06e224c9ee0","src/cdsl/settings.rs":"e6fd9a31925743b93b11f09c9c8271bab6aa2430aa053a2601957b4487df7d77","src/cdsl/type_inference.rs":"1efca8a095ffc899b7527bda6b9d9378c73d7283f8dceaa4819e8af599f8be21","src/cdsl/types.rs":"50620fb2a6271a7c9126dc30c433a1bf25646a4d84511f5745650aaaec700f42","src/cdsl/typevar.rs":"3cbe83a09d2402511b20415a8356f848fb82536926386bb42eaaa7740fb2457e","src/cdsl/xform.rs":"55da0c3f2403147b535ab6ae5d69c623fbe839edecf2a3af1de84420cd58402d","src/default_map.rs":"101bb0282a124f9c921f6bd095f529e8753621450d783c3273b0b0394c2c5c03","src/error.rs":"e9b11b2feb2d867b94c8810fdc5a6c4e0d9131604a0bfa5340ff2639a55100b4","src/gen_binemit.rs":"515e243420b30d1e01f8ea630282d9b6d78a715e1951f3f20392e19a48164442","src/gen_encodings.rs":"f00cded6b68a9b48c9e3cd39a8b6f0ba136f4062c8f8666109158a72c62c3ed1","src/gen_inst.rs":"1ff123ab481b48d82e13363043dfc98eaef837bbf6af485b8259c3863550e29c","src/gen_legalizer.rs":"a5e507eb46649a28252582cfc1907c77c9266fec7f92e959a03258bed7d124e9","src/gen_registers.rs":"a904119ed803c9de24dedd15149a65337ffc168bb1d63df53d7fdebfb5f4b158","src/gen_settings.rs":"f3cc3d31f6cc898f30606caf084f0de220db2d3b1b5e5e4145fa7c9a9a1597e2","src/gen_types.rs":"f6c090e1646a43bf2fe81ae0a7029cc6f7dc6d43285368f56d86c35a21c469a6","src/isa/arm32/mod.rs":"da18cb40c1a0a6b613ddefcc38a5d01d02c95de6f233ebd4ad84fefb992c008b","src/isa/arm64/mod.rs":"3a815eaa478d82b7f8b536b83f9debb6b79ec860f99fea6485f209a836c6939a","src/isa/mod.rs":"be483f9a406f603e69603f9489a41a53ee02aa0ece07f7ca396956dfe3815f71","src/isa/riscv/encodings.rs":"8abb1968d917588bc5fc5f5be6dd66bdec23ac456ba65f8138237c8e891e843c","src/isa/riscv/mod.rs":"a7b461a30bbfbc1e3b33645422ff40d5b1761c30cb5d4a8aa12e9a3b7f7aee51","src/isa/riscv/recipes.rs":"5be3bf7c9ba3c51ece384b7eee75a8f7fa0cbacc6a5babc9d0e1d92a2e54a4c2","src/isa/x86/encodings.rs":"e9f1645fec6e4b5cfba9b08cfff70f9d1a5ad3b392f5ee9f40cb1a8669a7c689","src/isa/x86/instructions.rs":"d4d581448f8f7bd5afb033650af0026468eecc6f4184b3bb7c06232bf08c456b","src/isa/x86/legalize.rs":"f2d3d1ece43c7f18bd7ef405715cd39f59433d8f33a7fa4d237c1de28528ff7c","src/isa/x86/mod.rs":"31571c281318e6f9bf17680feb96830983f5c1f9811aa4a89736f99f3d9a1831","src/isa/x86/opcodes.rs":"745ef09f4927b5334d68155fa047910ef96311feef7ec20964bb033c3419cd3c","src/isa/x86/recipes.rs":"744292109344363b2210ac1b42cb4704b4b692aa8bf5583e4230557cf3749298","src/isa/x86/registers.rs":"4be0a45d8acd465c31746b7976124025b06b453e3f6d587f93efb5af0e12b1a8","src/isa/x86/settings.rs":"47a5e9fb3b7917cfe817d56dcc77c0470545e451e0f38a875af0531fbd9b6a58","src/lib.rs":"23259ba28aa8f0b3586e9c60f4e67ae50660369f146f2a94249e8cff7d07b27b","src/shared/entities.rs":"90f774a70e1c2a2e9a553c07a5e80e0fe54cf127434bd83e67274bba4e1a19ba","src/shared/formats.rs":"14b668244b2afd71197c2dd8469af0e0602d590fcb14252c2b0b40cb9905a4ae","src/shared/immediates.rs":"563fa33accb992eb11a43f0f63259c62a2c44db59801431cc67ceec4b94f2ca3","src/shared/instructions.rs":"21d0f2b041a0bce64d3db614ca003ec9269ba0a31aa5dbdae34cb15e5a59d89f","src/shared/legalize.rs":"eb5f07fa107cadd67483881ccce29cc8fb9b698a0cd4f1d89853aac275cf7bcf","src/shared/mod.rs":"c219625990bf15507ac1077b349ce20e5312d4e4707426183676d469e78792b7","src/shared/settings.rs":"e7406ce17fb313fa05397dd8103f74eed67d35170d70b6e546e08954aef2ed87","src/shared/types.rs":"4702df132f4b5d70cc9411ec5221ba0b1bd4479252274e0223ae57b6d0331247","src/srcgen.rs":"dcfc159c8599270f17e6a978c4be255abca51556b5ef0da497faec4a4a1e62ce","src/unique_table.rs":"31aa54330ca4786af772d32e8cb6158b6504b88fa93fe177bf0c6cbe545a8d35"},"package":null}
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "cranelift-codegen-meta"
|
||||
authors = ["The Cranelift Project Developers"]
|
||||
version = "0.67.0"
|
||||
version = "0.68.0"
|
||||
description = "Metaprogram for cranelift-codegen code generator library"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
repository = "https://github.com/bytecodealliance/wasmtime"
|
||||
|
@ -12,8 +12,8 @@ edition = "2018"
|
|||
rustdoc-args = [ "--document-private-items" ]
|
||||
|
||||
[dependencies]
|
||||
cranelift-codegen-shared = { path = "../shared", version = "0.67.0" }
|
||||
cranelift-entity = { path = "../../entity", version = "0.67.0" }
|
||||
cranelift-codegen-shared = { path = "../shared", version = "0.68.0" }
|
||||
cranelift-entity = { path = "../../entity", version = "0.68.0" }
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "experimental" }
|
||||
|
|
|
@ -396,6 +396,7 @@ fn define_simd(
|
|||
let insertlane = insts.by_name("insertlane");
|
||||
let ishl = insts.by_name("ishl");
|
||||
let ishl_imm = insts.by_name("ishl_imm");
|
||||
let load_splat = insts.by_name("load_splat");
|
||||
let raw_bitcast = insts.by_name("raw_bitcast");
|
||||
let scalar_to_vector = insts.by_name("scalar_to_vector");
|
||||
let splat = insts.by_name("splat");
|
||||
|
@ -820,6 +821,7 @@ fn define_simd(
|
|||
narrow.custom_legalize(fcvt_to_sint_sat, "expand_fcvt_to_sint_sat_vector");
|
||||
narrow.custom_legalize(fmin, "expand_minmax_vector");
|
||||
narrow.custom_legalize(fmax, "expand_minmax_vector");
|
||||
narrow.custom_legalize(load_splat, "expand_load_splat");
|
||||
|
||||
narrow_avx.custom_legalize(imul, "convert_i64x2_imul");
|
||||
narrow_avx.custom_legalize(fcvt_from_uint, "expand_fcvt_from_uint_vector");
|
||||
|
|
|
@ -2193,6 +2193,24 @@ pub(crate) fn define(
|
|||
.operands_out(vec![s]),
|
||||
);
|
||||
|
||||
let a = &Operand::new("a", TxN);
|
||||
let x = &Operand::new("x", Int);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"vhigh_bits",
|
||||
r#"
|
||||
Reduce a vector to a scalar integer.
|
||||
|
||||
Return a scalar integer, consisting of the concatenation of the most significant bit
|
||||
of each lane of ``a``.
|
||||
"#,
|
||||
&formats.unary,
|
||||
)
|
||||
.operands_in(vec![a])
|
||||
.operands_out(vec![x]),
|
||||
);
|
||||
|
||||
let a = &Operand::new("a", &Int.as_bool());
|
||||
let Cond = &Operand::new("Cond", &imm.intcc);
|
||||
let x = &Operand::new("x", Int);
|
||||
|
@ -3559,6 +3577,22 @@ pub(crate) fn define(
|
|||
.operands_out(vec![a]),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"fmin_pseudo",
|
||||
r#"
|
||||
Floating point pseudo-minimum, propagating NaNs. This behaves differently from ``fmin``.
|
||||
See https://github.com/WebAssembly/simd/pull/122 for background.
|
||||
|
||||
The behaviour is defined as ``fmin_pseudo(a, b) = (b < a) ? b : a``, and the behaviour
|
||||
for zero or NaN inputs follows from the behaviour of ``<`` with such inputs.
|
||||
"#,
|
||||
&formats.binary,
|
||||
)
|
||||
.operands_in(vec![x, y])
|
||||
.operands_out(vec![a]),
|
||||
);
|
||||
|
||||
let a = &Operand::new("a", Float).with_doc("The larger of ``x`` and ``y``");
|
||||
|
||||
ig.push(
|
||||
|
@ -3575,6 +3609,22 @@ pub(crate) fn define(
|
|||
.operands_out(vec![a]),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"fmax_pseudo",
|
||||
r#"
|
||||
Floating point pseudo-maximum, propagating NaNs. This behaves differently from ``fmax``.
|
||||
See https://github.com/WebAssembly/simd/pull/122 for background.
|
||||
|
||||
The behaviour is defined as ``fmax_pseudo(a, b) = (a < b) ? b : a``, and the behaviour
|
||||
for zero or NaN inputs follows from the behaviour of ``<`` with such inputs.
|
||||
"#,
|
||||
&formats.binary,
|
||||
)
|
||||
.operands_in(vec![x, y])
|
||||
.operands_out(vec![a]),
|
||||
);
|
||||
|
||||
let a = &Operand::new("a", Float).with_doc("``x`` rounded to integral value");
|
||||
|
||||
ig.push(
|
||||
|
@ -3748,12 +3798,9 @@ pub(crate) fn define(
|
|||
Inst::new(
|
||||
"scalar_to_vector",
|
||||
r#"
|
||||
Scalar To Vector -- move a value out of a scalar register and into a vector register; the
|
||||
scalar will be moved to the lowest-order bits of the vector register. Note that this
|
||||
instruction is intended as a low-level legalization instruction and frontends should prefer
|
||||
insertlane; on certain architectures, scalar_to_vector may zero the highest-order bits for some
|
||||
types (e.g. integers) but not for others (e.g. floats).
|
||||
"#,
|
||||
Copies a scalar value to a vector value. The scalar is copied into the
|
||||
least significant lane of the vector, and all other lanes will be zero.
|
||||
"#,
|
||||
&formats.unary,
|
||||
)
|
||||
.operands_in(vec![s])
|
||||
|
@ -4028,6 +4075,41 @@ pub(crate) fn define(
|
|||
.operands_out(vec![a]),
|
||||
);
|
||||
|
||||
let I16x8 = &TypeVar::new(
|
||||
"I16x8",
|
||||
"A SIMD vector type containing 8 integer lanes each 16 bits wide.",
|
||||
TypeSetBuilder::new()
|
||||
.ints(16..16)
|
||||
.simd_lanes(8..8)
|
||||
.includes_scalars(false)
|
||||
.build(),
|
||||
);
|
||||
|
||||
let x = &Operand::new("x", I16x8);
|
||||
let y = &Operand::new("y", I16x8);
|
||||
let a = &Operand::new("a", &I16x8.merge_lanes());
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"widening_pairwise_dot_product_s",
|
||||
r#"
|
||||
Takes corresponding elements in `x` and `y`, performs a sign-extending length-doubling
|
||||
multiplication on them, then adds adjacent pairs of elements to form the result. For
|
||||
example, if the input vectors are `[x3, x2, x1, x0]` and `[y3, y2, y1, y0]`, it produces
|
||||
the vector `[r1, r0]`, where `r1 = sx(x3) * sx(y3) + sx(x2) * sx(y2)` and
|
||||
`r0 = sx(x1) * sx(y1) + sx(x0) * sx(y0)`, and `sx(n)` sign-extends `n` to twice its width.
|
||||
|
||||
This will double the lane width and halve the number of lanes. So the resulting
|
||||
vector has the same number of bits as `x` and `y` do (individually).
|
||||
|
||||
See https://github.com/WebAssembly/simd/pull/127 for background info.
|
||||
"#,
|
||||
&formats.binary,
|
||||
)
|
||||
.operands_in(vec![x, y])
|
||||
.operands_out(vec![a]),
|
||||
);
|
||||
|
||||
let IntTo = &TypeVar::new(
|
||||
"IntTo",
|
||||
"A larger integer type with the same number of lanes",
|
||||
|
@ -4409,5 +4491,24 @@ pub(crate) fn define(
|
|||
.other_side_effects(true),
|
||||
);
|
||||
|
||||
let Offset = &Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address");
|
||||
let a = &Operand::new("a", TxN);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"load_splat",
|
||||
r#"
|
||||
Load an element from memory at ``p + Offset`` and return a vector
|
||||
whose lanes are all set to that element.
|
||||
|
||||
This is equivalent to ``load`` followed by ``splat``.
|
||||
"#,
|
||||
&formats.load,
|
||||
)
|
||||
.operands_in(vec![MemFlags, p, Offset])
|
||||
.operands_out(vec![a])
|
||||
.can_load(true),
|
||||
);
|
||||
|
||||
ig.build()
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"c87e99b6131e876d16430cd2f40de7c03e6d72a8ba1423c8778b5c733bcd4362","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"a410bc2f5dcbde499c0cd299c2620bc8111e3c5b3fccdd9e2d85caf3c24fdab3","src/condcodes.rs":"b8d433b2217b86e172d25b6c65a3ce0cc8ca221062cad1b28b0c78d2159fbda9","src/constant_hash.rs":"ffc619f45aad62c6fdcb83553a05879691a72e9a0103375b2d6cc12d52cf72d0","src/constants.rs":"fed03a10a6316e06aa174091db6e7d1fbb5f73c82c31193012ec5ab52f1c603a","src/isa/mod.rs":"428a950eca14acbe783899ccb1aecf15027f8cbe205578308ebde203d10535f3","src/isa/x86/encoding_bits.rs":"7e013fb804b13f9f83a0d517c6f5105856938d08ad378cc44a6fe6a59adef270","src/isa/x86/mod.rs":"01ef4e4d7437f938badbe2137892183c1ac684da0f68a5bec7e06aad34f43b9b","src/lib.rs":"91f26f998f11fb9cb74d2ec171424e29badd417beef023674850ace57149c656"},"package":null}
|
||||
{"files":{"Cargo.toml":"322ab8efd1588c57313b18aaa231ee30a888741828cf27283e6c62735d73d02d","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"a410bc2f5dcbde499c0cd299c2620bc8111e3c5b3fccdd9e2d85caf3c24fdab3","src/condcodes.rs":"b8d433b2217b86e172d25b6c65a3ce0cc8ca221062cad1b28b0c78d2159fbda9","src/constant_hash.rs":"ffc619f45aad62c6fdcb83553a05879691a72e9a0103375b2d6cc12d52cf72d0","src/constants.rs":"fed03a10a6316e06aa174091db6e7d1fbb5f73c82c31193012ec5ab52f1c603a","src/isa/mod.rs":"428a950eca14acbe783899ccb1aecf15027f8cbe205578308ebde203d10535f3","src/isa/x86/encoding_bits.rs":"7e013fb804b13f9f83a0d517c6f5105856938d08ad378cc44a6fe6a59adef270","src/isa/x86/mod.rs":"01ef4e4d7437f938badbe2137892183c1ac684da0f68a5bec7e06aad34f43b9b","src/lib.rs":"7a8eda4dafcf47100c41e61b5c985f089d1985c500624956dc183fcf6bc7b183"},"package":null}
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
authors = ["The Cranelift Project Developers"]
|
||||
name = "cranelift-codegen-shared"
|
||||
version = "0.67.0"
|
||||
version = "0.68.0"
|
||||
description = "For code shared between cranelift-codegen-meta and cranelift-codegen"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
repository = "https://github.com/bytecodealliance/wasmtime"
|
||||
|
|
|
@ -12,8 +12,7 @@
|
|||
clippy::float_arithmetic,
|
||||
clippy::mut_mut,
|
||||
clippy::nonminimal_bool,
|
||||
clippy::option_map_unwrap_or,
|
||||
clippy::option_map_unwrap_or_else,
|
||||
clippy::map_unwrap_or,
|
||||
clippy::print_stdout,
|
||||
clippy::unicode_not_nfc,
|
||||
clippy::use_self
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
authors = ["The Cranelift Project Developers"]
|
||||
name = "cranelift-codegen"
|
||||
version = "0.67.0"
|
||||
version = "0.68.0"
|
||||
description = "Low-level code generator library"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
documentation = "https://docs.rs/cranelift-codegen"
|
||||
|
@ -13,31 +13,31 @@ build = "build.rs"
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
cranelift-codegen-shared = { path = "./shared", version = "0.67.0" }
|
||||
cranelift-entity = { path = "../entity", version = "0.67.0" }
|
||||
cranelift-bforest = { path = "../bforest", version = "0.67.0" }
|
||||
hashbrown = { version = "0.7", optional = true }
|
||||
cranelift-codegen-shared = { path = "./shared", version = "0.68.0" }
|
||||
cranelift-entity = { path = "../entity", version = "0.68.0" }
|
||||
cranelift-bforest = { path = "../bforest", version = "0.68.0" }
|
||||
hashbrown = { version = "0.9.1", optional = true }
|
||||
target-lexicon = "0.11"
|
||||
log = { version = "0.4.6", default-features = false }
|
||||
serde = { version = "1.0.94", features = ["derive"], optional = true }
|
||||
bincode = { version = "1.2.1", optional = true }
|
||||
gimli = { version = "0.22.0", default-features = false, features = ["write"], optional = true }
|
||||
gimli = { version = "0.23.0", default-features = false, features = ["write"], optional = true }
|
||||
smallvec = { version = "1.0.0" }
|
||||
thiserror = "1.0.4"
|
||||
byteorder = { version = "1.3.2", default-features = false }
|
||||
peepmatic = { path = "../peepmatic", optional = true, version = "0.67.0" }
|
||||
peepmatic-traits = { path = "../peepmatic/crates/traits", optional = true, version = "0.67.0" }
|
||||
peepmatic-runtime = { path = "../peepmatic/crates/runtime", optional = true, version = "0.67.0" }
|
||||
regalloc = "0.0.30"
|
||||
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 = "25.0.0", optional = true }
|
||||
wast = { version = "27.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
|
||||
# accomodated in `tests`.
|
||||
|
||||
[build-dependencies]
|
||||
cranelift-codegen-meta = { path = "meta", version = "0.67.0" }
|
||||
cranelift-codegen-meta = { path = "meta", version = "0.68.0" }
|
||||
|
||||
[features]
|
||||
default = ["std", "unwind"]
|
||||
|
|
|
@ -74,9 +74,6 @@ impl<'a> MemoryCodeSink<'a> {
|
|||
|
||||
/// A trait for receiving relocations for code that is emitted directly into memory.
|
||||
pub trait RelocSink {
|
||||
/// Add a relocation referencing a block at the current offset.
|
||||
fn reloc_block(&mut self, _: CodeOffset, _: Reloc, _: CodeOffset);
|
||||
|
||||
/// Add a relocation referencing an external symbol at the current offset.
|
||||
fn reloc_external(
|
||||
&mut self,
|
||||
|
@ -138,11 +135,6 @@ impl<'a> CodeSink for MemoryCodeSink<'a> {
|
|||
self.write(x);
|
||||
}
|
||||
|
||||
fn reloc_block(&mut self, rel: Reloc, block_offset: CodeOffset) {
|
||||
let ofs = self.offset();
|
||||
self.relocs.reloc_block(ofs, rel, block_offset);
|
||||
}
|
||||
|
||||
fn reloc_external(
|
||||
&mut self,
|
||||
srcloc: SourceLoc,
|
||||
|
@ -204,7 +196,6 @@ impl<'a> CodeSink for MemoryCodeSink<'a> {
|
|||
pub struct NullRelocSink {}
|
||||
|
||||
impl RelocSink for NullRelocSink {
|
||||
fn reloc_block(&mut self, _: CodeOffset, _: Reloc, _: CodeOffset) {}
|
||||
fn reloc_external(
|
||||
&mut self,
|
||||
_: CodeOffset,
|
||||
|
|
|
@ -140,9 +140,6 @@ pub trait CodeSink {
|
|||
/// Add 8 bytes to the code section.
|
||||
fn put8(&mut self, _: u64);
|
||||
|
||||
/// Add a relocation referencing a block at the current offset.
|
||||
fn reloc_block(&mut self, _: Reloc, _: CodeOffset);
|
||||
|
||||
/// Add a relocation referencing an external symbol plus the addend at the current offset.
|
||||
fn reloc_external(&mut self, _: SourceLoc, _: Reloc, _: &ExternalName, _: Addend);
|
||||
|
||||
|
|
|
@ -253,6 +253,17 @@ impl Context {
|
|||
&self,
|
||||
isa: &dyn TargetIsa,
|
||||
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
|
||||
if let Some(backend) = isa.get_mach_backend() {
|
||||
use crate::isa::CallConv;
|
||||
use crate::machinst::UnwindInfoKind;
|
||||
let unwind_info_kind = match self.func.signature.call_conv {
|
||||
CallConv::Fast | CallConv::Cold | CallConv::SystemV => UnwindInfoKind::SystemV,
|
||||
CallConv::WindowsFastcall => UnwindInfoKind::Windows,
|
||||
_ => UnwindInfoKind::None,
|
||||
};
|
||||
let result = self.mach_compile_result.as_ref().unwrap();
|
||||
return backend.emit_unwind_info(result, unwind_info_kind);
|
||||
}
|
||||
isa.create_unwind_info(&self.func)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
//! This module gives users to instantiate values that Cranelift understands. These values are used,
|
||||
//! for example, during interpretation and for wrapping immediates.
|
||||
use crate::ir::immediates::{Ieee32, Ieee64, Offset32};
|
||||
use crate::ir::{types, ConstantData, Type};
|
||||
use core::convert::TryInto;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::ptr;
|
||||
use thiserror::Error;
|
||||
|
||||
/// Represent a data value. Where [Value] is an SSA reference, [DataValue] is the type + value
|
||||
/// that would be referred to by a [Value].
|
||||
///
|
||||
/// [Value]: crate::ir::Value
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
||||
pub enum DataValue {
|
||||
B(bool),
|
||||
I8(i8),
|
||||
I16(i16),
|
||||
I32(i32),
|
||||
I64(i64),
|
||||
U8(u8),
|
||||
U16(u16),
|
||||
U32(u32),
|
||||
U64(u64),
|
||||
F32(Ieee32),
|
||||
F64(Ieee64),
|
||||
V128([u8; 16]),
|
||||
}
|
||||
|
||||
impl DataValue {
|
||||
/// Try to cast an immediate integer (a wrapped `i64` on most Cranelift instructions) to the
|
||||
/// given Cranelift [Type].
|
||||
pub fn from_integer(imm: i64, ty: Type) -> Result<DataValue, DataValueCastFailure> {
|
||||
match ty {
|
||||
types::I8 => Ok(DataValue::I8(imm as i8)),
|
||||
types::I16 => Ok(DataValue::I16(imm as i16)),
|
||||
types::I32 => Ok(DataValue::I32(imm as i32)),
|
||||
types::I64 => Ok(DataValue::I64(imm)),
|
||||
_ => Err(DataValueCastFailure::FromInteger(imm, ty)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the Cranelift IR [Type] for this [DataValue].
|
||||
pub fn ty(&self) -> Type {
|
||||
match self {
|
||||
DataValue::B(_) => types::B8, // A default type.
|
||||
DataValue::I8(_) | DataValue::U8(_) => types::I8,
|
||||
DataValue::I16(_) | DataValue::U16(_) => types::I16,
|
||||
DataValue::I32(_) | DataValue::U32(_) => types::I32,
|
||||
DataValue::I64(_) | DataValue::U64(_) => types::I64,
|
||||
DataValue::F32(_) => types::F32,
|
||||
DataValue::F64(_) => types::F64,
|
||||
DataValue::V128(_) => types::I8X16, // A default type.
|
||||
}
|
||||
}
|
||||
|
||||
/// Return true if the value is a vector (i.e. `DataValue::V128`).
|
||||
pub fn is_vector(&self) -> bool {
|
||||
match self {
|
||||
DataValue::V128(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a [DataValue] to a memory location.
|
||||
pub unsafe fn write_value_to(&self, p: *mut u128) {
|
||||
match self {
|
||||
DataValue::B(b) => ptr::write(p as *mut bool, *b),
|
||||
DataValue::I8(i) => ptr::write(p as *mut i8, *i),
|
||||
DataValue::I16(i) => ptr::write(p as *mut i16, *i),
|
||||
DataValue::I32(i) => ptr::write(p as *mut i32, *i),
|
||||
DataValue::I64(i) => ptr::write(p as *mut i64, *i),
|
||||
DataValue::F32(f) => ptr::write(p as *mut Ieee32, *f),
|
||||
DataValue::F64(f) => ptr::write(p as *mut Ieee64, *f),
|
||||
DataValue::V128(b) => ptr::write(p as *mut [u8; 16], *b),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a [DataValue] from a memory location using a given [Type].
|
||||
pub unsafe fn read_value_from(p: *const u128, ty: Type) -> Self {
|
||||
match ty {
|
||||
types::I8 => DataValue::I8(ptr::read(p as *const i8)),
|
||||
types::I16 => DataValue::I16(ptr::read(p as *const i16)),
|
||||
types::I32 => DataValue::I32(ptr::read(p as *const i32)),
|
||||
types::I64 => DataValue::I64(ptr::read(p as *const i64)),
|
||||
types::F32 => DataValue::F32(ptr::read(p as *const Ieee32)),
|
||||
types::F64 => DataValue::F64(ptr::read(p as *const Ieee64)),
|
||||
_ if ty.is_bool() => DataValue::B(ptr::read(p as *const bool)),
|
||||
_ if ty.is_vector() && ty.bytes() == 16 => {
|
||||
DataValue::V128(ptr::read(p as *const [u8; 16]))
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Record failures to cast [DataValue].
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum DataValueCastFailure {
|
||||
#[error("unable to cast data value of type {0} to type {1}")]
|
||||
TryInto(Type, Type),
|
||||
#[error("unable to cast i64({0}) to a data value of type {1}")]
|
||||
FromInteger(i64, Type),
|
||||
}
|
||||
|
||||
/// Helper for creating conversion implementations for [DataValue].
|
||||
macro_rules! build_conversion_impl {
|
||||
( $rust_ty:ty, $data_value_ty:ident, $cranelift_ty:ident ) => {
|
||||
impl From<$rust_ty> for DataValue {
|
||||
fn from(data: $rust_ty) -> Self {
|
||||
DataValue::$data_value_ty(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryInto<$rust_ty> for DataValue {
|
||||
type Error = DataValueCastFailure;
|
||||
fn try_into(self) -> Result<$rust_ty, Self::Error> {
|
||||
if let DataValue::$data_value_ty(v) = self {
|
||||
Ok(v)
|
||||
} else {
|
||||
Err(DataValueCastFailure::TryInto(
|
||||
self.ty(),
|
||||
types::$cranelift_ty,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
build_conversion_impl!(bool, B, B8);
|
||||
build_conversion_impl!(i8, I8, I8);
|
||||
build_conversion_impl!(i16, I16, I16);
|
||||
build_conversion_impl!(i32, I32, I32);
|
||||
build_conversion_impl!(i64, I64, I64);
|
||||
build_conversion_impl!(u8, U8, I8);
|
||||
build_conversion_impl!(u16, U16, I16);
|
||||
build_conversion_impl!(u32, U32, I32);
|
||||
build_conversion_impl!(u64, U64, I64);
|
||||
build_conversion_impl!(Ieee32, F32, F32);
|
||||
build_conversion_impl!(Ieee64, F64, F64);
|
||||
build_conversion_impl!([u8; 16], V128, I8X16);
|
||||
impl From<Offset32> for DataValue {
|
||||
fn from(o: Offset32) -> Self {
|
||||
DataValue::from(Into::<i32>::into(o))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DataValue {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
DataValue::B(dv) => write!(f, "{}", dv),
|
||||
DataValue::I8(dv) => write!(f, "{}", dv),
|
||||
DataValue::I16(dv) => write!(f, "{}", dv),
|
||||
DataValue::I32(dv) => write!(f, "{}", dv),
|
||||
DataValue::I64(dv) => write!(f, "{}", dv),
|
||||
DataValue::U8(dv) => write!(f, "{}", dv),
|
||||
DataValue::U16(dv) => write!(f, "{}", dv),
|
||||
DataValue::U32(dv) => write!(f, "{}", dv),
|
||||
DataValue::U64(dv) => write!(f, "{}", dv),
|
||||
// The Ieee* wrappers here print the expected syntax.
|
||||
DataValue::F32(dv) => write!(f, "{}", dv),
|
||||
DataValue::F64(dv) => write!(f, "{}", dv),
|
||||
// Again, for syntax consistency, use ConstantData, which in this case displays as hex.
|
||||
DataValue::V128(dv) => write!(f, "{}", ConstantData::from(&dv[..])),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper structure for printing bracket-enclosed vectors of [DataValue]s.
|
||||
/// - for empty vectors, display `[]`
|
||||
/// - for single item vectors, display `42`, e.g.
|
||||
/// - for multiple item vectors, display `[42, 43, 44]`, e.g.
|
||||
pub struct DisplayDataValues<'a>(pub &'a [DataValue]);
|
||||
|
||||
impl<'a> Display for DisplayDataValues<'a> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
if self.0.len() == 1 {
|
||||
write!(f, "{}", self.0[0])
|
||||
} else {
|
||||
write!(f, "[")?;
|
||||
write_data_value_list(f, &self.0)?;
|
||||
write!(f, "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function for displaying `Vec<DataValue>`.
|
||||
pub fn write_data_value_list(f: &mut Formatter<'_>, list: &[DataValue]) -> fmt::Result {
|
||||
match list.len() {
|
||||
0 => Ok(()),
|
||||
1 => write!(f, "{}", list[0]),
|
||||
_ => {
|
||||
write!(f, "{}", list[0])?;
|
||||
for dv in list.iter().skip(1) {
|
||||
write!(f, ", {}", dv)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn type_conversions() {
|
||||
assert_eq!(DataValue::B(true).ty(), types::B8);
|
||||
assert_eq!(
|
||||
TryInto::<bool>::try_into(DataValue::B(false)).unwrap(),
|
||||
false
|
||||
);
|
||||
assert_eq!(
|
||||
TryInto::<i32>::try_into(DataValue::B(false)).unwrap_err(),
|
||||
DataValueCastFailure::TryInto(types::B8, types::I32)
|
||||
);
|
||||
|
||||
assert_eq!(DataValue::V128([0; 16]).ty(), types::I8X16);
|
||||
assert_eq!(
|
||||
TryInto::<[u8; 16]>::try_into(DataValue::V128([0; 16])).unwrap(),
|
||||
[0; 16]
|
||||
);
|
||||
assert_eq!(
|
||||
TryInto::<i32>::try_into(DataValue::V128([0; 16])).unwrap_err(),
|
||||
DataValueCastFailure::TryInto(types::I8X16, types::I32)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
//! Instruction predicates/properties, shared by various analyses.
|
||||
|
||||
use crate::ir::{DataFlowGraph, Function, Inst, InstructionData, Opcode};
|
||||
use crate::machinst::ty_bits;
|
||||
use cranelift_entity::EntityRef;
|
||||
|
||||
/// Preserve instructions with used result values.
|
||||
|
@ -59,7 +60,21 @@ pub fn is_constant_64bit(func: &Function, inst: Inst) -> Option<u64> {
|
|||
&InstructionData::UnaryImm { imm, .. } => Some(imm.bits() as u64),
|
||||
&InstructionData::UnaryIeee32 { imm, .. } => Some(imm.bits() as u64),
|
||||
&InstructionData::UnaryIeee64 { imm, .. } => Some(imm.bits()),
|
||||
&InstructionData::UnaryBool { imm, .. } => Some(if imm { 1 } else { 0 }),
|
||||
&InstructionData::UnaryBool { imm, .. } => {
|
||||
let imm = if imm {
|
||||
let bits = ty_bits(func.dfg.value_type(func.dfg.inst_results(inst)[0]));
|
||||
|
||||
if bits < 64 {
|
||||
(1u64 << bits) - 1
|
||||
} else {
|
||||
u64::MAX
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
Some(imm)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,6 +63,11 @@ impl ConstantData {
|
|||
self.0.is_empty()
|
||||
}
|
||||
|
||||
/// Return the data as a slice.
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
self.0.as_slice()
|
||||
}
|
||||
|
||||
/// Convert the data to a vector.
|
||||
pub fn into_vec(self) -> Vec<u8> {
|
||||
self.0
|
||||
|
|
|
@ -94,7 +94,7 @@ pub struct Function {
|
|||
/// The instructions that mark the start (inclusive) of an epilogue in the function.
|
||||
///
|
||||
/// This is used for some ABIs to generate unwind information.
|
||||
pub epilogues_start: Vec<Inst>,
|
||||
pub epilogues_start: Vec<(Inst, Block)>,
|
||||
|
||||
/// An optional global value which represents an expression evaluating to
|
||||
/// the stack limit for this function. This `GlobalValue` will be
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
//! `cranelift-codegen/meta/src/shared/immediates` crate in the meta language.
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::str::FromStr;
|
||||
use core::{i32, u32};
|
||||
|
@ -450,6 +451,7 @@ impl FromStr for Offset32 {
|
|||
///
|
||||
/// All bit patterns are allowed.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
#[repr(C)]
|
||||
pub struct Ieee32(u32);
|
||||
|
||||
/// An IEEE binary64 immediate floating point value, represented as a u64
|
||||
|
@ -457,6 +459,7 @@ pub struct Ieee32(u32);
|
|||
///
|
||||
/// All bit patterns are allowed.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
#[repr(C)]
|
||||
pub struct Ieee64(u64);
|
||||
|
||||
/// Format a floating point number in a way that is reasonably human-readable, and that can be
|
||||
|
@ -737,6 +740,17 @@ impl Ieee32 {
|
|||
pub fn bits(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Check if the value is a NaN.
|
||||
pub fn is_nan(&self) -> bool {
|
||||
f32::from_bits(self.0).is_nan()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Ieee32 {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
f32::from_bits(self.0).partial_cmp(&f32::from_bits(other.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Ieee32 {
|
||||
|
@ -810,6 +824,18 @@ impl Ieee64 {
|
|||
pub fn bits(self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Check if the value is a NaN. For [Ieee64], this means checking that the 11 exponent bits are
|
||||
/// all set.
|
||||
pub fn is_nan(&self) -> bool {
|
||||
f64::from_bits(self.0).is_nan()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Ieee64 {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
f64::from_bits(self.0).partial_cmp(&f64::from_bits(other.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Ieee64 {
|
||||
|
|
|
@ -17,6 +17,7 @@ use crate::ir::{self, trapcode::TrapCode, types, Block, FuncRef, JumpTable, SigR
|
|||
use crate::isa;
|
||||
|
||||
use crate::bitset::BitSet;
|
||||
use crate::data_value::DataValue;
|
||||
use crate::entity;
|
||||
use ir::condcodes::{FloatCC, IntCC};
|
||||
|
||||
|
@ -284,6 +285,41 @@ impl InstructionData {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return the value of an immediate if the instruction has one or `None` otherwise. Only
|
||||
/// immediate values are considered, not global values, constant handles, condition codes, etc.
|
||||
pub fn imm_value(&self) -> Option<DataValue> {
|
||||
match self {
|
||||
&InstructionData::UnaryBool { imm, .. } => Some(DataValue::from(imm)),
|
||||
// 8-bit.
|
||||
&InstructionData::BinaryImm8 { imm, .. }
|
||||
| &InstructionData::BranchTableEntry { imm, .. } => Some(DataValue::from(imm as i8)), // Note the switch from unsigned to signed.
|
||||
// 32-bit
|
||||
&InstructionData::UnaryIeee32 { imm, .. } => Some(DataValue::from(imm)),
|
||||
&InstructionData::HeapAddr { imm, .. } => {
|
||||
let imm: u32 = imm.into();
|
||||
Some(DataValue::from(imm as i32)) // Note the switch from unsigned to signed.
|
||||
}
|
||||
&InstructionData::Load { offset, .. }
|
||||
| &InstructionData::LoadComplex { offset, .. }
|
||||
| &InstructionData::Store { offset, .. }
|
||||
| &InstructionData::StoreComplex { offset, .. }
|
||||
| &InstructionData::StackLoad { offset, .. }
|
||||
| &InstructionData::StackStore { offset, .. }
|
||||
| &InstructionData::TableAddr { offset, .. } => Some(DataValue::from(offset)),
|
||||
// 64-bit.
|
||||
&InstructionData::UnaryImm { imm, .. }
|
||||
| &InstructionData::BinaryImm64 { imm, .. }
|
||||
| &InstructionData::IntCompareImm { imm, .. } => Some(DataValue::from(imm.bits())),
|
||||
&InstructionData::UnaryIeee64 { imm, .. } => Some(DataValue::from(imm)),
|
||||
// 128-bit; though these immediates are present logically in the IR they are not
|
||||
// included in the `InstructionData` for memory-size reasons. This case, returning
|
||||
// `None`, is left here to alert users of this method that they should retrieve the
|
||||
// value using the `DataFlowGraph`.
|
||||
&InstructionData::Shuffle { mask: _, .. } => None,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// If this is a trapping instruction, get its trap code. Otherwise, return
|
||||
/// `None`.
|
||||
pub fn trap_code(&self) -> Option<TrapCode> {
|
||||
|
|
|
@ -12,9 +12,6 @@ use serde::{Deserialize, Serialize};
|
|||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
pub enum TrapCode {
|
||||
/// The current stack space was exhausted.
|
||||
///
|
||||
/// On some platforms, a stack overflow may also be indicated by a segmentation fault from the
|
||||
/// stack guard page.
|
||||
StackOverflow,
|
||||
|
||||
/// A `heap_addr` instruction detected an out-of-bounds error.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use crate::ir;
|
||||
use crate::ir::types;
|
||||
use crate::ir::types::*;
|
||||
use crate::ir::SourceLoc;
|
||||
use crate::ir::MemFlags;
|
||||
use crate::isa;
|
||||
use crate::isa::aarch64::{inst::EmitState, inst::*};
|
||||
use crate::machinst::*;
|
||||
|
@ -152,6 +152,11 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
64
|
||||
}
|
||||
|
||||
/// Return required stack alignment in bytes.
|
||||
fn stack_align(_call_conv: isa::CallConv) -> u32 {
|
||||
16
|
||||
}
|
||||
|
||||
fn compute_arg_locs(
|
||||
call_conv: isa::CallConv,
|
||||
params: &[ir::AbiParam],
|
||||
|
@ -308,11 +313,11 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
}
|
||||
|
||||
fn gen_load_stack(mem: StackAMode, into_reg: Writable<Reg>, ty: Type) -> Inst {
|
||||
Inst::gen_load(into_reg, mem.into(), ty)
|
||||
Inst::gen_load(into_reg, mem.into(), ty, MemFlags::trusted())
|
||||
}
|
||||
|
||||
fn gen_store_stack(mem: StackAMode, from_reg: Reg, ty: Type) -> Inst {
|
||||
Inst::gen_store(mem.into(), from_reg, ty)
|
||||
Inst::gen_store(mem.into(), from_reg, ty, MemFlags::trusted())
|
||||
}
|
||||
|
||||
fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Inst {
|
||||
|
@ -375,7 +380,7 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
extendop: ExtendOp::UXTX,
|
||||
});
|
||||
insts.push(Inst::TrapIf {
|
||||
trap_info: (ir::SourceLoc::default(), ir::TrapCode::StackOverflow),
|
||||
trap_code: ir::TrapCode::StackOverflow,
|
||||
// Here `Lo` == "less than" when interpreting the two
|
||||
// operands as unsigned integers.
|
||||
kind: CondBrKind::Cond(Cond::Lo),
|
||||
|
@ -398,12 +403,12 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
|
||||
fn gen_load_base_offset(into_reg: Writable<Reg>, base: Reg, offset: i32, ty: Type) -> Inst {
|
||||
let mem = AMode::RegOffset(base, offset as i64, ty);
|
||||
Inst::gen_load(into_reg, mem, ty)
|
||||
Inst::gen_load(into_reg, mem, ty, MemFlags::trusted())
|
||||
}
|
||||
|
||||
fn gen_store_base_offset(base: Reg, offset: i32, from_reg: Reg, ty: Type) -> Inst {
|
||||
let mem = AMode::RegOffset(base, offset as i64, ty);
|
||||
Inst::gen_store(mem, from_reg, ty)
|
||||
Inst::gen_store(mem, from_reg, ty, MemFlags::trusted())
|
||||
}
|
||||
|
||||
fn gen_sp_reg_adjust(amount: i32) -> SmallVec<[Inst; 2]> {
|
||||
|
@ -460,6 +465,7 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
writable_stack_reg(),
|
||||
SImm7Scaled::maybe_from_i64(-16, types::I64).unwrap(),
|
||||
),
|
||||
flags: MemFlags::trusted(),
|
||||
});
|
||||
// mov fp (x29), sp. This uses the ADDI rd, rs, 0 form of `MOV` because
|
||||
// the usual encoding (`ORR`) does not work with SP.
|
||||
|
@ -496,6 +502,7 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
writable_stack_reg(),
|
||||
SImm7Scaled::maybe_from_i64(16, types::I64).unwrap(),
|
||||
),
|
||||
flags: MemFlags::trusted(),
|
||||
});
|
||||
|
||||
insts
|
||||
|
@ -508,9 +515,10 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
_: &settings::Flags,
|
||||
clobbers: &Set<Writable<RealReg>>,
|
||||
fixed_frame_storage_size: u32,
|
||||
_outgoing_args_size: u32,
|
||||
) -> (u64, SmallVec<[Inst; 16]>) {
|
||||
let mut insts = SmallVec::new();
|
||||
let (clobbered_int, clobbered_vec) = get_callee_saves(call_conv, clobbers);
|
||||
let (clobbered_int, clobbered_vec) = get_regs_saved_in_prologue(call_conv, clobbers);
|
||||
|
||||
let (int_save_bytes, vec_save_bytes) = saved_reg_stack_size(&clobbered_int, &clobbered_vec);
|
||||
let total_save_bytes = (vec_save_bytes + int_save_bytes) as i32;
|
||||
|
@ -537,6 +545,7 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
stack_reg(),
|
||||
SImm7Scaled::maybe_from_i64((i * 16) as i64, types::I64).unwrap(),
|
||||
),
|
||||
flags: MemFlags::trusted(),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -548,7 +557,7 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
stack_reg(),
|
||||
SImm9::maybe_from_i64((vec_offset + (i * 16)) as i64).unwrap(),
|
||||
),
|
||||
srcloc: None,
|
||||
flags: MemFlags::trusted(),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -559,9 +568,11 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
call_conv: isa::CallConv,
|
||||
flags: &settings::Flags,
|
||||
clobbers: &Set<Writable<RealReg>>,
|
||||
_fixed_frame_storage_size: u32,
|
||||
_outgoing_args_size: u32,
|
||||
) -> SmallVec<[Inst; 16]> {
|
||||
let mut insts = SmallVec::new();
|
||||
let (clobbered_int, clobbered_vec) = get_callee_saves(call_conv, clobbers);
|
||||
let (clobbered_int, clobbered_vec) = get_regs_saved_in_prologue(call_conv, clobbers);
|
||||
|
||||
let (int_save_bytes, vec_save_bytes) = saved_reg_stack_size(&clobbered_int, &clobbered_vec);
|
||||
for (i, reg_pair) in clobbered_int.chunks(2).enumerate() {
|
||||
|
@ -585,6 +596,7 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
stack_reg(),
|
||||
SImm7Scaled::maybe_from_i64((i * 16) as i64, types::I64).unwrap(),
|
||||
),
|
||||
flags: MemFlags::trusted(),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -595,7 +607,7 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
stack_reg(),
|
||||
SImm9::maybe_from_i64(((i * 16) + int_save_bytes) as i64).unwrap(),
|
||||
),
|
||||
srcloc: None,
|
||||
flags: MemFlags::trusted(),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -616,6 +628,7 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
writable_xreg(BALDRDASH_TLS_REG),
|
||||
AMode::UnsignedOffset(fp_reg(), UImm12Scaled::maybe_from_i64(off, I64).unwrap()),
|
||||
I64,
|
||||
MemFlags::trusted(),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -626,9 +639,10 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
dest: &CallDest,
|
||||
uses: Vec<Reg>,
|
||||
defs: Vec<Writable<Reg>>,
|
||||
loc: SourceLoc,
|
||||
opcode: ir::Opcode,
|
||||
tmp: Writable<Reg>,
|
||||
callee_conv: isa::CallConv,
|
||||
caller_conv: isa::CallConv,
|
||||
) -> SmallVec<[(InstIsSafepoint, Inst); 2]> {
|
||||
let mut insts = SmallVec::new();
|
||||
match &dest {
|
||||
|
@ -639,8 +653,9 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
dest: name.clone(),
|
||||
uses,
|
||||
defs,
|
||||
loc,
|
||||
opcode,
|
||||
caller_callconv: caller_conv,
|
||||
callee_callconv: callee_conv,
|
||||
}),
|
||||
},
|
||||
)),
|
||||
|
@ -651,7 +666,6 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
rd: tmp,
|
||||
name: Box::new(name.clone()),
|
||||
offset: 0,
|
||||
srcloc: loc,
|
||||
},
|
||||
));
|
||||
insts.push((
|
||||
|
@ -661,8 +675,9 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
rn: tmp.to_reg(),
|
||||
uses,
|
||||
defs,
|
||||
loc,
|
||||
opcode,
|
||||
caller_callconv: caller_conv,
|
||||
callee_callconv: callee_conv,
|
||||
}),
|
||||
},
|
||||
));
|
||||
|
@ -674,8 +689,9 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
rn: *reg,
|
||||
uses,
|
||||
defs,
|
||||
loc,
|
||||
opcode,
|
||||
caller_callconv: caller_conv,
|
||||
callee_callconv: callee_conv,
|
||||
}),
|
||||
},
|
||||
)),
|
||||
|
@ -704,17 +720,17 @@ impl ABIMachineSpec for AArch64MachineDeps {
|
|||
s.nominal_sp_to_fp
|
||||
}
|
||||
|
||||
fn get_caller_saves(call_conv: isa::CallConv) -> Vec<Writable<Reg>> {
|
||||
fn get_regs_clobbered_by_call(call_conv_of_callee: isa::CallConv) -> Vec<Writable<Reg>> {
|
||||
let mut caller_saved = Vec::new();
|
||||
for i in 0..29 {
|
||||
let x = writable_xreg(i);
|
||||
if is_caller_save_reg(call_conv, x.to_reg().to_real_reg()) {
|
||||
if is_reg_clobbered_by_call(call_conv_of_callee, x.to_reg().to_real_reg()) {
|
||||
caller_saved.push(x);
|
||||
}
|
||||
}
|
||||
for i in 0..32 {
|
||||
let v = writable_vreg(i);
|
||||
if is_caller_save_reg(call_conv, v.to_reg().to_real_reg()) {
|
||||
if is_reg_clobbered_by_call(call_conv_of_callee, v.to_reg().to_real_reg()) {
|
||||
caller_saved.push(v);
|
||||
}
|
||||
}
|
||||
|
@ -731,7 +747,9 @@ fn legal_type_for_machine(ty: Type) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_callee_save_reg(call_conv: isa::CallConv, r: RealReg) -> bool {
|
||||
/// Is the given register saved in the prologue if clobbered, i.e., is it a
|
||||
/// callee-save?
|
||||
fn is_reg_saved_in_prologue(call_conv: isa::CallConv, r: RealReg) -> bool {
|
||||
if call_conv.extends_baldrdash() {
|
||||
match r.get_class() {
|
||||
RegClass::I64 => {
|
||||
|
@ -759,14 +777,17 @@ fn is_callee_save_reg(call_conv: isa::CallConv, r: RealReg) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_callee_saves(
|
||||
/// Return the set of all integer and vector registers that must be saved in the
|
||||
/// prologue and restored in the epilogue, given the set of all registers
|
||||
/// written by the function's body.
|
||||
fn get_regs_saved_in_prologue(
|
||||
call_conv: isa::CallConv,
|
||||
regs: &Set<Writable<RealReg>>,
|
||||
) -> (Vec<Writable<RealReg>>, Vec<Writable<RealReg>>) {
|
||||
let mut int_saves = vec![];
|
||||
let mut vec_saves = vec![];
|
||||
for ® in regs.iter() {
|
||||
if is_callee_save_reg(call_conv, reg.to_reg()) {
|
||||
if is_reg_saved_in_prologue(call_conv, reg.to_reg()) {
|
||||
match reg.to_reg().get_class() {
|
||||
RegClass::I64 => int_saves.push(reg),
|
||||
RegClass::V128 => vec_saves.push(reg),
|
||||
|
@ -781,8 +802,8 @@ fn get_callee_saves(
|
|||
(int_saves, vec_saves)
|
||||
}
|
||||
|
||||
fn is_caller_save_reg(call_conv: isa::CallConv, r: RealReg) -> bool {
|
||||
if call_conv.extends_baldrdash() {
|
||||
fn is_reg_clobbered_by_call(call_conv_of_callee: isa::CallConv, r: RealReg) -> bool {
|
||||
if call_conv_of_callee.extends_baldrdash() {
|
||||
match r.get_class() {
|
||||
RegClass::I64 => {
|
||||
let enc = r.get_hw_encoding();
|
||||
|
@ -808,8 +829,21 @@ fn is_caller_save_reg(call_conv: isa::CallConv, r: RealReg) -> bool {
|
|||
r.get_hw_encoding() <= 17
|
||||
}
|
||||
RegClass::V128 => {
|
||||
// v0 - v7 inclusive and v16 - v31 inclusive are caller-saves.
|
||||
r.get_hw_encoding() <= 7 || (r.get_hw_encoding() >= 16 && r.get_hw_encoding() <= 31)
|
||||
// v0 - v7 inclusive and v16 - v31 inclusive are caller-saves. The
|
||||
// upper 64 bits of v8 - v15 inclusive are also caller-saves.
|
||||
// However, because we cannot currently represent partial registers
|
||||
// to regalloc.rs, we indicate here that every vector register is
|
||||
// caller-save. Because this function is used at *callsites*,
|
||||
// approximating in this direction (save more than necessary) is
|
||||
// conservative and thus safe.
|
||||
//
|
||||
// Note that we set the 'not included in clobber set' flag in the
|
||||
// regalloc.rs API when a call instruction's callee has the same ABI
|
||||
// as the caller (the current function body); this is safe (anything
|
||||
// clobbered by callee can be clobbered by caller as well) and
|
||||
// avoids unnecessary saves of v8-v15 in the prologue even though we
|
||||
// include them as defs here.
|
||||
true
|
||||
}
|
||||
_ => panic!("Unexpected RegClass"),
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::ir::Type;
|
|||
use crate::isa::aarch64::inst::*;
|
||||
use crate::machinst::{ty_bits, MachLabel};
|
||||
|
||||
use regalloc::{RealRegUniverse, Reg, Writable};
|
||||
use regalloc::{PrettyPrint, RealRegUniverse, Reg, Writable};
|
||||
|
||||
use core::convert::Into;
|
||||
use std::string::String;
|
||||
|
@ -348,19 +348,19 @@ impl BranchTarget {
|
|||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for ShiftOpAndAmt {
|
||||
impl PrettyPrint for ShiftOpAndAmt {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
format!("{:?} {}", self.op(), self.amt().value())
|
||||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for ExtendOp {
|
||||
impl PrettyPrint for ExtendOp {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
format!("{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for MemLabel {
|
||||
impl PrettyPrint for MemLabel {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
match self {
|
||||
&MemLabel::PCRel(off) => format!("pc+{}", off),
|
||||
|
@ -379,7 +379,7 @@ fn shift_for_type(ty: Type) -> usize {
|
|||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for AMode {
|
||||
impl PrettyPrint for AMode {
|
||||
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
match self {
|
||||
&AMode::Unscaled(reg, simm9) => {
|
||||
|
@ -458,7 +458,7 @@ impl ShowWithRRU for AMode {
|
|||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for PairAMode {
|
||||
impl PrettyPrint for PairAMode {
|
||||
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
match self {
|
||||
&PairAMode::SignedOffset(reg, simm7) => {
|
||||
|
@ -482,7 +482,7 @@ impl ShowWithRRU for PairAMode {
|
|||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for Cond {
|
||||
impl PrettyPrint for Cond {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
let mut s = format!("{:?}", self);
|
||||
s.make_ascii_lowercase();
|
||||
|
@ -490,7 +490,7 @@ impl ShowWithRRU for Cond {
|
|||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for BranchTarget {
|
||||
impl PrettyPrint for BranchTarget {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
match self {
|
||||
&BranchTarget::Label(label) => format!("label{:?}", label.get()),
|
||||
|
@ -579,6 +579,15 @@ impl ScalarSize {
|
|||
}
|
||||
}
|
||||
|
||||
/// Convert to an integer operand size.
|
||||
pub fn operand_size(&self) -> OperandSize {
|
||||
match self {
|
||||
ScalarSize::Size32 => OperandSize::Size32,
|
||||
ScalarSize::Size64 => OperandSize::Size64,
|
||||
_ => panic!("Unexpected operand_size request for: {:?}", self),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert from a type into the smallest size that fits.
|
||||
pub fn from_ty(ty: Type) -> ScalarSize {
|
||||
Self::from_bits(ty_bits(ty))
|
||||
|
@ -609,10 +618,27 @@ pub enum VectorSize {
|
|||
}
|
||||
|
||||
impl VectorSize {
|
||||
/// Get the vector operand size with the given scalar size as lane size.
|
||||
pub fn from_lane_size(size: ScalarSize, is_128bit: bool) -> VectorSize {
|
||||
match (size, is_128bit) {
|
||||
(ScalarSize::Size8, false) => VectorSize::Size8x8,
|
||||
(ScalarSize::Size8, true) => VectorSize::Size8x16,
|
||||
(ScalarSize::Size16, false) => VectorSize::Size16x4,
|
||||
(ScalarSize::Size16, true) => VectorSize::Size16x8,
|
||||
(ScalarSize::Size32, false) => VectorSize::Size32x2,
|
||||
(ScalarSize::Size32, true) => VectorSize::Size32x4,
|
||||
(ScalarSize::Size64, true) => VectorSize::Size64x2,
|
||||
_ => panic!("Unexpected scalar FP operand size: {:?}", size),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert from a type into a vector operand size.
|
||||
pub fn from_ty(ty: Type) -> VectorSize {
|
||||
match ty {
|
||||
B8X16 => VectorSize::Size8x16,
|
||||
B16X8 => VectorSize::Size16x8,
|
||||
B32X4 => VectorSize::Size32x4,
|
||||
B64X2 => VectorSize::Size64x2,
|
||||
F32X2 => VectorSize::Size32x2,
|
||||
F32X4 => VectorSize::Size32x4,
|
||||
F64X2 => VectorSize::Size64x2,
|
||||
|
@ -660,6 +686,9 @@ impl VectorSize {
|
|||
}
|
||||
}
|
||||
|
||||
/// Produces a `VectorSize` with lanes twice as wide. Note that if the resulting
|
||||
/// size would exceed 128 bits, then the number of lanes is also halved, so as to
|
||||
/// ensure that the result size is at most 128 bits.
|
||||
pub fn widen(&self) -> VectorSize {
|
||||
match self {
|
||||
VectorSize::Size8x8 => VectorSize::Size16x8,
|
||||
|
@ -672,6 +701,7 @@ impl VectorSize {
|
|||
}
|
||||
}
|
||||
|
||||
/// Produces a `VectorSize` that has the same lane width, but half as many lanes.
|
||||
pub fn halve(&self) -> VectorSize {
|
||||
match self {
|
||||
VectorSize::Size8x16 => VectorSize::Size8x8,
|
||||
|
@ -680,4 +710,19 @@ impl VectorSize {
|
|||
_ => *self,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the encoding bits that are used by some SIMD instructions
|
||||
/// for a particular operand size.
|
||||
pub fn enc_size(&self) -> (u32, u32) {
|
||||
let q = self.is_128bits() as u32;
|
||||
let size = match self.lane_size() {
|
||||
ScalarSize::Size8 => 0b00,
|
||||
ScalarSize::Size16 => 0b01,
|
||||
ScalarSize::Size32 => 0b10,
|
||||
ScalarSize::Size64 => 0b11,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
(q, size)
|
||||
}
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -4,10 +4,9 @@
|
|||
#[allow(dead_code)]
|
||||
use crate::ir::types::*;
|
||||
use crate::ir::Type;
|
||||
use crate::isa::aarch64::inst::OperandSize;
|
||||
use crate::machinst::*;
|
||||
use crate::isa::aarch64::inst::{OperandSize, ScalarSize};
|
||||
|
||||
use regalloc::RealRegUniverse;
|
||||
use regalloc::{PrettyPrint, RealRegUniverse};
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use std::string::String;
|
||||
|
@ -668,7 +667,41 @@ impl MoveWideConst {
|
|||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for NZCV {
|
||||
/// Advanced SIMD modified immediate as used by MOVI/MVNI.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ASIMDMovModImm {
|
||||
imm: u8,
|
||||
shift: u8,
|
||||
shift_ones: bool,
|
||||
}
|
||||
|
||||
impl ASIMDMovModImm {
|
||||
pub fn maybe_from_u64(value: u64, size: ScalarSize) -> Option<ASIMDMovModImm> {
|
||||
match size {
|
||||
ScalarSize::Size8 => Some(ASIMDMovModImm {
|
||||
imm: value as u8,
|
||||
shift: 0,
|
||||
shift_ones: false,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a zero immediate of this format.
|
||||
pub fn zero() -> Self {
|
||||
ASIMDMovModImm {
|
||||
imm: 0,
|
||||
shift: 0,
|
||||
shift_ones: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(&self) -> (u8, u32, bool) {
|
||||
(self.imm, self.shift as u32, self.shift_ones)
|
||||
}
|
||||
}
|
||||
|
||||
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 };
|
||||
format!(
|
||||
|
@ -681,13 +714,13 @@ impl ShowWithRRU for NZCV {
|
|||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for UImm5 {
|
||||
impl PrettyPrint for UImm5 {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
format!("#{}", self.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for Imm12 {
|
||||
impl PrettyPrint for Imm12 {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
let shift = if self.shift12 { 12 } else { 0 };
|
||||
let value = u32::from(self.bits) << shift;
|
||||
|
@ -695,49 +728,49 @@ impl ShowWithRRU for Imm12 {
|
|||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for SImm7Scaled {
|
||||
impl PrettyPrint for SImm7Scaled {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
format!("#{}", self.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for FPULeftShiftImm {
|
||||
impl PrettyPrint for FPULeftShiftImm {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
format!("#{}", self.amount)
|
||||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for FPURightShiftImm {
|
||||
impl PrettyPrint for FPURightShiftImm {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
format!("#{}", self.amount)
|
||||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for SImm9 {
|
||||
impl PrettyPrint for SImm9 {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
format!("#{}", self.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for UImm12Scaled {
|
||||
impl PrettyPrint for UImm12Scaled {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
format!("#{}", self.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for ImmLogic {
|
||||
impl PrettyPrint for ImmLogic {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
format!("#{}", self.value())
|
||||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for ImmShift {
|
||||
impl PrettyPrint for ImmShift {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
format!("#{}", self.imm)
|
||||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for MoveWideConst {
|
||||
impl PrettyPrint for MoveWideConst {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
if self.shift == 0 {
|
||||
format!("#{}", self.bits)
|
||||
|
@ -747,6 +780,17 @@ impl ShowWithRRU for MoveWideConst {
|
|||
}
|
||||
}
|
||||
|
||||
impl PrettyPrint for ASIMDMovModImm {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
if self.shift == 0 {
|
||||
format!("#{}", self.imm)
|
||||
} else {
|
||||
let shift_type = if self.shift_ones { "MSL" } else { "LSL" };
|
||||
format!("#{}, {} #{}", self.imm, shift_type, self.shift)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -3,10 +3,11 @@
|
|||
use crate::isa::aarch64::inst::OperandSize;
|
||||
use crate::isa::aarch64::inst::ScalarSize;
|
||||
use crate::isa::aarch64::inst::VectorSize;
|
||||
use crate::machinst::*;
|
||||
use crate::settings;
|
||||
|
||||
use regalloc::{RealRegUniverse, Reg, RegClass, RegClassInfo, Writable, NUM_REG_CLASSES};
|
||||
use regalloc::{
|
||||
PrettyPrint, RealRegUniverse, Reg, RegClass, RegClassInfo, Writable, NUM_REG_CLASSES,
|
||||
};
|
||||
|
||||
use std::string::{String, ToString};
|
||||
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
use super::*;
|
||||
use crate::isa::aarch64::inst::{args::PairAMode, imms::Imm12, regs, ALUOp, Inst};
|
||||
use crate::isa::unwind::input::{UnwindCode, UnwindInfo};
|
||||
use crate::machinst::UnwindInfoContext;
|
||||
use crate::result::CodegenResult;
|
||||
use alloc::vec::Vec;
|
||||
use regalloc::Reg;
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
pub(crate) mod systemv;
|
||||
|
||||
pub struct AArch64UnwindInfo;
|
||||
|
||||
impl UnwindInfoGenerator<Inst> for AArch64UnwindInfo {
|
||||
fn create_unwind_info(
|
||||
context: UnwindInfoContext<Inst>,
|
||||
) -> CodegenResult<Option<UnwindInfo<Reg>>> {
|
||||
let word_size = 8u8;
|
||||
let pair_size = word_size * 2;
|
||||
let mut codes = Vec::new();
|
||||
|
||||
for i in context.prologue.clone() {
|
||||
let i = i as usize;
|
||||
let inst = &context.insts[i];
|
||||
let offset = context.insts_layout[i];
|
||||
|
||||
match inst {
|
||||
Inst::StoreP64 {
|
||||
rt,
|
||||
rt2,
|
||||
mem: PairAMode::PreIndexed(rn, imm7),
|
||||
..
|
||||
} if *rt == regs::fp_reg()
|
||||
&& *rt2 == regs::link_reg()
|
||||
&& *rn == regs::writable_stack_reg()
|
||||
&& imm7.value == -(pair_size as i16) =>
|
||||
{
|
||||
// stp fp (x29), lr (x30), [sp, #-16]!
|
||||
codes.push((
|
||||
offset,
|
||||
UnwindCode::StackAlloc {
|
||||
size: pair_size as u32,
|
||||
},
|
||||
));
|
||||
codes.push((
|
||||
offset,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: *rt,
|
||||
stack_offset: 0,
|
||||
},
|
||||
));
|
||||
codes.push((
|
||||
offset,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: *rt2,
|
||||
stack_offset: word_size as u32,
|
||||
},
|
||||
));
|
||||
}
|
||||
Inst::StoreP64 {
|
||||
rt,
|
||||
rt2,
|
||||
mem: PairAMode::PreIndexed(rn, imm7),
|
||||
..
|
||||
} if rn.to_reg() == regs::stack_reg() && imm7.value % (pair_size as i16) == 0 => {
|
||||
// stp r1, r2, [sp, #(i * #16)]
|
||||
let stack_offset = imm7.value as u32;
|
||||
codes.push((
|
||||
offset,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: *rt,
|
||||
stack_offset,
|
||||
},
|
||||
));
|
||||
if *rt2 != regs::zero_reg() {
|
||||
codes.push((
|
||||
offset,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: *rt2,
|
||||
stack_offset: stack_offset + word_size as u32,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
Inst::AluRRImm12 {
|
||||
alu_op: ALUOp::Add64,
|
||||
rd,
|
||||
rn,
|
||||
imm12:
|
||||
Imm12 {
|
||||
bits: 0,
|
||||
shift12: false,
|
||||
},
|
||||
} if *rd == regs::writable_fp_reg() && *rn == regs::stack_reg() => {
|
||||
// mov fp (x29), sp.
|
||||
codes.push((offset, UnwindCode::SetFramePointer { reg: rd.to_reg() }));
|
||||
}
|
||||
Inst::VirtualSPOffsetAdj { offset: adj } if offset > 0 => {
|
||||
codes.push((offset, UnwindCode::StackAlloc { size: *adj as u32 }));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO epilogues
|
||||
|
||||
let prologue_size = if context.prologue.is_empty() {
|
||||
0
|
||||
} else {
|
||||
context.insts_layout[context.prologue.end as usize - 1]
|
||||
};
|
||||
|
||||
Ok(Some(UnwindInfo {
|
||||
prologue_size,
|
||||
prologue_unwind_codes: codes,
|
||||
epilogues_unwind_codes: vec![],
|
||||
function_size: context.len,
|
||||
word_size,
|
||||
initial_sp_offset: 0,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::cursor::{Cursor, FuncCursor};
|
||||
use crate::ir::{ExternalName, Function, InstBuilder, Signature, StackSlotData, StackSlotKind};
|
||||
use crate::isa::{lookup, CallConv};
|
||||
use crate::settings::{builder, Flags};
|
||||
use crate::Context;
|
||||
use std::str::FromStr;
|
||||
use target_lexicon::triple;
|
||||
|
||||
#[test]
|
||||
fn test_simple_func() {
|
||||
let isa = lookup(triple!("aarch64"))
|
||||
.expect("expect aarch64 ISA")
|
||||
.finish(Flags::new(builder()));
|
||||
|
||||
let mut context = Context::for_function(create_function(
|
||||
CallConv::SystemV,
|
||||
Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 64)),
|
||||
));
|
||||
|
||||
context.compile(&*isa).expect("expected compilation");
|
||||
|
||||
let result = context.mach_compile_result.unwrap();
|
||||
let unwind_info = result.unwind_info.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
unwind_info,
|
||||
UnwindInfo {
|
||||
prologue_size: 12,
|
||||
prologue_unwind_codes: vec![
|
||||
(4, UnwindCode::StackAlloc { size: 16 }),
|
||||
(
|
||||
4,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: regs::fp_reg(),
|
||||
stack_offset: 0
|
||||
}
|
||||
),
|
||||
(
|
||||
4,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: regs::link_reg(),
|
||||
stack_offset: 8
|
||||
}
|
||||
),
|
||||
(
|
||||
8,
|
||||
UnwindCode::SetFramePointer {
|
||||
reg: regs::fp_reg()
|
||||
}
|
||||
)
|
||||
],
|
||||
epilogues_unwind_codes: vec![],
|
||||
function_size: 24,
|
||||
word_size: 8,
|
||||
initial_sp_offset: 0,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function {
|
||||
let mut func =
|
||||
Function::with_name_signature(ExternalName::user(0, 0), Signature::new(call_conv));
|
||||
|
||||
let block0 = func.dfg.make_block();
|
||||
let mut pos = FuncCursor::new(&mut func);
|
||||
pos.insert_block(block0);
|
||||
pos.ins().return_(&[]);
|
||||
|
||||
if let Some(stack_slot) = stack_slot {
|
||||
func.stack_slots.push(stack_slot);
|
||||
}
|
||||
|
||||
func
|
||||
}
|
||||
}
|
158
third_party/rust/cranelift-codegen/src/isa/aarch64/inst/unwind/systemv.rs
поставляемый
Normal file
158
third_party/rust/cranelift-codegen/src/isa/aarch64/inst/unwind/systemv.rs
поставляемый
Normal file
|
@ -0,0 +1,158 @@
|
|||
//! Unwind information for System V ABI (Aarch64).
|
||||
|
||||
use crate::isa::aarch64::inst::regs;
|
||||
use crate::isa::unwind::input;
|
||||
use crate::isa::unwind::systemv::{RegisterMappingError, UnwindInfo};
|
||||
use crate::result::CodegenResult;
|
||||
use gimli::{write::CommonInformationEntry, Encoding, Format, Register};
|
||||
use regalloc::{Reg, RegClass};
|
||||
|
||||
/// Creates a new aarch64 common information entry (CIE).
|
||||
pub fn create_cie() -> CommonInformationEntry {
|
||||
use gimli::write::CallFrameInstruction;
|
||||
|
||||
let mut entry = CommonInformationEntry::new(
|
||||
Encoding {
|
||||
address_size: 8,
|
||||
format: Format::Dwarf32,
|
||||
version: 1,
|
||||
},
|
||||
4, // Code alignment factor
|
||||
-8, // Data alignment factor
|
||||
Register(regs::link_reg().get_hw_encoding().into()),
|
||||
);
|
||||
|
||||
// Every frame will start with the call frame address (CFA) at SP
|
||||
let sp = Register(regs::stack_reg().get_hw_encoding().into());
|
||||
entry.add_instruction(CallFrameInstruction::Cfa(sp, 0));
|
||||
|
||||
entry
|
||||
}
|
||||
|
||||
/// Map Cranelift registers to their corresponding Gimli registers.
|
||||
pub fn map_reg(reg: Reg) -> Result<Register, RegisterMappingError> {
|
||||
match reg.get_class() {
|
||||
RegClass::I64 => Ok(Register(reg.get_hw_encoding().into())),
|
||||
_ => Err(RegisterMappingError::UnsupportedRegisterBank("class?")),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create_unwind_info(
|
||||
unwind: input::UnwindInfo<Reg>,
|
||||
) -> CodegenResult<Option<UnwindInfo>> {
|
||||
struct RegisterMapper;
|
||||
impl crate::isa::unwind::systemv::RegisterMapper<Reg> for RegisterMapper {
|
||||
fn map(&self, reg: Reg) -> Result<u16, RegisterMappingError> {
|
||||
Ok(map_reg(reg)?.0)
|
||||
}
|
||||
fn sp(&self) -> u16 {
|
||||
regs::stack_reg().get_hw_encoding().into()
|
||||
}
|
||||
}
|
||||
let map = RegisterMapper;
|
||||
Ok(Some(UnwindInfo::build(unwind, &map)?))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::cursor::{Cursor, FuncCursor};
|
||||
use crate::ir::{
|
||||
types, AbiParam, ExternalName, Function, InstBuilder, Signature, StackSlotData,
|
||||
StackSlotKind,
|
||||
};
|
||||
use crate::isa::{lookup, CallConv};
|
||||
use crate::settings::{builder, Flags};
|
||||
use crate::Context;
|
||||
use gimli::write::Address;
|
||||
use std::str::FromStr;
|
||||
use target_lexicon::triple;
|
||||
|
||||
#[test]
|
||||
fn test_simple_func() {
|
||||
let isa = lookup(triple!("aarch64"))
|
||||
.expect("expect aarch64 ISA")
|
||||
.finish(Flags::new(builder()));
|
||||
|
||||
let mut context = Context::for_function(create_function(
|
||||
CallConv::SystemV,
|
||||
Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 64)),
|
||||
));
|
||||
|
||||
context.compile(&*isa).expect("expected compilation");
|
||||
|
||||
let fde = match context
|
||||
.create_unwind_info(isa.as_ref())
|
||||
.expect("can create unwind info")
|
||||
{
|
||||
Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
|
||||
info.to_fde(Address::Constant(1234))
|
||||
}
|
||||
_ => panic!("expected unwind information"),
|
||||
};
|
||||
|
||||
assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(1234), length: 24, lsda: None, instructions: [(4, CfaOffset(16)), (4, Offset(Register(29), -16)), (4, Offset(Register(30), -8)), (8, CfaRegister(Register(29)))] }");
|
||||
}
|
||||
|
||||
fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function {
|
||||
let mut func =
|
||||
Function::with_name_signature(ExternalName::user(0, 0), Signature::new(call_conv));
|
||||
|
||||
let block0 = func.dfg.make_block();
|
||||
let mut pos = FuncCursor::new(&mut func);
|
||||
pos.insert_block(block0);
|
||||
pos.ins().return_(&[]);
|
||||
|
||||
if let Some(stack_slot) = stack_slot {
|
||||
func.stack_slots.push(stack_slot);
|
||||
}
|
||||
|
||||
func
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_return_func() {
|
||||
let isa = lookup(triple!("aarch64"))
|
||||
.expect("expect aarch64 ISA")
|
||||
.finish(Flags::new(builder()));
|
||||
|
||||
let mut context = Context::for_function(create_multi_return_function(CallConv::SystemV));
|
||||
|
||||
context.compile(&*isa).expect("expected compilation");
|
||||
|
||||
let fde = match context
|
||||
.create_unwind_info(isa.as_ref())
|
||||
.expect("can create unwind info")
|
||||
{
|
||||
Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
|
||||
info.to_fde(Address::Constant(4321))
|
||||
}
|
||||
_ => panic!("expected unwind information"),
|
||||
};
|
||||
|
||||
assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(4321), length: 40, lsda: None, instructions: [(4, CfaOffset(16)), (4, Offset(Register(29), -16)), (4, Offset(Register(30), -8)), (8, CfaRegister(Register(29)))] }");
|
||||
}
|
||||
|
||||
fn create_multi_return_function(call_conv: CallConv) -> Function {
|
||||
let mut sig = Signature::new(call_conv);
|
||||
sig.params.push(AbiParam::new(types::I32));
|
||||
let mut func = Function::with_name_signature(ExternalName::user(0, 0), sig);
|
||||
|
||||
let block0 = func.dfg.make_block();
|
||||
let v0 = func.dfg.append_block_param(block0, types::I32);
|
||||
let block1 = func.dfg.make_block();
|
||||
let block2 = func.dfg.make_block();
|
||||
|
||||
let mut pos = FuncCursor::new(&mut func);
|
||||
pos.insert_block(block0);
|
||||
pos.ins().brnz(v0, block2, &[]);
|
||||
pos.ins().jump(block1, &[]);
|
||||
|
||||
pos.insert_block(block1);
|
||||
pos.ins().return_(&[]);
|
||||
|
||||
pos.insert_block(block2);
|
||||
pos.ins().return_(&[]);
|
||||
|
||||
func
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@
|
|||
use crate::ir::condcodes::{FloatCC, IntCC};
|
||||
use crate::ir::types::*;
|
||||
use crate::ir::Inst as IRInst;
|
||||
use crate::ir::{InstructionData, Opcode, Type};
|
||||
use crate::ir::{Opcode, Type};
|
||||
use crate::machinst::lower::*;
|
||||
use crate::machinst::*;
|
||||
use crate::CodegenResult;
|
||||
|
@ -20,6 +20,7 @@ use crate::isa::aarch64::AArch64Backend;
|
|||
|
||||
use super::lower_inst;
|
||||
|
||||
use crate::data_value::DataValue;
|
||||
use log::{debug, trace};
|
||||
use regalloc::{Reg, RegClass, Writable};
|
||||
use smallvec::SmallVec;
|
||||
|
@ -126,22 +127,9 @@ pub(crate) fn const_param_to_u128<C: LowerCtx<I = Inst>>(
|
|||
ctx: &mut C,
|
||||
inst: IRInst,
|
||||
) -> Option<u128> {
|
||||
let data = match ctx.data(inst) {
|
||||
&InstructionData::Shuffle { mask, .. } => ctx.get_immediate(mask),
|
||||
&InstructionData::UnaryConst {
|
||||
constant_handle, ..
|
||||
} => ctx.get_constant_data(constant_handle),
|
||||
_ => return None,
|
||||
};
|
||||
let data = data.clone().into_vec();
|
||||
|
||||
if data.len() == 16 {
|
||||
let mut bytes = [0u8; 16];
|
||||
|
||||
bytes.copy_from_slice(&data);
|
||||
Some(u128::from_le_bytes(bytes))
|
||||
} else {
|
||||
None
|
||||
match ctx.get_immediate(inst) {
|
||||
Some(DataValue::V128(bytes)) => Some(u128::from_le_bytes(bytes)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -586,11 +574,11 @@ type AddressAddend64List = SmallVec<[Reg; 4]>;
|
|||
/// - NarrowValueMode::ZeroExtend64: the associated input is 32 bits wide;
|
||||
/// do a zero-extension.
|
||||
///
|
||||
/// We do not descend further into the inputs of extensions, because supporting
|
||||
/// (e.g.) a 32-bit add that is later extended would require additional masking
|
||||
/// of high-order bits, which is too complex. So, in essence, we descend any
|
||||
/// number of adds from the roots, collecting all 64-bit address addends; then
|
||||
/// possibly support extensions at these leaves.
|
||||
/// We do not descend further into the inputs of extensions (unless it is a constant),
|
||||
/// because supporting (e.g.) a 32-bit add that is later extended would require
|
||||
/// additional masking of high-order bits, which is too complex. So, in essence, we
|
||||
/// descend any number of adds from the roots, collecting all 64-bit address addends;
|
||||
/// then possibly support extensions at these leaves.
|
||||
fn collect_address_addends<C: LowerCtx<I = Inst>>(
|
||||
ctx: &mut C,
|
||||
roots: &[InsnInput],
|
||||
|
@ -621,8 +609,20 @@ fn collect_address_addends<C: LowerCtx<I = Inst>>(
|
|||
ExtendOp::SXTW
|
||||
};
|
||||
let extendee_input = InsnInput { insn, input: 0 };
|
||||
let reg = put_input_in_reg(ctx, extendee_input, NarrowValueMode::None);
|
||||
result32.push((reg, extendop));
|
||||
// If the input is a zero-extension of a constant, add the value to the known
|
||||
// offset.
|
||||
// Only do this for zero-extension, as generating a sign-extended
|
||||
// constant may be more instructions than using the 'SXTW' addressing mode.
|
||||
if let (Some(insn), ExtendOp::UXTW) = (
|
||||
maybe_input_insn(ctx, extendee_input, Opcode::Iconst),
|
||||
extendop,
|
||||
) {
|
||||
let value = ctx.get_constant(insn).unwrap() as i64;
|
||||
offset += value;
|
||||
} else {
|
||||
let reg = put_input_in_reg(ctx, extendee_input, NarrowValueMode::None);
|
||||
result32.push((reg, extendop));
|
||||
}
|
||||
}
|
||||
Opcode::Uextend | Opcode::Sextend => {
|
||||
let reg = put_input_in_reg(ctx, input, NarrowValueMode::None);
|
||||
|
@ -825,7 +825,11 @@ pub(crate) fn lower_constant_f32<C: LowerCtx<I = Inst>>(
|
|||
rd: Writable<Reg>,
|
||||
value: f32,
|
||||
) {
|
||||
ctx.emit(Inst::load_fp_constant32(rd, value));
|
||||
let alloc_tmp = |class, ty| ctx.alloc_tmp(class, ty);
|
||||
|
||||
for inst in Inst::load_fp_constant32(rd, value.to_bits(), alloc_tmp) {
|
||||
ctx.emit(inst);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn lower_constant_f64<C: LowerCtx<I = Inst>>(
|
||||
|
@ -833,7 +837,11 @@ pub(crate) fn lower_constant_f64<C: LowerCtx<I = Inst>>(
|
|||
rd: Writable<Reg>,
|
||||
value: f64,
|
||||
) {
|
||||
ctx.emit(Inst::load_fp_constant64(rd, value));
|
||||
let alloc_tmp = |class, ty| ctx.alloc_tmp(class, ty);
|
||||
|
||||
for inst in Inst::load_fp_constant64(rd, value.to_bits(), alloc_tmp) {
|
||||
ctx.emit(inst);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn lower_constant_f128<C: LowerCtx<I = Inst>>(
|
||||
|
@ -841,7 +849,48 @@ pub(crate) fn lower_constant_f128<C: LowerCtx<I = Inst>>(
|
|||
rd: Writable<Reg>,
|
||||
value: u128,
|
||||
) {
|
||||
ctx.emit(Inst::load_fp_constant128(rd, value));
|
||||
if value == 0 {
|
||||
// Fast-track a common case. The general case, viz, calling `Inst::load_fp_constant128`,
|
||||
// is potentially expensive.
|
||||
ctx.emit(Inst::VecDupImm {
|
||||
rd,
|
||||
imm: ASIMDMovModImm::zero(),
|
||||
invert: false,
|
||||
size: VectorSize::Size8x16,
|
||||
});
|
||||
} else {
|
||||
let alloc_tmp = |class, ty| ctx.alloc_tmp(class, ty);
|
||||
for inst in Inst::load_fp_constant128(rd, value, alloc_tmp) {
|
||||
ctx.emit(inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn lower_splat_const<C: LowerCtx<I = Inst>>(
|
||||
ctx: &mut C,
|
||||
rd: Writable<Reg>,
|
||||
value: u64,
|
||||
size: VectorSize,
|
||||
) {
|
||||
let (value, narrow_size) = match size.lane_size() {
|
||||
ScalarSize::Size8 => (value as u8 as u64, ScalarSize::Size128),
|
||||
ScalarSize::Size16 => (value as u16 as u64, ScalarSize::Size8),
|
||||
ScalarSize::Size32 => (value as u32 as u64, ScalarSize::Size16),
|
||||
ScalarSize::Size64 => (value, ScalarSize::Size32),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let (value, size) = match Inst::get_replicated_vector_pattern(value as u128, narrow_size) {
|
||||
Some((value, lane_size)) => (
|
||||
value,
|
||||
VectorSize::from_lane_size(lane_size, size.is_128bits()),
|
||||
),
|
||||
None => (value, size),
|
||||
};
|
||||
let alloc_tmp = |class, ty| ctx.alloc_tmp(class, ty);
|
||||
|
||||
for inst in Inst::load_replicated_vector_pattern(rd, value, size, alloc_tmp) {
|
||||
ctx.emit(inst);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn lower_condcode(cc: IntCC) -> Cond {
|
||||
|
|
|
@ -179,8 +179,16 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
let vb = ctx.alloc_tmp(RegClass::V128, I128);
|
||||
let ra = put_input_in_reg(ctx, inputs[0], narrow_mode);
|
||||
let rb = put_input_in_reg(ctx, inputs[1], narrow_mode);
|
||||
ctx.emit(Inst::MovToFpu { rd: va, rn: ra });
|
||||
ctx.emit(Inst::MovToFpu { rd: vb, rn: rb });
|
||||
ctx.emit(Inst::MovToFpu {
|
||||
rd: va,
|
||||
rn: ra,
|
||||
size: ScalarSize::Size64,
|
||||
});
|
||||
ctx.emit(Inst::MovToFpu {
|
||||
rd: vb,
|
||||
rn: rb,
|
||||
size: ScalarSize::Size64,
|
||||
});
|
||||
ctx.emit(Inst::FpuRRR {
|
||||
fpu_op,
|
||||
rd: va,
|
||||
|
@ -476,9 +484,9 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
// msub rd, rd, rm, rn ; rd = rn - rd * rm
|
||||
|
||||
// Check for divide by 0.
|
||||
let trap_info = (ctx.srcloc(insn), TrapCode::IntegerDivisionByZero);
|
||||
let trap_code = TrapCode::IntegerDivisionByZero;
|
||||
ctx.emit(Inst::TrapIf {
|
||||
trap_info,
|
||||
trap_code,
|
||||
kind: CondBrKind::Zero(rm),
|
||||
});
|
||||
|
||||
|
@ -499,9 +507,9 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
// udf ; signed overflow
|
||||
|
||||
// Check for divide by 0.
|
||||
let trap_info = (ctx.srcloc(insn), TrapCode::IntegerDivisionByZero);
|
||||
let trap_code = TrapCode::IntegerDivisionByZero;
|
||||
ctx.emit(Inst::TrapIf {
|
||||
trap_info,
|
||||
trap_code,
|
||||
kind: CondBrKind::Zero(rm),
|
||||
});
|
||||
|
||||
|
@ -527,9 +535,9 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
nzcv: NZCV::new(false, false, false, false),
|
||||
cond: Cond::Eq,
|
||||
});
|
||||
let trap_info = (ctx.srcloc(insn), TrapCode::IntegerOverflow);
|
||||
let trap_code = TrapCode::IntegerOverflow;
|
||||
ctx.emit(Inst::TrapIf {
|
||||
trap_info,
|
||||
trap_code,
|
||||
kind: CondBrKind::Cond(Cond::Vs),
|
||||
});
|
||||
} else {
|
||||
|
@ -537,9 +545,9 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
// udf ; divide by zero
|
||||
|
||||
// Check for divide by 0.
|
||||
let trap_info = (ctx.srcloc(insn), TrapCode::IntegerDivisionByZero);
|
||||
let trap_code = TrapCode::IntegerDivisionByZero;
|
||||
ctx.emit(Inst::TrapIf {
|
||||
trap_info,
|
||||
trap_code,
|
||||
kind: CondBrKind::Zero(rm),
|
||||
});
|
||||
}
|
||||
|
@ -1152,28 +1160,24 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
|
||||
let mem = lower_address(ctx, elem_ty, &inputs[..], off);
|
||||
let rd = get_output_reg(ctx, outputs[0]);
|
||||
|
||||
let memflags = ctx.memflags(insn).expect("memory flags");
|
||||
let srcloc = if !memflags.notrap() {
|
||||
Some(ctx.srcloc(insn))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let flags = ctx
|
||||
.memflags(insn)
|
||||
.expect("Load instruction should have memflags");
|
||||
|
||||
ctx.emit(match (ty_bits(elem_ty), sign_extend, is_float) {
|
||||
(1, _, _) => Inst::ULoad8 { rd, mem, srcloc },
|
||||
(8, false, _) => Inst::ULoad8 { rd, mem, srcloc },
|
||||
(8, true, _) => Inst::SLoad8 { rd, mem, srcloc },
|
||||
(16, false, _) => Inst::ULoad16 { rd, mem, srcloc },
|
||||
(16, true, _) => Inst::SLoad16 { rd, mem, srcloc },
|
||||
(32, false, false) => Inst::ULoad32 { rd, mem, srcloc },
|
||||
(32, true, false) => Inst::SLoad32 { rd, mem, srcloc },
|
||||
(32, _, true) => Inst::FpuLoad32 { rd, mem, srcloc },
|
||||
(64, _, false) => Inst::ULoad64 { rd, mem, srcloc },
|
||||
(1, _, _) => Inst::ULoad8 { rd, mem, flags },
|
||||
(8, false, _) => Inst::ULoad8 { rd, mem, flags },
|
||||
(8, true, _) => Inst::SLoad8 { rd, mem, flags },
|
||||
(16, false, _) => Inst::ULoad16 { rd, mem, flags },
|
||||
(16, true, _) => Inst::SLoad16 { rd, mem, flags },
|
||||
(32, false, false) => Inst::ULoad32 { rd, mem, flags },
|
||||
(32, true, false) => Inst::SLoad32 { rd, mem, flags },
|
||||
(32, _, true) => Inst::FpuLoad32 { rd, mem, flags },
|
||||
(64, _, false) => Inst::ULoad64 { rd, mem, flags },
|
||||
// Note that we treat some of the vector loads as scalar floating-point loads,
|
||||
// which is correct in a little endian environment.
|
||||
(64, _, true) => Inst::FpuLoad64 { rd, mem, srcloc },
|
||||
(128, _, _) => Inst::FpuLoad128 { rd, mem, srcloc },
|
||||
(64, _, true) => Inst::FpuLoad64 { rd, mem, flags },
|
||||
(128, _, _) => Inst::FpuLoad128 { rd, mem, flags },
|
||||
_ => panic!("Unsupported size in load"),
|
||||
});
|
||||
|
||||
|
@ -1197,6 +1201,22 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
}
|
||||
}
|
||||
|
||||
Opcode::LoadSplat => {
|
||||
let off = ctx.data(insn).load_store_offset().unwrap();
|
||||
let ty = ty.unwrap();
|
||||
let mem = lower_address(ctx, ty.lane_type(), &inputs[..], off);
|
||||
let rd = get_output_reg(ctx, outputs[0]);
|
||||
let size = VectorSize::from_ty(ty);
|
||||
let tmp = ctx.alloc_tmp(RegClass::I64, I64);
|
||||
|
||||
ctx.emit(Inst::LoadAddr { rd: tmp, mem });
|
||||
ctx.emit(Inst::VecLoadReplicate {
|
||||
rd,
|
||||
rn: tmp.to_reg(),
|
||||
size,
|
||||
});
|
||||
}
|
||||
|
||||
Opcode::Store
|
||||
| Opcode::Istore8
|
||||
| Opcode::Istore16
|
||||
|
@ -1214,25 +1234,21 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
_ => unreachable!(),
|
||||
};
|
||||
let is_float = ty_has_float_or_vec_representation(elem_ty);
|
||||
let flags = ctx
|
||||
.memflags(insn)
|
||||
.expect("Store instruction should have memflags");
|
||||
|
||||
let mem = lower_address(ctx, elem_ty, &inputs[1..], off);
|
||||
let rd = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
|
||||
|
||||
let memflags = ctx.memflags(insn).expect("memory flags");
|
||||
let srcloc = if !memflags.notrap() {
|
||||
Some(ctx.srcloc(insn))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
ctx.emit(match (ty_bits(elem_ty), is_float) {
|
||||
(1, _) | (8, _) => Inst::Store8 { rd, mem, srcloc },
|
||||
(16, _) => Inst::Store16 { rd, mem, srcloc },
|
||||
(32, false) => Inst::Store32 { rd, mem, srcloc },
|
||||
(32, true) => Inst::FpuStore32 { rd, mem, srcloc },
|
||||
(64, false) => Inst::Store64 { rd, mem, srcloc },
|
||||
(64, true) => Inst::FpuStore64 { rd, mem, srcloc },
|
||||
(128, _) => Inst::FpuStore128 { rd, mem, srcloc },
|
||||
(1, _) | (8, _) => Inst::Store8 { rd, mem, flags },
|
||||
(16, _) => Inst::Store16 { rd, mem, flags },
|
||||
(32, false) => Inst::Store32 { rd, mem, flags },
|
||||
(32, true) => Inst::FpuStore32 { rd, mem, flags },
|
||||
(64, false) => Inst::Store64 { rd, mem, flags },
|
||||
(64, true) => Inst::FpuStore64 { rd, mem, flags },
|
||||
(128, _) => Inst::FpuStore128 { rd, mem, flags },
|
||||
_ => panic!("Unsupported size in store"),
|
||||
});
|
||||
}
|
||||
|
@ -1260,12 +1276,6 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
let mut r_arg2 = put_input_in_reg(ctx, inputs[1], NarrowValueMode::None);
|
||||
let ty_access = ty.unwrap();
|
||||
assert!(is_valid_atomic_transaction_ty(ty_access));
|
||||
let memflags = ctx.memflags(insn).expect("memory flags");
|
||||
let srcloc = if !memflags.notrap() {
|
||||
Some(ctx.srcloc(insn))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// Make sure that both args are in virtual regs, since in effect
|
||||
// we have to do a parallel copy to get them safely to the AtomicRMW input
|
||||
// regs, and that's not guaranteed safe if either is in a real reg.
|
||||
|
@ -1276,11 +1286,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
ctx.emit(Inst::gen_move(Writable::from_reg(xreg(26)), r_arg2, I64));
|
||||
// Now the AtomicRMW insn itself
|
||||
let op = inst_common::AtomicRmwOp::from(ctx.data(insn).atomic_rmw_op().unwrap());
|
||||
ctx.emit(Inst::AtomicRMW {
|
||||
ty: ty_access,
|
||||
op,
|
||||
srcloc,
|
||||
});
|
||||
ctx.emit(Inst::AtomicRMW { ty: ty_access, op });
|
||||
// And finally, copy the preordained AtomicRMW output reg to its destination.
|
||||
ctx.emit(Inst::gen_move(r_dst, xreg(27), I64));
|
||||
// Also, x24 and x28 are trashed. `fn aarch64_get_regs` must mention that.
|
||||
|
@ -1296,12 +1302,6 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
let mut r_replacement = put_input_in_reg(ctx, inputs[2], NarrowValueMode::None);
|
||||
let ty_access = ty.unwrap();
|
||||
assert!(is_valid_atomic_transaction_ty(ty_access));
|
||||
let memflags = ctx.memflags(insn).expect("memory flags");
|
||||
let srcloc = if !memflags.notrap() {
|
||||
Some(ctx.srcloc(insn))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// Make sure that all three args are in virtual regs. See corresponding comment
|
||||
// for `Opcode::AtomicRmw` above.
|
||||
r_addr = ctx.ensure_in_vreg(r_addr, I64);
|
||||
|
@ -1320,10 +1320,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
I64,
|
||||
));
|
||||
// Now the AtomicCAS itself, implemented in the normal way, with an LL-SC loop
|
||||
ctx.emit(Inst::AtomicCAS {
|
||||
ty: ty_access,
|
||||
srcloc,
|
||||
});
|
||||
ctx.emit(Inst::AtomicCAS { ty: ty_access });
|
||||
// And finally, copy the preordained AtomicCAS output reg to its destination.
|
||||
ctx.emit(Inst::gen_move(r_dst, xreg(27), I64));
|
||||
// Also, x24 and x28 are trashed. `fn aarch64_get_regs` must mention that.
|
||||
|
@ -1334,17 +1331,10 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
let r_addr = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
|
||||
let ty_access = ty.unwrap();
|
||||
assert!(is_valid_atomic_transaction_ty(ty_access));
|
||||
let memflags = ctx.memflags(insn).expect("memory flags");
|
||||
let srcloc = if !memflags.notrap() {
|
||||
Some(ctx.srcloc(insn))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
ctx.emit(Inst::AtomicLoad {
|
||||
ty: ty_access,
|
||||
r_data,
|
||||
r_addr,
|
||||
srcloc,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1353,17 +1343,10 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
let r_addr = put_input_in_reg(ctx, inputs[1], NarrowValueMode::None);
|
||||
let ty_access = ctx.input_ty(insn, 0);
|
||||
assert!(is_valid_atomic_transaction_ty(ty_access));
|
||||
let memflags = ctx.memflags(insn).expect("memory flags");
|
||||
let srcloc = if !memflags.notrap() {
|
||||
Some(ctx.srcloc(insn))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
ctx.emit(Inst::AtomicStore {
|
||||
ty: ty_access,
|
||||
r_data,
|
||||
r_addr,
|
||||
srcloc,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1435,6 +1418,8 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
ctx.emit(Inst::FpuCSel32 { cond, rd, rn, rm });
|
||||
} else if is_float && bits == 64 {
|
||||
ctx.emit(Inst::FpuCSel64 { cond, rd, rn, rm });
|
||||
} else if is_float && bits == 128 {
|
||||
ctx.emit(Inst::VecCSel { cond, rd, rn, rm });
|
||||
} else {
|
||||
ctx.emit(Inst::CSel { cond, rd, rn, rm });
|
||||
}
|
||||
|
@ -1680,7 +1665,11 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
}
|
||||
(false, true) => {
|
||||
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::ZeroExtend64);
|
||||
ctx.emit(Inst::MovToFpu { rd, rn });
|
||||
ctx.emit(Inst::MovToFpu {
|
||||
rd,
|
||||
rn,
|
||||
size: ScalarSize::Size64,
|
||||
});
|
||||
}
|
||||
(true, false) => {
|
||||
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
|
||||
|
@ -1776,12 +1765,12 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
}
|
||||
|
||||
Opcode::Trap | Opcode::ResumableTrap => {
|
||||
let trap_info = (ctx.srcloc(insn), ctx.data(insn).trap_code().unwrap());
|
||||
ctx.emit_safepoint(Inst::Udf { trap_info });
|
||||
let trap_code = ctx.data(insn).trap_code().unwrap();
|
||||
ctx.emit_safepoint(Inst::Udf { trap_code });
|
||||
}
|
||||
|
||||
Opcode::Trapif | Opcode::Trapff => {
|
||||
let trap_info = (ctx.srcloc(insn), ctx.data(insn).trap_code().unwrap());
|
||||
let trap_code = ctx.data(insn).trap_code().unwrap();
|
||||
|
||||
let cond = if maybe_input_insn(ctx, inputs[0], Opcode::IaddIfcout).is_some() {
|
||||
let condcode = ctx.data(insn).cond_code().unwrap();
|
||||
|
@ -1812,7 +1801,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
};
|
||||
|
||||
ctx.emit_safepoint(Inst::TrapIf {
|
||||
trap_info,
|
||||
trap_code,
|
||||
kind: CondBrKind::Cond(cond),
|
||||
});
|
||||
}
|
||||
|
@ -1829,11 +1818,9 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
let rd = get_output_reg(ctx, outputs[0]);
|
||||
let (extname, _) = ctx.call_target(insn).unwrap();
|
||||
let extname = extname.clone();
|
||||
let loc = ctx.srcloc(insn);
|
||||
ctx.emit(Inst::LoadExtName {
|
||||
rd,
|
||||
name: Box::new(extname),
|
||||
srcloc: loc,
|
||||
offset: 0,
|
||||
});
|
||||
}
|
||||
|
@ -1846,17 +1833,15 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
let rd = get_output_reg(ctx, outputs[0]);
|
||||
let (extname, _, offset) = ctx.symbol_value(insn).unwrap();
|
||||
let extname = extname.clone();
|
||||
let loc = ctx.srcloc(insn);
|
||||
ctx.emit(Inst::LoadExtName {
|
||||
rd,
|
||||
name: Box::new(extname),
|
||||
srcloc: loc,
|
||||
offset,
|
||||
});
|
||||
}
|
||||
|
||||
Opcode::Call | Opcode::CallIndirect => {
|
||||
let loc = ctx.srcloc(insn);
|
||||
let caller_conv = ctx.abi().call_conv();
|
||||
let (mut abi, inputs) = match op {
|
||||
Opcode::Call => {
|
||||
let (extname, dist) = ctx.call_target(insn).unwrap();
|
||||
|
@ -1865,7 +1850,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
assert!(inputs.len() == sig.params.len());
|
||||
assert!(outputs.len() == sig.returns.len());
|
||||
(
|
||||
AArch64ABICaller::from_func(sig, &extname, dist, loc)?,
|
||||
AArch64ABICaller::from_func(sig, &extname, dist, caller_conv)?,
|
||||
&inputs[..],
|
||||
)
|
||||
}
|
||||
|
@ -1874,7 +1859,10 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
let sig = ctx.call_sig(insn).unwrap();
|
||||
assert!(inputs.len() - 1 == sig.params.len());
|
||||
assert!(outputs.len() == sig.returns.len());
|
||||
(AArch64ABICaller::from_ptr(sig, ptr, loc, op)?, &inputs[1..])
|
||||
(
|
||||
AArch64ABICaller::from_ptr(sig, ptr, op, caller_conv)?,
|
||||
&inputs[1..],
|
||||
)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
@ -1986,24 +1974,67 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
ctx.emit(Inst::VecMovElement {
|
||||
rd,
|
||||
rn,
|
||||
idx1: idx,
|
||||
idx2: 0,
|
||||
dest_idx: idx,
|
||||
src_idx: 0,
|
||||
size,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Opcode::Splat => {
|
||||
let rd = get_output_reg(ctx, outputs[0]);
|
||||
let size = VectorSize::from_ty(ty.unwrap());
|
||||
|
||||
if let Some((_, insn)) = maybe_input_insn_multi(
|
||||
ctx,
|
||||
inputs[0],
|
||||
&[
|
||||
Opcode::Bconst,
|
||||
Opcode::F32const,
|
||||
Opcode::F64const,
|
||||
Opcode::Iconst,
|
||||
],
|
||||
) {
|
||||
lower_splat_const(ctx, rd, ctx.get_constant(insn).unwrap(), size);
|
||||
} else if let Some(insn) =
|
||||
maybe_input_insn_via_conv(ctx, inputs[0], Opcode::Iconst, Opcode::Ireduce)
|
||||
{
|
||||
lower_splat_const(ctx, rd, ctx.get_constant(insn).unwrap(), size);
|
||||
} else if let Some(insn) =
|
||||
maybe_input_insn_via_conv(ctx, inputs[0], Opcode::Bconst, Opcode::Breduce)
|
||||
{
|
||||
lower_splat_const(ctx, rd, ctx.get_constant(insn).unwrap(), size);
|
||||
} else {
|
||||
let input_ty = ctx.input_ty(insn, 0);
|
||||
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
|
||||
let inst = if ty_has_int_representation(input_ty) {
|
||||
Inst::VecDup { rd, rn, size }
|
||||
} else {
|
||||
Inst::VecDupFromFpu { rd, rn, size }
|
||||
};
|
||||
|
||||
ctx.emit(inst);
|
||||
}
|
||||
}
|
||||
|
||||
Opcode::ScalarToVector => {
|
||||
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
|
||||
let rd = get_output_reg(ctx, outputs[0]);
|
||||
let input_ty = ctx.input_ty(insn, 0);
|
||||
let size = VectorSize::from_ty(ty.unwrap());
|
||||
let inst = if ty_has_int_representation(input_ty) {
|
||||
Inst::VecDup { rd, rn, size }
|
||||
if (input_ty == I32 && ty.unwrap() == I32X4)
|
||||
|| (input_ty == I64 && ty.unwrap() == I64X2)
|
||||
{
|
||||
ctx.emit(Inst::MovToFpu {
|
||||
rd,
|
||||
rn,
|
||||
size: ScalarSize::from_ty(input_ty),
|
||||
});
|
||||
} else {
|
||||
Inst::VecDupFromFpu { rd, rn, size }
|
||||
};
|
||||
ctx.emit(inst);
|
||||
return Err(CodegenError::Unsupported(format!(
|
||||
"ScalarToVector: unsupported types {:?} -> {:?}",
|
||||
input_ty, ty
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
Opcode::VanyTrue | Opcode::VallTrue => {
|
||||
|
@ -2056,6 +2087,197 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
normalize_bool_result(ctx, insn, rd);
|
||||
}
|
||||
|
||||
Opcode::VhighBits => {
|
||||
let dst_r = get_output_reg(ctx, outputs[0]);
|
||||
let src_v = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
|
||||
let ty = ctx.input_ty(insn, 0);
|
||||
// All three sequences use one integer temporary and two vector temporaries. The
|
||||
// shift is done early so as to give the register allocator the possibility of using
|
||||
// the same reg for `tmp_v1` and `src_v` in the case that this is the last use of
|
||||
// `src_v`. See https://github.com/WebAssembly/simd/pull/201 for the background and
|
||||
// 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);
|
||||
match ty {
|
||||
I8X16 => {
|
||||
// sshr tmp_v1.16b, src_v.16b, #7
|
||||
// mov tmp_r0, #0x0201
|
||||
// movk tmp_r0, #0x0804, lsl 16
|
||||
// movk tmp_r0, #0x2010, lsl 32
|
||||
// movk tmp_r0, #0x8040, lsl 48
|
||||
// dup tmp_v0.2d, tmp_r0
|
||||
// and tmp_v1.16b, tmp_v1.16b, tmp_v0.16b
|
||||
// ext tmp_v0.16b, tmp_v1.16b, tmp_v1.16b, #8
|
||||
// zip1 tmp_v0.16b, tmp_v1.16b, tmp_v0.16b
|
||||
// addv tmp_v0h, tmp_v0.8h
|
||||
// mov dst_r, tmp_v0.h[0]
|
||||
ctx.emit(Inst::VecShiftImm {
|
||||
op: VecShiftImmOp::Sshr,
|
||||
rd: tmp_v1,
|
||||
rn: src_v,
|
||||
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,
|
||||
});
|
||||
ctx.emit(Inst::VecRRR {
|
||||
alu_op: VecALUOp::And,
|
||||
rd: tmp_v1,
|
||||
rn: tmp_v1.to_reg(),
|
||||
rm: tmp_v0.to_reg(),
|
||||
size: VectorSize::Size8x16,
|
||||
});
|
||||
ctx.emit(Inst::VecExtract {
|
||||
rd: tmp_v0,
|
||||
rn: tmp_v1.to_reg(),
|
||||
rm: tmp_v1.to_reg(),
|
||||
imm4: 8,
|
||||
});
|
||||
ctx.emit(Inst::VecRRR {
|
||||
alu_op: VecALUOp::Zip1,
|
||||
rd: tmp_v0,
|
||||
rn: tmp_v1.to_reg(),
|
||||
rm: tmp_v0.to_reg(),
|
||||
size: VectorSize::Size8x16,
|
||||
});
|
||||
ctx.emit(Inst::VecLanes {
|
||||
op: VecLanesOp::Addv,
|
||||
rd: tmp_v0,
|
||||
rn: tmp_v0.to_reg(),
|
||||
size: VectorSize::Size16x8,
|
||||
});
|
||||
ctx.emit(Inst::MovFromVec {
|
||||
rd: dst_r,
|
||||
rn: tmp_v0.to_reg(),
|
||||
idx: 0,
|
||||
size: VectorSize::Size16x8,
|
||||
});
|
||||
}
|
||||
I16X8 => {
|
||||
// sshr tmp_v1.8h, src_v.8h, #15
|
||||
// mov tmp_r0, #0x1
|
||||
// movk tmp_r0, #0x2, lsl 16
|
||||
// movk tmp_r0, #0x4, lsl 32
|
||||
// movk tmp_r0, #0x8, lsl 48
|
||||
// dup tmp_v0.2d, tmp_r0
|
||||
// shl tmp_r0, tmp_r0, #4
|
||||
// mov tmp_v0.d[1], tmp_r0
|
||||
// and tmp_v0.16b, tmp_v1.16b, tmp_v0.16b
|
||||
// addv tmp_v0h, tmp_v0.8h
|
||||
// mov dst_r, tmp_v0.h[0]
|
||||
ctx.emit(Inst::VecShiftImm {
|
||||
op: VecShiftImmOp::Sshr,
|
||||
rd: tmp_v1,
|
||||
rn: src_v,
|
||||
size: VectorSize::Size16x8,
|
||||
imm: 15,
|
||||
});
|
||||
lower_constant_u64(ctx, tmp_r0, 0x0008000400020001u64);
|
||||
ctx.emit(Inst::VecDup {
|
||||
rd: tmp_v0,
|
||||
rn: tmp_r0.to_reg(),
|
||||
size: VectorSize::Size64x2,
|
||||
});
|
||||
ctx.emit(Inst::AluRRImmShift {
|
||||
alu_op: ALUOp::Lsl64,
|
||||
rd: tmp_r0,
|
||||
rn: tmp_r0.to_reg(),
|
||||
immshift: ImmShift { imm: 4 },
|
||||
});
|
||||
ctx.emit(Inst::MovToVec {
|
||||
rd: tmp_v0,
|
||||
rn: tmp_r0.to_reg(),
|
||||
idx: 1,
|
||||
size: VectorSize::Size64x2,
|
||||
});
|
||||
ctx.emit(Inst::VecRRR {
|
||||
alu_op: VecALUOp::And,
|
||||
rd: tmp_v0,
|
||||
rn: tmp_v1.to_reg(),
|
||||
rm: tmp_v0.to_reg(),
|
||||
size: VectorSize::Size8x16,
|
||||
});
|
||||
ctx.emit(Inst::VecLanes {
|
||||
op: VecLanesOp::Addv,
|
||||
rd: tmp_v0,
|
||||
rn: tmp_v0.to_reg(),
|
||||
size: VectorSize::Size16x8,
|
||||
});
|
||||
ctx.emit(Inst::MovFromVec {
|
||||
rd: dst_r,
|
||||
rn: tmp_v0.to_reg(),
|
||||
idx: 0,
|
||||
size: VectorSize::Size16x8,
|
||||
});
|
||||
}
|
||||
I32X4 => {
|
||||
// sshr tmp_v1.4s, src_v.4s, #31
|
||||
// mov tmp_r0, #0x1
|
||||
// movk tmp_r0, #0x2, lsl 32
|
||||
// dup tmp_v0.2d, tmp_r0
|
||||
// shl tmp_r0, tmp_r0, #2
|
||||
// mov tmp_v0.d[1], tmp_r0
|
||||
// and tmp_v0.16b, tmp_v1.16b, tmp_v0.16b
|
||||
// addv tmp_v0s, tmp_v0.4s
|
||||
// mov dst_r, tmp_v0.s[0]
|
||||
ctx.emit(Inst::VecShiftImm {
|
||||
op: VecShiftImmOp::Sshr,
|
||||
rd: tmp_v1,
|
||||
rn: src_v,
|
||||
size: VectorSize::Size32x4,
|
||||
imm: 31,
|
||||
});
|
||||
lower_constant_u64(ctx, tmp_r0, 0x0000000200000001u64);
|
||||
ctx.emit(Inst::VecDup {
|
||||
rd: tmp_v0,
|
||||
rn: tmp_r0.to_reg(),
|
||||
size: VectorSize::Size64x2,
|
||||
});
|
||||
ctx.emit(Inst::AluRRImmShift {
|
||||
alu_op: ALUOp::Lsl64,
|
||||
rd: tmp_r0,
|
||||
rn: tmp_r0.to_reg(),
|
||||
immshift: ImmShift { imm: 2 },
|
||||
});
|
||||
ctx.emit(Inst::MovToVec {
|
||||
rd: tmp_v0,
|
||||
rn: tmp_r0.to_reg(),
|
||||
idx: 1,
|
||||
size: VectorSize::Size64x2,
|
||||
});
|
||||
ctx.emit(Inst::VecRRR {
|
||||
alu_op: VecALUOp::And,
|
||||
rd: tmp_v0,
|
||||
rn: tmp_v1.to_reg(),
|
||||
rm: tmp_v0.to_reg(),
|
||||
size: VectorSize::Size8x16,
|
||||
});
|
||||
ctx.emit(Inst::VecLanes {
|
||||
op: VecLanesOp::Addv,
|
||||
rd: tmp_v0,
|
||||
rn: tmp_v0.to_reg(),
|
||||
size: VectorSize::Size32x4,
|
||||
});
|
||||
ctx.emit(Inst::MovFromVec {
|
||||
rd: dst_r,
|
||||
rn: tmp_v0.to_reg(),
|
||||
idx: 0,
|
||||
size: VectorSize::Size32x4,
|
||||
});
|
||||
}
|
||||
_ => panic!("arm64 isel: VhighBits unhandled, ty = {:?}", ty),
|
||||
}
|
||||
}
|
||||
|
||||
Opcode::Shuffle => {
|
||||
let mask = const_param_to_u128(ctx, insn).expect("Invalid immediate mask bytes");
|
||||
let rd = get_output_reg(ctx, outputs[0]);
|
||||
|
@ -2100,7 +2322,6 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
|
||||
Opcode::Vsplit
|
||||
| Opcode::Vconcat
|
||||
| Opcode::ScalarToVector
|
||||
| Opcode::Uload8x8Complex
|
||||
| Opcode::Sload8x8Complex
|
||||
| Opcode::Uload16x4Complex
|
||||
|
@ -2134,6 +2355,47 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
});
|
||||
}
|
||||
|
||||
Opcode::WideningPairwiseDotProductS => {
|
||||
let r_y = get_output_reg(ctx, outputs[0]);
|
||||
let r_a = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
|
||||
let r_b = put_input_in_reg(ctx, inputs[1], NarrowValueMode::None);
|
||||
let ty = ty.unwrap();
|
||||
if ty == I32X4 {
|
||||
let tmp = ctx.alloc_tmp(RegClass::V128, I8X16);
|
||||
// The args have type I16X8.
|
||||
// "y = i32x4.dot_i16x8_s(a, b)"
|
||||
// => smull tmp, a, b
|
||||
// smull2 y, a, b
|
||||
// addp y, tmp, y
|
||||
ctx.emit(Inst::VecRRR {
|
||||
alu_op: VecALUOp::Smull,
|
||||
rd: tmp,
|
||||
rn: r_a,
|
||||
rm: r_b,
|
||||
size: VectorSize::Size16x8,
|
||||
});
|
||||
ctx.emit(Inst::VecRRR {
|
||||
alu_op: VecALUOp::Smull2,
|
||||
rd: r_y,
|
||||
rn: r_a,
|
||||
rm: r_b,
|
||||
size: VectorSize::Size16x8,
|
||||
});
|
||||
ctx.emit(Inst::VecRRR {
|
||||
alu_op: VecALUOp::Addp,
|
||||
rd: r_y,
|
||||
rn: tmp.to_reg(),
|
||||
rm: r_y.to_reg(),
|
||||
size: VectorSize::Size32x4,
|
||||
});
|
||||
} else {
|
||||
return Err(CodegenError::Unsupported(format!(
|
||||
"Opcode::WideningPairwiseDotProductS: unsupported laneage: {:?}",
|
||||
ty
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
Opcode::Fadd | Opcode::Fsub | Opcode::Fmul | Opcode::Fdiv | Opcode::Fmin | Opcode::Fmax => {
|
||||
let ty = ty.unwrap();
|
||||
let bits = ty_bits(ty);
|
||||
|
@ -2178,6 +2440,43 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
}
|
||||
}
|
||||
|
||||
Opcode::FminPseudo | Opcode::FmaxPseudo => {
|
||||
let ty = ctx.input_ty(insn, 0);
|
||||
if ty == F32X4 || ty == F64X2 {
|
||||
// pmin(a,b) => bitsel(b, a, cmpgt(a, b))
|
||||
// pmax(a,b) => bitsel(b, a, cmpgt(b, a))
|
||||
let r_dst = get_output_reg(ctx, outputs[0]);
|
||||
let r_a = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
|
||||
let r_b = put_input_in_reg(ctx, inputs[1], NarrowValueMode::None);
|
||||
// Since we're going to write the output register `r_dst` anyway, we might as
|
||||
// well first use it to hold the comparison result. This has the slightly unusual
|
||||
// effect that we modify the output register in the first instruction (`fcmgt`)
|
||||
// but read both the inputs again in the second instruction (`bsl`), which means
|
||||
// that the output register can't be either of the input registers. Regalloc
|
||||
// should handle this correctly, nevertheless.
|
||||
ctx.emit(Inst::VecRRR {
|
||||
alu_op: VecALUOp::Fcmgt,
|
||||
rd: r_dst,
|
||||
rn: if op == Opcode::FminPseudo { r_a } else { r_b },
|
||||
rm: if op == Opcode::FminPseudo { r_b } else { r_a },
|
||||
size: if ty == F32X4 {
|
||||
VectorSize::Size32x4
|
||||
} else {
|
||||
VectorSize::Size64x2
|
||||
},
|
||||
});
|
||||
ctx.emit(Inst::VecRRR {
|
||||
alu_op: VecALUOp::Bsl,
|
||||
rd: r_dst,
|
||||
rn: r_b,
|
||||
rm: r_a,
|
||||
size: VectorSize::Size8x16,
|
||||
});
|
||||
} else {
|
||||
panic!("Opcode::FminPseudo | Opcode::FmaxPseudo: unhandled type");
|
||||
}
|
||||
}
|
||||
|
||||
Opcode::Sqrt | Opcode::Fneg | Opcode::Fabs | Opcode::Fpromote | Opcode::Fdemote => {
|
||||
let ty = ty.unwrap();
|
||||
let bits = ty_bits(ty);
|
||||
|
@ -2216,21 +2515,39 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
}
|
||||
|
||||
Opcode::Ceil | Opcode::Floor | Opcode::Trunc | Opcode::Nearest => {
|
||||
let bits = ty_bits(ctx.output_ty(insn, 0));
|
||||
let op = match (op, bits) {
|
||||
(Opcode::Ceil, 32) => FpuRoundMode::Plus32,
|
||||
(Opcode::Ceil, 64) => FpuRoundMode::Plus64,
|
||||
(Opcode::Floor, 32) => FpuRoundMode::Minus32,
|
||||
(Opcode::Floor, 64) => FpuRoundMode::Minus64,
|
||||
(Opcode::Trunc, 32) => FpuRoundMode::Zero32,
|
||||
(Opcode::Trunc, 64) => FpuRoundMode::Zero64,
|
||||
(Opcode::Nearest, 32) => FpuRoundMode::Nearest32,
|
||||
(Opcode::Nearest, 64) => FpuRoundMode::Nearest64,
|
||||
_ => panic!("Unknown op/bits combination"),
|
||||
};
|
||||
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
|
||||
let rd = get_output_reg(ctx, outputs[0]);
|
||||
ctx.emit(Inst::FpuRound { op, rd, rn });
|
||||
let ty = ctx.output_ty(insn, 0);
|
||||
if !ty.is_vector() {
|
||||
let bits = ty_bits(ty);
|
||||
let op = match (op, bits) {
|
||||
(Opcode::Ceil, 32) => FpuRoundMode::Plus32,
|
||||
(Opcode::Ceil, 64) => FpuRoundMode::Plus64,
|
||||
(Opcode::Floor, 32) => FpuRoundMode::Minus32,
|
||||
(Opcode::Floor, 64) => FpuRoundMode::Minus64,
|
||||
(Opcode::Trunc, 32) => FpuRoundMode::Zero32,
|
||||
(Opcode::Trunc, 64) => FpuRoundMode::Zero64,
|
||||
(Opcode::Nearest, 32) => FpuRoundMode::Nearest32,
|
||||
(Opcode::Nearest, 64) => FpuRoundMode::Nearest64,
|
||||
_ => panic!("Unknown op/bits combination (scalar)"),
|
||||
};
|
||||
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
|
||||
let rd = get_output_reg(ctx, outputs[0]);
|
||||
ctx.emit(Inst::FpuRound { op, rd, rn });
|
||||
} else {
|
||||
let (op, size) = match (op, ty) {
|
||||
(Opcode::Ceil, F32X4) => (VecMisc2::Frintp, VectorSize::Size32x4),
|
||||
(Opcode::Ceil, F64X2) => (VecMisc2::Frintp, VectorSize::Size64x2),
|
||||
(Opcode::Floor, F32X4) => (VecMisc2::Frintm, VectorSize::Size32x4),
|
||||
(Opcode::Floor, F64X2) => (VecMisc2::Frintm, VectorSize::Size64x2),
|
||||
(Opcode::Trunc, F32X4) => (VecMisc2::Frintz, VectorSize::Size32x4),
|
||||
(Opcode::Trunc, F64X2) => (VecMisc2::Frintz, VectorSize::Size64x2),
|
||||
(Opcode::Nearest, F32X4) => (VecMisc2::Frintn, VectorSize::Size32x4),
|
||||
(Opcode::Nearest, F64X2) => (VecMisc2::Frintn, VectorSize::Size64x2),
|
||||
_ => panic!("Unknown op/ty combination (vector){:?}", ty),
|
||||
};
|
||||
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
|
||||
let rd = get_output_reg(ctx, outputs[0]);
|
||||
ctx.emit(Inst::VecMisc { op, rd, rn, size });
|
||||
}
|
||||
}
|
||||
|
||||
Opcode::Fma => {
|
||||
|
@ -2319,9 +2636,9 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
} else {
|
||||
ctx.emit(Inst::FpuCmp64 { rn, rm: rn });
|
||||
}
|
||||
let trap_info = (ctx.srcloc(insn), TrapCode::BadConversionToInteger);
|
||||
let trap_code = TrapCode::BadConversionToInteger;
|
||||
ctx.emit(Inst::TrapIf {
|
||||
trap_info,
|
||||
trap_code,
|
||||
kind: CondBrKind::Cond(lower_fp_condcode(FloatCC::Unordered)),
|
||||
});
|
||||
|
||||
|
@ -2371,9 +2688,9 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
rn,
|
||||
rm: tmp.to_reg(),
|
||||
});
|
||||
let trap_info = (ctx.srcloc(insn), TrapCode::IntegerOverflow);
|
||||
let trap_code = TrapCode::IntegerOverflow;
|
||||
ctx.emit(Inst::TrapIf {
|
||||
trap_info,
|
||||
trap_code,
|
||||
kind: CondBrKind::Cond(lower_fp_condcode(low_cond).invert()),
|
||||
});
|
||||
|
||||
|
@ -2383,9 +2700,9 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
rn,
|
||||
rm: tmp.to_reg(),
|
||||
});
|
||||
let trap_info = (ctx.srcloc(insn), TrapCode::IntegerOverflow);
|
||||
let trap_code = TrapCode::IntegerOverflow;
|
||||
ctx.emit(Inst::TrapIf {
|
||||
trap_info,
|
||||
trap_code,
|
||||
kind: CondBrKind::Cond(lower_fp_condcode(FloatCC::LessThan).invert()),
|
||||
});
|
||||
} else {
|
||||
|
@ -2424,9 +2741,9 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
rn,
|
||||
rm: tmp.to_reg(),
|
||||
});
|
||||
let trap_info = (ctx.srcloc(insn), TrapCode::IntegerOverflow);
|
||||
let trap_code = TrapCode::IntegerOverflow;
|
||||
ctx.emit(Inst::TrapIf {
|
||||
trap_info,
|
||||
trap_code,
|
||||
kind: CondBrKind::Cond(lower_fp_condcode(low_cond).invert()),
|
||||
});
|
||||
|
||||
|
@ -2436,9 +2753,9 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
rn,
|
||||
rm: tmp.to_reg(),
|
||||
});
|
||||
let trap_info = (ctx.srcloc(insn), TrapCode::IntegerOverflow);
|
||||
let trap_code = TrapCode::IntegerOverflow;
|
||||
ctx.emit(Inst::TrapIf {
|
||||
trap_info,
|
||||
trap_code,
|
||||
kind: CondBrKind::Cond(lower_fp_condcode(FloatCC::LessThan).invert()),
|
||||
});
|
||||
};
|
||||
|
@ -2547,15 +2864,9 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
let rtmp2 = ctx.alloc_tmp(RegClass::V128, in_ty);
|
||||
|
||||
if in_bits == 32 {
|
||||
ctx.emit(Inst::LoadFpuConst32 {
|
||||
rd: rtmp1,
|
||||
const_data: max as f32,
|
||||
});
|
||||
lower_constant_f32(ctx, rtmp1, max as f32);
|
||||
} else {
|
||||
ctx.emit(Inst::LoadFpuConst64 {
|
||||
rd: rtmp1,
|
||||
const_data: max,
|
||||
});
|
||||
lower_constant_f64(ctx, rtmp1, max);
|
||||
}
|
||||
ctx.emit(Inst::FpuRRR {
|
||||
fpu_op: choose_32_64(in_ty, FPUOp2::Min32, FPUOp2::Min64),
|
||||
|
@ -2564,15 +2875,9 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
rm: rtmp1.to_reg(),
|
||||
});
|
||||
if in_bits == 32 {
|
||||
ctx.emit(Inst::LoadFpuConst32 {
|
||||
rd: rtmp1,
|
||||
const_data: min as f32,
|
||||
});
|
||||
lower_constant_f32(ctx, rtmp1, min as f32);
|
||||
} else {
|
||||
ctx.emit(Inst::LoadFpuConst64 {
|
||||
rd: rtmp1,
|
||||
const_data: min,
|
||||
});
|
||||
lower_constant_f64(ctx, rtmp1, min);
|
||||
}
|
||||
ctx.emit(Inst::FpuRRR {
|
||||
fpu_op: choose_32_64(in_ty, FPUOp2::Max32, FPUOp2::Max64),
|
||||
|
@ -2582,15 +2887,9 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
});
|
||||
if out_signed {
|
||||
if in_bits == 32 {
|
||||
ctx.emit(Inst::LoadFpuConst32 {
|
||||
rd: rtmp1,
|
||||
const_data: 0.0,
|
||||
});
|
||||
lower_constant_f32(ctx, rtmp1, 0.0);
|
||||
} else {
|
||||
ctx.emit(Inst::LoadFpuConst64 {
|
||||
rd: rtmp1,
|
||||
const_data: 0.0,
|
||||
});
|
||||
lower_constant_f64(ctx, rtmp1, 0.0);
|
||||
}
|
||||
}
|
||||
if in_bits == 32 {
|
||||
|
|
|
@ -3,15 +3,13 @@
|
|||
use crate::ir::condcodes::IntCC;
|
||||
use crate::ir::Function;
|
||||
use crate::isa::Builder as IsaBuilder;
|
||||
use crate::machinst::{
|
||||
compile, MachBackend, MachCompileResult, ShowWithRRU, TargetIsaAdapter, VCode,
|
||||
};
|
||||
use crate::machinst::{compile, MachBackend, MachCompileResult, TargetIsaAdapter, VCode};
|
||||
use crate::result::CodegenResult;
|
||||
use crate::settings;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
|
||||
use regalloc::RealRegUniverse;
|
||||
use regalloc::{PrettyPrint, RealRegUniverse};
|
||||
use target_lexicon::{Aarch64Architecture, Architecture, Triple};
|
||||
|
||||
// New backend:
|
||||
|
@ -22,6 +20,8 @@ mod lower_inst;
|
|||
|
||||
use inst::create_reg_universe;
|
||||
|
||||
use self::inst::EmitInfo;
|
||||
|
||||
/// An AArch64 backend.
|
||||
pub struct AArch64Backend {
|
||||
triple: Triple,
|
||||
|
@ -47,8 +47,9 @@ impl AArch64Backend {
|
|||
func: &Function,
|
||||
flags: settings::Flags,
|
||||
) -> CodegenResult<VCode<inst::Inst>> {
|
||||
let emit_info = EmitInfo::new(flags.clone());
|
||||
let abi = Box::new(abi::AArch64ABICallee::new(func, flags)?);
|
||||
compile::compile::<AArch64Backend>(func, self, abi)
|
||||
compile::compile::<AArch64Backend>(func, self, abi, emit_info)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,8 +61,10 @@ impl MachBackend for AArch64Backend {
|
|||
) -> CodegenResult<MachCompileResult> {
|
||||
let flags = self.flags();
|
||||
let vcode = self.compile_vcode(func, flags.clone())?;
|
||||
|
||||
let buffer = vcode.emit();
|
||||
let frame_size = vcode.frame_size();
|
||||
let unwind_info = vcode.unwind_info()?;
|
||||
|
||||
let disasm = if want_disasm {
|
||||
Some(vcode.show_rru(Some(&create_reg_universe(flags))))
|
||||
|
@ -75,6 +78,7 @@ impl MachBackend for AArch64Backend {
|
|||
buffer,
|
||||
frame_size,
|
||||
disasm,
|
||||
unwind_info,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -106,6 +110,31 @@ impl MachBackend for AArch64Backend {
|
|||
// opposite of x86).
|
||||
IntCC::UnsignedLessThan
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
fn emit_unwind_info(
|
||||
&self,
|
||||
result: &MachCompileResult,
|
||||
kind: crate::machinst::UnwindInfoKind,
|
||||
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
|
||||
use crate::isa::unwind::UnwindInfo;
|
||||
use crate::machinst::UnwindInfoKind;
|
||||
Ok(match (result.unwind_info.as_ref(), kind) {
|
||||
(Some(info), UnwindInfoKind::SystemV) => {
|
||||
inst::unwind::systemv::create_unwind_info(info.clone())?.map(UnwindInfo::SystemV)
|
||||
}
|
||||
(Some(_info), UnwindInfoKind::Windows) => {
|
||||
// TODO: support Windows unwind info on AArch64
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
|
||||
Some(inst::unwind::systemv::create_cie())
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `isa::Builder`.
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
use crate::ir;
|
||||
use crate::ir::types::*;
|
||||
use crate::ir::SourceLoc;
|
||||
use crate::isa;
|
||||
use crate::isa::arm32::inst::*;
|
||||
use crate::machinst::*;
|
||||
|
@ -45,6 +44,11 @@ impl ABIMachineSpec for Arm32MachineDeps {
|
|||
32
|
||||
}
|
||||
|
||||
/// Return required stack alignment in bytes.
|
||||
fn stack_align(_call_conv: isa::CallConv) -> u32 {
|
||||
8
|
||||
}
|
||||
|
||||
fn compute_arg_locs(
|
||||
_call_conv: isa::CallConv,
|
||||
params: &[ir::AbiParam],
|
||||
|
@ -212,7 +216,7 @@ impl ABIMachineSpec for Arm32MachineDeps {
|
|||
rm: limit_reg,
|
||||
});
|
||||
insts.push(Inst::TrapIf {
|
||||
trap_info: (ir::SourceLoc::default(), ir::TrapCode::StackOverflow),
|
||||
trap_info: ir::TrapCode::StackOverflow,
|
||||
// Here `Lo` == "less than" when interpreting the two
|
||||
// operands as unsigned integers.
|
||||
cond: Cond::Lo,
|
||||
|
@ -308,6 +312,7 @@ impl ABIMachineSpec for Arm32MachineDeps {
|
|||
_flags: &settings::Flags,
|
||||
clobbers: &Set<Writable<RealReg>>,
|
||||
fixed_frame_storage_size: u32,
|
||||
_outgoing_args_size: u32,
|
||||
) -> (u64, SmallVec<[Inst; 16]>) {
|
||||
let mut insts = SmallVec::new();
|
||||
if fixed_frame_storage_size > 0 {
|
||||
|
@ -336,6 +341,8 @@ impl ABIMachineSpec for Arm32MachineDeps {
|
|||
_call_conv: isa::CallConv,
|
||||
_flags: &settings::Flags,
|
||||
clobbers: &Set<Writable<RealReg>>,
|
||||
_fixed_frame_storage_size: u32,
|
||||
_outgoing_args_size: u32,
|
||||
) -> SmallVec<[Inst; 16]> {
|
||||
let mut insts = SmallVec::new();
|
||||
let clobbered_vec = get_callee_saves(clobbers);
|
||||
|
@ -358,9 +365,10 @@ impl ABIMachineSpec for Arm32MachineDeps {
|
|||
dest: &CallDest,
|
||||
uses: Vec<Reg>,
|
||||
defs: Vec<Writable<Reg>>,
|
||||
loc: SourceLoc,
|
||||
opcode: ir::Opcode,
|
||||
tmp: Writable<Reg>,
|
||||
_callee_conv: isa::CallConv,
|
||||
_caller_conv: isa::CallConv,
|
||||
) -> SmallVec<[(InstIsSafepoint, Inst); 2]> {
|
||||
let mut insts = SmallVec::new();
|
||||
match &dest {
|
||||
|
@ -371,7 +379,6 @@ impl ABIMachineSpec for Arm32MachineDeps {
|
|||
dest: name.clone(),
|
||||
uses,
|
||||
defs,
|
||||
loc,
|
||||
opcode,
|
||||
}),
|
||||
},
|
||||
|
@ -383,7 +390,6 @@ impl ABIMachineSpec for Arm32MachineDeps {
|
|||
rt: tmp,
|
||||
name: Box::new(name.clone()),
|
||||
offset: 0,
|
||||
srcloc: loc,
|
||||
},
|
||||
));
|
||||
insts.push((
|
||||
|
@ -393,7 +399,6 @@ impl ABIMachineSpec for Arm32MachineDeps {
|
|||
rm: tmp.to_reg(),
|
||||
uses,
|
||||
defs,
|
||||
loc,
|
||||
opcode,
|
||||
}),
|
||||
},
|
||||
|
@ -406,7 +411,6 @@ impl ABIMachineSpec for Arm32MachineDeps {
|
|||
rm: *reg,
|
||||
uses,
|
||||
defs,
|
||||
loc,
|
||||
opcode,
|
||||
}),
|
||||
},
|
||||
|
@ -431,11 +435,11 @@ impl ABIMachineSpec for Arm32MachineDeps {
|
|||
s.nominal_sp_to_fp
|
||||
}
|
||||
|
||||
fn get_caller_saves(_call_conv: isa::CallConv) -> Vec<Writable<Reg>> {
|
||||
fn get_regs_clobbered_by_call(_: isa::CallConv) -> Vec<Writable<Reg>> {
|
||||
let mut caller_saved = Vec::new();
|
||||
for i in 0..15 {
|
||||
let r = writable_rreg(i);
|
||||
if is_caller_save(r.to_reg().to_real_reg()) {
|
||||
if is_reg_clobbered_by_call(r.to_reg().to_real_reg()) {
|
||||
caller_saved.push(r);
|
||||
}
|
||||
}
|
||||
|
@ -461,7 +465,7 @@ fn get_callee_saves(regs: &Set<Writable<RealReg>>) -> Vec<Writable<RealReg>> {
|
|||
ret
|
||||
}
|
||||
|
||||
fn is_caller_save(r: RealReg) -> bool {
|
||||
fn is_reg_clobbered_by_call(r: RealReg) -> bool {
|
||||
let enc = r.get_hw_encoding();
|
||||
enc <= 3
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use crate::isa::arm32::inst::*;
|
||||
|
||||
use regalloc::{RealRegUniverse, Reg};
|
||||
use regalloc::{PrettyPrint, RealRegUniverse, Reg};
|
||||
|
||||
use std::string::String;
|
||||
|
||||
|
@ -265,7 +265,7 @@ impl BranchTarget {
|
|||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for ShiftOpAndAmt {
|
||||
impl PrettyPrint for ShiftOpAndAmt {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
let op = match self.op() {
|
||||
ShiftOp::LSL => "lsl",
|
||||
|
@ -277,19 +277,19 @@ impl ShowWithRRU for ShiftOpAndAmt {
|
|||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for UImm8 {
|
||||
impl PrettyPrint for UImm8 {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
format!("#{}", self.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for UImm12 {
|
||||
impl PrettyPrint for UImm12 {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
format!("#{}", self.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for AMode {
|
||||
impl PrettyPrint for AMode {
|
||||
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
match self {
|
||||
&AMode::RegReg(rn, rm, imm2) => {
|
||||
|
@ -317,7 +317,7 @@ impl ShowWithRRU for AMode {
|
|||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for Cond {
|
||||
impl PrettyPrint for Cond {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
let mut s = format!("{:?}", self);
|
||||
s.make_ascii_lowercase();
|
||||
|
@ -325,7 +325,7 @@ impl ShowWithRRU for Cond {
|
|||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for BranchTarget {
|
||||
impl PrettyPrint for BranchTarget {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
match self {
|
||||
&BranchTarget::Label(label) => format!("label{:?}", label.get()),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//! 32-bit ARM ISA: binary code emission.
|
||||
|
||||
use crate::binemit::{Reloc, StackMap};
|
||||
use crate::ir::SourceLoc;
|
||||
use crate::isa::arm32::inst::*;
|
||||
|
||||
use core::convert::TryFrom;
|
||||
|
@ -229,6 +230,8 @@ pub struct EmitState {
|
|||
pub(crate) nominal_sp_to_fp: i64,
|
||||
/// Safepoint stack map for upcoming instruction, as provided to `pre_safepoint()`.
|
||||
stack_map: Option<StackMap>,
|
||||
/// Source location of next machine code instruction to be emitted.
|
||||
cur_srcloc: SourceLoc,
|
||||
}
|
||||
|
||||
impl MachInstEmitState<Inst> for EmitState {
|
||||
|
@ -237,12 +240,17 @@ impl MachInstEmitState<Inst> for EmitState {
|
|||
virtual_sp_offset: 0,
|
||||
nominal_sp_to_fp: abi.frame_size() as i64,
|
||||
stack_map: None,
|
||||
cur_srcloc: SourceLoc::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn pre_safepoint(&mut self, stack_map: StackMap) {
|
||||
self.stack_map = Some(stack_map);
|
||||
}
|
||||
|
||||
fn pre_sourceloc(&mut self, srcloc: SourceLoc) {
|
||||
self.cur_srcloc = srcloc;
|
||||
}
|
||||
}
|
||||
|
||||
impl EmitState {
|
||||
|
@ -253,12 +261,34 @@ impl EmitState {
|
|||
fn clear_post_insn(&mut self) {
|
||||
self.stack_map = None;
|
||||
}
|
||||
|
||||
fn cur_srcloc(&self) -> SourceLoc {
|
||||
self.cur_srcloc
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EmitInfo {
|
||||
flags: settings::Flags,
|
||||
}
|
||||
|
||||
impl EmitInfo {
|
||||
pub(crate) fn new(flags: settings::Flags) -> Self {
|
||||
EmitInfo { flags }
|
||||
}
|
||||
}
|
||||
|
||||
impl MachInstEmitInfo for EmitInfo {
|
||||
fn flags(&self) -> &settings::Flags {
|
||||
&self.flags
|
||||
}
|
||||
}
|
||||
|
||||
impl MachInstEmit for Inst {
|
||||
type Info = EmitInfo;
|
||||
type State = EmitState;
|
||||
type UnwindInfo = super::unwind::Arm32UnwindInfo;
|
||||
|
||||
fn emit(&self, sink: &mut MachBuffer<Inst>, flags: &settings::Flags, state: &mut EmitState) {
|
||||
fn emit(&self, sink: &mut MachBuffer<Inst>, emit_info: &Self::Info, state: &mut EmitState) {
|
||||
let start_off = sink.cur_offset();
|
||||
|
||||
match self {
|
||||
|
@ -438,17 +468,13 @@ impl MachInstEmit for Inst {
|
|||
let inst = enc_32_regs(inst, None, None, None, Some(rn));
|
||||
emit_32(inst, sink);
|
||||
}
|
||||
&Inst::Store {
|
||||
rt,
|
||||
ref mem,
|
||||
srcloc,
|
||||
bits,
|
||||
} => {
|
||||
&Inst::Store { rt, ref mem, bits } => {
|
||||
let (mem_insts, mem) = mem_finalize(mem, state);
|
||||
for inst in mem_insts.into_iter() {
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, emit_info, state);
|
||||
}
|
||||
if let Some(srcloc) = srcloc {
|
||||
let srcloc = state.cur_srcloc();
|
||||
if srcloc != SourceLoc::default() {
|
||||
// Register the offset at which the store instruction starts.
|
||||
sink.add_trap(srcloc, TrapCode::HeapOutOfBounds);
|
||||
}
|
||||
|
@ -478,15 +504,15 @@ impl MachInstEmit for Inst {
|
|||
&Inst::Load {
|
||||
rt,
|
||||
ref mem,
|
||||
srcloc,
|
||||
bits,
|
||||
sign_extend,
|
||||
} => {
|
||||
let (mem_insts, mem) = mem_finalize(mem, state);
|
||||
for inst in mem_insts.into_iter() {
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, emit_info, state);
|
||||
}
|
||||
if let Some(srcloc) = srcloc {
|
||||
let srcloc = state.cur_srcloc();
|
||||
if srcloc != SourceLoc::default() {
|
||||
// Register the offset at which the load instruction starts.
|
||||
sink.add_trap(srcloc, TrapCode::HeapOutOfBounds);
|
||||
}
|
||||
|
@ -537,7 +563,7 @@ impl MachInstEmit for Inst {
|
|||
&Inst::LoadAddr { rd, ref mem } => {
|
||||
let (mem_insts, mem) = mem_finalize(mem, state);
|
||||
for inst in mem_insts.into_iter() {
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, emit_info, state);
|
||||
}
|
||||
let inst = match mem {
|
||||
AMode::RegReg(reg1, reg2, shift) => {
|
||||
|
@ -574,7 +600,7 @@ impl MachInstEmit for Inst {
|
|||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, emit_info, state);
|
||||
}
|
||||
&Inst::Extend {
|
||||
rd,
|
||||
|
@ -617,7 +643,7 @@ impl MachInstEmit for Inst {
|
|||
rn: rm,
|
||||
imm8: UImm8::maybe_from_i64(1).unwrap(),
|
||||
};
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, emit_info, state);
|
||||
|
||||
if signed {
|
||||
let inst = Inst::AluRRImm8 {
|
||||
|
@ -626,7 +652,7 @@ impl MachInstEmit for Inst {
|
|||
rn: rd.to_reg(),
|
||||
imm8: UImm8::maybe_from_i64(1).unwrap(),
|
||||
};
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, emit_info, state);
|
||||
}
|
||||
}
|
||||
&Inst::Extend { .. } => {
|
||||
|
@ -638,7 +664,7 @@ impl MachInstEmit for Inst {
|
|||
|
||||
sink.put2(enc_16_it(cond, insts));
|
||||
for inst in insts.iter() {
|
||||
inst.inst.emit(sink, flags, state);
|
||||
inst.inst.emit(sink, emit_info, state);
|
||||
}
|
||||
}
|
||||
&Inst::Push { ref reg_list } => match reg_list.len() {
|
||||
|
@ -678,23 +704,24 @@ impl MachInstEmit for Inst {
|
|||
}
|
||||
},
|
||||
&Inst::Call { ref info } => {
|
||||
sink.add_reloc(info.loc, Reloc::Arm32Call, &info.dest, 0);
|
||||
let srcloc = state.cur_srcloc();
|
||||
sink.add_reloc(srcloc, Reloc::Arm32Call, &info.dest, 0);
|
||||
emit_32(0b11110_0_0000000000_11_0_1_0_00000000000, sink);
|
||||
if info.opcode.is_call() {
|
||||
sink.add_call_site(info.loc, info.opcode);
|
||||
sink.add_call_site(srcloc, info.opcode);
|
||||
}
|
||||
}
|
||||
&Inst::CallInd { ref info } => {
|
||||
let srcloc = state.cur_srcloc();
|
||||
sink.put2(0b01000111_1_0000_000 | (machreg_to_gpr(info.rm) << 3));
|
||||
if info.opcode.is_call() {
|
||||
sink.add_call_site(info.loc, info.opcode);
|
||||
sink.add_call_site(srcloc, info.opcode);
|
||||
}
|
||||
}
|
||||
&Inst::LoadExtName {
|
||||
rt,
|
||||
ref name,
|
||||
offset,
|
||||
srcloc,
|
||||
} => {
|
||||
// maybe nop2 (0|2) bytes (pc is now 4-aligned)
|
||||
// ldr rt, [pc, #4] 4 bytes
|
||||
|
@ -703,7 +730,7 @@ impl MachInstEmit for Inst {
|
|||
// continue:
|
||||
//
|
||||
if start_off & 0x3 != 0 {
|
||||
Inst::Nop2.emit(sink, flags, state);
|
||||
Inst::Nop2.emit(sink, emit_info, state);
|
||||
}
|
||||
assert_eq!(sink.cur_offset() & 0x3, 0);
|
||||
|
||||
|
@ -711,17 +738,17 @@ impl MachInstEmit for Inst {
|
|||
let inst = Inst::Load {
|
||||
rt,
|
||||
mem,
|
||||
srcloc: Some(srcloc),
|
||||
bits: 32,
|
||||
sign_extend: false,
|
||||
};
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, emit_info, state);
|
||||
|
||||
let inst = Inst::Jump {
|
||||
dest: BranchTarget::ResolvedOffset(4),
|
||||
};
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, emit_info, state);
|
||||
|
||||
let srcloc = state.cur_srcloc();
|
||||
sink.add_reloc(srcloc, Reloc::Abs4, name, offset.into());
|
||||
sink.put4(0);
|
||||
}
|
||||
|
@ -766,7 +793,8 @@ impl MachInstEmit for Inst {
|
|||
sink.put2(inst);
|
||||
}
|
||||
&Inst::Udf { trap_info } => {
|
||||
let (srcloc, code) = trap_info;
|
||||
let srcloc = state.cur_srcloc();
|
||||
let code = trap_info;
|
||||
sink.add_trap(srcloc, code);
|
||||
sink.put2(0b11011110_00000000);
|
||||
}
|
||||
|
@ -779,7 +807,7 @@ impl MachInstEmit for Inst {
|
|||
emit_32(enc_32_cond_branch(cond, dest), sink);
|
||||
|
||||
let trap = Inst::Udf { trap_info };
|
||||
trap.emit(sink, flags, state);
|
||||
trap.emit(sink, emit_info, state);
|
||||
}
|
||||
&Inst::VirtualSPOffsetAdj { offset } => {
|
||||
debug!(
|
||||
|
|
|
@ -1244,7 +1244,6 @@ fn test_arm32_emit() {
|
|||
Inst::Store {
|
||||
rt: rreg(0),
|
||||
mem: AMode::reg_plus_reg(rreg(1), rreg(2), 0),
|
||||
srcloc: None,
|
||||
bits: 32,
|
||||
},
|
||||
"41F80200",
|
||||
|
@ -1254,7 +1253,6 @@ fn test_arm32_emit() {
|
|||
Inst::Store {
|
||||
rt: rreg(8),
|
||||
mem: AMode::reg_plus_reg(rreg(9), rreg(10), 3),
|
||||
srcloc: None,
|
||||
bits: 32,
|
||||
},
|
||||
"49F83A80",
|
||||
|
@ -1264,7 +1262,6 @@ fn test_arm32_emit() {
|
|||
Inst::Store {
|
||||
rt: rreg(0),
|
||||
mem: AMode::RegOffset(rreg(1), 4095),
|
||||
srcloc: None,
|
||||
bits: 32,
|
||||
},
|
||||
"C1F8FF0F",
|
||||
|
@ -1274,7 +1271,6 @@ fn test_arm32_emit() {
|
|||
Inst::Store {
|
||||
rt: rreg(8),
|
||||
mem: AMode::RegOffset(rreg(9), 0),
|
||||
srcloc: None,
|
||||
bits: 32,
|
||||
},
|
||||
"C9F80080",
|
||||
|
@ -1284,7 +1280,6 @@ fn test_arm32_emit() {
|
|||
Inst::Store {
|
||||
rt: rreg(7),
|
||||
mem: AMode::RegOffset(rreg(11), 65535),
|
||||
srcloc: None,
|
||||
bits: 32,
|
||||
},
|
||||
"4FF6FF7C4BF80C70",
|
||||
|
@ -1294,7 +1289,6 @@ fn test_arm32_emit() {
|
|||
Inst::Store {
|
||||
rt: rreg(10),
|
||||
mem: AMode::RegOffset(rreg(4), 16777215),
|
||||
srcloc: None,
|
||||
bits: 32,
|
||||
},
|
||||
"4FF6FF7CC0F2FF0C44F80CA0",
|
||||
|
@ -1304,7 +1298,6 @@ fn test_arm32_emit() {
|
|||
Inst::Store {
|
||||
rt: rreg(0),
|
||||
mem: AMode::reg_plus_reg(rreg(1), rreg(2), 0),
|
||||
srcloc: None,
|
||||
bits: 16,
|
||||
},
|
||||
"21F80200",
|
||||
|
@ -1314,7 +1307,6 @@ fn test_arm32_emit() {
|
|||
Inst::Store {
|
||||
rt: rreg(8),
|
||||
mem: AMode::reg_plus_reg(rreg(9), rreg(10), 2),
|
||||
srcloc: None,
|
||||
bits: 16,
|
||||
},
|
||||
"29F82A80",
|
||||
|
@ -1324,7 +1316,6 @@ fn test_arm32_emit() {
|
|||
Inst::Store {
|
||||
rt: rreg(0),
|
||||
mem: AMode::RegOffset(rreg(1), 3210),
|
||||
srcloc: None,
|
||||
bits: 16,
|
||||
},
|
||||
"A1F88A0C",
|
||||
|
@ -1334,7 +1325,6 @@ fn test_arm32_emit() {
|
|||
Inst::Store {
|
||||
rt: rreg(8),
|
||||
mem: AMode::RegOffset(rreg(9), 1),
|
||||
srcloc: None,
|
||||
bits: 16,
|
||||
},
|
||||
"A9F80180",
|
||||
|
@ -1344,7 +1334,6 @@ fn test_arm32_emit() {
|
|||
Inst::Store {
|
||||
rt: rreg(7),
|
||||
mem: AMode::RegOffset(rreg(11), 65535),
|
||||
srcloc: None,
|
||||
bits: 16,
|
||||
},
|
||||
"4FF6FF7C2BF80C70",
|
||||
|
@ -1354,7 +1343,6 @@ fn test_arm32_emit() {
|
|||
Inst::Store {
|
||||
rt: rreg(10),
|
||||
mem: AMode::RegOffset(rreg(4), 16777215),
|
||||
srcloc: None,
|
||||
bits: 16,
|
||||
},
|
||||
"4FF6FF7CC0F2FF0C24F80CA0",
|
||||
|
@ -1364,7 +1352,6 @@ fn test_arm32_emit() {
|
|||
Inst::Store {
|
||||
rt: rreg(0),
|
||||
mem: AMode::reg_plus_reg(rreg(1), rreg(2), 0),
|
||||
srcloc: None,
|
||||
bits: 8,
|
||||
},
|
||||
"01F80200",
|
||||
|
@ -1374,7 +1361,6 @@ fn test_arm32_emit() {
|
|||
Inst::Store {
|
||||
rt: rreg(8),
|
||||
mem: AMode::reg_plus_reg(rreg(9), rreg(10), 1),
|
||||
srcloc: None,
|
||||
bits: 8,
|
||||
},
|
||||
"09F81A80",
|
||||
|
@ -1384,7 +1370,6 @@ fn test_arm32_emit() {
|
|||
Inst::Store {
|
||||
rt: rreg(0),
|
||||
mem: AMode::RegOffset(rreg(1), 4),
|
||||
srcloc: None,
|
||||
bits: 8,
|
||||
},
|
||||
"81F80400",
|
||||
|
@ -1394,7 +1379,6 @@ fn test_arm32_emit() {
|
|||
Inst::Store {
|
||||
rt: rreg(8),
|
||||
mem: AMode::RegOffset(rreg(9), 777),
|
||||
srcloc: None,
|
||||
bits: 8,
|
||||
},
|
||||
"89F80983",
|
||||
|
@ -1404,7 +1388,6 @@ fn test_arm32_emit() {
|
|||
Inst::Store {
|
||||
rt: rreg(7),
|
||||
mem: AMode::RegOffset(rreg(11), 65535),
|
||||
srcloc: None,
|
||||
bits: 8,
|
||||
},
|
||||
"4FF6FF7C0BF80C70",
|
||||
|
@ -1414,7 +1397,6 @@ fn test_arm32_emit() {
|
|||
Inst::Store {
|
||||
rt: rreg(10),
|
||||
mem: AMode::RegOffset(rreg(4), 16777215),
|
||||
srcloc: None,
|
||||
bits: 8,
|
||||
},
|
||||
"4FF6FF7CC0F2FF0C04F80CA0",
|
||||
|
@ -1424,7 +1406,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(0),
|
||||
mem: AMode::reg_plus_reg(rreg(1), rreg(2), 0),
|
||||
srcloc: None,
|
||||
bits: 32,
|
||||
sign_extend: false,
|
||||
},
|
||||
|
@ -1435,7 +1416,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(8),
|
||||
mem: AMode::reg_plus_reg(rreg(9), rreg(10), 1),
|
||||
srcloc: None,
|
||||
bits: 32,
|
||||
sign_extend: false,
|
||||
},
|
||||
|
@ -1446,7 +1426,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(0),
|
||||
mem: AMode::RegOffset(rreg(1), 55),
|
||||
srcloc: None,
|
||||
bits: 32,
|
||||
sign_extend: false,
|
||||
},
|
||||
|
@ -1457,7 +1436,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(8),
|
||||
mem: AMode::RegOffset(rreg(9), 1234),
|
||||
srcloc: None,
|
||||
bits: 32,
|
||||
sign_extend: false,
|
||||
},
|
||||
|
@ -1468,7 +1446,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(7),
|
||||
mem: AMode::RegOffset(rreg(11), 9876),
|
||||
srcloc: None,
|
||||
bits: 32,
|
||||
sign_extend: false,
|
||||
},
|
||||
|
@ -1479,7 +1456,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(10),
|
||||
mem: AMode::RegOffset(rreg(4), 252645135),
|
||||
srcloc: None,
|
||||
bits: 32,
|
||||
sign_extend: false,
|
||||
},
|
||||
|
@ -1490,7 +1466,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(0),
|
||||
mem: AMode::PCRel(-56),
|
||||
srcloc: None,
|
||||
bits: 32,
|
||||
sign_extend: false,
|
||||
},
|
||||
|
@ -1501,7 +1476,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(8),
|
||||
mem: AMode::PCRel(1024),
|
||||
srcloc: None,
|
||||
bits: 32,
|
||||
sign_extend: false,
|
||||
},
|
||||
|
@ -1512,7 +1486,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(0),
|
||||
mem: AMode::reg_plus_reg(rreg(1), rreg(2), 0),
|
||||
srcloc: None,
|
||||
bits: 16,
|
||||
sign_extend: true,
|
||||
},
|
||||
|
@ -1523,7 +1496,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(8),
|
||||
mem: AMode::reg_plus_reg(rreg(9), rreg(10), 2),
|
||||
srcloc: None,
|
||||
bits: 16,
|
||||
sign_extend: false,
|
||||
},
|
||||
|
@ -1534,7 +1506,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(0),
|
||||
mem: AMode::RegOffset(rreg(1), 55),
|
||||
srcloc: None,
|
||||
bits: 16,
|
||||
sign_extend: false,
|
||||
},
|
||||
|
@ -1545,7 +1516,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(8),
|
||||
mem: AMode::RegOffset(rreg(9), 1234),
|
||||
srcloc: None,
|
||||
bits: 16,
|
||||
sign_extend: true,
|
||||
},
|
||||
|
@ -1556,7 +1526,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(7),
|
||||
mem: AMode::RegOffset(rreg(11), 9876),
|
||||
srcloc: None,
|
||||
bits: 16,
|
||||
sign_extend: true,
|
||||
},
|
||||
|
@ -1567,7 +1536,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(10),
|
||||
mem: AMode::RegOffset(rreg(4), 252645135),
|
||||
srcloc: None,
|
||||
bits: 16,
|
||||
sign_extend: false,
|
||||
},
|
||||
|
@ -1578,7 +1546,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(0),
|
||||
mem: AMode::PCRel(56),
|
||||
srcloc: None,
|
||||
bits: 16,
|
||||
sign_extend: false,
|
||||
},
|
||||
|
@ -1589,7 +1556,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(8),
|
||||
mem: AMode::PCRel(-1000),
|
||||
srcloc: None,
|
||||
bits: 16,
|
||||
sign_extend: true,
|
||||
},
|
||||
|
@ -1600,7 +1566,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(0),
|
||||
mem: AMode::reg_plus_reg(rreg(1), rreg(2), 0),
|
||||
srcloc: None,
|
||||
bits: 8,
|
||||
sign_extend: true,
|
||||
},
|
||||
|
@ -1611,7 +1576,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(8),
|
||||
mem: AMode::reg_plus_reg(rreg(9), rreg(10), 3),
|
||||
srcloc: None,
|
||||
bits: 8,
|
||||
sign_extend: false,
|
||||
},
|
||||
|
@ -1622,7 +1586,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(0),
|
||||
mem: AMode::RegOffset(rreg(1), 55),
|
||||
srcloc: None,
|
||||
bits: 8,
|
||||
sign_extend: false,
|
||||
},
|
||||
|
@ -1633,7 +1596,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(8),
|
||||
mem: AMode::RegOffset(rreg(9), 1234),
|
||||
srcloc: None,
|
||||
bits: 8,
|
||||
sign_extend: true,
|
||||
},
|
||||
|
@ -1644,7 +1606,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(7),
|
||||
mem: AMode::RegOffset(rreg(11), 9876),
|
||||
srcloc: None,
|
||||
bits: 8,
|
||||
sign_extend: true,
|
||||
},
|
||||
|
@ -1655,7 +1616,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(10),
|
||||
mem: AMode::RegOffset(rreg(4), 252645135),
|
||||
srcloc: None,
|
||||
bits: 8,
|
||||
sign_extend: false,
|
||||
},
|
||||
|
@ -1666,7 +1626,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(0),
|
||||
mem: AMode::PCRel(72),
|
||||
srcloc: None,
|
||||
bits: 8,
|
||||
sign_extend: false,
|
||||
},
|
||||
|
@ -1677,7 +1636,6 @@ fn test_arm32_emit() {
|
|||
Inst::Load {
|
||||
rt: writable_rreg(8),
|
||||
mem: AMode::PCRel(-1234),
|
||||
srcloc: None,
|
||||
bits: 8,
|
||||
sign_extend: true,
|
||||
},
|
||||
|
@ -1961,7 +1919,7 @@ fn test_arm32_emit() {
|
|||
insns.push((
|
||||
Inst::TrapIf {
|
||||
cond: Cond::Eq,
|
||||
trap_info: (SourceLoc::default(), TrapCode::Interrupt),
|
||||
trap_info: TrapCode::Interrupt,
|
||||
},
|
||||
"40F0018000DE",
|
||||
"bne 2 ; udf #0",
|
||||
|
@ -1969,14 +1927,14 @@ fn test_arm32_emit() {
|
|||
insns.push((
|
||||
Inst::TrapIf {
|
||||
cond: Cond::Hs,
|
||||
trap_info: (SourceLoc::default(), TrapCode::Interrupt),
|
||||
trap_info: TrapCode::Interrupt,
|
||||
},
|
||||
"C0F0018000DE",
|
||||
"blo 2 ; udf #0",
|
||||
));
|
||||
insns.push((
|
||||
Inst::Udf {
|
||||
trap_info: (SourceLoc::default(), TrapCode::Interrupt),
|
||||
trap_info: TrapCode::Interrupt,
|
||||
},
|
||||
"00DE",
|
||||
"udf #0",
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
|
||||
use crate::binemit::CodeOffset;
|
||||
use crate::ir::types::{B1, B16, B32, B8, I16, I32, I8, IFLAGS};
|
||||
use crate::ir::{ExternalName, Opcode, SourceLoc, TrapCode, Type};
|
||||
use crate::ir::{ExternalName, Opcode, TrapCode, Type};
|
||||
use crate::machinst::*;
|
||||
use crate::{settings, CodegenError, CodegenResult};
|
||||
|
||||
use regalloc::{RealRegUniverse, Reg, RegClass, SpillSlot, VirtualReg, Writable};
|
||||
use regalloc::{PrettyPrint, RealRegUniverse, Reg, RegClass, SpillSlot, VirtualReg, Writable};
|
||||
use regalloc::{RegUsageCollector, RegUsageMapper};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
|
@ -22,6 +22,7 @@ mod emit;
|
|||
pub use self::emit::*;
|
||||
mod regs;
|
||||
pub use self::regs::*;
|
||||
pub mod unwind;
|
||||
|
||||
#[cfg(test)]
|
||||
mod emit_tests;
|
||||
|
@ -82,7 +83,6 @@ pub struct CallInfo {
|
|||
pub dest: ExternalName,
|
||||
pub uses: Vec<Reg>,
|
||||
pub defs: Vec<Writable<Reg>>,
|
||||
pub loc: SourceLoc,
|
||||
pub opcode: Opcode,
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,6 @@ pub struct CallIndInfo {
|
|||
pub rm: Reg,
|
||||
pub uses: Vec<Reg>,
|
||||
pub defs: Vec<Writable<Reg>>,
|
||||
pub loc: SourceLoc,
|
||||
pub opcode: Opcode,
|
||||
}
|
||||
|
||||
|
@ -217,7 +216,6 @@ pub enum Inst {
|
|||
Store {
|
||||
rt: Reg,
|
||||
mem: AMode,
|
||||
srcloc: Option<SourceLoc>,
|
||||
bits: u8,
|
||||
},
|
||||
|
||||
|
@ -226,7 +224,6 @@ pub enum Inst {
|
|||
Load {
|
||||
rt: Writable<Reg>,
|
||||
mem: AMode,
|
||||
srcloc: Option<SourceLoc>,
|
||||
bits: u8,
|
||||
sign_extend: bool,
|
||||
},
|
||||
|
@ -275,7 +272,6 @@ pub enum Inst {
|
|||
LoadExtName {
|
||||
rt: Writable<Reg>,
|
||||
name: Box<ExternalName>,
|
||||
srcloc: SourceLoc,
|
||||
offset: i32,
|
||||
},
|
||||
|
||||
|
@ -307,13 +303,13 @@ pub enum Inst {
|
|||
/// unit to the register allocator.
|
||||
TrapIf {
|
||||
cond: Cond,
|
||||
trap_info: (SourceLoc, TrapCode),
|
||||
trap_info: TrapCode,
|
||||
},
|
||||
|
||||
/// An instruction guaranteed to always be undefined and to trigger an illegal instruction at
|
||||
/// runtime.
|
||||
Udf {
|
||||
trap_info: (SourceLoc, TrapCode),
|
||||
trap_info: TrapCode,
|
||||
},
|
||||
|
||||
/// A "breakpoint" instruction, used for e.g. traps and debug breakpoints.
|
||||
|
@ -389,7 +385,6 @@ impl Inst {
|
|||
Inst::Load {
|
||||
rt: into_reg,
|
||||
mem,
|
||||
srcloc: None,
|
||||
bits,
|
||||
sign_extend: false,
|
||||
}
|
||||
|
@ -404,7 +399,6 @@ impl Inst {
|
|||
Inst::Store {
|
||||
rt: from_reg,
|
||||
mem,
|
||||
srcloc: None,
|
||||
bits,
|
||||
}
|
||||
}
|
||||
|
@ -897,7 +891,7 @@ fn mem_finalize_for_show(
|
|||
(mem_str, mem)
|
||||
}
|
||||
|
||||
impl ShowWithRRU for Inst {
|
||||
impl PrettyPrint for Inst {
|
||||
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
self.pretty_print(mb_rru, &mut EmitState::default())
|
||||
}
|
||||
|
@ -1188,7 +1182,6 @@ impl Inst {
|
|||
rt,
|
||||
ref name,
|
||||
offset,
|
||||
srcloc: _srcloc,
|
||||
} => {
|
||||
let rt = rt.show_rru(mb_rru);
|
||||
format!("ldr {}, [pc, #4] ; b 4 ; data {:?} + {}", rt, name, offset)
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
use super::*;
|
||||
use crate::isa::unwind::input::UnwindInfo;
|
||||
use crate::result::CodegenResult;
|
||||
|
||||
pub struct Arm32UnwindInfo;
|
||||
|
||||
impl UnwindInfoGenerator<Inst> for Arm32UnwindInfo {
|
||||
fn create_unwind_info(
|
||||
_context: UnwindInfoContext<Inst>,
|
||||
) -> CodegenResult<Option<UnwindInfo<Reg>>> {
|
||||
// TODO
|
||||
Ok(None)
|
||||
}
|
||||
}
|
|
@ -386,19 +386,8 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
let base = input_to_reg(ctx, inputs[1], NarrowValueMode::None);
|
||||
|
||||
let mem = AMode::RegOffset(base, i64::from(off));
|
||||
let memflags = ctx.memflags(insn).expect("memory flags");
|
||||
|
||||
let srcloc = if !memflags.notrap() {
|
||||
Some(ctx.srcloc(insn))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
ctx.emit(Inst::Store {
|
||||
rt,
|
||||
mem,
|
||||
srcloc,
|
||||
bits,
|
||||
});
|
||||
ctx.emit(Inst::Store { rt, mem, bits });
|
||||
}
|
||||
Opcode::Load
|
||||
| Opcode::Uload8
|
||||
|
@ -429,17 +418,10 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
assert_eq!(inputs.len(), 2, "only one input for store memory operands");
|
||||
let base = input_to_reg(ctx, inputs[1], NarrowValueMode::None);
|
||||
let mem = AMode::RegOffset(base, i64::from(off));
|
||||
let memflags = ctx.memflags(insn).expect("memory flags");
|
||||
|
||||
let srcloc = if !memflags.notrap() {
|
||||
Some(ctx.srcloc(insn))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
ctx.emit(Inst::Load {
|
||||
rt: out_reg,
|
||||
mem,
|
||||
srcloc,
|
||||
bits,
|
||||
sign_extend,
|
||||
});
|
||||
|
@ -484,7 +466,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
ctx.emit(Inst::Bkpt);
|
||||
}
|
||||
Opcode::Trap => {
|
||||
let trap_info = (ctx.srcloc(insn), inst_trapcode(ctx.data(insn)).unwrap());
|
||||
let trap_info = inst_trapcode(ctx.data(insn)).unwrap();
|
||||
ctx.emit(Inst::Udf { trap_info })
|
||||
}
|
||||
Opcode::Trapif => {
|
||||
|
@ -496,7 +478,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
debug_assert_eq!(ctx.data(cmp_insn).opcode(), Opcode::Ifcmp);
|
||||
emit_cmp(ctx, cmp_insn);
|
||||
|
||||
let trap_info = (ctx.srcloc(insn), inst_trapcode(ctx.data(insn)).unwrap());
|
||||
let trap_info = inst_trapcode(ctx.data(insn)).unwrap();
|
||||
let condcode = inst_condcode(ctx.data(insn)).unwrap();
|
||||
let cond = lower_condcode(condcode);
|
||||
|
||||
|
@ -512,7 +494,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
}
|
||||
}
|
||||
Opcode::Call | Opcode::CallIndirect => {
|
||||
let loc = ctx.srcloc(insn);
|
||||
let caller_conv = ctx.abi().call_conv();
|
||||
let (mut abi, inputs) = match op {
|
||||
Opcode::Call => {
|
||||
let (extname, dist) = ctx.call_target(insn).unwrap();
|
||||
|
@ -521,7 +503,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
assert_eq!(inputs.len(), sig.params.len());
|
||||
assert_eq!(outputs.len(), sig.returns.len());
|
||||
(
|
||||
Arm32ABICaller::from_func(sig, &extname, dist, loc)?,
|
||||
Arm32ABICaller::from_func(sig, &extname, dist, caller_conv)?,
|
||||
&inputs[..],
|
||||
)
|
||||
}
|
||||
|
@ -530,7 +512,10 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||
let sig = ctx.call_sig(insn).unwrap();
|
||||
assert_eq!(inputs.len() - 1, sig.params.len());
|
||||
assert_eq!(outputs.len(), sig.returns.len());
|
||||
(Arm32ABICaller::from_ptr(sig, ptr, loc, op)?, &inputs[1..])
|
||||
(
|
||||
Arm32ABICaller::from_ptr(sig, ptr, op, caller_conv)?,
|
||||
&inputs[1..],
|
||||
)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
|
|
@ -3,14 +3,12 @@
|
|||
use crate::ir::condcodes::IntCC;
|
||||
use crate::ir::Function;
|
||||
use crate::isa::Builder as IsaBuilder;
|
||||
use crate::machinst::{
|
||||
compile, MachBackend, MachCompileResult, ShowWithRRU, TargetIsaAdapter, VCode,
|
||||
};
|
||||
use crate::machinst::{compile, MachBackend, MachCompileResult, TargetIsaAdapter, VCode};
|
||||
use crate::result::CodegenResult;
|
||||
use crate::settings;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use regalloc::RealRegUniverse;
|
||||
use regalloc::{PrettyPrint, RealRegUniverse};
|
||||
use target_lexicon::{Architecture, ArmArchitecture, Triple};
|
||||
|
||||
// New backend:
|
||||
|
@ -19,7 +17,7 @@ mod inst;
|
|||
mod lower;
|
||||
mod lower_inst;
|
||||
|
||||
use inst::create_reg_universe;
|
||||
use inst::{create_reg_universe, EmitInfo};
|
||||
|
||||
/// An ARM32 backend.
|
||||
pub struct Arm32Backend {
|
||||
|
@ -46,8 +44,9 @@ impl Arm32Backend {
|
|||
) -> CodegenResult<VCode<inst::Inst>> {
|
||||
// This performs lowering to VCode, register-allocates the code, computes
|
||||
// block layout and finalizes branches. The result is ready for binary emission.
|
||||
let emit_info = EmitInfo::new(flags.clone());
|
||||
let abi = Box::new(abi::Arm32ABICallee::new(func, flags)?);
|
||||
compile::compile::<Arm32Backend>(func, self, abi)
|
||||
compile::compile::<Arm32Backend>(func, self, abi, emit_info)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,6 +73,7 @@ impl MachBackend for Arm32Backend {
|
|||
buffer,
|
||||
frame_size,
|
||||
disasm,
|
||||
unwind_info: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -87,7 +87,6 @@ mod arm32;
|
|||
#[cfg(feature = "arm64")]
|
||||
pub(crate) mod aarch64;
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
pub mod unwind;
|
||||
|
||||
mod call_conv;
|
||||
|
|
|
@ -59,8 +59,6 @@ impl CodeSink for TestCodeSink {
|
|||
}
|
||||
}
|
||||
|
||||
fn reloc_block(&mut self, _rel: Reloc, _block_offset: CodeOffset) {}
|
||||
|
||||
fn reloc_external(
|
||||
&mut self,
|
||||
_srcloc: SourceLoc,
|
||||
|
|
|
@ -2,15 +2,87 @@
|
|||
#[cfg(feature = "enable-serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
pub mod systemv;
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
pub mod winx64;
|
||||
|
||||
/// Represents unwind information for a single function.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
#[non_exhaustive]
|
||||
pub enum UnwindInfo {
|
||||
/// Windows x64 ABI unwind information.
|
||||
#[cfg(feature = "unwind")]
|
||||
WindowsX64(winx64::UnwindInfo),
|
||||
/// System V ABI unwind information.
|
||||
#[cfg(feature = "unwind")]
|
||||
SystemV(systemv::UnwindInfo),
|
||||
}
|
||||
|
||||
/// Intermediate representation for the unwind information
|
||||
/// generated by a backend.
|
||||
pub mod input {
|
||||
use crate::binemit::CodeOffset;
|
||||
use alloc::vec::Vec;
|
||||
#[cfg(feature = "enable-serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Elementary operation in the unwind operations.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
pub enum UnwindCode<Reg> {
|
||||
/// Defines that a register is saved at the specified offset.
|
||||
SaveRegister {
|
||||
/// The saved register.
|
||||
reg: Reg,
|
||||
/// The specified offset relative to the stack pointer.
|
||||
stack_offset: u32,
|
||||
},
|
||||
/// Defines that a register is as defined before call.
|
||||
RestoreRegister {
|
||||
/// The restored register.
|
||||
reg: Reg,
|
||||
},
|
||||
/// The stack pointer was adjusted to allocate the stack.
|
||||
StackAlloc {
|
||||
/// Size to allocate.
|
||||
size: u32,
|
||||
},
|
||||
/// The stack pointer was adjusted to free the stack.
|
||||
StackDealloc {
|
||||
/// Size to deallocate.
|
||||
size: u32,
|
||||
},
|
||||
/// The alternative register was assigned as frame pointer base.
|
||||
SetFramePointer {
|
||||
/// The specified register.
|
||||
reg: Reg,
|
||||
},
|
||||
/// Restores a frame pointer base to default register.
|
||||
RestoreFramePointer,
|
||||
/// Saves the state.
|
||||
RememberState,
|
||||
/// Restores the state.
|
||||
RestoreState,
|
||||
}
|
||||
|
||||
/// Unwind information as generated by a backend.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
pub struct UnwindInfo<Reg> {
|
||||
/// Size of the prologue.
|
||||
pub prologue_size: CodeOffset,
|
||||
/// Unwind codes for prologue.
|
||||
pub prologue_unwind_codes: Vec<(CodeOffset, UnwindCode<Reg>)>,
|
||||
/// Unwind codes for epilogues.
|
||||
pub epilogues_unwind_codes: Vec<Vec<(CodeOffset, UnwindCode<Reg>)>>,
|
||||
/// Entire function size.
|
||||
pub function_size: CodeOffset,
|
||||
/// Platform word size in bytes.
|
||||
pub word_size: u8,
|
||||
/// Initial stack pointer offset.
|
||||
pub initial_sp_offset: u8,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
//! System V ABI unwind information.
|
||||
|
||||
use crate::isa::unwind::input;
|
||||
use crate::result::{CodegenError, CodegenResult};
|
||||
use alloc::vec::Vec;
|
||||
use gimli::write::{Address, FrameDescriptionEntry};
|
||||
use thiserror::Error;
|
||||
|
@ -92,6 +94,14 @@ impl Into<gimli::write::CallFrameInstruction> for CallFrameInstruction {
|
|||
}
|
||||
}
|
||||
|
||||
/// Maps UnwindInfo register to gimli's index space.
|
||||
pub(crate) trait RegisterMapper<Reg> {
|
||||
/// Maps Reg.
|
||||
fn map(&self, reg: Reg) -> Result<Register, RegisterMappingError>;
|
||||
/// Gets stack pointer register.
|
||||
fn sp(&self) -> Register;
|
||||
}
|
||||
|
||||
/// Represents unwind information for a single System V ABI function.
|
||||
///
|
||||
/// This representation is not ISA specific.
|
||||
|
@ -103,8 +113,58 @@ pub struct UnwindInfo {
|
|||
}
|
||||
|
||||
impl UnwindInfo {
|
||||
pub(crate) fn new(instructions: Vec<(u32, CallFrameInstruction)>, len: u32) -> Self {
|
||||
Self { instructions, len }
|
||||
pub(crate) fn build<'b, Reg: PartialEq + Copy>(
|
||||
unwind: input::UnwindInfo<Reg>,
|
||||
map_reg: &'b dyn RegisterMapper<Reg>,
|
||||
) -> CodegenResult<Self> {
|
||||
use input::UnwindCode;
|
||||
let mut builder = InstructionBuilder::new(unwind.initial_sp_offset, map_reg);
|
||||
|
||||
for (offset, c) in unwind.prologue_unwind_codes.iter().chain(
|
||||
unwind
|
||||
.epilogues_unwind_codes
|
||||
.iter()
|
||||
.map(|c| c.iter())
|
||||
.flatten(),
|
||||
) {
|
||||
match c {
|
||||
UnwindCode::SaveRegister { reg, stack_offset } => {
|
||||
builder
|
||||
.save_reg(*offset, *reg, *stack_offset)
|
||||
.map_err(CodegenError::RegisterMappingError)?;
|
||||
}
|
||||
UnwindCode::StackAlloc { size } => {
|
||||
builder.adjust_sp_down_imm(*offset, *size as i64);
|
||||
}
|
||||
UnwindCode::StackDealloc { size } => {
|
||||
builder.adjust_sp_up_imm(*offset, *size as i64);
|
||||
}
|
||||
UnwindCode::RestoreRegister { reg } => {
|
||||
builder
|
||||
.restore_reg(*offset, *reg)
|
||||
.map_err(CodegenError::RegisterMappingError)?;
|
||||
}
|
||||
UnwindCode::SetFramePointer { reg } => {
|
||||
builder
|
||||
.set_cfa_reg(*offset, *reg)
|
||||
.map_err(CodegenError::RegisterMappingError)?;
|
||||
}
|
||||
UnwindCode::RestoreFramePointer => {
|
||||
builder.restore_cfa(*offset);
|
||||
}
|
||||
UnwindCode::RememberState => {
|
||||
builder.remember_state(*offset);
|
||||
}
|
||||
UnwindCode::RestoreState => {
|
||||
builder.restore_state(*offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let instructions = builder.instructions;
|
||||
let len = unwind.function_size;
|
||||
|
||||
Ok(Self { instructions, len })
|
||||
}
|
||||
|
||||
/// Converts the unwind information into a `FrameDescriptionEntry`.
|
||||
|
@ -118,3 +178,136 @@ impl UnwindInfo {
|
|||
fde
|
||||
}
|
||||
}
|
||||
|
||||
struct InstructionBuilder<'a, Reg: PartialEq + Copy> {
|
||||
sp_offset: i32,
|
||||
frame_register: Option<Reg>,
|
||||
saved_state: Option<(i32, Option<Reg>)>,
|
||||
map_reg: &'a dyn RegisterMapper<Reg>,
|
||||
instructions: Vec<(u32, CallFrameInstruction)>,
|
||||
}
|
||||
|
||||
impl<'a, Reg: PartialEq + Copy> InstructionBuilder<'a, Reg> {
|
||||
fn new(sp_offset: u8, map_reg: &'a (dyn RegisterMapper<Reg> + 'a)) -> Self {
|
||||
Self {
|
||||
sp_offset: sp_offset as i32, // CFA offset starts at the specified offset to account for the return address on stack
|
||||
saved_state: None,
|
||||
frame_register: None,
|
||||
map_reg,
|
||||
instructions: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn save_reg(
|
||||
&mut self,
|
||||
offset: u32,
|
||||
reg: Reg,
|
||||
stack_offset: u32,
|
||||
) -> Result<(), RegisterMappingError> {
|
||||
// Pushes in the prologue are register saves, so record an offset of the save
|
||||
self.instructions.push((
|
||||
offset,
|
||||
CallFrameInstruction::Offset(
|
||||
self.map_reg.map(reg)?,
|
||||
stack_offset as i32 - self.sp_offset,
|
||||
),
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn adjust_sp_down_imm(&mut self, offset: u32, imm: i64) {
|
||||
assert!(imm <= core::u32::MAX as i64);
|
||||
|
||||
self.sp_offset += imm as i32;
|
||||
|
||||
// Don't adjust the CFA if we're using a frame pointer
|
||||
if self.frame_register.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.instructions
|
||||
.push((offset, CallFrameInstruction::CfaOffset(self.sp_offset)));
|
||||
}
|
||||
|
||||
fn adjust_sp_up_imm(&mut self, offset: u32, imm: i64) {
|
||||
assert!(imm <= core::u32::MAX as i64);
|
||||
|
||||
self.sp_offset -= imm as i32;
|
||||
|
||||
// Don't adjust the CFA if we're using a frame pointer
|
||||
if self.frame_register.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
let cfa_inst_ofs = {
|
||||
// Scan to find and merge with CFA instruction with the same offset.
|
||||
let mut it = self.instructions.iter_mut();
|
||||
loop {
|
||||
match it.next_back() {
|
||||
Some((i_offset, i)) if *i_offset == offset => {
|
||||
if let CallFrameInstruction::Cfa(_, o) = i {
|
||||
break Some(o);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
break None;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(o) = cfa_inst_ofs {
|
||||
// Update previous CFA instruction.
|
||||
*o = self.sp_offset;
|
||||
} else {
|
||||
// Add just CFA offset instruction.
|
||||
self.instructions
|
||||
.push((offset, CallFrameInstruction::CfaOffset(self.sp_offset)));
|
||||
}
|
||||
}
|
||||
|
||||
fn set_cfa_reg(&mut self, offset: u32, reg: Reg) -> Result<(), RegisterMappingError> {
|
||||
self.instructions.push((
|
||||
offset,
|
||||
CallFrameInstruction::CfaRegister(self.map_reg.map(reg)?),
|
||||
));
|
||||
self.frame_register = Some(reg);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn restore_cfa(&mut self, offset: u32) {
|
||||
// Restore SP and its offset.
|
||||
self.instructions.push((
|
||||
offset,
|
||||
CallFrameInstruction::Cfa(self.map_reg.sp(), self.sp_offset),
|
||||
));
|
||||
self.frame_register = None;
|
||||
}
|
||||
|
||||
fn restore_reg(&mut self, offset: u32, reg: Reg) -> Result<(), RegisterMappingError> {
|
||||
// Pops in the epilogue are register restores, so record a "same value" for the register
|
||||
self.instructions.push((
|
||||
offset,
|
||||
CallFrameInstruction::SameValue(self.map_reg.map(reg)?),
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remember_state(&mut self, offset: u32) {
|
||||
self.saved_state = Some((self.sp_offset, self.frame_register));
|
||||
|
||||
self.instructions
|
||||
.push((offset, CallFrameInstruction::RememberState));
|
||||
}
|
||||
|
||||
fn restore_state(&mut self, offset: u32) {
|
||||
let (sp_offset, frame_register) = self.saved_state.take().unwrap();
|
||||
self.sp_offset = sp_offset;
|
||||
self.frame_register = frame_register;
|
||||
|
||||
self.instructions
|
||||
.push((offset, CallFrameInstruction::RestoreState));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
//! Windows x64 ABI unwind information.
|
||||
|
||||
use crate::isa::{unwind::input, RegUnit};
|
||||
use crate::result::{CodegenError, CodegenResult};
|
||||
use alloc::vec::Vec;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use log::warn;
|
||||
#[cfg(feature = "enable-serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -134,6 +137,17 @@ impl UnwindCode {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) enum MappedRegister {
|
||||
Int(u8),
|
||||
Xmm(u8),
|
||||
}
|
||||
|
||||
/// Maps UnwindInfo register to Windows x64 unwind data.
|
||||
pub(crate) trait RegisterMapper {
|
||||
/// Maps RegUnit.
|
||||
fn map(reg: RegUnit) -> MappedRegister;
|
||||
}
|
||||
|
||||
/// Represents Windows x64 unwind information.
|
||||
///
|
||||
/// For information about Windows x64 unwind info, see:
|
||||
|
@ -204,4 +218,77 @@ impl UnwindInfo {
|
|||
.iter()
|
||||
.fold(0, |nodes, c| nodes + c.node_count())
|
||||
}
|
||||
|
||||
pub(crate) fn build<MR: RegisterMapper>(
|
||||
unwind: input::UnwindInfo<RegUnit>,
|
||||
) -> CodegenResult<Self> {
|
||||
use crate::isa::unwind::input::UnwindCode as InputUnwindCode;
|
||||
|
||||
let word_size: u32 = unwind.word_size.into();
|
||||
let mut unwind_codes = Vec::new();
|
||||
for (offset, c) in unwind.prologue_unwind_codes.iter() {
|
||||
match c {
|
||||
InputUnwindCode::SaveRegister { reg, stack_offset } => {
|
||||
let reg = MR::map(*reg);
|
||||
let offset = ensure_unwind_offset(*offset)?;
|
||||
match reg {
|
||||
MappedRegister::Int(reg) => {
|
||||
// Attempt to convert sequence of the `InputUnwindCode`:
|
||||
// `StackAlloc { size = word_size }`, `SaveRegister { stack_offset: 0 }`
|
||||
// to the shorter `UnwindCode::PushRegister`.
|
||||
let push_reg_sequence = if let Some(UnwindCode::StackAlloc {
|
||||
offset: alloc_offset,
|
||||
size,
|
||||
}) = unwind_codes.last()
|
||||
{
|
||||
*size == word_size && offset == *alloc_offset && *stack_offset == 0
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if push_reg_sequence {
|
||||
*unwind_codes.last_mut().unwrap() =
|
||||
UnwindCode::PushRegister { offset, reg };
|
||||
} else {
|
||||
// TODO add `UnwindCode::SaveRegister` to handle multiple register
|
||||
// pushes with single `UnwindCode::StackAlloc`.
|
||||
return Err(CodegenError::Unsupported(
|
||||
"Unsupported UnwindCode::PushRegister sequence".into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
MappedRegister::Xmm(reg) => {
|
||||
unwind_codes.push(UnwindCode::SaveXmm {
|
||||
offset,
|
||||
reg,
|
||||
stack_offset: *stack_offset,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
InputUnwindCode::StackAlloc { size } => {
|
||||
unwind_codes.push(UnwindCode::StackAlloc {
|
||||
offset: ensure_unwind_offset(*offset)?,
|
||||
size: *size,
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
flags: 0, // this assumes cranelift functions have no SEH handlers
|
||||
prologue_size: ensure_unwind_offset(unwind.prologue_size)?,
|
||||
frame_register: None,
|
||||
frame_register_offset: 0,
|
||||
unwind_codes,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_unwind_offset(offset: u32) -> CodegenResult<u8> {
|
||||
if offset > 255 {
|
||||
warn!("function prologues cannot exceed 255 bytes in size for Windows x64");
|
||||
return Err(CodegenError::CodeTooLarge);
|
||||
}
|
||||
Ok(offset as u8)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Implementation of the standard x64 ABI.
|
||||
|
||||
use crate::ir::types::*;
|
||||
use crate::ir::{self, types, SourceLoc, TrapCode, Type};
|
||||
use crate::ir::{self, types, MemFlags, TrapCode, Type};
|
||||
use crate::isa;
|
||||
use crate::isa::{x64::inst::*, CallConv};
|
||||
use crate::machinst::abi_impl::*;
|
||||
|
@ -90,6 +90,11 @@ impl ABIMachineSpec for X64ABIMachineSpec {
|
|||
64
|
||||
}
|
||||
|
||||
/// Return required stack alignment in bytes.
|
||||
fn stack_align(_call_conv: isa::CallConv) -> u32 {
|
||||
16
|
||||
}
|
||||
|
||||
fn compute_arg_locs(
|
||||
call_conv: isa::CallConv,
|
||||
params: &[ir::AbiParam],
|
||||
|
@ -247,11 +252,11 @@ impl ABIMachineSpec for X64ABIMachineSpec {
|
|||
_ if ty.bytes() == 16 => ExtKind::None,
|
||||
_ => panic!("load_stack({})", ty),
|
||||
};
|
||||
Inst::load(ty, mem, into_reg, ext_kind, /* infallible */ None)
|
||||
Inst::load(ty, mem, into_reg, ext_kind)
|
||||
}
|
||||
|
||||
fn gen_store_stack(mem: StackAMode, from_reg: Reg, ty: Type) -> Self::I {
|
||||
Inst::store(ty, from_reg, mem, /* infallible */ None)
|
||||
Inst::store(ty, from_reg, mem)
|
||||
}
|
||||
|
||||
fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Self::I {
|
||||
|
@ -269,18 +274,18 @@ impl ABIMachineSpec for X64ABIMachineSpec {
|
|||
let ext_mode = ExtMode::new(from_bits as u16, to_bits as u16)
|
||||
.expect(&format!("invalid extension: {} -> {}", from_bits, to_bits));
|
||||
if is_signed {
|
||||
Inst::movsx_rm_r(ext_mode, RegMem::reg(from_reg), to_reg, None)
|
||||
Inst::movsx_rm_r(ext_mode, RegMem::reg(from_reg), to_reg)
|
||||
} else {
|
||||
Inst::movzx_rm_r(ext_mode, RegMem::reg(from_reg), to_reg, None)
|
||||
Inst::movzx_rm_r(ext_mode, RegMem::reg(from_reg), to_reg)
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_ret() -> Self::I {
|
||||
Inst::Ret
|
||||
Inst::ret()
|
||||
}
|
||||
|
||||
fn gen_epilogue_placeholder() -> Self::I {
|
||||
Inst::EpiloguePlaceholder
|
||||
Inst::epilogue_placeholder()
|
||||
}
|
||||
|
||||
fn gen_add_imm(into_reg: Writable<Reg>, from_reg: Reg, imm: u32) -> SmallVec<[Self::I; 4]> {
|
||||
|
@ -303,7 +308,6 @@ impl ABIMachineSpec for X64ABIMachineSpec {
|
|||
Inst::TrapIf {
|
||||
// NBE == "> unsigned"; args above are reversed; this tests limit_reg > rsp.
|
||||
cc: CC::NBE,
|
||||
srcloc: SourceLoc::default(),
|
||||
trap_code: TrapCode::StackOverflow,
|
||||
},
|
||||
]
|
||||
|
@ -330,13 +334,13 @@ impl ABIMachineSpec for X64ABIMachineSpec {
|
|||
assert_eq!(ty, I64);
|
||||
let simm32 = offset as u32;
|
||||
let mem = Amode::imm_reg(simm32, base);
|
||||
Inst::load(ty, mem, into_reg, ExtKind::None, None)
|
||||
Inst::load(ty, mem, into_reg, ExtKind::None)
|
||||
}
|
||||
|
||||
fn gen_store_base_offset(base: Reg, offset: i32, from_reg: Reg, ty: Type) -> Self::I {
|
||||
let simm32 = offset as u32;
|
||||
let mem = Amode::imm_reg(simm32, base);
|
||||
Inst::store(ty, from_reg, mem, None)
|
||||
Inst::store(ty, from_reg, mem)
|
||||
}
|
||||
|
||||
fn gen_sp_reg_adjust(amount: i32) -> SmallVec<[Self::I; 2]> {
|
||||
|
@ -390,6 +394,7 @@ impl ABIMachineSpec for X64ABIMachineSpec {
|
|||
_: &settings::Flags,
|
||||
clobbers: &Set<Writable<RealReg>>,
|
||||
fixed_frame_storage_size: u32,
|
||||
_outgoing_args_size: u32,
|
||||
) -> (u64, SmallVec<[Self::I; 16]>) {
|
||||
let mut insts = SmallVec::new();
|
||||
// Find all clobbered registers that are callee-save. These are only I64
|
||||
|
@ -420,7 +425,6 @@ impl ABIMachineSpec for X64ABIMachineSpec {
|
|||
/* bytes = */ 8,
|
||||
r_reg.to_reg(),
|
||||
Amode::imm_reg(cur_offset, regs::rsp()),
|
||||
None,
|
||||
));
|
||||
cur_offset += 8;
|
||||
}
|
||||
|
@ -437,6 +441,8 @@ impl ABIMachineSpec for X64ABIMachineSpec {
|
|||
call_conv: isa::CallConv,
|
||||
flags: &settings::Flags,
|
||||
clobbers: &Set<Writable<RealReg>>,
|
||||
_fixed_frame_storage_size: u32,
|
||||
_outgoing_args_size: u32,
|
||||
) -> SmallVec<[Self::I; 16]> {
|
||||
let mut insts = SmallVec::new();
|
||||
|
||||
|
@ -453,7 +459,6 @@ impl ABIMachineSpec for X64ABIMachineSpec {
|
|||
insts.push(Inst::mov64_m_r(
|
||||
Amode::imm_reg(cur_offset, regs::rsp()),
|
||||
Writable::from_reg(rreg.to_reg()),
|
||||
None,
|
||||
));
|
||||
cur_offset += 8;
|
||||
}
|
||||
|
@ -478,7 +483,6 @@ impl ABIMachineSpec for X64ABIMachineSpec {
|
|||
insts.push(Inst::mov64_m_r(
|
||||
Amode::imm_reg(off as u32, regs::rbp()),
|
||||
Writable::from_reg(regs::r14()),
|
||||
None,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -490,16 +494,17 @@ impl ABIMachineSpec for X64ABIMachineSpec {
|
|||
dest: &CallDest,
|
||||
uses: Vec<Reg>,
|
||||
defs: Vec<Writable<Reg>>,
|
||||
loc: SourceLoc,
|
||||
opcode: ir::Opcode,
|
||||
tmp: Writable<Reg>,
|
||||
_callee_conv: isa::CallConv,
|
||||
_caller_conv: isa::CallConv,
|
||||
) -> SmallVec<[(InstIsSafepoint, Self::I); 2]> {
|
||||
let mut insts = SmallVec::new();
|
||||
match dest {
|
||||
&CallDest::ExtName(ref name, RelocDistance::Near) => {
|
||||
insts.push((
|
||||
InstIsSafepoint::Yes,
|
||||
Inst::call_known(name.clone(), uses, defs, loc, opcode),
|
||||
Inst::call_known(name.clone(), uses, defs, opcode),
|
||||
));
|
||||
}
|
||||
&CallDest::ExtName(ref name, RelocDistance::Far) => {
|
||||
|
@ -509,18 +514,17 @@ impl ABIMachineSpec for X64ABIMachineSpec {
|
|||
dst: tmp,
|
||||
name: Box::new(name.clone()),
|
||||
offset: 0,
|
||||
srcloc: loc,
|
||||
},
|
||||
));
|
||||
insts.push((
|
||||
InstIsSafepoint::Yes,
|
||||
Inst::call_unknown(RegMem::reg(tmp.to_reg()), uses, defs, loc, opcode),
|
||||
Inst::call_unknown(RegMem::reg(tmp.to_reg()), uses, defs, opcode),
|
||||
));
|
||||
}
|
||||
&CallDest::Reg(reg) => {
|
||||
insts.push((
|
||||
InstIsSafepoint::Yes,
|
||||
Inst::call_unknown(RegMem::reg(reg), uses, defs, loc, opcode),
|
||||
Inst::call_unknown(RegMem::reg(reg), uses, defs, opcode),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -545,7 +549,7 @@ impl ABIMachineSpec for X64ABIMachineSpec {
|
|||
s.nominal_sp_to_fp
|
||||
}
|
||||
|
||||
fn get_caller_saves(call_conv: isa::CallConv) -> Vec<Writable<Reg>> {
|
||||
fn get_regs_clobbered_by_call(call_conv_of_callee: isa::CallConv) -> Vec<Writable<Reg>> {
|
||||
let mut caller_saved = vec![
|
||||
// Systemv calling convention:
|
||||
// - GPR: all except RBX, RBP, R12 to R15 (which are callee-saved).
|
||||
|
@ -577,7 +581,7 @@ impl ABIMachineSpec for X64ABIMachineSpec {
|
|||
Writable::from_reg(regs::xmm15()),
|
||||
];
|
||||
|
||||
if call_conv.extends_baldrdash() {
|
||||
if call_conv_of_callee.extends_baldrdash() {
|
||||
caller_saved.push(Writable::from_reg(regs::r12()));
|
||||
caller_saved.push(Writable::from_reg(regs::r13()));
|
||||
// Not r14; implicitly preserved in the entry.
|
||||
|
@ -601,6 +605,7 @@ impl From<StackAMode> for SyntheticAmode {
|
|||
SyntheticAmode::Real(Amode::ImmReg {
|
||||
simm32,
|
||||
base: regs::rbp(),
|
||||
flags: MemFlags::trusted(),
|
||||
})
|
||||
}
|
||||
StackAMode::NominalSPOffset(off, _ty) => {
|
||||
|
@ -617,6 +622,7 @@ impl From<StackAMode> for SyntheticAmode {
|
|||
SyntheticAmode::Real(Amode::ImmReg {
|
||||
simm32,
|
||||
base: regs::rsp(),
|
||||
flags: MemFlags::trusted(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,18 +3,25 @@
|
|||
use super::regs::{self, show_ireg_sized};
|
||||
use super::EmitState;
|
||||
use crate::ir::condcodes::{FloatCC, IntCC};
|
||||
use crate::ir::MemFlags;
|
||||
use crate::machinst::*;
|
||||
use core::fmt::Debug;
|
||||
use regalloc::{RealRegUniverse, Reg, RegClass, RegUsageCollector, RegUsageMapper, Writable};
|
||||
use regalloc::{
|
||||
PrettyPrint, PrettyPrintSized, RealRegUniverse, Reg, RegClass, RegUsageCollector,
|
||||
RegUsageMapper, Writable,
|
||||
};
|
||||
use std::fmt;
|
||||
use std::string::{String, ToString};
|
||||
use std::string::String;
|
||||
|
||||
/// A possible addressing mode (amode) that can be used in instructions.
|
||||
/// These denote a 64-bit value only.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Amode {
|
||||
/// Immediate sign-extended and a Register.
|
||||
ImmReg { simm32: u32, base: Reg },
|
||||
ImmReg {
|
||||
simm32: u32,
|
||||
base: Reg,
|
||||
flags: MemFlags,
|
||||
},
|
||||
|
||||
/// sign-extend-32-to-64(Immediate) + Register1 + (Register2 << Shift)
|
||||
ImmRegRegShift {
|
||||
|
@ -22,17 +29,22 @@ pub enum Amode {
|
|||
base: Reg,
|
||||
index: Reg,
|
||||
shift: u8, /* 0 .. 3 only */
|
||||
flags: MemFlags,
|
||||
},
|
||||
|
||||
/// sign-extend-32-to-64(Immediate) + RIP (instruction pointer).
|
||||
/// To wit: not supported in 32-bits mode.
|
||||
RipRelative { target: BranchTarget },
|
||||
RipRelative { target: MachLabel },
|
||||
}
|
||||
|
||||
impl Amode {
|
||||
pub(crate) fn imm_reg(simm32: u32, base: Reg) -> Self {
|
||||
debug_assert!(base.get_class() == RegClass::I64);
|
||||
Self::ImmReg { simm32, base }
|
||||
Self::ImmReg {
|
||||
simm32,
|
||||
base,
|
||||
flags: MemFlags::trusted(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn imm_reg_reg_shift(simm32: u32, base: Reg, index: Reg, shift: u8) -> Self {
|
||||
|
@ -44,13 +56,38 @@ impl Amode {
|
|||
base,
|
||||
index,
|
||||
shift,
|
||||
flags: MemFlags::trusted(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn rip_relative(target: BranchTarget) -> Self {
|
||||
pub(crate) fn rip_relative(target: MachLabel) -> Self {
|
||||
Self::RipRelative { target }
|
||||
}
|
||||
|
||||
pub(crate) fn with_flags(&self, flags: MemFlags) -> Self {
|
||||
match self {
|
||||
&Self::ImmReg { simm32, base, .. } => Self::ImmReg {
|
||||
simm32,
|
||||
base,
|
||||
flags,
|
||||
},
|
||||
&Self::ImmRegRegShift {
|
||||
simm32,
|
||||
base,
|
||||
index,
|
||||
shift,
|
||||
..
|
||||
} => Self::ImmRegRegShift {
|
||||
simm32,
|
||||
base,
|
||||
index,
|
||||
shift,
|
||||
flags,
|
||||
},
|
||||
_ => panic!("Amode {:?} cannot take memflags", self),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add the regs mentioned by `self` to `collector`.
|
||||
pub(crate) fn get_regs_as_uses(&self, collector: &mut RegUsageCollector) {
|
||||
match self {
|
||||
|
@ -66,12 +103,24 @@ impl Amode {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_flags(&self) -> MemFlags {
|
||||
match self {
|
||||
Amode::ImmReg { flags, .. } => *flags,
|
||||
Amode::ImmRegRegShift { flags, .. } => *flags,
|
||||
Amode::RipRelative { .. } => MemFlags::trusted(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn can_trap(&self) -> bool {
|
||||
!self.get_flags().notrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for Amode {
|
||||
impl PrettyPrint for Amode {
|
||||
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
match self {
|
||||
Amode::ImmReg { simm32, base } => {
|
||||
Amode::ImmReg { simm32, base, .. } => {
|
||||
format!("{}({})", *simm32 as i32, base.show_rru(mb_rru))
|
||||
}
|
||||
Amode::ImmRegRegShift {
|
||||
|
@ -79,6 +128,7 @@ impl ShowWithRRU for Amode {
|
|||
base,
|
||||
index,
|
||||
shift,
|
||||
..
|
||||
} => format!(
|
||||
"{}({},{},{})",
|
||||
*simm32 as i32,
|
||||
|
@ -86,13 +136,7 @@ impl ShowWithRRU for Amode {
|
|||
index.show_rru(mb_rru),
|
||||
1 << shift
|
||||
),
|
||||
Amode::RipRelative { ref target } => format!(
|
||||
"{}(%rip)",
|
||||
match target {
|
||||
BranchTarget::Label(label) => format!("label{}", label.get()),
|
||||
BranchTarget::ResolvedOffset(offset) => offset.to_string(),
|
||||
}
|
||||
),
|
||||
Amode::RipRelative { ref target } => format!("label{}(%rip)", target.get()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,7 +200,7 @@ impl Into<SyntheticAmode> for Amode {
|
|||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for SyntheticAmode {
|
||||
impl PrettyPrint for SyntheticAmode {
|
||||
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
match self {
|
||||
SyntheticAmode::Real(addr) => addr.show_rru(mb_rru),
|
||||
|
@ -214,11 +258,13 @@ impl RegMemImm {
|
|||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for RegMemImm {
|
||||
impl PrettyPrint for RegMemImm {
|
||||
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
self.show_rru_sized(mb_rru, 8)
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyPrintSized for RegMemImm {
|
||||
fn show_rru_sized(&self, mb_rru: Option<&RealRegUniverse>, size: u8) -> String {
|
||||
match self {
|
||||
Self::Reg { reg } => show_ireg_sized(*reg, mb_rru, size),
|
||||
|
@ -271,11 +317,13 @@ impl From<Writable<Reg>> for RegMem {
|
|||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for RegMem {
|
||||
impl PrettyPrint for RegMem {
|
||||
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
self.show_rru_sized(mb_rru, 8)
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyPrintSized for RegMem {
|
||||
fn show_rru_sized(&self, mb_rru: Option<&RealRegUniverse>, size: u8) -> String {
|
||||
match self {
|
||||
RegMem::Reg { reg } => show_ireg_sized(*reg, mb_rru, size),
|
||||
|
@ -364,12 +412,14 @@ pub enum SseOpcode {
|
|||
Cmppd,
|
||||
Cmpss,
|
||||
Cmpsd,
|
||||
Cvtdq2ps,
|
||||
Cvtsd2ss,
|
||||
Cvtsd2si,
|
||||
Cvtsi2ss,
|
||||
Cvtsi2sd,
|
||||
Cvtss2si,
|
||||
Cvtss2sd,
|
||||
Cvttps2dq,
|
||||
Cvttss2si,
|
||||
Cvttsd2si,
|
||||
Divps,
|
||||
|
@ -391,6 +441,8 @@ pub enum SseOpcode {
|
|||
Movdqa,
|
||||
Movdqu,
|
||||
Movlhps,
|
||||
Movmskps,
|
||||
Movmskpd,
|
||||
Movq,
|
||||
Movss,
|
||||
Movsd,
|
||||
|
@ -405,6 +457,7 @@ pub enum SseOpcode {
|
|||
Pabsb,
|
||||
Pabsw,
|
||||
Pabsd,
|
||||
Packsswb,
|
||||
Paddb,
|
||||
Paddd,
|
||||
Paddq,
|
||||
|
@ -413,6 +466,8 @@ pub enum SseOpcode {
|
|||
Paddsw,
|
||||
Paddusb,
|
||||
Paddusw,
|
||||
Pand,
|
||||
Pandn,
|
||||
Pavgb,
|
||||
Pavgw,
|
||||
Pcmpeqb,
|
||||
|
@ -441,6 +496,7 @@ pub enum SseOpcode {
|
|||
Pminub,
|
||||
Pminuw,
|
||||
Pminud,
|
||||
Pmovmskb,
|
||||
Pmulld,
|
||||
Pmullw,
|
||||
Pmuludq,
|
||||
|
@ -459,6 +515,10 @@ pub enum SseOpcode {
|
|||
Psubd,
|
||||
Psubq,
|
||||
Psubw,
|
||||
Psubsb,
|
||||
Psubsw,
|
||||
Psubusb,
|
||||
Psubusw,
|
||||
Ptest,
|
||||
Pxor,
|
||||
Rcpss,
|
||||
|
@ -502,6 +562,7 @@ impl SseOpcode {
|
|||
| SseOpcode::Minss
|
||||
| SseOpcode::Movaps
|
||||
| SseOpcode::Movlhps
|
||||
| SseOpcode::Movmskps
|
||||
| SseOpcode::Movss
|
||||
| SseOpcode::Movups
|
||||
| SseOpcode::Mulps
|
||||
|
@ -523,10 +584,12 @@ impl SseOpcode {
|
|||
| SseOpcode::Cmppd
|
||||
| SseOpcode::Cmpsd
|
||||
| SseOpcode::Comisd
|
||||
| SseOpcode::Cvtdq2ps
|
||||
| SseOpcode::Cvtsd2ss
|
||||
| SseOpcode::Cvtsd2si
|
||||
| SseOpcode::Cvtsi2sd
|
||||
| SseOpcode::Cvtss2sd
|
||||
| SseOpcode::Cvttps2dq
|
||||
| SseOpcode::Cvttsd2si
|
||||
| SseOpcode::Divpd
|
||||
| SseOpcode::Divsd
|
||||
|
@ -536,6 +599,7 @@ impl SseOpcode {
|
|||
| SseOpcode::Minsd
|
||||
| SseOpcode::Movapd
|
||||
| SseOpcode::Movd
|
||||
| SseOpcode::Movmskpd
|
||||
| SseOpcode::Movq
|
||||
| SseOpcode::Movsd
|
||||
| SseOpcode::Movupd
|
||||
|
@ -544,6 +608,7 @@ impl SseOpcode {
|
|||
| SseOpcode::Mulpd
|
||||
| SseOpcode::Mulsd
|
||||
| SseOpcode::Orpd
|
||||
| SseOpcode::Packsswb
|
||||
| SseOpcode::Paddb
|
||||
| SseOpcode::Paddd
|
||||
| SseOpcode::Paddq
|
||||
|
@ -552,6 +617,8 @@ impl SseOpcode {
|
|||
| SseOpcode::Paddsw
|
||||
| SseOpcode::Paddusb
|
||||
| SseOpcode::Paddusw
|
||||
| SseOpcode::Pand
|
||||
| SseOpcode::Pandn
|
||||
| SseOpcode::Pavgb
|
||||
| SseOpcode::Pavgw
|
||||
| SseOpcode::Pcmpeqb
|
||||
|
@ -566,6 +633,7 @@ impl SseOpcode {
|
|||
| SseOpcode::Pmaxub
|
||||
| SseOpcode::Pminsw
|
||||
| SseOpcode::Pminub
|
||||
| SseOpcode::Pmovmskb
|
||||
| SseOpcode::Pmullw
|
||||
| SseOpcode::Pmuludq
|
||||
| SseOpcode::Por
|
||||
|
@ -582,6 +650,10 @@ impl SseOpcode {
|
|||
| SseOpcode::Psubd
|
||||
| SseOpcode::Psubq
|
||||
| SseOpcode::Psubw
|
||||
| SseOpcode::Psubsb
|
||||
| SseOpcode::Psubsw
|
||||
| SseOpcode::Psubusb
|
||||
| SseOpcode::Psubusw
|
||||
| SseOpcode::Pxor
|
||||
| SseOpcode::Sqrtpd
|
||||
| SseOpcode::Sqrtsd
|
||||
|
@ -641,12 +713,14 @@ impl fmt::Debug for SseOpcode {
|
|||
SseOpcode::Cmpsd => "cmpsd",
|
||||
SseOpcode::Comiss => "comiss",
|
||||
SseOpcode::Comisd => "comisd",
|
||||
SseOpcode::Cvtdq2ps => "cvtdq2ps",
|
||||
SseOpcode::Cvtsd2ss => "cvtsd2ss",
|
||||
SseOpcode::Cvtsd2si => "cvtsd2si",
|
||||
SseOpcode::Cvtsi2ss => "cvtsi2ss",
|
||||
SseOpcode::Cvtsi2sd => "cvtsi2sd",
|
||||
SseOpcode::Cvtss2si => "cvtss2si",
|
||||
SseOpcode::Cvtss2sd => "cvtss2sd",
|
||||
SseOpcode::Cvttps2dq => "cvttps2dq",
|
||||
SseOpcode::Cvttss2si => "cvttss2si",
|
||||
SseOpcode::Cvttsd2si => "cvttsd2si",
|
||||
SseOpcode::Divps => "divps",
|
||||
|
@ -668,6 +742,8 @@ impl fmt::Debug for SseOpcode {
|
|||
SseOpcode::Movdqa => "movdqa",
|
||||
SseOpcode::Movdqu => "movdqu",
|
||||
SseOpcode::Movlhps => "movlhps",
|
||||
SseOpcode::Movmskps => "movmskps",
|
||||
SseOpcode::Movmskpd => "movmskpd",
|
||||
SseOpcode::Movq => "movq",
|
||||
SseOpcode::Movss => "movss",
|
||||
SseOpcode::Movsd => "movsd",
|
||||
|
@ -682,6 +758,7 @@ impl fmt::Debug for SseOpcode {
|
|||
SseOpcode::Pabsb => "pabsb",
|
||||
SseOpcode::Pabsw => "pabsw",
|
||||
SseOpcode::Pabsd => "pabsd",
|
||||
SseOpcode::Packsswb => "packsswb",
|
||||
SseOpcode::Paddb => "paddb",
|
||||
SseOpcode::Paddd => "paddd",
|
||||
SseOpcode::Paddq => "paddq",
|
||||
|
@ -690,6 +767,8 @@ impl fmt::Debug for SseOpcode {
|
|||
SseOpcode::Paddsw => "paddsw",
|
||||
SseOpcode::Paddusb => "paddusb",
|
||||
SseOpcode::Paddusw => "paddusw",
|
||||
SseOpcode::Pand => "pand",
|
||||
SseOpcode::Pandn => "pandn",
|
||||
SseOpcode::Pavgb => "pavgb",
|
||||
SseOpcode::Pavgw => "pavgw",
|
||||
SseOpcode::Pcmpeqb => "pcmpeqb",
|
||||
|
@ -718,6 +797,7 @@ impl fmt::Debug for SseOpcode {
|
|||
SseOpcode::Pminub => "pminub",
|
||||
SseOpcode::Pminuw => "pminuw",
|
||||
SseOpcode::Pminud => "pminud",
|
||||
SseOpcode::Pmovmskb => "pmovmskb",
|
||||
SseOpcode::Pmulld => "pmulld",
|
||||
SseOpcode::Pmullw => "pmullw",
|
||||
SseOpcode::Pmuludq => "pmuludq",
|
||||
|
@ -736,6 +816,10 @@ impl fmt::Debug for SseOpcode {
|
|||
SseOpcode::Psubd => "psubd",
|
||||
SseOpcode::Psubq => "psubq",
|
||||
SseOpcode::Psubw => "psubw",
|
||||
SseOpcode::Psubsb => "psubsb",
|
||||
SseOpcode::Psubsw => "psubsw",
|
||||
SseOpcode::Psubusb => "psubusb",
|
||||
SseOpcode::Psubusw => "psubusw",
|
||||
SseOpcode::Ptest => "ptest",
|
||||
SseOpcode::Pxor => "pxor",
|
||||
SseOpcode::Rcpss => "rcpss",
|
||||
|
@ -1087,55 +1171,6 @@ impl From<FloatCC> for FcmpImm {
|
|||
}
|
||||
}
|
||||
|
||||
/// A branch target. Either unresolved (basic-block index) or resolved (offset
|
||||
/// from end of current instruction).
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum BranchTarget {
|
||||
/// An unresolved reference to a MachLabel.
|
||||
Label(MachLabel),
|
||||
|
||||
/// A resolved reference to another instruction, in bytes.
|
||||
ResolvedOffset(isize),
|
||||
}
|
||||
|
||||
impl ShowWithRRU for BranchTarget {
|
||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
match self {
|
||||
BranchTarget::Label(l) => format!("{:?}", l),
|
||||
BranchTarget::ResolvedOffset(offs) => format!("(offset {})", offs),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BranchTarget {
|
||||
/// Get the label.
|
||||
pub fn as_label(&self) -> Option<MachLabel> {
|
||||
match self {
|
||||
&BranchTarget::Label(l) => Some(l),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the offset as a signed 32 bit byte offset. This returns the
|
||||
/// offset in bytes between the first byte of the source and the first
|
||||
/// byte of the target. It does not take into account the Intel-specific
|
||||
/// rule that a branch offset is encoded as relative to the start of the
|
||||
/// following instruction. That is a problem for the emitter to deal
|
||||
/// with. If a label, returns zero.
|
||||
pub fn as_offset32_or_zero(&self) -> i32 {
|
||||
match self {
|
||||
&BranchTarget::ResolvedOffset(off) => {
|
||||
// Leave a bit of slack so that the emitter is guaranteed to
|
||||
// be able to add the length of the jump instruction encoding
|
||||
// to this value and still have a value in signed-32 range.
|
||||
assert!(off >= -0x7FFF_FF00 && off <= 0x7FFF_FF00);
|
||||
off as i32
|
||||
}
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An operand's size in bits.
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum OperandSize {
|
||||
|
@ -1169,6 +1204,7 @@ impl OperandSize {
|
|||
|
||||
/// An x64 memory fence kind.
|
||||
#[derive(Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub enum FenceKind {
|
||||
/// `mfence` instruction ("Memory Fence")
|
||||
MFence,
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,15 +1,15 @@
|
|||
//! This module defines x86_64-specific machine instruction types.
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::binemit::{CodeOffset, StackMap};
|
||||
use crate::ir::{types, ExternalName, Opcode, SourceLoc, TrapCode, Type};
|
||||
use crate::isa::x64::settings as x64_settings;
|
||||
use crate::machinst::*;
|
||||
use crate::{settings, settings::Flags, CodegenError, CodegenResult};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use regalloc::{
|
||||
RealRegUniverse, Reg, RegClass, RegUsageCollector, RegUsageMapper, SpillSlot, VirtualReg,
|
||||
Writable,
|
||||
PrettyPrint, PrettyPrintSized, RealRegUniverse, Reg, RegClass, RegUsageCollector,
|
||||
RegUsageMapper, SpillSlot, VirtualReg, Writable,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::fmt;
|
||||
|
@ -20,6 +20,7 @@ mod emit;
|
|||
#[cfg(test)]
|
||||
mod emit_tests;
|
||||
pub mod regs;
|
||||
pub mod unwind;
|
||||
|
||||
use args::*;
|
||||
use regs::{create_reg_universe_systemv, show_ireg_sized};
|
||||
|
@ -70,7 +71,6 @@ pub enum Inst {
|
|||
size: u8, // 1, 2, 4 or 8
|
||||
signed: bool,
|
||||
divisor: RegMem,
|
||||
loc: SourceLoc,
|
||||
},
|
||||
|
||||
/// The high bits (RDX) of a (un)signed multiply: RDX:RAX := RAX * rhs.
|
||||
|
@ -94,7 +94,6 @@ pub enum Inst {
|
|||
/// different from the temporary.
|
||||
divisor: Writable<Reg>,
|
||||
tmp: Option<Writable<Reg>>,
|
||||
loc: SourceLoc,
|
||||
},
|
||||
|
||||
/// Do a sign-extend based on the sign of the value in rax into rdx: (cwd cdq cqo)
|
||||
|
@ -125,16 +124,12 @@ pub enum Inst {
|
|||
ext_mode: ExtMode,
|
||||
src: RegMem,
|
||||
dst: Writable<Reg>,
|
||||
/// Source location, if the memory access can be out-of-bounds.
|
||||
srcloc: Option<SourceLoc>,
|
||||
},
|
||||
|
||||
/// A plain 64-bit integer load, since MovZX_RM_R can't represent that.
|
||||
Mov64MR {
|
||||
src: SyntheticAmode,
|
||||
dst: Writable<Reg>,
|
||||
/// Source location, if the memory access can be out-of-bounds.
|
||||
srcloc: Option<SourceLoc>,
|
||||
},
|
||||
|
||||
/// Loads the memory address of addr into dst.
|
||||
|
@ -148,8 +143,6 @@ pub enum Inst {
|
|||
ext_mode: ExtMode,
|
||||
src: RegMem,
|
||||
dst: Writable<Reg>,
|
||||
/// Source location, if the memory access can be out-of-bounds.
|
||||
srcloc: Option<SourceLoc>,
|
||||
},
|
||||
|
||||
/// Integer stores: mov (b w l q) reg addr.
|
||||
|
@ -157,8 +150,6 @@ pub enum Inst {
|
|||
size: u8, // 1, 2, 4 or 8.
|
||||
src: Reg,
|
||||
dst: SyntheticAmode,
|
||||
/// Source location, if the memory access can be out-of-bounds.
|
||||
srcloc: Option<SourceLoc>,
|
||||
},
|
||||
|
||||
/// Arithmetic shifts: (shl shr sar) (b w l q) imm reg.
|
||||
|
@ -224,8 +215,6 @@ pub enum Inst {
|
|||
op: SseOpcode,
|
||||
src: RegMem,
|
||||
dst: Writable<Reg>,
|
||||
/// Source location, if the memory access can be out-of-bounds.
|
||||
srcloc: Option<SourceLoc>,
|
||||
},
|
||||
|
||||
/// XMM (scalar or vector) unary op (from xmm to reg/mem): stores, movd, movq
|
||||
|
@ -233,13 +222,11 @@ pub enum Inst {
|
|||
op: SseOpcode,
|
||||
src: Reg,
|
||||
dst: SyntheticAmode,
|
||||
/// Source location, if the memory access can be out-of-bounds.
|
||||
srcloc: Option<SourceLoc>,
|
||||
},
|
||||
|
||||
/// XMM (vector) unary op (to move a constant value into an xmm register): movups
|
||||
XmmLoadConstSeq {
|
||||
val: Vec<u8>,
|
||||
XmmLoadConst {
|
||||
src: VCodeConstant,
|
||||
dst: Writable<Reg>,
|
||||
ty: Type,
|
||||
},
|
||||
|
@ -287,7 +274,6 @@ pub enum Inst {
|
|||
dst: Writable<Reg>,
|
||||
tmp_gpr: Writable<Reg>,
|
||||
tmp_xmm: Writable<Reg>,
|
||||
srcloc: SourceLoc,
|
||||
},
|
||||
|
||||
/// Converts a scalar xmm to an unsigned int32/int64.
|
||||
|
@ -303,7 +289,6 @@ pub enum Inst {
|
|||
dst: Writable<Reg>,
|
||||
tmp_gpr: Writable<Reg>,
|
||||
tmp_xmm: Writable<Reg>,
|
||||
srcloc: SourceLoc,
|
||||
},
|
||||
|
||||
/// A sequence to compute min/max with the proper NaN semantics for xmm registers.
|
||||
|
@ -347,7 +332,6 @@ pub enum Inst {
|
|||
dest: ExternalName,
|
||||
uses: Vec<Reg>,
|
||||
defs: Vec<Writable<Reg>>,
|
||||
loc: SourceLoc,
|
||||
opcode: Opcode,
|
||||
},
|
||||
|
||||
|
@ -356,7 +340,6 @@ pub enum Inst {
|
|||
dest: RegMem,
|
||||
uses: Vec<Reg>,
|
||||
defs: Vec<Writable<Reg>>,
|
||||
loc: SourceLoc,
|
||||
opcode: Opcode,
|
||||
},
|
||||
|
||||
|
@ -368,7 +351,7 @@ pub enum Inst {
|
|||
EpiloguePlaceholder,
|
||||
|
||||
/// Jump to a known target: jmp simm32.
|
||||
JmpKnown { dst: BranchTarget },
|
||||
JmpKnown { dst: MachLabel },
|
||||
|
||||
/// One-way conditional branch: jcond cond target.
|
||||
///
|
||||
|
@ -378,14 +361,14 @@ pub enum Inst {
|
|||
/// A note of caution: in contexts where the branch target is another block, this has to be the
|
||||
/// same successor as the one specified in the terminator branch of the current block.
|
||||
/// Otherwise, this might confuse register allocation by creating new invisible edges.
|
||||
JmpIf { cc: CC, taken: BranchTarget },
|
||||
JmpIf { cc: CC, taken: MachLabel },
|
||||
|
||||
/// Two-way conditional branch: jcond cond target target.
|
||||
/// Emitted as a compound sequence; the MachBuffer will shrink it as appropriate.
|
||||
JmpCond {
|
||||
cc: CC,
|
||||
taken: BranchTarget,
|
||||
not_taken: BranchTarget,
|
||||
taken: MachLabel,
|
||||
not_taken: MachLabel,
|
||||
},
|
||||
|
||||
/// Jump-table sequence, as one compound instruction (see note in lower.rs for rationale).
|
||||
|
@ -396,8 +379,8 @@ pub enum Inst {
|
|||
idx: Reg,
|
||||
tmp1: Writable<Reg>,
|
||||
tmp2: Writable<Reg>,
|
||||
default_target: BranchTarget,
|
||||
targets: Vec<BranchTarget>,
|
||||
default_target: MachLabel,
|
||||
targets: Vec<MachLabel>,
|
||||
targets_for_term: Vec<MachLabel>,
|
||||
},
|
||||
|
||||
|
@ -405,23 +388,18 @@ pub enum Inst {
|
|||
JmpUnknown { target: RegMem },
|
||||
|
||||
/// Traps if the condition code is set.
|
||||
TrapIf {
|
||||
cc: CC,
|
||||
trap_code: TrapCode,
|
||||
srcloc: SourceLoc,
|
||||
},
|
||||
TrapIf { cc: CC, trap_code: TrapCode },
|
||||
|
||||
/// A debug trap.
|
||||
Hlt,
|
||||
|
||||
/// An instruction that will always trigger the illegal instruction exception.
|
||||
Ud2 { trap_info: (SourceLoc, TrapCode) },
|
||||
Ud2 { trap_code: TrapCode },
|
||||
|
||||
/// Loads an external symbol in a register, with a relocation: movabsq $name, dst
|
||||
LoadExtName {
|
||||
dst: Writable<Reg>,
|
||||
name: Box<ExternalName>,
|
||||
srcloc: SourceLoc,
|
||||
offset: i64,
|
||||
},
|
||||
|
||||
|
@ -440,7 +418,6 @@ pub enum Inst {
|
|||
ty: Type, // I8, I16, I32 or I64
|
||||
src: Reg,
|
||||
dst: SyntheticAmode,
|
||||
srcloc: Option<SourceLoc>,
|
||||
},
|
||||
|
||||
/// A synthetic instruction, based on a loop around a native `lock cmpxchg` instruction.
|
||||
|
@ -469,7 +446,6 @@ pub enum Inst {
|
|||
AtomicRmwSeq {
|
||||
ty: Type, // I8, I16, I32 or I64
|
||||
op: inst_common::AtomicRmwOp,
|
||||
srcloc: Option<SourceLoc>,
|
||||
},
|
||||
|
||||
/// A memory fence (mfence, lfence or sfence).
|
||||
|
@ -501,6 +477,71 @@ pub(crate) fn low32_will_sign_extend_to_64(x: u64) -> bool {
|
|||
xs == ((xs << 32) >> 32)
|
||||
}
|
||||
|
||||
impl Inst {
|
||||
fn isa_requirement(&self) -> Option<InstructionSet> {
|
||||
match self {
|
||||
// These instructions are part of SSE2, which is a basic requirement in Cranelift, and
|
||||
// don't have to be checked.
|
||||
Inst::AluRmiR { .. }
|
||||
| Inst::AtomicRmwSeq { .. }
|
||||
| Inst::CallKnown { .. }
|
||||
| Inst::CallUnknown { .. }
|
||||
| Inst::CheckedDivOrRemSeq { .. }
|
||||
| Inst::Cmove { .. }
|
||||
| Inst::CmpRmiR { .. }
|
||||
| Inst::CvtFloatToSintSeq { .. }
|
||||
| Inst::CvtFloatToUintSeq { .. }
|
||||
| Inst::CvtUint64ToFloatSeq { .. }
|
||||
| Inst::Div { .. }
|
||||
| Inst::EpiloguePlaceholder
|
||||
| Inst::Fence { .. }
|
||||
| Inst::Hlt
|
||||
| Inst::Imm { .. }
|
||||
| Inst::JmpCond { .. }
|
||||
| Inst::JmpIf { .. }
|
||||
| Inst::JmpKnown { .. }
|
||||
| Inst::JmpTableSeq { .. }
|
||||
| Inst::JmpUnknown { .. }
|
||||
| Inst::LoadEffectiveAddress { .. }
|
||||
| Inst::LoadExtName { .. }
|
||||
| Inst::LockCmpxchg { .. }
|
||||
| Inst::Mov64MR { .. }
|
||||
| Inst::MovRM { .. }
|
||||
| Inst::MovRR { .. }
|
||||
| Inst::MovsxRmR { .. }
|
||||
| Inst::MovzxRmR { .. }
|
||||
| Inst::MulHi { .. }
|
||||
| Inst::Neg { .. }
|
||||
| Inst::Not { .. }
|
||||
| Inst::Nop { .. }
|
||||
| Inst::Pop64 { .. }
|
||||
| Inst::Push64 { .. }
|
||||
| Inst::Ret
|
||||
| Inst::Setcc { .. }
|
||||
| Inst::ShiftR { .. }
|
||||
| Inst::SignExtendData { .. }
|
||||
| Inst::TrapIf { .. }
|
||||
| Inst::Ud2 { .. }
|
||||
| Inst::UnaryRmR { .. }
|
||||
| Inst::VirtualSPOffsetAdj { .. }
|
||||
| Inst::XmmCmove { .. }
|
||||
| Inst::XmmCmpRmR { .. }
|
||||
| Inst::XmmLoadConst { .. }
|
||||
| Inst::XmmMinMaxSeq { .. }
|
||||
| Inst::XmmUninitializedValue { .. } => None,
|
||||
|
||||
// These use dynamic SSE opcodes.
|
||||
Inst::GprToXmm { op, .. }
|
||||
| Inst::XmmMovRM { op, .. }
|
||||
| Inst::XmmRmiReg { opcode: op, .. }
|
||||
| Inst::XmmRmR { op, .. }
|
||||
| Inst::XmmRmRImm { op, .. }
|
||||
| Inst::XmmToGpr { op, .. }
|
||||
| Inst::XmmUnaryRmR { op, .. } => Some(op.available_from()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handy constructors for Insts.
|
||||
|
||||
impl Inst {
|
||||
|
@ -549,14 +590,13 @@ impl Inst {
|
|||
Inst::Neg { size, src }
|
||||
}
|
||||
|
||||
pub(crate) fn div(size: u8, signed: bool, divisor: RegMem, loc: SourceLoc) -> Inst {
|
||||
pub(crate) fn div(size: u8, signed: bool, divisor: RegMem) -> Inst {
|
||||
divisor.assert_regclass_is(RegClass::I64);
|
||||
debug_assert!(size == 8 || size == 4 || size == 2 || size == 1);
|
||||
Inst::Div {
|
||||
size,
|
||||
signed,
|
||||
divisor,
|
||||
loc,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -571,7 +611,6 @@ impl Inst {
|
|||
size: u8,
|
||||
divisor: Writable<Reg>,
|
||||
tmp: Option<Writable<Reg>>,
|
||||
loc: SourceLoc,
|
||||
) -> Inst {
|
||||
debug_assert!(size == 8 || size == 4 || size == 2 || size == 1);
|
||||
debug_assert!(divisor.to_reg().get_class() == RegClass::I64);
|
||||
|
@ -583,7 +622,6 @@ impl Inst {
|
|||
size,
|
||||
divisor,
|
||||
tmp,
|
||||
loc,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -611,39 +649,23 @@ impl Inst {
|
|||
}
|
||||
|
||||
// TODO Can be replaced by `Inst::move` (high-level) and `Inst::unary_rm_r` (low-level)
|
||||
pub(crate) fn xmm_mov(
|
||||
op: SseOpcode,
|
||||
src: RegMem,
|
||||
dst: Writable<Reg>,
|
||||
srcloc: Option<SourceLoc>,
|
||||
) -> Inst {
|
||||
pub(crate) fn xmm_mov(op: SseOpcode, src: RegMem, dst: Writable<Reg>) -> Inst {
|
||||
src.assert_regclass_is(RegClass::V128);
|
||||
debug_assert!(dst.to_reg().get_class() == RegClass::V128);
|
||||
Inst::XmmUnaryRmR {
|
||||
op,
|
||||
src,
|
||||
dst,
|
||||
srcloc,
|
||||
}
|
||||
Inst::XmmUnaryRmR { op, src, dst }
|
||||
}
|
||||
|
||||
pub(crate) fn xmm_load_const_seq(val: Vec<u8>, dst: Writable<Reg>, ty: Type) -> Inst {
|
||||
debug_assert!(val.len() == 16);
|
||||
pub(crate) fn xmm_load_const(src: VCodeConstant, dst: Writable<Reg>, ty: Type) -> Inst {
|
||||
debug_assert!(dst.to_reg().get_class() == RegClass::V128);
|
||||
debug_assert!(ty.is_vector() && ty.bits() == 128);
|
||||
Inst::XmmLoadConstSeq { val, dst, ty }
|
||||
Inst::XmmLoadConst { src, dst, ty }
|
||||
}
|
||||
|
||||
/// Convenient helper for unary float operations.
|
||||
pub(crate) fn xmm_unary_rm_r(op: SseOpcode, src: RegMem, dst: Writable<Reg>) -> Inst {
|
||||
src.assert_regclass_is(RegClass::V128);
|
||||
debug_assert!(dst.to_reg().get_class() == RegClass::V128);
|
||||
Inst::XmmUnaryRmR {
|
||||
op,
|
||||
src,
|
||||
dst,
|
||||
srcloc: None,
|
||||
}
|
||||
Inst::XmmUnaryRmR { op, src, dst }
|
||||
}
|
||||
|
||||
pub(crate) fn xmm_rm_r(op: SseOpcode, src: RegMem, dst: Writable<Reg>) -> Self {
|
||||
|
@ -657,18 +679,12 @@ impl Inst {
|
|||
Inst::XmmUninitializedValue { dst }
|
||||
}
|
||||
|
||||
pub(crate) fn xmm_mov_r_m(
|
||||
op: SseOpcode,
|
||||
src: Reg,
|
||||
dst: impl Into<SyntheticAmode>,
|
||||
srcloc: Option<SourceLoc>,
|
||||
) -> Inst {
|
||||
pub(crate) fn xmm_mov_r_m(op: SseOpcode, src: Reg, dst: impl Into<SyntheticAmode>) -> Inst {
|
||||
debug_assert!(src.get_class() == RegClass::V128);
|
||||
Inst::XmmMovRM {
|
||||
op,
|
||||
src,
|
||||
dst: dst.into(),
|
||||
srcloc,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -738,7 +754,6 @@ impl Inst {
|
|||
dst: Writable<Reg>,
|
||||
tmp_gpr: Writable<Reg>,
|
||||
tmp_xmm: Writable<Reg>,
|
||||
srcloc: SourceLoc,
|
||||
) -> Inst {
|
||||
debug_assert!(src.to_reg().get_class() == RegClass::V128);
|
||||
debug_assert!(tmp_xmm.to_reg().get_class() == RegClass::V128);
|
||||
|
@ -752,7 +767,6 @@ impl Inst {
|
|||
dst,
|
||||
tmp_gpr,
|
||||
tmp_xmm,
|
||||
srcloc,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -764,7 +778,6 @@ impl Inst {
|
|||
dst: Writable<Reg>,
|
||||
tmp_gpr: Writable<Reg>,
|
||||
tmp_xmm: Writable<Reg>,
|
||||
srcloc: SourceLoc,
|
||||
) -> Inst {
|
||||
debug_assert!(src.to_reg().get_class() == RegClass::V128);
|
||||
debug_assert!(tmp_xmm.to_reg().get_class() == RegClass::V128);
|
||||
|
@ -778,7 +791,6 @@ impl Inst {
|
|||
dst,
|
||||
tmp_gpr,
|
||||
tmp_xmm,
|
||||
srcloc,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -814,20 +826,10 @@ impl Inst {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn movzx_rm_r(
|
||||
ext_mode: ExtMode,
|
||||
src: RegMem,
|
||||
dst: Writable<Reg>,
|
||||
srcloc: Option<SourceLoc>,
|
||||
) -> Inst {
|
||||
pub(crate) fn movzx_rm_r(ext_mode: ExtMode, src: RegMem, dst: Writable<Reg>) -> Inst {
|
||||
src.assert_regclass_is(RegClass::I64);
|
||||
debug_assert!(dst.to_reg().get_class() == RegClass::I64);
|
||||
Inst::MovzxRmR {
|
||||
ext_mode,
|
||||
src,
|
||||
dst,
|
||||
srcloc,
|
||||
}
|
||||
Inst::MovzxRmR { ext_mode, src, dst }
|
||||
}
|
||||
|
||||
pub(crate) fn xmm_rmi_reg(opcode: SseOpcode, src: RegMemImm, dst: Writable<Reg>) -> Inst {
|
||||
|
@ -836,41 +838,26 @@ impl Inst {
|
|||
Inst::XmmRmiReg { opcode, src, dst }
|
||||
}
|
||||
|
||||
pub(crate) fn movsx_rm_r(
|
||||
ext_mode: ExtMode,
|
||||
src: RegMem,
|
||||
dst: Writable<Reg>,
|
||||
srcloc: Option<SourceLoc>,
|
||||
) -> Inst {
|
||||
pub(crate) fn movsx_rm_r(ext_mode: ExtMode, src: RegMem, dst: Writable<Reg>) -> Inst {
|
||||
src.assert_regclass_is(RegClass::I64);
|
||||
debug_assert!(dst.to_reg().get_class() == RegClass::I64);
|
||||
Inst::MovsxRmR {
|
||||
ext_mode,
|
||||
src,
|
||||
dst,
|
||||
srcloc,
|
||||
}
|
||||
Inst::MovsxRmR { ext_mode, src, dst }
|
||||
}
|
||||
|
||||
pub(crate) fn mov64_m_r(
|
||||
src: impl Into<SyntheticAmode>,
|
||||
dst: Writable<Reg>,
|
||||
srcloc: Option<SourceLoc>,
|
||||
) -> Inst {
|
||||
pub(crate) fn mov64_m_r(src: impl Into<SyntheticAmode>, dst: Writable<Reg>) -> Inst {
|
||||
debug_assert!(dst.to_reg().get_class() == RegClass::I64);
|
||||
Inst::Mov64MR {
|
||||
src: src.into(),
|
||||
dst,
|
||||
srcloc,
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience function to be able to use a RegMem as the source of a move.
|
||||
pub(crate) fn mov64_rm_r(src: RegMem, dst: Writable<Reg>, srcloc: Option<SourceLoc>) -> Inst {
|
||||
pub(crate) fn mov64_rm_r(src: RegMem, dst: Writable<Reg>) -> Inst {
|
||||
src.assert_regclass_is(RegClass::I64);
|
||||
match src {
|
||||
RegMem::Reg { reg } => Self::mov_r_r(true, reg, dst),
|
||||
RegMem::Mem { addr } => Self::mov64_m_r(addr, dst, srcloc),
|
||||
RegMem::Mem { addr } => Self::mov64_m_r(addr, dst),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -878,7 +865,6 @@ impl Inst {
|
|||
size: u8, // 1, 2, 4 or 8
|
||||
src: Reg,
|
||||
dst: impl Into<SyntheticAmode>,
|
||||
srcloc: Option<SourceLoc>,
|
||||
) -> Inst {
|
||||
debug_assert!(size == 8 || size == 4 || size == 2 || size == 1);
|
||||
debug_assert!(src.get_class() == RegClass::I64);
|
||||
|
@ -886,7 +872,6 @@ impl Inst {
|
|||
size,
|
||||
src,
|
||||
dst: dst.into(),
|
||||
srcloc,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -932,9 +917,9 @@ impl Inst {
|
|||
Inst::CmpRmiR { size, src, dst }
|
||||
}
|
||||
|
||||
pub(crate) fn trap(srcloc: SourceLoc, trap_code: TrapCode) -> Inst {
|
||||
pub(crate) fn trap(trap_code: TrapCode) -> Inst {
|
||||
Inst::Ud2 {
|
||||
trap_info: (srcloc, trap_code),
|
||||
trap_code: trap_code,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -974,14 +959,12 @@ impl Inst {
|
|||
dest: ExternalName,
|
||||
uses: Vec<Reg>,
|
||||
defs: Vec<Writable<Reg>>,
|
||||
loc: SourceLoc,
|
||||
opcode: Opcode,
|
||||
) -> Inst {
|
||||
Inst::CallKnown {
|
||||
dest,
|
||||
uses,
|
||||
defs,
|
||||
loc,
|
||||
opcode,
|
||||
}
|
||||
}
|
||||
|
@ -990,7 +973,6 @@ impl Inst {
|
|||
dest: RegMem,
|
||||
uses: Vec<Reg>,
|
||||
defs: Vec<Writable<Reg>>,
|
||||
loc: SourceLoc,
|
||||
opcode: Opcode,
|
||||
) -> Inst {
|
||||
dest.assert_regclass_is(RegClass::I64);
|
||||
|
@ -998,7 +980,6 @@ impl Inst {
|
|||
dest,
|
||||
uses,
|
||||
defs,
|
||||
loc,
|
||||
opcode,
|
||||
}
|
||||
}
|
||||
|
@ -1011,15 +992,15 @@ impl Inst {
|
|||
Inst::EpiloguePlaceholder
|
||||
}
|
||||
|
||||
pub(crate) fn jmp_known(dst: BranchTarget) -> Inst {
|
||||
pub(crate) fn jmp_known(dst: MachLabel) -> Inst {
|
||||
Inst::JmpKnown { dst }
|
||||
}
|
||||
|
||||
pub(crate) fn jmp_if(cc: CC, taken: BranchTarget) -> Inst {
|
||||
pub(crate) fn jmp_if(cc: CC, taken: MachLabel) -> Inst {
|
||||
Inst::JmpIf { cc, taken }
|
||||
}
|
||||
|
||||
pub(crate) fn jmp_cond(cc: CC, taken: BranchTarget, not_taken: BranchTarget) -> Inst {
|
||||
pub(crate) fn jmp_cond(cc: CC, taken: MachLabel, not_taken: MachLabel) -> Inst {
|
||||
Inst::JmpCond {
|
||||
cc,
|
||||
taken,
|
||||
|
@ -1032,12 +1013,8 @@ impl Inst {
|
|||
Inst::JmpUnknown { target }
|
||||
}
|
||||
|
||||
pub(crate) fn trap_if(cc: CC, trap_code: TrapCode, srcloc: SourceLoc) -> Inst {
|
||||
Inst::TrapIf {
|
||||
cc,
|
||||
trap_code,
|
||||
srcloc,
|
||||
}
|
||||
pub(crate) fn trap_if(cc: CC, trap_code: TrapCode) -> Inst {
|
||||
Inst::TrapIf { cc, trap_code }
|
||||
}
|
||||
|
||||
/// Choose which instruction to use for loading a register value from memory. For loads smaller
|
||||
|
@ -1048,7 +1025,6 @@ impl Inst {
|
|||
from_addr: impl Into<SyntheticAmode>,
|
||||
to_reg: Writable<Reg>,
|
||||
ext_kind: ExtKind,
|
||||
srcloc: Option<SourceLoc>,
|
||||
) -> Inst {
|
||||
let rc = to_reg.to_reg().get_class();
|
||||
match rc {
|
||||
|
@ -1064,10 +1040,10 @@ impl Inst {
|
|||
// Values smaller than 64 bits must be extended in some way.
|
||||
match ext_kind {
|
||||
ExtKind::SignExtend => {
|
||||
Inst::movsx_rm_r(ext_mode, RegMem::mem(from_addr), to_reg, srcloc)
|
||||
Inst::movsx_rm_r(ext_mode, RegMem::mem(from_addr), to_reg)
|
||||
}
|
||||
ExtKind::ZeroExtend => {
|
||||
Inst::movzx_rm_r(ext_mode, RegMem::mem(from_addr), to_reg, srcloc)
|
||||
Inst::movzx_rm_r(ext_mode, RegMem::mem(from_addr), to_reg)
|
||||
}
|
||||
ExtKind::None => panic!(
|
||||
"expected an extension kind for extension mode: {:?}",
|
||||
|
@ -1076,7 +1052,7 @@ impl Inst {
|
|||
}
|
||||
} else {
|
||||
// 64-bit values can be moved directly.
|
||||
Inst::mov64_m_r(from_addr, to_reg, srcloc)
|
||||
Inst::mov64_m_r(from_addr, to_reg)
|
||||
}
|
||||
}
|
||||
RegClass::V128 => {
|
||||
|
@ -1095,15 +1071,14 @@ impl Inst {
|
|||
}
|
||||
|
||||
/// Choose which instruction to use for storing a register value to memory.
|
||||
pub(crate) fn store(
|
||||
ty: Type,
|
||||
from_reg: Reg,
|
||||
to_addr: impl Into<SyntheticAmode>,
|
||||
srcloc: Option<SourceLoc>,
|
||||
) -> Inst {
|
||||
pub(crate) fn store(ty: Type, from_reg: Reg, to_addr: impl Into<SyntheticAmode>) -> Inst {
|
||||
let rc = from_reg.get_class();
|
||||
match rc {
|
||||
RegClass::I64 => Inst::mov_r_m(ty.bytes() as u8, from_reg, to_addr, srcloc),
|
||||
RegClass::I64 => {
|
||||
// Always store the full register, to ensure that the high bits are properly set
|
||||
// when doing a full reload.
|
||||
Inst::mov_r_m(8 /* bytes */, from_reg, to_addr)
|
||||
}
|
||||
RegClass::V128 => {
|
||||
let opcode = match ty {
|
||||
types::F32 => SseOpcode::Movss,
|
||||
|
@ -1113,7 +1088,7 @@ impl Inst {
|
|||
_ if ty.is_vector() && ty.bits() == 128 => SseOpcode::Movdqu,
|
||||
_ => unimplemented!("unable to store type: {}", ty),
|
||||
};
|
||||
Inst::xmm_mov_r_m(opcode, from_reg, to_addr, srcloc)
|
||||
Inst::xmm_mov_r_m(opcode, from_reg, to_addr)
|
||||
}
|
||||
_ => panic!("unable to generate store for register class: {:?}", rc),
|
||||
}
|
||||
|
@ -1160,12 +1135,69 @@ impl Inst {
|
|||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Choose which instruction to use for comparing two values for equality.
|
||||
pub(crate) fn equals(ty: Type, from: RegMem, to: Writable<Reg>) -> Inst {
|
||||
match ty {
|
||||
types::I8X16 | types::B8X16 => Inst::xmm_rm_r(SseOpcode::Pcmpeqb, from, to),
|
||||
types::I16X8 | types::B16X8 => Inst::xmm_rm_r(SseOpcode::Pcmpeqw, from, to),
|
||||
types::I32X4 | types::B32X4 => Inst::xmm_rm_r(SseOpcode::Pcmpeqd, from, to),
|
||||
types::I64X2 | types::B64X2 => Inst::xmm_rm_r(SseOpcode::Pcmpeqq, from, to),
|
||||
types::F32X4 => {
|
||||
Inst::xmm_rm_r_imm(SseOpcode::Cmpps, from, to, FcmpImm::Equal.encode(), false)
|
||||
}
|
||||
types::F64X2 => {
|
||||
Inst::xmm_rm_r_imm(SseOpcode::Cmppd, from, to, FcmpImm::Equal.encode(), false)
|
||||
}
|
||||
_ => unimplemented!("unimplemented type for Inst::equals: {}", ty),
|
||||
}
|
||||
}
|
||||
|
||||
/// Choose which instruction to use for computing a bitwise AND on two values.
|
||||
pub(crate) fn and(ty: Type, from: RegMem, to: Writable<Reg>) -> Inst {
|
||||
match ty {
|
||||
types::F32X4 => Inst::xmm_rm_r(SseOpcode::Andps, from, to),
|
||||
types::F64X2 => Inst::xmm_rm_r(SseOpcode::Andpd, from, to),
|
||||
_ if ty.is_vector() && ty.bits() == 128 => Inst::xmm_rm_r(SseOpcode::Pand, from, to),
|
||||
_ => unimplemented!("unimplemented type for Inst::and: {}", ty),
|
||||
}
|
||||
}
|
||||
|
||||
/// Choose which instruction to use for computing a bitwise AND NOT on two values.
|
||||
pub(crate) fn and_not(ty: Type, from: RegMem, to: Writable<Reg>) -> Inst {
|
||||
match ty {
|
||||
types::F32X4 => Inst::xmm_rm_r(SseOpcode::Andnps, from, to),
|
||||
types::F64X2 => Inst::xmm_rm_r(SseOpcode::Andnpd, from, to),
|
||||
_ if ty.is_vector() && ty.bits() == 128 => Inst::xmm_rm_r(SseOpcode::Pandn, from, to),
|
||||
_ => unimplemented!("unimplemented type for Inst::and_not: {}", ty),
|
||||
}
|
||||
}
|
||||
|
||||
/// Choose which instruction to use for computing a bitwise OR on two values.
|
||||
pub(crate) fn or(ty: Type, from: RegMem, to: Writable<Reg>) -> Inst {
|
||||
match ty {
|
||||
types::F32X4 => Inst::xmm_rm_r(SseOpcode::Orps, from, to),
|
||||
types::F64X2 => Inst::xmm_rm_r(SseOpcode::Orpd, from, to),
|
||||
_ if ty.is_vector() && ty.bits() == 128 => Inst::xmm_rm_r(SseOpcode::Por, from, to),
|
||||
_ => unimplemented!("unimplemented type for Inst::or: {}", ty),
|
||||
}
|
||||
}
|
||||
|
||||
/// Choose which instruction to use for computing a bitwise XOR on two values.
|
||||
pub(crate) fn xor(ty: Type, from: RegMem, to: Writable<Reg>) -> Inst {
|
||||
match ty {
|
||||
types::F32X4 => Inst::xmm_rm_r(SseOpcode::Xorps, from, to),
|
||||
types::F64X2 => Inst::xmm_rm_r(SseOpcode::Xorpd, from, to),
|
||||
_ if ty.is_vector() && ty.bits() == 128 => Inst::xmm_rm_r(SseOpcode::Pxor, from, to),
|
||||
_ => unimplemented!("unimplemented type for Inst::xor: {}", ty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Instructions: printing
|
||||
|
||||
impl ShowWithRRU for Inst {
|
||||
impl PrettyPrint for Inst {
|
||||
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
fn ljustify(s: String) -> String {
|
||||
let w = 7;
|
||||
|
@ -1303,7 +1335,7 @@ impl ShowWithRRU for Inst {
|
|||
dst.show_rru(mb_rru),
|
||||
),
|
||||
|
||||
Inst::XmmRmR { op, src, dst } => format!(
|
||||
Inst::XmmRmR { op, src, dst, .. } => format!(
|
||||
"{} {}, {}",
|
||||
ljustify(op.to_string()),
|
||||
src.show_rru_sized(mb_rru, 8),
|
||||
|
@ -1333,7 +1365,7 @@ impl ShowWithRRU for Inst {
|
|||
show_ireg_sized(rhs_dst.to_reg(), mb_rru, 8),
|
||||
),
|
||||
|
||||
Inst::XmmRmRImm { op, src, dst, imm, is64 } => format!(
|
||||
Inst::XmmRmRImm { op, src, dst, imm, is64, .. } => format!(
|
||||
"{} ${}, {}, {}",
|
||||
ljustify(format!("{}{}", op.to_string(), if *is64 { ".w" } else { "" })),
|
||||
imm,
|
||||
|
@ -1347,8 +1379,8 @@ impl ShowWithRRU for Inst {
|
|||
dst.show_rru(mb_rru),
|
||||
),
|
||||
|
||||
Inst::XmmLoadConstSeq { val, dst, .. } => {
|
||||
format!("load_const ${:?}, {}", val, dst.show_rru(mb_rru),)
|
||||
Inst::XmmLoadConst { src, dst, .. } => {
|
||||
format!("load_const {:?}, {}", src, dst.show_rru(mb_rru),)
|
||||
}
|
||||
|
||||
Inst::XmmToGpr {
|
||||
|
@ -1613,13 +1645,13 @@ impl ShowWithRRU for Inst {
|
|||
Inst::EpiloguePlaceholder => "epilogue placeholder".to_string(),
|
||||
|
||||
Inst::JmpKnown { dst } => {
|
||||
format!("{} {}", ljustify("jmp".to_string()), dst.show_rru(mb_rru))
|
||||
format!("{} {}", ljustify("jmp".to_string()), dst.to_string())
|
||||
}
|
||||
|
||||
Inst::JmpIf { cc, taken } => format!(
|
||||
"{} {}",
|
||||
ljustify2("j".to_string(), cc.to_string()),
|
||||
taken.show_rru(mb_rru),
|
||||
taken.to_string(),
|
||||
),
|
||||
|
||||
Inst::JmpCond {
|
||||
|
@ -1629,8 +1661,8 @@ impl ShowWithRRU for Inst {
|
|||
} => format!(
|
||||
"{} {}; j {}",
|
||||
ljustify2("j".to_string(), cc.to_string()),
|
||||
taken.show_rru(mb_rru),
|
||||
not_taken.show_rru(mb_rru)
|
||||
taken.to_string(),
|
||||
not_taken.to_string()
|
||||
),
|
||||
|
||||
Inst::JmpTableSeq { idx, .. } => {
|
||||
|
@ -1681,7 +1713,7 @@ impl ShowWithRRU for Inst {
|
|||
|
||||
Inst::Hlt => "hlt".into(),
|
||||
|
||||
Inst::Ud2 { trap_info } => format!("ud2 {}", trap_info.1),
|
||||
Inst::Ud2 { trap_code } => format!("ud2 {}", trap_code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1778,7 +1810,7 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
|
|||
}
|
||||
}
|
||||
Inst::XmmUninitializedValue { dst } => collector.add_def(*dst),
|
||||
Inst::XmmLoadConstSeq { dst, .. } => collector.add_def(*dst),
|
||||
Inst::XmmLoadConst { dst, .. } => collector.add_def(*dst),
|
||||
Inst::XmmMinMaxSeq { lhs, rhs_dst, .. } => {
|
||||
collector.add_use(*lhs);
|
||||
collector.add_mod(*rhs_dst);
|
||||
|
@ -2115,7 +2147,7 @@ fn x64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
|
|||
Inst::XmmUninitializedValue { ref mut dst, .. } => {
|
||||
map_def(mapper, dst);
|
||||
}
|
||||
Inst::XmmLoadConstSeq { ref mut dst, .. } => {
|
||||
Inst::XmmLoadConst { ref mut dst, .. } => {
|
||||
map_def(mapper, dst);
|
||||
}
|
||||
Inst::XmmMinMaxSeq {
|
||||
|
@ -2380,10 +2412,10 @@ impl MachInst for Inst {
|
|||
match self {
|
||||
// Interesting cases.
|
||||
&Self::Ret | &Self::EpiloguePlaceholder => MachTerminator::Ret,
|
||||
&Self::JmpKnown { dst } => MachTerminator::Uncond(dst.as_label().unwrap()),
|
||||
&Self::JmpKnown { dst } => MachTerminator::Uncond(dst),
|
||||
&Self::JmpCond {
|
||||
taken, not_taken, ..
|
||||
} => MachTerminator::Cond(taken.as_label().unwrap(), not_taken.as_label().unwrap()),
|
||||
} => MachTerminator::Cond(taken, not_taken),
|
||||
&Self::JmpTableSeq {
|
||||
ref targets_for_term,
|
||||
..
|
||||
|
@ -2401,10 +2433,12 @@ impl MachInst for Inst {
|
|||
match rc_dst {
|
||||
RegClass::I64 => Inst::mov_r_r(true, src_reg, dst_reg),
|
||||
RegClass::V128 => {
|
||||
// The Intel optimization manual, in "3.5.1.13 Zero-Latency MOV Instructions",
|
||||
// doesn't include MOVSS/MOVSD as instructions with zero-latency. Use movaps for
|
||||
// those, which may write more lanes that we need, but are specified to have
|
||||
// zero-latency.
|
||||
let opcode = match ty {
|
||||
types::F32 => SseOpcode::Movss,
|
||||
types::F64 => SseOpcode::Movsd,
|
||||
types::F32X4 => SseOpcode::Movaps,
|
||||
types::F32 | types::F64 | types::F32X4 => SseOpcode::Movaps,
|
||||
types::F64X2 => SseOpcode::Movapd,
|
||||
_ if ty.is_vector() && ty.bits() == 128 => SseOpcode::Movdqa,
|
||||
_ => unimplemented!("unable to move type: {}", ty),
|
||||
|
@ -2419,8 +2453,8 @@ impl MachInst for Inst {
|
|||
Inst::Nop { len: 0 }
|
||||
}
|
||||
|
||||
fn gen_nop(_preferred_size: usize) -> Inst {
|
||||
unimplemented!()
|
||||
fn gen_nop(preferred_size: usize) -> Inst {
|
||||
Inst::nop((preferred_size % 16) as u8)
|
||||
}
|
||||
|
||||
fn maybe_direct_reload(&self, _reg: VirtualReg, _slot: SpillSlot) -> Option<Inst> {
|
||||
|
@ -2451,7 +2485,7 @@ impl MachInst for Inst {
|
|||
}
|
||||
|
||||
fn gen_jump(label: MachLabel) -> Inst {
|
||||
Inst::jmp_known(BranchTarget::Label(label))
|
||||
Inst::jmp_known(label)
|
||||
}
|
||||
|
||||
fn gen_constant<F: FnMut(RegClass, Type) -> Writable<Reg>>(
|
||||
|
@ -2522,7 +2556,7 @@ impl MachInst for Inst {
|
|||
} else {
|
||||
ret.push(Inst::imm(
|
||||
OperandSize::from_bytes(ty.bytes()),
|
||||
value,
|
||||
value.into(),
|
||||
to_reg,
|
||||
));
|
||||
}
|
||||
|
@ -2555,13 +2589,35 @@ pub struct EmitState {
|
|||
pub(crate) nominal_sp_to_fp: i64,
|
||||
/// Safepoint stack map for upcoming instruction, as provided to `pre_safepoint()`.
|
||||
stack_map: Option<StackMap>,
|
||||
/// Current source location.
|
||||
cur_srcloc: SourceLoc,
|
||||
}
|
||||
|
||||
/// Constant state used during emissions of a sequence of instructions.
|
||||
pub struct EmitInfo {
|
||||
flags: settings::Flags,
|
||||
isa_flags: x64_settings::Flags,
|
||||
}
|
||||
|
||||
impl EmitInfo {
|
||||
pub(crate) fn new(flags: settings::Flags, isa_flags: x64_settings::Flags) -> Self {
|
||||
Self { flags, isa_flags }
|
||||
}
|
||||
}
|
||||
|
||||
impl MachInstEmitInfo for EmitInfo {
|
||||
fn flags(&self) -> &Flags {
|
||||
&self.flags
|
||||
}
|
||||
}
|
||||
|
||||
impl MachInstEmit for Inst {
|
||||
type State = EmitState;
|
||||
type Info = EmitInfo;
|
||||
type UnwindInfo = unwind::X64UnwindInfo;
|
||||
|
||||
fn emit(&self, sink: &mut MachBuffer<Inst>, flags: &settings::Flags, state: &mut Self::State) {
|
||||
emit::emit(self, sink, flags, state);
|
||||
fn emit(&self, sink: &mut MachBuffer<Inst>, info: &Self::Info, state: &mut Self::State) {
|
||||
emit::emit(self, sink, info, state);
|
||||
}
|
||||
|
||||
fn pretty_print(&self, mb_rru: Option<&RealRegUniverse>, _: &mut Self::State) -> String {
|
||||
|
@ -2575,12 +2631,17 @@ impl MachInstEmitState<Inst> for EmitState {
|
|||
virtual_sp_offset: 0,
|
||||
nominal_sp_to_fp: abi.frame_size() as i64,
|
||||
stack_map: None,
|
||||
cur_srcloc: SourceLoc::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn pre_safepoint(&mut self, stack_map: StackMap) {
|
||||
self.stack_map = Some(stack_map);
|
||||
}
|
||||
|
||||
fn pre_sourceloc(&mut self, srcloc: SourceLoc) {
|
||||
self.cur_srcloc = srcloc;
|
||||
}
|
||||
}
|
||||
|
||||
impl EmitState {
|
||||
|
@ -2591,6 +2652,10 @@ impl EmitState {
|
|||
fn clear_post_insn(&mut self) {
|
||||
self.stack_map = None;
|
||||
}
|
||||
|
||||
fn cur_srcloc(&self) -> SourceLoc {
|
||||
self.cur_srcloc
|
||||
}
|
||||
}
|
||||
|
||||
/// A label-use (internal relocation) in generated code.
|
||||
|
|
|
@ -10,9 +10,11 @@
|
|||
//! Also, they will have to be ABI dependent. Need to find a way to avoid constructing a universe
|
||||
//! for each function we compile.
|
||||
|
||||
use crate::{machinst::pretty_print::ShowWithRRU, settings};
|
||||
use crate::settings;
|
||||
use alloc::vec::Vec;
|
||||
use regalloc::{RealReg, RealRegUniverse, Reg, RegClass, RegClassInfo, NUM_REG_CLASSES};
|
||||
use regalloc::{
|
||||
PrettyPrint, RealReg, RealRegUniverse, Reg, RegClass, RegClassInfo, NUM_REG_CLASSES,
|
||||
};
|
||||
use std::string::String;
|
||||
|
||||
// Hardware encodings for a few registers.
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
use crate::isa::unwind::input::UnwindInfo;
|
||||
use crate::isa::x64::inst::{
|
||||
args::{AluRmiROpcode, Amode, RegMemImm, SyntheticAmode},
|
||||
regs, Inst,
|
||||
};
|
||||
use crate::machinst::{UnwindInfoContext, UnwindInfoGenerator};
|
||||
use crate::result::CodegenResult;
|
||||
use alloc::vec::Vec;
|
||||
use regalloc::Reg;
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
pub(crate) mod systemv;
|
||||
|
||||
pub struct X64UnwindInfo;
|
||||
|
||||
impl UnwindInfoGenerator<Inst> for X64UnwindInfo {
|
||||
fn create_unwind_info(
|
||||
context: UnwindInfoContext<Inst>,
|
||||
) -> CodegenResult<Option<UnwindInfo<Reg>>> {
|
||||
use crate::isa::unwind::input::{self, UnwindCode};
|
||||
let mut codes = Vec::new();
|
||||
const WORD_SIZE: u8 = 8;
|
||||
|
||||
for i in context.prologue.clone() {
|
||||
let i = i as usize;
|
||||
let inst = &context.insts[i];
|
||||
let offset = context.insts_layout[i];
|
||||
|
||||
match inst {
|
||||
Inst::Push64 {
|
||||
src: RegMemImm::Reg { reg },
|
||||
} => {
|
||||
codes.push((
|
||||
offset,
|
||||
UnwindCode::StackAlloc {
|
||||
size: WORD_SIZE.into(),
|
||||
},
|
||||
));
|
||||
codes.push((
|
||||
offset,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: *reg,
|
||||
stack_offset: 0,
|
||||
},
|
||||
));
|
||||
}
|
||||
Inst::MovRR { src, dst, .. } => {
|
||||
if *src == regs::rsp() {
|
||||
codes.push((offset, UnwindCode::SetFramePointer { reg: dst.to_reg() }));
|
||||
}
|
||||
}
|
||||
Inst::AluRmiR {
|
||||
is_64: true,
|
||||
op: AluRmiROpcode::Sub,
|
||||
src: RegMemImm::Imm { simm32 },
|
||||
dst,
|
||||
..
|
||||
} if dst.to_reg() == regs::rsp() => {
|
||||
let imm = *simm32;
|
||||
codes.push((offset, UnwindCode::StackAlloc { size: imm }));
|
||||
}
|
||||
Inst::MovRM {
|
||||
src,
|
||||
dst: SyntheticAmode::Real(Amode::ImmReg { simm32, base, .. }),
|
||||
..
|
||||
} if *base == regs::rsp() => {
|
||||
// `mov reg, imm(rsp)`
|
||||
let imm = *simm32;
|
||||
codes.push((
|
||||
offset,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: *src,
|
||||
stack_offset: imm,
|
||||
},
|
||||
));
|
||||
}
|
||||
Inst::AluRmiR {
|
||||
is_64: true,
|
||||
op: AluRmiROpcode::Add,
|
||||
src: RegMemImm::Imm { simm32 },
|
||||
dst,
|
||||
..
|
||||
} if dst.to_reg() == regs::rsp() => {
|
||||
let imm = *simm32;
|
||||
codes.push((offset, UnwindCode::StackDealloc { size: imm }));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let last_epilogue_end = context.len;
|
||||
let epilogues_unwind_codes = context
|
||||
.epilogues
|
||||
.iter()
|
||||
.map(|epilogue| {
|
||||
// TODO add logic to process epilogue instruction instead of
|
||||
// returning empty array.
|
||||
let end = epilogue.end as usize - 1;
|
||||
let end_offset = context.insts_layout[end];
|
||||
if end_offset == last_epilogue_end {
|
||||
// Do not remember/restore for very last epilogue.
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let start = epilogue.start as usize;
|
||||
let offset = context.insts_layout[start];
|
||||
vec![
|
||||
(offset, UnwindCode::RememberState),
|
||||
// TODO epilogue instructions
|
||||
(end_offset, UnwindCode::RestoreState),
|
||||
]
|
||||
})
|
||||
.collect();
|
||||
|
||||
let prologue_size = context.insts_layout[context.prologue.end as usize];
|
||||
Ok(Some(input::UnwindInfo {
|
||||
prologue_size,
|
||||
prologue_unwind_codes: codes,
|
||||
epilogues_unwind_codes,
|
||||
function_size: context.len,
|
||||
word_size: WORD_SIZE,
|
||||
initial_sp_offset: WORD_SIZE,
|
||||
}))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
//! Unwind information for System V ABI (x86-64).
|
||||
|
||||
use crate::isa::unwind::input;
|
||||
use crate::isa::unwind::systemv::{RegisterMappingError, UnwindInfo};
|
||||
use crate::result::CodegenResult;
|
||||
use gimli::{write::CommonInformationEntry, Encoding, Format, Register, X86_64};
|
||||
use regalloc::{Reg, RegClass};
|
||||
|
||||
/// Creates a new x86-64 common information entry (CIE).
|
||||
pub fn create_cie() -> CommonInformationEntry {
|
||||
use gimli::write::CallFrameInstruction;
|
||||
|
||||
let mut entry = CommonInformationEntry::new(
|
||||
Encoding {
|
||||
address_size: 8,
|
||||
format: Format::Dwarf32,
|
||||
version: 1,
|
||||
},
|
||||
1, // Code alignment factor
|
||||
-8, // Data alignment factor
|
||||
X86_64::RA,
|
||||
);
|
||||
|
||||
// Every frame will start with the call frame address (CFA) at RSP+8
|
||||
// It is +8 to account for the push of the return address by the call instruction
|
||||
entry.add_instruction(CallFrameInstruction::Cfa(X86_64::RSP, 8));
|
||||
|
||||
// Every frame will start with the return address at RSP (CFA-8 = RSP+8-8 = RSP)
|
||||
entry.add_instruction(CallFrameInstruction::Offset(X86_64::RA, -8));
|
||||
|
||||
entry
|
||||
}
|
||||
|
||||
/// Map Cranelift registers to their corresponding Gimli registers.
|
||||
pub fn map_reg(reg: Reg) -> Result<Register, RegisterMappingError> {
|
||||
// Mapping from https://github.com/bytecodealliance/cranelift/pull/902 by @iximeow
|
||||
const X86_GP_REG_MAP: [gimli::Register; 16] = [
|
||||
X86_64::RAX,
|
||||
X86_64::RCX,
|
||||
X86_64::RDX,
|
||||
X86_64::RBX,
|
||||
X86_64::RSP,
|
||||
X86_64::RBP,
|
||||
X86_64::RSI,
|
||||
X86_64::RDI,
|
||||
X86_64::R8,
|
||||
X86_64::R9,
|
||||
X86_64::R10,
|
||||
X86_64::R11,
|
||||
X86_64::R12,
|
||||
X86_64::R13,
|
||||
X86_64::R14,
|
||||
X86_64::R15,
|
||||
];
|
||||
const X86_XMM_REG_MAP: [gimli::Register; 16] = [
|
||||
X86_64::XMM0,
|
||||
X86_64::XMM1,
|
||||
X86_64::XMM2,
|
||||
X86_64::XMM3,
|
||||
X86_64::XMM4,
|
||||
X86_64::XMM5,
|
||||
X86_64::XMM6,
|
||||
X86_64::XMM7,
|
||||
X86_64::XMM8,
|
||||
X86_64::XMM9,
|
||||
X86_64::XMM10,
|
||||
X86_64::XMM11,
|
||||
X86_64::XMM12,
|
||||
X86_64::XMM13,
|
||||
X86_64::XMM14,
|
||||
X86_64::XMM15,
|
||||
];
|
||||
|
||||
match reg.get_class() {
|
||||
RegClass::I64 => {
|
||||
// x86 GP registers have a weird mapping to DWARF registers, so we use a
|
||||
// lookup table.
|
||||
Ok(X86_GP_REG_MAP[reg.get_hw_encoding() as usize])
|
||||
}
|
||||
RegClass::V128 => Ok(X86_XMM_REG_MAP[reg.get_hw_encoding() as usize]),
|
||||
_ => Err(RegisterMappingError::UnsupportedRegisterBank("class?")),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create_unwind_info(
|
||||
unwind: input::UnwindInfo<Reg>,
|
||||
) -> CodegenResult<Option<UnwindInfo>> {
|
||||
struct RegisterMapper;
|
||||
impl crate::isa::unwind::systemv::RegisterMapper<Reg> for RegisterMapper {
|
||||
fn map(&self, reg: Reg) -> Result<u16, RegisterMappingError> {
|
||||
Ok(map_reg(reg)?.0)
|
||||
}
|
||||
fn sp(&self) -> u16 {
|
||||
X86_64::RSP.0
|
||||
}
|
||||
}
|
||||
let map = RegisterMapper;
|
||||
|
||||
Ok(Some(UnwindInfo::build(unwind, &map)?))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::cursor::{Cursor, FuncCursor};
|
||||
use crate::ir::{
|
||||
types, AbiParam, ExternalName, Function, InstBuilder, Signature, StackSlotData,
|
||||
StackSlotKind,
|
||||
};
|
||||
use crate::isa::{lookup, CallConv};
|
||||
use crate::settings::{builder, Flags};
|
||||
use crate::Context;
|
||||
use gimli::write::Address;
|
||||
use std::str::FromStr;
|
||||
use target_lexicon::triple;
|
||||
|
||||
#[test]
|
||||
fn test_simple_func() {
|
||||
let isa = lookup(triple!("x86_64"))
|
||||
.expect("expect x86 ISA")
|
||||
.finish(Flags::new(builder()));
|
||||
|
||||
let mut context = Context::for_function(create_function(
|
||||
CallConv::SystemV,
|
||||
Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 64)),
|
||||
));
|
||||
|
||||
context.compile(&*isa).expect("expected compilation");
|
||||
|
||||
let fde = match context
|
||||
.create_unwind_info(isa.as_ref())
|
||||
.expect("can create unwind info")
|
||||
{
|
||||
Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
|
||||
info.to_fde(Address::Constant(1234))
|
||||
}
|
||||
_ => panic!("expected unwind information"),
|
||||
};
|
||||
|
||||
assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(1234), length: 13, lsda: None, instructions: [(1, CfaOffset(16)), (1, Offset(Register(6), -16)), (4, CfaRegister(Register(6)))] }");
|
||||
}
|
||||
|
||||
fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function {
|
||||
let mut func =
|
||||
Function::with_name_signature(ExternalName::user(0, 0), Signature::new(call_conv));
|
||||
|
||||
let block0 = func.dfg.make_block();
|
||||
let mut pos = FuncCursor::new(&mut func);
|
||||
pos.insert_block(block0);
|
||||
pos.ins().return_(&[]);
|
||||
|
||||
if let Some(stack_slot) = stack_slot {
|
||||
func.stack_slots.push(stack_slot);
|
||||
}
|
||||
|
||||
func
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_return_func() {
|
||||
let isa = lookup(triple!("x86_64"))
|
||||
.expect("expect x86 ISA")
|
||||
.finish(Flags::new(builder()));
|
||||
|
||||
let mut context = Context::for_function(create_multi_return_function(CallConv::SystemV));
|
||||
|
||||
context.compile(&*isa).expect("expected compilation");
|
||||
|
||||
let fde = match context
|
||||
.create_unwind_info(isa.as_ref())
|
||||
.expect("can create unwind info")
|
||||
{
|
||||
Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
|
||||
info.to_fde(Address::Constant(4321))
|
||||
}
|
||||
_ => panic!("expected unwind information"),
|
||||
};
|
||||
|
||||
assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(4321), length: 23, lsda: None, instructions: [(1, CfaOffset(16)), (1, Offset(Register(6), -16)), (4, CfaRegister(Register(6))), (16, RememberState), (18, RestoreState)] }");
|
||||
}
|
||||
|
||||
fn create_multi_return_function(call_conv: CallConv) -> Function {
|
||||
let mut sig = Signature::new(call_conv);
|
||||
sig.params.push(AbiParam::new(types::I32));
|
||||
let mut func = Function::with_name_signature(ExternalName::user(0, 0), sig);
|
||||
|
||||
let block0 = func.dfg.make_block();
|
||||
let v0 = func.dfg.append_block_param(block0, types::I32);
|
||||
let block1 = func.dfg.make_block();
|
||||
let block2 = func.dfg.make_block();
|
||||
|
||||
let mut pos = FuncCursor::new(&mut func);
|
||||
pos.insert_block(block0);
|
||||
pos.ins().brnz(v0, block2, &[]);
|
||||
pos.ins().jump(block1, &[]);
|
||||
|
||||
pos.insert_block(block1);
|
||||
pos.ins().return_(&[]);
|
||||
|
||||
pos.insert_block(block2);
|
||||
pos.ins().return_(&[]);
|
||||
|
||||
func
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,16 +1,16 @@
|
|||
//! X86_64-bit Instruction Set Architecture.
|
||||
|
||||
use self::inst::EmitInfo;
|
||||
|
||||
use super::TargetIsa;
|
||||
use crate::ir::{condcodes::IntCC, Function};
|
||||
use crate::isa::x64::{inst::regs::create_reg_universe_systemv, settings as x64_settings};
|
||||
use crate::isa::Builder as IsaBuilder;
|
||||
use crate::machinst::{
|
||||
compile, pretty_print::ShowWithRRU, MachBackend, MachCompileResult, TargetIsaAdapter, VCode,
|
||||
};
|
||||
use crate::machinst::{compile, MachBackend, MachCompileResult, TargetIsaAdapter, VCode};
|
||||
use crate::result::CodegenResult;
|
||||
use crate::settings::{self as shared_settings, Flags};
|
||||
use alloc::boxed::Box;
|
||||
use regalloc::RealRegUniverse;
|
||||
use regalloc::{PrettyPrint, RealRegUniverse};
|
||||
use target_lexicon::Triple;
|
||||
|
||||
mod abi;
|
||||
|
@ -22,7 +22,7 @@ mod settings;
|
|||
pub(crate) struct X64Backend {
|
||||
triple: Triple,
|
||||
flags: Flags,
|
||||
_x64_flags: x64_settings::Flags,
|
||||
x64_flags: x64_settings::Flags,
|
||||
reg_universe: RealRegUniverse,
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ impl X64Backend {
|
|||
Self {
|
||||
triple,
|
||||
flags,
|
||||
_x64_flags: x64_flags,
|
||||
x64_flags,
|
||||
reg_universe,
|
||||
}
|
||||
}
|
||||
|
@ -41,8 +41,9 @@ impl X64Backend {
|
|||
fn compile_vcode(&self, func: &Function, flags: Flags) -> CodegenResult<VCode<inst::Inst>> {
|
||||
// This performs lowering to VCode, register-allocates the code, computes
|
||||
// block layout and finalizes branches. The result is ready for binary emission.
|
||||
let emit_info = EmitInfo::new(flags.clone(), self.x64_flags.clone());
|
||||
let abi = Box::new(abi::X64ABICallee::new(&func, flags)?);
|
||||
compile::compile::<Self>(&func, self, abi)
|
||||
compile::compile::<Self>(&func, self, abi, emit_info)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,9 +55,11 @@ impl MachBackend for X64Backend {
|
|||
) -> CodegenResult<MachCompileResult> {
|
||||
let flags = self.flags();
|
||||
let vcode = self.compile_vcode(func, flags.clone())?;
|
||||
|
||||
let buffer = vcode.emit();
|
||||
let buffer = buffer.finish();
|
||||
let frame_size = vcode.frame_size();
|
||||
let unwind_info = vcode.unwind_info()?;
|
||||
|
||||
let disasm = if want_disasm {
|
||||
Some(vcode.show_rru(Some(&create_reg_universe_systemv(flags))))
|
||||
|
@ -68,6 +71,7 @@ impl MachBackend for X64Backend {
|
|||
buffer,
|
||||
frame_size,
|
||||
disasm,
|
||||
unwind_info,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -98,6 +102,31 @@ impl MachBackend for X64Backend {
|
|||
// underflow of a subtract (carry is borrow for subtract).
|
||||
IntCC::UnsignedGreaterThanOrEqual
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
fn emit_unwind_info(
|
||||
&self,
|
||||
result: &MachCompileResult,
|
||||
kind: crate::machinst::UnwindInfoKind,
|
||||
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
|
||||
use crate::isa::unwind::UnwindInfo;
|
||||
use crate::machinst::UnwindInfoKind;
|
||||
Ok(match (result.unwind_info.as_ref(), kind) {
|
||||
(Some(info), UnwindInfoKind::SystemV) => {
|
||||
inst::unwind::systemv::create_unwind_info(info.clone())?.map(UnwindInfo::SystemV)
|
||||
}
|
||||
(Some(_info), UnwindInfoKind::Windows) => {
|
||||
//TODO inst::unwind::winx64::create_unwind_info(info.clone())?.map(|u| UnwindInfo::WindowsX64(u))
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
|
||||
Some(inst::unwind::systemv::create_cie())
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `isa::Builder`.
|
||||
|
|
|
@ -993,7 +993,7 @@ fn insert_common_epilogues(
|
|||
pos.goto_last_inst(block);
|
||||
if let Some(inst) = pos.current_inst() {
|
||||
if pos.func.dfg[inst].opcode().is_return() {
|
||||
insert_common_epilogue(inst, stack_size, pos, reg_type, csrs, sp_arg_index);
|
||||
insert_common_epilogue(inst, block, stack_size, pos, reg_type, csrs, sp_arg_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1003,6 +1003,7 @@ fn insert_common_epilogues(
|
|||
/// This is used by common calling conventions such as System V.
|
||||
fn insert_common_epilogue(
|
||||
inst: ir::Inst,
|
||||
block: ir::Block,
|
||||
stack_size: i64,
|
||||
pos: &mut EncCursor,
|
||||
reg_type: ir::types::Type,
|
||||
|
@ -1062,12 +1063,13 @@ fn insert_common_epilogue(
|
|||
assert!(csrs.iter(FPR).len() == 0);
|
||||
}
|
||||
|
||||
pos.func.epilogues_start.push(
|
||||
pos.func.epilogues_start.push((
|
||||
first_fpr_load
|
||||
.or(sp_adjust_inst)
|
||||
.or(first_csr_pop_inst)
|
||||
.unwrap_or(fp_pop_inst),
|
||||
);
|
||||
block,
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
|
@ -1081,8 +1083,7 @@ pub fn create_unwind_info(
|
|||
// In the future, we should be omitting frame pointer as an optimization, so this will change
|
||||
Ok(match func.signature.call_conv {
|
||||
CallConv::Fast | CallConv::Cold | CallConv::SystemV => {
|
||||
super::unwind::systemv::create_unwind_info(func, isa, Some(RU::rbp.into()))?
|
||||
.map(|u| UnwindInfo::SystemV(u))
|
||||
super::unwind::systemv::create_unwind_info(func, isa)?.map(|u| UnwindInfo::SystemV(u))
|
||||
}
|
||||
CallConv::WindowsFastcall => {
|
||||
super::unwind::winx64::create_unwind_info(func, isa)?.map(|u| UnwindInfo::WindowsX64(u))
|
||||
|
|
|
@ -1892,3 +1892,31 @@ fn expand_tls_value(
|
|||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_load_splat(
|
||||
inst: ir::Inst,
|
||||
func: &mut ir::Function,
|
||||
_cfg: &mut ControlFlowGraph,
|
||||
_isa: &dyn TargetIsa,
|
||||
) {
|
||||
let mut pos = FuncCursor::new(func).at_inst(inst);
|
||||
|
||||
pos.use_srcloc(inst);
|
||||
|
||||
let (ptr, offset, flags) = match pos.func.dfg[inst] {
|
||||
ir::InstructionData::Load {
|
||||
opcode: ir::Opcode::LoadSplat,
|
||||
arg,
|
||||
offset,
|
||||
flags,
|
||||
} => (arg, offset, flags),
|
||||
_ => panic!(
|
||||
"Expected load_splat: {}",
|
||||
pos.func.dfg.display_inst(inst, None)
|
||||
),
|
||||
};
|
||||
let ty = pos.func.dfg.ctrl_typevar(inst);
|
||||
let load = pos.ins().load(ty.lane_type(), flags, ptr, offset);
|
||||
|
||||
pos.func.dfg.replace(inst).splat(ty, load);
|
||||
}
|
||||
|
|
|
@ -2,3 +2,534 @@
|
|||
|
||||
pub mod systemv;
|
||||
pub mod winx64;
|
||||
|
||||
use crate::ir::{Function, InstructionData, Opcode, ValueLoc};
|
||||
use crate::isa::x86::registers::{FPR, RU};
|
||||
use crate::isa::{RegUnit, TargetIsa};
|
||||
use crate::result::CodegenResult;
|
||||
use alloc::vec::Vec;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::isa::unwind::input::{UnwindCode, UnwindInfo};
|
||||
|
||||
pub(crate) fn create_unwind_info(
|
||||
func: &Function,
|
||||
isa: &dyn TargetIsa,
|
||||
) -> CodegenResult<Option<UnwindInfo<RegUnit>>> {
|
||||
// Find last block based on max offset.
|
||||
let last_block = func
|
||||
.layout
|
||||
.blocks()
|
||||
.max_by_key(|b| func.offsets[*b])
|
||||
.expect("at least a block");
|
||||
// Find last instruction offset + size, and make it function size.
|
||||
let function_size = func
|
||||
.inst_offsets(last_block, &isa.encoding_info())
|
||||
.fold(0, |_, (offset, _, size)| offset + size);
|
||||
|
||||
let entry_block = func.layout.entry_block().expect("missing entry block");
|
||||
let prologue_end = func.prologue_end.unwrap();
|
||||
let epilogues_start = func
|
||||
.epilogues_start
|
||||
.iter()
|
||||
.map(|(i, b)| (*b, *i))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let word_size = isa.pointer_bytes();
|
||||
|
||||
let mut stack_size = None;
|
||||
let mut prologue_size = 0;
|
||||
let mut prologue_unwind_codes = Vec::new();
|
||||
let mut epilogues_unwind_codes = Vec::new();
|
||||
let mut frame_register: Option<RegUnit> = None;
|
||||
|
||||
// Process only entry block and blocks with epilogues.
|
||||
let mut blocks = func
|
||||
.epilogues_start
|
||||
.iter()
|
||||
.map(|(_, b)| *b)
|
||||
.collect::<Vec<_>>();
|
||||
if !blocks.contains(&entry_block) {
|
||||
blocks.push(entry_block);
|
||||
}
|
||||
blocks.sort_by_key(|b| func.offsets[*b]);
|
||||
|
||||
for block in blocks.iter() {
|
||||
let mut in_prologue = block == &entry_block;
|
||||
let mut in_epilogue = false;
|
||||
let mut epilogue_pop_offsets = Vec::new();
|
||||
|
||||
let epilogue_start = epilogues_start.get(block);
|
||||
let is_last_block = block == &last_block;
|
||||
|
||||
for (offset, inst, size) in func.inst_offsets(*block, &isa.encoding_info()) {
|
||||
let offset = offset + size;
|
||||
|
||||
let unwind_codes;
|
||||
if in_prologue {
|
||||
// Check for prologue end (inclusive)
|
||||
if prologue_end == inst {
|
||||
in_prologue = false;
|
||||
}
|
||||
prologue_size += size;
|
||||
unwind_codes = &mut prologue_unwind_codes;
|
||||
} else if !in_epilogue && epilogue_start == Some(&inst) {
|
||||
// Now in an epilogue, emit a remember state instruction if not last block
|
||||
in_epilogue = true;
|
||||
|
||||
epilogues_unwind_codes.push(Vec::new());
|
||||
unwind_codes = epilogues_unwind_codes.last_mut().unwrap();
|
||||
|
||||
if !is_last_block {
|
||||
unwind_codes.push((offset, UnwindCode::RememberState));
|
||||
}
|
||||
} else if in_epilogue {
|
||||
unwind_codes = epilogues_unwind_codes.last_mut().unwrap();
|
||||
} else {
|
||||
// Ignore normal instructions
|
||||
continue;
|
||||
}
|
||||
|
||||
match func.dfg[inst] {
|
||||
InstructionData::Unary { opcode, arg } => {
|
||||
match opcode {
|
||||
Opcode::X86Push => {
|
||||
let reg = func.locations[arg].unwrap_reg();
|
||||
unwind_codes.push((
|
||||
offset,
|
||||
UnwindCode::StackAlloc {
|
||||
size: word_size.into(),
|
||||
},
|
||||
));
|
||||
unwind_codes.push((
|
||||
offset,
|
||||
UnwindCode::SaveRegister {
|
||||
reg,
|
||||
stack_offset: 0,
|
||||
},
|
||||
));
|
||||
}
|
||||
Opcode::AdjustSpDown => {
|
||||
let stack_size =
|
||||
stack_size.expect("expected a previous stack size instruction");
|
||||
|
||||
// This is used when calling a stack check function
|
||||
// We need to track the assignment to RAX which has the size of the stack
|
||||
unwind_codes
|
||||
.push((offset, UnwindCode::StackAlloc { size: stack_size }));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
InstructionData::UnaryImm { opcode, imm } => {
|
||||
match opcode {
|
||||
Opcode::Iconst => {
|
||||
let imm: i64 = imm.into();
|
||||
assert!(imm <= core::u32::MAX as i64);
|
||||
assert!(stack_size.is_none());
|
||||
|
||||
// This instruction should only appear in a prologue to pass an
|
||||
// argument of the stack size to a stack check function.
|
||||
// Record the stack size so we know what it is when we encounter the adjustment
|
||||
// instruction (which will adjust via the register assigned to this instruction).
|
||||
stack_size = Some(imm as u32);
|
||||
}
|
||||
Opcode::AdjustSpDownImm => {
|
||||
let imm: i64 = imm.into();
|
||||
assert!(imm <= core::u32::MAX as i64);
|
||||
|
||||
stack_size = Some(imm as u32);
|
||||
|
||||
unwind_codes
|
||||
.push((offset, UnwindCode::StackAlloc { size: imm as u32 }));
|
||||
}
|
||||
Opcode::AdjustSpUpImm => {
|
||||
let imm: i64 = imm.into();
|
||||
assert!(imm <= core::u32::MAX as i64);
|
||||
|
||||
stack_size = Some(imm as u32);
|
||||
|
||||
unwind_codes
|
||||
.push((offset, UnwindCode::StackDealloc { size: imm as u32 }));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
InstructionData::Store {
|
||||
opcode: Opcode::Store,
|
||||
args: [arg1, arg2],
|
||||
offset: stack_offset,
|
||||
..
|
||||
} => {
|
||||
if let (ValueLoc::Reg(src), ValueLoc::Reg(dst)) =
|
||||
(func.locations[arg1], func.locations[arg2])
|
||||
{
|
||||
// If this is a save of an FPR, record an unwind operation
|
||||
// Note: the stack_offset here is relative to an adjusted SP
|
||||
if dst == (RU::rsp as RegUnit) && FPR.contains(src) {
|
||||
let stack_offset: i32 = stack_offset.into();
|
||||
unwind_codes.push((
|
||||
offset,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: src,
|
||||
stack_offset: stack_offset as u32,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
InstructionData::CopySpecial { src, dst, .. } if frame_register.is_none() => {
|
||||
// Check for change in CFA register (RSP is always the starting CFA)
|
||||
if src == (RU::rsp as RegUnit) {
|
||||
unwind_codes.push((offset, UnwindCode::SetFramePointer { reg: dst }));
|
||||
frame_register = Some(dst);
|
||||
}
|
||||
}
|
||||
InstructionData::NullAry { opcode } => match opcode {
|
||||
Opcode::X86Pop => {
|
||||
epilogue_pop_offsets.push(offset);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
InstructionData::MultiAry { opcode, .. } if in_epilogue => match opcode {
|
||||
Opcode::Return => {
|
||||
let args = func.dfg.inst_args(inst);
|
||||
for (i, arg) in args.iter().rev().enumerate() {
|
||||
// Only walk back the args for the pop instructions encountered
|
||||
if i >= epilogue_pop_offsets.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
let offset = epilogue_pop_offsets[i];
|
||||
|
||||
let reg = func.locations[*arg].unwrap_reg();
|
||||
unwind_codes.push((offset, UnwindCode::RestoreRegister { reg }));
|
||||
unwind_codes.push((
|
||||
offset,
|
||||
UnwindCode::StackDealloc {
|
||||
size: word_size.into(),
|
||||
},
|
||||
));
|
||||
|
||||
if Some(reg) == frame_register {
|
||||
unwind_codes.push((offset, UnwindCode::RestoreFramePointer));
|
||||
// Keep frame_register assigned for next epilogue.
|
||||
}
|
||||
}
|
||||
epilogue_pop_offsets.clear();
|
||||
|
||||
// TODO ensure unwind codes sorted by offsets ?
|
||||
|
||||
if !is_last_block {
|
||||
unwind_codes.push((offset, UnwindCode::RestoreState));
|
||||
}
|
||||
|
||||
in_epilogue = false;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(UnwindInfo {
|
||||
prologue_size,
|
||||
prologue_unwind_codes,
|
||||
epilogues_unwind_codes,
|
||||
function_size,
|
||||
word_size,
|
||||
initial_sp_offset: word_size,
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::cursor::{Cursor, FuncCursor};
|
||||
use crate::ir::{
|
||||
types, AbiParam, ExternalName, InstBuilder, Signature, StackSlotData, StackSlotKind,
|
||||
};
|
||||
use crate::isa::{lookup, CallConv};
|
||||
use crate::settings::{builder, Flags};
|
||||
use crate::Context;
|
||||
use std::str::FromStr;
|
||||
use target_lexicon::triple;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(feature = "x64", should_panic)] // TODO #2079
|
||||
fn test_small_alloc() {
|
||||
let isa = lookup(triple!("x86_64"))
|
||||
.expect("expect x86 ISA")
|
||||
.finish(Flags::new(builder()));
|
||||
|
||||
let mut context = Context::for_function(create_function(
|
||||
CallConv::WindowsFastcall,
|
||||
Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 64)),
|
||||
));
|
||||
|
||||
context.compile(&*isa).expect("expected compilation");
|
||||
|
||||
let unwind = create_unwind_info(&context.func, &*isa)
|
||||
.expect("can create unwind info")
|
||||
.expect("expected unwind info");
|
||||
|
||||
assert_eq!(
|
||||
unwind,
|
||||
UnwindInfo {
|
||||
prologue_size: 9,
|
||||
prologue_unwind_codes: vec![
|
||||
(2, UnwindCode::StackAlloc { size: 8 }),
|
||||
(
|
||||
2,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: RU::rbp.into(),
|
||||
stack_offset: 0,
|
||||
}
|
||||
),
|
||||
(
|
||||
5,
|
||||
UnwindCode::SetFramePointer {
|
||||
reg: RU::rbp.into(),
|
||||
}
|
||||
),
|
||||
(9, UnwindCode::StackAlloc { size: 64 })
|
||||
],
|
||||
epilogues_unwind_codes: vec![vec![
|
||||
(13, UnwindCode::StackDealloc { size: 64 }),
|
||||
(
|
||||
15,
|
||||
UnwindCode::RestoreRegister {
|
||||
reg: RU::rbp.into()
|
||||
}
|
||||
),
|
||||
(15, UnwindCode::StackDealloc { size: 8 }),
|
||||
(15, UnwindCode::RestoreFramePointer)
|
||||
]],
|
||||
function_size: 16,
|
||||
word_size: 8,
|
||||
initial_sp_offset: 8,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(feature = "x64", should_panic)] // TODO #2079
|
||||
fn test_medium_alloc() {
|
||||
let isa = lookup(triple!("x86_64"))
|
||||
.expect("expect x86 ISA")
|
||||
.finish(Flags::new(builder()));
|
||||
|
||||
let mut context = Context::for_function(create_function(
|
||||
CallConv::WindowsFastcall,
|
||||
Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 10000)),
|
||||
));
|
||||
|
||||
context.compile(&*isa).expect("expected compilation");
|
||||
|
||||
let unwind = create_unwind_info(&context.func, &*isa)
|
||||
.expect("can create unwind info")
|
||||
.expect("expected unwind info");
|
||||
|
||||
assert_eq!(
|
||||
unwind,
|
||||
UnwindInfo {
|
||||
prologue_size: 27,
|
||||
prologue_unwind_codes: vec![
|
||||
(2, UnwindCode::StackAlloc { size: 8 }),
|
||||
(
|
||||
2,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: RU::rbp.into(),
|
||||
stack_offset: 0,
|
||||
}
|
||||
),
|
||||
(
|
||||
5,
|
||||
UnwindCode::SetFramePointer {
|
||||
reg: RU::rbp.into(),
|
||||
}
|
||||
),
|
||||
(27, UnwindCode::StackAlloc { size: 10000 })
|
||||
],
|
||||
epilogues_unwind_codes: vec![vec![
|
||||
(34, UnwindCode::StackDealloc { size: 10000 }),
|
||||
(
|
||||
36,
|
||||
UnwindCode::RestoreRegister {
|
||||
reg: RU::rbp.into()
|
||||
}
|
||||
),
|
||||
(36, UnwindCode::StackDealloc { size: 8 }),
|
||||
(36, UnwindCode::RestoreFramePointer)
|
||||
]],
|
||||
function_size: 37,
|
||||
word_size: 8,
|
||||
initial_sp_offset: 8,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(feature = "x64", should_panic)] // TODO #2079
|
||||
fn test_large_alloc() {
|
||||
let isa = lookup(triple!("x86_64"))
|
||||
.expect("expect x86 ISA")
|
||||
.finish(Flags::new(builder()));
|
||||
|
||||
let mut context = Context::for_function(create_function(
|
||||
CallConv::WindowsFastcall,
|
||||
Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 1000000)),
|
||||
));
|
||||
|
||||
context.compile(&*isa).expect("expected compilation");
|
||||
|
||||
let unwind = create_unwind_info(&context.func, &*isa)
|
||||
.expect("can create unwind info")
|
||||
.expect("expected unwind info");
|
||||
|
||||
assert_eq!(
|
||||
unwind,
|
||||
UnwindInfo {
|
||||
prologue_size: 27,
|
||||
prologue_unwind_codes: vec![
|
||||
(2, UnwindCode::StackAlloc { size: 8 }),
|
||||
(
|
||||
2,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: RU::rbp.into(),
|
||||
stack_offset: 0,
|
||||
}
|
||||
),
|
||||
(
|
||||
5,
|
||||
UnwindCode::SetFramePointer {
|
||||
reg: RU::rbp.into(),
|
||||
}
|
||||
),
|
||||
(27, UnwindCode::StackAlloc { size: 1000000 })
|
||||
],
|
||||
epilogues_unwind_codes: vec![vec![
|
||||
(34, UnwindCode::StackDealloc { size: 1000000 }),
|
||||
(
|
||||
36,
|
||||
UnwindCode::RestoreRegister {
|
||||
reg: RU::rbp.into()
|
||||
}
|
||||
),
|
||||
(36, UnwindCode::StackDealloc { size: 8 }),
|
||||
(36, UnwindCode::RestoreFramePointer)
|
||||
]],
|
||||
function_size: 37,
|
||||
word_size: 8,
|
||||
initial_sp_offset: 8,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function {
|
||||
let mut func =
|
||||
Function::with_name_signature(ExternalName::user(0, 0), Signature::new(call_conv));
|
||||
|
||||
let block0 = func.dfg.make_block();
|
||||
let mut pos = FuncCursor::new(&mut func);
|
||||
pos.insert_block(block0);
|
||||
pos.ins().return_(&[]);
|
||||
|
||||
if let Some(stack_slot) = stack_slot {
|
||||
func.stack_slots.push(stack_slot);
|
||||
}
|
||||
|
||||
func
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(feature = "x64", should_panic)] // TODO #2079
|
||||
fn test_multi_return_func() {
|
||||
let isa = lookup(triple!("x86_64"))
|
||||
.expect("expect x86 ISA")
|
||||
.finish(Flags::new(builder()));
|
||||
|
||||
let mut context = Context::for_function(create_multi_return_function(CallConv::SystemV));
|
||||
|
||||
context.compile(&*isa).expect("expected compilation");
|
||||
|
||||
let unwind = create_unwind_info(&context.func, &*isa)
|
||||
.expect("can create unwind info")
|
||||
.expect("expected unwind info");
|
||||
|
||||
assert_eq!(
|
||||
unwind,
|
||||
UnwindInfo {
|
||||
prologue_size: 5,
|
||||
prologue_unwind_codes: vec![
|
||||
(2, UnwindCode::StackAlloc { size: 8 }),
|
||||
(
|
||||
2,
|
||||
UnwindCode::SaveRegister {
|
||||
reg: RU::rbp.into(),
|
||||
stack_offset: 0,
|
||||
}
|
||||
),
|
||||
(
|
||||
5,
|
||||
UnwindCode::SetFramePointer {
|
||||
reg: RU::rbp.into()
|
||||
}
|
||||
)
|
||||
],
|
||||
epilogues_unwind_codes: vec![
|
||||
vec![
|
||||
(12, UnwindCode::RememberState),
|
||||
(
|
||||
12,
|
||||
UnwindCode::RestoreRegister {
|
||||
reg: RU::rbp.into()
|
||||
}
|
||||
),
|
||||
(12, UnwindCode::StackDealloc { size: 8 }),
|
||||
(12, UnwindCode::RestoreFramePointer),
|
||||
(13, UnwindCode::RestoreState)
|
||||
],
|
||||
vec![
|
||||
(
|
||||
15,
|
||||
UnwindCode::RestoreRegister {
|
||||
reg: RU::rbp.into()
|
||||
}
|
||||
),
|
||||
(15, UnwindCode::StackDealloc { size: 8 }),
|
||||
(15, UnwindCode::RestoreFramePointer)
|
||||
]
|
||||
],
|
||||
function_size: 16,
|
||||
word_size: 8,
|
||||
initial_sp_offset: 8,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
fn create_multi_return_function(call_conv: CallConv) -> Function {
|
||||
let mut sig = Signature::new(call_conv);
|
||||
sig.params.push(AbiParam::new(types::I32));
|
||||
let mut func = Function::with_name_signature(ExternalName::user(0, 0), sig);
|
||||
|
||||
let block0 = func.dfg.make_block();
|
||||
let v0 = func.dfg.append_block_param(block0, types::I32);
|
||||
let block1 = func.dfg.make_block();
|
||||
let block2 = func.dfg.make_block();
|
||||
|
||||
let mut pos = FuncCursor::new(&mut func);
|
||||
pos.insert_block(block0);
|
||||
pos.ins().brnz(v0, block2, &[]);
|
||||
pos.ins().jump(block1, &[]);
|
||||
|
||||
pos.insert_block(block1);
|
||||
pos.ins().return_(&[]);
|
||||
|
||||
pos.insert_block(block2);
|
||||
pos.ins().return_(&[]);
|
||||
|
||||
func
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
//! Unwind information for System V ABI (x86-64).
|
||||
|
||||
use crate::ir::{Function, Inst, InstructionData, Opcode, Value};
|
||||
use crate::ir::Function;
|
||||
use crate::isa::{
|
||||
unwind::systemv::{CallFrameInstruction, RegisterMappingError, UnwindInfo},
|
||||
x86::registers::RU,
|
||||
unwind::systemv::{RegisterMappingError, UnwindInfo},
|
||||
CallConv, RegUnit, TargetIsa,
|
||||
};
|
||||
use crate::result::{CodegenError, CodegenResult};
|
||||
use alloc::vec::Vec;
|
||||
use crate::result::CodegenResult;
|
||||
use gimli::{write::CommonInformationEntry, Encoding, Format, Register, X86_64};
|
||||
|
||||
/// Creates a new x86-64 common information entry (CIE).
|
||||
|
@ -94,195 +92,9 @@ pub fn map_reg(isa: &dyn TargetIsa, reg: RegUnit) -> Result<Register, RegisterMa
|
|||
}
|
||||
}
|
||||
|
||||
struct InstructionBuilder<'a> {
|
||||
func: &'a Function,
|
||||
isa: &'a dyn TargetIsa,
|
||||
cfa_offset: i32,
|
||||
frame_register: Option<RegUnit>,
|
||||
instructions: Vec<(u32, CallFrameInstruction)>,
|
||||
stack_size: Option<i32>,
|
||||
epilogue_pop_offsets: Vec<u32>,
|
||||
}
|
||||
|
||||
impl<'a> InstructionBuilder<'a> {
|
||||
fn new(func: &'a Function, isa: &'a dyn TargetIsa, frame_register: Option<RegUnit>) -> Self {
|
||||
Self {
|
||||
func,
|
||||
isa,
|
||||
cfa_offset: 8, // CFA offset starts at 8 to account to return address on stack
|
||||
frame_register,
|
||||
instructions: Vec::new(),
|
||||
stack_size: None,
|
||||
epilogue_pop_offsets: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn push_reg(&mut self, offset: u32, arg: Value) -> Result<(), RegisterMappingError> {
|
||||
self.cfa_offset += 8;
|
||||
|
||||
let reg = self.func.locations[arg].unwrap_reg();
|
||||
|
||||
// Update the CFA if this is the save of the frame pointer register or if a frame pointer isn't being used
|
||||
// When using a frame pointer, we only need to update the CFA to account for the push of the frame pointer itself
|
||||
if match self.frame_register {
|
||||
Some(fp) => reg == fp,
|
||||
None => true,
|
||||
} {
|
||||
self.instructions
|
||||
.push((offset, CallFrameInstruction::CfaOffset(self.cfa_offset)));
|
||||
}
|
||||
|
||||
// Pushes in the prologue are register saves, so record an offset of the save
|
||||
self.instructions.push((
|
||||
offset,
|
||||
CallFrameInstruction::Offset(map_reg(self.isa, reg)?.0, -self.cfa_offset),
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn adjust_sp_down(&mut self, offset: u32) {
|
||||
// Don't adjust the CFA if we're using a frame pointer
|
||||
if self.frame_register.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.cfa_offset += self
|
||||
.stack_size
|
||||
.expect("expected a previous stack size instruction");
|
||||
self.instructions
|
||||
.push((offset, CallFrameInstruction::CfaOffset(self.cfa_offset)));
|
||||
}
|
||||
|
||||
fn adjust_sp_down_imm(&mut self, offset: u32, imm: i64) {
|
||||
assert!(imm <= core::u32::MAX as i64);
|
||||
|
||||
// Don't adjust the CFA if we're using a frame pointer
|
||||
if self.frame_register.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.cfa_offset += imm as i32;
|
||||
self.instructions
|
||||
.push((offset, CallFrameInstruction::CfaOffset(self.cfa_offset)));
|
||||
}
|
||||
|
||||
fn adjust_sp_up_imm(&mut self, offset: u32, imm: i64) {
|
||||
assert!(imm <= core::u32::MAX as i64);
|
||||
|
||||
// Don't adjust the CFA if we're using a frame pointer
|
||||
if self.frame_register.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.cfa_offset -= imm as i32;
|
||||
self.instructions
|
||||
.push((offset, CallFrameInstruction::CfaOffset(self.cfa_offset)));
|
||||
}
|
||||
|
||||
fn move_reg(
|
||||
&mut self,
|
||||
offset: u32,
|
||||
src: RegUnit,
|
||||
dst: RegUnit,
|
||||
) -> Result<(), RegisterMappingError> {
|
||||
if let Some(fp) = self.frame_register {
|
||||
// Check for change in CFA register (RSP is always the starting CFA)
|
||||
if src == (RU::rsp as RegUnit) && dst == fp {
|
||||
self.instructions.push((
|
||||
offset,
|
||||
CallFrameInstruction::CfaRegister(map_reg(self.isa, dst)?.0),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prologue_imm_const(&mut self, imm: i64) {
|
||||
assert!(imm <= core::u32::MAX as i64);
|
||||
assert!(self.stack_size.is_none());
|
||||
|
||||
// This instruction should only appear in a prologue to pass an
|
||||
// argument of the stack size to a stack check function.
|
||||
// Record the stack size so we know what it is when we encounter the adjustment
|
||||
// instruction (which will adjust via the register assigned to this instruction).
|
||||
self.stack_size = Some(imm as i32);
|
||||
}
|
||||
|
||||
fn ret(&mut self, inst: Inst) -> Result<(), RegisterMappingError> {
|
||||
let args = self.func.dfg.inst_args(inst);
|
||||
|
||||
for (i, arg) in args.iter().rev().enumerate() {
|
||||
// Only walk back the args for the pop instructions encountered
|
||||
if i >= self.epilogue_pop_offsets.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
self.cfa_offset -= 8;
|
||||
let reg = self.func.locations[*arg].unwrap_reg();
|
||||
|
||||
// Update the CFA if this is the restore of the frame pointer register or if a frame pointer isn't being used
|
||||
match self.frame_register {
|
||||
Some(fp) => {
|
||||
if reg == fp {
|
||||
self.instructions.push((
|
||||
self.epilogue_pop_offsets[i],
|
||||
CallFrameInstruction::Cfa(
|
||||
map_reg(self.isa, RU::rsp as RegUnit)?.0,
|
||||
self.cfa_offset,
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.instructions.push((
|
||||
self.epilogue_pop_offsets[i],
|
||||
CallFrameInstruction::CfaOffset(self.cfa_offset),
|
||||
));
|
||||
|
||||
// Pops in the epilogue are register restores, so record a "same value" for the register
|
||||
// This isn't necessary when using a frame pointer as the CFA doesn't change for CSR restores
|
||||
self.instructions.push((
|
||||
self.epilogue_pop_offsets[i],
|
||||
CallFrameInstruction::SameValue(map_reg(self.isa, reg)?.0),
|
||||
));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
self.epilogue_pop_offsets.clear();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn insert_pop_offset(&mut self, offset: u32) {
|
||||
self.epilogue_pop_offsets.push(offset);
|
||||
}
|
||||
|
||||
fn remember_state(&mut self, offset: u32) {
|
||||
self.instructions
|
||||
.push((offset, CallFrameInstruction::RememberState));
|
||||
}
|
||||
|
||||
fn restore_state(&mut self, offset: u32) {
|
||||
self.instructions
|
||||
.push((offset, CallFrameInstruction::RestoreState));
|
||||
}
|
||||
|
||||
fn is_prologue_end(&self, inst: Inst) -> bool {
|
||||
self.func.prologue_end == Some(inst)
|
||||
}
|
||||
|
||||
fn is_epilogue_start(&self, inst: Inst) -> bool {
|
||||
self.func.epilogues_start.contains(&inst)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create_unwind_info(
|
||||
func: &Function,
|
||||
isa: &dyn TargetIsa,
|
||||
frame_register: Option<RegUnit>,
|
||||
) -> CodegenResult<Option<UnwindInfo>> {
|
||||
// Only System V-like calling conventions are supported
|
||||
match func.signature.call_conv {
|
||||
|
@ -294,92 +106,25 @@ pub(crate) fn create_unwind_info(
|
|||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut builder = InstructionBuilder::new(func, isa, frame_register);
|
||||
let mut in_prologue = true;
|
||||
let mut in_epilogue = false;
|
||||
let mut len = 0;
|
||||
let unwind = match super::create_unwind_info(func, isa)? {
|
||||
Some(u) => u,
|
||||
None => {
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
let mut blocks = func.layout.blocks().collect::<Vec<_>>();
|
||||
blocks.sort_by_key(|b| func.offsets[*b]);
|
||||
|
||||
for (i, block) in blocks.iter().enumerate() {
|
||||
for (offset, inst, size) in func.inst_offsets(*block, &isa.encoding_info()) {
|
||||
let offset = offset + size;
|
||||
assert!(len <= offset);
|
||||
len = offset;
|
||||
|
||||
let is_last_block = i == blocks.len() - 1;
|
||||
|
||||
if in_prologue {
|
||||
// Check for prologue end (inclusive)
|
||||
in_prologue = !builder.is_prologue_end(inst);
|
||||
} else if !in_epilogue && builder.is_epilogue_start(inst) {
|
||||
// Now in an epilogue, emit a remember state instruction if not last block
|
||||
in_epilogue = true;
|
||||
|
||||
if !is_last_block {
|
||||
builder.remember_state(offset);
|
||||
}
|
||||
} else if !in_epilogue {
|
||||
// Ignore normal instructions
|
||||
continue;
|
||||
}
|
||||
|
||||
match builder.func.dfg[inst] {
|
||||
InstructionData::Unary { opcode, arg } => match opcode {
|
||||
Opcode::X86Push => {
|
||||
builder
|
||||
.push_reg(offset, arg)
|
||||
.map_err(CodegenError::RegisterMappingError)?;
|
||||
}
|
||||
Opcode::AdjustSpDown => {
|
||||
builder.adjust_sp_down(offset);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
InstructionData::CopySpecial { src, dst, .. } => {
|
||||
builder
|
||||
.move_reg(offset, src, dst)
|
||||
.map_err(CodegenError::RegisterMappingError)?;
|
||||
}
|
||||
InstructionData::NullAry { opcode } => match opcode {
|
||||
Opcode::X86Pop => {
|
||||
builder.insert_pop_offset(offset);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
InstructionData::UnaryImm { opcode, imm } => match opcode {
|
||||
Opcode::Iconst => {
|
||||
builder.prologue_imm_const(imm.into());
|
||||
}
|
||||
Opcode::AdjustSpDownImm => {
|
||||
builder.adjust_sp_down_imm(offset, imm.into());
|
||||
}
|
||||
Opcode::AdjustSpUpImm => {
|
||||
builder.adjust_sp_up_imm(offset, imm.into());
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
InstructionData::MultiAry { opcode, .. } => match opcode {
|
||||
Opcode::Return => {
|
||||
builder
|
||||
.ret(inst)
|
||||
.map_err(CodegenError::RegisterMappingError)?;
|
||||
|
||||
if !is_last_block {
|
||||
builder.restore_state(offset);
|
||||
}
|
||||
|
||||
in_epilogue = false;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
};
|
||||
struct RegisterMapper<'a, 'b>(&'a (dyn TargetIsa + 'b));
|
||||
impl<'a, 'b> crate::isa::unwind::systemv::RegisterMapper<RegUnit> for RegisterMapper<'a, 'b> {
|
||||
fn map(&self, reg: RegUnit) -> Result<u16, RegisterMappingError> {
|
||||
Ok(map_reg(self.0, reg)?.0)
|
||||
}
|
||||
fn sp(&self) -> u16 {
|
||||
X86_64::RSP.0
|
||||
}
|
||||
}
|
||||
let map = RegisterMapper(isa);
|
||||
|
||||
Ok(Some(UnwindInfo::new(builder.instructions, len)))
|
||||
Ok(Some(UnwindInfo::build(unwind, &map)?))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -420,7 +165,7 @@ mod tests {
|
|||
_ => panic!("expected unwind information"),
|
||||
};
|
||||
|
||||
assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(1234), length: 16, lsda: None, instructions: [(2, CfaOffset(16)), (2, Offset(Register(6), -16)), (5, CfaRegister(Register(6))), (15, Cfa(Register(7), 8))] }");
|
||||
assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(1234), length: 16, lsda: None, instructions: [(2, CfaOffset(16)), (2, Offset(Register(6), -16)), (5, CfaRegister(Register(6))), (15, SameValue(Register(6))), (15, Cfa(Register(7), 8))] }");
|
||||
}
|
||||
|
||||
fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function {
|
||||
|
@ -460,7 +205,7 @@ mod tests {
|
|||
_ => panic!("expected unwind information"),
|
||||
};
|
||||
|
||||
assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(4321), length: 16, lsda: None, instructions: [(2, CfaOffset(16)), (2, Offset(Register(6), -16)), (5, CfaRegister(Register(6))), (12, RememberState), (12, Cfa(Register(7), 8)), (13, RestoreState), (15, Cfa(Register(7), 0))] }");
|
||||
assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(4321), length: 16, lsda: None, instructions: [(2, CfaOffset(16)), (2, Offset(Register(6), -16)), (5, CfaRegister(Register(6))), (12, RememberState), (12, SameValue(Register(6))), (12, Cfa(Register(7), 8)), (13, RestoreState), (15, SameValue(Register(6))), (15, Cfa(Register(7), 8))] }");
|
||||
}
|
||||
|
||||
fn create_multi_return_function(call_conv: CallConv) -> Function {
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
//! Unwind information for Windows x64 ABI.
|
||||
|
||||
use crate::ir::{Function, InstructionData, Opcode, ValueLoc};
|
||||
use crate::isa::x86::registers::{FPR, GPR, RU};
|
||||
use crate::isa::{
|
||||
unwind::winx64::{UnwindCode, UnwindInfo},
|
||||
CallConv, RegUnit, TargetIsa,
|
||||
};
|
||||
use crate::result::{CodegenError, CodegenResult};
|
||||
use alloc::vec::Vec;
|
||||
use log::warn;
|
||||
use crate::ir::Function;
|
||||
use crate::isa::x86::registers::{FPR, GPR};
|
||||
use crate::isa::{unwind::winx64::UnwindInfo, CallConv, RegUnit, TargetIsa};
|
||||
use crate::result::CodegenResult;
|
||||
|
||||
pub(crate) fn create_unwind_info(
|
||||
func: &Function,
|
||||
|
@ -19,115 +14,29 @@ pub(crate) fn create_unwind_info(
|
|||
return Ok(None);
|
||||
}
|
||||
|
||||
let prologue_end = func.prologue_end.unwrap();
|
||||
let entry_block = func.layout.entry_block().expect("missing entry block");
|
||||
|
||||
// Stores the stack size when SP is not adjusted via an immediate value
|
||||
let mut stack_size = None;
|
||||
let mut prologue_size = 0;
|
||||
let mut unwind_codes = Vec::new();
|
||||
let mut found_end = false;
|
||||
|
||||
for (offset, inst, size) in func.inst_offsets(entry_block, &isa.encoding_info()) {
|
||||
// x64 ABI prologues cannot exceed 255 bytes in length
|
||||
if (offset + size) > 255 {
|
||||
warn!("function prologues cannot exceed 255 bytes in size for Windows x64");
|
||||
return Err(CodegenError::CodeTooLarge);
|
||||
let unwind = match super::create_unwind_info(func, isa)? {
|
||||
Some(u) => u,
|
||||
None => {
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
prologue_size += size;
|
||||
Ok(Some(UnwindInfo::build::<RegisterMapper>(unwind)?))
|
||||
}
|
||||
|
||||
let unwind_offset = (offset + size) as u8;
|
||||
struct RegisterMapper;
|
||||
|
||||
match func.dfg[inst] {
|
||||
InstructionData::Unary { opcode, arg } => {
|
||||
match opcode {
|
||||
Opcode::X86Push => {
|
||||
unwind_codes.push(UnwindCode::PushRegister {
|
||||
offset: unwind_offset,
|
||||
reg: GPR.index_of(func.locations[arg].unwrap_reg()) as u8,
|
||||
});
|
||||
}
|
||||
Opcode::AdjustSpDown => {
|
||||
let stack_size =
|
||||
stack_size.expect("expected a previous stack size instruction");
|
||||
|
||||
// This is used when calling a stack check function
|
||||
// We need to track the assignment to RAX which has the size of the stack
|
||||
unwind_codes.push(UnwindCode::StackAlloc {
|
||||
offset: unwind_offset,
|
||||
size: stack_size,
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
InstructionData::UnaryImm { opcode, imm } => {
|
||||
match opcode {
|
||||
Opcode::Iconst => {
|
||||
let imm: i64 = imm.into();
|
||||
assert!(imm <= core::u32::MAX as i64);
|
||||
assert!(stack_size.is_none());
|
||||
|
||||
// This instruction should only appear in a prologue to pass an
|
||||
// argument of the stack size to a stack check function.
|
||||
// Record the stack size so we know what it is when we encounter the adjustment
|
||||
// instruction (which will adjust via the register assigned to this instruction).
|
||||
stack_size = Some(imm as u32);
|
||||
}
|
||||
Opcode::AdjustSpDownImm => {
|
||||
let imm: i64 = imm.into();
|
||||
assert!(imm <= core::u32::MAX as i64);
|
||||
|
||||
stack_size = Some(imm as u32);
|
||||
|
||||
unwind_codes.push(UnwindCode::StackAlloc {
|
||||
offset: unwind_offset,
|
||||
size: imm as u32,
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
InstructionData::Store {
|
||||
opcode: Opcode::Store,
|
||||
args: [arg1, arg2],
|
||||
offset,
|
||||
..
|
||||
} => {
|
||||
if let (ValueLoc::Reg(src), ValueLoc::Reg(dst)) =
|
||||
(func.locations[arg1], func.locations[arg2])
|
||||
{
|
||||
// If this is a save of an FPR, record an unwind operation
|
||||
// Note: the stack_offset here is relative to an adjusted SP
|
||||
if dst == (RU::rsp as RegUnit) && FPR.contains(src) {
|
||||
let offset: i32 = offset.into();
|
||||
unwind_codes.push(UnwindCode::SaveXmm {
|
||||
offset: unwind_offset,
|
||||
reg: src as u8,
|
||||
stack_offset: offset as u32,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
if inst == prologue_end {
|
||||
found_end = true;
|
||||
break;
|
||||
impl crate::isa::unwind::winx64::RegisterMapper for RegisterMapper {
|
||||
fn map(reg: RegUnit) -> crate::isa::unwind::winx64::MappedRegister {
|
||||
use crate::isa::unwind::winx64::MappedRegister;
|
||||
if GPR.contains(reg) {
|
||||
MappedRegister::Int(GPR.index_of(reg) as u8)
|
||||
} else if FPR.contains(reg) {
|
||||
MappedRegister::Xmm(reg as u8)
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
assert!(found_end);
|
||||
|
||||
Ok(Some(UnwindInfo {
|
||||
flags: 0, // this assumes cranelift functions have no SEH handlers
|
||||
prologue_size: prologue_size as u8,
|
||||
frame_register: None,
|
||||
frame_register_offset: 0,
|
||||
unwind_codes,
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -135,6 +44,8 @@ mod tests {
|
|||
use super::*;
|
||||
use crate::cursor::{Cursor, FuncCursor};
|
||||
use crate::ir::{ExternalName, InstBuilder, Signature, StackSlotData, StackSlotKind};
|
||||
use crate::isa::unwind::winx64::UnwindCode;
|
||||
use crate::isa::x86::registers::RU;
|
||||
use crate::isa::{lookup, CallConv};
|
||||
use crate::settings::{builder, Flags};
|
||||
use crate::Context;
|
||||
|
|
|
@ -774,12 +774,12 @@ fn narrow_icmp_imm(
|
|||
let ty = pos.func.dfg.ctrl_typevar(inst);
|
||||
let ty_half = ty.half_width().unwrap();
|
||||
|
||||
let imm_low = pos
|
||||
.ins()
|
||||
.iconst(ty_half, imm & ((1u128 << ty_half.bits()) - 1) as i64);
|
||||
let imm_high = pos
|
||||
.ins()
|
||||
.iconst(ty_half, imm.wrapping_shr(ty_half.bits().into()));
|
||||
let mask = ((1u128 << ty_half.bits()) - 1) as i64;
|
||||
let imm_low = pos.ins().iconst(ty_half, imm & mask);
|
||||
let imm_high = pos.ins().iconst(
|
||||
ty_half,
|
||||
imm.checked_shr(ty_half.bits().into()).unwrap_or(0) & mask,
|
||||
);
|
||||
let (arg_low, arg_high) = pos.ins().isplit(arg);
|
||||
|
||||
match cond {
|
||||
|
|
|
@ -31,8 +31,7 @@
|
|||
clippy::float_arithmetic,
|
||||
clippy::mut_mut,
|
||||
clippy::nonminimal_bool,
|
||||
clippy::option_map_unwrap_or,
|
||||
clippy::option_map_unwrap_or_else,
|
||||
clippy::map_unwrap_or,
|
||||
clippy::unicode_not_nfc,
|
||||
clippy::use_self
|
||||
)
|
||||
|
@ -71,6 +70,7 @@ pub use cranelift_entity as entity;
|
|||
pub mod binemit;
|
||||
pub mod cfg_printer;
|
||||
pub mod cursor;
|
||||
pub mod data_value;
|
||||
pub mod dbg;
|
||||
pub mod dominator_tree;
|
||||
pub mod flowgraph;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use crate::binemit::StackMap;
|
||||
use crate::ir::StackSlot;
|
||||
use crate::isa::CallConv;
|
||||
use crate::machinst::*;
|
||||
use crate::settings;
|
||||
|
||||
|
@ -22,9 +23,18 @@ pub trait ABICallee {
|
|||
/// lowering context exists.
|
||||
fn init(&mut self, maybe_tmp: Option<Writable<Reg>>);
|
||||
|
||||
/// Accumulate outgoing arguments. This ensures that at least SIZE bytes
|
||||
/// are allocated in the prologue to be available for use in function calls
|
||||
/// to hold arguments and/or return values. If this function is called
|
||||
/// multiple times, the maximum of all SIZE values will be available.
|
||||
fn accumulate_outgoing_args_size(&mut self, size: u32);
|
||||
|
||||
/// Get the settings controlling this function's compilation.
|
||||
fn flags(&self) -> &settings::Flags;
|
||||
|
||||
/// Get the calling convention implemented by this ABI object.
|
||||
fn call_conv(&self) -> CallConv;
|
||||
|
||||
/// Get the liveins of the function.
|
||||
fn liveins(&self) -> Set<RealReg>;
|
||||
|
||||
|
@ -151,6 +161,9 @@ pub trait ABICallee {
|
|||
from_slot: SpillSlot,
|
||||
ty: Option<Type>,
|
||||
) -> Self::I;
|
||||
|
||||
/// Desired unwind info type.
|
||||
fn unwind_info_kind(&self) -> UnwindInfoKind;
|
||||
}
|
||||
|
||||
/// Trait implemented by an object that tracks ABI-related state and can
|
||||
|
@ -196,6 +209,13 @@ pub trait ABICaller {
|
|||
/// Emit code to post-adjust the satck, after call return and return-value copies.
|
||||
fn emit_stack_post_adjust<C: LowerCtx<I = Self::I>>(&self, ctx: &mut C);
|
||||
|
||||
/// Accumulate outgoing arguments. This ensures that the caller (as
|
||||
/// identified via the CTX argument) allocates enough space in the
|
||||
/// prologue to hold all arguments and return values for this call.
|
||||
/// There is no code emitted at the call site, everything is done
|
||||
/// in the caller's function prologue.
|
||||
fn accumulate_outgoing_args_size<C: LowerCtx<I = Self::I>>(&self, ctx: &mut C);
|
||||
|
||||
/// Emit the call itself.
|
||||
///
|
||||
/// The returned instruction should have proper use- and def-sets according
|
||||
|
|
|
@ -111,7 +111,7 @@
|
|||
use super::abi::*;
|
||||
use crate::binemit::StackMap;
|
||||
use crate::ir::types::*;
|
||||
use crate::ir::{ArgumentExtension, SourceLoc, StackSlot};
|
||||
use crate::ir::{ArgumentExtension, StackSlot};
|
||||
use crate::machinst::*;
|
||||
use crate::settings;
|
||||
use crate::CodegenResult;
|
||||
|
@ -216,6 +216,9 @@ pub trait ABIMachineSpec {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns required stack alignment in bytes.
|
||||
fn stack_align(call_conv: isa::CallConv) -> u32;
|
||||
|
||||
/// Process a list of parameters or return values and allocate them to registers
|
||||
/// and stack slots.
|
||||
///
|
||||
|
@ -326,6 +329,7 @@ pub trait ABIMachineSpec {
|
|||
flags: &settings::Flags,
|
||||
clobbers: &Set<Writable<RealReg>>,
|
||||
fixed_frame_storage_size: u32,
|
||||
outgoing_args_size: u32,
|
||||
) -> (u64, SmallVec<[Self::I; 16]>);
|
||||
|
||||
/// Generate a clobber-restore sequence. This sequence should perform the
|
||||
|
@ -336,6 +340,8 @@ pub trait ABIMachineSpec {
|
|||
call_conv: isa::CallConv,
|
||||
flags: &settings::Flags,
|
||||
clobbers: &Set<Writable<RealReg>>,
|
||||
fixed_frame_storage_size: u32,
|
||||
outgoing_args_size: u32,
|
||||
) -> SmallVec<[Self::I; 16]>;
|
||||
|
||||
/// Generate a call instruction/sequence. This method is provided one
|
||||
|
@ -344,9 +350,10 @@ pub trait ABIMachineSpec {
|
|||
dest: &CallDest,
|
||||
uses: Vec<Reg>,
|
||||
defs: Vec<Writable<Reg>>,
|
||||
loc: SourceLoc,
|
||||
opcode: ir::Opcode,
|
||||
tmp: Writable<Reg>,
|
||||
callee_conv: isa::CallConv,
|
||||
callee_conv: isa::CallConv,
|
||||
) -> SmallVec<[(InstIsSafepoint, Self::I); 2]>;
|
||||
|
||||
/// Get the number of spillslots required for the given register-class and
|
||||
|
@ -359,8 +366,9 @@ pub trait ABIMachineSpec {
|
|||
/// Get the "nominal SP to FP" offset from an instruction-emission state.
|
||||
fn get_nominal_sp_to_fp(s: &<Self::I as MachInstEmit>::State) -> i64;
|
||||
|
||||
/// Get all caller-save registers.
|
||||
fn get_caller_saves(call_conv: isa::CallConv) -> Vec<Writable<Reg>>;
|
||||
/// Get all caller-save registers, that is, registers that we expect
|
||||
/// not to be saved across a call to a callee with the given ABI.
|
||||
fn get_regs_clobbered_by_call(call_conv_of_callee: isa::CallConv) -> Vec<Writable<Reg>>;
|
||||
}
|
||||
|
||||
/// ABI information shared between body (callee) and caller.
|
||||
|
@ -428,10 +436,16 @@ pub struct ABICalleeImpl<M: ABIMachineSpec> {
|
|||
stackslots: Vec<u32>,
|
||||
/// Total stack size of all stackslots.
|
||||
stackslots_size: u32,
|
||||
/// Stack size to be reserved for outgoing arguments.
|
||||
outgoing_args_size: u32,
|
||||
/// Clobbered registers, from regalloc.
|
||||
clobbered: Set<Writable<RealReg>>,
|
||||
/// Total number of spillslots, from regalloc.
|
||||
spillslots: Option<usize>,
|
||||
/// Storage allocated for the fixed part of the stack frame. This is
|
||||
/// usually the same as the total frame size below, except in the case
|
||||
/// of the baldrdash calling convention.
|
||||
fixed_frame_storage_size: u32,
|
||||
/// "Total frame size", as defined by "distance between FP and nominal SP".
|
||||
/// Some items are pushed below nominal SP, so the function may actually use
|
||||
/// more stack than this would otherwise imply. It is simply the initial
|
||||
|
@ -516,8 +530,10 @@ impl<M: ABIMachineSpec> ABICalleeImpl<M> {
|
|||
sig,
|
||||
stackslots,
|
||||
stackslots_size: stack_offset,
|
||||
outgoing_args_size: 0,
|
||||
clobbered: Set::empty(),
|
||||
spillslots: None,
|
||||
fixed_frame_storage_size: 0,
|
||||
total_frame_size: None,
|
||||
ret_area_ptr: None,
|
||||
call_conv,
|
||||
|
@ -678,10 +694,20 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
|||
}
|
||||
}
|
||||
|
||||
fn accumulate_outgoing_args_size(&mut self, size: u32) {
|
||||
if size > self.outgoing_args_size {
|
||||
self.outgoing_args_size = size;
|
||||
}
|
||||
}
|
||||
|
||||
fn flags(&self) -> &settings::Flags {
|
||||
&self.flags
|
||||
}
|
||||
|
||||
fn call_conv(&self) -> isa::CallConv {
|
||||
self.sig.call_conv
|
||||
}
|
||||
|
||||
fn liveins(&self) -> Set<RealReg> {
|
||||
let mut set: Set<RealReg> = Set::empty();
|
||||
for &arg in &self.sig.args {
|
||||
|
@ -929,11 +955,9 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
|||
);
|
||||
total_stacksize += self.flags.baldrdash_prologue_words() as u32 * bytes;
|
||||
}
|
||||
let mask = 2 * bytes - 1;
|
||||
let mask = M::stack_align(self.call_conv) - 1;
|
||||
let total_stacksize = (total_stacksize + mask) & !mask; // 16-align the stack.
|
||||
|
||||
let mut fixed_frame_storage_size = 0;
|
||||
|
||||
if !self.call_conv.extends_baldrdash() {
|
||||
// Leaf functions with zero stack don't need a stack check if one's
|
||||
// specified, otherwise always insert the stack check.
|
||||
|
@ -944,7 +968,7 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
|||
}
|
||||
}
|
||||
if total_stacksize > 0 {
|
||||
fixed_frame_storage_size += total_stacksize;
|
||||
self.fixed_frame_storage_size += total_stacksize;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -963,12 +987,14 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
|||
self.call_conv,
|
||||
&self.flags,
|
||||
&self.clobbered,
|
||||
fixed_frame_storage_size,
|
||||
self.fixed_frame_storage_size,
|
||||
self.outgoing_args_size,
|
||||
);
|
||||
insts.extend(clobber_insts);
|
||||
|
||||
if clobber_size > 0 {
|
||||
insts.push(M::gen_nominal_sp_adj(clobber_size as i32));
|
||||
let sp_adj = self.outgoing_args_size as i32 + clobber_size as i32;
|
||||
if sp_adj > 0 {
|
||||
insts.push(M::gen_nominal_sp_adj(sp_adj));
|
||||
}
|
||||
|
||||
self.total_frame_size = Some(total_stacksize);
|
||||
|
@ -983,6 +1009,8 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
|||
self.call_conv,
|
||||
&self.flags,
|
||||
&self.clobbered,
|
||||
self.fixed_frame_storage_size,
|
||||
self.outgoing_args_size,
|
||||
));
|
||||
|
||||
// N.B.: we do *not* emit a nominal SP adjustment here, because (i) there will be no
|
||||
|
@ -1027,6 +1055,18 @@ impl<M: ABIMachineSpec> ABICallee for ABICalleeImpl<M> {
|
|||
let ty = ty_from_ty_hint_or_reg_class::<M>(to_reg.to_reg().to_reg(), ty);
|
||||
self.load_spillslot(from_slot, ty, to_reg.map(|r| r.to_reg()))
|
||||
}
|
||||
|
||||
fn unwind_info_kind(&self) -> UnwindInfoKind {
|
||||
match self.sig.call_conv {
|
||||
#[cfg(feature = "unwind")]
|
||||
isa::CallConv::Fast | isa::CallConv::Cold | isa::CallConv::SystemV => {
|
||||
UnwindInfoKind::SystemV
|
||||
}
|
||||
#[cfg(feature = "unwind")]
|
||||
isa::CallConv::WindowsFastcall => UnwindInfoKind::Windows,
|
||||
_ => UnwindInfoKind::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn abisig_to_uses_and_defs<M: ABIMachineSpec>(sig: &ABISig) -> (Vec<Reg>, Vec<Writable<Reg>>) {
|
||||
|
@ -1040,7 +1080,7 @@ fn abisig_to_uses_and_defs<M: ABIMachineSpec>(sig: &ABISig) -> (Vec<Reg>, Vec<Wr
|
|||
}
|
||||
|
||||
// Compute defs: all retval regs, and all caller-save (clobbered) regs.
|
||||
let mut defs = M::get_caller_saves(sig.call_conv);
|
||||
let mut defs = M::get_regs_clobbered_by_call(sig.call_conv);
|
||||
for ret in &sig.rets {
|
||||
match ret {
|
||||
&ABIArg::Reg(reg, ..) => defs.push(Writable::from_reg(reg.to_reg())),
|
||||
|
@ -1061,10 +1101,10 @@ pub struct ABICallerImpl<M: ABIMachineSpec> {
|
|||
defs: Vec<Writable<Reg>>,
|
||||
/// Call destination.
|
||||
dest: CallDest,
|
||||
/// Location of callsite.
|
||||
loc: ir::SourceLoc,
|
||||
/// Actuall call opcode; used to distinguish various types of calls.
|
||||
/// Actual call opcode; used to distinguish various types of calls.
|
||||
opcode: ir::Opcode,
|
||||
/// Caller's calling convention.
|
||||
caller_conv: isa::CallConv,
|
||||
|
||||
_mach: PhantomData<M>,
|
||||
}
|
||||
|
@ -1084,7 +1124,7 @@ impl<M: ABIMachineSpec> ABICallerImpl<M> {
|
|||
sig: &ir::Signature,
|
||||
extname: &ir::ExternalName,
|
||||
dist: RelocDistance,
|
||||
loc: ir::SourceLoc,
|
||||
caller_conv: isa::CallConv,
|
||||
) -> CodegenResult<ABICallerImpl<M>> {
|
||||
let sig = ABISig::from_func_sig::<M>(sig)?;
|
||||
let (uses, defs) = abisig_to_uses_and_defs::<M>(&sig);
|
||||
|
@ -1093,8 +1133,8 @@ impl<M: ABIMachineSpec> ABICallerImpl<M> {
|
|||
uses,
|
||||
defs,
|
||||
dest: CallDest::ExtName(extname.clone(), dist),
|
||||
loc,
|
||||
opcode: ir::Opcode::Call,
|
||||
caller_conv,
|
||||
_mach: PhantomData,
|
||||
})
|
||||
}
|
||||
|
@ -1104,8 +1144,8 @@ impl<M: ABIMachineSpec> ABICallerImpl<M> {
|
|||
pub fn from_ptr(
|
||||
sig: &ir::Signature,
|
||||
ptr: Reg,
|
||||
loc: ir::SourceLoc,
|
||||
opcode: ir::Opcode,
|
||||
caller_conv: isa::CallConv,
|
||||
) -> CodegenResult<ABICallerImpl<M>> {
|
||||
let sig = ABISig::from_func_sig::<M>(sig)?;
|
||||
let (uses, defs) = abisig_to_uses_and_defs::<M>(&sig);
|
||||
|
@ -1114,8 +1154,8 @@ impl<M: ABIMachineSpec> ABICallerImpl<M> {
|
|||
uses,
|
||||
defs,
|
||||
dest: CallDest::Reg(ptr),
|
||||
loc,
|
||||
opcode,
|
||||
caller_conv,
|
||||
_mach: PhantomData,
|
||||
})
|
||||
}
|
||||
|
@ -1147,6 +1187,11 @@ impl<M: ABIMachineSpec> ABICaller for ABICallerImpl<M> {
|
|||
}
|
||||
}
|
||||
|
||||
fn accumulate_outgoing_args_size<C: LowerCtx<I = Self::I>>(&self, ctx: &mut C) {
|
||||
let off = self.sig.stack_arg_space + self.sig.stack_ret_space;
|
||||
ctx.abi().accumulate_outgoing_args_size(off as u32);
|
||||
}
|
||||
|
||||
fn emit_stack_pre_adjust<C: LowerCtx<I = Self::I>>(&self, ctx: &mut C) {
|
||||
let off = self.sig.stack_arg_space + self.sig.stack_ret_space;
|
||||
adjust_stack_and_nominal_sp::<M, C>(ctx, off as i32, /* is_sub = */ true)
|
||||
|
@ -1255,8 +1300,16 @@ impl<M: ABIMachineSpec> ABICaller for ABICallerImpl<M> {
|
|||
self.emit_copy_reg_to_arg(ctx, i, rd.to_reg());
|
||||
}
|
||||
let tmp = ctx.alloc_tmp(word_rc, word_type);
|
||||
for (is_safepoint, inst) in
|
||||
M::gen_call(&self.dest, uses, defs, self.loc, self.opcode, tmp).into_iter()
|
||||
for (is_safepoint, inst) in M::gen_call(
|
||||
&self.dest,
|
||||
uses,
|
||||
defs,
|
||||
self.opcode,
|
||||
tmp,
|
||||
self.sig.call_conv,
|
||||
self.caller_conv,
|
||||
)
|
||||
.into_iter()
|
||||
{
|
||||
match is_safepoint {
|
||||
InstIsSafepoint::Yes => ctx.emit_safepoint(inst),
|
||||
|
|
|
@ -129,6 +129,11 @@ impl TargetIsa for TargetIsaAdapter {
|
|||
self.backend.unsigned_sub_overflow_condition()
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
|
||||
self.backend.create_systemv_cie()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self as &dyn Any
|
||||
}
|
||||
|
|
|
@ -142,12 +142,14 @@
|
|||
|
||||
use crate::binemit::{Addend, CodeOffset, CodeSink, Reloc, StackMap};
|
||||
use crate::ir::{ExternalName, Opcode, SourceLoc, TrapCode};
|
||||
use crate::machinst::{BlockIndex, MachInstLabelUse, VCodeInst};
|
||||
use crate::machinst::{BlockIndex, MachInstLabelUse, VCodeConstant, VCodeConstants, VCodeInst};
|
||||
use crate::timing;
|
||||
use cranelift_entity::{entity_impl, SecondaryMap};
|
||||
|
||||
use log::trace;
|
||||
use smallvec::SmallVec;
|
||||
use std::mem;
|
||||
use std::string::String;
|
||||
|
||||
/// A buffer of output to be produced, fixed up, and then emitted to a CodeSink
|
||||
/// in bulk.
|
||||
|
@ -217,6 +219,8 @@ pub struct MachBuffer<I: VCodeInst> {
|
|||
/// when the offset has grown past this (`labels_at_tail_off`) point.
|
||||
/// Always <= `cur_offset()`.
|
||||
labels_at_tail_off: CodeOffset,
|
||||
/// Map used constants to their [MachLabel].
|
||||
constant_labels: SecondaryMap<VCodeConstant, MachLabel>,
|
||||
}
|
||||
|
||||
/// A `MachBuffer` once emission is completed: holds generated code and records,
|
||||
|
@ -247,6 +251,7 @@ static UNKNOWN_LABEL: MachLabel = MachLabel(0xffff_ffff);
|
|||
/// appropriately when the label's location is eventually known.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct MachLabel(u32);
|
||||
entity_impl!(MachLabel);
|
||||
|
||||
impl MachLabel {
|
||||
/// Get a label for a block. (The first N MachLabels are always reseved for
|
||||
|
@ -259,6 +264,17 @@ impl MachLabel {
|
|||
pub fn get(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Creates a string representing this label, for convenience.
|
||||
pub fn to_string(&self) -> String {
|
||||
format!("label{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MachLabel {
|
||||
fn default() -> Self {
|
||||
UNKNOWN_LABEL
|
||||
}
|
||||
}
|
||||
|
||||
/// A stack map extent, when creating a stack map.
|
||||
|
@ -293,6 +309,7 @@ impl<I: VCodeInst> MachBuffer<I> {
|
|||
latest_branches: SmallVec::new(),
|
||||
labels_at_tail: SmallVec::new(),
|
||||
labels_at_tail_off: 0,
|
||||
constant_labels: SecondaryMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -462,6 +479,24 @@ impl<I: VCodeInst> MachBuffer<I> {
|
|||
// Post-invariant: as for `get_label()`.
|
||||
}
|
||||
|
||||
/// Reserve the next N MachLabels for constants.
|
||||
pub fn reserve_labels_for_constants(&mut self, constants: &VCodeConstants) {
|
||||
trace!(
|
||||
"MachBuffer: next {} labels are for constants",
|
||||
constants.len()
|
||||
);
|
||||
for c in constants.keys() {
|
||||
self.constant_labels[c] = self.get_label();
|
||||
}
|
||||
|
||||
// Post-invariant: as for `get_label()`.
|
||||
}
|
||||
|
||||
/// Retrieve the reserved label for a constant.
|
||||
pub fn get_label_for_constant(&self, constant: VCodeConstant) -> MachLabel {
|
||||
self.constant_labels[constant]
|
||||
}
|
||||
|
||||
/// Bind a label to the current offset. A label can only be bound once.
|
||||
pub fn bind_label(&mut self, label: MachLabel) {
|
||||
trace!(
|
||||
|
@ -992,9 +1027,16 @@ impl<I: VCodeInst> MachBuffer<I> {
|
|||
data: &[u8],
|
||||
max_distance: CodeOffset,
|
||||
) {
|
||||
let deadline = self.cur_offset() + max_distance;
|
||||
trace!(
|
||||
"defer_constant: eventually emit {} bytes aligned to {} at label {:?}",
|
||||
data.len(),
|
||||
align,
|
||||
label
|
||||
);
|
||||
let deadline = self.cur_offset().saturating_add(max_distance);
|
||||
self.island_worst_case_size += data.len() as CodeOffset;
|
||||
self.island_worst_case_size &= !(I::LabelUse::ALIGN - 1);
|
||||
self.island_worst_case_size =
|
||||
(self.island_worst_case_size + I::LabelUse::ALIGN - 1) & !(I::LabelUse::ALIGN - 1);
|
||||
self.pending_constants.push(MachLabelConstant {
|
||||
label,
|
||||
align,
|
||||
|
@ -1130,14 +1172,6 @@ impl<I: VCodeInst> MachBuffer<I> {
|
|||
pub fn finish(mut self) -> MachBufferFinalized {
|
||||
let _tt = timing::vcode_emit_finish();
|
||||
|
||||
// Ensure that all labels are defined. This is a full (release-mode)
|
||||
// assert because we must avoid looping indefinitely below; an
|
||||
// unresolved label will prevent the fixup_records vec from emptying.
|
||||
assert!(self
|
||||
.label_offsets
|
||||
.iter()
|
||||
.all(|&off| off != UNKNOWN_LABEL_OFFSET));
|
||||
|
||||
while !self.pending_constants.is_empty() || !self.fixup_records.is_empty() {
|
||||
// `emit_island()` will emit any pending veneers and constants, and
|
||||
// as a side-effect, will also take care of any fixups with resolved
|
||||
|
@ -1145,6 +1179,11 @@ impl<I: VCodeInst> MachBuffer<I> {
|
|||
self.emit_island();
|
||||
}
|
||||
|
||||
// Ensure that all labels have been fixed up after the last island is emitted. This is a
|
||||
// full (release-mode) assert because an unresolved label means the emitted code is
|
||||
// incorrect.
|
||||
assert!(self.fixup_records.is_empty());
|
||||
|
||||
MachBufferFinalized {
|
||||
data: self.data,
|
||||
relocs: self.relocs,
|
||||
|
@ -1421,7 +1460,7 @@ impl MachBranch {
|
|||
mod test {
|
||||
use super::*;
|
||||
use crate::isa::aarch64::inst::xreg;
|
||||
use crate::isa::aarch64::inst::{BranchTarget, CondBrKind, Inst};
|
||||
use crate::isa::aarch64::inst::{BranchTarget, CondBrKind, EmitInfo, Inst};
|
||||
use crate::machinst::MachInstEmit;
|
||||
use crate::settings;
|
||||
use std::default::Default;
|
||||
|
@ -1435,14 +1474,14 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_elide_jump_to_next() {
|
||||
let flags = settings::Flags::new(settings::builder());
|
||||
let info = EmitInfo::new(settings::Flags::new(settings::builder()));
|
||||
let mut buf = MachBuffer::new();
|
||||
let mut state = Default::default();
|
||||
|
||||
buf.reserve_labels_for_blocks(2);
|
||||
buf.bind_label(label(0));
|
||||
let inst = Inst::Jump { dest: target(1) };
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
buf.bind_label(label(1));
|
||||
let buf = buf.finish();
|
||||
assert_eq!(0, buf.total_size());
|
||||
|
@ -1450,7 +1489,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_elide_trivial_jump_blocks() {
|
||||
let flags = settings::Flags::new(settings::builder());
|
||||
let info = EmitInfo::new(settings::Flags::new(settings::builder()));
|
||||
let mut buf = MachBuffer::new();
|
||||
let mut state = Default::default();
|
||||
|
||||
|
@ -1462,15 +1501,15 @@ mod test {
|
|||
taken: target(1),
|
||||
not_taken: target(2),
|
||||
};
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(1));
|
||||
let inst = Inst::Jump { dest: target(3) };
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(2));
|
||||
let inst = Inst::Jump { dest: target(3) };
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(3));
|
||||
|
||||
|
@ -1480,7 +1519,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_flip_cond() {
|
||||
let flags = settings::Flags::new(settings::builder());
|
||||
let info = EmitInfo::new(settings::Flags::new(settings::builder()));
|
||||
let mut buf = MachBuffer::new();
|
||||
let mut state = Default::default();
|
||||
|
||||
|
@ -1492,17 +1531,17 @@ mod test {
|
|||
taken: target(1),
|
||||
not_taken: target(2),
|
||||
};
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(1));
|
||||
let inst = Inst::Udf {
|
||||
trap_info: (SourceLoc::default(), TrapCode::Interrupt),
|
||||
trap_code: TrapCode::Interrupt,
|
||||
};
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(2));
|
||||
let inst = Inst::Nop4;
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(3));
|
||||
|
||||
|
@ -1512,11 +1551,11 @@ mod test {
|
|||
let mut state = Default::default();
|
||||
let inst = Inst::TrapIf {
|
||||
kind: CondBrKind::NotZero(xreg(0)),
|
||||
trap_info: (SourceLoc::default(), TrapCode::Interrupt),
|
||||
trap_code: TrapCode::Interrupt,
|
||||
};
|
||||
inst.emit(&mut buf2, &flags, &mut state);
|
||||
inst.emit(&mut buf2, &info, &mut state);
|
||||
let inst = Inst::Nop4;
|
||||
inst.emit(&mut buf2, &flags, &mut state);
|
||||
inst.emit(&mut buf2, &info, &mut state);
|
||||
|
||||
let buf2 = buf2.finish();
|
||||
|
||||
|
@ -1525,7 +1564,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_island() {
|
||||
let flags = settings::Flags::new(settings::builder());
|
||||
let info = EmitInfo::new(settings::Flags::new(settings::builder()));
|
||||
let mut buf = MachBuffer::new();
|
||||
let mut state = Default::default();
|
||||
|
||||
|
@ -1537,7 +1576,7 @@ mod test {
|
|||
taken: target(2),
|
||||
not_taken: target(3),
|
||||
};
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(1));
|
||||
while buf.cur_offset() < 2000000 {
|
||||
|
@ -1545,16 +1584,16 @@ mod test {
|
|||
buf.emit_island();
|
||||
}
|
||||
let inst = Inst::Nop4;
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
}
|
||||
|
||||
buf.bind_label(label(2));
|
||||
let inst = Inst::Nop4;
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(3));
|
||||
let inst = Inst::Nop4;
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
let buf = buf.finish();
|
||||
|
||||
|
@ -1567,7 +1606,7 @@ mod test {
|
|||
taken: BranchTarget::ResolvedOffset(1048576 - 4),
|
||||
not_taken: BranchTarget::ResolvedOffset(2000000 + 4 - 4),
|
||||
};
|
||||
inst.emit(&mut buf2, &flags, &mut state);
|
||||
inst.emit(&mut buf2, &info, &mut state);
|
||||
|
||||
let buf2 = buf2.finish();
|
||||
|
||||
|
@ -1576,7 +1615,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_island_backward() {
|
||||
let flags = settings::Flags::new(settings::builder());
|
||||
let info = EmitInfo::new(settings::Flags::new(settings::builder()));
|
||||
let mut buf = MachBuffer::new();
|
||||
let mut state = Default::default();
|
||||
|
||||
|
@ -1584,16 +1623,16 @@ mod test {
|
|||
|
||||
buf.bind_label(label(0));
|
||||
let inst = Inst::Nop4;
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(1));
|
||||
let inst = Inst::Nop4;
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(2));
|
||||
while buf.cur_offset() < 2000000 {
|
||||
let inst = Inst::Nop4;
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
}
|
||||
|
||||
buf.bind_label(label(3));
|
||||
|
@ -1602,7 +1641,7 @@ mod test {
|
|||
taken: target(0),
|
||||
not_taken: target(1),
|
||||
};
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
let buf = buf.finish();
|
||||
|
||||
|
@ -1615,11 +1654,11 @@ mod test {
|
|||
taken: BranchTarget::ResolvedOffset(8),
|
||||
not_taken: BranchTarget::ResolvedOffset(4 - (2000000 + 4)),
|
||||
};
|
||||
inst.emit(&mut buf2, &flags, &mut state);
|
||||
inst.emit(&mut buf2, &info, &mut state);
|
||||
let inst = Inst::Jump {
|
||||
dest: BranchTarget::ResolvedOffset(-(2000000 + 8)),
|
||||
};
|
||||
inst.emit(&mut buf2, &flags, &mut state);
|
||||
inst.emit(&mut buf2, &info, &mut state);
|
||||
|
||||
let buf2 = buf2.finish();
|
||||
|
||||
|
@ -1661,7 +1700,7 @@ mod test {
|
|||
// label7:
|
||||
// ret
|
||||
|
||||
let flags = settings::Flags::new(settings::builder());
|
||||
let info = EmitInfo::new(settings::Flags::new(settings::builder()));
|
||||
let mut buf = MachBuffer::new();
|
||||
let mut state = Default::default();
|
||||
|
||||
|
@ -1673,38 +1712,38 @@ mod test {
|
|||
taken: target(1),
|
||||
not_taken: target(2),
|
||||
};
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(1));
|
||||
let inst = Inst::Jump { dest: target(3) };
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(2));
|
||||
let inst = Inst::Nop4;
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
let inst = Inst::Jump { dest: target(0) };
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(3));
|
||||
let inst = Inst::Jump { dest: target(4) };
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(4));
|
||||
let inst = Inst::Jump { dest: target(5) };
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(5));
|
||||
let inst = Inst::Jump { dest: target(7) };
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(6));
|
||||
let inst = Inst::Nop4;
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(7));
|
||||
let inst = Inst::Ret;
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
let buf = buf.finish();
|
||||
|
||||
|
@ -1737,7 +1776,7 @@ mod test {
|
|||
//
|
||||
// label0, label1, ..., label4:
|
||||
// b label0
|
||||
let flags = settings::Flags::new(settings::builder());
|
||||
let info = EmitInfo::new(settings::Flags::new(settings::builder()));
|
||||
let mut buf = MachBuffer::new();
|
||||
let mut state = Default::default();
|
||||
|
||||
|
@ -1745,23 +1784,23 @@ mod test {
|
|||
|
||||
buf.bind_label(label(0));
|
||||
let inst = Inst::Jump { dest: target(1) };
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(1));
|
||||
let inst = Inst::Jump { dest: target(2) };
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(2));
|
||||
let inst = Inst::Jump { dest: target(3) };
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(3));
|
||||
let inst = Inst::Jump { dest: target(4) };
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
buf.bind_label(label(4));
|
||||
let inst = Inst::Jump { dest: target(1) };
|
||||
inst.emit(&mut buf, &flags, &mut state);
|
||||
inst.emit(&mut buf, &info, &mut state);
|
||||
|
||||
let buf = buf.finish();
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::settings;
|
|||
use crate::timing;
|
||||
|
||||
use log::debug;
|
||||
use regalloc::{allocate_registers_with_opts, Algorithm, Options};
|
||||
use regalloc::{allocate_registers_with_opts, Algorithm, Options, PrettyPrint};
|
||||
|
||||
/// Compile the given function down to VCode with allocated registers, ready
|
||||
/// for binary emission.
|
||||
|
@ -14,14 +14,15 @@ pub fn compile<B: LowerBackend + MachBackend>(
|
|||
f: &Function,
|
||||
b: &B,
|
||||
abi: Box<dyn ABICallee<I = B::MInst>>,
|
||||
emit_info: <B::MInst as MachInstEmit>::Info,
|
||||
) -> CodegenResult<VCode<B::MInst>>
|
||||
where
|
||||
B::MInst: ShowWithRRU,
|
||||
B::MInst: PrettyPrint,
|
||||
{
|
||||
// Compute lowered block order.
|
||||
let block_order = BlockLoweringOrder::new(f);
|
||||
// Build the lowering context.
|
||||
let lower = Lower::new(f, abi, block_order)?;
|
||||
let lower = Lower::new(f, abi, emit_info, block_order)?;
|
||||
// Lower the IR.
|
||||
let (mut vcode, stack_map_request_info) = {
|
||||
let _tt = timing::vcode_lower();
|
||||
|
|
|
@ -8,20 +8,21 @@ use crate::inst_predicates::{has_lowering_side_effect, is_constant_64bit};
|
|||
use crate::ir::instructions::BranchInfo;
|
||||
use crate::ir::types::I64;
|
||||
use crate::ir::{
|
||||
ArgumentPurpose, Block, Constant, ConstantData, ExternalName, Function, GlobalValueData,
|
||||
Immediate, Inst, InstructionData, MemFlags, Opcode, Signature, SourceLoc, Type, Value,
|
||||
ValueDef,
|
||||
ArgumentPurpose, Block, Constant, ConstantData, ExternalName, Function, GlobalValueData, Inst,
|
||||
InstructionData, MemFlags, Opcode, Signature, SourceLoc, Type, Value, ValueDef,
|
||||
};
|
||||
use crate::machinst::{
|
||||
ABICallee, BlockIndex, BlockLoweringOrder, LoweredBlock, MachLabel, VCode, VCodeBuilder,
|
||||
VCodeInst,
|
||||
VCodeConstant, VCodeConstantData, VCodeConstants, VCodeInst,
|
||||
};
|
||||
use crate::CodegenResult;
|
||||
|
||||
use regalloc::{Reg, RegClass, StackmapRequestInfo, VirtualReg, Writable};
|
||||
|
||||
use crate::data_value::DataValue;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use core::convert::TryInto;
|
||||
use log::debug;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
|
@ -62,7 +63,7 @@ pub trait LowerCtx {
|
|||
// Function-level queries:
|
||||
|
||||
/// Get the `ABICallee`.
|
||||
fn abi(&mut self) -> &dyn ABICallee<I = Self::I>;
|
||||
fn abi(&mut self) -> &mut dyn ABICallee<I = Self::I>;
|
||||
/// Get the (virtual) register that receives the return value. A return
|
||||
/// instruction should lower into a sequence that fills this register. (Why
|
||||
/// not allow the backend to specify its own result register for the return?
|
||||
|
@ -161,8 +162,11 @@ pub trait LowerCtx {
|
|||
fn is_reg_needed(&self, ir_inst: Inst, reg: Reg) -> bool;
|
||||
/// Retrieve constant data given a handle.
|
||||
fn get_constant_data(&self, constant_handle: Constant) -> &ConstantData;
|
||||
/// Retrieve an immediate given a reference.
|
||||
fn get_immediate(&self, imm: Immediate) -> &ConstantData;
|
||||
/// Indicate that a constant should be emitted.
|
||||
fn use_constant(&mut self, constant: VCodeConstantData) -> VCodeConstant;
|
||||
/// Retrieve the value immediate from an instruction. This will perform necessary lookups on the
|
||||
/// `DataFlowGraph` to retrieve even large immediates.
|
||||
fn get_immediate(&self, ir_inst: Inst) -> Option<DataValue>;
|
||||
/// Cause the value in `reg` to be in a virtual reg, by copying it into a new virtual reg
|
||||
/// if `reg` is a real reg. `ty` describes the type of the value in `reg`.
|
||||
fn ensure_in_vreg(&mut self, reg: Reg, ty: Type) -> Reg;
|
||||
|
@ -313,9 +317,11 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
|||
pub fn new(
|
||||
f: &'func Function,
|
||||
abi: Box<dyn ABICallee<I = I>>,
|
||||
emit_info: I::Info,
|
||||
block_order: BlockLoweringOrder,
|
||||
) -> CodegenResult<Lower<'func, I>> {
|
||||
let mut vcode = VCodeBuilder::new(abi, block_order);
|
||||
let constants = VCodeConstants::with_capacity(f.dfg.constants.len());
|
||||
let mut vcode = VCodeBuilder::new(abi, emit_info, block_order, constants);
|
||||
|
||||
let mut next_vreg: u32 = 0;
|
||||
|
||||
|
@ -847,7 +853,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
|||
impl<'func, I: VCodeInst> LowerCtx for Lower<'func, I> {
|
||||
type I = I;
|
||||
|
||||
fn abi(&mut self) -> &dyn ABICallee<I = I> {
|
||||
fn abi(&mut self) -> &mut dyn ABICallee<I = I> {
|
||||
self.vcode.abi()
|
||||
}
|
||||
|
||||
|
@ -1007,8 +1013,27 @@ impl<'func, I: VCodeInst> LowerCtx for Lower<'func, I> {
|
|||
self.f.dfg.constants.get(constant_handle)
|
||||
}
|
||||
|
||||
fn get_immediate(&self, imm: Immediate) -> &ConstantData {
|
||||
self.f.dfg.immediates.get(imm).unwrap()
|
||||
fn use_constant(&mut self, constant: VCodeConstantData) -> VCodeConstant {
|
||||
self.vcode.constants().insert(constant)
|
||||
}
|
||||
|
||||
fn get_immediate(&self, ir_inst: Inst) -> Option<DataValue> {
|
||||
let inst_data = self.data(ir_inst);
|
||||
match inst_data {
|
||||
InstructionData::Shuffle { mask, .. } => {
|
||||
let buffer = self.f.dfg.immediates.get(mask.clone()).unwrap().as_slice();
|
||||
let value = DataValue::V128(buffer.try_into().expect("a 16-byte data buffer"));
|
||||
Some(value)
|
||||
}
|
||||
InstructionData::UnaryConst {
|
||||
constant_handle, ..
|
||||
} => {
|
||||
let buffer = self.f.dfg.constants.get(constant_handle.clone()).as_slice();
|
||||
let value = DataValue::V128(buffer.try_into().expect("a 16-byte data buffer"));
|
||||
Some(value)
|
||||
}
|
||||
_ => inst_data.imm_value(),
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_in_vreg(&mut self, reg: Reg, ty: Type) -> Reg {
|
||||
|
|
|
@ -98,13 +98,15 @@
|
|||
|
||||
use crate::binemit::{CodeInfo, CodeOffset, StackMap};
|
||||
use crate::ir::condcodes::IntCC;
|
||||
use crate::ir::{Function, Type};
|
||||
use crate::ir::{Function, SourceLoc, Type};
|
||||
use crate::isa::unwind::input as unwind_input;
|
||||
use crate::result::CodegenResult;
|
||||
use crate::settings::Flags;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt::Debug;
|
||||
use core::ops::Range;
|
||||
use regalloc::RegUsageCollector;
|
||||
use regalloc::{
|
||||
RealReg, RealRegUniverse, Reg, RegClass, RegUsageMapper, SpillSlot, VirtualReg, Writable,
|
||||
|
@ -125,8 +127,6 @@ pub mod abi;
|
|||
pub use abi::*;
|
||||
pub mod abi_impl;
|
||||
pub use abi_impl::*;
|
||||
pub mod pretty_print;
|
||||
pub use pretty_print::*;
|
||||
pub mod buffer;
|
||||
pub use buffer::*;
|
||||
pub mod adapter;
|
||||
|
@ -156,6 +156,11 @@ pub trait MachInst: Clone + Debug {
|
|||
/// Returns true if the instruction is an epilogue placeholder.
|
||||
fn is_epilogue_placeholder(&self) -> bool;
|
||||
|
||||
/// Should this instruction be included in the clobber-set?
|
||||
fn is_included_in_clobbers(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Generate a move.
|
||||
fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Self;
|
||||
|
||||
|
@ -272,12 +277,23 @@ pub enum MachTerminator<'a> {
|
|||
pub trait MachInstEmit: MachInst {
|
||||
/// Persistent state carried across `emit` invocations.
|
||||
type State: MachInstEmitState<Self>;
|
||||
/// Constant information used in `emit` invocations.
|
||||
type Info: MachInstEmitInfo;
|
||||
/// Unwind info generator.
|
||||
type UnwindInfo: UnwindInfoGenerator<Self>;
|
||||
/// Emit the instruction.
|
||||
fn emit(&self, code: &mut MachBuffer<Self>, flags: &Flags, state: &mut Self::State);
|
||||
fn emit(&self, code: &mut MachBuffer<Self>, info: &Self::Info, state: &mut Self::State);
|
||||
/// Pretty-print the instruction.
|
||||
fn pretty_print(&self, mb_rru: Option<&RealRegUniverse>, state: &mut Self::State) -> String;
|
||||
}
|
||||
|
||||
/// Constant information used to emit an instruction.
|
||||
pub trait MachInstEmitInfo {
|
||||
/// Return the target-independent settings used for the compilation of this
|
||||
/// particular function.
|
||||
fn flags(&self) -> &Flags;
|
||||
}
|
||||
|
||||
/// A trait describing the emission state carried between MachInsts when
|
||||
/// emitting a function body.
|
||||
pub trait MachInstEmitState<I: MachInst>: Default + Clone + Debug {
|
||||
|
@ -286,6 +302,9 @@ pub trait MachInstEmitState<I: MachInst>: Default + Clone + Debug {
|
|||
/// Update the emission state before emitting an instruction that is a
|
||||
/// safepoint.
|
||||
fn pre_safepoint(&mut self, _stack_map: StackMap) {}
|
||||
/// Update the emission state to indicate instructions are associated with a
|
||||
/// particular SourceLoc.
|
||||
fn pre_sourceloc(&mut self, _srcloc: SourceLoc) {}
|
||||
}
|
||||
|
||||
/// The result of a `MachBackend::compile_function()` call. Contains machine
|
||||
|
@ -297,6 +316,8 @@ pub struct MachCompileResult {
|
|||
pub frame_size: u32,
|
||||
/// Disassembly, if requested.
|
||||
pub disasm: Option<String>,
|
||||
/// Unwind info.
|
||||
pub unwind_info: Option<unwind_input::UnwindInfo<Reg>>,
|
||||
}
|
||||
|
||||
impl MachCompileResult {
|
||||
|
@ -341,4 +362,60 @@ pub trait MachBackend {
|
|||
/// Machine-specific condcode info needed by TargetIsa.
|
||||
/// Condition that will be true when an IsubIfcout overflows.
|
||||
fn unsigned_sub_overflow_condition(&self) -> IntCC;
|
||||
|
||||
/// Produces unwind info based on backend results.
|
||||
#[cfg(feature = "unwind")]
|
||||
fn emit_unwind_info(
|
||||
&self,
|
||||
_result: &MachCompileResult,
|
||||
_kind: UnwindInfoKind,
|
||||
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
|
||||
// By default, an backend cannot produce unwind info.
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Machine-specific condcode info needed by TargetIsa.
|
||||
/// Creates a new System V Common Information Entry for the ISA.
|
||||
#[cfg(feature = "unwind")]
|
||||
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
|
||||
// By default, an ISA cannot create a System V CIE
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Expected unwind info type.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub enum UnwindInfoKind {
|
||||
/// No unwind info.
|
||||
None,
|
||||
/// SystemV CIE/FDE unwind info.
|
||||
#[cfg(feature = "unwind")]
|
||||
SystemV,
|
||||
/// Windows X64 Unwind info
|
||||
#[cfg(feature = "unwind")]
|
||||
Windows,
|
||||
}
|
||||
|
||||
/// Input data for UnwindInfoGenerator.
|
||||
pub struct UnwindInfoContext<'a, Inst: MachInstEmit> {
|
||||
/// Function instructions.
|
||||
pub insts: &'a [Inst],
|
||||
/// Instruction layout: end offsets
|
||||
pub insts_layout: &'a [CodeOffset],
|
||||
/// Length of the function.
|
||||
pub len: CodeOffset,
|
||||
/// Prologue range.
|
||||
pub prologue: Range<u32>,
|
||||
/// Epilogue ranges.
|
||||
pub epilogues: &'a [Range<u32>],
|
||||
}
|
||||
|
||||
/// UnwindInfo generator/helper.
|
||||
pub trait UnwindInfoGenerator<I: MachInstEmit> {
|
||||
/// Creates unwind info based on function signature and
|
||||
/// emitted instructions.
|
||||
fn create_unwind_info(
|
||||
context: UnwindInfoContext<I>,
|
||||
) -> CodegenResult<Option<unwind_input::UnwindInfo<Reg>>>;
|
||||
}
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
//! Pretty-printing for machine code (virtual-registerized or final).
|
||||
|
||||
use regalloc::{RealRegUniverse, Reg, Writable};
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::string::{String, ToString};
|
||||
|
||||
// FIXME: Should this go into regalloc.rs instead?
|
||||
|
||||
/// A trait for printing instruction bits and pieces, with the the ability to
|
||||
/// take a contextualising RealRegUniverse that is used to give proper names to
|
||||
/// registers.
|
||||
pub trait ShowWithRRU {
|
||||
/// Return a string that shows the implementing object in context of the
|
||||
/// given `RealRegUniverse`, if provided.
|
||||
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String;
|
||||
|
||||
/// The same as |show_rru|, but with an optional hint giving a size in
|
||||
/// bytes. Its interpretation is object-dependent, and it is intended to
|
||||
/// pass around enough information to facilitate printing sub-parts of
|
||||
/// real registers correctly. Objects may ignore size hints that are
|
||||
/// irrelevant to them.
|
||||
fn show_rru_sized(&self, mb_rru: Option<&RealRegUniverse>, _size: u8) -> String {
|
||||
// Default implementation is to ignore the hint.
|
||||
self.show_rru(mb_rru)
|
||||
}
|
||||
}
|
||||
|
||||
impl ShowWithRRU for Reg {
|
||||
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
if self.is_real() {
|
||||
if let Some(rru) = mb_rru {
|
||||
let reg_ix = self.get_index();
|
||||
if reg_ix < rru.regs.len() {
|
||||
return rru.regs[reg_ix].1.to_string();
|
||||
} else {
|
||||
// We have a real reg which isn't listed in the universe.
|
||||
// Per the regalloc.rs interface requirements, this is
|
||||
// Totally Not Allowed. Print it generically anyway, so
|
||||
// we have something to debug.
|
||||
return format!("!!{:?}!!", self);
|
||||
}
|
||||
}
|
||||
}
|
||||
// The reg is virtual, or we have no universe. Be generic.
|
||||
format!("%{:?}", self)
|
||||
}
|
||||
|
||||
fn show_rru_sized(&self, _mb_rru: Option<&RealRegUniverse>, _size: u8) -> String {
|
||||
// For the specific case of Reg, we demand not to have a size hint,
|
||||
// since interpretation of the size is target specific, but this code
|
||||
// is used by all targets.
|
||||
panic!("Reg::show_rru_sized: impossible to implement");
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: ShowWithRRU + Copy + Ord + Hash + Eq + Debug> ShowWithRRU for Writable<R> {
|
||||
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
self.to_reg().show_rru(mb_rru)
|
||||
}
|
||||
|
||||
fn show_rru_sized(&self, mb_rru: Option<&RealRegUniverse>, size: u8) -> String {
|
||||
self.to_reg().show_rru_sized(mb_rru, size)
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@
|
|||
//! See the main module comment in `mod.rs` for more details on the VCode-based
|
||||
//! backend pipeline.
|
||||
|
||||
use crate::ir::{self, types, SourceLoc};
|
||||
use crate::ir::{self, types, Constant, ConstantData, SourceLoc};
|
||||
use crate::machinst::*;
|
||||
use crate::settings;
|
||||
use crate::timing;
|
||||
|
@ -25,12 +25,15 @@ use crate::timing;
|
|||
use regalloc::Function as RegallocFunction;
|
||||
use regalloc::Set as RegallocSet;
|
||||
use regalloc::{
|
||||
BlockIx, InstIx, Range, RegAllocResult, RegClass, RegUsageCollector, RegUsageMapper, SpillSlot,
|
||||
StackmapRequestInfo,
|
||||
BlockIx, InstIx, PrettyPrint, Range, RegAllocResult, RegClass, RegUsageCollector,
|
||||
RegUsageMapper, SpillSlot, StackmapRequestInfo,
|
||||
};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::{borrow::Cow, vec::Vec};
|
||||
use cranelift_entity::{entity_impl, Keys, PrimaryMap};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::iter;
|
||||
use std::string::String;
|
||||
|
@ -39,6 +42,8 @@ use std::string::String;
|
|||
pub type InsnIndex = u32;
|
||||
/// Index referring to a basic block in VCode.
|
||||
pub type BlockIndex = u32;
|
||||
/// Range of an instructions in VCode.
|
||||
pub type InsnRange = core::ops::Range<InsnIndex>;
|
||||
|
||||
/// VCodeInst wraps all requirements for a MachInst to be in VCode: it must be
|
||||
/// a `MachInst` and it must be able to emit itself at least to a `SizeCodeSink`.
|
||||
|
@ -88,6 +93,10 @@ pub struct VCode<I: VCodeInst> {
|
|||
/// ABI object.
|
||||
abi: Box<dyn ABICallee<I = I>>,
|
||||
|
||||
/// Constant information used during code emission. This should be
|
||||
/// immutable across function compilations within the same module.
|
||||
emit_info: I::Info,
|
||||
|
||||
/// Safepoint instruction indices. Filled in post-regalloc. (Prior to
|
||||
/// regalloc, the safepoint instructions are listed in the separate
|
||||
/// `StackmapRequestInfo` held separate from the `VCode`.)
|
||||
|
@ -97,6 +106,15 @@ pub struct VCode<I: VCodeInst> {
|
|||
/// These are used to generate actual stack maps at emission. Filled in
|
||||
/// post-regalloc.
|
||||
safepoint_slots: Vec<Vec<SpillSlot>>,
|
||||
|
||||
/// Ranges for prologue and epilogue instructions.
|
||||
prologue_epilogue_ranges: Option<(InsnRange, Box<[InsnRange]>)>,
|
||||
|
||||
/// Instruction end offsets
|
||||
insts_layout: RefCell<(Vec<u32>, u32)>,
|
||||
|
||||
/// Constants.
|
||||
constants: VCodeConstants,
|
||||
}
|
||||
|
||||
/// A builder for a VCode function body. This builder is designed for the
|
||||
|
@ -132,9 +150,14 @@ pub struct VCodeBuilder<I: VCodeInst> {
|
|||
|
||||
impl<I: VCodeInst> VCodeBuilder<I> {
|
||||
/// Create a new VCodeBuilder.
|
||||
pub fn new(abi: Box<dyn ABICallee<I = I>>, block_order: BlockLoweringOrder) -> VCodeBuilder<I> {
|
||||
pub fn new(
|
||||
abi: Box<dyn ABICallee<I = I>>,
|
||||
emit_info: I::Info,
|
||||
block_order: BlockLoweringOrder,
|
||||
constants: VCodeConstants,
|
||||
) -> VCodeBuilder<I> {
|
||||
let reftype_class = I::ref_type_regclass(abi.flags());
|
||||
let vcode = VCode::new(abi, block_order);
|
||||
let vcode = VCode::new(abi, emit_info, block_order, constants);
|
||||
let stack_map_info = StackmapRequestInfo {
|
||||
reftype_class,
|
||||
reftyped_vregs: vec![],
|
||||
|
@ -238,6 +261,11 @@ impl<I: VCodeInst> VCodeBuilder<I> {
|
|||
self.cur_srcloc = srcloc;
|
||||
}
|
||||
|
||||
/// Access the constants.
|
||||
pub fn constants(&mut self) -> &mut VCodeConstants {
|
||||
&mut self.vcode.constants
|
||||
}
|
||||
|
||||
/// Build the final VCode, returning the vcode itself as well as auxiliary
|
||||
/// information, such as the stack map request information.
|
||||
pub fn build(self) -> (VCode<I>, StackmapRequestInfo) {
|
||||
|
@ -263,7 +291,12 @@ fn is_reftype(ty: Type) -> bool {
|
|||
|
||||
impl<I: VCodeInst> VCode<I> {
|
||||
/// New empty VCode.
|
||||
fn new(abi: Box<dyn ABICallee<I = I>>, block_order: BlockLoweringOrder) -> VCode<I> {
|
||||
fn new(
|
||||
abi: Box<dyn ABICallee<I = I>>,
|
||||
emit_info: I::Info,
|
||||
block_order: BlockLoweringOrder,
|
||||
constants: VCodeConstants,
|
||||
) -> VCode<I> {
|
||||
VCode {
|
||||
liveins: abi.liveins(),
|
||||
liveouts: abi.liveouts(),
|
||||
|
@ -277,8 +310,12 @@ impl<I: VCodeInst> VCode<I> {
|
|||
block_succs: vec![],
|
||||
block_order,
|
||||
abi,
|
||||
emit_info,
|
||||
safepoint_insns: vec![],
|
||||
safepoint_slots: vec![],
|
||||
prologue_epilogue_ranges: None,
|
||||
insts_layout: RefCell::new((vec![], 0)),
|
||||
constants,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -340,6 +377,10 @@ impl<I: VCodeInst> VCode<I> {
|
|||
let mut final_safepoint_insns = vec![];
|
||||
let mut safept_idx = 0;
|
||||
|
||||
let mut prologue_start = None;
|
||||
let mut prologue_end = None;
|
||||
let mut epilogue_islands = vec![];
|
||||
|
||||
assert!(result.target_map.elems().len() == self.num_blocks());
|
||||
for block in 0..self.num_blocks() {
|
||||
let start = result.target_map.elems()[block].get() as usize;
|
||||
|
@ -352,11 +393,13 @@ impl<I: VCodeInst> VCode<I> {
|
|||
let final_start = final_insns.len() as InsnIndex;
|
||||
|
||||
if block == self.entry {
|
||||
prologue_start = Some(final_insns.len() as InsnIndex);
|
||||
// Start with the prologue.
|
||||
let prologue = self.abi.gen_prologue();
|
||||
let len = prologue.len();
|
||||
final_insns.extend(prologue.into_iter());
|
||||
final_srclocs.extend(iter::repeat(SourceLoc::default()).take(len));
|
||||
prologue_end = Some(final_insns.len() as InsnIndex);
|
||||
}
|
||||
|
||||
for i in start..end {
|
||||
|
@ -382,10 +425,12 @@ impl<I: VCodeInst> VCode<I> {
|
|||
// with the epilogue.
|
||||
let is_ret = insn.is_term() == MachTerminator::Ret;
|
||||
if is_ret {
|
||||
let epilogue_start = final_insns.len() as InsnIndex;
|
||||
let epilogue = self.abi.gen_epilogue();
|
||||
let len = epilogue.len();
|
||||
final_insns.extend(epilogue.into_iter());
|
||||
final_srclocs.extend(iter::repeat(srcloc).take(len));
|
||||
epilogue_islands.push(epilogue_start..final_insns.len() as InsnIndex);
|
||||
} else {
|
||||
final_insns.push(insn.clone());
|
||||
final_srclocs.push(srcloc);
|
||||
|
@ -417,6 +462,11 @@ impl<I: VCodeInst> VCode<I> {
|
|||
// for the machine backend during emission so that it can do
|
||||
// target-specific translations of slot numbers to stack offsets.
|
||||
self.safepoint_slots = result.stackmaps;
|
||||
|
||||
self.prologue_epilogue_ranges = Some((
|
||||
prologue_start.unwrap()..prologue_end.unwrap(),
|
||||
epilogue_islands.into_boxed_slice(),
|
||||
));
|
||||
}
|
||||
|
||||
/// Emit the instructions to a `MachBuffer`, containing fixed-up code and external
|
||||
|
@ -429,9 +479,13 @@ impl<I: VCodeInst> VCode<I> {
|
|||
let mut buffer = MachBuffer::new();
|
||||
let mut state = I::State::new(&*self.abi);
|
||||
|
||||
buffer.reserve_labels_for_blocks(self.num_blocks() as BlockIndex); // first N MachLabels are simply block indices.
|
||||
// The first M MachLabels are reserved for block indices, the next N MachLabels for
|
||||
// constants.
|
||||
buffer.reserve_labels_for_blocks(self.num_blocks() as BlockIndex);
|
||||
buffer.reserve_labels_for_constants(&self.constants);
|
||||
|
||||
let mut insts_layout = vec![0; self.insts.len()];
|
||||
|
||||
let flags = self.abi.flags();
|
||||
let mut safepoint_idx = 0;
|
||||
let mut cur_srcloc = None;
|
||||
for block in 0..self.num_blocks() {
|
||||
|
@ -440,7 +494,7 @@ impl<I: VCodeInst> VCode<I> {
|
|||
while new_offset > buffer.cur_offset() {
|
||||
// Pad with NOPs up to the aligned block offset.
|
||||
let nop = I::gen_nop((new_offset - buffer.cur_offset()) as usize);
|
||||
nop.emit(&mut buffer, flags, &mut Default::default());
|
||||
nop.emit(&mut buffer, &self.emit_info, &mut Default::default());
|
||||
}
|
||||
assert_eq!(buffer.cur_offset(), new_offset);
|
||||
|
||||
|
@ -455,6 +509,7 @@ impl<I: VCodeInst> VCode<I> {
|
|||
buffer.start_srcloc(srcloc);
|
||||
cur_srcloc = Some(srcloc);
|
||||
}
|
||||
state.pre_sourceloc(cur_srcloc.unwrap_or(SourceLoc::default()));
|
||||
|
||||
if safepoint_idx < self.safepoint_insns.len()
|
||||
&& self.safepoint_insns[safepoint_idx] == iix
|
||||
|
@ -469,7 +524,9 @@ impl<I: VCodeInst> VCode<I> {
|
|||
safepoint_idx += 1;
|
||||
}
|
||||
|
||||
self.insts[iix as usize].emit(&mut buffer, flags, &mut state);
|
||||
self.insts[iix as usize].emit(&mut buffer, &self.emit_info, &mut state);
|
||||
|
||||
insts_layout[iix as usize] = buffer.cur_offset();
|
||||
}
|
||||
|
||||
if cur_srcloc.is_some() {
|
||||
|
@ -490,9 +547,33 @@ impl<I: VCodeInst> VCode<I> {
|
|||
}
|
||||
}
|
||||
|
||||
// Emit the constants used by the function.
|
||||
for (constant, data) in self.constants.iter() {
|
||||
let label = buffer.get_label_for_constant(constant);
|
||||
buffer.defer_constant(label, data.alignment(), data.as_slice(), u32::max_value());
|
||||
}
|
||||
|
||||
*self.insts_layout.borrow_mut() = (insts_layout, buffer.cur_offset());
|
||||
|
||||
buffer
|
||||
}
|
||||
|
||||
/// Generates unwind info.
|
||||
pub fn unwind_info(
|
||||
&self,
|
||||
) -> crate::result::CodegenResult<Option<crate::isa::unwind::input::UnwindInfo<Reg>>> {
|
||||
let layout = &self.insts_layout.borrow();
|
||||
let (prologue, epilogues) = self.prologue_epilogue_ranges.as_ref().unwrap();
|
||||
let context = UnwindInfoContext {
|
||||
insts: &self.insts,
|
||||
insts_layout: &layout.0,
|
||||
len: layout.1,
|
||||
prologue: prologue.clone(),
|
||||
epilogues,
|
||||
};
|
||||
I::UnwindInfo::create_unwind_info(context)
|
||||
}
|
||||
|
||||
/// Get the IR block for a BlockIndex, if one exists.
|
||||
pub fn bindex_to_bb(&self, block: BlockIndex) -> Option<ir::Block> {
|
||||
self.block_order.lowered_order()[block as usize].orig_block()
|
||||
|
@ -543,6 +624,10 @@ impl<I: VCodeInst> RegallocFunction for VCode<I> {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_included_in_clobbers(&self, insn: &I) -> bool {
|
||||
insn.is_included_in_clobbers()
|
||||
}
|
||||
|
||||
fn get_regs(insn: &I, collector: &mut RegUsageCollector) {
|
||||
insn.get_regs(collector)
|
||||
}
|
||||
|
@ -624,7 +709,7 @@ impl<I: VCodeInst> fmt::Debug for VCode<I> {
|
|||
}
|
||||
|
||||
/// Pretty-printing with `RealRegUniverse` context.
|
||||
impl<I: VCodeInst> ShowWithRRU for VCode<I> {
|
||||
impl<I: VCodeInst> PrettyPrint for VCode<I> {
|
||||
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
|
||||
use std::fmt::Write;
|
||||
|
||||
|
@ -673,3 +758,138 @@ impl<I: VCodeInst> ShowWithRRU for VCode<I> {
|
|||
s
|
||||
}
|
||||
}
|
||||
|
||||
/// This structure tracks the large constants used in VCode that will be emitted separately by the
|
||||
/// [MachBuffer].
|
||||
///
|
||||
/// First, during the lowering phase, constants are inserted using
|
||||
/// [VCodeConstants.insert]; an intermediate handle, [VCodeConstant], tracks what constants are
|
||||
/// used in this phase. Some deduplication is performed, when possible, as constant
|
||||
/// values are inserted.
|
||||
///
|
||||
/// Secondly, during the emission phase, the [MachBuffer] assigns [MachLabel]s for each of the
|
||||
/// constants so that instructions can refer to the value's memory location. The [MachBuffer]
|
||||
/// then writes the constant values to the buffer.
|
||||
#[derive(Default)]
|
||||
pub struct VCodeConstants {
|
||||
constants: PrimaryMap<VCodeConstant, VCodeConstantData>,
|
||||
pool_uses: HashMap<Constant, VCodeConstant>,
|
||||
well_known_uses: HashMap<*const [u8], VCodeConstant>,
|
||||
}
|
||||
impl VCodeConstants {
|
||||
/// Initialize the structure with the expected number of constants.
|
||||
pub fn with_capacity(expected_num_constants: usize) -> Self {
|
||||
Self {
|
||||
constants: PrimaryMap::with_capacity(expected_num_constants),
|
||||
pool_uses: HashMap::with_capacity(expected_num_constants),
|
||||
well_known_uses: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a constant; using this method indicates that a constant value will be used and thus
|
||||
/// will be emitted to the `MachBuffer`. The current implementation can deduplicate constants
|
||||
/// that are [VCodeConstantData::Pool] or [VCodeConstantData::WellKnown] but not
|
||||
/// [VCodeConstantData::Generated].
|
||||
pub fn insert(&mut self, data: VCodeConstantData) -> VCodeConstant {
|
||||
match data {
|
||||
VCodeConstantData::Generated(_) => self.constants.push(data),
|
||||
VCodeConstantData::Pool(constant, _) => match self.pool_uses.get(&constant) {
|
||||
None => {
|
||||
let vcode_constant = self.constants.push(data);
|
||||
self.pool_uses.insert(constant, vcode_constant);
|
||||
vcode_constant
|
||||
}
|
||||
Some(&vcode_constant) => vcode_constant,
|
||||
},
|
||||
VCodeConstantData::WellKnown(data_ref) => {
|
||||
match self.well_known_uses.get(&(data_ref as *const [u8])) {
|
||||
None => {
|
||||
let vcode_constant = self.constants.push(data);
|
||||
self.well_known_uses
|
||||
.insert(data_ref as *const [u8], vcode_constant);
|
||||
vcode_constant
|
||||
}
|
||||
Some(&vcode_constant) => vcode_constant,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve a byte slice for the given [VCodeConstant], if available.
|
||||
pub fn get(&self, constant: VCodeConstant) -> Option<&[u8]> {
|
||||
self.constants.get(constant).map(|d| d.as_slice())
|
||||
}
|
||||
|
||||
/// Return the number of constants inserted.
|
||||
pub fn len(&self) -> usize {
|
||||
self.constants.len()
|
||||
}
|
||||
|
||||
/// Iterate over the [VCodeConstant] keys inserted in this structure.
|
||||
pub fn keys(&self) -> Keys<VCodeConstant> {
|
||||
self.constants.keys()
|
||||
}
|
||||
|
||||
/// Iterate over the [VCodeConstant] keys and the data (as a byte slice) inserted in this
|
||||
/// structure.
|
||||
pub fn iter(&self) -> impl Iterator<Item = (VCodeConstant, &VCodeConstantData)> {
|
||||
self.constants.iter()
|
||||
}
|
||||
}
|
||||
|
||||
/// A use of a constant by one or more VCode instructions; see [VCodeConstants].
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct VCodeConstant(u32);
|
||||
entity_impl!(VCodeConstant);
|
||||
|
||||
/// Identify the different types of constant that can be inserted into [VCodeConstants]. Tracking
|
||||
/// these separately instead of as raw byte buffers allows us to avoid some duplication.
|
||||
pub enum VCodeConstantData {
|
||||
/// A constant already present in the Cranelift IR
|
||||
/// [ConstantPool](crate::ir::constant::ConstantPool).
|
||||
Pool(Constant, ConstantData),
|
||||
/// A reference to a well-known constant value that is statically encoded within the compiler.
|
||||
WellKnown(&'static [u8]),
|
||||
/// A constant value generated during lowering; the value may depend on the instruction context
|
||||
/// which makes it difficult to de-duplicate--if possible, use other variants.
|
||||
Generated(ConstantData),
|
||||
}
|
||||
impl VCodeConstantData {
|
||||
/// Retrieve the constant data as a byte slice.
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
match self {
|
||||
VCodeConstantData::Pool(_, d) | VCodeConstantData::Generated(d) => d.as_slice(),
|
||||
VCodeConstantData::WellKnown(d) => d,
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the alignment of the constant data.
|
||||
pub fn alignment(&self) -> u32 {
|
||||
if self.as_slice().len() <= 8 {
|
||||
8
|
||||
} else {
|
||||
16
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::mem::size_of;
|
||||
|
||||
#[test]
|
||||
fn size_of_constant_structs() {
|
||||
assert_eq!(size_of::<Constant>(), 4);
|
||||
assert_eq!(size_of::<VCodeConstant>(), 4);
|
||||
assert_eq!(size_of::<ConstantData>(), 24);
|
||||
assert_eq!(size_of::<VCodeConstantData>(), 32);
|
||||
assert_eq!(
|
||||
size_of::<PrimaryMap<VCodeConstant, VCodeConstantData>>(),
|
||||
24
|
||||
);
|
||||
// TODO The VCodeConstants structure's memory size could be further optimized.
|
||||
// With certain versions of Rust, each `HashMap` in `VCodeConstants` occupied at
|
||||
// least 48 bytes, making an empty `VCodeConstants` cost 120 bytes.
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,14 +14,13 @@ use peepmatic_runtime::{
|
|||
cc::ConditionCode,
|
||||
instruction_set::InstructionSet,
|
||||
part::{Constant, Part},
|
||||
paths::Path,
|
||||
r#type::{BitWidth, Kind, Type},
|
||||
PeepholeOptimizations, PeepholeOptimizer,
|
||||
};
|
||||
use peepmatic_traits::TypingRules;
|
||||
use std::borrow::Cow;
|
||||
use std::boxed::Box;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::iter;
|
||||
use std::ptr;
|
||||
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||
|
||||
|
@ -573,35 +572,6 @@ fn intcc_to_peepmatic(cc: IntCC) -> ConditionCode {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_immediate(dfg: &DataFlowGraph, inst: Inst, i: usize) -> Part<ValueOrInst> {
|
||||
return match dfg[inst] {
|
||||
InstructionData::BinaryImm64 { imm, .. } if i == 0 => imm.into(),
|
||||
InstructionData::BranchIcmp { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(),
|
||||
InstructionData::BranchInt { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(),
|
||||
InstructionData::IntCompare { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(),
|
||||
InstructionData::IntCompareImm { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(),
|
||||
InstructionData::IntCompareImm { imm, .. } if i == 1 => imm.into(),
|
||||
InstructionData::IntCond { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(),
|
||||
InstructionData::IntCondTrap { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(),
|
||||
InstructionData::IntSelect { cond, .. } if i == 0 => intcc_to_peepmatic(cond).into(),
|
||||
InstructionData::UnaryBool { imm, .. } if i == 0 => {
|
||||
Constant::Bool(imm, BitWidth::Polymorphic).into()
|
||||
}
|
||||
InstructionData::UnaryImm { imm, .. } if i == 0 => imm.into(),
|
||||
ref otherwise => unsupported(otherwise),
|
||||
};
|
||||
|
||||
#[inline(never)]
|
||||
#[cold]
|
||||
fn unsupported(data: &InstructionData) -> ! {
|
||||
panic!("unsupported instruction data: {:?}", data)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_argument(dfg: &DataFlowGraph, inst: Inst, i: usize) -> Option<Value> {
|
||||
dfg.inst_args(inst).get(i).copied()
|
||||
}
|
||||
|
||||
fn peepmatic_ty_to_ir_ty(ty: Type, dfg: &DataFlowGraph, root: Inst) -> types::Type {
|
||||
match (ty.kind, bit_width(dfg, ty.bit_width, root)) {
|
||||
(Kind::Int, 8) => types::I8,
|
||||
|
@ -681,39 +651,290 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_part_at_path(
|
||||
fn operator<E>(
|
||||
&self,
|
||||
pos: &mut FuncCursor<'b>,
|
||||
root: ValueOrInst,
|
||||
path: Path,
|
||||
) -> Option<Part<ValueOrInst>> {
|
||||
// The root is path [0].
|
||||
debug_assert!(!path.0.is_empty());
|
||||
debug_assert_eq!(path.0[0], 0);
|
||||
|
||||
let mut part = Part::Instruction(root);
|
||||
for p in path.0[1..].iter().copied() {
|
||||
let inst = part.as_instruction()?.resolve_inst(&pos.func.dfg)?;
|
||||
let operator = pos.func.dfg[inst].opcode();
|
||||
|
||||
if p < operator.immediates_arity() {
|
||||
part = get_immediate(&pos.func.dfg, inst, p as usize);
|
||||
continue;
|
||||
value_or_inst: ValueOrInst,
|
||||
operands: &mut E,
|
||||
) -> Option<Opcode>
|
||||
where
|
||||
E: Extend<Part<Self::Instruction>>,
|
||||
{
|
||||
let inst = value_or_inst.resolve_inst(&pos.func.dfg)?;
|
||||
Some(match pos.func.dfg[inst] {
|
||||
InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Band,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Bor,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Bxor,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Iadd,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Ifcmp,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Imul,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Ishl,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Isub,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Rotl,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Rotr,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Sdiv,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Srem,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Sshr,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Udiv,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Urem,
|
||||
args,
|
||||
}
|
||||
| InstructionData::Binary {
|
||||
opcode: opcode @ Opcode::Ushr,
|
||||
args,
|
||||
} => {
|
||||
operands.extend(args.iter().map(|v| Part::Instruction((*v).into())));
|
||||
opcode
|
||||
}
|
||||
|
||||
let arg = p - operator.immediates_arity();
|
||||
let arg = arg as usize;
|
||||
let value = get_argument(&pos.func.dfg, inst, arg)?;
|
||||
part = Part::Instruction(value.into());
|
||||
}
|
||||
InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::BandImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::BorImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::BxorImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::IaddImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::IfcmpImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::ImulImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::IrsubImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::IshlImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::RotlImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::RotrImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::SdivImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::SremImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::SshrImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::UdivImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::UremImm,
|
||||
imm,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::BinaryImm64 {
|
||||
opcode: opcode @ Opcode::UshrImm,
|
||||
imm,
|
||||
arg,
|
||||
} => {
|
||||
operands.extend(
|
||||
iter::once(imm.into()).chain(iter::once(Part::Instruction(arg.into()))),
|
||||
);
|
||||
opcode
|
||||
}
|
||||
|
||||
log::trace!("get_part_at_path({:?}) = {:?}", path, part);
|
||||
Some(part)
|
||||
}
|
||||
InstructionData::Branch {
|
||||
opcode: opcode @ Opcode::Brnz,
|
||||
ref args,
|
||||
destination: _,
|
||||
}
|
||||
| InstructionData::Branch {
|
||||
opcode: opcode @ Opcode::Brz,
|
||||
ref args,
|
||||
destination: _,
|
||||
} => {
|
||||
operands.extend(
|
||||
args.as_slice(&pos.func.dfg.value_lists)
|
||||
.iter()
|
||||
.map(|v| Part::Instruction((*v).into()))
|
||||
// NB: Peepmatic only knows about the condition, not any
|
||||
// of the arguments to the block, which are special
|
||||
// cased elsewhere, if/when we actually replace the
|
||||
// instruction.
|
||||
.take(1),
|
||||
);
|
||||
opcode
|
||||
}
|
||||
|
||||
fn operator(&self, pos: &mut FuncCursor<'b>, value_or_inst: ValueOrInst) -> Option<Opcode> {
|
||||
let inst = value_or_inst.resolve_inst(&pos.func.dfg)?;
|
||||
Some(pos.func.dfg[inst].opcode())
|
||||
InstructionData::CondTrap {
|
||||
opcode: opcode @ Opcode::Trapnz,
|
||||
arg,
|
||||
code: _,
|
||||
}
|
||||
| InstructionData::CondTrap {
|
||||
opcode: opcode @ Opcode::Trapz,
|
||||
arg,
|
||||
code: _,
|
||||
} => {
|
||||
operands.extend(iter::once(Part::Instruction(arg.into())));
|
||||
opcode
|
||||
}
|
||||
|
||||
InstructionData::IntCompare {
|
||||
opcode: opcode @ Opcode::Icmp,
|
||||
cond,
|
||||
args,
|
||||
} => {
|
||||
operands.extend(
|
||||
iter::once(intcc_to_peepmatic(cond).into())
|
||||
.chain(args.iter().map(|v| Part::Instruction((*v).into()))),
|
||||
);
|
||||
opcode
|
||||
}
|
||||
|
||||
InstructionData::IntCompareImm {
|
||||
opcode: opcode @ Opcode::IcmpImm,
|
||||
cond,
|
||||
imm,
|
||||
arg,
|
||||
} => {
|
||||
operands.extend(
|
||||
iter::once(intcc_to_peepmatic(cond).into())
|
||||
.chain(iter::once(Part::Constant(imm.into())))
|
||||
.chain(iter::once(Part::Instruction(arg.into()))),
|
||||
);
|
||||
opcode
|
||||
}
|
||||
|
||||
InstructionData::Ternary {
|
||||
opcode: opcode @ Opcode::Select,
|
||||
ref args,
|
||||
} => {
|
||||
operands.extend(args.iter().map(|v| Part::Instruction((*v).into())));
|
||||
opcode
|
||||
}
|
||||
|
||||
InstructionData::Unary {
|
||||
opcode: opcode @ Opcode::AdjustSpDown,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::Unary {
|
||||
opcode: opcode @ Opcode::Bint,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::Unary {
|
||||
opcode: opcode @ Opcode::Ireduce,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::Unary {
|
||||
opcode: opcode @ Opcode::Sextend,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::Unary {
|
||||
opcode: opcode @ Opcode::Uextend,
|
||||
arg,
|
||||
} => {
|
||||
operands.extend(iter::once(Part::Instruction(arg.into())));
|
||||
opcode
|
||||
}
|
||||
|
||||
InstructionData::UnaryBool { opcode, imm } => {
|
||||
operands.extend(iter::once(Part::Constant(Constant::Bool(
|
||||
imm,
|
||||
BitWidth::Polymorphic,
|
||||
))));
|
||||
opcode
|
||||
}
|
||||
|
||||
InstructionData::UnaryImm {
|
||||
opcode: opcode @ Opcode::AdjustSpDownImm,
|
||||
imm,
|
||||
}
|
||||
| InstructionData::UnaryImm {
|
||||
opcode: opcode @ Opcode::Iconst,
|
||||
imm,
|
||||
} => {
|
||||
operands.extend(iter::once(imm.into()));
|
||||
opcode
|
||||
}
|
||||
ref otherwise => {
|
||||
log::trace!("Not supported by Peepmatic: {:?}", otherwise);
|
||||
return None;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn make_inst_1(
|
||||
|
|
Двоичный файл не отображается.
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"6caa0a14fad8486a13273989457e57d6180be91eb57ded8977f67a784d982f4a","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"96ceffbfd88fb06e3b41aa4d3087cffbbf8441d04eba7ab09662a72ab600a321","src/boxed_slice.rs":"69d539b72460c0aba1d30e0b72efb0c29d61558574d751c784794e14abf41352","src/iter.rs":"61fefdc49cafad4cacba5f5a7ad2396a23160642c688a7f0b0734277391847cd","src/keys.rs":"b8c2fba26dee15bf3d1880bb2b41e8d66fe1428d242ee6d9fd30ee94bbd0407d","src/lib.rs":"72aca3bf830dce85a8b5f2325b589810ca06ae09e8d2daf137524ad6e6737bbe","src/list.rs":"4bf609eb7cc7c000c18da746596d5fcc67eece3f919ee2d76e19f6ac371640d1","src/map.rs":"e5ce79a7536dc147092be4965785b55e24b11356554be57afab38a7a93f47f4e","src/packed_option.rs":"d931ba5ce07a5c77c8a62bb07316db21c101bc3fa1eb6ffd396f8a8944958185","src/primary.rs":"20fe2c1b9645606c5fd5d416225f1e6a4bea17ee7de73ef5492c113263a29dd6","src/set.rs":"b040054b8baa0599e64df9ee841640688e2a73b6eabbdc5a4f15334412db052a","src/sparse.rs":"536e31fdcf64450526f5e5b85e97406c26b998bc7e0d8161b6b449c24265449f"},"package":null}
|
||||
{"files":{"Cargo.toml":"e2b299fe4d2287845ae2843e10b5e9eac1dab170dc7bddc25fe461ba0868ff69","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"96ceffbfd88fb06e3b41aa4d3087cffbbf8441d04eba7ab09662a72ab600a321","src/boxed_slice.rs":"69d539b72460c0aba1d30e0b72efb0c29d61558574d751c784794e14abf41352","src/iter.rs":"61fefdc49cafad4cacba5f5a7ad2396a23160642c688a7f0b0734277391847cd","src/keys.rs":"b8c2fba26dee15bf3d1880bb2b41e8d66fe1428d242ee6d9fd30ee94bbd0407d","src/lib.rs":"6c59d159948c2a0787d882e9278c269b5a87f073fdcb372df852a1dd2ba20695","src/list.rs":"4bf609eb7cc7c000c18da746596d5fcc67eece3f919ee2d76e19f6ac371640d1","src/map.rs":"e5ce79a7536dc147092be4965785b55e24b11356554be57afab38a7a93f47f4e","src/packed_option.rs":"d931ba5ce07a5c77c8a62bb07316db21c101bc3fa1eb6ffd396f8a8944958185","src/primary.rs":"20fe2c1b9645606c5fd5d416225f1e6a4bea17ee7de73ef5492c113263a29dd6","src/set.rs":"b040054b8baa0599e64df9ee841640688e2a73b6eabbdc5a4f15334412db052a","src/sparse.rs":"536e31fdcf64450526f5e5b85e97406c26b998bc7e0d8161b6b449c24265449f"},"package":null}
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
authors = ["The Cranelift Project Developers"]
|
||||
name = "cranelift-entity"
|
||||
version = "0.67.0"
|
||||
version = "0.68.0"
|
||||
description = "Data structures using entity references as mapping keys"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
documentation = "https://docs.rs/cranelift-entity"
|
||||
|
|
|
@ -39,9 +39,8 @@
|
|||
clippy::float_arithmetic,
|
||||
clippy::mut_mut,
|
||||
clippy::nonminimal_bool,
|
||||
clippy::option_map_unwrap_or,
|
||||
clippy::option_map_unwrap_or_else,
|
||||
clippy::print_stdout,
|
||||
clippy::map_unwrap_or,
|
||||
clippy::clippy::print_stdout,
|
||||
clippy::unicode_not_nfc,
|
||||
clippy::use_self
|
||||
)
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"c58c988dd6e749bd15cbae1115575fdf4f43bdf952bd3c90bbb28d805759984a","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"dea43e8044284df50f8b8772e9b48ba8b109b45c74111ff73619775d57ad8d67","src/frontend.rs":"53e108a04ead6890ee78f674b9ed6862443c66dd85ec214e4c27e0da3f67228e","src/lib.rs":"5197f467d1625ee2b117a168f4b1886b4b69d4250faea6618360a5adc70b4e0c","src/ssa.rs":"650d26025706cfb63935f956bca6f166b0edfa32260cd2a8c27f9b49fcc743c3","src/switch.rs":"73f9058a899d2b19ed7135e028cc6005c6e3016f8530619519eac9627cb1383e","src/variable.rs":"399437bd7d2ac11a7a748bad7dd1f6dac58824d374ec318f36367a9d077cc225"},"package":null}
|
||||
{"files":{"Cargo.toml":"efedd48411232c30fe72899fca348ca08a8cc83b373163376ee1780994608d8f","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"dea43e8044284df50f8b8772e9b48ba8b109b45c74111ff73619775d57ad8d67","src/frontend.rs":"53e108a04ead6890ee78f674b9ed6862443c66dd85ec214e4c27e0da3f67228e","src/lib.rs":"e757197479cc26732f5f872ffe40f60fdc376c748e1c16e9b42976fef51a2161","src/ssa.rs":"650d26025706cfb63935f956bca6f166b0edfa32260cd2a8c27f9b49fcc743c3","src/switch.rs":"73f9058a899d2b19ed7135e028cc6005c6e3016f8530619519eac9627cb1383e","src/variable.rs":"399437bd7d2ac11a7a748bad7dd1f6dac58824d374ec318f36367a9d077cc225"},"package":null}
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
authors = ["The Cranelift Project Developers"]
|
||||
name = "cranelift-frontend"
|
||||
version = "0.67.0"
|
||||
version = "0.68.0"
|
||||
description = "Cranelift IR builder helper"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
documentation = "https://docs.rs/cranelift-frontend"
|
||||
|
@ -11,10 +11,10 @@ readme = "README.md"
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
cranelift-codegen = { path = "../codegen", version = "0.67.0", default-features = false }
|
||||
cranelift-codegen = { path = "../codegen", version = "0.68.0", default-features = false }
|
||||
target-lexicon = "0.11"
|
||||
log = { version = "0.4.6", default-features = false }
|
||||
hashbrown = { version = "0.7", optional = true }
|
||||
hashbrown = { version = "0.9.1", optional = true }
|
||||
smallvec = { version = "1.0.0" }
|
||||
|
||||
[features]
|
||||
|
|
|
@ -172,9 +172,8 @@
|
|||
clippy::float_arithmetic,
|
||||
clippy::mut_mut,
|
||||
clippy::nonminimal_bool,
|
||||
clippy::option_map_unwrap_or,
|
||||
clippy::option_map_unwrap_or_else,
|
||||
clippy::print_stdout,
|
||||
clippy::map_unwrap_or,
|
||||
clippy::clippy::print_stdout,
|
||||
clippy::unicode_not_nfc,
|
||||
clippy::use_self
|
||||
)
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"53367fadfe62cdc349a55a3460992677690300d6e568e38b8d73e2b5c7376c7d","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"c82c252fbeeaa101a0eef042b9a925eb1fa3d2b51d19481b9c22e593e6a8d772","src/code_translator.rs":"b25c4a4b002f2fab0364fd890539de76bc163e6f8a817d1a53bec271970ea903","src/environ/dummy.rs":"f780d523b7024d4fed923992bbe95ca4816e3a8770a83ea01de94d4cd1625b6e","src/environ/mod.rs":"692f35d75f125f9c071f7166252f427e4bac29401356f73307c6c36e23c667fb","src/environ/spec.rs":"ed806f95bd13e26ae14684287f547e77bdcc31c10a6a23ee45764f573ff4793f","src/func_translator.rs":"ebc7e7f872d03fc05e9c013e0eb575b0ae2828322b7194c60b8e764f2816d12e","src/lib.rs":"83fbcf3561b7255d346b6b33114f4a051f3a7133f11f1a238c0fe12ddbdd1ba7","src/module_translator.rs":"dc63d45d1917034f9d06008899143e32041e6d3475629d3c6eee3bb0195a0b3e","src/sections_translator.rs":"00012d18f1467f71959c4b68dcb2c059523011cac50f7ac869d3206ce9021475","src/state/func_state.rs":"581a5648b11fa07aef3cff0752597864c7cd44a4d44e27c50fc7349955b3fda3","src/state/mod.rs":"20014cb93615467b4d20321b52f67f66040417efcaa739a4804093bb559eed19","src/state/module_state.rs":"7ca3cb06b4481bc3ae74697fbcd437aea1d851eaa3cfe18cc013a4af43728957","src/translation_utils.rs":"1b9d3d63fc259c9c657747da750d0434f0acbc53381ca32860b04d017754eb55","tests/wasm_testsuite.rs":"5e9f8441acdafe1552b4ae79c8c27603dad2b047791b035d28354d9f29b4d4e7"},"package":null}
|
||||
{"files":{"Cargo.toml":"291b2f3b504aecfe9c0a2186c6b36f657d6ed464964adb5b8253d542ccfd6d2b","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"c82c252fbeeaa101a0eef042b9a925eb1fa3d2b51d19481b9c22e593e6a8d772","src/code_translator.rs":"ca8f47c4273e506eae4395af3427c82a95b2cc0ec3896f5f0ed65c9b9b5ed420","src/environ/dummy.rs":"e22052b6d966744658a189a6d77595d2b92e9d0bc8b6fbc1be2d68f2dbfccb28","src/environ/mod.rs":"692f35d75f125f9c071f7166252f427e4bac29401356f73307c6c36e23c667fb","src/environ/spec.rs":"f38c2f2c69d60ebd665b7991def3d06340fe5593e44b2f441d212adc756ac2d4","src/func_translator.rs":"ebc7e7f872d03fc05e9c013e0eb575b0ae2828322b7194c60b8e764f2816d12e","src/lib.rs":"fbbbe573ec5b4bd0f667c20500b2836a302d378933db56141df445e9aa8fba42","src/module_translator.rs":"9c24c44bcf866ac46ca90e22db5321080c1b507dca40fef73c4cdb0918391be6","src/sections_translator.rs":"8ce41a58daacc706f2a73c1cdb52c8838faa230413bb29a50826cf5a2c550608","src/state/func_state.rs":"0a6bcb31db482bdccf90c9260f9ea05a19e7439a24f81fd46173ed6c810cd1a7","src/state/mod.rs":"20014cb93615467b4d20321b52f67f66040417efcaa739a4804093bb559eed19","src/state/module_state.rs":"7ca3cb06b4481bc3ae74697fbcd437aea1d851eaa3cfe18cc013a4af43728957","src/translation_utils.rs":"4a70e54ce4e9040a05f4d49ca3595da4a213e865f2baa12965bef4236780681a","tests/wasm_testsuite.rs":"5e9f8441acdafe1552b4ae79c8c27603dad2b047791b035d28354d9f29b4d4e7"},"package":null}
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "cranelift-wasm"
|
||||
version = "0.67.0"
|
||||
version = "0.68.0"
|
||||
authors = ["The Cranelift Project Developers"]
|
||||
description = "Translator from WebAssembly to Cranelift IR"
|
||||
documentation = "https://docs.rs/cranelift-wasm"
|
||||
|
@ -12,21 +12,22 @@ keywords = ["webassembly", "wasm"]
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmparser = { version = "0.63.0", default-features = false }
|
||||
cranelift-codegen = { path = "../codegen", version = "0.67.0", default-features = false }
|
||||
cranelift-entity = { path = "../entity", version = "0.67.0" }
|
||||
cranelift-frontend = { path = "../frontend", version = "0.67.0", default-features = false }
|
||||
hashbrown = { version = "0.7", optional = true }
|
||||
wasmparser = { version = "0.67.0", 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 }
|
||||
hashbrown = { version = "0.9.1", optional = true }
|
||||
itertools = "0.9.0"
|
||||
log = { version = "0.4.6", default-features = false }
|
||||
serde = { version = "1.0.94", features = ["derive"], optional = true }
|
||||
smallvec = "1.0.0"
|
||||
thiserror = "1.0.4"
|
||||
|
||||
[dev-dependencies]
|
||||
wat = "1.0.23"
|
||||
target-lexicon = "0.11"
|
||||
# Enable the riscv feature for cranelift-codegen, as some tests require it
|
||||
cranelift-codegen = { path = "../codegen", version = "0.67.0", default-features = false, features = ["riscv"] }
|
||||
cranelift-codegen = { path = "../codegen", version = "0.68.0", default-features = false, features = ["riscv"] }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
|
|
|
@ -22,13 +22,62 @@
|
|||
//!
|
||||
//! That is why `translate_function_body` takes an object having the `WasmRuntime` trait as
|
||||
//! argument.
|
||||
//!
|
||||
//! There is extra complexity associated with translation of 128-bit SIMD instructions.
|
||||
//! Wasm only considers there to be a single 128-bit vector type. But CLIF's type system
|
||||
//! distinguishes different lane configurations, so considers 8X16, 16X8, 32X4 and 64X2 to be
|
||||
//! different types. The result is that, in wasm, it's perfectly OK to take the output of (eg)
|
||||
//! an `add.16x8` and use that as an operand of a `sub.32x4`, without using any cast. But when
|
||||
//! translated into CLIF, that will cause a verifier error due to the apparent type mismatch.
|
||||
//!
|
||||
//! This file works around that problem by liberally inserting `bitcast` instructions in many
|
||||
//! places -- mostly, before the use of vector values, either as arguments to CLIF instructions
|
||||
//! or as block actual parameters. These are no-op casts which nevertheless have different
|
||||
//! input and output types, and are used (mostly) to "convert" 16X8, 32X4 and 64X2-typed vectors
|
||||
//! to the "canonical" type, 8X16. Hence the functions `optionally_bitcast_vector`,
|
||||
//! `bitcast_arguments`, `pop*_with_bitcast`, `canonicalise_then_jump`,
|
||||
//! `canonicalise_then_br{z,nz}`, `is_non_canonical_v128` and `canonicalise_v128_values`.
|
||||
//! Note that the `bitcast*` functions are occasionally used to convert to some type other than
|
||||
//! 8X16, but the `canonicalise*` functions always convert to type 8X16.
|
||||
//!
|
||||
//! Be careful when adding support for new vector instructions. And when adding new jumps, even
|
||||
//! if they are apparently don't have any connection to vectors. Never generate any kind of
|
||||
//! (inter-block) jump directly. Instead use `canonicalise_then_jump` and
|
||||
//! `canonicalise_then_br{z,nz}`.
|
||||
//!
|
||||
//! The use of bitcasts is ugly and inefficient, but currently unavoidable:
|
||||
//!
|
||||
//! * they make the logic in this file fragile: miss out a bitcast for any reason, and there is
|
||||
//! the risk of the system failing in the verifier. At least for debug builds.
|
||||
//!
|
||||
//! * in the new backends, they potentially interfere with pattern matching on CLIF -- the
|
||||
//! patterns need to take into account the presence of bitcast nodes.
|
||||
//!
|
||||
//! * in the new backends, they get translated into machine-level vector-register-copy
|
||||
//! instructions, none of which are actually necessary. We then depend on the register
|
||||
//! allocator to coalesce them all out.
|
||||
//!
|
||||
//! * they increase the total number of CLIF nodes that have to be processed, hence slowing down
|
||||
//! the compilation pipeline. Also, the extra coalescing work generates a slowdown.
|
||||
//!
|
||||
//! A better solution which would avoid all four problems would be to remove the 8X16, 16X8,
|
||||
//! 32X4 and 64X2 types from CLIF and instead have a single V128 type.
|
||||
//!
|
||||
//! For further background see also:
|
||||
//! https://github.com/bytecodealliance/wasmtime/issues/1147
|
||||
//! ("Too many raw_bitcasts in SIMD code")
|
||||
//! https://github.com/bytecodealliance/cranelift/pull/1251
|
||||
//! ("Add X128 type to represent WebAssembly's V128 type")
|
||||
//! https://github.com/bytecodealliance/cranelift/pull/1236
|
||||
//! ("Relax verification to allow I8X16 to act as a default vector type")
|
||||
|
||||
use super::{hash_map, HashMap};
|
||||
use crate::environ::{FuncEnvironment, GlobalVariable, ReturnMode, WasmResult};
|
||||
use crate::state::{ControlStackFrame, ElseData, FuncTranslationState};
|
||||
use crate::translation_utils::{
|
||||
block_with_params, blocktype_params_results, f32_translation, f64_translation,
|
||||
};
|
||||
use crate::translation_utils::{FuncIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
|
||||
use crate::translation_utils::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex, TypeIndex};
|
||||
use crate::wasm_unsupported;
|
||||
use core::convert::TryInto;
|
||||
use core::{i32, u32};
|
||||
|
@ -40,6 +89,7 @@ use cranelift_codegen::ir::{
|
|||
};
|
||||
use cranelift_codegen::packed_option::ReservedValue;
|
||||
use cranelift_frontend::{FunctionBuilder, Variable};
|
||||
use smallvec::SmallVec;
|
||||
use std::cmp;
|
||||
use std::convert::TryFrom;
|
||||
use std::vec::Vec;
|
||||
|
@ -188,7 +238,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
let (params, results) = blocktype_params_results(validator, *ty)?;
|
||||
let loop_body = block_with_params(builder, params.clone(), environ)?;
|
||||
let next = block_with_params(builder, results.clone(), environ)?;
|
||||
builder.ins().jump(loop_body, state.peekn(params.len()));
|
||||
canonicalise_then_jump(builder, loop_body, state.peekn(params.len()));
|
||||
state.push_loop(loop_body, next, params.len(), results.len());
|
||||
|
||||
// Pop the initial `Block` actuals and replace them with the `Block`'s
|
||||
|
@ -213,24 +263,21 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
// up discovering an `else`, then we will allocate a block for it
|
||||
// and go back and patch the jump.
|
||||
let destination = block_with_params(builder, results.clone(), environ)?;
|
||||
let branch_inst = builder
|
||||
.ins()
|
||||
.brz(val, destination, state.peekn(params.len()));
|
||||
let branch_inst =
|
||||
canonicalise_then_brz(builder, val, destination, state.peekn(params.len()));
|
||||
(destination, ElseData::NoElse { branch_inst })
|
||||
} else {
|
||||
// The `if` type signature is not valid without an `else` block,
|
||||
// so we eagerly allocate the `else` block here.
|
||||
let destination = block_with_params(builder, results.clone(), environ)?;
|
||||
let else_block = block_with_params(builder, params.clone(), environ)?;
|
||||
builder
|
||||
.ins()
|
||||
.brz(val, else_block, state.peekn(params.len()));
|
||||
canonicalise_then_brz(builder, val, else_block, state.peekn(params.len()));
|
||||
builder.seal_block(else_block);
|
||||
(destination, ElseData::WithElse { else_block })
|
||||
};
|
||||
|
||||
let next_block = builder.create_block();
|
||||
builder.ins().jump(next_block, &[]);
|
||||
canonicalise_then_jump(builder, next_block, &[]);
|
||||
builder.seal_block(next_block); // Only predecessor is the current block.
|
||||
builder.switch_to_block(next_block);
|
||||
|
||||
|
@ -272,7 +319,11 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
debug_assert_eq!(params.len(), num_return_values);
|
||||
let else_block =
|
||||
block_with_params(builder, params.clone(), environ)?;
|
||||
builder.ins().jump(destination, state.peekn(params.len()));
|
||||
canonicalise_then_jump(
|
||||
builder,
|
||||
destination,
|
||||
state.peekn(params.len()),
|
||||
);
|
||||
state.popn(params.len());
|
||||
|
||||
builder.change_jump_destination(branch_inst, else_block);
|
||||
|
@ -280,9 +331,11 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
else_block
|
||||
}
|
||||
ElseData::WithElse { else_block } => {
|
||||
builder
|
||||
.ins()
|
||||
.jump(destination, state.peekn(num_return_values));
|
||||
canonicalise_then_jump(
|
||||
builder,
|
||||
destination,
|
||||
state.peekn(num_return_values),
|
||||
);
|
||||
state.popn(num_return_values);
|
||||
else_block
|
||||
}
|
||||
|
@ -314,9 +367,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
if !builder.is_unreachable() || !builder.is_pristine() {
|
||||
let return_count = frame.num_return_values();
|
||||
let return_args = state.peekn_mut(return_count);
|
||||
let next_block_types = builder.func.dfg.block_param_types(next_block);
|
||||
bitcast_arguments(return_args, &next_block_types, builder);
|
||||
builder.ins().jump(frame.following_code(), return_args);
|
||||
canonicalise_then_jump(builder, frame.following_code(), return_args);
|
||||
// You might expect that if we just finished an `if` block that
|
||||
// didn't have a corresponding `else` block, then we would clean
|
||||
// up our duplicate set of parameters that we pushed earlier
|
||||
|
@ -372,17 +423,8 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
};
|
||||
(return_count, frame.br_destination())
|
||||
};
|
||||
|
||||
// Bitcast any vector arguments to their default type, I8X16, before jumping.
|
||||
let destination_args = state.peekn_mut(return_count);
|
||||
let destination_types = builder.func.dfg.block_param_types(br_destination);
|
||||
bitcast_arguments(
|
||||
destination_args,
|
||||
&destination_types[..return_count],
|
||||
builder,
|
||||
);
|
||||
|
||||
builder.ins().jump(br_destination, destination_args);
|
||||
canonicalise_then_jump(builder, br_destination, destination_args);
|
||||
state.popn(return_count);
|
||||
state.reachable = false;
|
||||
}
|
||||
|
@ -462,17 +504,8 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
frame.set_branched_to_exit();
|
||||
frame.br_destination()
|
||||
};
|
||||
|
||||
// Bitcast any vector arguments to their default type, I8X16, before jumping.
|
||||
let destination_args = state.peekn_mut(return_count);
|
||||
let destination_types = builder.func.dfg.block_param_types(real_dest_block);
|
||||
bitcast_arguments(
|
||||
destination_args,
|
||||
&destination_types[..return_count],
|
||||
builder,
|
||||
);
|
||||
|
||||
builder.ins().jump(real_dest_block, destination_args);
|
||||
canonicalise_then_jump(builder, real_dest_block, destination_args);
|
||||
}
|
||||
state.popn(return_count);
|
||||
}
|
||||
|
@ -494,7 +527,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
match environ.return_mode() {
|
||||
ReturnMode::NormalReturns => builder.ins().return_(return_args),
|
||||
ReturnMode::FallthroughReturn => {
|
||||
builder.ins().jump(br_destination, return_args)
|
||||
canonicalise_then_jump(builder, br_destination, return_args)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -554,7 +587,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
builder.cursor(),
|
||||
TableIndex::from_u32(*table_index),
|
||||
table,
|
||||
SignatureIndex::from_u32(*index),
|
||||
TypeIndex::from_u32(*index),
|
||||
sigref,
|
||||
callee,
|
||||
state.peekn(num_args),
|
||||
|
@ -992,7 +1025,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
let index = FuncIndex::from_u32(*function_index);
|
||||
state.push1(environ.translate_ref_func(builder.cursor(), index)?);
|
||||
}
|
||||
Operator::MemoryAtomicWait32 { .. } | Operator::MemoryAtomicWait64 { .. } => {
|
||||
Operator::MemoryAtomicWait32 { memarg } | Operator::MemoryAtomicWait64 { memarg } => {
|
||||
// The WebAssembly MVP only supports one linear memory and
|
||||
// wasmparser will ensure that the memory indices specified are
|
||||
// zero.
|
||||
|
@ -1001,8 +1034,8 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
Operator::MemoryAtomicWait32 { .. } => I32,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let heap_index = MemoryIndex::from_u32(0);
|
||||
let heap = state.get_heap(builder.func, 0, environ)?;
|
||||
let heap_index = MemoryIndex::from_u32(memarg.memory);
|
||||
let heap = state.get_heap(builder.func, memarg.memory, environ)?;
|
||||
let timeout = state.pop1(); // 64 (fixed)
|
||||
let expected = state.pop1(); // 32 or 64 (per the `Ixx` in `IxxAtomicWait`)
|
||||
let addr = state.pop1(); // 32 (fixed)
|
||||
|
@ -1019,12 +1052,9 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
)?;
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::MemoryAtomicNotify { .. } => {
|
||||
// The WebAssembly MVP only supports one linear memory and
|
||||
// wasmparser will ensure that the memory indices specified are
|
||||
// zero.
|
||||
let heap_index = MemoryIndex::from_u32(0);
|
||||
let heap = state.get_heap(builder.func, 0, environ)?;
|
||||
Operator::MemoryAtomicNotify { memarg } => {
|
||||
let heap_index = MemoryIndex::from_u32(memarg.memory);
|
||||
let heap = state.get_heap(builder.func, memarg.memory, environ)?;
|
||||
let count = state.pop1(); // 32 (fixed)
|
||||
let addr = state.pop1(); // 32 (fixed)
|
||||
let res =
|
||||
|
@ -1233,16 +1263,23 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
builder.ins().fence();
|
||||
}
|
||||
Operator::MemoryCopy { src, dst } => {
|
||||
// The WebAssembly MVP only supports one linear memory and
|
||||
// wasmparser will ensure that the memory indices specified are
|
||||
// zero.
|
||||
assert_eq!(src, dst, "unimplemented between-memories copy");
|
||||
let heap_index = MemoryIndex::from_u32(*src);
|
||||
let heap = state.get_heap(builder.func, *src, environ)?;
|
||||
let src_index = MemoryIndex::from_u32(*src);
|
||||
let dst_index = MemoryIndex::from_u32(*dst);
|
||||
let src_heap = state.get_heap(builder.func, *src, environ)?;
|
||||
let dst_heap = state.get_heap(builder.func, *dst, environ)?;
|
||||
let len = state.pop1();
|
||||
let src = state.pop1();
|
||||
let dest = state.pop1();
|
||||
environ.translate_memory_copy(builder.cursor(), heap_index, heap, dest, src, len)?;
|
||||
let src_pos = state.pop1();
|
||||
let dst_pos = state.pop1();
|
||||
environ.translate_memory_copy(
|
||||
builder.cursor(),
|
||||
src_index,
|
||||
src_heap,
|
||||
dst_index,
|
||||
dst_heap,
|
||||
dst_pos,
|
||||
src_pos,
|
||||
len,
|
||||
)?;
|
||||
}
|
||||
Operator::MemoryFill { mem } => {
|
||||
let heap_index = MemoryIndex::from_u32(*mem);
|
||||
|
@ -1357,7 +1394,8 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
let data = value.bytes().to_vec().into();
|
||||
let handle = builder.func.dfg.constants.insert(data);
|
||||
let value = builder.ins().vconst(I8X16, handle);
|
||||
// the v128.const is typed in CLIF as a I8x16 but raw_bitcast to a different type before use
|
||||
// the v128.const is typed in CLIF as a I8x16 but raw_bitcast to a different type
|
||||
// before use
|
||||
state.push1(value)
|
||||
}
|
||||
Operator::I8x16Splat | Operator::I16x8Splat => {
|
||||
|
@ -1376,9 +1414,19 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
| Operator::V128Load16Splat { memarg }
|
||||
| Operator::V128Load32Splat { memarg }
|
||||
| Operator::V128Load64Splat { memarg } => {
|
||||
// TODO: For spec compliance, this is initially implemented as a combination of `load +
|
||||
// splat` but could be implemented eventually as a single instruction (`load_splat`).
|
||||
// See https://github.com/bytecodealliance/wasmtime/issues/1175.
|
||||
let opcode = ir::Opcode::LoadSplat;
|
||||
let result_ty = type_of(op);
|
||||
let (flags, base, offset) = prepare_load(
|
||||
memarg,
|
||||
mem_op_size(opcode, result_ty.lane_type()),
|
||||
builder,
|
||||
state,
|
||||
environ,
|
||||
)?;
|
||||
let (load, dfg) = builder.ins().Load(opcode, result_ty, flags, offset, base);
|
||||
state.push1(dfg.first_result(load))
|
||||
}
|
||||
Operator::V128Load32Zero { memarg } | Operator::V128Load64Zero { memarg } => {
|
||||
translate_load(
|
||||
memarg,
|
||||
ir::Opcode::Load,
|
||||
|
@ -1387,8 +1435,8 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
state,
|
||||
environ,
|
||||
)?;
|
||||
let splatted = builder.ins().splat(type_of(op), state.pop1());
|
||||
state.push1(splatted)
|
||||
let as_vector = builder.ins().scalar_to_vector(type_of(op), state.pop1());
|
||||
state.push1(as_vector)
|
||||
}
|
||||
Operator::I8x16ExtractLaneS { lane } | Operator::I16x8ExtractLaneS { lane } => {
|
||||
let vector = pop1_with_bitcast(state, type_of(op), builder);
|
||||
|
@ -1562,6 +1610,10 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
let bool_result = builder.ins().vall_true(a);
|
||||
state.push1(builder.ins().bint(I32, bool_result))
|
||||
}
|
||||
Operator::I8x16Bitmask | Operator::I16x8Bitmask | Operator::I32x4Bitmask => {
|
||||
let a = pop1_with_bitcast(state, type_of(op), builder);
|
||||
state.push1(builder.ins().vhigh_bits(I32, a));
|
||||
}
|
||||
Operator::I8x16Eq | Operator::I16x8Eq | Operator::I32x4Eq => {
|
||||
translate_vector_icmp(IntCC::Equal, type_of(op), builder, state)
|
||||
}
|
||||
|
@ -1637,6 +1689,14 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
|
||||
state.push1(builder.ins().fmin(a, b))
|
||||
}
|
||||
Operator::F32x4PMax | Operator::F64x2PMax => {
|
||||
let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
|
||||
state.push1(builder.ins().fmax_pseudo(a, b))
|
||||
}
|
||||
Operator::F32x4PMin | Operator::F64x2PMin => {
|
||||
let (a, b) = pop2_with_bitcast(state, type_of(op), builder);
|
||||
state.push1(builder.ins().fmin_pseudo(a, b))
|
||||
}
|
||||
Operator::F32x4Sqrt | Operator::F64x2Sqrt => {
|
||||
let a = pop1_with_bitcast(state, type_of(op), builder);
|
||||
state.push1(builder.ins().sqrt(a))
|
||||
|
@ -1714,22 +1774,29 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||
state.push1(builder.ins().uwiden_high(a))
|
||||
}
|
||||
|
||||
Operator::F32x4Ceil
|
||||
| Operator::F32x4Floor
|
||||
| Operator::F32x4Trunc
|
||||
| Operator::F32x4Nearest
|
||||
| Operator::F32x4PMin
|
||||
| Operator::F32x4PMax
|
||||
| Operator::F64x2Ceil
|
||||
| Operator::F64x2Floor
|
||||
| Operator::F64x2Trunc
|
||||
| Operator::F64x2PMin
|
||||
| Operator::F64x2PMax
|
||||
| Operator::F64x2Nearest
|
||||
| Operator::I8x16Bitmask
|
||||
| Operator::I16x8Bitmask
|
||||
| Operator::I32x4Bitmask => {
|
||||
return Err(wasm_unsupported!("proposed SIMD operator {:?}", op));
|
||||
Operator::F32x4Ceil | Operator::F64x2Ceil => {
|
||||
// This is something of a misuse of `type_of`, because that produces the return type
|
||||
// of `op`. In this case we want the arg type, but we know it's the same as the
|
||||
// return type. Same for the 3 cases below.
|
||||
let arg = pop1_with_bitcast(state, type_of(op), builder);
|
||||
state.push1(builder.ins().ceil(arg));
|
||||
}
|
||||
Operator::F32x4Floor | Operator::F64x2Floor => {
|
||||
let arg = pop1_with_bitcast(state, type_of(op), builder);
|
||||
state.push1(builder.ins().floor(arg));
|
||||
}
|
||||
Operator::F32x4Trunc | Operator::F64x2Trunc => {
|
||||
let arg = pop1_with_bitcast(state, type_of(op), builder);
|
||||
state.push1(builder.ins().trunc(arg));
|
||||
}
|
||||
Operator::F32x4Nearest | Operator::F64x2Nearest => {
|
||||
let arg = pop1_with_bitcast(state, type_of(op), builder);
|
||||
state.push1(builder.ins().nearest(arg));
|
||||
}
|
||||
|
||||
Operator::I32x4DotI16x8S => {
|
||||
let (a, b) = pop2_with_bitcast(state, I16X8, builder);
|
||||
state.push1(builder.ins().widening_pairwise_dot_product_s(a, b));
|
||||
}
|
||||
|
||||
Operator::ReturnCall { .. } | Operator::ReturnCallIndirect { .. } => {
|
||||
|
@ -2036,7 +2103,7 @@ fn mem_op_size(opcode: ir::Opcode, ty: Type) -> u32 {
|
|||
ir::Opcode::Istore8 | ir::Opcode::Sload8 | ir::Opcode::Uload8 => 1,
|
||||
ir::Opcode::Istore16 | ir::Opcode::Sload16 | ir::Opcode::Uload16 => 2,
|
||||
ir::Opcode::Istore32 | ir::Opcode::Sload32 | ir::Opcode::Uload32 => 4,
|
||||
ir::Opcode::Store | ir::Opcode::Load => ty.bytes(),
|
||||
ir::Opcode::Store | ir::Opcode::Load | ir::Opcode::LoadSplat => ty.bytes(),
|
||||
_ => panic!("unknown size of mem op for {:?}", opcode),
|
||||
}
|
||||
}
|
||||
|
@ -2313,15 +2380,10 @@ fn translate_br_if(
|
|||
) {
|
||||
let val = state.pop1();
|
||||
let (br_destination, inputs) = translate_br_if_args(relative_depth, state);
|
||||
|
||||
// Bitcast any vector arguments to their default type, I8X16, before jumping.
|
||||
let destination_types = builder.func.dfg.block_param_types(br_destination);
|
||||
bitcast_arguments(inputs, &destination_types[..inputs.len()], builder);
|
||||
|
||||
builder.ins().brnz(val, br_destination, inputs);
|
||||
canonicalise_then_brnz(builder, val, br_destination, inputs);
|
||||
|
||||
let next_block = builder.create_block();
|
||||
builder.ins().jump(next_block, &[]);
|
||||
canonicalise_then_jump(builder, next_block, &[]);
|
||||
builder.seal_block(next_block); // The only predecessor is the current block.
|
||||
builder.switch_to_block(next_block);
|
||||
}
|
||||
|
@ -2462,7 +2524,8 @@ fn type_of(operator: &Operator) -> Type {
|
|||
| Operator::I32x4MaxU
|
||||
| Operator::F32x4ConvertI32x4S
|
||||
| Operator::F32x4ConvertI32x4U
|
||||
| Operator::I32x4Bitmask => I32X4,
|
||||
| Operator::I32x4Bitmask
|
||||
| Operator::V128Load32Zero { .. } => I32X4,
|
||||
|
||||
Operator::I64x2Splat
|
||||
| Operator::V128Load64Splat { .. }
|
||||
|
@ -2474,7 +2537,8 @@ fn type_of(operator: &Operator) -> Type {
|
|||
| Operator::I64x2ShrU
|
||||
| Operator::I64x2Add
|
||||
| Operator::I64x2Sub
|
||||
| Operator::I64x2Mul => I64X2,
|
||||
| Operator::I64x2Mul
|
||||
| Operator::V128Load64Zero { .. } => I64X2,
|
||||
|
||||
Operator::F32x4Splat
|
||||
| Operator::F32x4ExtractLane { .. }
|
||||
|
@ -2494,8 +2558,14 @@ fn type_of(operator: &Operator) -> Type {
|
|||
| Operator::F32x4Div
|
||||
| Operator::F32x4Min
|
||||
| Operator::F32x4Max
|
||||
| Operator::F32x4PMin
|
||||
| Operator::F32x4PMax
|
||||
| Operator::I32x4TruncSatF32x4S
|
||||
| Operator::I32x4TruncSatF32x4U => F32X4,
|
||||
| Operator::I32x4TruncSatF32x4U
|
||||
| Operator::F32x4Ceil
|
||||
| Operator::F32x4Floor
|
||||
| Operator::F32x4Trunc
|
||||
| Operator::F32x4Nearest => F32X4,
|
||||
|
||||
Operator::F64x2Splat
|
||||
| Operator::F64x2ExtractLane { .. }
|
||||
|
@ -2514,7 +2584,13 @@ fn type_of(operator: &Operator) -> Type {
|
|||
| Operator::F64x2Mul
|
||||
| Operator::F64x2Div
|
||||
| Operator::F64x2Min
|
||||
| Operator::F64x2Max => F64X2,
|
||||
| Operator::F64x2Max
|
||||
| Operator::F64x2PMin
|
||||
| Operator::F64x2PMax
|
||||
| Operator::F64x2Ceil
|
||||
| Operator::F64x2Floor
|
||||
| Operator::F64x2Trunc
|
||||
| Operator::F64x2Nearest => F64X2,
|
||||
|
||||
_ => unimplemented!(
|
||||
"Currently only SIMD instructions are mapped to their return type; the \
|
||||
|
@ -2526,7 +2602,7 @@ fn type_of(operator: &Operator) -> Type {
|
|||
|
||||
/// Some SIMD operations only operate on I8X16 in CLIF; this will convert them to that type by
|
||||
/// adding a raw_bitcast if necessary.
|
||||
pub fn optionally_bitcast_vector(
|
||||
fn optionally_bitcast_vector(
|
||||
value: Value,
|
||||
needed_type: Type,
|
||||
builder: &mut FunctionBuilder,
|
||||
|
@ -2538,6 +2614,80 @@ pub fn optionally_bitcast_vector(
|
|||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn is_non_canonical_v128(ty: ir::Type) -> bool {
|
||||
match ty {
|
||||
B8X16 | B16X8 | B32X4 | B64X2 | I64X2 | I32X4 | I16X8 | F32X4 | F64X2 => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Cast to I8X16, any vector values in `values` that are of "non-canonical" type (meaning, not
|
||||
/// I8X16), and return them in a slice. A pre-scan is made to determine whether any casts are
|
||||
/// actually necessary, and if not, the original slice is returned. Otherwise the cast values
|
||||
/// are returned in a slice that belongs to the caller-supplied `SmallVec`.
|
||||
fn canonicalise_v128_values<'a>(
|
||||
tmp_canonicalised: &'a mut SmallVec<[ir::Value; 16]>,
|
||||
builder: &mut FunctionBuilder,
|
||||
values: &'a [ir::Value],
|
||||
) -> &'a [ir::Value] {
|
||||
debug_assert!(tmp_canonicalised.is_empty());
|
||||
// First figure out if any of the parameters need to be cast. Mostly they don't need to be.
|
||||
let any_non_canonical = values
|
||||
.iter()
|
||||
.any(|v| is_non_canonical_v128(builder.func.dfg.value_type(*v)));
|
||||
// Hopefully we take this exit most of the time, hence doing no heap allocation.
|
||||
if !any_non_canonical {
|
||||
return values;
|
||||
}
|
||||
// Otherwise we'll have to cast, and push the resulting `Value`s into `canonicalised`.
|
||||
for v in values {
|
||||
tmp_canonicalised.push(if is_non_canonical_v128(builder.func.dfg.value_type(*v)) {
|
||||
builder.ins().raw_bitcast(I8X16, *v)
|
||||
} else {
|
||||
*v
|
||||
});
|
||||
}
|
||||
tmp_canonicalised.as_slice()
|
||||
}
|
||||
|
||||
/// Generate a `jump` instruction, but first cast all 128-bit vector values to I8X16 if they
|
||||
/// don't have that type. This is done in somewhat roundabout way so as to ensure that we
|
||||
/// almost never have to do any heap allocation.
|
||||
fn canonicalise_then_jump(
|
||||
builder: &mut FunctionBuilder,
|
||||
destination: ir::Block,
|
||||
params: &[ir::Value],
|
||||
) -> ir::Inst {
|
||||
let mut tmp_canonicalised = SmallVec::<[ir::Value; 16]>::new();
|
||||
let canonicalised = canonicalise_v128_values(&mut tmp_canonicalised, builder, params);
|
||||
builder.ins().jump(destination, canonicalised)
|
||||
}
|
||||
|
||||
/// The same but for a `brz` instruction.
|
||||
fn canonicalise_then_brz(
|
||||
builder: &mut FunctionBuilder,
|
||||
cond: ir::Value,
|
||||
destination: ir::Block,
|
||||
params: &[Value],
|
||||
) -> ir::Inst {
|
||||
let mut tmp_canonicalised = SmallVec::<[ir::Value; 16]>::new();
|
||||
let canonicalised = canonicalise_v128_values(&mut tmp_canonicalised, builder, params);
|
||||
builder.ins().brz(cond, destination, canonicalised)
|
||||
}
|
||||
|
||||
/// The same but for a `brnz` instruction.
|
||||
fn canonicalise_then_brnz(
|
||||
builder: &mut FunctionBuilder,
|
||||
cond: ir::Value,
|
||||
destination: ir::Block,
|
||||
params: &[Value],
|
||||
) -> ir::Inst {
|
||||
let mut tmp_canonicalised = SmallVec::<[ir::Value; 16]>::new();
|
||||
let canonicalised = canonicalise_v128_values(&mut tmp_canonicalised, builder, params);
|
||||
builder.ins().brnz(cond, destination, canonicalised)
|
||||
}
|
||||
|
||||
/// A helper for popping and bitcasting a single value; since SIMD values can lose their type by
|
||||
/// using v128 (i.e. CLIF's I8x16) we must re-type the values using a bitcast to avoid CLIF
|
||||
/// typing issues.
|
||||
|
|
|
@ -12,7 +12,7 @@ use crate::environ::{
|
|||
use crate::func_translator::FuncTranslator;
|
||||
use crate::translation_utils::{
|
||||
DataIndex, DefinedFuncIndex, ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex,
|
||||
SignatureIndex, Table, TableIndex,
|
||||
Table, TableIndex, TypeIndex,
|
||||
};
|
||||
use core::convert::TryFrom;
|
||||
use cranelift_codegen::cursor::FuncCursor;
|
||||
|
@ -58,7 +58,7 @@ pub struct DummyModuleInfo {
|
|||
config: TargetFrontendConfig,
|
||||
|
||||
/// Signatures as provided by `declare_signature`.
|
||||
pub signatures: PrimaryMap<SignatureIndex, ir::Signature>,
|
||||
pub signatures: PrimaryMap<TypeIndex, ir::Signature>,
|
||||
|
||||
/// Module and field names of imported functions as provided by `declare_func_import`.
|
||||
pub imported_funcs: Vec<(String, String)>,
|
||||
|
@ -73,7 +73,7 @@ pub struct DummyModuleInfo {
|
|||
pub imported_memories: Vec<(String, String)>,
|
||||
|
||||
/// Functions, imported and local.
|
||||
pub functions: PrimaryMap<FuncIndex, Exportable<SignatureIndex>>,
|
||||
pub functions: PrimaryMap<FuncIndex, Exportable<TypeIndex>>,
|
||||
|
||||
/// Function bodies.
|
||||
pub function_bodies: PrimaryMap<DefinedFuncIndex, ir::Function>,
|
||||
|
@ -157,7 +157,7 @@ impl DummyEnvironment {
|
|||
DummyFuncEnvironment::new(&self.info, self.return_mode)
|
||||
}
|
||||
|
||||
fn get_func_type(&self, func_index: FuncIndex) -> SignatureIndex {
|
||||
fn get_func_type(&self, func_index: FuncIndex) -> TypeIndex {
|
||||
self.info.functions[func_index].entity
|
||||
}
|
||||
|
||||
|
@ -190,7 +190,7 @@ impl<'dummy_environment> DummyFuncEnvironment<'dummy_environment> {
|
|||
|
||||
// Create a signature for `sigidx` amended with a `vmctx` argument after the standard wasm
|
||||
// arguments.
|
||||
fn vmctx_sig(&self, sigidx: SignatureIndex) -> ir::Signature {
|
||||
fn vmctx_sig(&self, sigidx: TypeIndex) -> ir::Signature {
|
||||
let mut sig = self.mod_info.signatures[sigidx].clone();
|
||||
sig.params.push(ir::AbiParam::special(
|
||||
self.pointer_type(),
|
||||
|
@ -283,7 +283,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
|
|||
fn make_indirect_sig(
|
||||
&mut self,
|
||||
func: &mut ir::Function,
|
||||
index: SignatureIndex,
|
||||
index: TypeIndex,
|
||||
) -> WasmResult<ir::SigRef> {
|
||||
// A real implementation would probably change the calling convention and add `vmctx` and
|
||||
// signature index arguments.
|
||||
|
@ -312,7 +312,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
|
|||
mut pos: FuncCursor,
|
||||
_table_index: TableIndex,
|
||||
_table: ir::Table,
|
||||
_sig_index: SignatureIndex,
|
||||
_sig_index: TypeIndex,
|
||||
sig_ref: ir::SigRef,
|
||||
callee: ir::Value,
|
||||
call_args: &[ir::Value],
|
||||
|
@ -393,8 +393,10 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
|
|||
fn translate_memory_copy(
|
||||
&mut self,
|
||||
_pos: FuncCursor,
|
||||
_index: MemoryIndex,
|
||||
_heap: ir::Heap,
|
||||
_src_index: MemoryIndex,
|
||||
_src_heap: ir::Heap,
|
||||
_dst_index: MemoryIndex,
|
||||
_dst_heap: ir::Heap,
|
||||
_dst: ir::Value,
|
||||
_src: ir::Value,
|
||||
_len: ir::Value,
|
||||
|
@ -570,14 +572,14 @@ impl TargetEnvironment for DummyEnvironment {
|
|||
}
|
||||
|
||||
impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
|
||||
fn declare_signature(&mut self, _wasm: WasmFuncType, sig: ir::Signature) -> WasmResult<()> {
|
||||
fn declare_type_func(&mut self, _wasm: WasmFuncType, sig: ir::Signature) -> WasmResult<()> {
|
||||
self.info.signatures.push(sig);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn declare_func_import(
|
||||
&mut self,
|
||||
sig_index: SignatureIndex,
|
||||
index: TypeIndex,
|
||||
module: &'data str,
|
||||
field: &'data str,
|
||||
) -> WasmResult<()> {
|
||||
|
@ -586,15 +588,15 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
|
|||
self.info.imported_funcs.len(),
|
||||
"Imported functions must be declared first"
|
||||
);
|
||||
self.info.functions.push(Exportable::new(sig_index));
|
||||
self.info.functions.push(Exportable::new(index));
|
||||
self.info
|
||||
.imported_funcs
|
||||
.push((String::from(module), String::from(field)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn declare_func_type(&mut self, sig_index: SignatureIndex) -> WasmResult<()> {
|
||||
self.info.functions.push(Exportable::new(sig_index));
|
||||
fn declare_func_type(&mut self, index: TypeIndex) -> WasmResult<()> {
|
||||
self.info.functions.push(Exportable::new(index));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
use crate::state::FuncTranslationState;
|
||||
use crate::translation_utils::{
|
||||
DataIndex, ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex,
|
||||
Table, TableIndex,
|
||||
DataIndex, ElemIndex, EntityType, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, Table,
|
||||
TableIndex, TypeIndex,
|
||||
};
|
||||
use core::convert::From;
|
||||
use core::convert::TryFrom;
|
||||
|
@ -293,7 +293,7 @@ pub trait FuncEnvironment: TargetEnvironment {
|
|||
fn make_indirect_sig(
|
||||
&mut self,
|
||||
func: &mut ir::Function,
|
||||
index: SignatureIndex,
|
||||
index: TypeIndex,
|
||||
) -> WasmResult<ir::SigRef>;
|
||||
|
||||
/// Set up an external function definition in the preamble of `func` that can be used to
|
||||
|
@ -328,7 +328,7 @@ pub trait FuncEnvironment: TargetEnvironment {
|
|||
pos: FuncCursor,
|
||||
table_index: TableIndex,
|
||||
table: ir::Table,
|
||||
sig_index: SignatureIndex,
|
||||
sig_index: TypeIndex,
|
||||
sig_ref: ir::SigRef,
|
||||
callee: ir::Value,
|
||||
call_args: &[ir::Value],
|
||||
|
@ -387,8 +387,10 @@ pub trait FuncEnvironment: TargetEnvironment {
|
|||
fn translate_memory_copy(
|
||||
&mut self,
|
||||
pos: FuncCursor,
|
||||
index: MemoryIndex,
|
||||
heap: ir::Heap,
|
||||
src_index: MemoryIndex,
|
||||
src_heap: ir::Heap,
|
||||
dst_index: MemoryIndex,
|
||||
dst_heap: ir::Heap,
|
||||
dst: ir::Value,
|
||||
src: ir::Value,
|
||||
len: ir::Value,
|
||||
|
@ -628,19 +630,35 @@ pub trait FuncEnvironment: TargetEnvironment {
|
|||
/// [`translate_module`](fn.translate_module.html) function. These methods should not be called
|
||||
/// by the user, they are only for `cranelift-wasm` internal use.
|
||||
pub trait ModuleEnvironment<'data>: TargetEnvironment {
|
||||
/// Provides the number of signatures up front. By default this does nothing, but
|
||||
/// Provides the number of types up front. By default this does nothing, but
|
||||
/// implementations can use this to preallocate memory if desired.
|
||||
fn reserve_signatures(&mut self, _num: u32) -> WasmResult<()> {
|
||||
fn reserve_types(&mut self, _num: u32) -> WasmResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Declares a function signature to the environment.
|
||||
fn declare_signature(
|
||||
fn declare_type_func(
|
||||
&mut self,
|
||||
wasm_func_type: WasmFuncType,
|
||||
sig: ir::Signature,
|
||||
) -> WasmResult<()>;
|
||||
|
||||
/// Declares a module type signature to the environment.
|
||||
fn declare_type_module(
|
||||
&mut self,
|
||||
imports: &[(&'data str, Option<&'data str>, EntityType)],
|
||||
exports: &[(&'data str, EntityType)],
|
||||
) -> WasmResult<()> {
|
||||
drop((imports, exports));
|
||||
Err(WasmError::Unsupported("module linking".to_string()))
|
||||
}
|
||||
|
||||
/// Declares an instance type signature to the environment.
|
||||
fn declare_type_instance(&mut self, exports: &[(&'data str, EntityType)]) -> WasmResult<()> {
|
||||
drop(exports);
|
||||
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<()> {
|
||||
|
@ -650,7 +668,7 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
|
|||
/// Declares a function import to the environment.
|
||||
fn declare_func_import(
|
||||
&mut self,
|
||||
sig_index: SignatureIndex,
|
||||
index: TypeIndex,
|
||||
module: &'data str,
|
||||
field: &'data str,
|
||||
) -> WasmResult<()>;
|
||||
|
@ -679,6 +697,28 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
|
|||
field: &'data str,
|
||||
) -> WasmResult<()>;
|
||||
|
||||
/// Declares a module import to the environment.
|
||||
fn declare_module_import(
|
||||
&mut self,
|
||||
ty_index: TypeIndex,
|
||||
module: &'data str,
|
||||
field: &'data str,
|
||||
) -> WasmResult<()> {
|
||||
drop((ty_index, module, field));
|
||||
Err(WasmError::Unsupported("module linking".to_string()))
|
||||
}
|
||||
|
||||
/// Declares an instance import to the environment.
|
||||
fn declare_instance_import(
|
||||
&mut self,
|
||||
ty_index: TypeIndex,
|
||||
module: &'data str,
|
||||
field: &'data str,
|
||||
) -> WasmResult<()> {
|
||||
drop((ty_index, module, field));
|
||||
Err(WasmError::Unsupported("module linking".to_string()))
|
||||
}
|
||||
|
||||
/// Notifies the implementation that all imports have been declared.
|
||||
fn finish_imports(&mut self) -> WasmResult<()> {
|
||||
Ok(())
|
||||
|
@ -691,7 +731,7 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
|
|||
}
|
||||
|
||||
/// Declares the type (signature) of a local function in the module.
|
||||
fn declare_func_type(&mut self, sig_index: SignatureIndex) -> WasmResult<()>;
|
||||
fn declare_func_type(&mut self, index: TypeIndex) -> WasmResult<()>;
|
||||
|
||||
/// Provides the number of defined tables up front. By default this does nothing, but
|
||||
/// implementations can use this to preallocate memory if desired.
|
||||
|
@ -845,4 +885,31 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
|
|||
fn wasm_features(&self) -> WasmFeatures {
|
||||
WasmFeatures::default()
|
||||
}
|
||||
|
||||
/// Indicates that this module will have `amount` submodules.
|
||||
///
|
||||
/// Note that this is just child modules of this module, and each child
|
||||
/// module may have yet more submodules.
|
||||
fn reserve_modules(&mut self, amount: u32) {
|
||||
drop(amount);
|
||||
}
|
||||
|
||||
/// Called at the beginning of translating a module.
|
||||
///
|
||||
/// The `index` argument is a monotonically increasing index which
|
||||
/// corresponds to the nth module that's being translated. This is not the
|
||||
/// 32-bit index in the current module's index space. For example the first
|
||||
/// call to `module_start` will have index 0.
|
||||
///
|
||||
/// Note that for nested modules this may be called multiple times.
|
||||
fn module_start(&mut self, index: usize) {
|
||||
drop(index);
|
||||
}
|
||||
|
||||
/// Called at the end of translating a module.
|
||||
///
|
||||
/// Note that for nested modules this may be called multiple times.
|
||||
fn module_end(&mut self, index: usize) {
|
||||
drop(index);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,9 +20,8 @@
|
|||
clippy::float_arithmetic,
|
||||
clippy::mut_mut,
|
||||
clippy::nonminimal_bool,
|
||||
clippy::option_map_unwrap_or,
|
||||
clippy::option_map_unwrap_or_else,
|
||||
clippy::print_stdout,
|
||||
clippy::map_unwrap_or,
|
||||
clippy::clippy::print_stdout,
|
||||
clippy::unicode_not_nfc,
|
||||
clippy::use_self
|
||||
)
|
||||
|
@ -65,11 +64,7 @@ pub use crate::func_translator::FuncTranslator;
|
|||
pub use crate::module_translator::translate_module;
|
||||
pub use crate::state::func_state::FuncTranslationState;
|
||||
pub use crate::state::module_state::ModuleTranslationState;
|
||||
pub use crate::translation_utils::{
|
||||
get_vmctx_value_label, DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex,
|
||||
DefinedTableIndex, ElemIndex, FuncIndex, Global, GlobalIndex, GlobalInit, Memory, MemoryIndex,
|
||||
SignatureIndex, Table, TableElementType, TableIndex,
|
||||
};
|
||||
pub use crate::translation_utils::*;
|
||||
pub use cranelift_frontend::FunctionBuilder;
|
||||
|
||||
// Convenience reexport of the wasmparser crate that we're linking against,
|
||||
|
|
|
@ -8,6 +8,7 @@ use crate::sections_translator::{
|
|||
};
|
||||
use crate::state::ModuleTranslationState;
|
||||
use cranelift_codegen::timing;
|
||||
use std::prelude::v1::*;
|
||||
use wasmparser::{NameSectionReader, Parser, Payload, Validator};
|
||||
|
||||
/// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cranelift IR
|
||||
|
@ -20,14 +21,23 @@ pub fn translate_module<'data>(
|
|||
let mut module_translation_state = ModuleTranslationState::new();
|
||||
let mut validator = Validator::new();
|
||||
validator.wasm_features(environ.wasm_features());
|
||||
let mut stack = Vec::new();
|
||||
let mut modules = 1;
|
||||
let mut cur_module = 0;
|
||||
|
||||
for payload in Parser::new(0).parse_all(data) {
|
||||
match payload? {
|
||||
Payload::Version { num, range } => {
|
||||
validator.version(num, &range)?;
|
||||
environ.module_start(cur_module);
|
||||
}
|
||||
Payload::End => {
|
||||
validator.end()?;
|
||||
environ.module_end(cur_module);
|
||||
if let Some((other, other_index)) = stack.pop() {
|
||||
validator = other;
|
||||
cur_module = other_index;
|
||||
}
|
||||
}
|
||||
|
||||
Payload::TypeSection(types) => {
|
||||
|
@ -97,7 +107,7 @@ pub fn translate_module<'data>(
|
|||
|
||||
Payload::ModuleSection(s) => {
|
||||
validator.module_section(&s)?;
|
||||
unimplemented!("module linking not implemented yet")
|
||||
environ.reserve_modules(s.get_count());
|
||||
}
|
||||
Payload::InstanceSection(s) => {
|
||||
validator.instance_section(&s)?;
|
||||
|
@ -113,11 +123,14 @@ pub fn translate_module<'data>(
|
|||
size: _,
|
||||
} => {
|
||||
validator.module_code_section_start(count, &range)?;
|
||||
unimplemented!("module linking not implemented yet")
|
||||
}
|
||||
|
||||
Payload::ModuleCodeSectionEntry { .. } => {
|
||||
unimplemented!("module linking not implemented yet")
|
||||
let subvalidator = validator.module_code_section_entry();
|
||||
stack.push((validator, cur_module));
|
||||
validator = subvalidator;
|
||||
cur_module = modules;
|
||||
modules += 1;
|
||||
}
|
||||
|
||||
Payload::CustomSection {
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
use crate::environ::{ModuleEnvironment, WasmError, WasmResult};
|
||||
use crate::state::ModuleTranslationState;
|
||||
use crate::translation_utils::{
|
||||
tabletype_to_type, type_to_type, DataIndex, ElemIndex, FuncIndex, Global, GlobalIndex,
|
||||
GlobalInit, Memory, MemoryIndex, SignatureIndex, Table, TableElementType, TableIndex,
|
||||
tabletype_to_type, type_to_type, DataIndex, ElemIndex, EntityType, FuncIndex, Global,
|
||||
GlobalIndex, GlobalInit, Memory, MemoryIndex, Table, TableElementType, TableIndex, TypeIndex,
|
||||
};
|
||||
use crate::wasm_unsupported;
|
||||
use core::convert::TryFrom;
|
||||
|
@ -27,39 +27,113 @@ use wasmparser::{
|
|||
ElementSectionReader, Export, ExportSectionReader, ExternalKind, FunctionSectionReader,
|
||||
GlobalSectionReader, GlobalType, ImportSectionEntryType, ImportSectionReader,
|
||||
MemorySectionReader, MemoryType, NameSectionReader, Naming, Operator, TableSectionReader,
|
||||
TypeDef, TypeSectionReader,
|
||||
TableType, TypeDef, TypeSectionReader,
|
||||
};
|
||||
|
||||
fn entity_type(
|
||||
ty: ImportSectionEntryType,
|
||||
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::Memory(ty) => EntityType::Memory(memory(ty)),
|
||||
ImportSectionEntryType::Global(ty) => {
|
||||
EntityType::Global(global(ty, environ, GlobalInit::Import)?)
|
||||
}
|
||||
ImportSectionEntryType::Table(ty) => EntityType::Table(table(ty, environ)?),
|
||||
})
|
||||
}
|
||||
|
||||
fn memory(ty: MemoryType) -> Memory {
|
||||
match ty {
|
||||
MemoryType::M32 { limits, shared } => Memory {
|
||||
minimum: limits.initial,
|
||||
maximum: limits.maximum,
|
||||
shared: shared,
|
||||
},
|
||||
// FIXME(#2361)
|
||||
MemoryType::M64 { .. } => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn table(ty: TableType, environ: &mut dyn ModuleEnvironment<'_>) -> WasmResult<Table> {
|
||||
Ok(Table {
|
||||
wasm_ty: ty.element_type.try_into()?,
|
||||
ty: match tabletype_to_type(ty.element_type, environ)? {
|
||||
Some(t) => TableElementType::Val(t),
|
||||
None => TableElementType::Func,
|
||||
},
|
||||
minimum: ty.limits.initial,
|
||||
maximum: ty.limits.maximum,
|
||||
})
|
||||
}
|
||||
|
||||
fn global(
|
||||
ty: GlobalType,
|
||||
environ: &mut dyn ModuleEnvironment<'_>,
|
||||
initializer: GlobalInit,
|
||||
) -> WasmResult<Global> {
|
||||
Ok(Global {
|
||||
wasm_ty: ty.content_type.try_into()?,
|
||||
ty: type_to_type(ty.content_type, environ).unwrap(),
|
||||
mutability: ty.mutable,
|
||||
initializer,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses the Type section of the wasm module.
|
||||
pub fn parse_type_section(
|
||||
types: TypeSectionReader,
|
||||
pub fn parse_type_section<'a>(
|
||||
types: TypeSectionReader<'a>,
|
||||
module_translation_state: &mut ModuleTranslationState,
|
||||
environ: &mut dyn ModuleEnvironment,
|
||||
environ: &mut dyn ModuleEnvironment<'a>,
|
||||
) -> WasmResult<()> {
|
||||
let count = types.get_count();
|
||||
module_translation_state.wasm_types.reserve(count as usize);
|
||||
environ.reserve_signatures(count)?;
|
||||
environ.reserve_types(count)?;
|
||||
|
||||
for entry in types {
|
||||
if let Ok(TypeDef::Func(wasm_func_ty)) = entry {
|
||||
let mut sig =
|
||||
Signature::new(ModuleEnvironment::target_config(environ).default_call_conv);
|
||||
sig.params.extend(wasm_func_ty.params.iter().map(|ty| {
|
||||
let cret_arg: ir::Type = type_to_type(*ty, environ)
|
||||
.expect("only numeric types are supported in function signatures");
|
||||
AbiParam::new(cret_arg)
|
||||
}));
|
||||
sig.returns.extend(wasm_func_ty.returns.iter().map(|ty| {
|
||||
let cret_arg: ir::Type = type_to_type(*ty, environ)
|
||||
.expect("only numeric types are supported in function signatures");
|
||||
AbiParam::new(cret_arg)
|
||||
}));
|
||||
environ.declare_signature(wasm_func_ty.clone().try_into()?, sig)?;
|
||||
module_translation_state
|
||||
.wasm_types
|
||||
.push((wasm_func_ty.params, wasm_func_ty.returns));
|
||||
} else {
|
||||
unimplemented!("module linking not implemented yet")
|
||||
match entry? {
|
||||
TypeDef::Func(wasm_func_ty) => {
|
||||
let mut sig =
|
||||
Signature::new(ModuleEnvironment::target_config(environ).default_call_conv);
|
||||
sig.params.extend(wasm_func_ty.params.iter().map(|ty| {
|
||||
let cret_arg: ir::Type = type_to_type(*ty, environ)
|
||||
.expect("only numeric types are supported in function signatures");
|
||||
AbiParam::new(cret_arg)
|
||||
}));
|
||||
sig.returns.extend(wasm_func_ty.returns.iter().map(|ty| {
|
||||
let cret_arg: ir::Type = type_to_type(*ty, environ)
|
||||
.expect("only numeric types are supported in function signatures");
|
||||
AbiParam::new(cret_arg)
|
||||
}));
|
||||
environ.declare_type_func(wasm_func_ty.clone().try_into()?, sig)?;
|
||||
module_translation_state
|
||||
.wasm_types
|
||||
.push((wasm_func_ty.params, wasm_func_ty.returns));
|
||||
}
|
||||
TypeDef::Module(t) => {
|
||||
let imports = t
|
||||
.imports
|
||||
.iter()
|
||||
.map(|i| Ok((i.module, i.field, entity_type(i.ty, environ)?)))
|
||||
.collect::<WasmResult<Vec<_>>>()?;
|
||||
let exports = t
|
||||
.exports
|
||||
.iter()
|
||||
.map(|e| Ok((e.name, entity_type(e.ty, environ)?)))
|
||||
.collect::<WasmResult<Vec<_>>>()?;
|
||||
environ.declare_type_module(&imports, &exports)?;
|
||||
}
|
||||
TypeDef::Instance(t) => {
|
||||
let exports = t
|
||||
.exports
|
||||
.iter()
|
||||
.map(|e| Ok((e.name, entity_type(e.ty, environ)?)))
|
||||
.collect::<WasmResult<Vec<_>>>()?;
|
||||
environ.declare_type_instance(&exports)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -76,61 +150,24 @@ pub fn parse_import_section<'data>(
|
|||
let import = entry?;
|
||||
let module_name = import.module;
|
||||
let field_name = import.field.unwrap(); // TODO Handle error when module linking is implemented.
|
||||
|
||||
match import.ty {
|
||||
ImportSectionEntryType::Function(sig) => {
|
||||
environ.declare_func_import(
|
||||
SignatureIndex::from_u32(sig),
|
||||
module_name,
|
||||
field_name,
|
||||
)?;
|
||||
match entity_type(import.ty, environ)? {
|
||||
EntityType::Function(idx) => {
|
||||
environ.declare_func_import(idx, module_name, field_name)?;
|
||||
}
|
||||
ImportSectionEntryType::Module(_sig) | ImportSectionEntryType::Instance(_sig) => {
|
||||
unimplemented!("module linking not implemented yet")
|
||||
EntityType::Module(idx) => {
|
||||
environ.declare_module_import(idx, module_name, field_name)?;
|
||||
}
|
||||
ImportSectionEntryType::Memory(MemoryType::M32 {
|
||||
limits: ref memlimits,
|
||||
shared,
|
||||
}) => {
|
||||
environ.declare_memory_import(
|
||||
Memory {
|
||||
minimum: memlimits.initial,
|
||||
maximum: memlimits.maximum,
|
||||
shared,
|
||||
},
|
||||
module_name,
|
||||
field_name,
|
||||
)?;
|
||||
EntityType::Instance(idx) => {
|
||||
environ.declare_instance_import(idx, module_name, field_name)?;
|
||||
}
|
||||
ImportSectionEntryType::Memory(MemoryType::M64 { .. }) => {
|
||||
unimplemented!();
|
||||
EntityType::Memory(ty) => {
|
||||
environ.declare_memory_import(ty, module_name, field_name)?;
|
||||
}
|
||||
ImportSectionEntryType::Global(ref ty) => {
|
||||
environ.declare_global_import(
|
||||
Global {
|
||||
wasm_ty: ty.content_type.try_into()?,
|
||||
ty: type_to_type(ty.content_type, environ).unwrap(),
|
||||
mutability: ty.mutable,
|
||||
initializer: GlobalInit::Import,
|
||||
},
|
||||
module_name,
|
||||
field_name,
|
||||
)?;
|
||||
EntityType::Global(ty) => {
|
||||
environ.declare_global_import(ty, module_name, field_name)?;
|
||||
}
|
||||
ImportSectionEntryType::Table(ref tab) => {
|
||||
environ.declare_table_import(
|
||||
Table {
|
||||
wasm_ty: tab.element_type.try_into()?,
|
||||
ty: match tabletype_to_type(tab.element_type, environ)? {
|
||||
Some(t) => TableElementType::Val(t),
|
||||
None => TableElementType::Func,
|
||||
},
|
||||
minimum: tab.limits.initial,
|
||||
maximum: tab.limits.maximum,
|
||||
},
|
||||
module_name,
|
||||
field_name,
|
||||
)?;
|
||||
EntityType::Table(ty) => {
|
||||
environ.declare_table_import(ty, module_name, field_name)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -154,7 +191,7 @@ pub fn parse_function_section(
|
|||
|
||||
for entry in functions {
|
||||
let sigindex = entry?;
|
||||
environ.declare_func_type(SignatureIndex::from_u32(sigindex))?;
|
||||
environ.declare_func_type(TypeIndex::from_u32(sigindex))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -168,16 +205,8 @@ pub fn parse_table_section(
|
|||
environ.reserve_tables(tables.get_count())?;
|
||||
|
||||
for entry in tables {
|
||||
let table = entry?;
|
||||
environ.declare_table(Table {
|
||||
wasm_ty: table.element_type.try_into()?,
|
||||
ty: match tabletype_to_type(table.element_type, environ)? {
|
||||
Some(t) => TableElementType::Val(t),
|
||||
None => TableElementType::Func,
|
||||
},
|
||||
minimum: table.limits.initial,
|
||||
maximum: table.limits.maximum,
|
||||
})?;
|
||||
let ty = table(entry?, environ)?;
|
||||
environ.declare_table(ty)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -191,17 +220,8 @@ pub fn parse_memory_section(
|
|||
environ.reserve_memories(memories.get_count())?;
|
||||
|
||||
for entry in memories {
|
||||
let memory = entry?;
|
||||
match memory {
|
||||
MemoryType::M32 { limits, shared } => {
|
||||
environ.declare_memory(Memory {
|
||||
minimum: limits.initial,
|
||||
maximum: limits.maximum,
|
||||
shared: shared,
|
||||
})?;
|
||||
}
|
||||
MemoryType::M64 { .. } => unimplemented!(),
|
||||
}
|
||||
let memory = memory(entry?);
|
||||
environ.declare_memory(memory)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -215,13 +235,7 @@ pub fn parse_global_section(
|
|||
environ.reserve_globals(globals.get_count())?;
|
||||
|
||||
for entry in globals {
|
||||
let wasmparser::Global {
|
||||
ty: GlobalType {
|
||||
content_type,
|
||||
mutable,
|
||||
},
|
||||
init_expr,
|
||||
} = entry?;
|
||||
let wasmparser::Global { ty, init_expr } = entry?;
|
||||
let mut init_expr_reader = init_expr.get_binary_reader();
|
||||
let initializer = match init_expr_reader.read_operator()? {
|
||||
Operator::I32Const { value } => GlobalInit::I32Const(value),
|
||||
|
@ -245,13 +259,8 @@ pub fn parse_global_section(
|
|||
));
|
||||
}
|
||||
};
|
||||
let global = Global {
|
||||
wasm_ty: content_type.try_into()?,
|
||||
ty: type_to_type(content_type, environ).unwrap(),
|
||||
mutability: mutable,
|
||||
initializer,
|
||||
};
|
||||
environ.declare_global(global)?;
|
||||
let ty = global(ty, environ, initializer)?;
|
||||
environ.declare_global(ty)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
//! value and control stacks during the translation of a single function.
|
||||
|
||||
use crate::environ::{FuncEnvironment, GlobalVariable, WasmResult};
|
||||
use crate::translation_utils::{FuncIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
|
||||
use crate::translation_utils::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex, TypeIndex};
|
||||
use crate::{HashMap, Occupied, Vacant};
|
||||
use cranelift_codegen::ir::{self, Block, Inst, Value};
|
||||
use std::vec::Vec;
|
||||
|
@ -236,7 +236,7 @@ pub struct FuncTranslationState {
|
|||
// Map of indirect call signatures that have been created by
|
||||
// `FuncEnvironment::make_indirect_sig()`.
|
||||
// Stores both the signature reference and the number of WebAssembly arguments
|
||||
signatures: HashMap<SignatureIndex, (ir::SigRef, usize)>,
|
||||
signatures: HashMap<TypeIndex, (ir::SigRef, usize)>,
|
||||
|
||||
// Imported and local functions that have been created by
|
||||
// `FuncEnvironment::make_direct_func()`.
|
||||
|
@ -498,7 +498,7 @@ impl FuncTranslationState {
|
|||
index: u32,
|
||||
environ: &mut FE,
|
||||
) -> WasmResult<(ir::SigRef, usize)> {
|
||||
let index = SignatureIndex::from_u32(index);
|
||||
let index = TypeIndex::from_u32(index);
|
||||
match self.signatures.entry(index) {
|
||||
Occupied(entry) => Ok(*entry.get()),
|
||||
Vacant(entry) => {
|
||||
|
|
|
@ -73,6 +73,65 @@ entity_impl!(DataIndex);
|
|||
pub struct ElemIndex(u32);
|
||||
entity_impl!(ElemIndex);
|
||||
|
||||
/// Index type of a type inside the WebAssembly module.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
pub struct TypeIndex(u32);
|
||||
entity_impl!(TypeIndex);
|
||||
|
||||
/// Index type of a module inside the WebAssembly module.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
pub struct ModuleIndex(u32);
|
||||
entity_impl!(ModuleIndex);
|
||||
|
||||
/// Index type of an instance inside the WebAssembly module.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
pub struct InstanceIndex(u32);
|
||||
entity_impl!(InstanceIndex);
|
||||
|
||||
/// An index of an entity.
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
pub enum EntityIndex {
|
||||
/// Function index.
|
||||
Function(FuncIndex),
|
||||
/// Table index.
|
||||
Table(TableIndex),
|
||||
/// Memory index.
|
||||
Memory(MemoryIndex),
|
||||
/// Global index.
|
||||
Global(GlobalIndex),
|
||||
/// Module index.
|
||||
Module(ModuleIndex),
|
||||
/// Instance index.
|
||||
Instance(InstanceIndex),
|
||||
}
|
||||
|
||||
/// A type of an item in a wasm module where an item is typically something that
|
||||
/// can be exported.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
pub enum EntityType {
|
||||
/// A global variable with the specified content type
|
||||
Global(Global),
|
||||
/// A linear memory with the specified limits
|
||||
Memory(Memory),
|
||||
/// A table with the specified element type and limits
|
||||
Table(Table),
|
||||
/// A function type where the index points to the type section and records a
|
||||
/// function signature.
|
||||
Function(TypeIndex),
|
||||
/// An instance where the index points to the type section and records a
|
||||
/// instance's exports.
|
||||
Instance(TypeIndex),
|
||||
/// A module where the index points to the type section and records a
|
||||
/// module's imports and exports.
|
||||
Module(TypeIndex),
|
||||
}
|
||||
|
||||
/// A WebAssembly global.
|
||||
///
|
||||
/// Note that we record both the original Wasm type and the Cranelift IR type
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"71ed793f6957ed5fce7a18d86f5b77ad881fc5ba74fce18f23e8a62121fdeb80","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","src/analysis_control_flow.rs":"599a720f00e31391216bc2ede8dd414f6acdf953b392c0e58ed733e9e9d4173a","src/analysis_data_flow.rs":"abb8a9d0038a992f17facb61787e0708516954843d991accd404662d23146293","src/analysis_main.rs":"d634ab87144a23eda45ffe7e48f1ad5e13da3a209b6257d5812046425bad77e3","src/analysis_reftypes.rs":"d3440d6674079db8c62e69759ed2193627e33d7b7845fd2700cef279ee9e1e0a","src/avl_tree.rs":"df8646916f68da0e322d0ea3944d2d7b2acbb29d93f383f32f7126079b713dfd","src/bt_coalescing_analysis.rs":"142680acac8965fa01cd104a84d0b871352f323580eb04ab178127d602cdd02c","src/bt_commitment_map.rs":"2dabf2896b7b2c96d9cf9bf26cdd397765b468772bf44fbb0337eff85433d0f7","src/bt_main.rs":"61a7b94f4b9d4c58d0189c151543b2a488ca385614666ca91734120d5d3bf471","src/bt_spillslot_allocator.rs":"3534171c6e156c3d5a948036430a80a2ca7ba728a3e4b33e598479740cffe0e3","src/bt_vlr_priority_queue.rs":"082d9ede8c1e7ec3129356450b42056e090f1dae77688589f71be7055eff5333","src/checker.rs":"fa20229ea7ce12c7cf2f11c0dce062f793eaf4f77e9227603a2ce5dcf9991368","src/data_structures.rs":"b828d429c02cf5b76355b21ba1b7e14183392ac38ec60d7459fe7b531137eb9e","src/inst_stream.rs":"f40f660dcabfeb1a1aa04ed781a5504eb30e26134776490f38ae71a9c86cff8d","src/lib.rs":"aa07a5e33bb2b5d6599bca5a3f10a964bb3b62d0a8d52db46b6b4f3ae75f148a","src/linear_scan/analysis.rs":"d23114c90ba74ad39dbee6b648730aafd2cffe667a9a435502bfbe35366e7a16","src/linear_scan/assign_registers.rs":"c4e75bc01f447ee8abeb7420172550d114c83813f8b4e81fce1d10c423955088","src/linear_scan/mod.rs":"e579abc91cb590f0d2d2d52f4d73f71c87a0f64b6500937be1e2eced9355beb4","src/linear_scan/resolve_moves.rs":"2c51e4d6a096454db79090b078780ee9938eae4dd1fe0d32103bdc4e56e4e3c8","src/reg_maps.rs":"87ede67e8e550fd8e3aa4d880989b2bdf6f21b5668c5fb657039627aa8c1c3d3","src/snapshot.rs":"62ff934004a93697d48049e0dae1b99717c56ca35154d3a12d6ba22e47fe0d16","src/sparse_set.rs":"0474e23c90f2b4bfe8bcfdae5e12462002b7a5278989926213ecacc34a38b39f","src/union_find.rs":"78f5863cd61ad8c6a21b1b1ccc0996d9c19b469ff8ebd101a1e9f0a3e10f1b7c"},"package":"2041c2d34f6ff346d6f428974f03d8bf12679b0c816bb640dc5eb1d48848d8d1"}
|
||||
{"files":{"Cargo.toml":"e3f7398aa276b5ad4dbb14ca40b825ce39da395a7ba137953d693e52a5e10287","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","src/analysis_control_flow.rs":"599a720f00e31391216bc2ede8dd414f6acdf953b392c0e58ed733e9e9d4173a","src/analysis_data_flow.rs":"abb8a9d0038a992f17facb61787e0708516954843d991accd404662d23146293","src/analysis_main.rs":"d634ab87144a23eda45ffe7e48f1ad5e13da3a209b6257d5812046425bad77e3","src/analysis_reftypes.rs":"d3440d6674079db8c62e69759ed2193627e33d7b7845fd2700cef279ee9e1e0a","src/avl_tree.rs":"df8646916f68da0e322d0ea3944d2d7b2acbb29d93f383f32f7126079b713dfd","src/bt_coalescing_analysis.rs":"142680acac8965fa01cd104a84d0b871352f323580eb04ab178127d602cdd02c","src/bt_commitment_map.rs":"2dabf2896b7b2c96d9cf9bf26cdd397765b468772bf44fbb0337eff85433d0f7","src/bt_main.rs":"b866200405cc8102665b871a7dec0ab057fe27fcddd1707052f4ea67673f2a6c","src/bt_spillslot_allocator.rs":"3534171c6e156c3d5a948036430a80a2ca7ba728a3e4b33e598479740cffe0e3","src/bt_vlr_priority_queue.rs":"082d9ede8c1e7ec3129356450b42056e090f1dae77688589f71be7055eff5333","src/checker.rs":"fa20229ea7ce12c7cf2f11c0dce062f793eaf4f77e9227603a2ce5dcf9991368","src/data_structures.rs":"029b6e35611c9de6f7d9b02588e5b89b577d9ff3c0bc040434f57d644b9a768d","src/inst_stream.rs":"f40f660dcabfeb1a1aa04ed781a5504eb30e26134776490f38ae71a9c86cff8d","src/lib.rs":"ee96849593f57d0413bfeb8fe38a4c12c536f9cfb35f1a3ee8d50c812c6be14c","src/linear_scan/analysis.rs":"d23114c90ba74ad39dbee6b648730aafd2cffe667a9a435502bfbe35366e7a16","src/linear_scan/assign_registers.rs":"c4e75bc01f447ee8abeb7420172550d114c83813f8b4e81fce1d10c423955088","src/linear_scan/mod.rs":"4a74263d0ef9ad8fbd4d0f2f08e3db49c22a0a1ec4c21676b2fbc70d49174d8c","src/linear_scan/resolve_moves.rs":"2c51e4d6a096454db79090b078780ee9938eae4dd1fe0d32103bdc4e56e4e3c8","src/pretty_print.rs":"702aba67634ddc76feb7384a766341cb59f8d902ca06340088f6fa522bed286d","src/reg_maps.rs":"87ede67e8e550fd8e3aa4d880989b2bdf6f21b5668c5fb657039627aa8c1c3d3","src/snapshot.rs":"62ff934004a93697d48049e0dae1b99717c56ca35154d3a12d6ba22e47fe0d16","src/sparse_set.rs":"0474e23c90f2b4bfe8bcfdae5e12462002b7a5278989926213ecacc34a38b39f","src/union_find.rs":"78f5863cd61ad8c6a21b1b1ccc0996d9c19b469ff8ebd101a1e9f0a3e10f1b7c"},"package":"571f7f397d61c4755285cd37853fe8e03271c243424a907415909379659381c5"}
|
|
@ -13,7 +13,7 @@
|
|||
[package]
|
||||
edition = "2018"
|
||||
name = "regalloc"
|
||||
version = "0.0.30"
|
||||
version = "0.0.31"
|
||||
authors = ["The Regalloc.rs Developers"]
|
||||
description = "Modular register allocation algorithms"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
|
|
|
@ -1792,7 +1792,9 @@ pub fn alloc_main<F: Function>(
|
|||
let mut reg_vecs = RegVecs::new(/*sanitized=*/ false);
|
||||
let mut dummy_bounds = RegVecBounds::new();
|
||||
for insn in &final_insns {
|
||||
add_raw_reg_vecs_for_insn::<F>(insn, &mut reg_vecs, &mut dummy_bounds);
|
||||
if func.is_included_in_clobbers(insn) {
|
||||
add_raw_reg_vecs_for_insn::<F>(insn, &mut reg_vecs, &mut dummy_bounds);
|
||||
}
|
||||
}
|
||||
for reg in reg_vecs.defs.iter().chain(reg_vecs.mods.iter()) {
|
||||
assert!(reg.is_real());
|
||||
|
|
|
@ -902,9 +902,9 @@ impl Reg {
|
|||
/// create a distinction, at the Rust type level, between a plain "register"
|
||||
/// and a "writable register".
|
||||
///
|
||||
/// There is nothing that ensures that Writable<..> is only wrapped around Reg
|
||||
/// and its variants (`RealReg`, `VirtualReg`). That however is its intended
|
||||
/// and currently its only use.
|
||||
/// Only structs that implement the `WritableBase` trait can be wrapped with
|
||||
/// `Writable`. These are the Reg, RealReg and VirtualReg data structures only,
|
||||
/// since `WritableBase` is not exposed to end users.
|
||||
///
|
||||
/// Writable<..> can be used by the client to ensure that, internally, it only
|
||||
/// generates instructions that write to registers that should be written. The
|
||||
|
@ -918,11 +918,21 @@ impl Reg {
|
|||
/// error.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
pub struct Writable<R: Copy + Clone + PartialEq + Eq + Hash + PartialOrd + Ord + fmt::Debug> {
|
||||
pub struct Writable<R: WritableBase> {
|
||||
reg: R,
|
||||
}
|
||||
|
||||
impl<R: Copy + Clone + PartialEq + Eq + Hash + PartialOrd + Ord + fmt::Debug> Writable<R> {
|
||||
/// Set of requirements for types that can be wrapped in Writable.
|
||||
pub trait WritableBase:
|
||||
Copy + Clone + PartialEq + Eq + Hash + PartialOrd + Ord + fmt::Debug
|
||||
{
|
||||
}
|
||||
|
||||
impl WritableBase for Reg {}
|
||||
impl WritableBase for RealReg {}
|
||||
impl WritableBase for VirtualReg {}
|
||||
|
||||
impl<R: WritableBase> Writable<R> {
|
||||
/// Create a Writable<R> from an R. The client should carefully audit where
|
||||
/// it calls this constructor to ensure correctness (see `Writable<..>`
|
||||
/// struct documentation).
|
||||
|
@ -939,26 +949,23 @@ impl<R: Copy + Clone + PartialEq + Eq + Hash + PartialOrd + Ord + fmt::Debug> Wr
|
|||
pub fn map<F, U>(&self, f: F) -> Writable<U>
|
||||
where
|
||||
F: Fn(R) -> U,
|
||||
U: Copy + Clone + PartialEq + Eq + Hash + PartialOrd + Ord + fmt::Debug,
|
||||
U: WritableBase,
|
||||
{
|
||||
Writable { reg: f(self.reg) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum SpillSlot {
|
||||
SpillSlot(u32),
|
||||
}
|
||||
pub struct SpillSlot(u32);
|
||||
|
||||
impl SpillSlot {
|
||||
#[inline(always)]
|
||||
pub fn new(n: u32) -> Self {
|
||||
SpillSlot::SpillSlot(n)
|
||||
Self(n)
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn get(self) -> u32 {
|
||||
match self {
|
||||
SpillSlot::SpillSlot(n) => n,
|
||||
}
|
||||
self.0
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn get_usize(self) -> usize {
|
||||
|
@ -972,6 +979,7 @@ impl SpillSlot {
|
|||
SpillSlot::new(self.get() + num_slots)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SpillSlot {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "S{}", self.get())
|
||||
|
|
|
@ -26,6 +26,7 @@ mod checker;
|
|||
mod data_structures;
|
||||
mod inst_stream;
|
||||
mod linear_scan;
|
||||
mod pretty_print;
|
||||
mod reg_maps;
|
||||
mod snapshot;
|
||||
mod sparse_set;
|
||||
|
@ -37,6 +38,9 @@ use std::{borrow::Cow, fmt};
|
|||
|
||||
// Stuff that is defined by the library
|
||||
|
||||
// Pretty-printing utilities.
|
||||
pub use crate::pretty_print::*;
|
||||
|
||||
// Sets and maps of things. We can refine these later; but for now the
|
||||
// interface needs some way to speak about them, so let's use the
|
||||
// library-provided versions.
|
||||
|
@ -220,6 +224,25 @@ pub trait Function {
|
|||
/// Determine whether an instruction is a return instruction.
|
||||
fn is_ret(&self, insn: InstIx) -> bool;
|
||||
|
||||
/// Determine whether an instruction should be considered while computing
|
||||
/// the set of registers that need to be saved/restored in the function's
|
||||
/// prologue/epilogue, that is, the registers returned in
|
||||
/// `clobbered_registers` in `RegAllocResult`. computation. Only
|
||||
/// instructions for which this function returns `true` will be used to
|
||||
/// compute that set.
|
||||
///
|
||||
/// One reason that a client might *not* want an instruction to be included
|
||||
/// would be if it can handle the clobbers some other way: for example,
|
||||
/// ABI-support code might exclude call instructions' defs and mods from the
|
||||
/// clobber set, because (given the callee has same ABI as the caller) the
|
||||
/// registers possibly written by the callee are all registers that the
|
||||
/// caller is also allowed to clobber (not save/restore in
|
||||
/// prologue/epilogue).
|
||||
fn is_included_in_clobbers(&self, _insn: &Self::Inst) -> bool {
|
||||
// Default impl includes all instructions.
|
||||
true
|
||||
}
|
||||
|
||||
// --------------------------
|
||||
// Instruction register slots
|
||||
// --------------------------
|
||||
|
|
|
@ -724,6 +724,7 @@ fn set_registers<F: Function>(
|
|||
mapper.set_use(vreg, rreg);
|
||||
}
|
||||
|
||||
let included_in_clobbers = func.is_included_in_clobbers(func.get_insn(iix));
|
||||
if mention_set.is_mod() {
|
||||
if let Some(prev_rreg) = mapper.lookup_use(vreg) {
|
||||
debug_assert_eq!(prev_rreg, rreg, "different use allocs for {:?}", vreg);
|
||||
|
@ -734,7 +735,9 @@ fn set_registers<F: Function>(
|
|||
|
||||
mapper.set_use(vreg, rreg);
|
||||
mapper.set_def(vreg, rreg);
|
||||
clobbered_registers.insert(rreg);
|
||||
if included_in_clobbers {
|
||||
clobbered_registers.insert(rreg);
|
||||
}
|
||||
}
|
||||
|
||||
if mention_set.is_def() {
|
||||
|
@ -743,7 +746,9 @@ fn set_registers<F: Function>(
|
|||
}
|
||||
|
||||
mapper.set_def(vreg, rreg);
|
||||
clobbered_registers.insert(rreg);
|
||||
if included_in_clobbers {
|
||||
clobbered_registers.insert(rreg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
//! Pretty-printing for the main data structures.
|
||||
|
||||
use crate::data_structures::WritableBase;
|
||||
use crate::{RealRegUniverse, Reg, Writable};
|
||||
|
||||
/// A trait for printing instruction bits and pieces, with the the ability to take a
|
||||
/// contextualising `RealRegUniverse` that is used to give proper names to registers.
|
||||
pub trait PrettyPrint {
|
||||
/// Return a string that shows the implementing object in context of the given
|
||||
/// `RealRegUniverse`, if provided.
|
||||
fn show_rru(&self, maybe_reg_universe: Option<&RealRegUniverse>) -> String;
|
||||
}
|
||||
|
||||
/// Same as `PrettyPrint`, but can also take a size hint into account to specialize the displayed
|
||||
/// string.
|
||||
pub trait PrettyPrintSized: PrettyPrint {
|
||||
/// The same as |show_rru|, but with an optional hint giving a size in bytes. Its
|
||||
/// interpretation is object-dependent, and it is intended to pass around enough information to
|
||||
/// facilitate printing sub-parts of real registers correctly. Objects may ignore size hints
|
||||
/// that are irrelevant to them.
|
||||
///
|
||||
/// The default implementation ignores the size hint.
|
||||
fn show_rru_sized(&self, maybe_reg_universe: Option<&RealRegUniverse>, _size: u8) -> String {
|
||||
self.show_rru(maybe_reg_universe)
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyPrint for Reg {
|
||||
fn show_rru(&self, maybe_reg_universe: Option<&RealRegUniverse>) -> String {
|
||||
if self.is_real() {
|
||||
if let Some(rru) = maybe_reg_universe {
|
||||
let reg_ix = self.get_index();
|
||||
assert!(
|
||||
reg_ix < rru.regs.len(),
|
||||
"unknown real register with index {:?}",
|
||||
reg_ix
|
||||
);
|
||||
return rru.regs[reg_ix].1.to_string();
|
||||
}
|
||||
}
|
||||
// The reg is virtual, or we have no universe. Be generic.
|
||||
format!("%{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: PrettyPrint + WritableBase> PrettyPrint for Writable<R> {
|
||||
fn show_rru(&self, maybe_reg_universe: Option<&RealRegUniverse>) -> String {
|
||||
self.to_reg().show_rru(maybe_reg_universe)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: PrettyPrintSized + WritableBase> PrettyPrintSized for Writable<R> {
|
||||
fn show_rru_sized(&self, maybe_reg_universe: Option<&RealRegUniverse>, size: u8) -> String {
|
||||
self.to_reg().show_rru_sized(maybe_reg_universe, size)
|
||||
}
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче