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:
Julian Seward 2020-11-19 18:50:58 +00:00
Родитель f28cf588c0
Коммит 3455a0f670
108 изменённых файлов: 7772 добавлений и 3165 удалений

Просмотреть файл

@ -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"

48
Cargo.lock сгенерированный
Просмотреть файл

@ -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

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

24
third_party/rust/cranelift-codegen/Cargo.toml поставляемый
Просмотреть файл

@ -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)
}

231
third_party/rust/cranelift-codegen/src/data_value.rs поставляемый Normal file
Просмотреть файл

@ -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 &reg 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
}
}

Просмотреть файл

@ -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)

14
third_party/rust/cranelift-codegen/src/isa/arm32/inst/unwind.rs поставляемый Normal file
Просмотреть файл

@ -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.

125
third_party/rust/cranelift-codegen/src/isa/x64/inst/unwind.rs поставляемый Normal file
Просмотреть файл

@ -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(

Двоичные данные
third_party/rust/cranelift-codegen/src/preopt.serialized поставляемый

Двоичный файл не отображается.

Просмотреть файл

@ -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}

15
third_party/rust/cranelift-wasm/Cargo.toml поставляемый
Просмотреть файл

@ -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);
}
}

11
third_party/rust/cranelift-wasm/src/lib.rs поставляемый
Просмотреть файл

@ -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"}

2
third_party/rust/regalloc/Cargo.toml поставляемый
Просмотреть файл

@ -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"

4
third_party/rust/regalloc/src/bt_main.rs поставляемый
Просмотреть файл

@ -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())

23
third_party/rust/regalloc/src/lib.rs поставляемый
Просмотреть файл

@ -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);
}
}
}

56
third_party/rust/regalloc/src/pretty_print.rs поставляемый Normal file
Просмотреть файл

@ -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)
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше