Skip to content

Conversation

@Enselic
Copy link
Member

@Enselic Enselic commented Oct 8, 2025

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

This is kind of a revert of bd0aae9 (cg_llvm: use index-based loop in write_operand_repeatedly), 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 (it was added later in #144876). I have retroactively bisected to find that out.

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

The test that bd0aae9 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 PR takes us one step closer to fixing #33013.

CC #147426 which is related (there will be trivial conflicts for me to resolve in basic-stepping.rs once one of them lands)

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

This is kind of a revert of bd0aae9, 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 bd0aae9 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.
@rustbot rustbot added A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Oct 8, 2025
@rustbot
Copy link
Collaborator

rustbot commented Oct 8, 2025

r? @jdonszelmann

rustbot has assigned @jdonszelmann.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@Enselic Enselic changed the title rustc_codegen_llvm: Require opt-level >= 1 for index-based loop rustc_codegen_llvm: Require opt-level >= 1 for index-based write_operand_repeatedly() loop Oct 8, 2025
@saethlin
Copy link
Member

r? saethlin

@rustbot rustbot assigned saethlin and unassigned jdonszelmann Oct 11, 2025
@saethlin
Copy link
Member

Do you know why this codegen change made the stepping work differently? It's not clear to me at all what this diff has to do with the stepping order, but clearly it does. So I can't tell if this is actually papering over a bug in LLVM, or deeper in our debuginfo handling.

@saethlin saethlin added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Oct 12, 2025
@Enselic
Copy link
Member Author

Enselic commented Oct 24, 2025

@saethlin Good call. I have investigated more deeply now. Stepping stopped working because the only emitted LLVM instructions with debug location metadata were removed. No LLVM instructions with debug location metadata remained.

However, simply adding debug info to all remaining instructions does not work well, because then stepping will loop for as many times as we repeat.

Let's look at the details with this program:

fn main() {
    let whatever = ["whatever"; 8];
}

When stepping worked, it looked like this:

rustc +nightly-2023-04-23 -g --emit=llvm-ir $src/whatever.rs
; whatever::main
; Function Attrs: nonlazybind uwtable
define internal void @_ZN8whatever4main17h46e92f5877e9c5b3E() unnamed_addr #1 !dbg !175 {
start:
  %whatever = alloca [8 x { ptr, i64 }], align 8
  call void @llvm.dbg.declare(metadata ptr %whatever, metadata !179, metadata !DIExpression()), !dbg !189
  %0 = getelementptr inbounds [8 x { ptr, i64 }], ptr %whatever, i64 0, i64 0, !dbg !190
  %1 = getelementptr inbounds [8 x { ptr, i64 }], ptr %whatever, i64 0, i64 8, !dbg !190
  br label %repeat_loop_header, !dbg !190

repeat_loop_header:                               ; preds = %repeat_loop_body, %start
  %2 = phi ptr [ %0, %start ], [ %6, %repeat_loop_body ]
  %3 = icmp ne ptr %2, %1
  br i1 %3, label %repeat_loop_body, label %repeat_loop_next

repeat_loop_body:                                 ; preds = %repeat_loop_header
  %4 = getelementptr inbounds { ptr, i64 }, ptr %2, i32 0, i32 0
  store ptr @alloc_3db654700ddfbbbd22c59221279a79d2, ptr %4, align 8
  %5 = getelementptr inbounds { ptr, i64 }, ptr %2, i32 0, i32 1
  store i64 8, ptr %5, align 8
  %6 = getelementptr inbounds { ptr, i64 }, ptr %2, i64 1
  br label %repeat_loop_header

repeat_loop_next:                                 ; preds = %repeat_loop_header
  ret void, !dbg !191
}

!190 = !DILocation(line: 2, column: 20, scope: !175)

