-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Possible codegen bug flagged by valgrind #11710
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
Comments
Ah, I should also note that the valgrind warning only shows up when building with optimizations. |
Slightly simpler code (IMHO): use std::util;
enum Option<T> {
None,
Some(T),
}
#[inline(never)]
fn function<T>(slot: &mut Option<T>, f: T) {
let a = util::replace(slot, None);
let _breaker = match a {
Some(_) => bar(f),
None => f,
};
}
#[inline(never)]
fn bar<T>(g: T) -> T {
g
}
#[start]
fn main(_: int, _: **u8) -> int {
let mut slot = None;
function(&mut slot, proc() {});
0
} The rest of the code seems to be required. Or at least various attempts to simplify it further made the problem disappear. What I figured out so far is that the problem is in the cleanup path for |
OK, so at some point LLVM moved the loads not only for the discriminator, but also for the define internal fastcc void @_ZN8function19hf4c7ff781ac735e1an4v0.0E(%"enum.Option<proc:Send()>"* nocapture, { void ({ i64, %tydesc*, i8*, i8*, i8 }*)*, { i64, %tydesc*, i8*, i8*, i8 }* }* noalias nocapture readonly) unnamed_addr #1 {
normal-return:
%.sroa.2 = alloca [15 x i8], align 1
%_breaker = alloca { void ({ i64, %tydesc*, i8*, i8*, i8 }*)*, { i64, %tydesc*, i8*, i8*, i8 }* }, align 8
%arg = alloca { void ({ i64, %tydesc*, i8*, i8*, i8 }*)*, { i64, %tydesc*, i8*, i8*, i8 }* }, align 8
%f.sroa.0.0.idx = getelementptr inbounds { void ({ i64, %tydesc*, i8*, i8*, i8 }*)*, { i64, %tydesc*, i8*, i8*, i8 }* }* %1, i64 0, i32 0
%f.sroa.0.0.copyload = load void ({ i64, %tydesc*, i8*, i8*, i8 }*)** %f.sroa.0.0.idx, align 8
%f.sroa.5.0.idx19 = getelementptr inbounds { void ({ i64, %tydesc*, i8*, i8*, i8 }*)*, { i64, %tydesc*, i8*, i8*, i8 }* }* %1, i64 0, i32 1
%f.sroa.5.0.copyload = load { i64, %tydesc*, i8*, i8*, i8 }** %f.sroa.5.0.idx19, align 8
%.sroa.2.1.idx10 = getelementptr inbounds [15 x i8]* %.sroa.2, i64 0, i64 0
; Loads from slot occur here
%tmp.sroa.0.0.idx6.i.i = getelementptr inbounds %"enum.Option<proc:Send()>"* %0, i64 0, i32 0
; Discriminator for slot
%tmp.sroa.0.0.copyload7.i.i = load i8* %tmp.sroa.0.0.idx6.i.i, align 8
%tmp.sroa.6.0.idx12.i.i = getelementptr inbounds %"enum.Option<proc:Send()>"* %0, i64 0, i32 1, i64 0
%tmp.sroa.616.0.idx17.i.i = getelementptr inbounds %"enum.Option<proc:Send()>"* %0, i64 0, i32 2, i64 1
%tmp.sroa.616.0.cast18.i.i = bitcast i64* %tmp.sroa.616.0.idx17.i.i to { i64, %tydesc*, i8*, i8*, i8 }**
; Env pointer for slot
%tmp.sroa.616.0.copyload19.i.i = load { i64, %tydesc*, i8*, i8*, i8 }** %tmp.sroa.616.0.cast18.i.i, align 8 And later on we have: "_ZN17proc.Send$LP$$RP$9glue_drop19hd34fc795812c1ef2akE.exit5": ; preds = %join, %cond.i4
%cond.not = xor i1 %cond, true
%18 = icmp eq { i64, %tydesc*, i8*, i8*, i8 }* %tmp.sroa.616.0.copyload19.i.i, null
%or.cond = or i1 %18, %cond.not
br i1 %or.cond, label %"_ZN17proc.Send$LP$$RP$9glue_drop19hd34fc795812c1ef2akE.exit", label %cond.i.i8
At the moment, I have no idea why that happens though. |
Getting simpler :-) struct X {
x: ~uint,
}
#[inline(never)]
fn function(mut slot: Option<X>) {
let a = std::util::replace(&mut slot, None);
let _breaker = match a {
Some(_) => ~1,
None => ~1,
};
}
#[start]
fn main(_: int, _: **u8) -> int {
function(None);
0
} |
This is a known valgrind false positive and Rust already has a lot of suppressions for it. It's reported upstream on the LLVM bug tracker but is not really an LLVM issue, just a limitation of valgrind. LLVM is allowed to generate undefined reads if it can not change the program behavior. |
Just for the record, the undefined read doesn't change the behaviour because it's just a check whether |
Verify Borrow<T> semantics for types that implement Hash, Borrow<str> and Borrow<[u8]>. Fixes rust-lang#11710 The essence of the issue is that types that implement Borrow<T> provide a facet or a representation of the underlying type. Under these semantics `hash(a) == hash(a.borrow())`. This is a problem when a type implements `Borrow<str>`, `Borrow<[u8]>` and Hash, it is expected that the hash of all three types is identical. The problem is that the hash of [u8] is not the same as that of a String, even when the byte reference ([u8]) is derived from `.as_bytes()` - [x] Followed [lint naming conventions][lint_naming] - [x] Added passing UI tests (including committed `.stderr` file) - [x] `cargo test` passes locally - [x] Executed `cargo dev update_lints` - [x] Added lint documentation - [x] Run `cargo dev fmt` --- - [x] Explanation of the issue in the code - [x] Tests reproducing the issue - [x] Lint rule and emission --- changelog: New lint: [`impl_hash_borrow_with_str_and_bytes`] [rust-lang#11781](rust-lang/rust-clippy#11781)
I'm not sure if this is a false positive, a codegen bug, or an LLVM bug.
That will generate a warning in valgrind about a jump based on an uninitialized value.
I've simplified it to:
I tried to make this test case have as little dependence on libstd as possible, but it didn't really help much with the output IR. The function
function
is the one that has the valgrind warning inside it, but I can't quite figure out where.The output assembly is https://gist.github.com/alexcrichton/8549277 and the suspicious instruction is
0x405235
The text was updated successfully, but these errors were encountered: