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 bd0aae92dc76d9, 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 bd0aae92dc76d9 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.
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index f0c1dfc..b10a128 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -760,30 +760,18 @@ fn write_operand_repeatedly(
         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 @@ pub(crate) fn set_unpredictable(&mut self, inst: &'ll Value) {
         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])
     }
diff --git a/tests/debuginfo/basic-stepping.rs b/tests/debuginfo/basic-stepping.rs
index aa9f292..f8198f5 100644
--- a/tests/debuginfo/basic-stepping.rs
+++ b/tests/debuginfo/basic-stepping.rs
@@ -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");