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

Only instantiate inline- and const-fns if they are referenced (again). #45575

Merged
merged 14 commits into from
Nov 8, 2017

Conversation

michaelwoerister
Copy link
Member

@michaelwoerister michaelwoerister commented Oct 27, 2017

It seems that we have regressed on not translating #[inline] functions unless they are actually used. This should bring back this optimization. I also added a regression test this time so it doesn't happen again accidentally.

Fixes #40392.

r? @alexcrichton

UPDATE & PSA

This patch makes translation very lazy -- in general this is a good thing (we don't want the compiler to do unnecessary work) but it has two consequences:

  1. Some error messages are only generated when an item is actually translated. Consequently, this patch will lead to more cases where the compiler will only start emitting errors when the erroneous function is actually used. This has always been true to some extend (e.g. when passing generic values to an intrinsic) but since this is something user-facing it's worth mentioning.
  2. When writing tests, one has to make sure that the functions in question are actually generated. In other words, it must not be dead code. This can usually be achieved by either
    1. making sure the function is exported from the resulting binary or
    2. by making sure the function is called from something that is exported (or main()).

Note that it depends on the crate type what functions are exported:

  1. For rlibs and dylibs everything that is reachable from the outside is exported.
  2. For executables, cdylibs, and staticlibs, items are only exported if they are additionally #[no_mangle] or have an #[export_name].

The commits in this PR contain many examples of how tests can be updated to comply to the new requirements.

@kennytm kennytm added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Oct 27, 2017
@alexcrichton
Copy link
Member

Looks great to me! r=me with passing tests

@kennytm kennytm added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Oct 27, 2017
@kennytm
Copy link
Member

kennytm commented Oct 27, 2017

CI is still failing. These tests compiled successfully while they should not:

[00:51:51]     [compile-fail] compile-fail/E0534.rs
[00:51:51]     [compile-fail] compile-fail/bad-intrinsic-monomorphization.rs
[00:51:51]     [compile-fail] compile-fail/dupe-symbols-2.rs
[00:51:51]     [compile-fail] compile-fail/invalid-inline.rs
[00:51:51]     [compile-fail] compile-fail/issue-22638.rs
[00:51:51]     [compile-fail] compile-fail/non-interger-atomic.rs

@kennytm kennytm added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Oct 27, 2017
@kennytm kennytm added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Oct 30, 2017
@alexcrichton
Copy link
Member

This looks great to me, thanks @michaelwoerister! I'm a little hesitant to personally r+ due to the change in semantics here. We chatted on IRC and it sounded like you're going to wait for discussion amongst the @rust-lang/compiler team, so I'll tag the PR appropriately for that.

@alexcrichton alexcrichton added S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Oct 31, 2017
@kennytm kennytm added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Oct 31, 2017
@eddyb
Copy link
Member

eddyb commented Oct 31, 2017

Some error messages are only generated when an item is actually translated.

I should note that this mainly has to do with unstable features (e.g. direct uses of intrinsics).
AFAIK, there are only two other ways to get an error (not a warning) during codegen:

  • a type with a size in bits that cannot be represented in i64 (LLVM limitation), or more generally, with a size in bytes that cannot be represented in the target platform's usize
  • resource limits, typically recursion either during trait matching or the function monomorphizations themselves

We could turn these into warnings and generate runtime panics instead, in fact the warnings could be done ahead-of-time and distinct from codegen anyway.

@michaelwoerister
Copy link
Member Author

We could turn these into warnings and generate runtime panics instead, in fact the warnings could be done ahead-of-time and distinct from codegen anyway.

Yes, I think this should be the preferred way to do it anyway.

What does the rest of @rust-lang/compiler say? Can we land this? I think the general goal of being as lazy as possible is worth pursuing.

#[no_mangle]
pub extern fn fail() {
}
}

mod b {
pub mod b {
#[no_mangle]
pub extern fn fail() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't private no_mangle functions always be compiled in?

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like it would be good if that were true.

@@ -21,4 +21,8 @@ fn b() {
fn c() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't we check the inline attribute for functions that aren't translated? I don't want people to create functions with an inline attribute that explode when a child crate finally instantiates them.

@arielb1
Copy link
Contributor

arielb1 commented Nov 2, 2017

I'm not sure this is a difference in kind, merely a difference in degree: even today you can write things that depend on reachability and blow up only when a downstream crate uses them, for example:

#![feature(core_intrinsics)]

use std::intrinsics;

#[inline(fail)]
pub fn foo<T>(_t: T) {
    unsafe {
        intrinsics::atomic_load(1 as *const [u8; 1048576]);
    }
}

#[inline(always)]
pub fn bar() {
    unsafe {
        intrinsics::atomic_load(1 as *const [u8; 1048576]);
    }
}

fn main() {}

I still think we want to sanitize attributes during type-checking, but intrinsics (which are unstable and intentionally error on monomorphization time) and resource limits (which are always a problem) should be OK.

@michaelwoerister
Copy link
Member Author

Discussed in the @rust-lang/compiler team meeting yesterday and the conclusion was to proceed with this. The main concern was that #[inline] attributes would not be validated for functions that are not called. This should be simple to fix and I opened #45738 to keep track of that.

@bors r=nikomatsakis

@bors
Copy link
Contributor

bors commented Nov 3, 2017

📌 Commit 8d4c92f has been approved by nikomatsakis

@shepmaster shepmaster 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-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). labels Nov 3, 2017
@bluss bluss added the relnotes Marks issues that should be documented in the release notes of the next release. label Nov 3, 2017
@bors
Copy link
Contributor

bors commented Nov 4, 2017

⌛ Testing commit 8d4c92f80d97bc0e42d935ce7d23f6e3083a4937 with merge df38d994283db81c5176dc109d1894cd5232625f...

@bors
Copy link
Contributor

bors commented Nov 4, 2017

💔 Test failed - status-travis

@kennytm
Copy link
Member

kennytm commented Nov 4, 2017

Test failed, test/mir-opt/match_false_edges/rustc.node17.SimplifyBranches-initial.before.mir does not exist:

[00:48:30] failures:
[00:48:30] 
[00:48:30] ---- [mir-opt] mir-opt/match_false_edges.rs stdout ----
[00:48:30] 	thread '[mir-opt] mir-opt/match_false_edges.rs' panicked at 'Output file `/checkout/obj/build/x86_64-unknown-linux-gnu/test/mir-opt/match_false_edges/rustc.node17.SimplifyBranches-initial.before.mir` from test does not exist', /checkout/src/tools/compiletest/src/runtest.rs:2290:12
[00:48:30] note: Run with `RUST_BACKTRACE=1` for a backtrace.
[00:48:30]
[00:48:30] 
[00:48:30] failures:
[00:48:30]     [mir-opt] mir-opt/match_false_edges.rs

@michaelwoerister
Copy link
Member Author

So I think these new failures are because #45710 got merged in between. The custom allocator implementations in these test cases fail linking because (I assume) the respective functions are not instantiated because they are not considered exported? Does that sound right, @alexcrichton?

I don't quite understand where these functions are supposed to come from (__rg_alloc, __rg_oom, __rg_grow_in_place, etc).

@alexcrichton
Copy link
Member

@michaelwoerister oh dear sorry about that!

The #[global_allocator] attribute is where they come from, the expand module will turn such a static into something like:

static FOO: MyAllocator = ...;

mod some_internal_name {
    #[std_special_symbol]
    #[no_mangle]
    fn __rg_alloc(...) -> ... {
        FOO.some_method(...)
    }

