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

llvm lint: "Undefined behavior: Call argument type mismatches callee parameter type" with mixing debug and release #48310

Closed
matthiaskrgr opened this issue Feb 17, 2018 · 14 comments
Labels
A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. C-bug Category: This is a bug. P-high High priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@matthiaskrgr
Copy link
Member

matthiaskrgr commented Feb 17, 2018

This was reduced from https://github.com/ogham/rust-ansi-term (lib crate) @ 090e0a383d73a43e2f80a7b466e8feeee97c4c76

add this to cargo.toml

[profile.release]
debug=true
opt-level=3

and this as your lib.rs:

use std::fmt;

pub struct Infix(bool, bool);

impl fmt::Display for Infix {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let f: &mut fmt::Write = f;
        write!(f, "")
    }
}

build with

$ RUSTFLAGS="-C passes=lint"  cargo build --release 
   Compiling ansi_term v0.10.2 (file:///tmp/rust-ansi-term)
Undefined behavior: Call argument type mismatches callee parameter type
  %6 = call zeroext i1 bitcast (i1 (%"core::fmt::Formatter"*, %"core::fmt::Arguments"*)* @"_ZN71_$LT$core..fmt..Formatter$LT$$u27$a$GT$$u20$as$u20$core..fmt..Write$GT$9write_fmt17hf4780c35b427924cE" to i1 ({}*, %"core::fmt::Arguments"*)*)({}* nonnull %4, %"core::fmt::Arguments"* noalias nocapture dereferenceable(48) %3), !dbg !119
    Finished release [optimized + debuginfo] target(s) in 0.20 secs

EDIT: meta:

rustc 1.25.0-nightly (58a8e0c27 2018-02-16)
binary: rustc
commit-hash: 58a8e0c27152e9306f8e0cd4fa3a162f5ae8e8c4
commit-date: 2018-02-16
host: x86_64-unknown-linux-gnu
release: 1.25.0-nightly
LLVM version: 6.0

// cc #7463

@bstrie bstrie added the A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. label Feb 19, 2018
@TimNN TimNN added the C-bug Category: This is a bug. label Feb 20, 2018
@matthiaskrgr
Copy link
Member Author

matthiaskrgr commented Apr 27, 2019

Another example from rusts own codebase

trait Foo {
    extern fn borrow(&self);
    extern fn take(self: Box<Self>);
}

struct Bar;
impl Foo for Bar {
    extern fn borrow(&self) {}
    extern fn take(self: Box<Self>) {}
}

fn main() {
    let foo: Box<dyn Foo> = Box::new(Bar);
    foo.borrow();
    foo.take()
}

src/test/run-pass/issues/issue-51907.rs ( when compiled with rustc -C passes=lint -C opt-level=3)

Undefined behavior: Call argument type mismatches callee parameter type
  invoke void bitcast (void (%Bar*)* @"_ZN53_$LT$issue_51907..Bar$u20$as$u20$issue_51907..Foo$GT$6borrow17h4e556b6e6bab9df2E" to void ({}*)*)({}* align 1 %7)
          to label %9 unwind label %19

@jonas-schievink jonas-schievink added I-nominated T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 27, 2019
@jonas-schievink
Copy link
Contributor

Nominating to figure out how bad this is. Might be related to #55976.

@hanna-kruppe
Copy link
Contributor

hanna-kruppe commented Apr 27, 2019

I believe LLVM's linting is in the wrong here. The purported type mismatch is that the caller passes {}* as first argument but the callee was declared with parameter type %Bar*. But the pointee type doesn't matter [1], all pointers are passed the same, so this call is perfectly ABI-compatible.

[1] slight caveat -- currently it still matters for sret "arguments", because those are not simply passing a pointer but modifying the stack layout based on the size of the pointed-to type, but I don't see that happening here.

@pnkfelix
Copy link
Member

pnkfelix commented May 2, 2019

triage: P-high for two tasks: 1. resolve whether this is indeed an LLVM bug, and if so, 2. filing a bug against LLVM.

removing nomination since I don't think there's much to discuss beyond 1. what's already in @rkruppe's comment and 2. assigning the issue (like any other P-high T-compiler issue) to someone on the compiler team to address.

@pnkfelix pnkfelix added P-high High priority and removed I-nominated labels May 2, 2019
@pnkfelix
Copy link
Member

pnkfelix commented Jun 24, 2019

This seems like it was resolved between rustc 1.28.0 (9634041 2018-07-30) and rustc 1.29.0 (aa3ca19 2018-09-11)

@matthiaskrgr
Copy link
Member Author

I can still reproduce with rustc 1.37.0-nightly (929b48ec9 2019-06-21)

@pnkfelix
Copy link
Member

pnkfelix commented Jun 24, 2019

More specifically, it was resolved between rustc 1.29.0-nightly (e5f6498 2018-07-10) and rustc 1.29.0-nightly (64f7de9 2018-07-12)

@pnkfelix
Copy link
Member

@matthiaskrgr hmm, interesting. Are you reproducing via the reduced version from this comment above (which is what I am doing)? Or some other reproduction?

@pnkfelix
Copy link
Member

(I was going to assume that this was fixed by #51966, but @matthiaskrgr is making me pause. I'll see what I can figure out.)

@matthiaskrgr
Copy link
Member Author

I was trying the very first example in the original ticket description
#48310 (comment)

   Compiling f v0.1.0 (/tmp/f)
warning: trait objects without an explicit `dyn` are deprecated
 --> src/lib.rs:7:21
  |
7 |         let f: &mut fmt::Write = f;
  |                     ^^^^^^^^^^ help: use `dyn`: `dyn fmt::Write`
  |
  = note: #[warn(bare_trait_objects)] on by default

Unusual: noalias argument aliases another argument
  call void @_ZN4core3fmt9Arguments6new_v117hf2b276cf93a5bc30E(%"core::fmt::Arguments"* noalias nocapture sret dereferenceable(48) %3, [0 x { [0 x i8]*, i64 }]* noalias nonnull readonly align 8 bitcast (<{ [0 x i8] }>* @1 to [0 x { [0 x i8]*, i64 }]*), i64 0, [0 x { i8*, i8* }]* noalias nonnull readonly align 8 bitcast (<{ [0 x i8] }>* @1 to [0 x { i8*, i8* }]*), i64 0), !dbg !124
Undefined behavior: Call argument type mismatches callee parameter type
  %6 = call zeroext i1 bitcast (i1 (%"core::fmt::Formatter"*, %"core::fmt::Arguments"*)* @"_ZN57_$LT$core..fmt..Formatter$u20$as$u20$core..fmt..Write$GT$9write_fmt17h96fcf612e8ddc9f1E" to i1 ({}*, %"core::fmt::Arguments"*)*)({}* align 1 %4, %"core::fmt::Arguments"* noalias nocapture dereferenceable(48) %3), !dbg !124
    Finished release [optimized + debuginfo] target(s) in 0.68s

@pnkfelix
Copy link
Member

Ah I just realized that I was not correctly reproducing the original bug; there is a different ICE, from librustc_codegen_llvm/abi.rs, that is resolved in the range I mentioned.

@pnkfelix
Copy link
Member

Okay, I'm going to try to course-correct my signal-to-noise ratio for my comments on this issue.

Since the bug seems to only occur with -C opt-level=3, I figured this must be due to some combination of an LLVM pass with an LLVM lint.

So I used -Zprint-llvm-passes to observe which LLVM passes are being run, and then I used -C no-prepopulate-passes and -C passes to narrow down the passes to identify which seems to be the culprit.

The answer: "-Cpasses= early-cse lint ":

% rustc +nightly -C no-prepopulate-passes "-Cpasses=  lint  "  ../../narrowed-48310.rs
% rustc +nightly -C no-prepopulate-passes "-Cpasses= early-cse lint  "  ../../narrowed-48310.rs
Undefined behavior: Call argument type mismatches callee parameter type
  invoke void bitcast (void (%Bar*)* @"_ZN59_$LT$narrowed_48310..Bar$u20$as$u20$narrowed_48310..Foo$GT$6borrow1\
7hdd495dfcc8d99ffcE" to void ({}*)*)({}* align 1 %14)
          to label %15 unwind label %18

(So: its possible that there is a bug in the LLVM lint. Or it is possible that the LLVM lint is exposing a bug in the LLVM early-cse pass.)

@pnkfelix
Copy link
Member

pnkfelix commented Jun 24, 2019

I'm in the process of registering an account on bugs.llvm.org. While I wait for that to happen, I'll jot my last note here, which I think provides sufficient information to illustrate the problem to LLVM developers:

  1. I ran rustc +nightly -C no-prepopulate-passes "-Cpasses= lint " -Zprint-llvm-passes --emit llvm-ir ../../narrowed-48310.rs to generate a standalone .ll file, without the opt-level=3 optimizations.
Click to see contents of `narrowed-48310.rs` that I used
// compile-flags: -Copt-level=3 -Cpasses=lint

trait Foo {
    extern fn borrow(&self);
}

struct Bar;
impl Foo for Bar {
    extern fn borrow(&self) {}
}

fn main() {
    let foo: Box<dyn Foo> = Box::new(Bar);
    foo.borrow();
}

This generated narrowed-48310.ll

  1. Then I used the opt command from our local llvm build (./build/x86_64-unknown-linux-gnu/llvm/bin/opt) and applied it repeatedly with different sets of passes to narrowed-48310.ll, to confirm that I could reproduce the bug in that manner with the same subset of passes that I identified in the previous comment. (And yes, it is again early-cse that causes the lint pass to complain.):
% ./build/x86_64-unknown-linux-gnu/llvm/bin/opt  -lint -o /dev/null narrowed-48310.ll
% ./build/x86_64-unknown-linux-gnu/llvm/bin/opt  -early-cse  -lint -o /dev/null narrowed-48310.ll
Undefined behavior: Call argument type mismatches callee parameter type
  invoke void bitcast (void (%Bar*)* @"_ZN59_$LT$narrowed_48310..Bar$u20$as$u20$narrowed_48310..Foo$GT$6borrow17hdd495dfcc8d99ffcE" to void ({}*)*)({}* align 1 %9)
          to label %bb3 unwind label %cleanup
%
  1. Then I applied -early-cse on its own to the narrowed-48310.ll file, using -S to generate LLVM ll-assembly for it too:
 % ./build/x86_64-unknown-linux-gnu/llvm/bin/opt -early-cse  -S -o earlycsed-48310.ll narrowed-48310.ll
  1. and then I verified that I could apply the lint pass on its own to that file holding the result of early-cse, and see the same error:
% ./build/x86_64-unknown-linux-gnu/llvm/bin/opt -lint -o /dev/null earlycsed-48310.ll
Undefined behavior: Call argument type mismatches callee parameter type
  invoke void bitcast (void (%Bar*)* @"_ZN59_$LT$narrowed_48310..Bar$u20$as$u20$narrowed_48310..Foo$GT$6borrow17hdd495dfcc8d99ffcE" to void ({}*)*)({}* align 1 %9)
          to label %bb3 unwind label %cleanup

So now I have two files, which present the input to and output from LLVM's early-cse, where the input passes the lint and the output fails the lint.

narrowed-48310.ll gist

earlycsed-48310.ll gist

((I also had a go at trying to use bugpoint to reduce the .ll file to something smaller, but I did not see a significant reduction in the input from that tool. Admittedly, I am a novice at its usage.))

@pnkfelix
Copy link
Member

Okay, filed an LLVM bug here: https://bugs.llvm.org/show_bug.cgi?id=42380

At this point, given that this bug only has an impact on LLVM's internal lint pass (which we do not have turned on by default), and does not (as far as we know) represent an observable problem in code-generation, I am going to close this as an LLVM bug that is not our job to fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. C-bug Category: This is a bug. P-high High priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

6 participants