rustc_codegen_llvm: Require opt-level >= 1 for index-based loop

To make debugger stepping intuitive with `-Copt-level=0`. See the
adjusted `basic-stepping.rs` test.

This is kind of a revert of bd0aae92dc, except we don't revert it,
we just make it conditional on `opt-level`. That commit regressed
`basic-stepping.rs`, but it was not noticed since that test did not
exist back then. I have retroactively bisected to find that out.

It seems messy to sprinkle if-cases inside of the
`write_operand_repeatedly()` so make the whole function conditional.

The test that bd0aae92dc added in
`tests/codegen/issues/issue-111603.rs` already use `-Copt-level=3`, so
we don't need to adjust the compiler flags for it to keep passing.
This commit is contained in:
Martin Nordholts 2025-10-08 05:57:24 +02:00
parent 503dce33e2
commit 5ad2f434dc
2 changed files with 86 additions and 26 deletions

View file

@ -760,30 +760,18 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
count: u64,
dest: PlaceRef<'tcx, &'ll Value>,
) {
let zero = self.const_usize(0);
let count = self.const_usize(count);
let header_bb = self.append_sibling_block("repeat_loop_header");
let body_bb = self.append_sibling_block("repeat_loop_body");
let next_bb = self.append_sibling_block("repeat_loop_next");
self.br(header_bb);
let mut header_bx = Self::build(self.cx, header_bb);
let i = header_bx.phi(self.val_ty(zero), &[zero], &[self.llbb()]);
let keep_going = header_bx.icmp(IntPredicate::IntULT, i, count);
header_bx.cond_br(keep_going, body_bb, next_bb);
let mut body_bx = Self::build(self.cx, body_bb);
let dest_elem = dest.project_index(&mut body_bx, i);
cg_elem.val.store(&mut body_bx, dest_elem);
let next = body_bx.unchecked_uadd(i, self.const_usize(1));
body_bx.br(header_bb);
header_bx.add_incoming_to_phi(i, next, body_bb);
*self = Self::build(self.cx, next_bb);
if self.cx.sess().opts.optimize == OptLevel::No {
// To let debuggers single-step over lines like
//
// let foo = ["bar"; 42];
//
// we need the debugger-friendly LLVM IR that `_unoptimized()`
// provides. The `_optimized()` version generates trickier LLVM IR.
// See PR #148058 for a failed attempt at handling that.
self.write_operand_repeatedly_unoptimized(cg_elem, count, dest);
} else {
self.write_operand_repeatedly_optimized(cg_elem, count, dest);
}
}
fn range_metadata(&mut self, load: &'ll Value, range: WrappingRange) {
@ -1514,6 +1502,78 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
self.set_metadata_node(inst, llvm::MD_unpredictable, &[]);
}
fn write_operand_repeatedly_optimized(
&mut self,
cg_elem: OperandRef<'tcx, &'ll Value>,
count: u64,
dest: PlaceRef<'tcx, &'ll Value>,
) {
let zero = self.const_usize(0);
let count = self.const_usize(count);
let header_bb = self.append_sibling_block("repeat_loop_header");
let body_bb = self.append_sibling_block("repeat_loop_body");
let next_bb = self.append_sibling_block("repeat_loop_next");
self.br(header_bb);
let mut header_bx = Self::build(self.cx, header_bb);
let i = header_bx.phi(self.val_ty(zero), &[zero], &[self.llbb()]);
let keep_going = header_bx.icmp(IntPredicate::IntULT, i, count);
header_bx.cond_br(keep_going, body_bb, next_bb);
let mut body_bx = Self::build(self.cx, body_bb);
let dest_elem = dest.project_index(&mut body_bx, i);
cg_elem.val.store(&mut body_bx, dest_elem);
let next = body_bx.unchecked_uadd(i, self.const_usize(1));
body_bx.br(header_bb);
header_bx.add_incoming_to_phi(i, next, body_bb);
*self = Self::build(self.cx, next_bb);
}
fn write_operand_repeatedly_unoptimized(
&mut self,
cg_elem: OperandRef<'tcx, &'ll Value>,
count: u64,
dest: PlaceRef<'tcx, &'ll Value>,
) {
let zero = self.const_usize(0);
let count = self.const_usize(count);
let start = dest.project_index(self, zero).val.llval;
let end = dest.project_index(self, count).val.llval;
let header_bb = self.append_sibling_block("repeat_loop_header");
let body_bb = self.append_sibling_block("repeat_loop_body");
let next_bb = self.append_sibling_block("repeat_loop_next");
self.br(header_bb);
let mut header_bx = Self::build(self.cx, header_bb);
let current = header_bx.phi(self.val_ty(start), &[start], &[self.llbb()]);
let keep_going = header_bx.icmp(IntPredicate::IntNE, current, end);
header_bx.cond_br(keep_going, body_bb, next_bb);
let mut body_bx = Self::build(self.cx, body_bb);
let align = dest.val.align.restrict_for_offset(dest.layout.field(self.cx(), 0).size);
cg_elem
.val
.store(&mut body_bx, PlaceRef::new_sized_aligned(current, cg_elem.layout, align));
let next = body_bx.inbounds_gep(
self.backend_type(cg_elem.layout),
current,
&[self.const_usize(1)],
);
body_bx.br(header_bb);
header_bx.add_incoming_to_phi(current, next, body_bb);
*self = Self::build(self.cx, next_bb);
}
pub(crate) fn minnum(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
self.call_intrinsic("llvm.minnum", &[self.val_ty(lhs)], &[lhs, rhs])
}

View file

@ -21,8 +21,8 @@
// FIXME(#33013): gdb-command: next
// FIXME(#33013): gdb-check: let g = b'9';
// FIXME(#33013): gdb-command: next
// FIXME(#33013): gdb-check: let h = ["whatever"; 8];
// FIXME(#33013): gdb-command: next
// gdb-check: let h = ["whatever"; 8];
// gdb-command: next
// gdb-check: let i = [1,2,3,4];
// gdb-command: next
// gdb-check: let j = (23, "hi");