Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use checked casts and arithmetic in Miri engine #70226

Merged
merged 11 commits into from
Mar 25, 2020
Merged

Conversation

RalfJung
Copy link
Member

This is unfortunately pretty annoying because we have to cast back and forth between u64 and usize more often that should be necessary, and that cast is considered fallible.

For example, should this really be usize?

Also, LayoutDetails uses usize for field indices, but in Miri we use u64 to be able to also handle array indexing. Maybe methods like mplace_field should be suitably generalized to accept both u64 and usize?

r? @oli-obk Cc @eddyb

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Mar 21, 2020
@eddyb
Copy link
Member

eddyb commented Mar 21, 2020

This is unfortunately pretty annoying because we have to cast back and forth between u64 and usize more often that should be necessary, and that cast is considered fallible.

Only u64 -> usize should be fallible, usize -> u64 shouldn't, but you're saying they both implement TryFrom and neither From?

Did we really future-proof the standard library for 128-bit address spaces?
Actually, that makes sense - the compiler has to care about what's currently implemented, but the standard library must be future-compatible with anything we might want to support.

@RalfJung
Copy link
Member Author

Actually, that makes sense - the compiler has to care about what's currently implemented, but the standard library must be future-compatible with anything we might want to support.

Yeah, exactly that. And that is quite annoying. Maybe the compiler should have its own From that permits usize->u64.

Anyway, I made this a bit less annoying for Miri by now having both usize and u64-based projection methods (place_field/place_index, respectively).

let width = t.bit_width().unwrap_or_else(|| self.pointer_size().bits() as usize);
let v = f.to_u128(width).value;
let width = t.bit_width().unwrap_or_else(|| self.pointer_size().bits());
let v = f.to_u128(usize::try_from(width).unwrap()).value;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is apfloat using usize instead of u64 for sizes...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, it could be an u8 just as well, the max value here is 128.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO it would make sense to consistently use the same type for sizes, that avoids casts. Note how changing bit_width to return u64 got rid of a bunch of casts both in Miri and codegen.


#[inline]
pub fn from_bits(bits: u64) -> Size {
pub fn from_bits(bits: impl TryInto<u64>) -> Size {
let bits = bits.try_into().ok().unwrap();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this result in a worse panic message than if you remove the .ok()?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah but it doesn't compile without the ok() as we don't know if the error type implements Debug.

pub const fn from_bytes(bytes: u64) -> Size {
Size { raw: bytes }
pub fn from_bytes(bytes: impl TryInto<u64>) -> Size {
Size { raw: bytes.try_into().ok().unwrap() }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here (removing the .ok()).

@rust-highfive

This comment has been minimized.

Comment on lines +434 to +437
pub fn mplace_index(
&self,
base: MPlaceTy<'tcx, M::PointerTag>,
index: u64,
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is partially a duplication of logic in mplace_field -- but note that that already was a duplication of logic in the layout code. We cannot use the layout code as it is written for usize.

Allocation {
bytes: vec![0; size.bytes() as usize],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe give Size a method bytes_usize which does this check for you so it isn't spread around everywhere. Make it #[track_caller] though

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added bytes_usize, but was worried track_caller would be bad for performance.

"cannot handle this access on this host architecture"
);
let end = end.bytes() as usize;
let end = Size::add(offset, size); // This does overflow checking.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the Size::bin_op style very much. Why are the regular binary operators not ok?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I see a + I have to assume it is not overflow-checked. Figuring out the type of the operands can be non-trivial. So the only way to write overflow-clean Rust code for sure is to make sure there is, syntactically, no +/*/*-anywhere in the code. Hence the explicitSize::{add,mul}`.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alternative is to just turn on overflow checks in release mode.

I still don't like it. This is much less readable and most of the time the types are a line or two above in the arguments.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not gonna block the PR on it, but I want someone else to also say it's ok to write lisp-math in rustc if infix math would produce the exact same machine code.

Copy link
Member Author

@RalfJung RalfJung Mar 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I did the audit, I think I'm fine going back to infix notation. I will add a comment though in places where the type is not immediately obvious.

The alternative is to just turn on overflow checks in release mode.

Would be great if that could be controlled on a per-module basis. Given that this is a MIR building thing, I see no reason why per-module should not be possible.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could probably start out with a rustc_overflow_checks attribute, seems straight forward enough to do.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could probably start out with a rustc_overflow_checks attribute, seems straight forward enough to do.

#70358

@eddyb
Copy link
Member

eddyb commented Mar 23, 2020

@oli-obk's comment (#70226 (comment)) reminded me there used to be this pattern:

let x: u128 = ...; // Some potentially large value...
let x_as_u64 = x as u64; // ... that we want to fit in a smaller type
assert_eq!(x_as_u64 as u128, x);
// Use `x_as_u64` now that it's been validated.

I believe that u64::try_from(x).unwrap() is an appropriate replacement.
So a bunch of assert_eq!s can probably be removed now.

@RalfJung
Copy link
Member Author

@eddyb Indeed as part of this PR I replaced all instances of this pattern that I found by try_from. If there are any left, I overlooked them.

@bors
Copy link
Contributor

bors commented Mar 25, 2020

☔ The latest upstream changes (presumably #70371) made this pull request unmergeable. Please resolve the merge conflicts.

@RalfJung RalfJung mentioned this pull request Mar 25, 2020
@oli-obk
Copy link
Contributor

oli-obk commented Mar 25, 2020

r=me after a rebase

@RalfJung
Copy link
Member Author

Rebased.
@bors r=oli-obk

@bors
Copy link
Contributor

bors commented Mar 25, 2020

📌 Commit 7400955 has been approved by oli-obk

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 25, 2020
@bors
Copy link
Contributor

bors commented Mar 25, 2020

⌛ Testing commit 7400955 with merge cf1bd7d3721a751f3f7737ba56e6e73bee07b40e...

Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Mar 25, 2020
use checked casts and arithmetic in Miri engine

This is unfortunately pretty annoying because we have to cast back and forth between `u64` and `usize` more often that should be necessary, and that cast is considered fallible.

For example, should [this](https://doc.rust-lang.org/nightly/nightly-rustc/rustc/mir/interpret/value/enum.ConstValue.html) really be `usize`?

Also, `LayoutDetails` uses `usize` for field indices, but in Miri we use `u64` to be able to also handle array indexing. Maybe methods like `mplace_field` should be suitably generalized to accept both `u64` and `usize`?

r? @oli-obk Cc @eddyb
This was referenced Mar 25, 2020
@Dylan-DPC-zz
Copy link

@bors retry yielding to rollup (which also contains this one)

@bors
Copy link
Contributor

bors commented Mar 25, 2020

⌛ Testing commit 7400955 with merge d38c9793e9a3fda2dc9d8a052f3998e9cc57c1ad...

@Dylan-DPC-zz
Copy link

@bors retry yielding to rollup

bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 25, 2020
Rollup of 5 pull requests

Successful merges:

 - rust-lang#70226 (use checked casts and arithmetic in Miri engine)
 - rust-lang#70319 (correctly normalize constants)
 - rust-lang#70352 (Add long error explanation for E0710 )
 - rust-lang#70366 (Implement Fuse with Option)
 - rust-lang#70379 (fix incorrect type name in doc comments)

Failed merges:

 - rust-lang#70375 (avoid catching InterpError)

r? @ghost
@bors bors merged commit 97f0a9e into rust-lang:master Mar 25, 2020
@RalfJung RalfJung deleted the checked branch March 25, 2020 23:01
bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 26, 2020
update miri

Usually I'd wait until rust-lang#70226 lands which will break Miri again, but... the queue is empty (!), so whatever.^^

r? @ghost Cc @oli-obk
Fixes rust-lang#70346
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants