Closed
Description
This is the current LLVM IR generated at --opt-level=0
for a seemingly noop transmute:
fn foo(x: ~[u8]) -> ~str {unsafe{transmute(x)}}
define internal { i64, i64, [0 x i8] }* @foo::h5f52f1de17121161af::v0.0({ i64, %tydesc*, i8*, i8*, i8 }*, { i64, i64, [0 x i8] }* noalias) unnamed_addr #4 {
"function top level":
%__arg = alloca { i64, i64, [0 x i8] }*
%__self = alloca { i64, i64, [0 x i8] }*
%2 = alloca { i8*, i32 }
store { i64, i64, [0 x i8] }* %1, { i64, i64, [0 x i8] }** %__arg
%3 = load { i64, i64, [0 x i8] }** %__arg
store { i64, i64, [0 x i8] }* %3, { i64, i64, [0 x i8] }** %__self
%4 = bitcast { i64, i64, [0 x i8] }** %__arg to i8*
call void @llvm.memset.p0i8.i64(i8* %4, i8 0, i64 8, i32 8, i1 false)
%5 = load { i64, i64, [0 x i8] }** %__self
br label %"normal return"
"normal return": ; preds = %"function top level"
%6 = bitcast { i64, i64, [0 x i8] }** %__arg to i32**
call void @"_$UP$u32::glue_drop::hb6a3c7b062d25f8far"({}* null, i32** %6)
ret { i64, i64, [0 x i8] }* %5
unwind: ; No predecessors!
%7 = landingpad { i8*, i32 } personality i32 (i32, i32, i64, %"struct.std::rt::unwind::libunwind::_Unwind_Exception[#1]"*, %"enum.std::rt::unwind::libunwind::_Unwind_Context[#1]"*)* @rust_eh_personality
cleanup
store { i8*, i32 } %7, { i8*, i32 }* %2
br label %cleanup
cleanup: ; preds = %unwind
%8 = bitcast { i64, i64, [0 x i8] }** %__arg to i32**
call void @"_$UP$u32::glue_drop::hb6a3c7b062d25f8far"({}* null, i32** %8)
%9 = load { i8*, i32 }* %2
resume { i8*, i32 } %9
}
Wherever x
is moved out of, a memset
call is used to zero the pointer, to "cancel the cleanup" (drop glue ignores NULL pointers and objects with a drop flag of 0).
The generated IR could look like this, without affecting semantics:
define internal { i64, i64, [0 x i8] }* @foo::h5f52f1de17121161af::v0.0({ i64, %tydesc*, i8*, i8*, i8 }*, { i64, i64, [0 x i8] }* noalias) unnamed_addr #4 {
"function top level":
%__self = alloca { i64, i64, [0 x i8] }*
store { i64, i64, [0 x i8] }* %1 { i64, i64, [0 x i8] }** %__self
%2 = load { i64, i64, [0 x i8] }** %__self
br label %"normal return"
"normal return": ; preds = %"function top level"
ret { i64, i64, [0 x i8] }* %2
(it might be even possible to remove the alloca used for the inlined transmute
call)
The reason I wasn't able to implement this behavior in #11252 is the differing scope between the cleanup creation and the cleanup cancellation. It becomes obvious when we have multiple branches:
fn foo<T: Clone>(x: T) {
if rand() % 1 == 0 {
x // moves out of x (zeroes x to cancel cleanup)
} else {
x.clone() // doesn't move out of x
}
}
@huonw recalled someone mentioning requiring to move out of x
in both branches, but I don't know if that's a viable solution.