stepping worked because of (note that these instructions don't repeat):

  %0 = getelementptr inbounds [8 x { ptr, i64 }], ptr %whatever, i64 0, i64 0, !dbg !190
  %1 = getelementptr inbounds [8 x { ptr, i64 }], ptr %whatever, i64 0, i64 8, !dbg !190

We can make stepping work if we add !dbg to the looped over instructions which I do in #148058. Here is what the IR looks like in that PR:

./build/x86_64-unknown-linux-gnu/stage1/bin/rustc -g --emit=llvm-ir $src/whatever.rs
; whatever::main
; Function Attrs: nonlazybind uwtable
define hidden void @_ZN8whatever4main17h0930ac910df4df44E() unnamed_addr #0 !dbg !159 {
start:
  %whatever = alloca [128 x i8], align 8
    #dbg_declare(ptr %whatever, !163, !DIExpression(), !173)
  br label %repeat_loop_header, !dbg !174

repeat_loop_header:                               ; preds = %repeat_loop_body, %start
  %0 = phi i64 [ 0, %start ], [ %4, %repeat_loop_body ], !dbg !174
  %1 = icmp ult i64 %0, 8, !dbg !174
  br i1 %1, label %repeat_loop_body, label %repeat_loop_next, !dbg !174

repeat_loop_body:                                 ; preds = %repeat_loop_header
  %2 = getelementptr inbounds nuw { ptr, i64 }, ptr %whatever, i64 %0, !dbg !174
  store ptr @alloc_3db654700ddfbbbd22c59221279a79d2, ptr %2, align 8, !dbg !174
  %3 = getelementptr inbounds i8, ptr %2, i64 8, !dbg !174
  store i64 8, ptr %3, align 8, !dbg !174
  %4 = add nuw i64 %0, 1, !dbg !174
  br label %repeat_loop_header, !dbg !174

repeat_loop_next:                                 ; preds = %repeat_loop_header
  ret void, !dbg !175
}

!174 = !DILocation(line: 2, column: 20, scope: !159)

Now stepping works, but since all instructions with !174 are repeated, we will now step 8 times (because of ; 8];). br label %repeat_loop_header, !dbg !174 probably don't count since no machine code is emitted for that instruction (I assume. I haven't double-checked. But why would it?.)

I don't have any good idea on how to solve this. The only thing I can think of is to force emission of a no-op instruction before we enter the loop and then attach !dbg !174 to it. It might work in non-optimized builds. But I don't know if that's possible or wanted.

Maybe you have some other idea?

@saethlin
Copy link
Member

saethlin commented Oct 24, 2025

Stepping stopped working because the only emitted LLVM instructions with debug location metadata were removed. No LLVM instructions with debug location metadata remained.

Is that true? I'm looking at this IR:

; demo::main
; Function Attrs: nonlazybind uwtable
define hidden void @_ZN4demo4main17h1e5d7b00d81e2d71E() unnamed_addr #0 !dbg !151 {
start:
  %h = alloca [128 x i8], align 8
    #dbg_declare(ptr %h, !155, !DIExpression(), !165)
  br label %repeat_loop_header, !dbg !166

repeat_loop_header:                               ; preds = %repeat_loop_body, %start
  %0 = phi i64 [ 0, %start ], [ %4, %repeat_loop_body ]
  %1 = icmp ult i64 %0, 8
  br i1 %1, label %repeat_loop_body, label %repeat_loop_next

repeat_loop_body:                                 ; preds = %repeat_loop_header
  %2 = getelementptr inbounds nuw { ptr, i64 }, ptr %h, i64 %0
  store ptr @alloc_3db654700ddfbbbd22c59221279a79d2, ptr %2, align 8
  %3 = getelementptr inbounds i8, ptr %2, i64 8
  store i64 8, ptr %3, align 8
  %4 = add nuw i64 %0, 1
  br label %repeat_loop_header

repeat_loop_next:                                 ; preds = %repeat_loop_header
  ret void, !dbg !167
}

In the start block, I see dbg 165 and 166 mentioned, and those are

!165 = !DILocation(line: 2, column: 9, scope: !156) 
!166 = !DILocation(line: 2, column: 13, scope: !151)

Which are the the h and the [ in let h = ["whatever; 8].

It almost seems like the getelementptr inbounds instructions that we used to emit in the start block matter to LLVM in some way that the other instructions don't, right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants