explicit tail calls: pass caller's argument slots as arguments for untuple indirect arguments#158248
explicit tail calls: pass caller's argument slots as arguments for untuple indirect arguments#158248dianqk wants to merge 1 commit into
Conversation
|
|
| // temporaries, then copy back to the caller's argument slots. | ||
| // Finally, we pass the caller's argument slots as arguments. | ||
| // | ||
| // To do that, the argument must be MUST-by-move value. |
There was a problem hiding this comment.
MUST-by-move is removed in this PR.
FYI @nikic
| let local = self.mir.args_iter().nth(i).unwrap(); | ||
|
|
||
| match &self.locals[local] { |
There was a problem hiding this comment.
This is not a reliable way to get the LLVM value for the argument. We are allowed to introduce a copy alloca in this case:
rust/compiler/rustc_codegen_ssa/src/mir/mod.rs
Lines 539 to 541 in 9030e34
cc @folkertdev
| // We don't need `tail_call_temporaries` for the untuple arguments because they won't be | ||
| // modified, they are the last arguments. |
There was a problem hiding this comment.
I don't get what this comment is saying...
There was a problem hiding this comment.
The new comment should be clearer.
5493c9b to
a61a2b4
Compare
| // For untupled arguments, it is safe to store them directly into the caller's | ||
| // argument slots without temporaries, because the untupled arguments are | ||
| // always passed through a tuple alloca. The alloca serves as a temporary | ||
| // that does not overlap with any caller argument. | ||
| // No temporaries are needed for the caller's untupled arguments either, | ||
| // because they are used last. |
There was a problem hiding this comment.
I still don't quite get this. First of all, this should probably mention tail calls, as otherwise it doesn't make sense why anything here wouldn't be ok.
But second of all, I checked out your branch locally and I'm getting incorrect codegen (rustc t.rs --emit=llvm-ir -O):
#![feature(unboxed_closures)]
#![feature(explicit_tail_calls)]
#![crate_type = "lib"]
type Tuple = (u64, u64, u64, u64);
trait X {
extern "rust-call" fn f(self, args: Tuple) -> u8;
extern "rust-call" fn g(self, args: Tuple) -> u8;
}
impl X for Tuple {
extern "rust-call" fn f(self, args: Tuple) -> u8 {
become args.g(self)
}
#[inline(never)]
extern "rust-call" fn g(self, args: Tuple) -> u8 {
std::hint::black_box((self, args));
0
}
}; <(u64, u64, u64, u64) as t::X>::f
; Function Attrs: nounwind nonlazybind uwtable
define noundef i8 @_RNvXCs146zwHEvxau_1tTyyyyENtB2_1X1f(ptr dead_on_return noalias nofree noundef align 8 captures(none) dereferenceable(32) initializes((0, 32)) %self, i64 noundef %0, i64 noundef %1, i64 noundef %2, i64 noundef %3) unnamed_addr #0 {
start:
store i64 %0, ptr %self, align 8
%.sroa.4.0.self.sroa_idx = getelementptr inbounds nuw i8, ptr %self, i64 8
store i64 %1, ptr %.sroa.4.0.self.sroa_idx, align 8
%.sroa.5.0.self.sroa_idx = getelementptr inbounds nuw i8, ptr %self, i64 16
store i64 %2, ptr %.sroa.5.0.self.sroa_idx, align 8
%.sroa.6.0.self.sroa_idx = getelementptr inbounds nuw i8, ptr %self, i64 24
store i64 %3, ptr %.sroa.6.0.self.sroa_idx, align 8
; call <(u64, u64, u64, u64) as t::X>::g
%4 = musttail call noundef i8 @_RNvXCs146zwHEvxau_1tTyyyyENtB2_1X1g(ptr noalias nofree noundef nonnull align 8 captures(address) dereferenceable(32) %self, i64 noundef %0, i64 noundef %1, i64 noundef %2, i64 noundef %3) #4
ret i8 %4
}Granted, this code is weird and I couldn't come up with a more normal looking one, but I don't get the justification for why this is sound...
There was a problem hiding this comment.
I see. Thanks for the case.
|
I could honestly see us just banning tail calling |
a61a2b4 to
c48e49c
Compare
I don’t have a strong opinion on this decision. I think it’s up to you. |
|
The job Click to see the possible cause of the failure (guessed by this bot) |
tracking issue: #112788
Fixes #158017.
Same as #151143, but for untuple indirect arguments.
r? WaffleLapkin