mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-06 20:28:33 +00:00
Auto merge of #134604 - RalfJung:miri-sync, r=RalfJung
Miri subtree update r? `@ghost`
This commit is contained in:
commit
54dcff104b
@ -375,16 +375,19 @@ to Miri failing to detect cases of undefined behavior in a program.
|
|||||||
* `-Zmiri-disable-weak-memory-emulation` disables the emulation of some C++11 weak
|
* `-Zmiri-disable-weak-memory-emulation` disables the emulation of some C++11 weak
|
||||||
memory effects.
|
memory effects.
|
||||||
* `-Zmiri-native-lib=<path to a shared object file>` is an experimental flag for providing support
|
* `-Zmiri-native-lib=<path to a shared object file>` is an experimental flag for providing support
|
||||||
for calling native functions from inside the interpreter via FFI. Functions not provided by that
|
for calling native functions from inside the interpreter via FFI. The flag is supported only on
|
||||||
file are still executed via the usual Miri shims.
|
Unix systems. Functions not provided by that file are still executed via the usual Miri shims.
|
||||||
**WARNING**: If an invalid/incorrect `.so` file is specified, this can cause Undefined Behavior in Miri itself!
|
**WARNING**: If an invalid/incorrect `.so` file is specified, this can cause Undefined Behavior in
|
||||||
And of course, Miri cannot do any checks on the actions taken by the native code.
|
Miri itself! And of course, Miri cannot do any checks on the actions taken by the native code.
|
||||||
Note that Miri has its own handling of file descriptors, so if you want to replace *some* functions
|
Note that Miri has its own handling of file descriptors, so if you want to replace *some*
|
||||||
working on file descriptors, you will have to replace *all* of them, or the two kinds of
|
functions working on file descriptors, you will have to replace *all* of them, or the two kinds of
|
||||||
file descriptors will be mixed up.
|
file descriptors will be mixed up. This is **work in progress**; currently, only integer and
|
||||||
This is **work in progress**; currently, only integer arguments and return values are
|
pointers arguments and return values are supported and memory allocated by the native code cannot
|
||||||
supported (and no, pointer/integer casts to work around this limitation will not work;
|
be accessed from Rust (only the other way around). Native code must not spawn threads that keep
|
||||||
they will fail horribly). It also only works on Unix hosts for now.
|
running in the background after the call has returned to Rust and that access Rust-allocated
|
||||||
|
memory. Finally, the flag is **unsound** in the sense that Miri stops tracking details such as
|
||||||
|
initialization and provenance on memory shared with native code, so it is easily possible to write
|
||||||
|
code that has UB which is missed by Miri.
|
||||||
* `-Zmiri-measureme=<name>` enables `measureme` profiling for the interpreted program.
|
* `-Zmiri-measureme=<name>` enables `measureme` profiling for the interpreted program.
|
||||||
This can be used to find which parts of your program are executing slowly under Miri.
|
This can be used to find which parts of your program are executing slowly under Miri.
|
||||||
The profile is written out to a file inside a directory called `<name>`, and can be processed
|
The profile is written out to a file inside a directory called `<name>`, and can be processed
|
||||||
|
@ -1,35 +1,35 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
version = "0.17.0"
|
version = "0.24.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
|
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gimli",
|
"gimli",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "adler"
|
name = "adler2"
|
||||||
version = "1.0.2"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.65"
|
version = "0.3.74"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61"
|
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"addr2line",
|
"addr2line",
|
||||||
"cc",
|
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
"object",
|
"object",
|
||||||
"rustc-demangle",
|
"rustc-demangle",
|
||||||
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -39,15 +39,6 @@ dependencies = [
|
|||||||
"backtrace",
|
"backtrace",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cc"
|
|
||||||
version = "1.1.22"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0"
|
|
||||||
dependencies = [
|
|
||||||
"shlex",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@ -56,48 +47,106 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.26.1"
|
version = "0.31.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.126"
|
version = "0.2.168"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.5.0"
|
version = "2.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.5.3"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
|
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler",
|
"adler2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.28.4"
|
version = "0.36.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424"
|
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.21"
|
version = "0.1.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "windows-targets"
|
||||||
version = "1.3.0"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cargo-miri-test"
|
name = "cargo-miri-test"
|
||||||
@ -12,48 +12,54 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.2"
|
version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.39"
|
version = "1.0.92"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.18"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.10"
|
version = "1.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.137"
|
version = "1.0.216"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.137"
|
version = "1.0.216"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
|
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -62,20 +68,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.81"
|
version = "1.0.133"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
|
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
|
"memchr",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.96"
|
version = "2.0.90"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
|
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -84,6 +91,6 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.0"
|
version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cargo-miri-test"
|
name = "cargo-miri-test"
|
||||||
@ -12,48 +12,54 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.2"
|
version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.39"
|
version = "1.0.92"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.18"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.10"
|
version = "1.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.137"
|
version = "1.0.216"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.137"
|
version = "1.0.216"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
|
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -62,20 +68,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.81"
|
version = "1.0.133"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
|
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
|
"memchr",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.96"
|
version = "2.0.90"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
|
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -84,6 +91,6 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.0"
|
version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode"
|
name = "unicode"
|
||||||
@ -11,6 +11,6 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.3"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
@ -18,7 +18,7 @@ export RUSTFLAGS="-D warnings"
|
|||||||
export CARGO_INCREMENTAL=0
|
export CARGO_INCREMENTAL=0
|
||||||
export CARGO_EXTRA_FLAGS="--locked"
|
export CARGO_EXTRA_FLAGS="--locked"
|
||||||
|
|
||||||
# Determine configuration for installed build (used by test-cargo-miri).
|
# Determine configuration for installed build (used by test-cargo-miri and `./miri bench`).
|
||||||
echo "Installing release version of Miri"
|
echo "Installing release version of Miri"
|
||||||
time ./miri install
|
time ./miri install
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ function run_tests {
|
|||||||
fi
|
fi
|
||||||
if [ -n "${TEST_BENCH-}" ]; then
|
if [ -n "${TEST_BENCH-}" ]; then
|
||||||
# Check that the benchmarks build and run, but only once.
|
# Check that the benchmarks build and run, but only once.
|
||||||
time HYPERFINE="hyperfine -w0 -r1" ./miri bench $TARGET_FLAG
|
time HYPERFINE="hyperfine -w0 -r1 --show-output" ./miri bench $TARGET_FLAG --no-install
|
||||||
fi
|
fi
|
||||||
# Smoke-test `./miri run --dep`.
|
# Smoke-test `./miri run --dep`.
|
||||||
./miri run $TARGET_FLAG --dep tests/pass-dep/getrandom.rs
|
./miri run $TARGET_FLAG --dep tests/pass-dep/getrandom.rs
|
||||||
|
@ -2,6 +2,55 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"is_terminal_polyfill",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.80"
|
version = "1.0.80"
|
||||||
@ -20,6 +69,52 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.5.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "directories"
|
name = "directories"
|
||||||
version = "5.0.1"
|
version = "5.0.1"
|
||||||
@ -80,6 +175,12 @@ dependencies = [
|
|||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "home"
|
name = "home"
|
||||||
version = "0.5.9"
|
version = "0.5.9"
|
||||||
@ -89,6 +190,12 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@ -137,6 +244,7 @@ name = "miri-script"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"clap",
|
||||||
"directories",
|
"directories",
|
||||||
"dunce",
|
"dunce",
|
||||||
"itertools",
|
"itertools",
|
||||||
@ -278,6 +386,12 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.50"
|
version = "2.0.50"
|
||||||
@ -328,6 +442,12 @@ version = "1.0.12"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "walkdir"
|
name = "walkdir"
|
||||||
version = "2.4.0"
|
version = "2.4.0"
|
||||||
@ -362,7 +482,7 @@ version = "0.1.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -25,3 +25,4 @@ dunce = "1.0.4"
|
|||||||
directories = "5"
|
directories = "5"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
tempfile = "3.13.0"
|
tempfile = "3.13.0"
|
||||||
|
clap = { version = "4.5.21", features = ["derive"] }
|
||||||
|
@ -1,135 +0,0 @@
|
|||||||
use std::{env, iter};
|
|
||||||
|
|
||||||
use anyhow::{Result, bail};
|
|
||||||
|
|
||||||
pub struct Args {
|
|
||||||
args: iter::Peekable<env::Args>,
|
|
||||||
/// Set to `true` once we saw a `--`.
|
|
||||||
terminated: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Args {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let mut args = Args { args: env::args().peekable(), terminated: false };
|
|
||||||
args.args.next().unwrap(); // skip program name
|
|
||||||
args
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the next argument without any interpretation.
|
|
||||||
pub fn next_raw(&mut self) -> Option<String> {
|
|
||||||
self.args.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consume a `-$f` flag if present.
|
|
||||||
pub fn get_short_flag(&mut self, flag: char) -> Result<bool> {
|
|
||||||
if self.terminated {
|
|
||||||
return Ok(false);
|
|
||||||
}
|
|
||||||
if let Some(next) = self.args.peek() {
|
|
||||||
if let Some(next) = next.strip_prefix("-") {
|
|
||||||
if let Some(next) = next.strip_prefix(flag) {
|
|
||||||
if next.is_empty() {
|
|
||||||
self.args.next().unwrap(); // consume this argument
|
|
||||||
return Ok(true);
|
|
||||||
} else {
|
|
||||||
bail!("`-{flag}` followed by value");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consume a `--$name` flag if present.
|
|
||||||
pub fn get_long_flag(&mut self, name: &str) -> Result<bool> {
|
|
||||||
if self.terminated {
|
|
||||||
return Ok(false);
|
|
||||||
}
|
|
||||||
if let Some(next) = self.args.peek() {
|
|
||||||
if let Some(next) = next.strip_prefix("--") {
|
|
||||||
if next == name {
|
|
||||||
self.args.next().unwrap(); // consume this argument
|
|
||||||
return Ok(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consume a `--$name val` or `--$name=val` option if present.
|
|
||||||
pub fn get_long_opt(&mut self, name: &str) -> Result<Option<String>> {
|
|
||||||
assert!(!name.is_empty());
|
|
||||||
if self.terminated {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
let Some(next) = self.args.peek() else { return Ok(None) };
|
|
||||||
let Some(next) = next.strip_prefix("--") else { return Ok(None) };
|
|
||||||
let Some(next) = next.strip_prefix(name) else { return Ok(None) };
|
|
||||||
// Starts with `--flag`.
|
|
||||||
Ok(if let Some(val) = next.strip_prefix("=") {
|
|
||||||
// `--flag=val` form
|
|
||||||
let val = val.into();
|
|
||||||
self.args.next().unwrap(); // consume this argument
|
|
||||||
Some(val)
|
|
||||||
} else if next.is_empty() {
|
|
||||||
// `--flag val` form
|
|
||||||
self.args.next().unwrap(); // consume this argument
|
|
||||||
let Some(val) = self.args.next() else { bail!("`--{name}` not followed by value") };
|
|
||||||
Some(val)
|
|
||||||
} else {
|
|
||||||
// Some unrelated flag, like `--flag-more` or so.
|
|
||||||
None
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consume a `--$name=val` or `--$name` option if present; the latter
|
|
||||||
/// produces a default value. (`--$name val` is *not* accepted for this form
|
|
||||||
/// of argument, it understands `val` already as the next argument!)
|
|
||||||
pub fn get_long_opt_with_default(
|
|
||||||
&mut self,
|
|
||||||
name: &str,
|
|
||||||
default: &str,
|
|
||||||
) -> Result<Option<String>> {
|
|
||||||
assert!(!name.is_empty());
|
|
||||||
if self.terminated {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
let Some(next) = self.args.peek() else { return Ok(None) };
|
|
||||||
let Some(next) = next.strip_prefix("--") else { return Ok(None) };
|
|
||||||
let Some(next) = next.strip_prefix(name) else { return Ok(None) };
|
|
||||||
// Starts with `--flag`.
|
|
||||||
Ok(if let Some(val) = next.strip_prefix("=") {
|
|
||||||
// `--flag=val` form
|
|
||||||
let val = val.into();
|
|
||||||
self.args.next().unwrap(); // consume this argument
|
|
||||||
Some(val)
|
|
||||||
} else if next.is_empty() {
|
|
||||||
// `--flag` form
|
|
||||||
self.args.next().unwrap(); // consume this argument
|
|
||||||
Some(default.into())
|
|
||||||
} else {
|
|
||||||
// Some unrelated flag, like `--flag-more` or so.
|
|
||||||
None
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the next free argument or uninterpreted flag, or `None` if there are no more
|
|
||||||
/// arguments left. `--` is returned as well, but it is interpreted in the sense that no more
|
|
||||||
/// flags will be parsed after this.
|
|
||||||
pub fn get_other(&mut self) -> Option<String> {
|
|
||||||
if self.terminated {
|
|
||||||
return self.args.next();
|
|
||||||
}
|
|
||||||
let next = self.args.next()?;
|
|
||||||
if next == "--" {
|
|
||||||
self.terminated = true; // don't parse any more flags
|
|
||||||
// This is where our parser is special, we do yield the `--`.
|
|
||||||
}
|
|
||||||
Some(next)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the rest of the aguments entirely unparsed.
|
|
||||||
pub fn remainder(self) -> Vec<String> {
|
|
||||||
self.args.collect()
|
|
||||||
}
|
|
||||||
}
|
|
@ -179,7 +179,8 @@ impl Command {
|
|||||||
Command::Doc { flags } => Self::doc(flags),
|
Command::Doc { flags } => Self::doc(flags),
|
||||||
Command::Fmt { flags } => Self::fmt(flags),
|
Command::Fmt { flags } => Self::fmt(flags),
|
||||||
Command::Clippy { flags } => Self::clippy(flags),
|
Command::Clippy { flags } => Self::clippy(flags),
|
||||||
Command::Bench { target, benches } => Self::bench(target, benches),
|
Command::Bench { target, no_install, benches } =>
|
||||||
|
Self::bench(target, no_install, benches),
|
||||||
Command::Toolchain { flags } => Self::toolchain(flags),
|
Command::Toolchain { flags } => Self::toolchain(flags),
|
||||||
Command::RustcPull { commit } => Self::rustc_pull(commit.clone()),
|
Command::RustcPull { commit } => Self::rustc_pull(commit.clone()),
|
||||||
Command::RustcPush { github_user, branch } => Self::rustc_push(github_user, branch),
|
Command::RustcPush { github_user, branch } => Self::rustc_push(github_user, branch),
|
||||||
@ -378,7 +379,7 @@ impl Command {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bench(target: Option<String>, benches: Vec<String>) -> Result<()> {
|
fn bench(target: Option<String>, no_install: bool, benches: Vec<String>) -> Result<()> {
|
||||||
// The hyperfine to use
|
// The hyperfine to use
|
||||||
let hyperfine = env::var("HYPERFINE");
|
let hyperfine = env::var("HYPERFINE");
|
||||||
let hyperfine = hyperfine.as_deref().unwrap_or("hyperfine -w 1 -m 5 --shell=none");
|
let hyperfine = hyperfine.as_deref().unwrap_or("hyperfine -w 1 -m 5 --shell=none");
|
||||||
@ -386,8 +387,10 @@ impl Command {
|
|||||||
let Some((program_name, args)) = hyperfine.split_first() else {
|
let Some((program_name, args)) = hyperfine.split_first() else {
|
||||||
bail!("expected HYPERFINE environment variable to be non-empty");
|
bail!("expected HYPERFINE environment variable to be non-empty");
|
||||||
};
|
};
|
||||||
|
if !no_install {
|
||||||
// Make sure we have an up-to-date Miri installed and selected the right toolchain.
|
// Make sure we have an up-to-date Miri installed and selected the right toolchain.
|
||||||
Self::install(vec![])?;
|
Self::install(vec![])?;
|
||||||
|
}
|
||||||
|
|
||||||
let sh = Shell::new()?;
|
let sh = Shell::new()?;
|
||||||
sh.change_dir(miri_dir()?);
|
sh.change_dir(miri_dir()?);
|
||||||
@ -409,6 +412,7 @@ impl Command {
|
|||||||
OsString::new()
|
OsString::new()
|
||||||
};
|
};
|
||||||
let target_flag = &target_flag;
|
let target_flag = &target_flag;
|
||||||
|
let toolchain = active_toolchain()?;
|
||||||
// Run the requested benchmarks
|
// Run the requested benchmarks
|
||||||
for bench in benches {
|
for bench in benches {
|
||||||
let current_bench = path!(benches_dir / bench / "Cargo.toml");
|
let current_bench = path!(benches_dir / bench / "Cargo.toml");
|
||||||
@ -416,7 +420,7 @@ impl Command {
|
|||||||
// That seems to make Windows CI happy.
|
// That seems to make Windows CI happy.
|
||||||
cmd!(
|
cmd!(
|
||||||
sh,
|
sh,
|
||||||
"{program_name} {args...} 'cargo miri run '{target_flag}' --manifest-path \"'{current_bench}'\"'"
|
"{program_name} {args...} 'cargo +'{toolchain}' miri run '{target_flag}' --manifest-path \"'{current_bench}'\"'"
|
||||||
)
|
)
|
||||||
.run()?;
|
.run()?;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#![allow(clippy::needless_question_mark)]
|
#![allow(clippy::needless_question_mark)]
|
||||||
|
|
||||||
mod args;
|
|
||||||
mod commands;
|
mod commands;
|
||||||
mod coverage;
|
mod coverage;
|
||||||
mod util;
|
mod util;
|
||||||
@ -8,250 +7,191 @@ mod util;
|
|||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use anyhow::{Context, Result, anyhow, bail};
|
use anyhow::{Context, Result, anyhow, bail};
|
||||||
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
/// Parses a seed range
|
||||||
pub enum Command {
|
///
|
||||||
/// Installs the miri driver and cargo-miri.
|
/// This function is used for the `--many-seeds` flag. It expects the range in the form
|
||||||
/// Sets up the rpath such that the installed binary should work in any
|
/// `<from>..<to>`. `<from>` is inclusive, `<to>` is exclusive. `<from>` can be omitted,
|
||||||
/// working directory. Note that the binaries are placed in the `miri` toolchain
|
/// in which case it is assumed to be `0`.
|
||||||
/// sysroot, to prevent conflicts with other toolchains.
|
fn parse_range(val: &str) -> anyhow::Result<Range<u32>> {
|
||||||
Install {
|
let (from, to) = val
|
||||||
/// Flags that are passed through to `cargo install`.
|
.split_once("..")
|
||||||
flags: Vec<String>,
|
.ok_or_else(|| anyhow!("invalid format for `--many-seeds`: expected `from..to`"))?;
|
||||||
},
|
|
||||||
/// Just build miri.
|
|
||||||
Build {
|
|
||||||
/// Flags that are passed through to `cargo build`.
|
|
||||||
flags: Vec<String>,
|
|
||||||
},
|
|
||||||
/// Just check miri.
|
|
||||||
Check {
|
|
||||||
/// Flags that are passed through to `cargo check`.
|
|
||||||
flags: Vec<String>,
|
|
||||||
},
|
|
||||||
/// Build miri, set up a sysroot and then run the test suite.
|
|
||||||
Test {
|
|
||||||
bless: bool,
|
|
||||||
/// The cross-interpretation target.
|
|
||||||
/// If none then the host is the target.
|
|
||||||
target: Option<String>,
|
|
||||||
/// Produce coverage report if set.
|
|
||||||
coverage: bool,
|
|
||||||
/// Flags that are passed through to the test harness.
|
|
||||||
flags: Vec<String>,
|
|
||||||
},
|
|
||||||
/// Build miri, set up a sysroot and then run the driver with the given <flags>.
|
|
||||||
/// (Also respects MIRIFLAGS environment variable.)
|
|
||||||
Run {
|
|
||||||
dep: bool,
|
|
||||||
verbose: bool,
|
|
||||||
many_seeds: Option<Range<u32>>,
|
|
||||||
target: Option<String>,
|
|
||||||
edition: Option<String>,
|
|
||||||
/// Flags that are passed through to `miri`.
|
|
||||||
flags: Vec<String>,
|
|
||||||
},
|
|
||||||
/// Build documentation
|
|
||||||
Doc {
|
|
||||||
/// Flags that are passed through to `cargo doc`.
|
|
||||||
flags: Vec<String>,
|
|
||||||
},
|
|
||||||
/// Format all sources and tests.
|
|
||||||
Fmt {
|
|
||||||
/// Flags that are passed through to `rustfmt`.
|
|
||||||
flags: Vec<String>,
|
|
||||||
},
|
|
||||||
/// Runs clippy on all sources.
|
|
||||||
Clippy {
|
|
||||||
/// Flags that are passed through to `cargo clippy`.
|
|
||||||
flags: Vec<String>,
|
|
||||||
},
|
|
||||||
/// Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
|
|
||||||
Bench {
|
|
||||||
target: Option<String>,
|
|
||||||
/// List of benchmarks to run. By default all benchmarks are run.
|
|
||||||
benches: Vec<String>,
|
|
||||||
},
|
|
||||||
/// Update and activate the rustup toolchain 'miri' to the commit given in the
|
|
||||||
/// `rust-version` file.
|
|
||||||
/// `rustup-toolchain-install-master` must be installed for this to work. Any extra
|
|
||||||
/// flags are passed to `rustup-toolchain-install-master`.
|
|
||||||
Toolchain { flags: Vec<String> },
|
|
||||||
/// Pull and merge Miri changes from the rustc repo. Defaults to fetching the latest
|
|
||||||
/// rustc commit. The fetched commit is stored in the `rust-version` file, so the
|
|
||||||
/// next `./miri toolchain` will install the rustc that just got pulled.
|
|
||||||
RustcPull { commit: Option<String> },
|
|
||||||
/// Push Miri changes back to the rustc repo. This will pull a copy of the rustc
|
|
||||||
/// history into the Miri repo, unless you set the RUSTC_GIT env var to an existing
|
|
||||||
/// clone of the rustc repo.
|
|
||||||
RustcPush { github_user: String, branch: String },
|
|
||||||
}
|
|
||||||
|
|
||||||
const HELP: &str = r#" COMMANDS
|
|
||||||
|
|
||||||
./miri build <flags>:
|
|
||||||
Just build miri. <flags> are passed to `cargo build`.
|
|
||||||
|
|
||||||
./miri check <flags>:
|
|
||||||
Just check miri. <flags> are passed to `cargo check`.
|
|
||||||
|
|
||||||
./miri test [--bless] [--target <target>] <flags>:
|
|
||||||
Build miri, set up a sysroot and then run the test suite.
|
|
||||||
<flags> are passed to the test harness.
|
|
||||||
|
|
||||||
./miri run [--dep] [-v|--verbose] [--many-seeds|--many-seeds=..to|--many-seeds=from..to] <flags>:
|
|
||||||
Build miri, set up a sysroot and then run the driver with the given <flags>.
|
|
||||||
(Also respects MIRIFLAGS environment variable.)
|
|
||||||
If `--many-seeds` is present, Miri is run many times in parallel with different seeds.
|
|
||||||
The range defaults to `0..64`.
|
|
||||||
|
|
||||||
./miri fmt <flags>:
|
|
||||||
Format all sources and tests. <flags> are passed to `rustfmt`.
|
|
||||||
|
|
||||||
./miri clippy <flags>:
|
|
||||||
Runs clippy on all sources. <flags> are passed to `cargo clippy`.
|
|
||||||
|
|
||||||
./miri cargo <flags>:
|
|
||||||
Runs just `cargo <flags>` with the Miri-specific environment variables.
|
|
||||||
Mainly meant to be invoked by rust-analyzer.
|
|
||||||
|
|
||||||
./miri install <flags>:
|
|
||||||
Installs the miri driver and cargo-miri. <flags> are passed to `cargo
|
|
||||||
install`. Sets up the rpath such that the installed binary should work in any
|
|
||||||
working directory. Note that the binaries are placed in the `miri` toolchain
|
|
||||||
sysroot, to prevent conflicts with other toolchains.
|
|
||||||
|
|
||||||
./miri bench [--target <target>] <benches>:
|
|
||||||
Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
|
|
||||||
<benches> can explicitly list the benchmarks to run; by default, all of them are run.
|
|
||||||
|
|
||||||
./miri toolchain <flags>:
|
|
||||||
Update and activate the rustup toolchain 'miri' to the commit given in the
|
|
||||||
`rust-version` file.
|
|
||||||
`rustup-toolchain-install-master` must be installed for this to work. Any extra
|
|
||||||
flags are passed to `rustup-toolchain-install-master`.
|
|
||||||
|
|
||||||
./miri rustc-pull <commit>:
|
|
||||||
Pull and merge Miri changes from the rustc repo. Defaults to fetching the latest
|
|
||||||
rustc commit. The fetched commit is stored in the `rust-version` file, so the
|
|
||||||
next `./miri toolchain` will install the rustc that just got pulled.
|
|
||||||
|
|
||||||
./miri rustc-push <github user> [<branch>]:
|
|
||||||
Push Miri changes back to the rustc repo. This will pull a copy of the rustc
|
|
||||||
history into the Miri repo, unless you set the RUSTC_GIT env var to an existing
|
|
||||||
clone of the rustc repo. The branch defaults to `miri-sync`.
|
|
||||||
|
|
||||||
ENVIRONMENT VARIABLES
|
|
||||||
|
|
||||||
MIRI_SYSROOT:
|
|
||||||
If already set, the "sysroot setup" step is skipped.
|
|
||||||
|
|
||||||
CARGO_EXTRA_FLAGS:
|
|
||||||
Pass extra flags to all cargo invocations. (Ignored by `./miri cargo`.)"#;
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
|
||||||
// We are hand-rolling our own argument parser, since `clap` can't express what we need
|
|
||||||
// (https://github.com/clap-rs/clap/issues/5055).
|
|
||||||
let mut args = args::Args::new();
|
|
||||||
let command = match args.next_raw().as_deref() {
|
|
||||||
Some("build") => Command::Build { flags: args.remainder() },
|
|
||||||
Some("check") => Command::Check { flags: args.remainder() },
|
|
||||||
Some("doc") => Command::Doc { flags: args.remainder() },
|
|
||||||
Some("test") => {
|
|
||||||
let mut target = None;
|
|
||||||
let mut bless = false;
|
|
||||||
let mut flags = Vec::new();
|
|
||||||
let mut coverage = false;
|
|
||||||
loop {
|
|
||||||
if args.get_long_flag("bless")? {
|
|
||||||
bless = true;
|
|
||||||
} else if args.get_long_flag("coverage")? {
|
|
||||||
coverage = true;
|
|
||||||
} else if let Some(val) = args.get_long_opt("target")? {
|
|
||||||
target = Some(val);
|
|
||||||
} else if let Some(flag) = args.get_other() {
|
|
||||||
flags.push(flag);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Command::Test { bless, flags, target, coverage }
|
|
||||||
}
|
|
||||||
Some("run") => {
|
|
||||||
let mut dep = false;
|
|
||||||
let mut verbose = false;
|
|
||||||
let mut many_seeds = None;
|
|
||||||
let mut target = None;
|
|
||||||
let mut edition = None;
|
|
||||||
let mut flags = Vec::new();
|
|
||||||
loop {
|
|
||||||
if args.get_long_flag("dep")? {
|
|
||||||
dep = true;
|
|
||||||
} else if args.get_long_flag("verbose")? || args.get_short_flag('v')? {
|
|
||||||
verbose = true;
|
|
||||||
} else if let Some(val) = args.get_long_opt_with_default("many-seeds", "0..64")? {
|
|
||||||
let (from, to) = val.split_once("..").ok_or_else(|| {
|
|
||||||
anyhow!("invalid format for `--many-seeds`: expected `from..to`")
|
|
||||||
})?;
|
|
||||||
let from: u32 = if from.is_empty() {
|
let from: u32 = if from.is_empty() {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
from.parse().context("invalid `from` in `--many-seeds=from..to")?
|
from.parse().context("invalid `from` in `--many-seeds=from..to")?
|
||||||
};
|
};
|
||||||
let to: u32 = to.parse().context("invalid `to` in `--many-seeds=from..to")?;
|
let to: u32 = to.parse().context("invalid `to` in `--many-seeds=from..to")?;
|
||||||
many_seeds = Some(from..to);
|
Ok(from..to)
|
||||||
} else if let Some(val) = args.get_long_opt("target")? {
|
}
|
||||||
target = Some(val);
|
|
||||||
} else if let Some(val) = args.get_long_opt("edition")? {
|
#[derive(Clone, Debug, Subcommand)]
|
||||||
edition = Some(val);
|
pub enum Command {
|
||||||
} else if let Some(flag) = args.get_other() {
|
/// Installs the miri driver and cargo-miri to the sysroot of the active toolchain.
|
||||||
flags.push(flag);
|
///
|
||||||
} else {
|
/// Sets up the rpath such that the installed binary should work in any
|
||||||
break;
|
/// working directory.
|
||||||
|
Install {
|
||||||
|
/// Flags that are passed through to `cargo install`.
|
||||||
|
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||||
|
flags: Vec<String>,
|
||||||
|
},
|
||||||
|
/// Build Miri.
|
||||||
|
Build {
|
||||||
|
/// Flags that are passed through to `cargo build`.
|
||||||
|
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||||
|
flags: Vec<String>,
|
||||||
|
},
|
||||||
|
/// Check Miri.
|
||||||
|
Check {
|
||||||
|
/// Flags that are passed through to `cargo check`.
|
||||||
|
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||||
|
flags: Vec<String>,
|
||||||
|
},
|
||||||
|
/// Check Miri with Clippy.
|
||||||
|
Clippy {
|
||||||
|
/// Flags that are passed through to `cargo clippy`.
|
||||||
|
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||||
|
flags: Vec<String>,
|
||||||
|
},
|
||||||
|
/// Run the Miri test suite.
|
||||||
|
Test {
|
||||||
|
/// Update stdout/stderr reference files.
|
||||||
|
#[arg(long)]
|
||||||
|
bless: bool,
|
||||||
|
/// The cross-interpretation target.
|
||||||
|
#[arg(long)]
|
||||||
|
target: Option<String>,
|
||||||
|
/// Produce coverage report.
|
||||||
|
#[arg(long)]
|
||||||
|
coverage: bool,
|
||||||
|
/// Flags that are passed through to the test harness.
|
||||||
|
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||||
|
flags: Vec<String>,
|
||||||
|
},
|
||||||
|
/// Run the Miri driver.
|
||||||
|
///
|
||||||
|
/// Also respects MIRIFLAGS environment variable.
|
||||||
|
Run {
|
||||||
|
/// Build the program with the dependencies declared in `test_dependencies/Cargo.toml`.
|
||||||
|
#[arg(long)]
|
||||||
|
dep: bool,
|
||||||
|
/// Show build progress.
|
||||||
|
#[arg(long, short)]
|
||||||
|
verbose: bool,
|
||||||
|
/// Run the driver with the seeds in the given range (`..to` or `from..to`, default: `0..64`).
|
||||||
|
#[arg(long, value_parser = parse_range)]
|
||||||
|
many_seeds: Option<Range<u32>>,
|
||||||
|
/// The cross-interpretation target.
|
||||||
|
#[arg(long)]
|
||||||
|
target: Option<String>,
|
||||||
|
/// The Rust edition.
|
||||||
|
#[arg(long)]
|
||||||
|
edition: Option<String>,
|
||||||
|
/// Flags that are passed through to `miri`.
|
||||||
|
///
|
||||||
|
/// The flags set in `MIRIFLAGS` are added in front of these flags.
|
||||||
|
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||||
|
flags: Vec<String>,
|
||||||
|
},
|
||||||
|
/// Build documentation.
|
||||||
|
Doc {
|
||||||
|
/// Flags that are passed through to `cargo doc`.
|
||||||
|
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||||
|
flags: Vec<String>,
|
||||||
|
},
|
||||||
|
/// Format all sources and tests.
|
||||||
|
Fmt {
|
||||||
|
/// Flags that are passed through to `rustfmt`.
|
||||||
|
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||||
|
flags: Vec<String>,
|
||||||
|
},
|
||||||
|
/// Runs the benchmarks from bench-cargo-miri in hyperfine.
|
||||||
|
///
|
||||||
|
/// hyperfine needs to be installed.
|
||||||
|
Bench {
|
||||||
|
#[arg(long)]
|
||||||
|
target: Option<String>,
|
||||||
|
/// When `true`, skip the `./miri install` step.
|
||||||
|
#[arg(long)]
|
||||||
|
no_install: bool,
|
||||||
|
/// List of benchmarks to run (default: run all benchmarks).
|
||||||
|
benches: Vec<String>,
|
||||||
|
},
|
||||||
|
/// Update and activate the rustup toolchain 'miri'.
|
||||||
|
///
|
||||||
|
/// The `rust-version` file is used to determine the commit that will be intsalled.
|
||||||
|
/// `rustup-toolchain-install-master` must be installed for this to work.
|
||||||
|
Toolchain {
|
||||||
|
/// Flags that are passed through to `rustup-toolchain-install-master`.
|
||||||
|
flags: Vec<String>,
|
||||||
|
},
|
||||||
|
/// Pull and merge Miri changes from the rustc repo.
|
||||||
|
///
|
||||||
|
/// The fetched commit is stored in the `rust-version` file, so the next `./miri toolchain` will
|
||||||
|
/// install the rustc that just got pulled.
|
||||||
|
RustcPull {
|
||||||
|
/// The commit to fetch (default: latest rustc commit).
|
||||||
|
commit: Option<String>,
|
||||||
|
},
|
||||||
|
/// Push Miri changes back to the rustc repo.
|
||||||
|
///
|
||||||
|
/// This will pull a copy of the rustc history into the Miri repo, unless you set the RUSTC_GIT
|
||||||
|
/// env var to an existing clone of the rustc repo.
|
||||||
|
RustcPush {
|
||||||
|
/// The Github user that owns the rustc fork to which we should push.
|
||||||
|
github_user: String,
|
||||||
|
/// The branch to push to.
|
||||||
|
#[arg(default_value = "miri-sync")]
|
||||||
|
branch: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Command {
|
||||||
|
fn add_remainder(&mut self, remainder: Vec<String>) -> Result<()> {
|
||||||
|
if remainder.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::Install { flags }
|
||||||
|
| Self::Build { flags }
|
||||||
|
| Self::Check { flags }
|
||||||
|
| Self::Doc { flags }
|
||||||
|
| Self::Fmt { flags }
|
||||||
|
| Self::Toolchain { flags }
|
||||||
|
| Self::Clippy { flags }
|
||||||
|
| Self::Run { flags, .. }
|
||||||
|
| Self::Test { flags, .. } => {
|
||||||
|
flags.extend(remainder);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Self::Bench { .. } | Self::RustcPull { .. } | Self::RustcPush { .. } =>
|
||||||
|
bail!("unexpected \"--\" found in arguments"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::Run { dep, verbose, many_seeds, target, edition, flags }
|
}
|
||||||
}
|
|
||||||
Some("fmt") => Command::Fmt { flags: args.remainder() },
|
#[derive(Parser)]
|
||||||
Some("clippy") => Command::Clippy { flags: args.remainder() },
|
#[command(after_help = "Environment variables:
|
||||||
Some("install") => Command::Install { flags: args.remainder() },
|
MIRI_SYSROOT: If already set, the \"sysroot setup\" step is skipped
|
||||||
Some("bench") => {
|
CARGO_EXTRA_FLAGS: Pass extra flags to all cargo invocations")]
|
||||||
let mut target = None;
|
pub struct Cli {
|
||||||
let mut benches = Vec::new();
|
#[command(subcommand)]
|
||||||
loop {
|
pub command: Command,
|
||||||
if let Some(val) = args.get_long_opt("target")? {
|
}
|
||||||
target = Some(val);
|
|
||||||
} else if let Some(flag) = args.get_other() {
|
fn main() -> Result<()> {
|
||||||
benches.push(flag);
|
// Split the arguments into the part before the `--` and the part after.
|
||||||
} else {
|
// The `--` itself ends up in the second part.
|
||||||
break;
|
let miri_args: Vec<_> = std::env::args().take_while(|x| *x != "--").collect();
|
||||||
}
|
let remainder: Vec<_> = std::env::args().skip_while(|x| *x != "--").collect();
|
||||||
}
|
|
||||||
Command::Bench { target, benches }
|
let args = Cli::parse_from(miri_args);
|
||||||
}
|
let mut command = args.command;
|
||||||
Some("toolchain") => Command::Toolchain { flags: args.remainder() },
|
command.add_remainder(remainder)?;
|
||||||
Some("rustc-pull") => {
|
|
||||||
let commit = args.next_raw();
|
|
||||||
if args.next_raw().is_some() {
|
|
||||||
bail!("Too many arguments for `./miri rustc-pull`");
|
|
||||||
}
|
|
||||||
Command::RustcPull { commit }
|
|
||||||
}
|
|
||||||
Some("rustc-push") => {
|
|
||||||
let github_user = args.next_raw().ok_or_else(|| {
|
|
||||||
anyhow!("Missing first argument for `./miri rustc-push GITHUB_USER [BRANCH]`")
|
|
||||||
})?;
|
|
||||||
let branch = args.next_raw().unwrap_or_else(|| "miri-sync".into());
|
|
||||||
if args.next_raw().is_some() {
|
|
||||||
bail!("Too many arguments for `./miri rustc-push GITHUB_USER BRANCH`");
|
|
||||||
}
|
|
||||||
Command::RustcPush { github_user, branch }
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
eprintln!("Unknown or missing command. Usage:\n\n{HELP}");
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
command.exec()?;
|
command.exec()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
728f2daab42ba8f1b3d5caab62495798d1eabfa1
|
13170cd787cb733ed24842ee825bcbd98dc01476
|
||||||
|
@ -237,6 +237,10 @@ impl Permission {
|
|||||||
pub fn is_active(&self) -> bool {
|
pub fn is_active(&self) -> bool {
|
||||||
self.inner == Active
|
self.inner == Active
|
||||||
}
|
}
|
||||||
|
/// Check if `self` is the never-allow-writes-again state of a pointer (is `Frozen`).
|
||||||
|
pub fn is_frozen(&self) -> bool {
|
||||||
|
self.inner == Frozen
|
||||||
|
}
|
||||||
|
|
||||||
/// Default initial permission of the root of a new tree at inbounds positions.
|
/// Default initial permission of the root of a new tree at inbounds positions.
|
||||||
/// Must *only* be used for the root, this is not in general an "initial" permission!
|
/// Must *only* be used for the root, this is not in general an "initial" permission!
|
||||||
|
@ -153,8 +153,31 @@ impl LocationState {
|
|||||||
) -> ContinueTraversal {
|
) -> ContinueTraversal {
|
||||||
if rel_pos.is_foreign() {
|
if rel_pos.is_foreign() {
|
||||||
let happening_now = IdempotentForeignAccess::from_foreign(access_kind);
|
let happening_now = IdempotentForeignAccess::from_foreign(access_kind);
|
||||||
let new_access_noop =
|
let mut new_access_noop =
|
||||||
self.idempotent_foreign_access.can_skip_foreign_access(happening_now);
|
self.idempotent_foreign_access.can_skip_foreign_access(happening_now);
|
||||||
|
if self.permission.is_disabled() {
|
||||||
|
// A foreign access to a `Disabled` tag will have almost no observable effect.
|
||||||
|
// It's a theorem that `Disabled` node have no protected initialized children,
|
||||||
|
// and so this foreign access will never trigger any protector.
|
||||||
|
// (Intuition: You're either protected initialized, and thus can't become Disabled
|
||||||
|
// or you're already Disabled protected, but not initialized, and then can't
|
||||||
|
// become initialized since that requires a child access, which Disabled blocks.)
|
||||||
|
// Further, the children will never be able to read or write again, since they
|
||||||
|
// have a `Disabled` parent. So this only affects diagnostics, such that the
|
||||||
|
// blocking write will still be identified directly, just at a different tag.
|
||||||
|
new_access_noop = true;
|
||||||
|
}
|
||||||
|
if self.permission.is_frozen() && access_kind == AccessKind::Read {
|
||||||
|
// A foreign read to a `Frozen` tag will have almost no observable effect.
|
||||||
|
// It's a theorem that `Frozen` nodes have no active children, so all children
|
||||||
|
// already survive foreign reads. Foreign reads in general have almost no
|
||||||
|
// effect, the only further thing they could do is make protected `Reserved`
|
||||||
|
// nodes become conflicted, i.e. make them reject child writes for the further
|
||||||
|
// duration of their protector. But such a child write is already rejected
|
||||||
|
// because this node is frozen. So this only affects diagnostics, but the
|
||||||
|
// blocking read will still be identified directly, just at a different tag.
|
||||||
|
new_access_noop = true;
|
||||||
|
}
|
||||||
if new_access_noop {
|
if new_access_noop {
|
||||||
// Abort traversal if the new access is indeed guaranteed
|
// Abort traversal if the new access is indeed guaranteed
|
||||||
// to be noop.
|
// to be noop.
|
||||||
|
@ -159,6 +159,8 @@ pub enum BlockReason {
|
|||||||
Epoll,
|
Epoll,
|
||||||
/// Blocked on eventfd.
|
/// Blocked on eventfd.
|
||||||
Eventfd,
|
Eventfd,
|
||||||
|
/// Blocked on unnamed_socket.
|
||||||
|
UnnamedSocket,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The state of a thread.
|
/// The state of a thread.
|
||||||
|
@ -126,6 +126,7 @@ pub enum NonHaltingDiagnostic {
|
|||||||
Int2Ptr {
|
Int2Ptr {
|
||||||
details: bool,
|
details: bool,
|
||||||
},
|
},
|
||||||
|
NativeCallSharedMem,
|
||||||
WeakMemoryOutdatedLoad {
|
WeakMemoryOutdatedLoad {
|
||||||
ptr: Pointer,
|
ptr: Pointer,
|
||||||
},
|
},
|
||||||
@ -602,6 +603,8 @@ impl<'tcx> MiriMachine<'tcx> {
|
|||||||
RejectedIsolatedOp(_) =>
|
RejectedIsolatedOp(_) =>
|
||||||
("operation rejected by isolation".to_string(), DiagLevel::Warning),
|
("operation rejected by isolation".to_string(), DiagLevel::Warning),
|
||||||
Int2Ptr { .. } => ("integer-to-pointer cast".to_string(), DiagLevel::Warning),
|
Int2Ptr { .. } => ("integer-to-pointer cast".to_string(), DiagLevel::Warning),
|
||||||
|
NativeCallSharedMem =>
|
||||||
|
("sharing memory with a native function".to_string(), DiagLevel::Warning),
|
||||||
ExternTypeReborrow =>
|
ExternTypeReborrow =>
|
||||||
("reborrow of reference to `extern type`".to_string(), DiagLevel::Warning),
|
("reborrow of reference to `extern type`".to_string(), DiagLevel::Warning),
|
||||||
CreatedPointerTag(..)
|
CreatedPointerTag(..)
|
||||||
@ -637,6 +640,7 @@ impl<'tcx> MiriMachine<'tcx> {
|
|||||||
ProgressReport { .. } =>
|
ProgressReport { .. } =>
|
||||||
format!("progress report: current operation being executed is here"),
|
format!("progress report: current operation being executed is here"),
|
||||||
Int2Ptr { .. } => format!("integer-to-pointer cast"),
|
Int2Ptr { .. } => format!("integer-to-pointer cast"),
|
||||||
|
NativeCallSharedMem => format!("sharing memory with a native function called via FFI"),
|
||||||
WeakMemoryOutdatedLoad { ptr } =>
|
WeakMemoryOutdatedLoad { ptr } =>
|
||||||
format!("weak memory emulation: outdated value returned from load at {ptr}"),
|
format!("weak memory emulation: outdated value returned from load at {ptr}"),
|
||||||
ExternTypeReborrow =>
|
ExternTypeReborrow =>
|
||||||
@ -679,7 +683,29 @@ impl<'tcx> MiriMachine<'tcx> {
|
|||||||
}
|
}
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
NativeCallSharedMem => {
|
||||||
|
vec![
|
||||||
|
note!(
|
||||||
|
"when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory"
|
||||||
|
),
|
||||||
|
note!(
|
||||||
|
"in particular, Miri assumes that the native call initializes all memory it has access to"
|
||||||
|
),
|
||||||
|
note!(
|
||||||
|
"Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory"
|
||||||
|
),
|
||||||
|
note!(
|
||||||
|
"what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
ExternTypeReborrow => {
|
ExternTypeReborrow => {
|
||||||
|
assert!(self.borrow_tracker.as_ref().is_some_and(|b| {
|
||||||
|
matches!(
|
||||||
|
b.borrow().borrow_tracker_method(),
|
||||||
|
BorrowTrackerMethod::StackedBorrows
|
||||||
|
)
|
||||||
|
}));
|
||||||
vec![
|
vec![
|
||||||
note!(
|
note!(
|
||||||
"`extern type` are not compatible with the Stacked Borrows aliasing model implemented by Miri; Miri may miss bugs in this code"
|
"`extern type` are not compatible with the Stacked Borrows aliasing model implemented by Miri; Miri may miss bugs in this code"
|
||||||
|
@ -310,18 +310,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.layout_of(array_ty).unwrap()
|
this.layout_of(array_ty).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Project to the given *named* field (which must be a struct or union type).
|
||||||
|
fn try_project_field_named<P: Projectable<'tcx, Provenance>>(
|
||||||
|
&self,
|
||||||
|
base: &P,
|
||||||
|
name: &str,
|
||||||
|
) -> InterpResult<'tcx, Option<P>> {
|
||||||
|
let this = self.eval_context_ref();
|
||||||
|
let adt = base.layout().ty.ty_adt_def().unwrap();
|
||||||
|
for (idx, field) in adt.non_enum_variant().fields.iter().enumerate() {
|
||||||
|
if field.name.as_str() == name {
|
||||||
|
return interp_ok(Some(this.project_field(base, idx)?));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interp_ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
/// Project to the given *named* field (which must be a struct or union type).
|
/// Project to the given *named* field (which must be a struct or union type).
|
||||||
fn project_field_named<P: Projectable<'tcx, Provenance>>(
|
fn project_field_named<P: Projectable<'tcx, Provenance>>(
|
||||||
&self,
|
&self,
|
||||||
base: &P,
|
base: &P,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> InterpResult<'tcx, P> {
|
) -> InterpResult<'tcx, P> {
|
||||||
let this = self.eval_context_ref();
|
if let Some(field) = self.try_project_field_named(base, name)? {
|
||||||
let adt = base.layout().ty.ty_adt_def().unwrap();
|
return interp_ok(field);
|
||||||
for (idx, field) in adt.non_enum_variant().fields.iter().enumerate() {
|
|
||||||
if field.name.as_str() == name {
|
|
||||||
return this.project_field(base, idx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
bug!("No field named {} in type {}", name, base.layout().ty);
|
bug!("No field named {} in type {}", name, base.layout().ty);
|
||||||
}
|
}
|
||||||
@ -332,13 +344,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
base: &P,
|
base: &P,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let adt = base.layout().ty.ty_adt_def().unwrap();
|
self.try_project_field_named(base, name).unwrap().is_some()
|
||||||
for field in adt.non_enum_variant().fields.iter() {
|
|
||||||
if field.name.as_str() == name {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write an int of the appropriate size to `dest`. The target type may be signed or unsigned,
|
/// Write an int of the appropriate size to `dest`. The target type may be signed or unsigned,
|
||||||
@ -921,7 +927,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
if fn_abi.conv != exp_abi {
|
if fn_abi.conv != exp_abi {
|
||||||
throw_ub_format!(
|
throw_ub_format!(
|
||||||
"calling a function with ABI {:?} using caller ABI {:?}",
|
"calling a function with ABI {:?} using caller ABI {:?}",
|
||||||
exp_abi, fn_abi.conv);
|
exp_abi,
|
||||||
|
fn_abi.conv
|
||||||
|
);
|
||||||
}
|
}
|
||||||
interp_ok(())
|
interp_ok(())
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ use rand::{Rng, SeedableRng};
|
|||||||
use rustc_abi::{Align, ExternAbi, Size};
|
use rustc_abi::{Align, ExternAbi, Size};
|
||||||
use rustc_attr_parsing::InlineAttr;
|
use rustc_attr_parsing::InlineAttr;
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||||
use rustc_target::callconv::FnAbi;
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
use rustc_data_structures::static_assert_size;
|
use rustc_data_structures::static_assert_size;
|
||||||
use rustc_middle::mir;
|
use rustc_middle::mir;
|
||||||
@ -25,6 +24,7 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
|||||||
use rustc_session::config::InliningThreshold;
|
use rustc_session::config::InliningThreshold;
|
||||||
use rustc_span::def_id::{CrateNum, DefId};
|
use rustc_span::def_id::{CrateNum, DefId};
|
||||||
use rustc_span::{Span, SpanData, Symbol};
|
use rustc_span::{Span, SpanData, Symbol};
|
||||||
|
use rustc_target::callconv::FnAbi;
|
||||||
|
|
||||||
use crate::concurrency::cpu_affinity::{self, CpuAffinityMask};
|
use crate::concurrency::cpu_affinity::{self, CpuAffinityMask};
|
||||||
use crate::concurrency::data_race::{self, NaReadType, NaWriteType};
|
use crate::concurrency::data_race::{self, NaReadType, NaWriteType};
|
||||||
|
@ -9,12 +9,11 @@ use rustc_ast::expand::allocator::alloc_error_handler_name;
|
|||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::def_id::CrateNum;
|
use rustc_hir::def_id::CrateNum;
|
||||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||||
use rustc_middle::{mir, ty};
|
|
||||||
use rustc_middle::ty::Ty;
|
use rustc_middle::ty::Ty;
|
||||||
|
use rustc_middle::{mir, ty};
|
||||||
use rustc_span::Symbol;
|
use rustc_span::Symbol;
|
||||||
use rustc_target::callconv::{Conv, FnAbi};
|
use rustc_target::callconv::{Conv, FnAbi};
|
||||||
|
|
||||||
|
|
||||||
use self::helpers::{ToHost, ToSoft};
|
use self::helpers::{ToHost, ToSoft};
|
||||||
use super::alloc::EvalContextExt as _;
|
use super::alloc::EvalContextExt as _;
|
||||||
use super::backtrace::EvalContextExt as _;
|
use super::backtrace::EvalContextExt as _;
|
||||||
@ -312,8 +311,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
"miri_pointer_name" => {
|
"miri_pointer_name" => {
|
||||||
// This associates a name to a tag. Very useful for debugging, and also makes
|
// This associates a name to a tag. Very useful for debugging, and also makes
|
||||||
// tests more strict.
|
// tests more strict.
|
||||||
let [ptr, nth_parent, name] =
|
let [ptr, nth_parent, name] = this.check_shim(abi, Conv::Rust, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::Rust, link_name, args)?;
|
|
||||||
let ptr = this.read_pointer(ptr)?;
|
let ptr = this.read_pointer(ptr)?;
|
||||||
let nth_parent = this.read_scalar(nth_parent)?.to_u8()?;
|
let nth_parent = this.read_scalar(nth_parent)?.to_u8()?;
|
||||||
let name = this.read_immediate(name)?;
|
let name = this.read_immediate(name)?;
|
||||||
@ -337,8 +335,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.machine.static_roots.push(alloc_id);
|
this.machine.static_roots.push(alloc_id);
|
||||||
}
|
}
|
||||||
"miri_host_to_target_path" => {
|
"miri_host_to_target_path" => {
|
||||||
let [ptr, out, out_size] =
|
let [ptr, out, out_size] = this.check_shim(abi, Conv::Rust, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::Rust, link_name, args)?;
|
|
||||||
let ptr = this.read_pointer(ptr)?;
|
let ptr = this.read_pointer(ptr)?;
|
||||||
let out = this.read_pointer(out)?;
|
let out = this.read_pointer(out)?;
|
||||||
let out_size = this.read_scalar(out_size)?.to_target_usize(this)?;
|
let out_size = this.read_scalar(out_size)?.to_target_usize(this)?;
|
||||||
@ -429,13 +426,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
// Aborting the process.
|
// Aborting the process.
|
||||||
"exit" => {
|
"exit" => {
|
||||||
let [code] =
|
let [code] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
|
||||||
let code = this.read_scalar(code)?.to_i32()?;
|
let code = this.read_scalar(code)?.to_i32()?;
|
||||||
throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false });
|
throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false });
|
||||||
}
|
}
|
||||||
"abort" => {
|
"abort" => {
|
||||||
let [] = this.check_shim(abi, Conv::C , link_name, args)?;
|
let [] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
throw_machine_stop!(TerminationInfo::Abort(
|
throw_machine_stop!(TerminationInfo::Abort(
|
||||||
"the program aborted execution".to_owned()
|
"the program aborted execution".to_owned()
|
||||||
))
|
))
|
||||||
@ -443,8 +439,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
// Standard C allocation
|
// Standard C allocation
|
||||||
"malloc" => {
|
"malloc" => {
|
||||||
let [size] =
|
let [size] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
|
||||||
let size = this.read_target_usize(size)?;
|
let size = this.read_target_usize(size)?;
|
||||||
if size <= this.max_size_of_val().bytes() {
|
if size <= this.max_size_of_val().bytes() {
|
||||||
let res = this.malloc(size, /*zero_init:*/ false)?;
|
let res = this.malloc(size, /*zero_init:*/ false)?;
|
||||||
@ -458,8 +453,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"calloc" => {
|
"calloc" => {
|
||||||
let [items, elem_size] =
|
let [items, elem_size] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
|
||||||
let items = this.read_target_usize(items)?;
|
let items = this.read_target_usize(items)?;
|
||||||
let elem_size = this.read_target_usize(elem_size)?;
|
let elem_size = this.read_target_usize(elem_size)?;
|
||||||
if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) {
|
if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) {
|
||||||
@ -474,14 +468,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"free" => {
|
"free" => {
|
||||||
let [ptr] =
|
let [ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
|
||||||
let ptr = this.read_pointer(ptr)?;
|
let ptr = this.read_pointer(ptr)?;
|
||||||
this.free(ptr)?;
|
this.free(ptr)?;
|
||||||
}
|
}
|
||||||
"realloc" => {
|
"realloc" => {
|
||||||
let [old_ptr, new_size] =
|
let [old_ptr, new_size] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
|
||||||
let old_ptr = this.read_pointer(old_ptr)?;
|
let old_ptr = this.read_pointer(old_ptr)?;
|
||||||
let new_size = this.read_target_usize(new_size)?;
|
let new_size = this.read_target_usize(new_size)?;
|
||||||
if new_size <= this.max_size_of_val().bytes() {
|
if new_size <= this.max_size_of_val().bytes() {
|
||||||
@ -619,8 +611,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
// C memory handling functions
|
// C memory handling functions
|
||||||
"memcmp" => {
|
"memcmp" => {
|
||||||
let [left, right, n] =
|
let [left, right, n] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
|
||||||
let left = this.read_pointer(left)?;
|
let left = this.read_pointer(left)?;
|
||||||
let right = this.read_pointer(right)?;
|
let right = this.read_pointer(right)?;
|
||||||
let n = Size::from_bytes(this.read_target_usize(n)?);
|
let n = Size::from_bytes(this.read_target_usize(n)?);
|
||||||
@ -644,8 +635,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||||
}
|
}
|
||||||
"memrchr" => {
|
"memrchr" => {
|
||||||
let [ptr, val, num] =
|
let [ptr, val, num] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
|
||||||
let ptr = this.read_pointer(ptr)?;
|
let ptr = this.read_pointer(ptr)?;
|
||||||
let val = this.read_scalar(val)?.to_i32()?;
|
let val = this.read_scalar(val)?.to_i32()?;
|
||||||
let num = this.read_target_usize(num)?;
|
let num = this.read_target_usize(num)?;
|
||||||
@ -671,8 +661,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"memchr" => {
|
"memchr" => {
|
||||||
let [ptr, val, num] =
|
let [ptr, val, num] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
|
||||||
let ptr = this.read_pointer(ptr)?;
|
let ptr = this.read_pointer(ptr)?;
|
||||||
let val = this.read_scalar(val)?.to_i32()?;
|
let val = this.read_scalar(val)?.to_i32()?;
|
||||||
let num = this.read_target_usize(num)?;
|
let num = this.read_target_usize(num)?;
|
||||||
@ -695,8 +684,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"strlen" => {
|
"strlen" => {
|
||||||
let [ptr] =
|
let [ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
|
||||||
let ptr = this.read_pointer(ptr)?;
|
let ptr = this.read_pointer(ptr)?;
|
||||||
// This reads at least 1 byte, so we are already enforcing that this is a valid pointer.
|
// This reads at least 1 byte, so we are already enforcing that this is a valid pointer.
|
||||||
let n = this.read_c_str(ptr)?.len();
|
let n = this.read_c_str(ptr)?.len();
|
||||||
@ -706,8 +694,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
"wcslen" => {
|
"wcslen" => {
|
||||||
let [ptr] =
|
let [ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
|
||||||
let ptr = this.read_pointer(ptr)?;
|
let ptr = this.read_pointer(ptr)?;
|
||||||
// This reads at least 1 byte, so we are already enforcing that this is a valid pointer.
|
// This reads at least 1 byte, so we are already enforcing that this is a valid pointer.
|
||||||
let n = this.read_wchar_t_str(ptr)?.len();
|
let n = this.read_wchar_t_str(ptr)?.len();
|
||||||
@ -717,8 +704,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
"memcpy" => {
|
"memcpy" => {
|
||||||
let [ptr_dest, ptr_src, n] =
|
let [ptr_dest, ptr_src, n] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
|
||||||
let ptr_dest = this.read_pointer(ptr_dest)?;
|
let ptr_dest = this.read_pointer(ptr_dest)?;
|
||||||
let ptr_src = this.read_pointer(ptr_src)?;
|
let ptr_src = this.read_pointer(ptr_src)?;
|
||||||
let n = this.read_target_usize(n)?;
|
let n = this.read_target_usize(n)?;
|
||||||
@ -732,8 +718,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.write_pointer(ptr_dest, dest)?;
|
this.write_pointer(ptr_dest, dest)?;
|
||||||
}
|
}
|
||||||
"strcpy" => {
|
"strcpy" => {
|
||||||
let [ptr_dest, ptr_src] =
|
let [ptr_dest, ptr_src] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
|
||||||
let ptr_dest = this.read_pointer(ptr_dest)?;
|
let ptr_dest = this.read_pointer(ptr_dest)?;
|
||||||
let ptr_src = this.read_pointer(ptr_src)?;
|
let ptr_src = this.read_pointer(ptr_src)?;
|
||||||
|
|
||||||
@ -878,8 +863,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.write_scalar(res, dest)?;
|
this.write_scalar(res, dest)?;
|
||||||
}
|
}
|
||||||
"lgammaf_r" => {
|
"lgammaf_r" => {
|
||||||
let [x, signp] =
|
let [x, signp] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
|
||||||
let x = this.read_scalar(x)?.to_f32()?;
|
let x = this.read_scalar(x)?.to_f32()?;
|
||||||
let signp = this.deref_pointer(signp)?;
|
let signp = this.deref_pointer(signp)?;
|
||||||
|
|
||||||
@ -890,8 +874,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.write_scalar(res, dest)?;
|
this.write_scalar(res, dest)?;
|
||||||
}
|
}
|
||||||
"lgamma_r" => {
|
"lgamma_r" => {
|
||||||
let [x, signp] =
|
let [x, signp] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
|
||||||
let x = this.read_scalar(x)?.to_f64()?;
|
let x = this.read_scalar(x)?.to_f64()?;
|
||||||
let signp = this.deref_pointer(signp)?;
|
let signp = this.deref_pointer(signp)?;
|
||||||
|
|
||||||
@ -904,8 +887,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
// LLVM intrinsics
|
// LLVM intrinsics
|
||||||
"llvm.prefetch" => {
|
"llvm.prefetch" => {
|
||||||
let [p, rw, loc, ty] =
|
let [p, rw, loc, ty] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C , link_name, args)?;
|
|
||||||
|
|
||||||
let _ = this.read_pointer(p)?;
|
let _ = this.read_pointer(p)?;
|
||||||
let rw = this.read_scalar(rw)?.to_i32()?;
|
let rw = this.read_scalar(rw)?.to_i32()?;
|
||||||
@ -932,7 +914,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Used to implement the x86 `_mm{,256,512}_popcnt_epi{8,16,32,64}` and wasm
|
// Used to implement the x86 `_mm{,256,512}_popcnt_epi{8,16,32,64}` and wasm
|
||||||
// `{i,u}8x16_popcnt` functions.
|
// `{i,u}8x16_popcnt` functions.
|
||||||
name if name.starts_with("llvm.ctpop.v") => {
|
name if name.starts_with("llvm.ctpop.v") => {
|
||||||
let [op] = this.check_shim(abi, Conv::C , link_name, args)?;
|
let [op] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
|
|
||||||
let (op, op_len) = this.project_to_simd(op)?;
|
let (op, op_len) = this.project_to_simd(op)?;
|
||||||
let (dest, dest_len) = this.project_to_simd(dest)?;
|
let (dest, dest_len) = this.project_to_simd(dest)?;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
//! Implements calling functions from a native library.
|
//! Implements calling functions from a native library.
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use libffi::high::call as ffi;
|
use libffi::high::call as ffi;
|
||||||
@ -172,6 +173,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Wildcard pointer, whatever it points to must be already exposed.
|
// Wildcard pointer, whatever it points to must be already exposed.
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
// The first time this happens at a particular location, print a warning.
|
||||||
|
thread_local! {
|
||||||
|
static HAVE_WARNED: RefCell<bool> = const { RefCell::new(false) };
|
||||||
|
}
|
||||||
|
HAVE_WARNED.with_borrow_mut(|have_warned| {
|
||||||
|
if !*have_warned {
|
||||||
|
// Newly inserted, so first time we see this span.
|
||||||
|
this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem);
|
||||||
|
*have_warned = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.prepare_for_native_call(alloc_id, prov)?;
|
this.prepare_for_native_call(alloc_id, prov)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,6 +180,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
if !matches!(&*this.tcx.sess.target.os, "solaris" | "illumos") {
|
if !matches!(&*this.tcx.sess.target.os, "solaris" | "illumos") {
|
||||||
// tm_zone represents the timezone value in the form of: +0730, +08, -0730 or -08.
|
// tm_zone represents the timezone value in the form of: +0730, +08, -0730 or -08.
|
||||||
// This may not be consistent with libc::localtime_r's result.
|
// This may not be consistent with libc::localtime_r's result.
|
||||||
|
|
||||||
let offset_in_seconds = dt.offset().fix().local_minus_utc();
|
let offset_in_seconds = dt.offset().fix().local_minus_utc();
|
||||||
let tm_gmtoff = offset_in_seconds;
|
let tm_gmtoff = offset_in_seconds;
|
||||||
let mut tm_zone = String::new();
|
let mut tm_zone = String::new();
|
||||||
@ -195,11 +196,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
write!(tm_zone, "{:02}", offset_min).unwrap();
|
write!(tm_zone, "{:02}", offset_min).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: String de-duplication is needed so that we only allocate this string only once
|
// Add null terminator for C string compatibility.
|
||||||
// even when there are multiple calls to this function.
|
tm_zone.push('\0');
|
||||||
let tm_zone_ptr = this
|
|
||||||
.alloc_os_str_as_c_str(&OsString::from(tm_zone), MiriMemoryKind::Machine.into())?;
|
|
||||||
|
|
||||||
|
// Deduplicate and allocate the string.
|
||||||
|
let tm_zone_ptr = this.allocate_bytes_dedup(tm_zone.as_bytes())?;
|
||||||
|
|
||||||
|
// Write the timezone pointer and offset into the result structure.
|
||||||
this.write_pointer(tm_zone_ptr, &this.project_field_named(&result, "tm_zone")?)?;
|
this.write_pointer(tm_zone_ptr, &this.project_field_named(&result, "tm_zone")?)?;
|
||||||
this.write_int_fields_named(&[("tm_gmtoff", tm_gmtoff.into())], &result)?;
|
this.write_int_fields_named(&[("tm_gmtoff", tm_gmtoff.into())], &result)?;
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,6 @@ use rustc_middle::ty::Ty;
|
|||||||
use rustc_span::Symbol;
|
use rustc_span::Symbol;
|
||||||
use rustc_target::callconv::{Conv, FnAbi};
|
use rustc_target::callconv::{Conv, FnAbi};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use crate::shims::unix::android::thread::prctl;
|
use crate::shims::unix::android::thread::prctl;
|
||||||
use crate::shims::unix::linux_like::epoll::EvalContextExt as _;
|
use crate::shims::unix::linux_like::epoll::EvalContextExt as _;
|
||||||
use crate::shims::unix::linux_like::eventfd::EvalContextExt as _;
|
use crate::shims::unix::linux_like::eventfd::EvalContextExt as _;
|
||||||
@ -27,14 +25,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
match link_name.as_str() {
|
match link_name.as_str() {
|
||||||
// epoll, eventfd
|
// epoll, eventfd
|
||||||
"epoll_create1" => {
|
"epoll_create1" => {
|
||||||
let [flag] =
|
let [flag] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.epoll_create1(flag)?;
|
let result = this.epoll_create1(flag)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"epoll_ctl" => {
|
"epoll_ctl" => {
|
||||||
let [epfd, op, fd, event] =
|
let [epfd, op, fd, event] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.epoll_ctl(epfd, op, fd, event)?;
|
let result = this.epoll_ctl(epfd, op, fd, event)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
@ -44,8 +40,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.epoll_wait(epfd, events, maxevents, timeout, dest)?;
|
this.epoll_wait(epfd, events, maxevents, timeout, dest)?;
|
||||||
}
|
}
|
||||||
"eventfd" => {
|
"eventfd" => {
|
||||||
let [val, flag] =
|
let [val, flag] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.eventfd(val, flag)?;
|
let result = this.eventfd(val, flag)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ use std::ffi::OsStr;
|
|||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
use rustc_abi::Size;
|
use rustc_abi::Size;
|
||||||
use rustc_middle::ty::layout::LayoutOf;
|
|
||||||
use rustc_middle::ty::Ty;
|
use rustc_middle::ty::Ty;
|
||||||
|
use rustc_middle::ty::layout::LayoutOf;
|
||||||
use rustc_span::Symbol;
|
use rustc_span::Symbol;
|
||||||
use rustc_target::callconv::{Conv, FnAbi};
|
use rustc_target::callconv::{Conv, FnAbi};
|
||||||
|
|
||||||
|
@ -22,8 +22,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
match link_name.as_str() {
|
match link_name.as_str() {
|
||||||
// Threading
|
// Threading
|
||||||
"pthread_set_name_np" => {
|
"pthread_set_name_np" => {
|
||||||
let [thread, name] =
|
let [thread, name] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let max_len = usize::MAX; // FreeBSD does not seem to have a limit.
|
let max_len = usize::MAX; // FreeBSD does not seem to have a limit.
|
||||||
// FreeBSD's pthread_set_name_np does not return anything.
|
// FreeBSD's pthread_set_name_np does not return anything.
|
||||||
this.pthread_setname_np(
|
this.pthread_setname_np(
|
||||||
@ -34,8 +33,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
"pthread_get_name_np" => {
|
"pthread_get_name_np" => {
|
||||||
let [thread, name, len] =
|
let [thread, name, len] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
// FreeBSD's pthread_get_name_np does not return anything
|
// FreeBSD's pthread_get_name_np does not return anything
|
||||||
// and uses strlcpy, which truncates the resulting value,
|
// and uses strlcpy, which truncates the resulting value,
|
||||||
// but always adds a null terminator (except for zero-sized buffers).
|
// but always adds a null terminator (except for zero-sized buffers).
|
||||||
@ -52,26 +50,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// For those, we both intercept `func` and `call@FBSD_1.0` symbols cases
|
// For those, we both intercept `func` and `call@FBSD_1.0` symbols cases
|
||||||
// since freebsd 12 the former form can be expected.
|
// since freebsd 12 the former form can be expected.
|
||||||
"stat" | "stat@FBSD_1.0" => {
|
"stat" | "stat@FBSD_1.0" => {
|
||||||
let [path, buf] =
|
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.macos_fbsd_solaris_stat(path, buf)?;
|
let result = this.macos_fbsd_solaris_stat(path, buf)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"lstat" | "lstat@FBSD_1.0" => {
|
"lstat" | "lstat@FBSD_1.0" => {
|
||||||
let [path, buf] =
|
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.macos_fbsd_solaris_lstat(path, buf)?;
|
let result = this.macos_fbsd_solaris_lstat(path, buf)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"fstat" | "fstat@FBSD_1.0" => {
|
"fstat" | "fstat@FBSD_1.0" => {
|
||||||
let [fd, buf] =
|
let [fd, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
|
let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"readdir_r" | "readdir_r@FBSD_1.0" => {
|
"readdir_r" | "readdir_r@FBSD_1.0" => {
|
||||||
let [dirp, entry, result] =
|
let [dirp, entry, result] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
|
let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
@ -86,8 +80,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
|
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
|
||||||
// These shims are enabled only when the caller is in the standard library.
|
// These shims are enabled only when the caller is in the standard library.
|
||||||
"pthread_attr_get_np" if this.frame_in_std() => {
|
"pthread_attr_get_np" if this.frame_in_std() => {
|
||||||
let [_thread, _attr] =
|
let [_thread, _attr] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
this.write_null(dest)?;
|
this.write_null(dest)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1048,10 +1048,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn linux_readdir64(&mut self, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
|
fn linux_solarish_readdir64(
|
||||||
|
&mut self,
|
||||||
|
dirent_type: &str,
|
||||||
|
dirp_op: &OpTy<'tcx>,
|
||||||
|
) -> InterpResult<'tcx, Scalar> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
|
|
||||||
this.assert_target_os("linux", "readdir64");
|
if !matches!(&*this.tcx.sess.target.os, "linux" | "solaris" | "illumos") {
|
||||||
|
panic!("`linux_solaris_readdir64` should not be called on {}", this.tcx.sess.target.os);
|
||||||
|
}
|
||||||
|
|
||||||
let dirp = this.read_target_usize(dirp_op)?;
|
let dirp = this.read_target_usize(dirp_op)?;
|
||||||
|
|
||||||
@ -1070,9 +1076,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
Some(Ok(dir_entry)) => {
|
Some(Ok(dir_entry)) => {
|
||||||
// Write the directory entry into a newly allocated buffer.
|
// Write the directory entry into a newly allocated buffer.
|
||||||
// The name is written with write_bytes, while the rest of the
|
// The name is written with write_bytes, while the rest of the
|
||||||
// dirent64 struct is written using write_int_fields.
|
// dirent64 (or dirent) struct is written using write_int_fields.
|
||||||
|
|
||||||
// For reference:
|
// For reference:
|
||||||
|
// On Linux:
|
||||||
// pub struct dirent64 {
|
// pub struct dirent64 {
|
||||||
// pub d_ino: ino64_t,
|
// pub d_ino: ino64_t,
|
||||||
// pub d_off: off64_t,
|
// pub d_off: off64_t,
|
||||||
@ -1080,19 +1087,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// pub d_type: c_uchar,
|
// pub d_type: c_uchar,
|
||||||
// pub d_name: [c_char; 256],
|
// pub d_name: [c_char; 256],
|
||||||
// }
|
// }
|
||||||
|
//
|
||||||
|
// On Solaris:
|
||||||
|
// pub struct dirent {
|
||||||
|
// pub d_ino: ino64_t,
|
||||||
|
// pub d_off: off64_t,
|
||||||
|
// pub d_reclen: c_ushort,
|
||||||
|
// pub d_name: [c_char; 3],
|
||||||
|
// }
|
||||||
|
|
||||||
let mut name = dir_entry.file_name(); // not a Path as there are no separators!
|
let mut name = dir_entry.file_name(); // not a Path as there are no separators!
|
||||||
name.push("\0"); // Add a NUL terminator
|
name.push("\0"); // Add a NUL terminator
|
||||||
let name_bytes = name.as_encoded_bytes();
|
let name_bytes = name.as_encoded_bytes();
|
||||||
let name_len = u64::try_from(name_bytes.len()).unwrap();
|
let name_len = u64::try_from(name_bytes.len()).unwrap();
|
||||||
|
|
||||||
let dirent64_layout = this.libc_ty_layout("dirent64");
|
let dirent_layout = this.libc_ty_layout(dirent_type);
|
||||||
let d_name_offset = dirent64_layout.fields.offset(4 /* d_name */).bytes();
|
let fields = &dirent_layout.fields;
|
||||||
|
let last_field = fields.count().strict_sub(1);
|
||||||
|
let d_name_offset = fields.offset(last_field).bytes();
|
||||||
let size = d_name_offset.strict_add(name_len);
|
let size = d_name_offset.strict_add(name_len);
|
||||||
|
|
||||||
let entry = this.allocate_ptr(
|
let entry = this.allocate_ptr(
|
||||||
Size::from_bytes(size),
|
Size::from_bytes(size),
|
||||||
dirent64_layout.align.abi,
|
dirent_layout.align.abi,
|
||||||
MiriMemoryKind::Runtime.into(),
|
MiriMemoryKind::Runtime.into(),
|
||||||
)?;
|
)?;
|
||||||
let entry: Pointer = entry.into();
|
let entry: Pointer = entry.into();
|
||||||
@ -1105,17 +1122,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
let ino = 0u64;
|
let ino = 0u64;
|
||||||
|
|
||||||
let file_type = this.file_type_to_d_type(dir_entry.file_type())?;
|
let file_type = this.file_type_to_d_type(dir_entry.file_type())?;
|
||||||
|
|
||||||
this.write_int_fields_named(
|
this.write_int_fields_named(
|
||||||
&[
|
&[("d_ino", ino.into()), ("d_off", 0), ("d_reclen", size.into())],
|
||||||
("d_ino", ino.into()),
|
&this.ptr_to_mplace(entry, dirent_layout),
|
||||||
("d_off", 0),
|
|
||||||
("d_reclen", size.into()),
|
|
||||||
("d_type", file_type.into()),
|
|
||||||
],
|
|
||||||
&this.ptr_to_mplace(entry, dirent64_layout),
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
if let Some(d_type) = this
|
||||||
|
.try_project_field_named(&this.ptr_to_mplace(entry, dirent_layout), "d_type")?
|
||||||
|
{
|
||||||
|
this.write_int(file_type, &d_type)?;
|
||||||
|
}
|
||||||
|
|
||||||
let name_ptr = entry.wrapping_offset(Size::from_bytes(d_name_offset), this);
|
let name_ptr = entry.wrapping_offset(Size::from_bytes(d_name_offset), this);
|
||||||
this.write_bytes_ptr(name_ptr, name_bytes.iter().copied())?;
|
this.write_bytes_ptr(name_ptr, name_bytes.iter().copied())?;
|
||||||
|
|
||||||
|
@ -36,14 +36,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
match link_name.as_str() {
|
match link_name.as_str() {
|
||||||
// File related shims
|
// File related shims
|
||||||
"readdir64" => {
|
"readdir64" => {
|
||||||
let [dirp] =
|
let [dirp] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
let result = this.linux_solarish_readdir64("dirent64", dirp)?;
|
||||||
let result = this.linux_readdir64(dirp)?;
|
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"sync_file_range" => {
|
"sync_file_range" => {
|
||||||
let [fd, offset, nbytes, flags] =
|
let [fd, offset, nbytes, flags] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.sync_file_range(fd, offset, nbytes, flags)?;
|
let result = this.sync_file_range(fd, offset, nbytes, flags)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
@ -56,14 +54,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
// epoll, eventfd
|
// epoll, eventfd
|
||||||
"epoll_create1" => {
|
"epoll_create1" => {
|
||||||
let [flag] =
|
let [flag] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.epoll_create1(flag)?;
|
let result = this.epoll_create1(flag)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"epoll_ctl" => {
|
"epoll_ctl" => {
|
||||||
let [epfd, op, fd, event] =
|
let [epfd, op, fd, event] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.epoll_ctl(epfd, op, fd, event)?;
|
let result = this.epoll_ctl(epfd, op, fd, event)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
@ -73,16 +69,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.epoll_wait(epfd, events, maxevents, timeout, dest)?;
|
this.epoll_wait(epfd, events, maxevents, timeout, dest)?;
|
||||||
}
|
}
|
||||||
"eventfd" => {
|
"eventfd" => {
|
||||||
let [val, flag] =
|
let [val, flag] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.eventfd(val, flag)?;
|
let result = this.eventfd(val, flag)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Threading
|
// Threading
|
||||||
"pthread_setname_np" => {
|
"pthread_setname_np" => {
|
||||||
let [thread, name] =
|
let [thread, name] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let res = match this.pthread_setname_np(
|
let res = match this.pthread_setname_np(
|
||||||
this.read_scalar(thread)?,
|
this.read_scalar(thread)?,
|
||||||
this.read_scalar(name)?,
|
this.read_scalar(name)?,
|
||||||
@ -97,8 +91,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.write_scalar(res, dest)?;
|
this.write_scalar(res, dest)?;
|
||||||
}
|
}
|
||||||
"pthread_getname_np" => {
|
"pthread_getname_np" => {
|
||||||
let [thread, name, len] =
|
let [thread, name, len] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
// The function's behavior isn't portable between platforms.
|
// The function's behavior isn't portable between platforms.
|
||||||
// In case of glibc, the length of the output buffer must
|
// In case of glibc, the length of the output buffer must
|
||||||
// be not shorter than TASK_COMM_LEN.
|
// be not shorter than TASK_COMM_LEN.
|
||||||
@ -146,8 +139,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.write_scalar(ptr, dest)?;
|
this.write_scalar(ptr, dest)?;
|
||||||
}
|
}
|
||||||
"__xpg_strerror_r" => {
|
"__xpg_strerror_r" => {
|
||||||
let [errnum, buf, buflen] =
|
let [errnum, buf, buflen] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.strerror_r(errnum, buf, buflen)?;
|
let result = this.strerror_r(errnum, buf, buflen)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
@ -170,8 +162,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
|
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
|
||||||
// These shims are enabled only when the caller is in the standard library.
|
// These shims are enabled only when the caller is in the standard library.
|
||||||
"pthread_getattr_np" if this.frame_in_std() => {
|
"pthread_getattr_np" if this.frame_in_std() => {
|
||||||
let [_thread, _attr] =
|
let [_thread, _attr] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
this.write_null(dest)?;
|
this.write_null(dest)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ use rustc_middle::ty::Ty;
|
|||||||
use rustc_span::Symbol;
|
use rustc_span::Symbol;
|
||||||
use rustc_target::callconv::{Conv, FnAbi};
|
use rustc_target::callconv::{Conv, FnAbi};
|
||||||
|
|
||||||
|
|
||||||
use crate::helpers::check_min_arg_count;
|
use crate::helpers::check_min_arg_count;
|
||||||
use crate::shims::unix::linux_like::eventfd::EvalContextExt as _;
|
use crate::shims::unix::linux_like::eventfd::EvalContextExt as _;
|
||||||
use crate::shims::unix::linux_like::sync::futex;
|
use crate::shims::unix::linux_like::sync::futex;
|
||||||
|
@ -33,44 +33,37 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
// File related shims
|
// File related shims
|
||||||
"close$NOCANCEL" => {
|
"close$NOCANCEL" => {
|
||||||
let [result] =
|
let [result] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.close(result)?;
|
let result = this.close(result)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"stat" | "stat64" | "stat$INODE64" => {
|
"stat" | "stat64" | "stat$INODE64" => {
|
||||||
let [path, buf] =
|
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.macos_fbsd_solaris_stat(path, buf)?;
|
let result = this.macos_fbsd_solaris_stat(path, buf)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"lstat" | "lstat64" | "lstat$INODE64" => {
|
"lstat" | "lstat64" | "lstat$INODE64" => {
|
||||||
let [path, buf] =
|
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.macos_fbsd_solaris_lstat(path, buf)?;
|
let result = this.macos_fbsd_solaris_lstat(path, buf)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"fstat" | "fstat64" | "fstat$INODE64" => {
|
"fstat" | "fstat64" | "fstat$INODE64" => {
|
||||||
let [fd, buf] =
|
let [fd, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
|
let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"opendir$INODE64" => {
|
"opendir$INODE64" => {
|
||||||
let [name] =
|
let [name] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.opendir(name)?;
|
let result = this.opendir(name)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"readdir_r" | "readdir_r$INODE64" => {
|
"readdir_r" | "readdir_r$INODE64" => {
|
||||||
let [dirp, entry, result] =
|
let [dirp, entry, result] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
|
let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"realpath$DARWIN_EXTSN" => {
|
"realpath$DARWIN_EXTSN" => {
|
||||||
let [path, resolved_path] =
|
let [path, resolved_path] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.realpath(path, resolved_path)?;
|
let result = this.realpath(path, resolved_path)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
@ -84,8 +77,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
// Random data generation
|
// Random data generation
|
||||||
"CCRandomGenerateBytes" => {
|
"CCRandomGenerateBytes" => {
|
||||||
let [bytes, count] =
|
let [bytes, count] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let bytes = this.read_pointer(bytes)?;
|
let bytes = this.read_pointer(bytes)?;
|
||||||
let count = this.read_target_usize(count)?;
|
let count = this.read_target_usize(count)?;
|
||||||
let success = this.eval_libc_i32("kCCSuccess");
|
let success = this.eval_libc_i32("kCCSuccess");
|
||||||
@ -101,8 +93,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
"mach_timebase_info" => {
|
"mach_timebase_info" => {
|
||||||
let [info] =
|
let [info] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.mach_timebase_info(info)?;
|
let result = this.mach_timebase_info(info)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
@ -117,8 +108,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.write_pointer(this.machine.argv.expect("machine must be initialized"), dest)?;
|
this.write_pointer(this.machine.argv.expect("machine must be initialized"), dest)?;
|
||||||
}
|
}
|
||||||
"_NSGetExecutablePath" => {
|
"_NSGetExecutablePath" => {
|
||||||
let [buf, bufsize] =
|
let [buf, bufsize] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
this.check_no_isolation("`_NSGetExecutablePath`")?;
|
this.check_no_isolation("`_NSGetExecutablePath`")?;
|
||||||
|
|
||||||
let buf_ptr = this.read_pointer(buf)?;
|
let buf_ptr = this.read_pointer(buf)?;
|
||||||
@ -143,8 +133,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
// Thread-local storage
|
// Thread-local storage
|
||||||
"_tlv_atexit" => {
|
"_tlv_atexit" => {
|
||||||
let [dtor, data] =
|
let [dtor, data] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let dtor = this.read_pointer(dtor)?;
|
let dtor = this.read_pointer(dtor)?;
|
||||||
let dtor = this.get_ptr_fn(dtor)?.as_instance()?;
|
let dtor = this.get_ptr_fn(dtor)?.as_instance()?;
|
||||||
let data = this.read_scalar(data)?;
|
let data = this.read_scalar(data)?;
|
||||||
@ -154,15 +143,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
// Querying system information
|
// Querying system information
|
||||||
"pthread_get_stackaddr_np" => {
|
"pthread_get_stackaddr_np" => {
|
||||||
let [thread] =
|
let [thread] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
this.read_target_usize(thread)?;
|
this.read_target_usize(thread)?;
|
||||||
let stack_addr = Scalar::from_uint(this.machine.stack_addr, this.pointer_size());
|
let stack_addr = Scalar::from_uint(this.machine.stack_addr, this.pointer_size());
|
||||||
this.write_scalar(stack_addr, dest)?;
|
this.write_scalar(stack_addr, dest)?;
|
||||||
}
|
}
|
||||||
"pthread_get_stacksize_np" => {
|
"pthread_get_stacksize_np" => {
|
||||||
let [thread] =
|
let [thread] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
this.read_target_usize(thread)?;
|
this.read_target_usize(thread)?;
|
||||||
let stack_size = Scalar::from_uint(this.machine.stack_size, this.pointer_size());
|
let stack_size = Scalar::from_uint(this.machine.stack_size, this.pointer_size());
|
||||||
this.write_scalar(stack_size, dest)?;
|
this.write_scalar(stack_size, dest)?;
|
||||||
@ -170,8 +157,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
// Threading
|
// Threading
|
||||||
"pthread_setname_np" => {
|
"pthread_setname_np" => {
|
||||||
let [name] =
|
let [name] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
// The real implementation has logic in two places:
|
// The real implementation has logic in two places:
|
||||||
// * in userland at https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1178-L1200,
|
// * in userland at https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1178-L1200,
|
||||||
@ -198,8 +184,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.write_scalar(res, dest)?;
|
this.write_scalar(res, dest)?;
|
||||||
}
|
}
|
||||||
"pthread_getname_np" => {
|
"pthread_getname_np" => {
|
||||||
let [thread, name, len] =
|
let [thread, name, len] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
// The function's behavior isn't portable between platforms.
|
// The function's behavior isn't portable between platforms.
|
||||||
// In case of macOS, a truncated name (due to a too small buffer)
|
// In case of macOS, a truncated name (due to a too small buffer)
|
||||||
@ -224,28 +209,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
"os_unfair_lock_lock" => {
|
"os_unfair_lock_lock" => {
|
||||||
let [lock_op] =
|
let [lock_op] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
this.os_unfair_lock_lock(lock_op)?;
|
this.os_unfair_lock_lock(lock_op)?;
|
||||||
}
|
}
|
||||||
"os_unfair_lock_trylock" => {
|
"os_unfair_lock_trylock" => {
|
||||||
let [lock_op] =
|
let [lock_op] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
this.os_unfair_lock_trylock(lock_op, dest)?;
|
this.os_unfair_lock_trylock(lock_op, dest)?;
|
||||||
}
|
}
|
||||||
"os_unfair_lock_unlock" => {
|
"os_unfair_lock_unlock" => {
|
||||||
let [lock_op] =
|
let [lock_op] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
this.os_unfair_lock_unlock(lock_op)?;
|
this.os_unfair_lock_unlock(lock_op)?;
|
||||||
}
|
}
|
||||||
"os_unfair_lock_assert_owner" => {
|
"os_unfair_lock_assert_owner" => {
|
||||||
let [lock_op] =
|
let [lock_op] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
this.os_unfair_lock_assert_owner(lock_op)?;
|
this.os_unfair_lock_assert_owner(lock_op)?;
|
||||||
}
|
}
|
||||||
"os_unfair_lock_assert_not_owner" => {
|
"os_unfair_lock_assert_not_owner" => {
|
||||||
let [lock_op] =
|
let [lock_op] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
this.os_unfair_lock_assert_not_owner(lock_op)?;
|
this.os_unfair_lock_assert_not_owner(lock_op)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,8 +23,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
match link_name.as_str() {
|
match link_name.as_str() {
|
||||||
// Threading
|
// Threading
|
||||||
"pthread_setname_np" => {
|
"pthread_setname_np" => {
|
||||||
let [thread, name] =
|
let [thread, name] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
// THREAD_NAME_MAX allows a thread name of 31+1 length
|
// THREAD_NAME_MAX allows a thread name of 31+1 length
|
||||||
// https://github.com/illumos/illumos-gate/blob/7671517e13b8123748eda4ef1ee165c6d9dba7fe/usr/src/uts/common/sys/thread.h#L613
|
// https://github.com/illumos/illumos-gate/blob/7671517e13b8123748eda4ef1ee165c6d9dba7fe/usr/src/uts/common/sys/thread.h#L613
|
||||||
let max_len = 32;
|
let max_len = 32;
|
||||||
@ -42,8 +41,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.write_scalar(res, dest)?;
|
this.write_scalar(res, dest)?;
|
||||||
}
|
}
|
||||||
"pthread_getname_np" => {
|
"pthread_getname_np" => {
|
||||||
let [thread, name, len] =
|
let [thread, name, len] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
// See https://illumos.org/man/3C/pthread_getname_np for the error codes.
|
// See https://illumos.org/man/3C/pthread_getname_np for the error codes.
|
||||||
let res = match this.pthread_getname_np(
|
let res = match this.pthread_getname_np(
|
||||||
this.read_scalar(thread)?,
|
this.read_scalar(thread)?,
|
||||||
@ -60,23 +58,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
// File related shims
|
// File related shims
|
||||||
"stat" | "stat64" => {
|
"stat" | "stat64" => {
|
||||||
let [path, buf] =
|
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.macos_fbsd_solaris_stat(path, buf)?;
|
let result = this.macos_fbsd_solaris_stat(path, buf)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"lstat" | "lstat64" => {
|
"lstat" | "lstat64" => {
|
||||||
let [path, buf] =
|
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.macos_fbsd_solaris_lstat(path, buf)?;
|
let result = this.macos_fbsd_solaris_lstat(path, buf)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"fstat" | "fstat64" => {
|
"fstat" | "fstat64" => {
|
||||||
let [fd, buf] =
|
let [fd, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
|
let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
|
"readdir" => {
|
||||||
|
let [dirp] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
|
let result = this.linux_solarish_readdir64("dirent", dirp)?;
|
||||||
|
this.write_scalar(result, dest)?;
|
||||||
|
}
|
||||||
|
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
"___errno" => {
|
"___errno" => {
|
||||||
@ -86,8 +86,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
"stack_getbounds" => {
|
"stack_getbounds" => {
|
||||||
let [stack] =
|
let [stack] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let stack = this.deref_pointer_as(stack, this.libc_ty_layout("stack_t"))?;
|
let stack = this.deref_pointer_as(stack, this.libc_ty_layout("stack_t"))?;
|
||||||
|
|
||||||
this.write_int_fields_named(
|
this.write_int_fields_named(
|
||||||
@ -105,8 +104,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
"pset_info" => {
|
"pset_info" => {
|
||||||
let [pset, tpe, cpus, list] =
|
let [pset, tpe, cpus, list] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
// We do not need to handle the current process cpu mask, available_parallelism
|
// We do not need to handle the current process cpu mask, available_parallelism
|
||||||
// implementation pass null anyway. We only care for the number of
|
// implementation pass null anyway. We only care for the number of
|
||||||
// cpus.
|
// cpus.
|
||||||
@ -135,8 +133,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
"__sysconf_xpg7" => {
|
"__sysconf_xpg7" => {
|
||||||
let [val] =
|
let [val] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.sysconf(val)?;
|
let result = this.sysconf(val)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,12 @@ struct AnonSocket {
|
|||||||
/// This flag is set to `true` if the peer's `readbuf` is non-empty at the time
|
/// This flag is set to `true` if the peer's `readbuf` is non-empty at the time
|
||||||
/// of closure.
|
/// of closure.
|
||||||
peer_lost_data: Cell<bool>,
|
peer_lost_data: Cell<bool>,
|
||||||
|
/// A list of thread ids blocked because the buffer was empty.
|
||||||
|
/// Once another thread writes some bytes, these threads will be unblocked.
|
||||||
|
blocked_read_tid: RefCell<Vec<ThreadId>>,
|
||||||
|
/// A list of thread ids blocked because the buffer was full.
|
||||||
|
/// Once another thread reads some bytes, these threads will be unblocked.
|
||||||
|
blocked_write_tid: RefCell<Vec<ThreadId>>,
|
||||||
is_nonblock: bool,
|
is_nonblock: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +89,7 @@ impl FileDescription for AnonSocket {
|
|||||||
|
|
||||||
fn read<'tcx>(
|
fn read<'tcx>(
|
||||||
&self,
|
&self,
|
||||||
_self_ref: &FileDescriptionRef,
|
self_ref: &FileDescriptionRef,
|
||||||
_communicate_allowed: bool,
|
_communicate_allowed: bool,
|
||||||
ptr: Pointer,
|
ptr: Pointer,
|
||||||
len: usize,
|
len: usize,
|
||||||
@ -100,33 +106,21 @@ impl FileDescription for AnonSocket {
|
|||||||
// corresponding ErrorKind variant.
|
// corresponding ErrorKind variant.
|
||||||
throw_unsup_format!("reading from the write end of a pipe");
|
throw_unsup_format!("reading from the write end of a pipe");
|
||||||
};
|
};
|
||||||
if readbuf.borrow().buf.is_empty() {
|
|
||||||
if self.peer_fd().upgrade().is_none() {
|
if readbuf.borrow().buf.is_empty() && self.is_nonblock {
|
||||||
// Socketpair with no peer and empty buffer.
|
|
||||||
// 0 bytes successfully read indicates end-of-file.
|
|
||||||
return ecx.return_read_success(ptr, &[], 0, dest);
|
|
||||||
} else {
|
|
||||||
if self.is_nonblock {
|
|
||||||
// Non-blocking socketpair with writer and empty buffer.
|
// Non-blocking socketpair with writer and empty buffer.
|
||||||
// https://linux.die.net/man/2/read
|
// https://linux.die.net/man/2/read
|
||||||
// EAGAIN or EWOULDBLOCK can be returned for socket,
|
// EAGAIN or EWOULDBLOCK can be returned for socket,
|
||||||
// POSIX.1-2001 allows either error to be returned for this case.
|
// POSIX.1-2001 allows either error to be returned for this case.
|
||||||
// Since there is no ErrorKind for EAGAIN, WouldBlock is used.
|
// Since there is no ErrorKind for EAGAIN, WouldBlock is used.
|
||||||
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
|
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
|
||||||
} else {
|
|
||||||
// Blocking socketpair with writer and empty buffer.
|
|
||||||
// FIXME: blocking is currently not supported
|
|
||||||
throw_unsup_format!("socketpair/pipe/pipe2 read: blocking isn't supported yet");
|
|
||||||
}
|
}
|
||||||
}
|
anonsocket_read(self_ref.downgrade(), len, ptr, dest.clone(), ecx)
|
||||||
}
|
|
||||||
// TODO: We might need to decide what to do if peer_fd is closed when read is blocked.
|
|
||||||
anonsocket_read(self, self.peer_fd().upgrade(), len, ptr, dest, ecx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write<'tcx>(
|
fn write<'tcx>(
|
||||||
&self,
|
&self,
|
||||||
_self_ref: &FileDescriptionRef,
|
self_ref: &FileDescriptionRef,
|
||||||
_communicate_allowed: bool,
|
_communicate_allowed: bool,
|
||||||
ptr: Pointer,
|
ptr: Pointer,
|
||||||
len: usize,
|
len: usize,
|
||||||
@ -153,16 +147,11 @@ impl FileDescription for AnonSocket {
|
|||||||
};
|
};
|
||||||
let available_space =
|
let available_space =
|
||||||
MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(writebuf.borrow().buf.len());
|
MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(writebuf.borrow().buf.len());
|
||||||
if available_space == 0 {
|
if available_space == 0 && self.is_nonblock {
|
||||||
if self.is_nonblock {
|
|
||||||
// Non-blocking socketpair with a full buffer.
|
// Non-blocking socketpair with a full buffer.
|
||||||
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
|
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
|
||||||
} else {
|
|
||||||
// Blocking socketpair with a full buffer.
|
|
||||||
throw_unsup_format!("socketpair/pipe/pipe2 write: blocking isn't supported yet");
|
|
||||||
}
|
}
|
||||||
}
|
anonsocket_write(self_ref.downgrade(), ptr, len, dest.clone(), ecx)
|
||||||
anonsocket_write(available_space, &peer_fd, ptr, len, dest, ecx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_unix(&self) -> &dyn UnixFileDescription {
|
fn as_unix(&self) -> &dyn UnixFileDescription {
|
||||||
@ -172,20 +161,51 @@ impl FileDescription for AnonSocket {
|
|||||||
|
|
||||||
/// Write to AnonSocket based on the space available and return the written byte size.
|
/// Write to AnonSocket based on the space available and return the written byte size.
|
||||||
fn anonsocket_write<'tcx>(
|
fn anonsocket_write<'tcx>(
|
||||||
available_space: usize,
|
weak_self_ref: WeakFileDescriptionRef,
|
||||||
peer_fd: &FileDescriptionRef,
|
|
||||||
ptr: Pointer,
|
ptr: Pointer,
|
||||||
len: usize,
|
len: usize,
|
||||||
dest: &MPlaceTy<'tcx>,
|
dest: MPlaceTy<'tcx>,
|
||||||
ecx: &mut MiriInterpCx<'tcx>,
|
ecx: &mut MiriInterpCx<'tcx>,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
|
let Some(self_ref) = weak_self_ref.upgrade() else {
|
||||||
|
// FIXME: We should raise a deadlock error if the self_ref upgrade failed.
|
||||||
|
throw_unsup_format!("This will be a deadlock error in future")
|
||||||
|
};
|
||||||
|
let self_anonsocket = self_ref.downcast::<AnonSocket>().unwrap();
|
||||||
|
let Some(peer_fd) = self_anonsocket.peer_fd().upgrade() else {
|
||||||
|
// If the upgrade from Weak to Rc fails, it indicates that all read ends have been
|
||||||
|
// closed.
|
||||||
|
return ecx.set_last_error_and_return(ErrorKind::BrokenPipe, &dest);
|
||||||
|
};
|
||||||
let Some(writebuf) = &peer_fd.downcast::<AnonSocket>().unwrap().readbuf else {
|
let Some(writebuf) = &peer_fd.downcast::<AnonSocket>().unwrap().readbuf else {
|
||||||
// FIXME: This should return EBADF, but there's no nice way to do that as there's no
|
// FIXME: This should return EBADF, but there's no nice way to do that as there's no
|
||||||
// corresponding ErrorKind variant.
|
// corresponding ErrorKind variant.
|
||||||
throw_unsup_format!("writing to the reading end of a pipe")
|
throw_unsup_format!("writing to the reading end of a pipe")
|
||||||
};
|
};
|
||||||
let mut writebuf = writebuf.borrow_mut();
|
|
||||||
|
|
||||||
|
let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(writebuf.borrow().buf.len());
|
||||||
|
|
||||||
|
if available_space == 0 {
|
||||||
|
// Blocking socketpair with a full buffer.
|
||||||
|
let dest = dest.clone();
|
||||||
|
self_anonsocket.blocked_write_tid.borrow_mut().push(ecx.active_thread());
|
||||||
|
ecx.block_thread(
|
||||||
|
BlockReason::UnnamedSocket,
|
||||||
|
None,
|
||||||
|
callback!(
|
||||||
|
@capture<'tcx> {
|
||||||
|
weak_self_ref: WeakFileDescriptionRef,
|
||||||
|
ptr: Pointer,
|
||||||
|
len: usize,
|
||||||
|
dest: MPlaceTy<'tcx>,
|
||||||
|
}
|
||||||
|
@unblock = |this| {
|
||||||
|
anonsocket_write(weak_self_ref, ptr, len, dest, this)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let mut writebuf = writebuf.borrow_mut();
|
||||||
// Remember this clock so `read` can synchronize with us.
|
// Remember this clock so `read` can synchronize with us.
|
||||||
ecx.release_clock(|clock| {
|
ecx.release_clock(|clock| {
|
||||||
writebuf.clock.join(clock);
|
writebuf.clock.join(clock);
|
||||||
@ -200,29 +220,68 @@ fn anonsocket_write<'tcx>(
|
|||||||
|
|
||||||
// Notification should be provided for peer fd as it became readable.
|
// Notification should be provided for peer fd as it became readable.
|
||||||
// The kernel does this even if the fd was already readable before, so we follow suit.
|
// The kernel does this even if the fd was already readable before, so we follow suit.
|
||||||
ecx.check_and_update_readiness(peer_fd)?;
|
ecx.check_and_update_readiness(&peer_fd)?;
|
||||||
|
let peer_anonsocket = peer_fd.downcast::<AnonSocket>().unwrap();
|
||||||
|
// Unblock all threads that are currently blocked on peer_fd's read.
|
||||||
|
let waiting_threads = std::mem::take(&mut *peer_anonsocket.blocked_read_tid.borrow_mut());
|
||||||
|
// FIXME: We can randomize the order of unblocking.
|
||||||
|
for thread_id in waiting_threads {
|
||||||
|
ecx.unblock_thread(thread_id, BlockReason::UnnamedSocket)?;
|
||||||
|
}
|
||||||
|
|
||||||
ecx.return_write_success(actual_write_size, dest)
|
return ecx.return_write_success(actual_write_size, &dest);
|
||||||
|
}
|
||||||
|
interp_ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read from AnonSocket and return the number of bytes read.
|
/// Read from AnonSocket and return the number of bytes read.
|
||||||
fn anonsocket_read<'tcx>(
|
fn anonsocket_read<'tcx>(
|
||||||
anonsocket: &AnonSocket,
|
weak_self_ref: WeakFileDescriptionRef,
|
||||||
peer_fd: Option<FileDescriptionRef>,
|
|
||||||
len: usize,
|
len: usize,
|
||||||
ptr: Pointer,
|
ptr: Pointer,
|
||||||
dest: &MPlaceTy<'tcx>,
|
dest: MPlaceTy<'tcx>,
|
||||||
ecx: &mut MiriInterpCx<'tcx>,
|
ecx: &mut MiriInterpCx<'tcx>,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
let mut bytes = vec![0; len];
|
let Some(self_ref) = weak_self_ref.upgrade() else {
|
||||||
|
// FIXME: We should raise a deadlock error if the self_ref upgrade failed.
|
||||||
|
throw_unsup_format!("This will be a deadlock error in future")
|
||||||
|
};
|
||||||
|
let self_anonsocket = self_ref.downcast::<AnonSocket>().unwrap();
|
||||||
|
|
||||||
let Some(readbuf) = &anonsocket.readbuf else {
|
let Some(readbuf) = &self_anonsocket.readbuf else {
|
||||||
// FIXME: This should return EBADF, but there's no nice way to do that as there's no
|
// FIXME: This should return EBADF, but there's no nice way to do that as there's no
|
||||||
// corresponding ErrorKind variant.
|
// corresponding ErrorKind variant.
|
||||||
throw_unsup_format!("reading from the write end of a pipe")
|
throw_unsup_format!("reading from the write end of a pipe")
|
||||||
};
|
};
|
||||||
let mut readbuf = readbuf.borrow_mut();
|
|
||||||
|
|
||||||
|
if readbuf.borrow_mut().buf.is_empty() {
|
||||||
|
if self_anonsocket.peer_fd().upgrade().is_none() {
|
||||||
|
// Socketpair with no peer and empty buffer.
|
||||||
|
// 0 bytes successfully read indicates end-of-file.
|
||||||
|
return ecx.return_read_success(ptr, &[], 0, &dest);
|
||||||
|
} else {
|
||||||
|
// Blocking socketpair with writer and empty buffer.
|
||||||
|
let weak_self_ref = weak_self_ref.clone();
|
||||||
|
self_anonsocket.blocked_read_tid.borrow_mut().push(ecx.active_thread());
|
||||||
|
ecx.block_thread(
|
||||||
|
BlockReason::UnnamedSocket,
|
||||||
|
None,
|
||||||
|
callback!(
|
||||||
|
@capture<'tcx> {
|
||||||
|
weak_self_ref: WeakFileDescriptionRef,
|
||||||
|
len: usize,
|
||||||
|
ptr: Pointer,
|
||||||
|
dest: MPlaceTy<'tcx>,
|
||||||
|
}
|
||||||
|
@unblock = |this| {
|
||||||
|
anonsocket_read(weak_self_ref, len, ptr, dest, this)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut bytes = vec![0; len];
|
||||||
|
let mut readbuf = readbuf.borrow_mut();
|
||||||
// Synchronize with all previous writes to this buffer.
|
// Synchronize with all previous writes to this buffer.
|
||||||
// FIXME: this over-synchronizes; a more precise approach would be to
|
// FIXME: this over-synchronizes; a more precise approach would be to
|
||||||
// only sync with the writes whose data we will read.
|
// only sync with the writes whose data we will read.
|
||||||
@ -242,11 +301,21 @@ fn anonsocket_read<'tcx>(
|
|||||||
// don't know what that *certain number* is, we will provide a notification every time
|
// don't know what that *certain number* is, we will provide a notification every time
|
||||||
// a read is successful. This might result in our epoll emulation providing more
|
// a read is successful. This might result in our epoll emulation providing more
|
||||||
// notifications than the real system.
|
// notifications than the real system.
|
||||||
if let Some(peer_fd) = peer_fd {
|
if let Some(peer_fd) = self_anonsocket.peer_fd().upgrade() {
|
||||||
ecx.check_and_update_readiness(&peer_fd)?;
|
ecx.check_and_update_readiness(&peer_fd)?;
|
||||||
|
let peer_anonsocket = peer_fd.downcast::<AnonSocket>().unwrap();
|
||||||
|
// Unblock all threads that are currently blocked on peer_fd's write.
|
||||||
|
let waiting_threads =
|
||||||
|
std::mem::take(&mut *peer_anonsocket.blocked_write_tid.borrow_mut());
|
||||||
|
// FIXME: We can randomize the order of unblocking.
|
||||||
|
for thread_id in waiting_threads {
|
||||||
|
ecx.unblock_thread(thread_id, BlockReason::UnnamedSocket)?;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
ecx.return_read_success(ptr, &bytes, actual_read_size, dest)
|
return ecx.return_read_success(ptr, &bytes, actual_read_size, &dest);
|
||||||
|
}
|
||||||
|
interp_ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnixFileDescription for AnonSocket {
|
impl UnixFileDescription for AnonSocket {
|
||||||
@ -360,12 +429,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
readbuf: Some(RefCell::new(Buffer::new())),
|
readbuf: Some(RefCell::new(Buffer::new())),
|
||||||
peer_fd: OnceCell::new(),
|
peer_fd: OnceCell::new(),
|
||||||
peer_lost_data: Cell::new(false),
|
peer_lost_data: Cell::new(false),
|
||||||
|
blocked_read_tid: RefCell::new(Vec::new()),
|
||||||
|
blocked_write_tid: RefCell::new(Vec::new()),
|
||||||
is_nonblock: is_sock_nonblock,
|
is_nonblock: is_sock_nonblock,
|
||||||
});
|
});
|
||||||
let fd1 = fds.new_ref(AnonSocket {
|
let fd1 = fds.new_ref(AnonSocket {
|
||||||
readbuf: Some(RefCell::new(Buffer::new())),
|
readbuf: Some(RefCell::new(Buffer::new())),
|
||||||
peer_fd: OnceCell::new(),
|
peer_fd: OnceCell::new(),
|
||||||
peer_lost_data: Cell::new(false),
|
peer_lost_data: Cell::new(false),
|
||||||
|
blocked_read_tid: RefCell::new(Vec::new()),
|
||||||
|
blocked_write_tid: RefCell::new(Vec::new()),
|
||||||
is_nonblock: is_sock_nonblock,
|
is_nonblock: is_sock_nonblock,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -424,12 +497,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
readbuf: Some(RefCell::new(Buffer::new())),
|
readbuf: Some(RefCell::new(Buffer::new())),
|
||||||
peer_fd: OnceCell::new(),
|
peer_fd: OnceCell::new(),
|
||||||
peer_lost_data: Cell::new(false),
|
peer_lost_data: Cell::new(false),
|
||||||
|
blocked_read_tid: RefCell::new(Vec::new()),
|
||||||
|
blocked_write_tid: RefCell::new(Vec::new()),
|
||||||
is_nonblock,
|
is_nonblock,
|
||||||
});
|
});
|
||||||
let fd1 = fds.new_ref(AnonSocket {
|
let fd1 = fds.new_ref(AnonSocket {
|
||||||
readbuf: None,
|
readbuf: None,
|
||||||
peer_fd: OnceCell::new(),
|
peer_fd: OnceCell::new(),
|
||||||
peer_lost_data: Cell::new(false),
|
peer_lost_data: Cell::new(false),
|
||||||
|
blocked_read_tid: RefCell::new(Vec::new()),
|
||||||
|
blocked_write_tid: RefCell::new(Vec::new()),
|
||||||
is_nonblock,
|
is_nonblock,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -22,14 +22,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
match link_name.as_str() {
|
match link_name.as_str() {
|
||||||
// Allocation
|
// Allocation
|
||||||
"posix_memalign" => {
|
"posix_memalign" => {
|
||||||
let [memptr, align, size] =
|
let [memptr, align, size] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let result = this.posix_memalign(memptr, align, size)?;
|
let result = this.posix_memalign(memptr, align, size)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"aligned_alloc" => {
|
"aligned_alloc" => {
|
||||||
let [align, size] =
|
let [align, size] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let res = this.aligned_alloc(align, size)?;
|
let res = this.aligned_alloc(align, size)?;
|
||||||
this.write_pointer(res, dest)?;
|
this.write_pointer(res, dest)?;
|
||||||
}
|
}
|
||||||
|
@ -108,50 +108,42 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
match link_name.as_str() {
|
match link_name.as_str() {
|
||||||
// Environment related shims
|
// Environment related shims
|
||||||
"GetEnvironmentVariableW" => {
|
"GetEnvironmentVariableW" => {
|
||||||
let [name, buf, size] =
|
let [name, buf, size] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let result = this.GetEnvironmentVariableW(name, buf, size)?;
|
let result = this.GetEnvironmentVariableW(name, buf, size)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"SetEnvironmentVariableW" => {
|
"SetEnvironmentVariableW" => {
|
||||||
let [name, value] =
|
let [name, value] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let result = this.SetEnvironmentVariableW(name, value)?;
|
let result = this.SetEnvironmentVariableW(name, value)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"GetEnvironmentStringsW" => {
|
"GetEnvironmentStringsW" => {
|
||||||
let [] =
|
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let result = this.GetEnvironmentStringsW()?;
|
let result = this.GetEnvironmentStringsW()?;
|
||||||
this.write_pointer(result, dest)?;
|
this.write_pointer(result, dest)?;
|
||||||
}
|
}
|
||||||
"FreeEnvironmentStringsW" => {
|
"FreeEnvironmentStringsW" => {
|
||||||
let [env_block] =
|
let [env_block] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let result = this.FreeEnvironmentStringsW(env_block)?;
|
let result = this.FreeEnvironmentStringsW(env_block)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"GetCurrentDirectoryW" => {
|
"GetCurrentDirectoryW" => {
|
||||||
let [size, buf] =
|
let [size, buf] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let result = this.GetCurrentDirectoryW(size, buf)?;
|
let result = this.GetCurrentDirectoryW(size, buf)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"SetCurrentDirectoryW" => {
|
"SetCurrentDirectoryW" => {
|
||||||
let [path] =
|
let [path] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let result = this.SetCurrentDirectoryW(path)?;
|
let result = this.SetCurrentDirectoryW(path)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"GetUserProfileDirectoryW" => {
|
"GetUserProfileDirectoryW" => {
|
||||||
let [token, buf, size] =
|
let [token, buf, size] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let result = this.GetUserProfileDirectoryW(token, buf, size)?;
|
let result = this.GetUserProfileDirectoryW(token, buf, size)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"GetCurrentProcessId" => {
|
"GetCurrentProcessId" => {
|
||||||
let [] =
|
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let result = this.GetCurrentProcessId()?;
|
let result = this.GetCurrentProcessId()?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
@ -257,8 +249,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
// Allocation
|
// Allocation
|
||||||
"HeapAlloc" => {
|
"HeapAlloc" => {
|
||||||
let [handle, flags, size] =
|
let [handle, flags, size] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
this.read_target_isize(handle)?;
|
this.read_target_isize(handle)?;
|
||||||
let flags = this.read_scalar(flags)?.to_u32()?;
|
let flags = this.read_scalar(flags)?.to_u32()?;
|
||||||
let size = this.read_target_usize(size)?;
|
let size = this.read_target_usize(size)?;
|
||||||
@ -281,8 +272,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.write_pointer(ptr, dest)?;
|
this.write_pointer(ptr, dest)?;
|
||||||
}
|
}
|
||||||
"HeapFree" => {
|
"HeapFree" => {
|
||||||
let [handle, flags, ptr] =
|
let [handle, flags, ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
this.read_target_isize(handle)?;
|
this.read_target_isize(handle)?;
|
||||||
this.read_scalar(flags)?.to_u32()?;
|
this.read_scalar(flags)?.to_u32()?;
|
||||||
let ptr = this.read_pointer(ptr)?;
|
let ptr = this.read_pointer(ptr)?;
|
||||||
@ -314,8 +304,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.write_pointer(new_ptr, dest)?;
|
this.write_pointer(new_ptr, dest)?;
|
||||||
}
|
}
|
||||||
"LocalFree" => {
|
"LocalFree" => {
|
||||||
let [ptr] =
|
let [ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let ptr = this.read_pointer(ptr)?;
|
let ptr = this.read_pointer(ptr)?;
|
||||||
// "If the hMem parameter is NULL, LocalFree ignores the parameter and returns NULL."
|
// "If the hMem parameter is NULL, LocalFree ignores the parameter and returns NULL."
|
||||||
// (https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localfree)
|
// (https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localfree)
|
||||||
@ -327,14 +316,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
// errno
|
// errno
|
||||||
"SetLastError" => {
|
"SetLastError" => {
|
||||||
let [error] =
|
let [error] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let error = this.read_scalar(error)?;
|
let error = this.read_scalar(error)?;
|
||||||
this.set_last_error(error)?;
|
this.set_last_error(error)?;
|
||||||
}
|
}
|
||||||
"GetLastError" => {
|
"GetLastError" => {
|
||||||
let [] =
|
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let last_error = this.get_last_error()?;
|
let last_error = this.get_last_error()?;
|
||||||
this.write_scalar(last_error, dest)?;
|
this.write_scalar(last_error, dest)?;
|
||||||
}
|
}
|
||||||
@ -342,8 +329,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Querying system information
|
// Querying system information
|
||||||
"GetSystemInfo" => {
|
"GetSystemInfo" => {
|
||||||
// Also called from `page_size` crate.
|
// Also called from `page_size` crate.
|
||||||
let [system_info] =
|
let [system_info] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let system_info =
|
let system_info =
|
||||||
this.deref_pointer_as(system_info, this.windows_ty_layout("SYSTEM_INFO"))?;
|
this.deref_pointer_as(system_info, this.windows_ty_layout("SYSTEM_INFO"))?;
|
||||||
// Initialize with `0`.
|
// Initialize with `0`.
|
||||||
@ -366,22 +352,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// This just creates a key; Windows does not natively support TLS destructors.
|
// This just creates a key; Windows does not natively support TLS destructors.
|
||||||
|
|
||||||
// Create key and return it.
|
// Create key and return it.
|
||||||
let [] =
|
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
|
let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
|
||||||
this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
|
this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
|
||||||
}
|
}
|
||||||
"TlsGetValue" => {
|
"TlsGetValue" => {
|
||||||
let [key] =
|
let [key] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let key = u128::from(this.read_scalar(key)?.to_u32()?);
|
let key = u128::from(this.read_scalar(key)?.to_u32()?);
|
||||||
let active_thread = this.active_thread();
|
let active_thread = this.active_thread();
|
||||||
let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
|
let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
|
||||||
this.write_scalar(ptr, dest)?;
|
this.write_scalar(ptr, dest)?;
|
||||||
}
|
}
|
||||||
"TlsSetValue" => {
|
"TlsSetValue" => {
|
||||||
let [key, new_ptr] =
|
let [key, new_ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let key = u128::from(this.read_scalar(key)?.to_u32()?);
|
let key = u128::from(this.read_scalar(key)?.to_u32()?);
|
||||||
let active_thread = this.active_thread();
|
let active_thread = this.active_thread();
|
||||||
let new_data = this.read_scalar(new_ptr)?;
|
let new_data = this.read_scalar(new_ptr)?;
|
||||||
@ -401,8 +384,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
// Access to command-line arguments
|
// Access to command-line arguments
|
||||||
"GetCommandLineW" => {
|
"GetCommandLineW" => {
|
||||||
let [] =
|
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
this.write_pointer(
|
this.write_pointer(
|
||||||
this.machine.cmd_line.expect("machine must be initialized"),
|
this.machine.cmd_line.expect("machine must be initialized"),
|
||||||
dest,
|
dest,
|
||||||
@ -412,27 +394,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Time related shims
|
// Time related shims
|
||||||
"GetSystemTimeAsFileTime" | "GetSystemTimePreciseAsFileTime" => {
|
"GetSystemTimeAsFileTime" | "GetSystemTimePreciseAsFileTime" => {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let [LPFILETIME] =
|
let [LPFILETIME] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
this.GetSystemTimeAsFileTime(link_name.as_str(), LPFILETIME)?;
|
this.GetSystemTimeAsFileTime(link_name.as_str(), LPFILETIME)?;
|
||||||
}
|
}
|
||||||
"QueryPerformanceCounter" => {
|
"QueryPerformanceCounter" => {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let [lpPerformanceCount] =
|
let [lpPerformanceCount] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
|
let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"QueryPerformanceFrequency" => {
|
"QueryPerformanceFrequency" => {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let [lpFrequency] =
|
let [lpFrequency] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let result = this.QueryPerformanceFrequency(lpFrequency)?;
|
let result = this.QueryPerformanceFrequency(lpFrequency)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
"Sleep" => {
|
"Sleep" => {
|
||||||
let [timeout] =
|
let [timeout] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
|
|
||||||
this.Sleep(timeout)?;
|
this.Sleep(timeout)?;
|
||||||
}
|
}
|
||||||
@ -456,8 +434,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.InitOnceBeginInitialize(ptr, flags, pending, context, dest)?;
|
this.InitOnceBeginInitialize(ptr, flags, pending, context, dest)?;
|
||||||
}
|
}
|
||||||
"InitOnceComplete" => {
|
"InitOnceComplete" => {
|
||||||
let [ptr, flags, context] =
|
let [ptr, flags, context] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let result = this.InitOnceComplete(ptr, flags, context)?;
|
let result = this.InitOnceComplete(ptr, flags, context)?;
|
||||||
this.write_scalar(result, dest)?;
|
this.write_scalar(result, dest)?;
|
||||||
}
|
}
|
||||||
@ -468,14 +445,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.WaitOnAddress(ptr_op, compare_op, size_op, timeout_op, dest)?;
|
this.WaitOnAddress(ptr_op, compare_op, size_op, timeout_op, dest)?;
|
||||||
}
|
}
|
||||||
"WakeByAddressSingle" => {
|
"WakeByAddressSingle" => {
|
||||||
let [ptr_op] =
|
let [ptr_op] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
|
|
||||||
this.WakeByAddressSingle(ptr_op)?;
|
this.WakeByAddressSingle(ptr_op)?;
|
||||||
}
|
}
|
||||||
"WakeByAddressAll" => {
|
"WakeByAddressAll" => {
|
||||||
let [ptr_op] =
|
let [ptr_op] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
|
|
||||||
this.WakeByAddressAll(ptr_op)?;
|
this.WakeByAddressAll(ptr_op)?;
|
||||||
}
|
}
|
||||||
@ -483,8 +458,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Dynamic symbol loading
|
// Dynamic symbol loading
|
||||||
"GetProcAddress" => {
|
"GetProcAddress" => {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let [hModule, lpProcName] =
|
let [hModule, lpProcName] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
this.read_target_isize(hModule)?;
|
this.read_target_isize(hModule)?;
|
||||||
let name = this.read_c_str(this.read_pointer(lpProcName)?)?;
|
let name = this.read_c_str(this.read_pointer(lpProcName)?)?;
|
||||||
if let Ok(name) = str::from_utf8(name)
|
if let Ok(name) = str::from_utf8(name)
|
||||||
@ -508,15 +482,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.write_scalar(Handle::Thread(thread_id).to_scalar(this), dest)?;
|
this.write_scalar(Handle::Thread(thread_id).to_scalar(this), dest)?;
|
||||||
}
|
}
|
||||||
"WaitForSingleObject" => {
|
"WaitForSingleObject" => {
|
||||||
let [handle, timeout] =
|
let [handle, timeout] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
|
|
||||||
let ret = this.WaitForSingleObject(handle, timeout)?;
|
let ret = this.WaitForSingleObject(handle, timeout)?;
|
||||||
this.write_scalar(ret, dest)?;
|
this.write_scalar(ret, dest)?;
|
||||||
}
|
}
|
||||||
"GetCurrentThread" => {
|
"GetCurrentThread" => {
|
||||||
let [] =
|
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
|
|
||||||
this.write_scalar(
|
this.write_scalar(
|
||||||
Handle::Pseudo(PseudoHandle::CurrentThread).to_scalar(this),
|
Handle::Pseudo(PseudoHandle::CurrentThread).to_scalar(this),
|
||||||
@ -524,8 +496,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
"SetThreadDescription" => {
|
"SetThreadDescription" => {
|
||||||
let [handle, name] =
|
let [handle, name] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
|
|
||||||
let handle = this.read_scalar(handle)?;
|
let handle = this.read_scalar(handle)?;
|
||||||
let name = this.read_wide_str(this.read_pointer(name)?)?;
|
let name = this.read_wide_str(this.read_pointer(name)?)?;
|
||||||
@ -549,8 +520,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.write_scalar(res, dest)?;
|
this.write_scalar(res, dest)?;
|
||||||
}
|
}
|
||||||
"GetThreadDescription" => {
|
"GetThreadDescription" => {
|
||||||
let [handle, name_ptr] =
|
let [handle, name_ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
|
|
||||||
let handle = this.read_scalar(handle)?;
|
let handle = this.read_scalar(handle)?;
|
||||||
let name_ptr = this.deref_pointer(name_ptr)?; // the pointer where we should store the ptr to the name
|
let name_ptr = this.deref_pointer(name_ptr)?; // the pointer where we should store the ptr to the name
|
||||||
@ -581,16 +551,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
|
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
"ExitProcess" => {
|
"ExitProcess" => {
|
||||||
let [code] =
|
let [code] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let code = this.read_scalar(code)?.to_u32()?;
|
let code = this.read_scalar(code)?.to_u32()?;
|
||||||
throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false });
|
throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false });
|
||||||
}
|
}
|
||||||
"SystemFunction036" => {
|
"SystemFunction036" => {
|
||||||
// used by getrandom 0.1
|
// used by getrandom 0.1
|
||||||
// This is really 'RtlGenRandom'.
|
// This is really 'RtlGenRandom'.
|
||||||
let [ptr, len] =
|
let [ptr, len] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let ptr = this.read_pointer(ptr)?;
|
let ptr = this.read_pointer(ptr)?;
|
||||||
let len = this.read_scalar(len)?.to_u32()?;
|
let len = this.read_scalar(len)?.to_u32()?;
|
||||||
this.gen_random(ptr, len.into())?;
|
this.gen_random(ptr, len.into())?;
|
||||||
@ -598,8 +566,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
}
|
}
|
||||||
"ProcessPrng" => {
|
"ProcessPrng" => {
|
||||||
// used by `std`
|
// used by `std`
|
||||||
let [ptr, len] =
|
let [ptr, len] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let ptr = this.read_pointer(ptr)?;
|
let ptr = this.read_pointer(ptr)?;
|
||||||
let len = this.read_target_usize(len)?;
|
let len = this.read_target_usize(len)?;
|
||||||
this.gen_random(ptr, len)?;
|
this.gen_random(ptr, len)?;
|
||||||
@ -642,8 +609,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
}
|
}
|
||||||
"GetConsoleScreenBufferInfo" => {
|
"GetConsoleScreenBufferInfo" => {
|
||||||
// `term` needs this, so we fake it.
|
// `term` needs this, so we fake it.
|
||||||
let [console, buffer_info] =
|
let [console, buffer_info] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
this.read_target_isize(console)?;
|
this.read_target_isize(console)?;
|
||||||
// FIXME: this should use deref_pointer_as, but CONSOLE_SCREEN_BUFFER_INFO is not in std
|
// FIXME: this should use deref_pointer_as, but CONSOLE_SCREEN_BUFFER_INFO is not in std
|
||||||
this.deref_pointer(buffer_info)?;
|
this.deref_pointer(buffer_info)?;
|
||||||
@ -652,8 +618,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.write_null(dest)?;
|
this.write_null(dest)?;
|
||||||
}
|
}
|
||||||
"GetStdHandle" => {
|
"GetStdHandle" => {
|
||||||
let [which] =
|
let [which] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
let which = this.read_scalar(which)?.to_i32()?;
|
let which = this.read_scalar(which)?.to_i32()?;
|
||||||
// We just make this the identity function, so we know later in `NtWriteFile` which
|
// We just make this the identity function, so we know later in `NtWriteFile` which
|
||||||
// one it is. This is very fake, but libtest needs it so we cannot make it a
|
// one it is. This is very fake, but libtest needs it so we cannot make it a
|
||||||
@ -662,16 +627,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.write_scalar(Scalar::from_target_isize(which.into(), this), dest)?;
|
this.write_scalar(Scalar::from_target_isize(which.into(), this), dest)?;
|
||||||
}
|
}
|
||||||
"CloseHandle" => {
|
"CloseHandle" => {
|
||||||
let [handle] =
|
let [handle] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
|
|
||||||
let ret = this.CloseHandle(handle)?;
|
let ret = this.CloseHandle(handle)?;
|
||||||
|
|
||||||
this.write_scalar(ret, dest)?;
|
this.write_scalar(ret, dest)?;
|
||||||
}
|
}
|
||||||
"GetModuleFileNameW" => {
|
"GetModuleFileNameW" => {
|
||||||
let [handle, filename, size] =
|
let [handle, filename, size] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
this.check_no_isolation("`GetModuleFileNameW`")?;
|
this.check_no_isolation("`GetModuleFileNameW`")?;
|
||||||
|
|
||||||
let handle = this.read_target_usize(handle)?;
|
let handle = this.read_target_usize(handle)?;
|
||||||
@ -740,16 +703,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
|
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
|
||||||
// These shims are enabled only when the caller is in the standard library.
|
// These shims are enabled only when the caller is in the standard library.
|
||||||
"GetProcessHeap" if this.frame_in_std() => {
|
"GetProcessHeap" if this.frame_in_std() => {
|
||||||
let [] =
|
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
// Just fake a HANDLE
|
// Just fake a HANDLE
|
||||||
// It's fine to not use the Handle type here because its a stub
|
// It's fine to not use the Handle type here because its a stub
|
||||||
this.write_int(1, dest)?;
|
this.write_int(1, dest)?;
|
||||||
}
|
}
|
||||||
"GetModuleHandleA" if this.frame_in_std() => {
|
"GetModuleHandleA" if this.frame_in_std() => {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let [_lpModuleName] =
|
let [_lpModuleName] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
// We need to return something non-null here to make `compat_fn!` work.
|
// We need to return something non-null here to make `compat_fn!` work.
|
||||||
this.write_int(1, dest)?;
|
this.write_int(1, dest)?;
|
||||||
}
|
}
|
||||||
@ -761,8 +722,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
this.write_null(dest)?;
|
this.write_null(dest)?;
|
||||||
}
|
}
|
||||||
"GetConsoleMode" if this.frame_in_std() => {
|
"GetConsoleMode" if this.frame_in_std() => {
|
||||||
let [console, mode] =
|
let [console, mode] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
this.read_target_isize(console)?;
|
this.read_target_isize(console)?;
|
||||||
this.deref_pointer(mode)?;
|
this.deref_pointer(mode)?;
|
||||||
// Indicate an error.
|
// Indicate an error.
|
||||||
@ -770,29 +730,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
}
|
}
|
||||||
"GetFileType" if this.frame_in_std() => {
|
"GetFileType" if this.frame_in_std() => {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let [_hFile] =
|
let [_hFile] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
// Return unknown file type.
|
// Return unknown file type.
|
||||||
this.write_null(dest)?;
|
this.write_null(dest)?;
|
||||||
}
|
}
|
||||||
"AddVectoredExceptionHandler" if this.frame_in_std() => {
|
"AddVectoredExceptionHandler" if this.frame_in_std() => {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let [_First, _Handler] =
|
let [_First, _Handler] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
// Any non zero value works for the stdlib. This is just used for stack overflows anyway.
|
// Any non zero value works for the stdlib. This is just used for stack overflows anyway.
|
||||||
this.write_int(1, dest)?;
|
this.write_int(1, dest)?;
|
||||||
}
|
}
|
||||||
"SetThreadStackGuarantee" if this.frame_in_std() => {
|
"SetThreadStackGuarantee" if this.frame_in_std() => {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let [_StackSizeInBytes] =
|
let [_StackSizeInBytes] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
// Any non zero value works for the stdlib. This is just used for stack overflows anyway.
|
// Any non zero value works for the stdlib. This is just used for stack overflows anyway.
|
||||||
this.write_int(1, dest)?;
|
this.write_int(1, dest)?;
|
||||||
}
|
}
|
||||||
// this is only callable from std because we know that std ignores the return value
|
// this is only callable from std because we know that std ignores the return value
|
||||||
"SwitchToThread" if this.frame_in_std() => {
|
"SwitchToThread" if this.frame_in_std() => {
|
||||||
let [] =
|
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
|
||||||
this.check_shim(abi, sys_conv, link_name, args)?;
|
|
||||||
|
|
||||||
this.yield_active_thread();
|
this.yield_active_thread();
|
||||||
|
|
||||||
@ -811,8 +767,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
// This function looks and behaves excatly like miri_start_unwind.
|
// This function looks and behaves excatly like miri_start_unwind.
|
||||||
let [payload] =
|
let [payload] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
this.handle_miri_start_unwind(payload)?;
|
this.handle_miri_start_unwind(payload)?;
|
||||||
return interp_ok(EmulateItemResult::NeedsUnwind);
|
return interp_ok(EmulateItemResult::NeedsUnwind);
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// `state` with the corresponding 128-bit key of `key`.
|
// `state` with the corresponding 128-bit key of `key`.
|
||||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdec_si128
|
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdec_si128
|
||||||
"aesdec" | "aesdec.256" | "aesdec.512" => {
|
"aesdec" | "aesdec.256" | "aesdec.512" => {
|
||||||
let [state, key] =
|
let [state, key] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
aes_round(this, state, key, dest, |state, key| {
|
aes_round(this, state, key, dest, |state, key| {
|
||||||
let key = aes::Block::from(key.to_le_bytes());
|
let key = aes::Block::from(key.to_le_bytes());
|
||||||
let mut state = aes::Block::from(state.to_le_bytes());
|
let mut state = aes::Block::from(state.to_le_bytes());
|
||||||
@ -43,8 +42,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// `state` with the corresponding 128-bit key of `key`.
|
// `state` with the corresponding 128-bit key of `key`.
|
||||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdeclast_si128
|
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdeclast_si128
|
||||||
"aesdeclast" | "aesdeclast.256" | "aesdeclast.512" => {
|
"aesdeclast" | "aesdeclast.256" | "aesdeclast.512" => {
|
||||||
let [state, key] =
|
let [state, key] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
aes_round(this, state, key, dest, |state, key| {
|
aes_round(this, state, key, dest, |state, key| {
|
||||||
let mut state = aes::Block::from(state.to_le_bytes());
|
let mut state = aes::Block::from(state.to_le_bytes());
|
||||||
@ -68,8 +66,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// `state` with the corresponding 128-bit key of `key`.
|
// `state` with the corresponding 128-bit key of `key`.
|
||||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenc_si128
|
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenc_si128
|
||||||
"aesenc" | "aesenc.256" | "aesenc.512" => {
|
"aesenc" | "aesenc.256" | "aesenc.512" => {
|
||||||
let [state, key] =
|
let [state, key] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
aes_round(this, state, key, dest, |state, key| {
|
aes_round(this, state, key, dest, |state, key| {
|
||||||
let key = aes::Block::from(key.to_le_bytes());
|
let key = aes::Block::from(key.to_le_bytes());
|
||||||
let mut state = aes::Block::from(state.to_le_bytes());
|
let mut state = aes::Block::from(state.to_le_bytes());
|
||||||
@ -85,8 +82,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// `state` with the corresponding 128-bit key of `key`.
|
// `state` with the corresponding 128-bit key of `key`.
|
||||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenclast_si128
|
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenclast_si128
|
||||||
"aesenclast" | "aesenclast.256" | "aesenclast.512" => {
|
"aesenclast" | "aesenclast.256" | "aesenclast.512" => {
|
||||||
let [state, key] =
|
let [state, key] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
aes_round(this, state, key, dest, |state, key| {
|
aes_round(this, state, key, dest, |state, key| {
|
||||||
let mut state = aes::Block::from(state.to_le_bytes());
|
let mut state = aes::Block::from(state.to_le_bytes());
|
||||||
// `aes::hazmat::cipher_round` does the following operations:
|
// `aes::hazmat::cipher_round` does the following operations:
|
||||||
|
@ -33,8 +33,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// matches the IEEE min/max operations, while x86 has different
|
// matches the IEEE min/max operations, while x86 has different
|
||||||
// semantics.
|
// semantics.
|
||||||
"min.ps.256" | "max.ps.256" => {
|
"min.ps.256" | "max.ps.256" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let which = match unprefixed_name {
|
let which = match unprefixed_name {
|
||||||
"min.ps.256" => FloatBinOp::Min,
|
"min.ps.256" => FloatBinOp::Min,
|
||||||
@ -46,8 +45,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
}
|
}
|
||||||
// Used to implement _mm256_min_pd and _mm256_max_pd functions.
|
// Used to implement _mm256_min_pd and _mm256_max_pd functions.
|
||||||
"min.pd.256" | "max.pd.256" => {
|
"min.pd.256" | "max.pd.256" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let which = match unprefixed_name {
|
let which = match unprefixed_name {
|
||||||
"min.pd.256" => FloatBinOp::Min,
|
"min.pd.256" => FloatBinOp::Min,
|
||||||
@ -60,16 +58,14 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Used to implement the _mm256_round_ps function.
|
// Used to implement the _mm256_round_ps function.
|
||||||
// Rounds the elements of `op` according to `rounding`.
|
// Rounds the elements of `op` according to `rounding`.
|
||||||
"round.ps.256" => {
|
"round.ps.256" => {
|
||||||
let [op, rounding] =
|
let [op, rounding] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
round_all::<rustc_apfloat::ieee::Single>(this, op, rounding, dest)?;
|
round_all::<rustc_apfloat::ieee::Single>(this, op, rounding, dest)?;
|
||||||
}
|
}
|
||||||
// Used to implement the _mm256_round_pd function.
|
// Used to implement the _mm256_round_pd function.
|
||||||
// Rounds the elements of `op` according to `rounding`.
|
// Rounds the elements of `op` according to `rounding`.
|
||||||
"round.pd.256" => {
|
"round.pd.256" => {
|
||||||
let [op, rounding] =
|
let [op, rounding] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
round_all::<rustc_apfloat::ieee::Double>(this, op, rounding, dest)?;
|
round_all::<rustc_apfloat::ieee::Double>(this, op, rounding, dest)?;
|
||||||
}
|
}
|
||||||
@ -88,8 +84,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
}
|
}
|
||||||
// Used to implement the _mm256_dp_ps function.
|
// Used to implement the _mm256_dp_ps function.
|
||||||
"dp.ps.256" => {
|
"dp.ps.256" => {
|
||||||
let [left, right, imm] =
|
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
conditional_dot_product(this, left, right, imm, dest)?;
|
conditional_dot_product(this, left, right, imm, dest)?;
|
||||||
}
|
}
|
||||||
@ -97,8 +92,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Horizontally add/subtract adjacent floating point values
|
// Horizontally add/subtract adjacent floating point values
|
||||||
// in `left` and `right`.
|
// in `left` and `right`.
|
||||||
"hadd.ps.256" | "hadd.pd.256" | "hsub.ps.256" | "hsub.pd.256" => {
|
"hadd.ps.256" | "hadd.pd.256" | "hsub.ps.256" | "hsub.pd.256" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let which = match unprefixed_name {
|
let which = match unprefixed_name {
|
||||||
"hadd.ps.256" | "hadd.pd.256" => mir::BinOp::Add,
|
"hadd.ps.256" | "hadd.pd.256" => mir::BinOp::Add,
|
||||||
@ -113,8 +107,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// and `right`. For each component, returns 0 if false or u32::MAX
|
// and `right`. For each component, returns 0 if false or u32::MAX
|
||||||
// if true.
|
// if true.
|
||||||
"cmp.ps.256" => {
|
"cmp.ps.256" => {
|
||||||
let [left, right, imm] =
|
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let which =
|
let which =
|
||||||
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
|
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
|
||||||
@ -126,8 +119,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// and `right`. For each component, returns 0 if false or u64::MAX
|
// and `right`. For each component, returns 0 if false or u64::MAX
|
||||||
// if true.
|
// if true.
|
||||||
"cmp.pd.256" => {
|
"cmp.pd.256" => {
|
||||||
let [left, right, imm] =
|
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let which =
|
let which =
|
||||||
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
|
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
|
||||||
@ -156,8 +148,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// sequence of 4-element arrays, and we shuffle each of these arrays, where
|
// sequence of 4-element arrays, and we shuffle each of these arrays, where
|
||||||
// `control` determines which element of the current `data` array is written.
|
// `control` determines which element of the current `data` array is written.
|
||||||
"vpermilvar.ps" | "vpermilvar.ps.256" => {
|
"vpermilvar.ps" | "vpermilvar.ps.256" => {
|
||||||
let [data, control] =
|
let [data, control] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (data, data_len) = this.project_to_simd(data)?;
|
let (data, data_len) = this.project_to_simd(data)?;
|
||||||
let (control, control_len) = this.project_to_simd(control)?;
|
let (control, control_len) = this.project_to_simd(control)?;
|
||||||
@ -190,8 +181,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// where `right` determines which element of the current `left` array is
|
// where `right` determines which element of the current `left` array is
|
||||||
// written.
|
// written.
|
||||||
"vpermilvar.pd" | "vpermilvar.pd.256" => {
|
"vpermilvar.pd" | "vpermilvar.pd.256" => {
|
||||||
let [data, control] =
|
let [data, control] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (data, data_len) = this.project_to_simd(data)?;
|
let (data, data_len) = this.project_to_simd(data)?;
|
||||||
let (control, control_len) = this.project_to_simd(control)?;
|
let (control, control_len) = this.project_to_simd(control)?;
|
||||||
@ -223,8 +213,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// For each 128-bit element of `dest`, copies one from `left`, `right` or
|
// For each 128-bit element of `dest`, copies one from `left`, `right` or
|
||||||
// zero, according to `imm`.
|
// zero, according to `imm`.
|
||||||
"vperm2f128.ps.256" | "vperm2f128.pd.256" | "vperm2f128.si.256" => {
|
"vperm2f128.ps.256" | "vperm2f128.pd.256" | "vperm2f128.si.256" => {
|
||||||
let [left, right, imm] =
|
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
assert_eq!(dest.layout, left.layout);
|
assert_eq!(dest.layout, left.layout);
|
||||||
assert_eq!(dest.layout, right.layout);
|
assert_eq!(dest.layout, right.layout);
|
||||||
@ -267,8 +256,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// is one, it is loaded from `ptr.wrapping_add(i)`, otherwise zero is
|
// is one, it is loaded from `ptr.wrapping_add(i)`, otherwise zero is
|
||||||
// loaded.
|
// loaded.
|
||||||
"maskload.ps" | "maskload.pd" | "maskload.ps.256" | "maskload.pd.256" => {
|
"maskload.ps" | "maskload.pd" | "maskload.ps.256" | "maskload.pd.256" => {
|
||||||
let [ptr, mask] =
|
let [ptr, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
mask_load(this, ptr, mask, dest)?;
|
mask_load(this, ptr, mask, dest)?;
|
||||||
}
|
}
|
||||||
@ -278,8 +266,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// is one, it is stored into `ptr.wapping_add(i)`.
|
// is one, it is stored into `ptr.wapping_add(i)`.
|
||||||
// Unlike SSE2's _mm_maskmoveu_si128, these are not non-temporal stores.
|
// Unlike SSE2's _mm_maskmoveu_si128, these are not non-temporal stores.
|
||||||
"maskstore.ps" | "maskstore.pd" | "maskstore.ps.256" | "maskstore.pd.256" => {
|
"maskstore.ps" | "maskstore.pd" | "maskstore.ps.256" | "maskstore.pd.256" => {
|
||||||
let [ptr, mask, value] =
|
let [ptr, mask, value] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
mask_store(this, ptr, mask, value)?;
|
mask_store(this, ptr, mask, value)?;
|
||||||
}
|
}
|
||||||
@ -289,8 +276,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// the data crosses a cache line, but for Miri this is just a regular
|
// the data crosses a cache line, but for Miri this is just a regular
|
||||||
// unaligned read.
|
// unaligned read.
|
||||||
"ldu.dq.256" => {
|
"ldu.dq.256" => {
|
||||||
let [src_ptr] =
|
let [src_ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let src_ptr = this.read_pointer(src_ptr)?;
|
let src_ptr = this.read_pointer(src_ptr)?;
|
||||||
let dest = dest.force_mplace(this)?;
|
let dest = dest.force_mplace(this)?;
|
||||||
|
|
||||||
@ -302,8 +288,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Tests `op & mask == 0`, `op & mask == mask` or
|
// Tests `op & mask == 0`, `op & mask == mask` or
|
||||||
// `op & mask != 0 && op & mask != mask`
|
// `op & mask != 0 && op & mask != mask`
|
||||||
"ptestz.256" | "ptestc.256" | "ptestnzc.256" => {
|
"ptestz.256" | "ptestc.256" | "ptestnzc.256" => {
|
||||||
let [op, mask] =
|
let [op, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (all_zero, masked_set) = test_bits_masked(this, op, mask)?;
|
let (all_zero, masked_set) = test_bits_masked(this, op, mask)?;
|
||||||
let res = match unprefixed_name {
|
let res = match unprefixed_name {
|
||||||
@ -326,8 +311,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
"vtestz.pd.256" | "vtestc.pd.256" | "vtestnzc.pd.256" | "vtestz.pd" | "vtestc.pd"
|
"vtestz.pd.256" | "vtestc.pd.256" | "vtestnzc.pd.256" | "vtestz.pd" | "vtestc.pd"
|
||||||
| "vtestnzc.pd" | "vtestz.ps.256" | "vtestc.ps.256" | "vtestnzc.ps.256"
|
| "vtestnzc.pd" | "vtestz.ps.256" | "vtestc.ps.256" | "vtestnzc.ps.256"
|
||||||
| "vtestz.ps" | "vtestc.ps" | "vtestnzc.ps" => {
|
| "vtestz.ps" | "vtestc.ps" | "vtestnzc.ps" => {
|
||||||
let [op, mask] =
|
let [op, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (direct, negated) = test_high_bits_masked(this, op, mask)?;
|
let (direct, negated) = test_high_bits_masked(this, op, mask)?;
|
||||||
let res = match unprefixed_name {
|
let res = match unprefixed_name {
|
||||||
|
@ -36,8 +36,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Horizontally add / add with saturation / subtract adjacent 16/32-bit
|
// Horizontally add / add with saturation / subtract adjacent 16/32-bit
|
||||||
// integer values in `left` and `right`.
|
// integer values in `left` and `right`.
|
||||||
"phadd.w" | "phadd.sw" | "phadd.d" | "phsub.w" | "phsub.sw" | "phsub.d" => {
|
"phadd.w" | "phadd.sw" | "phadd.d" | "phsub.w" | "phsub.sw" | "phsub.d" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (which, saturating) = match unprefixed_name {
|
let (which, saturating) = match unprefixed_name {
|
||||||
"phadd.w" | "phadd.d" => (mir::BinOp::Add, false),
|
"phadd.w" | "phadd.d" => (mir::BinOp::Add, false),
|
||||||
@ -115,8 +114,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// intermediate signed 32-bit integers. Horizontally add adjacent pairs of
|
// intermediate signed 32-bit integers. Horizontally add adjacent pairs of
|
||||||
// intermediate 32-bit integers, and pack the results in `dest`.
|
// intermediate 32-bit integers, and pack the results in `dest`.
|
||||||
"pmadd.wd" => {
|
"pmadd.wd" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (left, left_len) = this.project_to_simd(left)?;
|
let (left, left_len) = this.project_to_simd(left)?;
|
||||||
let (right, right_len) = this.project_to_simd(right)?;
|
let (right, right_len) = this.project_to_simd(right)?;
|
||||||
@ -152,8 +150,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// the saturating sum of the products with indices `2*i` and `2*i+1`
|
// the saturating sum of the products with indices `2*i` and `2*i+1`
|
||||||
// produces the output at index `i`.
|
// produces the output at index `i`.
|
||||||
"pmadd.ub.sw" => {
|
"pmadd.ub.sw" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (left, left_len) = this.project_to_simd(left)?;
|
let (left, left_len) = this.project_to_simd(left)?;
|
||||||
let (right, right_len) = this.project_to_simd(right)?;
|
let (right, right_len) = this.project_to_simd(right)?;
|
||||||
@ -187,8 +184,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// is one, it is loaded from `ptr.wrapping_add(i)`, otherwise zero is
|
// is one, it is loaded from `ptr.wrapping_add(i)`, otherwise zero is
|
||||||
// loaded.
|
// loaded.
|
||||||
"maskload.d" | "maskload.q" | "maskload.d.256" | "maskload.q.256" => {
|
"maskload.d" | "maskload.q" | "maskload.d.256" | "maskload.q.256" => {
|
||||||
let [ptr, mask] =
|
let [ptr, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
mask_load(this, ptr, mask, dest)?;
|
mask_load(this, ptr, mask, dest)?;
|
||||||
}
|
}
|
||||||
@ -198,8 +194,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// is one, it is stored into `ptr.wapping_add(i)`.
|
// is one, it is stored into `ptr.wapping_add(i)`.
|
||||||
// Unlike SSE2's _mm_maskmoveu_si128, these are not non-temporal stores.
|
// Unlike SSE2's _mm_maskmoveu_si128, these are not non-temporal stores.
|
||||||
"maskstore.d" | "maskstore.q" | "maskstore.d.256" | "maskstore.q.256" => {
|
"maskstore.d" | "maskstore.q" | "maskstore.d.256" | "maskstore.q.256" => {
|
||||||
let [ptr, mask, value] =
|
let [ptr, mask, value] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
mask_store(this, ptr, mask, value)?;
|
mask_store(this, ptr, mask, value)?;
|
||||||
}
|
}
|
||||||
@ -210,8 +205,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// offsets specified in `imm`.
|
// offsets specified in `imm`.
|
||||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mpsadbw_epu8
|
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mpsadbw_epu8
|
||||||
"mpsadbw" => {
|
"mpsadbw" => {
|
||||||
let [left, right, imm] =
|
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
mpsadbw(this, left, right, imm, dest)?;
|
mpsadbw(this, left, right, imm, dest)?;
|
||||||
}
|
}
|
||||||
@ -222,8 +216,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// 1 and then taking the bits `1..=16`.
|
// 1 and then taking the bits `1..=16`.
|
||||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mulhrs_epi16
|
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mulhrs_epi16
|
||||||
"pmul.hr.sw" => {
|
"pmul.hr.sw" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
pmulhrsw(this, left, right, dest)?;
|
pmulhrsw(this, left, right, dest)?;
|
||||||
}
|
}
|
||||||
@ -231,8 +224,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Converts two 16-bit integer vectors to a single 8-bit integer
|
// Converts two 16-bit integer vectors to a single 8-bit integer
|
||||||
// vector with signed saturation.
|
// vector with signed saturation.
|
||||||
"packsswb" => {
|
"packsswb" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
packsswb(this, left, right, dest)?;
|
packsswb(this, left, right, dest)?;
|
||||||
}
|
}
|
||||||
@ -240,8 +232,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Converts two 32-bit integer vectors to a single 16-bit integer
|
// Converts two 32-bit integer vectors to a single 16-bit integer
|
||||||
// vector with signed saturation.
|
// vector with signed saturation.
|
||||||
"packssdw" => {
|
"packssdw" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
packssdw(this, left, right, dest)?;
|
packssdw(this, left, right, dest)?;
|
||||||
}
|
}
|
||||||
@ -249,8 +240,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Converts two 16-bit signed integer vectors to a single 8-bit
|
// Converts two 16-bit signed integer vectors to a single 8-bit
|
||||||
// unsigned integer vector with saturation.
|
// unsigned integer vector with saturation.
|
||||||
"packuswb" => {
|
"packuswb" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
packuswb(this, left, right, dest)?;
|
packuswb(this, left, right, dest)?;
|
||||||
}
|
}
|
||||||
@ -258,8 +248,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Concatenates two 32-bit signed integer vectors and converts
|
// Concatenates two 32-bit signed integer vectors and converts
|
||||||
// the result to a 16-bit unsigned integer vector with saturation.
|
// the result to a 16-bit unsigned integer vector with saturation.
|
||||||
"packusdw" => {
|
"packusdw" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
packusdw(this, left, right, dest)?;
|
packusdw(this, left, right, dest)?;
|
||||||
}
|
}
|
||||||
@ -268,8 +257,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Shuffles `left` using the three low bits of each element of `right`
|
// Shuffles `left` using the three low bits of each element of `right`
|
||||||
// as indices.
|
// as indices.
|
||||||
"permd" | "permps" => {
|
"permd" | "permps" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (left, left_len) = this.project_to_simd(left)?;
|
let (left, left_len) = this.project_to_simd(left)?;
|
||||||
let (right, right_len) = this.project_to_simd(right)?;
|
let (right, right_len) = this.project_to_simd(right)?;
|
||||||
@ -289,8 +277,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Used to implement the _mm256_permute2x128_si256 function.
|
// Used to implement the _mm256_permute2x128_si256 function.
|
||||||
// Shuffles 128-bit blocks of `a` and `b` using `imm` as pattern.
|
// Shuffles 128-bit blocks of `a` and `b` using `imm` as pattern.
|
||||||
"vperm2i128" => {
|
"vperm2i128" => {
|
||||||
let [left, right, imm] =
|
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
assert_eq!(left.layout.size.bits(), 256);
|
assert_eq!(left.layout.size.bits(), 256);
|
||||||
assert_eq!(right.layout.size.bits(), 256);
|
assert_eq!(right.layout.size.bits(), 256);
|
||||||
@ -327,8 +314,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// in `dest`.
|
// in `dest`.
|
||||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_sad_epu8
|
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_sad_epu8
|
||||||
"psad.bw" => {
|
"psad.bw" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (left, left_len) = this.project_to_simd(left)?;
|
let (left, left_len) = this.project_to_simd(left)?;
|
||||||
let (right, right_len) = this.project_to_simd(right)?;
|
let (right, right_len) = this.project_to_simd(right)?;
|
||||||
@ -360,8 +346,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Shuffles bytes from `left` using `right` as pattern.
|
// Shuffles bytes from `left` using `right` as pattern.
|
||||||
// Each 128-bit block is shuffled independently.
|
// Each 128-bit block is shuffled independently.
|
||||||
"pshuf.b" => {
|
"pshuf.b" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (left, left_len) = this.project_to_simd(left)?;
|
let (left, left_len) = this.project_to_simd(left)?;
|
||||||
let (right, right_len) = this.project_to_simd(right)?;
|
let (right, right_len) = this.project_to_simd(right)?;
|
||||||
@ -392,8 +377,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// is writen to the corresponding output element.
|
// is writen to the corresponding output element.
|
||||||
// Basically, we multiply `left` with `right.signum()`.
|
// Basically, we multiply `left` with `right.signum()`.
|
||||||
"psign.b" | "psign.w" | "psign.d" => {
|
"psign.b" | "psign.w" | "psign.d" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
psign(this, left, right, dest)?;
|
psign(this, left, right, dest)?;
|
||||||
}
|
}
|
||||||
@ -407,8 +391,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// is copied to remaining bits.
|
// is copied to remaining bits.
|
||||||
"psll.w" | "psrl.w" | "psra.w" | "psll.d" | "psrl.d" | "psra.d" | "psll.q"
|
"psll.w" | "psrl.w" | "psra.w" | "psll.d" | "psrl.d" | "psra.d" | "psll.q"
|
||||||
| "psrl.q" => {
|
| "psrl.q" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let which = match unprefixed_name {
|
let which = match unprefixed_name {
|
||||||
"psll.w" | "psll.d" | "psll.q" => ShiftOp::Left,
|
"psll.w" | "psll.d" | "psll.q" => ShiftOp::Left,
|
||||||
@ -423,8 +406,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// (except _mm{,256}_srav_epi64, which are not available in AVX2).
|
// (except _mm{,256}_srav_epi64, which are not available in AVX2).
|
||||||
"psllv.d" | "psllv.d.256" | "psllv.q" | "psllv.q.256" | "psrlv.d" | "psrlv.d.256"
|
"psllv.d" | "psllv.d.256" | "psllv.q" | "psllv.q.256" | "psrlv.d" | "psrlv.d.256"
|
||||||
| "psrlv.q" | "psrlv.q.256" | "psrav.d" | "psrav.d.256" => {
|
| "psrlv.q" | "psrlv.q.256" | "psrav.d" | "psrav.d.256" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let which = match unprefixed_name {
|
let which = match unprefixed_name {
|
||||||
"psllv.d" | "psllv.d.256" | "psllv.q" | "psllv.q.256" => ShiftOp::Left,
|
"psllv.d" | "psllv.d.256" | "psllv.q" | "psllv.q.256" => ShiftOp::Left,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use rustc_span::Symbol;
|
|
||||||
use rustc_middle::ty::Ty;
|
use rustc_middle::ty::Ty;
|
||||||
|
use rustc_span::Symbol;
|
||||||
use rustc_target::callconv::{Conv, FnAbi};
|
use rustc_target::callconv::{Conv, FnAbi};
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
@ -34,8 +34,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
return interp_ok(EmulateItemResult::NotSupported);
|
return interp_ok(EmulateItemResult::NotSupported);
|
||||||
}
|
}
|
||||||
|
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let left = this.read_scalar(left)?;
|
let left = this.read_scalar(left)?;
|
||||||
let right = this.read_scalar(right)?;
|
let right = this.read_scalar(right)?;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use rustc_span::Symbol;
|
|
||||||
use rustc_middle::ty::Ty;
|
use rustc_middle::ty::Ty;
|
||||||
|
use rustc_span::Symbol;
|
||||||
use rustc_target::callconv::{Conv, FnAbi};
|
use rustc_target::callconv::{Conv, FnAbi};
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
@ -30,16 +30,14 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// See `affine_transform` for details.
|
// See `affine_transform` for details.
|
||||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8affine_
|
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8affine_
|
||||||
"vgf2p8affineqb.128" | "vgf2p8affineqb.256" | "vgf2p8affineqb.512" => {
|
"vgf2p8affineqb.128" | "vgf2p8affineqb.256" | "vgf2p8affineqb.512" => {
|
||||||
let [left, right, imm8] =
|
let [left, right, imm8] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
affine_transform(this, left, right, imm8, dest, /* inverse */ false)?;
|
affine_transform(this, left, right, imm8, dest, /* inverse */ false)?;
|
||||||
}
|
}
|
||||||
// Used to implement the `_mm{, 256, 512}_gf2p8affineinv_epi64_epi8` functions.
|
// Used to implement the `_mm{, 256, 512}_gf2p8affineinv_epi64_epi8` functions.
|
||||||
// See `affine_transform` for details.
|
// See `affine_transform` for details.
|
||||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8affineinv
|
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8affineinv
|
||||||
"vgf2p8affineinvqb.128" | "vgf2p8affineinvqb.256" | "vgf2p8affineinvqb.512" => {
|
"vgf2p8affineinvqb.128" | "vgf2p8affineinvqb.256" | "vgf2p8affineinvqb.512" => {
|
||||||
let [left, right, imm8] =
|
let [left, right, imm8] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
affine_transform(this, left, right, imm8, dest, /* inverse */ true)?;
|
affine_transform(this, left, right, imm8, dest, /* inverse */ true)?;
|
||||||
}
|
}
|
||||||
// Used to implement the `_mm{, 256, 512}_gf2p8mul_epi8` functions.
|
// Used to implement the `_mm{, 256, 512}_gf2p8mul_epi8` functions.
|
||||||
@ -48,8 +46,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// polynomial representation with the reduction polynomial x^8 + x^4 + x^3 + x + 1.
|
// polynomial representation with the reduction polynomial x^8 + x^4 + x^3 + x + 1.
|
||||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8mul
|
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8mul
|
||||||
"vgf2p8mulb.128" | "vgf2p8mulb.256" | "vgf2p8mulb.512" => {
|
"vgf2p8mulb.128" | "vgf2p8mulb.256" | "vgf2p8mulb.512" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let (left, left_len) = this.project_to_simd(left)?;
|
let (left, left_len) = this.project_to_simd(left)?;
|
||||||
let (right, right_len) = this.project_to_simd(right)?;
|
let (right, right_len) = this.project_to_simd(right)?;
|
||||||
let (dest, dest_len) = this.project_to_simd(dest)?;
|
let (dest, dest_len) = this.project_to_simd(dest)?;
|
||||||
|
@ -68,8 +68,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
if is_u64 && this.tcx.sess.target.arch != "x86_64" {
|
if is_u64 && this.tcx.sess.target.arch != "x86_64" {
|
||||||
return interp_ok(EmulateItemResult::NotSupported);
|
return interp_ok(EmulateItemResult::NotSupported);
|
||||||
}
|
}
|
||||||
let [c_in, a, b, out] =
|
let [c_in, a, b, out] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let out = this.deref_pointer_as(
|
let out = this.deref_pointer_as(
|
||||||
out,
|
out,
|
||||||
if is_u64 { this.machine.layouts.u64 } else { this.machine.layouts.u32 },
|
if is_u64 { this.machine.layouts.u64 } else { this.machine.layouts.u32 },
|
||||||
@ -105,8 +104,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
len = 8;
|
len = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [left, right, imm] =
|
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
pclmulqdq(this, left, right, imm, dest, len)?;
|
pclmulqdq(this, left, right, imm, dest, len)?;
|
||||||
}
|
}
|
||||||
|
@ -52,8 +52,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
match unprefixed_name {
|
match unprefixed_name {
|
||||||
// Used to implement the _mm_sha256rnds2_epu32 function.
|
// Used to implement the _mm_sha256rnds2_epu32 function.
|
||||||
"256rnds2" => {
|
"256rnds2" => {
|
||||||
let [a, b, k] =
|
let [a, b, k] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (a_reg, a_len) = this.project_to_simd(a)?;
|
let (a_reg, a_len) = this.project_to_simd(a)?;
|
||||||
let (b_reg, b_len) = this.project_to_simd(b)?;
|
let (b_reg, b_len) = this.project_to_simd(b)?;
|
||||||
@ -74,8 +73,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
}
|
}
|
||||||
// Used to implement the _mm_sha256msg1_epu32 function.
|
// Used to implement the _mm_sha256msg1_epu32 function.
|
||||||
"256msg1" => {
|
"256msg1" => {
|
||||||
let [a, b] =
|
let [a, b] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (a_reg, a_len) = this.project_to_simd(a)?;
|
let (a_reg, a_len) = this.project_to_simd(a)?;
|
||||||
let (b_reg, b_len) = this.project_to_simd(b)?;
|
let (b_reg, b_len) = this.project_to_simd(b)?;
|
||||||
@ -93,8 +91,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
}
|
}
|
||||||
// Used to implement the _mm_sha256msg2_epu32 function.
|
// Used to implement the _mm_sha256msg2_epu32 function.
|
||||||
"256msg2" => {
|
"256msg2" => {
|
||||||
let [a, b] =
|
let [a, b] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (a_reg, a_len) = this.project_to_simd(a)?;
|
let (a_reg, a_len) = this.project_to_simd(a)?;
|
||||||
let (b_reg, b_len) = this.project_to_simd(b)?;
|
let (b_reg, b_len) = this.project_to_simd(b)?;
|
||||||
|
@ -33,8 +33,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Performs the operations on the first component of `left` and
|
// Performs the operations on the first component of `left` and
|
||||||
// `right` and copies the remaining components from `left`.
|
// `right` and copies the remaining components from `left`.
|
||||||
"min.ss" | "max.ss" => {
|
"min.ss" | "max.ss" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let which = match unprefixed_name {
|
let which = match unprefixed_name {
|
||||||
"min.ss" => FloatBinOp::Min,
|
"min.ss" => FloatBinOp::Min,
|
||||||
@ -50,8 +49,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// matches the IEEE min/max operations, while x86 has different
|
// matches the IEEE min/max operations, while x86 has different
|
||||||
// semantics.
|
// semantics.
|
||||||
"min.ps" | "max.ps" => {
|
"min.ps" | "max.ps" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let which = match unprefixed_name {
|
let which = match unprefixed_name {
|
||||||
"min.ps" => FloatBinOp::Min,
|
"min.ps" => FloatBinOp::Min,
|
||||||
@ -97,8 +95,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_ss are SSE functions
|
// _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_ss are SSE functions
|
||||||
// with hard-coded operations.
|
// with hard-coded operations.
|
||||||
"cmp.ss" => {
|
"cmp.ss" => {
|
||||||
let [left, right, imm] =
|
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let which =
|
let which =
|
||||||
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
|
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
|
||||||
@ -114,8 +111,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_ps are SSE functions
|
// _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_ps are SSE functions
|
||||||
// with hard-coded operations.
|
// with hard-coded operations.
|
||||||
"cmp.ps" => {
|
"cmp.ps" => {
|
||||||
let [left, right, imm] =
|
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let which =
|
let which =
|
||||||
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
|
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
|
||||||
@ -128,8 +124,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
"comieq.ss" | "comilt.ss" | "comile.ss" | "comigt.ss" | "comige.ss" | "comineq.ss"
|
"comieq.ss" | "comilt.ss" | "comile.ss" | "comigt.ss" | "comige.ss" | "comineq.ss"
|
||||||
| "ucomieq.ss" | "ucomilt.ss" | "ucomile.ss" | "ucomigt.ss" | "ucomige.ss"
|
| "ucomieq.ss" | "ucomilt.ss" | "ucomile.ss" | "ucomigt.ss" | "ucomige.ss"
|
||||||
| "ucomineq.ss" => {
|
| "ucomineq.ss" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (left, left_len) = this.project_to_simd(left)?;
|
let (left, left_len) = this.project_to_simd(left)?;
|
||||||
let (right, right_len) = this.project_to_simd(right)?;
|
let (right, right_len) = this.project_to_simd(right)?;
|
||||||
@ -185,8 +180,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// are copied from `left`.
|
// are copied from `left`.
|
||||||
// https://www.felixcloutier.com/x86/cvtsi2ss
|
// https://www.felixcloutier.com/x86/cvtsi2ss
|
||||||
"cvtsi2ss" | "cvtsi642ss" => {
|
"cvtsi2ss" | "cvtsi642ss" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (left, left_len) = this.project_to_simd(left)?;
|
let (left, left_len) = this.project_to_simd(left)?;
|
||||||
let (dest, dest_len) = this.project_to_simd(dest)?;
|
let (dest, dest_len) = this.project_to_simd(dest)?;
|
||||||
|
@ -40,8 +40,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// intermediate signed 32-bit integers. Horizontally add adjacent pairs of
|
// intermediate signed 32-bit integers. Horizontally add adjacent pairs of
|
||||||
// intermediate 32-bit integers, and pack the results in `dest`.
|
// intermediate 32-bit integers, and pack the results in `dest`.
|
||||||
"pmadd.wd" => {
|
"pmadd.wd" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (left, left_len) = this.project_to_simd(left)?;
|
let (left, left_len) = this.project_to_simd(left)?;
|
||||||
let (right, right_len) = this.project_to_simd(right)?;
|
let (right, right_len) = this.project_to_simd(right)?;
|
||||||
@ -79,8 +78,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
//
|
//
|
||||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sad_epu8
|
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sad_epu8
|
||||||
"psad.bw" => {
|
"psad.bw" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (left, left_len) = this.project_to_simd(left)?;
|
let (left, left_len) = this.project_to_simd(left)?;
|
||||||
let (right, right_len) = this.project_to_simd(right)?;
|
let (right, right_len) = this.project_to_simd(right)?;
|
||||||
@ -118,8 +116,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// is copied to remaining bits.
|
// is copied to remaining bits.
|
||||||
"psll.w" | "psrl.w" | "psra.w" | "psll.d" | "psrl.d" | "psra.d" | "psll.q"
|
"psll.w" | "psrl.w" | "psra.w" | "psll.d" | "psrl.d" | "psra.d" | "psll.q"
|
||||||
| "psrl.q" => {
|
| "psrl.q" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let which = match unprefixed_name {
|
let which = match unprefixed_name {
|
||||||
"psll.w" | "psll.d" | "psll.q" => ShiftOp::Left,
|
"psll.w" | "psll.d" | "psll.q" => ShiftOp::Left,
|
||||||
@ -171,8 +168,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Converts two 16-bit integer vectors to a single 8-bit integer
|
// Converts two 16-bit integer vectors to a single 8-bit integer
|
||||||
// vector with signed saturation.
|
// vector with signed saturation.
|
||||||
"packsswb.128" => {
|
"packsswb.128" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
packsswb(this, left, right, dest)?;
|
packsswb(this, left, right, dest)?;
|
||||||
}
|
}
|
||||||
@ -180,8 +176,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Converts two 16-bit signed integer vectors to a single 8-bit
|
// Converts two 16-bit signed integer vectors to a single 8-bit
|
||||||
// unsigned integer vector with saturation.
|
// unsigned integer vector with saturation.
|
||||||
"packuswb.128" => {
|
"packuswb.128" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
packuswb(this, left, right, dest)?;
|
packuswb(this, left, right, dest)?;
|
||||||
}
|
}
|
||||||
@ -189,8 +184,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Converts two 32-bit integer vectors to a single 16-bit integer
|
// Converts two 32-bit integer vectors to a single 16-bit integer
|
||||||
// vector with signed saturation.
|
// vector with signed saturation.
|
||||||
"packssdw.128" => {
|
"packssdw.128" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
packssdw(this, left, right, dest)?;
|
packssdw(this, left, right, dest)?;
|
||||||
}
|
}
|
||||||
@ -200,8 +194,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// matches the IEEE min/max operations, while x86 has different
|
// matches the IEEE min/max operations, while x86 has different
|
||||||
// semantics.
|
// semantics.
|
||||||
"min.sd" | "max.sd" => {
|
"min.sd" | "max.sd" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let which = match unprefixed_name {
|
let which = match unprefixed_name {
|
||||||
"min.sd" => FloatBinOp::Min,
|
"min.sd" => FloatBinOp::Min,
|
||||||
@ -217,8 +210,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// matches the IEEE min/max operations, while x86 has different
|
// matches the IEEE min/max operations, while x86 has different
|
||||||
// semantics.
|
// semantics.
|
||||||
"min.pd" | "max.pd" => {
|
"min.pd" | "max.pd" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let which = match unprefixed_name {
|
let which = match unprefixed_name {
|
||||||
"min.pd" => FloatBinOp::Min,
|
"min.pd" => FloatBinOp::Min,
|
||||||
@ -237,8 +229,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_sd are SSE2 functions
|
// _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_sd are SSE2 functions
|
||||||
// with hard-coded operations.
|
// with hard-coded operations.
|
||||||
"cmp.sd" => {
|
"cmp.sd" => {
|
||||||
let [left, right, imm] =
|
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let which =
|
let which =
|
||||||
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
|
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
|
||||||
@ -254,8 +245,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_pd are SSE2 functions
|
// _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_pd are SSE2 functions
|
||||||
// with hard-coded operations.
|
// with hard-coded operations.
|
||||||
"cmp.pd" => {
|
"cmp.pd" => {
|
||||||
let [left, right, imm] =
|
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let which =
|
let which =
|
||||||
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
|
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
|
||||||
@ -268,8 +258,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
"comieq.sd" | "comilt.sd" | "comile.sd" | "comigt.sd" | "comige.sd" | "comineq.sd"
|
"comieq.sd" | "comilt.sd" | "comile.sd" | "comigt.sd" | "comige.sd" | "comineq.sd"
|
||||||
| "ucomieq.sd" | "ucomilt.sd" | "ucomile.sd" | "ucomigt.sd" | "ucomige.sd"
|
| "ucomieq.sd" | "ucomilt.sd" | "ucomile.sd" | "ucomigt.sd" | "ucomige.sd"
|
||||||
| "ucomineq.sd" => {
|
| "ucomineq.sd" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (left, left_len) = this.project_to_simd(left)?;
|
let (left, left_len) = this.project_to_simd(left)?;
|
||||||
let (right, right_len) = this.project_to_simd(right)?;
|
let (right, right_len) = this.project_to_simd(right)?;
|
||||||
@ -323,8 +312,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Converts the first f64/f32 from `right` to f32/f64 and copies
|
// Converts the first f64/f32 from `right` to f32/f64 and copies
|
||||||
// the remaining elements from `left`
|
// the remaining elements from `left`
|
||||||
"cvtsd2ss" | "cvtss2sd" => {
|
"cvtsd2ss" | "cvtss2sd" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (left, left_len) = this.project_to_simd(left)?;
|
let (left, left_len) = this.project_to_simd(left)?;
|
||||||
let (right, _) = this.project_to_simd(right)?;
|
let (right, _) = this.project_to_simd(right)?;
|
||||||
|
@ -25,8 +25,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Horizontally add/subtract adjacent floating point values
|
// Horizontally add/subtract adjacent floating point values
|
||||||
// in `left` and `right`.
|
// in `left` and `right`.
|
||||||
"hadd.ps" | "hadd.pd" | "hsub.ps" | "hsub.pd" => {
|
"hadd.ps" | "hadd.pd" | "hsub.ps" | "hsub.pd" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let which = match unprefixed_name {
|
let which = match unprefixed_name {
|
||||||
"hadd.ps" | "hadd.pd" => mir::BinOp::Add,
|
"hadd.ps" | "hadd.pd" => mir::BinOp::Add,
|
||||||
@ -42,8 +41,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// the data crosses a cache line, but for Miri this is just a regular
|
// the data crosses a cache line, but for Miri this is just a regular
|
||||||
// unaligned read.
|
// unaligned read.
|
||||||
"ldu.dq" => {
|
"ldu.dq" => {
|
||||||
let [src_ptr] =
|
let [src_ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let src_ptr = this.read_pointer(src_ptr)?;
|
let src_ptr = this.read_pointer(src_ptr)?;
|
||||||
let dest = dest.force_mplace(this)?;
|
let dest = dest.force_mplace(this)?;
|
||||||
|
|
||||||
|
@ -27,8 +27,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// bits `4..=5` if `imm`, and `i`th bit specifies whether element
|
// bits `4..=5` if `imm`, and `i`th bit specifies whether element
|
||||||
// `i` is zeroed.
|
// `i` is zeroed.
|
||||||
"insertps" => {
|
"insertps" => {
|
||||||
let [left, right, imm] =
|
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (left, left_len) = this.project_to_simd(left)?;
|
let (left, left_len) = this.project_to_simd(left)?;
|
||||||
let (right, right_len) = this.project_to_simd(right)?;
|
let (right, right_len) = this.project_to_simd(right)?;
|
||||||
@ -63,8 +62,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Concatenates two 32-bit signed integer vectors and converts
|
// Concatenates two 32-bit signed integer vectors and converts
|
||||||
// the result to a 16-bit unsigned integer vector with saturation.
|
// the result to a 16-bit unsigned integer vector with saturation.
|
||||||
"packusdw" => {
|
"packusdw" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
packusdw(this, left, right, dest)?;
|
packusdw(this, left, right, dest)?;
|
||||||
}
|
}
|
||||||
@ -74,8 +72,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// products, and conditionally stores the sum in `dest` using the low
|
// products, and conditionally stores the sum in `dest` using the low
|
||||||
// 4 bits of `imm`.
|
// 4 bits of `imm`.
|
||||||
"dpps" | "dppd" => {
|
"dpps" | "dppd" => {
|
||||||
let [left, right, imm] =
|
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
conditional_dot_product(this, left, right, imm, dest)?;
|
conditional_dot_product(this, left, right, imm, dest)?;
|
||||||
}
|
}
|
||||||
@ -83,16 +80,14 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// functions. Rounds the first element of `right` according to `rounding`
|
// functions. Rounds the first element of `right` according to `rounding`
|
||||||
// and copies the remaining elements from `left`.
|
// and copies the remaining elements from `left`.
|
||||||
"round.ss" => {
|
"round.ss" => {
|
||||||
let [left, right, rounding] =
|
let [left, right, rounding] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
round_first::<rustc_apfloat::ieee::Single>(this, left, right, rounding, dest)?;
|
round_first::<rustc_apfloat::ieee::Single>(this, left, right, rounding, dest)?;
|
||||||
}
|
}
|
||||||
// Used to implement the _mm_floor_ps, _mm_ceil_ps and _mm_round_ps
|
// Used to implement the _mm_floor_ps, _mm_ceil_ps and _mm_round_ps
|
||||||
// functions. Rounds the elements of `op` according to `rounding`.
|
// functions. Rounds the elements of `op` according to `rounding`.
|
||||||
"round.ps" => {
|
"round.ps" => {
|
||||||
let [op, rounding] =
|
let [op, rounding] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
round_all::<rustc_apfloat::ieee::Single>(this, op, rounding, dest)?;
|
round_all::<rustc_apfloat::ieee::Single>(this, op, rounding, dest)?;
|
||||||
}
|
}
|
||||||
@ -100,16 +95,14 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// functions. Rounds the first element of `right` according to `rounding`
|
// functions. Rounds the first element of `right` according to `rounding`
|
||||||
// and copies the remaining elements from `left`.
|
// and copies the remaining elements from `left`.
|
||||||
"round.sd" => {
|
"round.sd" => {
|
||||||
let [left, right, rounding] =
|
let [left, right, rounding] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
round_first::<rustc_apfloat::ieee::Double>(this, left, right, rounding, dest)?;
|
round_first::<rustc_apfloat::ieee::Double>(this, left, right, rounding, dest)?;
|
||||||
}
|
}
|
||||||
// Used to implement the _mm_floor_pd, _mm_ceil_pd and _mm_round_pd
|
// Used to implement the _mm_floor_pd, _mm_ceil_pd and _mm_round_pd
|
||||||
// functions. Rounds the elements of `op` according to `rounding`.
|
// functions. Rounds the elements of `op` according to `rounding`.
|
||||||
"round.pd" => {
|
"round.pd" => {
|
||||||
let [op, rounding] =
|
let [op, rounding] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
round_all::<rustc_apfloat::ieee::Double>(this, op, rounding, dest)?;
|
round_all::<rustc_apfloat::ieee::Double>(this, op, rounding, dest)?;
|
||||||
}
|
}
|
||||||
@ -151,8 +144,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// offsets specified in `imm`.
|
// offsets specified in `imm`.
|
||||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mpsadbw_epu8
|
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mpsadbw_epu8
|
||||||
"mpsadbw" => {
|
"mpsadbw" => {
|
||||||
let [left, right, imm] =
|
let [left, right, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
mpsadbw(this, left, right, imm, dest)?;
|
mpsadbw(this, left, right, imm, dest)?;
|
||||||
}
|
}
|
||||||
@ -161,8 +153,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Tests `(op & mask) == 0`, `(op & mask) == mask` or
|
// Tests `(op & mask) == 0`, `(op & mask) == mask` or
|
||||||
// `(op & mask) != 0 && (op & mask) != mask`
|
// `(op & mask) != 0 && (op & mask) != mask`
|
||||||
"ptestz" | "ptestc" | "ptestnzc" => {
|
"ptestz" | "ptestc" | "ptestnzc" => {
|
||||||
let [op, mask] =
|
let [op, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (all_zero, masked_set) = test_bits_masked(this, op, mask)?;
|
let (all_zero, masked_set) = test_bits_masked(this, op, mask)?;
|
||||||
let res = match unprefixed_name {
|
let res = match unprefixed_name {
|
||||||
|
@ -223,8 +223,7 @@ fn deconstruct_args<'tcx>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if is_explicit {
|
if is_explicit {
|
||||||
let [str1, len1, str2, len2, imm] =
|
let [str1, len1, str2, len2, imm] = ecx.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
ecx.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let imm = ecx.read_scalar(imm)?.to_u8()?;
|
let imm = ecx.read_scalar(imm)?.to_u8()?;
|
||||||
|
|
||||||
let default_len = default_len::<u32>(imm);
|
let default_len = default_len::<u32>(imm);
|
||||||
@ -237,8 +236,7 @@ fn deconstruct_args<'tcx>(
|
|||||||
|
|
||||||
interp_ok((str1, str2, Some((len1, len2)), imm))
|
interp_ok((str1, str2, Some((len1, len2)), imm))
|
||||||
} else {
|
} else {
|
||||||
let [str1, str2, imm] =
|
let [str1, str2, imm] = ecx.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
ecx.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let imm = ecx.read_scalar(imm)?.to_u8()?;
|
let imm = ecx.read_scalar(imm)?.to_u8()?;
|
||||||
|
|
||||||
let array_layout = array_layout_fn(ecx, imm)?;
|
let array_layout = array_layout_fn(ecx, imm)?;
|
||||||
@ -388,8 +386,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// search for a null terminator (see `deconstruct_args` for more details).
|
// search for a null terminator (see `deconstruct_args` for more details).
|
||||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=924,925
|
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=924,925
|
||||||
"pcmpistriz128" | "pcmpistris128" => {
|
"pcmpistriz128" | "pcmpistris128" => {
|
||||||
let [str1, str2, imm] =
|
let [str1, str2, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let imm = this.read_scalar(imm)?.to_u8()?;
|
let imm = this.read_scalar(imm)?.to_u8()?;
|
||||||
|
|
||||||
let str = if unprefixed_name == "pcmpistris128" { str1 } else { str2 };
|
let str = if unprefixed_name == "pcmpistris128" { str1 } else { str2 };
|
||||||
@ -409,8 +406,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// than 16 for byte-sized operands or 8 for word-sized operands.
|
// than 16 for byte-sized operands or 8 for word-sized operands.
|
||||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=1046,1047
|
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=1046,1047
|
||||||
"pcmpestriz128" | "pcmpestris128" => {
|
"pcmpestriz128" | "pcmpestris128" => {
|
||||||
let [_, len1, _, len2, imm] =
|
let [_, len1, _, len2, imm] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let len = if unprefixed_name == "pcmpestris128" { len1 } else { len2 };
|
let len = if unprefixed_name == "pcmpestris128" { len1 } else { len2 };
|
||||||
let len = this.read_scalar(len)?.to_i32()?;
|
let len = this.read_scalar(len)?.to_i32()?;
|
||||||
let imm = this.read_scalar(imm)?.to_u8()?;
|
let imm = this.read_scalar(imm)?.to_u8()?;
|
||||||
@ -437,8 +433,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
return interp_ok(EmulateItemResult::NotSupported);
|
return interp_ok(EmulateItemResult::NotSupported);
|
||||||
}
|
}
|
||||||
|
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
let left = this.read_scalar(left)?;
|
let left = this.read_scalar(left)?;
|
||||||
let right = this.read_scalar(right)?;
|
let right = this.read_scalar(right)?;
|
||||||
|
|
||||||
|
@ -32,8 +32,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// Shuffles bytes from `left` using `right` as pattern.
|
// Shuffles bytes from `left` using `right` as pattern.
|
||||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_epi8
|
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_epi8
|
||||||
"pshuf.b.128" => {
|
"pshuf.b.128" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (left, left_len) = this.project_to_simd(left)?;
|
let (left, left_len) = this.project_to_simd(left)?;
|
||||||
let (right, right_len) = this.project_to_simd(right)?;
|
let (right, right_len) = this.project_to_simd(right)?;
|
||||||
@ -62,8 +61,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// integer values in `left` and `right`.
|
// integer values in `left` and `right`.
|
||||||
"phadd.w.128" | "phadd.sw.128" | "phadd.d.128" | "phsub.w.128" | "phsub.sw.128"
|
"phadd.w.128" | "phadd.sw.128" | "phadd.d.128" | "phsub.w.128" | "phsub.sw.128"
|
||||||
| "phsub.d.128" => {
|
| "phsub.d.128" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (which, saturating) = match unprefixed_name {
|
let (which, saturating) = match unprefixed_name {
|
||||||
"phadd.w.128" | "phadd.d.128" => (mir::BinOp::Add, false),
|
"phadd.w.128" | "phadd.d.128" => (mir::BinOp::Add, false),
|
||||||
@ -82,8 +80,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// produces the output at index `i`.
|
// produces the output at index `i`.
|
||||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_maddubs_epi16
|
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_maddubs_epi16
|
||||||
"pmadd.ub.sw.128" => {
|
"pmadd.ub.sw.128" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
let (left, left_len) = this.project_to_simd(left)?;
|
let (left, left_len) = this.project_to_simd(left)?;
|
||||||
let (right, right_len) = this.project_to_simd(right)?;
|
let (right, right_len) = this.project_to_simd(right)?;
|
||||||
@ -118,8 +115,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// 1 and then taking the bits `1..=16`.
|
// 1 and then taking the bits `1..=16`.
|
||||||
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhrs_epi16
|
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhrs_epi16
|
||||||
"pmul.hr.sw.128" => {
|
"pmul.hr.sw.128" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
pmulhrsw(this, left, right, dest)?;
|
pmulhrsw(this, left, right, dest)?;
|
||||||
}
|
}
|
||||||
@ -129,8 +125,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||||||
// is writen to the corresponding output element.
|
// is writen to the corresponding output element.
|
||||||
// Basically, we multiply `left` with `right.signum()`.
|
// Basically, we multiply `left` with `right.signum()`.
|
||||||
"psign.b.128" | "psign.w.128" | "psign.d.128" => {
|
"psign.b.128" | "psign.w.128" | "psign.d.128" => {
|
||||||
let [left, right] =
|
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
|
||||||
|
|
||||||
psign(this, left, right, dest)?;
|
psign(this, left, right, dest)?;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
//@ignore-target: windows # No libc socketpair on Windows
|
||||||
|
//~^ERROR: deadlocked
|
||||||
|
//~^^ERROR: deadlocked
|
||||||
|
// test_race depends on a deterministic schedule.
|
||||||
|
//@compile-flags: -Zmiri-preemption-rate=0
|
||||||
|
//@error-in-other-file: deadlock
|
||||||
|
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
// Test the behaviour of a thread being blocked on read, get unblocked, then blocked again.
|
||||||
|
|
||||||
|
// The expected execution is
|
||||||
|
// 1. Thread 1 blocks.
|
||||||
|
// 2. Thread 2 blocks.
|
||||||
|
// 3. Thread 3 unblocks both thread 1 and thread 2.
|
||||||
|
// 4. Thread 1 reads.
|
||||||
|
// 5. Thread 2's `read` can never complete -> deadlocked.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut fds = [-1, -1];
|
||||||
|
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
||||||
|
assert_eq!(res, 0);
|
||||||
|
let thread1 = thread::spawn(move || {
|
||||||
|
// Let this thread block on read.
|
||||||
|
let mut buf: [u8; 3] = [0; 3];
|
||||||
|
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||||
|
assert_eq!(res, 3);
|
||||||
|
assert_eq!(&buf, "abc".as_bytes());
|
||||||
|
});
|
||||||
|
let thread2 = thread::spawn(move || {
|
||||||
|
// Let this thread block on read.
|
||||||
|
let mut buf: [u8; 3] = [0; 3];
|
||||||
|
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||||
|
//~^ERROR: deadlocked
|
||||||
|
assert_eq!(res, 3);
|
||||||
|
assert_eq!(&buf, "abc".as_bytes());
|
||||||
|
});
|
||||||
|
let thread3 = thread::spawn(move || {
|
||||||
|
// Unblock thread1 by writing something.
|
||||||
|
let data = "abc".as_bytes().as_ptr();
|
||||||
|
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
|
||||||
|
assert_eq!(res, 3);
|
||||||
|
});
|
||||||
|
thread1.join().unwrap();
|
||||||
|
thread2.join().unwrap();
|
||||||
|
thread3.join().unwrap();
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
error: deadlock: the evaluated program deadlocked
|
||||||
|
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
||||||
|
| ^ the evaluated program deadlocked
|
||||||
|
|
|
||||||
|
= note: BACKTRACE:
|
||||||
|
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
||||||
|
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
|
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
|
note: inside `main`
|
||||||
|
--> tests/fail-dep/libc/socketpair_block_read_twice.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | thread2.join().unwrap();
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: deadlock: the evaluated program deadlocked
|
||||||
|
|
|
||||||
|
= note: the evaluated program deadlocked
|
||||||
|
= note: (no span available)
|
||||||
|
= note: BACKTRACE on thread `unnamed-ID`:
|
||||||
|
|
||||||
|
error: deadlock: the evaluated program deadlocked
|
||||||
|
--> tests/fail-dep/libc/socketpair_block_read_twice.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||||
|
| ^ the evaluated program deadlocked
|
||||||
|
|
|
||||||
|
= note: BACKTRACE on thread `unnamed-ID`:
|
||||||
|
= note: inside closure at tests/fail-dep/libc/socketpair_block_read_twice.rs:LL:CC
|
||||||
|
|
||||||
|
error: deadlock: the evaluated program deadlocked
|
||||||
|
|
|
||||||
|
= note: the evaluated program deadlocked
|
||||||
|
= note: (no span available)
|
||||||
|
= note: BACKTRACE on thread `unnamed-ID`:
|
||||||
|
|
||||||
|
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
@ -0,0 +1,49 @@
|
|||||||
|
//@ignore-target: windows # No libc socketpair on Windows
|
||||||
|
//~^ERROR: deadlocked
|
||||||
|
//~^^ERROR: deadlocked
|
||||||
|
// test_race depends on a deterministic schedule.
|
||||||
|
//@compile-flags: -Zmiri-preemption-rate=0
|
||||||
|
//@error-in-other-file: deadlock
|
||||||
|
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
// Test the behaviour of a thread being blocked on write, get unblocked, then blocked again.
|
||||||
|
|
||||||
|
// The expected execution is
|
||||||
|
// 1. Thread 1 blocks.
|
||||||
|
// 2. Thread 2 blocks.
|
||||||
|
// 3. Thread 3 unblocks both thread 1 and thread 2.
|
||||||
|
// 4. Thread 1 reads.
|
||||||
|
// 5. Thread 2's `write` can never complete -> deadlocked.
|
||||||
|
fn main() {
|
||||||
|
let mut fds = [-1, -1];
|
||||||
|
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
||||||
|
assert_eq!(res, 0);
|
||||||
|
let arr1: [u8; 212992] = [1; 212992];
|
||||||
|
// Exhaust the space in the buffer so the subsequent write will block.
|
||||||
|
let res = unsafe { libc::write(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) };
|
||||||
|
assert_eq!(res, 212992);
|
||||||
|
let thread1 = thread::spawn(move || {
|
||||||
|
let data = "abc".as_bytes().as_ptr();
|
||||||
|
// The write below will be blocked because the buffer is already full.
|
||||||
|
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
|
||||||
|
assert_eq!(res, 3);
|
||||||
|
});
|
||||||
|
let thread2 = thread::spawn(move || {
|
||||||
|
let data = "abc".as_bytes().as_ptr();
|
||||||
|
// The write below will be blocked because the buffer is already full.
|
||||||
|
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
|
||||||
|
//~^ERROR: deadlocked
|
||||||
|
assert_eq!(res, 3);
|
||||||
|
});
|
||||||
|
let thread3 = thread::spawn(move || {
|
||||||
|
// Unblock thread1 by freeing up some space.
|
||||||
|
let mut buf: [u8; 3] = [0; 3];
|
||||||
|
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||||
|
assert_eq!(res, 3);
|
||||||
|
assert_eq!(buf, [1, 1, 1]);
|
||||||
|
});
|
||||||
|
thread1.join().unwrap();
|
||||||
|
thread2.join().unwrap();
|
||||||
|
thread3.join().unwrap();
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
error: deadlock: the evaluated program deadlocked
|
||||||
|
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
|
||||||
|
| ^ the evaluated program deadlocked
|
||||||
|
|
|
||||||
|
= note: BACKTRACE:
|
||||||
|
= note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
|
||||||
|
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
|
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||||
|
note: inside `main`
|
||||||
|
--> tests/fail-dep/libc/socketpair_block_write_twice.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | thread2.join().unwrap();
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: deadlock: the evaluated program deadlocked
|
||||||
|
|
|
||||||
|
= note: the evaluated program deadlocked
|
||||||
|
= note: (no span available)
|
||||||
|
= note: BACKTRACE on thread `unnamed-ID`:
|
||||||
|
|
||||||
|
error: deadlock: the evaluated program deadlocked
|
||||||
|
--> tests/fail-dep/libc/socketpair_block_write_twice.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
|
||||||
|
| ^ the evaluated program deadlocked
|
||||||
|
|
|
||||||
|
= note: BACKTRACE on thread `unnamed-ID`:
|
||||||
|
= note: inside closure at tests/fail-dep/libc/socketpair_block_write_twice.rs:LL:CC
|
||||||
|
|
||||||
|
error: deadlock: the evaluated program deadlocked
|
||||||
|
|
|
||||||
|
= note: the evaluated program deadlocked
|
||||||
|
= note: (no span available)
|
||||||
|
= note: BACKTRACE on thread `unnamed-ID`:
|
||||||
|
|
||||||
|
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
@ -1,12 +0,0 @@
|
|||||||
//@ignore-target: windows # no libc socketpair on Windows
|
|
||||||
|
|
||||||
// This is temporarily here because blocking on fd is not supported yet.
|
|
||||||
// When blocking is eventually supported, this will be moved to pass-dep/libc/libc-socketpair
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut fds = [-1, -1];
|
|
||||||
let _ = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
|
||||||
// The read below will be blocked because the buffer is empty.
|
|
||||||
let mut buf: [u8; 3] = [0; 3];
|
|
||||||
let _res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; //~ERROR: blocking isn't supported
|
|
||||||
}
|
|
@ -1,10 +1,9 @@
|
|||||||
error: unsupported operation: socketpair/pipe/pipe2 read: blocking isn't supported yet
|
error: deadlock: the evaluated program deadlocked
|
||||||
--> tests/fail-dep/libc/socketpair_read_blocking.rs:LL:CC
|
--> tests/fail-dep/libc/socketpair_read_blocking.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | let _res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
LL | let _res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ socketpair/pipe/pipe2 read: blocking isn't supported yet
|
| ^ the evaluated program deadlocked
|
||||||
|
|
|
|
||||||
= help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
|
|
||||||
= note: BACKTRACE:
|
= note: BACKTRACE:
|
||||||
= note: inside `main` at tests/fail-dep/libc/socketpair_read_blocking.rs:LL:CC
|
= note: inside `main` at tests/fail-dep/libc/socketpair_read_blocking.rs:LL:CC
|
||||||
|
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
//@ignore-target: windows # no libc socketpair on Windows
|
|
||||||
// This is temporarily here because blocking on fd is not supported yet.
|
|
||||||
// When blocking is eventually supported, this will be moved to pass-dep/libc/libc-socketpair
|
|
||||||
fn main() {
|
|
||||||
let mut fds = [-1, -1];
|
|
||||||
let _ = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
|
||||||
// Write size > buffer capacity
|
|
||||||
// Used up all the space in the buffer.
|
|
||||||
let arr1: [u8; 212992] = [1; 212992];
|
|
||||||
let _ = unsafe { libc::write(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) };
|
|
||||||
let data = "abc".as_bytes().as_ptr();
|
|
||||||
// The write below will be blocked as the buffer is full.
|
|
||||||
let _ = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; //~ERROR: blocking isn't supported
|
|
||||||
let mut buf: [u8; 3] = [0; 3];
|
|
||||||
let _res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
|
||||||
}
|
|
@ -1,10 +1,9 @@
|
|||||||
error: unsupported operation: socketpair/pipe/pipe2 write: blocking isn't supported yet
|
error: deadlock: the evaluated program deadlocked
|
||||||
--> tests/fail-dep/libc/socketpair_write_blocking.rs:LL:CC
|
--> tests/fail-dep/libc/socketpair_write_blocking.rs:LL:CC
|
||||||
|
|
|
|
||||||
LL | let _ = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
|
LL | let _ = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ socketpair/pipe/pipe2 write: blocking isn't supported yet
|
| ^ the evaluated program deadlocked
|
||||||
|
|
|
|
||||||
= help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
|
|
||||||
= note: BACKTRACE:
|
= note: BACKTRACE:
|
||||||
= note: inside `main` at tests/fail-dep/libc/socketpair_write_blocking.rs:LL:CC
|
= note: inside `main` at tests/fail-dep/libc/socketpair_write_blocking.rs:LL:CC
|
||||||
|
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
//@compile-flags: -Zmiri-tree-borrows -Zmiri-provenance-gc=0
|
||||||
|
|
||||||
|
// Shows the effect of the optimization of #4008.
|
||||||
|
// The diagnostics change, but not the error itself.
|
||||||
|
|
||||||
|
// When this method is called, the tree will be a single line and look like this,
|
||||||
|
// with other_ptr being the root at the top
|
||||||
|
// other_ptr = root : Active
|
||||||
|
// intermediary : Frozen // an intermediary node
|
||||||
|
// m : Reserved
|
||||||
|
fn write_to_mut(m: &mut u8, other_ptr: *const u8) {
|
||||||
|
unsafe {
|
||||||
|
std::hint::black_box(*other_ptr);
|
||||||
|
}
|
||||||
|
// In line 17 above, m should have become Reserved (protected) so that this write is impossible.
|
||||||
|
// However, that does not happen because the read above is not forwarded to the subtree below
|
||||||
|
// the Frozen intermediary node. This does not affect UB, however, because the Frozen that blocked
|
||||||
|
// the read already prevents any child writes.
|
||||||
|
*m = 42; //~ERROR: /write access through .* is forbidden/
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let root = 42u8;
|
||||||
|
unsafe {
|
||||||
|
let intermediary = &root;
|
||||||
|
let data = &mut *(core::ptr::addr_of!(*intermediary) as *mut u8);
|
||||||
|
write_to_mut(data, core::ptr::addr_of!(root));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
error: Undefined Behavior: write access through <TAG> at ALLOC[0x0] is forbidden
|
||||||
|
--> tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | *m = 42;
|
||||||
|
| ^^^^^^^ write access through <TAG> at ALLOC[0x0] is forbidden
|
||||||
|
|
|
||||||
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||||
|
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
|
||||||
|
= help: the conflicting tag <TAG> has state Frozen which forbids this child write access
|
||||||
|
help: the accessed tag <TAG> was created here
|
||||||
|
--> tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | fn write_to_mut(m: &mut u8, other_ptr: *const u8) {
|
||||||
|
| ^
|
||||||
|
help: the conflicting tag <TAG> was created here, in the initial state Frozen
|
||||||
|
--> tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | let intermediary = &root;
|
||||||
|
| ^^^^^
|
||||||
|
= note: BACKTRACE (of the first span):
|
||||||
|
= note: inside `write_to_mut` at tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs:LL:CC
|
||||||
|
note: inside `main`
|
||||||
|
--> tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | write_to_mut(data, core::ptr::addr_of!(root));
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
18
src/tools/miri/tests/native-lib/pass/ptr_read_access.stderr
Normal file
18
src/tools/miri/tests/native-lib/pass/ptr_read_access.stderr
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
warning: sharing memory with a native function
|
||||||
|
--> tests/native-lib/pass/ptr_read_access.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | unsafe { print_pointer(&x) };
|
||||||
|
| ^^^^^^^^^^^^^^^^^ sharing memory with a native function called via FFI
|
||||||
|
|
|
||||||
|
= help: when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory
|
||||||
|
= help: in particular, Miri assumes that the native call initializes all memory it has access to
|
||||||
|
= help: Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory
|
||||||
|
= help: what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free
|
||||||
|
= note: BACKTRACE:
|
||||||
|
= note: inside `test_access_pointer` at tests/native-lib/pass/ptr_read_access.rs:LL:CC
|
||||||
|
note: inside `main`
|
||||||
|
--> tests/native-lib/pass/ptr_read_access.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | test_access_pointer();
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
18
src/tools/miri/tests/native-lib/pass/ptr_write_access.stderr
Normal file
18
src/tools/miri/tests/native-lib/pass/ptr_write_access.stderr
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
warning: sharing memory with a native function
|
||||||
|
--> tests/native-lib/pass/ptr_write_access.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | unsafe { increment_int(&mut x) };
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^ sharing memory with a native function called via FFI
|
||||||
|
|
|
||||||
|
= help: when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory
|
||||||
|
= help: in particular, Miri assumes that the native call initializes all memory it has access to
|
||||||
|
= help: Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory
|
||||||
|
= help: what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free
|
||||||
|
= note: BACKTRACE:
|
||||||
|
= note: inside `test_increment_int` at tests/native-lib/pass/ptr_write_access.rs:LL:CC
|
||||||
|
note: inside `main`
|
||||||
|
--> tests/native-lib/pass/ptr_write_access.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | test_increment_int();
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -10,6 +10,8 @@ fn main() {
|
|||||||
test_socketpair();
|
test_socketpair();
|
||||||
test_socketpair_threaded();
|
test_socketpair_threaded();
|
||||||
test_race();
|
test_race();
|
||||||
|
test_blocking_read();
|
||||||
|
test_blocking_write();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_socketpair() {
|
fn test_socketpair() {
|
||||||
@ -136,3 +138,51 @@ fn test_race() {
|
|||||||
thread::yield_now();
|
thread::yield_now();
|
||||||
thread1.join().unwrap();
|
thread1.join().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test the behaviour of a socketpair getting blocked on read and subsequently unblocked.
|
||||||
|
fn test_blocking_read() {
|
||||||
|
let mut fds = [-1, -1];
|
||||||
|
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
||||||
|
assert_eq!(res, 0);
|
||||||
|
let thread1 = thread::spawn(move || {
|
||||||
|
// Let this thread block on read.
|
||||||
|
let mut buf: [u8; 3] = [0; 3];
|
||||||
|
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||||
|
assert_eq!(res, 3);
|
||||||
|
assert_eq!(&buf, "abc".as_bytes());
|
||||||
|
});
|
||||||
|
let thread2 = thread::spawn(move || {
|
||||||
|
// Unblock thread1 by doing writing something.
|
||||||
|
let data = "abc".as_bytes().as_ptr();
|
||||||
|
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
|
||||||
|
assert_eq!(res, 3);
|
||||||
|
});
|
||||||
|
thread1.join().unwrap();
|
||||||
|
thread2.join().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the behaviour of a socketpair getting blocked on write and subsequently unblocked.
|
||||||
|
fn test_blocking_write() {
|
||||||
|
let mut fds = [-1, -1];
|
||||||
|
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
||||||
|
assert_eq!(res, 0);
|
||||||
|
let arr1: [u8; 212992] = [1; 212992];
|
||||||
|
// Exhaust the space in the buffer so the subsequent write will block.
|
||||||
|
let res = unsafe { libc::write(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) };
|
||||||
|
assert_eq!(res, 212992);
|
||||||
|
let thread1 = thread::spawn(move || {
|
||||||
|
let data = "abc".as_bytes().as_ptr();
|
||||||
|
// The write below will be blocked because the buffer is already full.
|
||||||
|
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) };
|
||||||
|
assert_eq!(res, 3);
|
||||||
|
});
|
||||||
|
let thread2 = thread::spawn(move || {
|
||||||
|
// Unblock thread1 by freeing up some space.
|
||||||
|
let mut buf: [u8; 3] = [0; 3];
|
||||||
|
let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||||
|
assert_eq!(res, 3);
|
||||||
|
assert_eq!(buf, [1, 1, 1]);
|
||||||
|
});
|
||||||
|
thread1.join().unwrap();
|
||||||
|
thread2.join().unwrap();
|
||||||
|
}
|
||||||
|
@ -5,7 +5,21 @@ use std::{env, mem, ptr};
|
|||||||
fn main() {
|
fn main() {
|
||||||
test_clocks();
|
test_clocks();
|
||||||
test_posix_gettimeofday();
|
test_posix_gettimeofday();
|
||||||
test_localtime_r();
|
test_localtime_r_gmt();
|
||||||
|
test_localtime_r_pst();
|
||||||
|
test_localtime_r_epoch();
|
||||||
|
#[cfg(any(
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "android"
|
||||||
|
))]
|
||||||
|
test_localtime_r_multiple_calls_deduplication();
|
||||||
|
// Architecture-specific tests.
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
test_localtime_r_future_32b();
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
test_localtime_r_future_64b();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tests whether clock support exists at all
|
/// Tests whether clock support exists at all
|
||||||
@ -46,14 +60,9 @@ fn test_posix_gettimeofday() {
|
|||||||
assert_eq!(is_error, -1);
|
assert_eq!(is_error, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_localtime_r() {
|
/// Helper function to create an empty tm struct.
|
||||||
// Set timezone to GMT.
|
fn create_empty_tm() -> libc::tm {
|
||||||
let key = "TZ";
|
libc::tm {
|
||||||
env::set_var(key, "GMT");
|
|
||||||
|
|
||||||
const TIME_SINCE_EPOCH: libc::time_t = 1712475836;
|
|
||||||
let custom_time_ptr = &TIME_SINCE_EPOCH;
|
|
||||||
let mut tm = libc::tm {
|
|
||||||
tm_sec: 0,
|
tm_sec: 0,
|
||||||
tm_min: 0,
|
tm_min: 0,
|
||||||
tm_hour: 0,
|
tm_hour: 0,
|
||||||
@ -77,7 +86,17 @@ fn test_localtime_r() {
|
|||||||
target_os = "android"
|
target_os = "android"
|
||||||
))]
|
))]
|
||||||
tm_zone: std::ptr::null_mut::<libc::c_char>(),
|
tm_zone: std::ptr::null_mut::<libc::c_char>(),
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Original GMT test
|
||||||
|
fn test_localtime_r_gmt() {
|
||||||
|
// Set timezone to GMT.
|
||||||
|
let key = "TZ";
|
||||||
|
env::set_var(key, "GMT");
|
||||||
|
const TIME_SINCE_EPOCH: libc::time_t = 1712475836; // 2024-04-07 07:43:56 GMT
|
||||||
|
let custom_time_ptr = &TIME_SINCE_EPOCH;
|
||||||
|
let mut tm = create_empty_tm();
|
||||||
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
|
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
|
||||||
|
|
||||||
assert_eq!(tm.tm_sec, 56);
|
assert_eq!(tm.tm_sec, 56);
|
||||||
@ -95,16 +114,12 @@ fn test_localtime_r() {
|
|||||||
target_os = "freebsd",
|
target_os = "freebsd",
|
||||||
target_os = "android"
|
target_os = "android"
|
||||||
))]
|
))]
|
||||||
|
{
|
||||||
assert_eq!(tm.tm_gmtoff, 0);
|
assert_eq!(tm.tm_gmtoff, 0);
|
||||||
#[cfg(any(
|
|
||||||
target_os = "linux",
|
|
||||||
target_os = "macos",
|
|
||||||
target_os = "freebsd",
|
|
||||||
target_os = "android"
|
|
||||||
))]
|
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00")
|
assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00");
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The returned value is the pointer passed in.
|
// The returned value is the pointer passed in.
|
||||||
assert!(ptr::eq(res, &mut tm));
|
assert!(ptr::eq(res, &mut tm));
|
||||||
@ -112,3 +127,191 @@ fn test_localtime_r() {
|
|||||||
// Remove timezone setting.
|
// Remove timezone setting.
|
||||||
env::remove_var(key);
|
env::remove_var(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// PST timezone test (testing different timezone handling).
|
||||||
|
fn test_localtime_r_pst() {
|
||||||
|
let key = "TZ";
|
||||||
|
env::set_var(key, "PST8PDT");
|
||||||
|
const TIME_SINCE_EPOCH: libc::time_t = 1712475836; // 2024-04-07 07:43:56 GMT
|
||||||
|
let custom_time_ptr = &TIME_SINCE_EPOCH;
|
||||||
|
let mut tm = create_empty_tm();
|
||||||
|
|
||||||
|
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
|
||||||
|
|
||||||
|
assert_eq!(tm.tm_sec, 56);
|
||||||
|
assert_eq!(tm.tm_min, 43);
|
||||||
|
assert_eq!(tm.tm_hour, 0); // 7 - 7 = 0 (PDT offset)
|
||||||
|
assert_eq!(tm.tm_mday, 7);
|
||||||
|
assert_eq!(tm.tm_mon, 3);
|
||||||
|
assert_eq!(tm.tm_year, 124);
|
||||||
|
assert_eq!(tm.tm_wday, 0);
|
||||||
|
assert_eq!(tm.tm_yday, 97);
|
||||||
|
assert_eq!(tm.tm_isdst, -1); // DST information unavailable
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "android"
|
||||||
|
))]
|
||||||
|
{
|
||||||
|
assert_eq!(tm.tm_gmtoff, -7 * 3600); // -7 hours in seconds
|
||||||
|
unsafe {
|
||||||
|
assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "-07");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(ptr::eq(res, &mut tm));
|
||||||
|
env::remove_var(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unix epoch test (edge case testing).
|
||||||
|
fn test_localtime_r_epoch() {
|
||||||
|
let key = "TZ";
|
||||||
|
env::set_var(key, "GMT");
|
||||||
|
const TIME_SINCE_EPOCH: libc::time_t = 0; // 1970-01-01 00:00:00
|
||||||
|
let custom_time_ptr = &TIME_SINCE_EPOCH;
|
||||||
|
let mut tm = create_empty_tm();
|
||||||
|
|
||||||
|
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
|
||||||
|
|
||||||
|
assert_eq!(tm.tm_sec, 0);
|
||||||
|
assert_eq!(tm.tm_min, 0);
|
||||||
|
assert_eq!(tm.tm_hour, 0);
|
||||||
|
assert_eq!(tm.tm_mday, 1);
|
||||||
|
assert_eq!(tm.tm_mon, 0);
|
||||||
|
assert_eq!(tm.tm_year, 70);
|
||||||
|
assert_eq!(tm.tm_wday, 4); // Thursday
|
||||||
|
assert_eq!(tm.tm_yday, 0);
|
||||||
|
assert_eq!(tm.tm_isdst, -1);
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "android"
|
||||||
|
))]
|
||||||
|
{
|
||||||
|
assert_eq!(tm.tm_gmtoff, 0);
|
||||||
|
unsafe {
|
||||||
|
assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(ptr::eq(res, &mut tm));
|
||||||
|
env::remove_var(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Future date test (testing large values).
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
fn test_localtime_r_future_64b() {
|
||||||
|
let key = "TZ";
|
||||||
|
env::set_var(key, "GMT");
|
||||||
|
|
||||||
|
// Using 2050-01-01 00:00:00 for 64-bit systems
|
||||||
|
// value that's safe for 64-bit time_t
|
||||||
|
const TIME_SINCE_EPOCH: libc::time_t = 2524608000;
|
||||||
|
let custom_time_ptr = &TIME_SINCE_EPOCH;
|
||||||
|
let mut tm = create_empty_tm();
|
||||||
|
|
||||||
|
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
|
||||||
|
|
||||||
|
assert_eq!(tm.tm_sec, 0);
|
||||||
|
assert_eq!(tm.tm_min, 0);
|
||||||
|
assert_eq!(tm.tm_hour, 0);
|
||||||
|
assert_eq!(tm.tm_mday, 1);
|
||||||
|
assert_eq!(tm.tm_mon, 0);
|
||||||
|
assert_eq!(tm.tm_year, 150); // 2050 - 1900
|
||||||
|
assert_eq!(tm.tm_wday, 6); // Saturday
|
||||||
|
assert_eq!(tm.tm_yday, 0);
|
||||||
|
assert_eq!(tm.tm_isdst, -1);
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "android"
|
||||||
|
))]
|
||||||
|
{
|
||||||
|
assert_eq!(tm.tm_gmtoff, 0);
|
||||||
|
unsafe {
|
||||||
|
assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(ptr::eq(res, &mut tm));
|
||||||
|
env::remove_var(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Future date test (testing large values for 32b target).
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
fn test_localtime_r_future_32b() {
|
||||||
|
let key = "TZ";
|
||||||
|
env::set_var(key, "GMT");
|
||||||
|
|
||||||
|
// Using 2030-01-01 00:00:00 for 32-bit systems
|
||||||
|
// Safe value within i32 range
|
||||||
|
const TIME_SINCE_EPOCH: libc::time_t = 1893456000;
|
||||||
|
let custom_time_ptr = &TIME_SINCE_EPOCH;
|
||||||
|
let mut tm = create_empty_tm();
|
||||||
|
|
||||||
|
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
|
||||||
|
|
||||||
|
// Verify 2030-01-01 00:00:00
|
||||||
|
assert_eq!(tm.tm_sec, 0);
|
||||||
|
assert_eq!(tm.tm_min, 0);
|
||||||
|
assert_eq!(tm.tm_hour, 0);
|
||||||
|
assert_eq!(tm.tm_mday, 1);
|
||||||
|
assert_eq!(tm.tm_mon, 0);
|
||||||
|
assert_eq!(tm.tm_year, 130); // 2030 - 1900
|
||||||
|
assert_eq!(tm.tm_wday, 2); // Tuesday
|
||||||
|
assert_eq!(tm.tm_yday, 0);
|
||||||
|
assert_eq!(tm.tm_isdst, -1);
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "android"
|
||||||
|
))]
|
||||||
|
{
|
||||||
|
assert_eq!(tm.tm_gmtoff, 0);
|
||||||
|
unsafe {
|
||||||
|
assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(ptr::eq(res, &mut tm));
|
||||||
|
env::remove_var(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests the behavior of `localtime_r` with multiple calls to ensure deduplication of `tm_zone` pointers.
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd", target_os = "android"))]
|
||||||
|
fn test_localtime_r_multiple_calls_deduplication() {
|
||||||
|
let key = "TZ";
|
||||||
|
env::set_var(key, "PST8PDT");
|
||||||
|
|
||||||
|
const TIME_SINCE_EPOCH_BASE: libc::time_t = 1712475836; // Base timestamp: 2024-04-07 07:43:56 GMT
|
||||||
|
const NUM_CALLS: usize = 50;
|
||||||
|
|
||||||
|
let mut unique_pointers = std::collections::HashSet::new();
|
||||||
|
|
||||||
|
for i in 0..NUM_CALLS {
|
||||||
|
let timestamp = TIME_SINCE_EPOCH_BASE + (i as libc::time_t * 3600); // Increment by 1 hour for each call
|
||||||
|
let mut tm: libc::tm = create_empty_tm();
|
||||||
|
let tm_ptr = unsafe { libc::localtime_r(×tamp, &mut tm) };
|
||||||
|
|
||||||
|
assert!(!tm_ptr.is_null(), "localtime_r failed for timestamp {timestamp}");
|
||||||
|
|
||||||
|
unique_pointers.insert(tm.tm_zone);
|
||||||
|
}
|
||||||
|
|
||||||
|
let unique_count = unique_pointers.len();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
unique_count >= 2 && unique_count <= (NUM_CALLS - 1),
|
||||||
|
"Unexpected number of unique tm_zone pointers: {} (expected between 2 and {})",
|
||||||
|
unique_count,
|
||||||
|
NUM_CALLS - 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -27,11 +27,8 @@ fn main() {
|
|||||||
test_file_sync();
|
test_file_sync();
|
||||||
test_errors();
|
test_errors();
|
||||||
test_rename();
|
test_rename();
|
||||||
// solarish needs to support readdir/readdir64 for these tests.
|
|
||||||
if cfg!(not(any(target_os = "solaris", target_os = "illumos"))) {
|
|
||||||
test_directory();
|
test_directory();
|
||||||
test_canonicalize();
|
test_canonicalize();
|
||||||
}
|
|
||||||
test_from_raw_os_error();
|
test_from_raw_os_error();
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
test_pread_pwrite();
|
test_pread_pwrite();
|
||||||
@ -279,7 +276,12 @@ fn test_directory() {
|
|||||||
.collect::<BTreeMap<_, _>>()
|
.collect::<BTreeMap<_, _>>()
|
||||||
);
|
);
|
||||||
// Deleting the directory should fail, since it is not empty.
|
// Deleting the directory should fail, since it is not empty.
|
||||||
assert_eq!(ErrorKind::DirectoryNotEmpty, remove_dir(&dir_path).unwrap_err().kind());
|
|
||||||
|
// Solaris/Illumos `rmdir` call set errno to EEXIST if directory contains
|
||||||
|
// other entries than `.` and `..`.
|
||||||
|
// https://docs.oracle.com/cd/E86824_01/html/E54765/rmdir-2.html
|
||||||
|
let err = remove_dir(&dir_path).unwrap_err().kind();
|
||||||
|
assert!(matches!(err, ErrorKind::AlreadyExists | ErrorKind::DirectoryNotEmpty));
|
||||||
// Clean up the files in the directory
|
// Clean up the files in the directory
|
||||||
remove_file(&path_1).unwrap();
|
remove_file(&path_1).unwrap();
|
||||||
remove_file(&path_2).unwrap();
|
remove_file(&path_2).unwrap();
|
||||||
|
@ -29,3 +29,6 @@ review_labels = ["S-waiting-on-review"]
|
|||||||
remove_labels = ["S-waiting-on-author"]
|
remove_labels = ["S-waiting-on-author"]
|
||||||
# Those labels are added when PR author requests a review from an assignee
|
# Those labels are added when PR author requests a review from an assignee
|
||||||
add_labels = ["S-waiting-on-review"]
|
add_labels = ["S-waiting-on-review"]
|
||||||
|
|
||||||
|
# Automatically close and reopen PRs made by bots to run CI on them
|
||||||
|
[bot-pull-requests]
|
||||||
|
Loading…
Reference in New Issue
Block a user