    ...
}

So the symbols are injected by the compiler and their attributes are generated here. IIRC the linkage = "external" puts them on the path towards reachability and then rustc_std_internal_symbol was a change to make them hidden visibility instead of public.

@michaelwoerister
Copy link
Member Author

Ah ok, the collector does not see the functions generated in librustc_trans/allocator.rs, and since we are creating an executable, these seemingly unreferenced SymbolExportLevel::Rust functions will not be instantiated.

@michaelwoerister
Copy link
Member Author

@alexcrichton Do you think that there's a case where a #[rustc_std_internal_symbol] should not be instantiated? If not I think this could be solved by treating such functions the same as regularly exported functions in the collector.

@alexcrichton
Copy link
Member

@michaelwoerister nah I think everything tagged with that should be unconditionally instantiated. They're all tiny anyway and will get gc'd by the linker if they're not used.

@michaelwoerister
Copy link
Member Author

@alexcrichton 👍

@michaelwoerister
Copy link
Member Author

OK, travis looks good. Let's give it another try.

@bors r=nikomatsakis

@bors
Copy link
Contributor

bors commented Nov 7, 2017

📌 Commit d141abf has been approved by nikomatsakis

@bors
Copy link
Contributor

bors commented Nov 8, 2017

⌛ Testing commit d141abf with merge 6dc72035c92f954c39240fbe4eeec544ef159331...

@bors
Copy link
Contributor

bors commented Nov 8, 2017

💔 Test failed - status-travis

@kennytm
Copy link
Member

kennytm commented Nov 8, 2017

dist-i586-gnu-i686-musl failed at codegen/fastcall-inreg.rs.

[00:51:57] ---- [codegen] codegen/fastcall-inreg.rs stdout ----
[00:51:57] 	
[00:51:57] error: verification with 'FileCheck' failed
[00:51:57] status: exit code: 1
[00:51:57] command: "/checkout/obj/build/x86_64-unknown-linux-gnu/llvm/build/bin/FileCheck" "--input-file" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/codegen/fastcall-inreg.ll" "/checkout/src/test/codegen/fastcall-inreg.rs"
[00:51:57] stdout:
[00:51:57] ------------------------------------------
[00:51:57] 
[00:51:57] ------------------------------------------
[00:51:57] stderr:
[00:51:57] ------------------------------------------
[00:51:57] /checkout/src/test/codegen/fastcall-inreg.rs:63:12: error: expected string not found in input
[00:51:57]  // CHECK: @f1(i32 inreg %arg0, i32 inreg %arg1, i32 %arg2)
[00:51:57]            ^
[00:51:57] /checkout/obj/build/x86_64-unknown-linux-gnu/test/codegen/fastcall-inreg.ll:1:1: note: scanning from here
[00:51:57] ; ModuleID = 'fastcall_inreg0-8787f43e282added376259c1adb08b80.rs'
[00:51:57] ^
[00:51:57] /checkout/obj/build/x86_64-unknown-linux-gnu/test/codegen/fastcall-inreg.ll:3:35: note: possible intended match here
[00:51:57] target datalayout = "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128"
[00:51:57]                                   ^
[00:51:57] 
[00:51:57] ------------------------------------------
[00:51:57] 
[00:51:57] thread '[codegen] codegen/fastcall-inreg.rs' panicked at 'explicit panic', /checkout/src/tools/compiletest/src/runtest.rs:2498:8
[00:51:57] note: Run with `RUST_BACKTRACE=1` for a backtrace.
[00:51:57] 
[00:51:57] 
[00:51:57] failures:
[00:51:57]     [codegen] codegen/fastcall-inreg.rs
[00:51:57] 
[00:51:57] test result: �[31mFAILED�(B�[m. 47 passed; 1 failed; 5 ignored; 0 measured; 0 filtered out

@michaelwoerister
Copy link
Member Author

Updated test case.

@bors r=nikomatsakis

@bors
Copy link
Contributor

bors commented Nov 8, 2017

📌 Commit 081ef8e has been approved by nikomatsakis

@bors
Copy link
Contributor

bors commented Nov 8, 2017

⌛ Testing commit 081ef8e with merge 4bb96f6...

bors added a commit that referenced this pull request Nov 8, 2017
…ikomatsakis

Only instantiate inline- and const-fns if they are referenced (again).

It seems that we have regressed on not translating `#[inline]` functions unless they are actually used. This should bring back this optimization. I also added a regression test this time so it doesn't happen again accidentally.

Fixes #40392.

r? @alexcrichton

UPDATE & PSA
---------------------
This patch **makes translation very lazy** -- in general this is a good thing (we don't want the compiler to do unnecessary work) but it has two consequences:
1. Some error messages are only generated when an item is actually translated. Consequently, this patch will lead to more cases where the compiler will only start emitting errors when the erroneous function is actually used. This has always been true to some extend (e.g. when passing generic values to an intrinsic) but since this is something user-facing it's worth mentioning.
2. When writing tests, one has to make sure that the functions in question are actually generated. In other words, it must not be dead code. This can usually  be achieved by either
    1. making sure the function is exported from the resulting binary or
    2. by making sure the function is called from something that is exported (or `main()`).

Note that it depends on the crate type what functions are exported:
   1. For rlibs and dylibs everything that is reachable from the outside is exported.
   2. For executables, cdylibs, and staticlibs, items are only exported if they are additionally `#[no_mangle]` or have an `#[export_name]`.

The commits in this PR contain many examples of how tests can be updated to comply to the new requirements.
@kennytm kennytm 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 Nov 8, 2017
@bors
Copy link
Contributor

bors commented Nov 8, 2017

☀️ Test successful - status-appveyor, status-travis
Approved by: nikomatsakis
Pushing 4bb96f6 to master...

@bors bors merged commit 081ef8e into rust-lang:master Nov 8, 2017
@michaelwoerister
Copy link
Member Author

Wow, cool :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
relnotes Marks issues that should be documented in the release notes of the next release. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants