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

Miri engine cleanup #53671

Merged
merged 21 commits into from
Aug 29, 2018
Merged

Miri engine cleanup #53671

merged 21 commits into from
Aug 29, 2018

Conversation

RalfJung
Copy link
Member

  • Unify the two maps in memory to store the allocation and its kind together.
  • Share the handling of statics between CTFE and miri: The miri engine always
    uses "lazy" AllocType::Static when encountering a static. Acessing that
    static invokes CTFE (no matter the machine). The machine only has any
    influence when writing to a static, which CTFE outright rejects (but miri
    makes a copy-on-write).
  • Add an AllocId to by-ref consts so miri can use them as operands without
    making copies.
  • Move responsibilities around for the eval_fn_call machine hook: The hook
    just has to find the MIR (or entirely take care of everything); pushing the
    new stack frame is taken care of by the miri engine.
  • Expose the intrinsics and lang items implemented by CTFE so miri does not
    have to reimplement them.
  • Allow Machine to hook into foreign statics (used by miri to get rid of some other hacks).
  • Clean up function calling.
  • Switch const sanity check to work on operands, not mplaces.
  • Move const_eval out of rustc_mir::interpret, to make sure that it does not access private implementation details.

In particular, we can finally make eval_operand take &self. :-)

Should be merged after #53609, across which I will rebase.

@rust-highfive
Copy link
Collaborator

r? @varkor

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive
Copy link
Collaborator

⚠️ Warning ⚠️

  • These commits modify submodules.

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Aug 24, 2018
@RalfJung
Copy link
Member Author

r? @oli-obk

Cc @eddyb

@rust-highfive rust-highfive assigned oli-obk and unassigned varkor Aug 24, 2018
@RalfJung RalfJung force-pushed the miri-refactor branch 2 times, most recently from 0627c37 to 395a1a7 Compare August 24, 2018 17:51
@bors
Copy link
Contributor

bors commented Aug 25, 2018

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

@RalfJung
Copy link
Member Author

@eddyb pointed out that the way validation handles user-defined types was wrong. I am working on fixing that. Good news is that the PlaceExtra type is entirely gone (MemPlace just has an Option<Scalar> now), and this fixed some other definitely-wrong previous code as well :D

@RalfJung
Copy link
Member Author

Done! PlaceExtra is gone. :) And also function calls are nicer now that @eddyb pointed me at mir.spread_arg.

I will stopp adding stuff to this PR now, I promise. :) (If it ends up green.)

@bors
Copy link
Contributor

bors commented Aug 26, 2018

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

// This should always be (), but getting it from the sig seems
// easier than creating a layout of ().
let dest = PlaceTy::null(&self, self.layout_of(fn_sig.output())?);
let ty = self.tcx.mk_tup((&[] as &[ty::Ty<'tcx>]).iter()); // return type is ()
Copy link
Contributor

Choose a reason for hiding this comment

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

this is just self.tcx.mk_nil()

Copy link
Member Author

Choose a reason for hiding this comment

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

D'oh, thanks! "nil" is not the name I was looking for...

Copy link
Contributor

Choose a reason for hiding this comment

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

Yea this is naming from ancient Rust... We should probably rename all nil in the compiler to unit. I'll open an easy issue

Copy link
Member Author

Choose a reason for hiding this comment

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

Done (with next push)

@@ -275,19 +275,19 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
return Ok(false);
}

/// Call this function -- pushing the stack frame and initializing the arguments.
/// `sig` is ptional in case of FnPtr/FnDef -- but mandatory for closures!
Copy link
Contributor

Choose a reason for hiding this comment

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

ptional

Copy link
Member Author

Choose a reason for hiding this comment

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

Done (with next push)

// a fake pointer? Are we even called for ZST?

// We need the layout of the local. We can NOT use the layout we got,
// that might e.g. be a downcast variant!
Copy link
Contributor

Choose a reason for hiding this comment

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

what's wrong with the downcast variant? It should be the same size and alignment

Copy link
Member Author

Choose a reason for hiding this comment

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

The comment was introduced by the previous PR, where it was needed when I had places pointing into locals through a field field in the Place::local variant.

Is the downcast variant always guaranteed to be like that? And there can also be structs wrapping other structs, maintaining the Scalar layout -- and some of the outer structs might have repr but the place we are seeing here is already for the inner field. That would change the alignment and be relevant here, right?

Copy link
Contributor

Choose a reason for hiding this comment

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

right. That makes sense. Just expand the comment to mention the inner field case

Copy link
Member Author

Choose a reason for hiding this comment

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

Done (with next push)

{
return Ok(handled);
// Handle non-integer operations
if let ty::Char = left_layout.ty.sty {
Copy link
Contributor

Choose a reason for hiding this comment

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

can you make these if lets a match over the type + method calls instead of having the logic right here?

Copy link
Member Author

Choose a reason for hiding this comment

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

You want one function per type?

Copy link
Contributor

Choose a reason for hiding this comment

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

if that's not too much hazzle, yes

Copy link
Member Author

Choose a reason for hiding this comment

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

Done (with next push)

let variant = match self.read_discriminant(dest) {
Ok(res) => res.1,
Err(err) => match err.kind {
EvalErrorKind::InvalidDiscriminant |
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a slight regression in diagnostics. Can you include the invalid value and expected values in the error variant and report that in the form of found "42" expected one of [5, 6, 7] or similar?

Also the ReadPointerAsBytes error should report that it found a pointer where it expected an integral discriminant

Copy link
Member Author

Choose a reason for hiding this comment

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

You think those lists of possible discriminant values are useful? The old messages showed a range, giving the lower and largest possible value. That was certainly not useful because intermediate values were also invalid. So in terms of usefulness this is not a regression...

Getting all that information here will be really hard, I think.

In a later commit, this is changed to always say "invalid discriminant", even for ReadPointerAsBytes.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, let's leave this alone for now. Seems like a minor issue, as the user can figure out the possible values themselves. Displaying the found value is still helpful 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.

Done (with next push)

Adding/changing a variant in the EvalErrorKind enum is way too much effort...

extra: PlaceExtra::None,
}
}
let mplace = if layout.is_unsized() {
Copy link
Contributor

Choose a reason for hiding this comment

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

won't this get us into trouble with extern types? They are unsized but have no extra

Copy link
Member Author

Choose a reason for hiding this comment

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

@eddyb says they technically have an extra of (). But you are right that this might still not work because their extra is not pointer-sized.

The old code here assumed that Dynamic, Str and Slice are the only possible unsized tails.

What could a testcase look like?

Copy link
Contributor

Choose a reason for hiding this comment

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

#![feature(extern_types)]

extern {
    type Foo;
}

fn main() {
    let x: &Foo = unsafe { &*(16 as *const Foo) };
    let y: &Foo = &*x;
}

might already trigger it

Copy link
Member Author

Choose a reason for hiding this comment

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

Hm, you are right, And I have no idea why this worked on old miri...

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

@RalfJung
Copy link
Member Author

I rebased, and also managed to get rid of the IsStatic trait again by instead having an associated const in the machine that says which machine memory kind (if any) to use for mutated statics.

@RalfJung
Copy link
Member Author

Let's ask perf...

@bors try

@bors
Copy link
Contributor

bors commented Aug 26, 2018

⌛ Trying commit a11e9ed with merge 3c7dbff...

bors added a commit that referenced this pull request Aug 26, 2018
Miri engine cleanup

* Unify the two maps in memory to store the allocation and its kind together.
* Share the handling of statics between CTFE and miri: The miri engine always
      uses "lazy" `AllocType::Static` when encountering a static.  Acessing that
      static invokes CTFE (no matter the machine).  The machine only has any
      influence when writing to a static, which CTFE outright rejects (but miri
      makes a copy-on-write).
* Add an `AllocId` to by-ref consts so miri can use them as operands without
      making copies.
* Move responsibilities around for the `eval_fn_call` machine hook: The hook
      just has to find the MIR (or entirely take care of everything); pushing the
      new stack frame is taken care of by the miri engine.
* Expose the intrinsics and lang items implemented by CTFE so miri does not
      have to reimplement them.
* Allow Machine to hook into foreign statics (used by miri to get rid of some other hacks).
* Clean up function calling.
* Switch const sanity check to work on operands, not mplaces.
* Move const_eval out of rustc_mir::interpret, to make sure that it does not access private implementation details.

In particular, we can finally make `eval_operand` take `&self`. :-)

Should be merged after #53609, across which I will rebase.
@rust-highfive

This comment has been minimized.

@rust-highfive
Copy link
Collaborator

The job x86_64-gnu-llvm-5.0 of your PR failed on Travis (raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
[00:48:15] ....................................................................................................
[00:48:18] ....................................................................................................
[00:48:20] ......i.............................................................................................
[00:48:24] ....................................................................................................
[00:48:26] .......................................................iiiiiiiii....................................
[00:48:32] ....................................................................................................
[00:48:35] ....................................................................................................
[00:48:38] ....................................i...............................................................
[00:48:41] .....................................................................................i.i..ii........
---
[00:50:31] ....................................................................................................
[00:50:40] ....................................................................................................
[00:50:52] ....................................................................................................
[00:51:01] ....................................................................................................
[00:51:11] .............................................................F......................................
[00:51:25] ....................................................................................................
[00:51:33] ....................................................................................................
[00:51:44] ...........................................................................i........................
[00:51:54] ....................................................................................................
---
[00:54:54] ---- [run-pass] run-pass/issue-15523-big.rs stdout ----
[00:54:54] 
[00:54:54] error: compilation failed!
[00:54:54] status: exit code: 1
[00:54:54] command: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2/bin/rustc" "/checkout/src/test/run-pass/issue-15523-big.rs" "--target=x86_64-unknown-linux-gnu" "-C" "prefer-dynamic" "-o" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/run-pass/issue-15523-big/a" "-Crpath" "-O" "-Zunstable-options" "-Lnative=/checkout/obj/build/x86_64-unknown-linux-gnu/native/rust-test-helpers" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/run-pass/issue-15523-big/auxiliary"
[00:54:54] ------------------------------------------
[00:54:54] 
[00:54:54] ------------------------------------------
[00:54:54] stderr:
[00:54:54] stderr:
[00:54:54] ------------------------------------------
[00:54:54] error: this expression will panic at runtime
[00:54:54]   --> /checkout/src/test/run-pass/issue-15523-big.rs:31:14
[00:54:54]    |
[00:54:54] 31 |     PosMax = !(1 << 63),
[00:54:54]    |              ^^^^^^^^^^ attempt to negate with overflow
[00:54:54]    = note: #[deny(const_err)] on by default
[00:54:54] 
[00:54:54] error: aborting due to previous error
[00:54:54] 
---
[00:54:54] 
[00:54:54] thread 'main' panicked at 'Some tests failed', tools/compiletest/src/main.rs:497:22
[00:54:54] 
[00:54:54] 
[00:54:54] command did not execute successfully: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-tools-bin/compiletest" "--compile-lib-path" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2/lib" "--run-lib-path" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2/lib/rustlib/x86_64-unknown-linux-gnu/lib" "--rustc-path" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2/bin/rustc" "--src-base" "/checkout/src/test/run-pass" "--build-base" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/run-pass" "--stage-id" "stage2-x86_64-unknown-linux-gnu" "--mode" "run-pass" "--target" "x86_64-unknown-linux-gnu" "--host" "x86_64-unknown-linux-gnu" "--llvm-filecheck" "/usr/lib/llvm-5.0/bin/FileCheck" "--host-rustcflags" "-Crpath -O -Zunstable-options " "--target-rustcflags" "-Crpath -O -Zunstable-options  -Lnative=/checkout/obj/build/x86_64-unknown-linux-gnu/native/rust-test-helpers" "--docck-python" "/usr/bin/python2.7" "--lldb-python" "/usr/bin/python2.7" "--gdb" "/usr/bin/gdb" "--quiet" "--llvm-version" "5.0.0\n" "--system-llvm" "--cc" "" "--cxx" "" "--cflags" "" "--llvm-components" "" "--llvm-cxxflags" "" "--adb-path" "adb" "--adb-test-dir" "/data/tmp/work" "--android-cross-path" "" "--color" "always"
[00:54:54] 
[00:54:54] 
[00:54:54] failed to run: /checkout/obj/build/bootstrap/debug/bootstrap test
[00:54:54] Build completed unsuccessfully in 0:10:40
[00:54:54] Build completed unsuccessfully in 0:10:40
[00:54:54] Makefile:58: recipe for target 'check' failed
[00:54:54] make: *** [check] Error 1

The command "stamp sh -x -c "$RUN_SCRIPT"" exited with 2.
travis_time:start:19ff6878
$ date && (curl -fs --head https://google.com | grep ^Date: | sed 's/Date: //g' || true)
---
travis_time:end:22c3ac30:start=1535297572596274392,finish=1535297572605087884,duration=8813492
travis_fold:end:after_failure.3
travis_fold:start:after_failure.4
travis_time:start:07daf4c0
$ ln -s . checkout && for CORE in obj/cores/core.*; do EXE=$(echo $CORE | sed 's|obj/cores/core\.[0-9]*\.!checkout!\(.*\)|\1|;y|!|/|'); if [ -f "$EXE" ]; then printf travis_fold":start:crashlog\n\033[31;1m%s\033[0m\n" "$CORE"; gdb -q -c "$CORE" "$EXE" -iex 'set auto-load off' -iex 'dir src/' -iex 'set sysroot .' -ex bt -ex q; echo travis_fold":"end:crashlog; fi; done || true
travis_fold:end:after_failure.4
travis_fold:start:after_failure.5
travis_time:start:05d2e2a8
travis_time:start:05d2e2a8
$ cat ./obj/build/x86_64-unknown-linux-gnu/native/asan/build/lib/asan/clang_rt.asan-dynamic-i386.vers || true
cat: ./obj/build/x86_64-unknown-linux-gnu/native/asan/build/lib/asan/clang_rt.asan-dynamic-i386.vers: No such file or directory
travis_fold:end:after_failure.5
travis_fold:start:after_failure.6
travis_time:start:06f478d4
$ dmesg | grep -i kill

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @TimNN. (Feature Requests)

@RalfJung

This comment has been minimized.

@rust-timer

This comment has been minimized.

@RalfJung
Copy link
Member Author

@rust-timer build 3c7dbff

@rust-timer
Copy link
Collaborator

Success: Queued 3c7dbff with parent caed80b, comparison URL.

@RalfJung
Copy link
Member Author

Surprisingly there are perf differences. Seems the only things that got noticeably slower are the CTFE stress tests; OTOH coercions got significantly faster -- likely because strings are now validated all at once using read_str, instead of validating every byte individually.

@RalfJung
Copy link
Member Author

Ah yes... getting miri to work again on all platforms will take a while.

I will back out the miri update part of this, and solve that in the miri repo.

@RalfJung
Copy link
Member Author

@bors r=oli-obk

@bors
Copy link
Contributor

bors commented Aug 28, 2018

📌 Commit c9b5fac 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-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Aug 28, 2018
@Mark-Simulacrum
Copy link
Member

@bors p=6

bors added a commit that referenced this pull request Aug 29, 2018
Miri engine cleanup

* Unify the two maps in memory to store the allocation and its kind together.
* Share the handling of statics between CTFE and miri: The miri engine always
      uses "lazy" `AllocType::Static` when encountering a static.  Acessing that
      static invokes CTFE (no matter the machine).  The machine only has any
      influence when writing to a static, which CTFE outright rejects (but miri
      makes a copy-on-write).
* Add an `AllocId` to by-ref consts so miri can use them as operands without
      making copies.
* Move responsibilities around for the `eval_fn_call` machine hook: The hook
      just has to find the MIR (or entirely take care of everything); pushing the
      new stack frame is taken care of by the miri engine.
* Expose the intrinsics and lang items implemented by CTFE so miri does not
      have to reimplement them.
* Allow Machine to hook into foreign statics (used by miri to get rid of some other hacks).
* Clean up function calling.
* Switch const sanity check to work on operands, not mplaces.
* Move const_eval out of rustc_mir::interpret, to make sure that it does not access private implementation details.

In particular, we can finally make `eval_operand` take `&self`. :-)

Should be merged after #53609, across which I will rebase.
@bors
Copy link
Contributor

bors commented Aug 29, 2018

⌛ Testing commit c9b5fac with merge f1d02c3...

@bors
Copy link
Contributor

bors commented Aug 29, 2018

☀️ Test successful - status-appveyor, status-travis
Approved by: oli-obk
Pushing f1d02c3 to master...

@bors bors merged commit c9b5fac into rust-lang:master Aug 29, 2018
@nnethercote
Copy link
Contributor

This was a big (~10%) compile speed win for coercions:
https://perf.rust-lang.org/compare.html?start=7061b2775782bb48c0a70d3c79ec711134396687&end=f1d02c307348057fd0554ad934006b186f8b6826&stat=instructions:u

Other changes are smaller, and might just be noise, it's a bit hard to tell.

@RalfJung
Copy link
Member Author

@nnethercote Yes, constants containing strings got faster because we now validate the entire string at once, instead of byte-for-byte.

@oli-obk I suppose we could make similar optimizations for slices of simple types (integer, float). Not sure if it's worth it? Probably worth writing down somewhere, at least.

@oli-obk
Copy link
Contributor

oli-obk commented Aug 31, 2018

Done #53845

@nnethercote
Copy link
Contributor

This was also a huge memory win for the old ctfe-stress-force-alloc benchmark:
https://perf.rust-lang.org/compare.html?start=7061b2775782bb48c0a70d3c79ec711134396687&end=f1d02c307348057fd0554ad934006b186f8b6826&stat=max-rss

}
impl<'tcx> PartialEq for OpTy<'tcx> {
fn eq(&self, other: &Self) -> bool {
self.op == other.op && self.layout.ty == other.layout.ty
Copy link
Member

Choose a reason for hiding this comment

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

This is a similar problem as with #53424. Maybe we should just derive PartialEq, Eq, Hash on Layout and TyLayout?!

Copy link
Member Author

Choose a reason for hiding this comment

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

It is the same problem in fact.

Why have they not been derived in the first place?

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 got a notification for you writing a comment here but now cannot see the comment, @eddyb... wtf?)

FWIW, in the mean time #54936 has landed.

Copy link
Member

Choose a reason for hiding this comment

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

I deleted the comment after seeing that PR.

/// Metadata for unsized places. Interpretation is up to the type.
/// Must not be present for sized types, but can be missing for unsized types
/// (e.g. `extern type`).
pub extra: Option<Scalar>,
Copy link
Member

Choose a reason for hiding this comment

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

I'd rename this to "meta".

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 that to #54461

pietroalbini added a commit to pietroalbini/rust that referenced this pull request Oct 9, 2018
impl Eq+Hash for TyLayout

As proposed by @eddyb at rust-lang#53671 (review).

I have an upcoming PR that would also significantly benefit from this.
pietroalbini added a commit to pietroalbini/rust that referenced this pull request Oct 10, 2018
impl Eq+Hash for TyLayout

As proposed by @eddyb at rust-lang#53671 (review).

I have an upcoming PR that would also significantly benefit from this.
Mark-Simulacrum added a commit to Mark-Simulacrum/rust that referenced this pull request Oct 11, 2018
impl Eq+Hash for TyLayout

As proposed by @eddyb at rust-lang#53671 (review).

I have an upcoming PR that would also significantly benefit from this.
kennytm added a commit to kennytm/rust that referenced this pull request Oct 12, 2018
impl Eq+Hash for TyLayout

As proposed by @eddyb at rust-lang#53671 (review).

I have an upcoming PR that would also significantly benefit from this.
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.