Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Miscompilation of trivial loop after NodePushOut and NodePullIn #586

Open
haved opened this issue Aug 20, 2024 · 3 comments
Open

Miscompilation of trivial loop after NodePushOut and NodePullIn #586

haved opened this issue Aug 20, 2024 · 3 comments
Assignees
Labels

Comments

@haved
Copy link
Collaborator

haved commented Aug 20, 2024

Given the following code

int main() {
    for (int y = 0; y < 1; y++);
    return 0;
}

there is a miscompilation causing an infinite loop when compiled like so:

clang-17 src/main.c -o build/main.ll -S -emit-llvm -Xclang -disable-O0-optnone
opt-17 build/main.ll -o build/main-mem2reg.ll --passes=mem2reg -S
jlm-opt build/main-mem2reg.ll -o build/main-mem2reg-jlmopt.ll --NodePushOut
jlm-opt build/main-mem2reg-jlmopt.ll -o build/main-mem2reg-jlmopt-twice.ll --NodePullIn
clang-17 build/main-mem2reg-jlmopt-twice.ll -o build/jlm-opt-twice

The issue persists if breaking the for-loop into a while-loop.

The issue only appears when invoking NodePushOut and NodePullIn in two separate jlm-optcommands.

Removing any one of these passes causes the issue to disappear.

Compiling main-mem2reg-jlmopt.ll works, so the issue seams to appear in NodePullIn.

The mem2reg is also needed for the issue to appear. For reference, this is the content of main-mem2reg.ll:

; ModuleID = 'build/main.ll'
source_filename = "src/main.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

; Function Attrs: noinline nounwind uwtable
define dso_local i32 @main() #0 {
  br label %1

1:                                                ; preds = %4, %0
  %.0 = phi i32 [ 0, %0 ], [ %5, %4 ]
  %2 = icmp slt i32 %.0, 1
  br i1 %2, label %3, label %6

3:                                                ; preds = %1
  br label %4

4:                                                ; preds = %3
  %5 = add nsw i32 %.0, 1
  br label %1, !llvm.loop !6

6:                                                ; preds = %1
  ret i32 0
}

attributes #0 = { noinline nounwind uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }

!llvm.module.flags = !{!0, !1, !2, !3, !4}
!llvm.ident = !{!5}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 8, !"PIC Level", i32 2}
!2 = !{i32 7, !"PIE Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 2}
!4 = !{i32 7, !"frame-pointer", i32 2}
!5 = !{!"clang version 17.0.6 ([email protected]:llvm/llvm-project.git 6009708b4367171ccdbf4b5905cb6a803753fe18)"}
!6 = distinct !{!6, !7}
!7 = !{!"llvm.loop.mustprogress"}

Here is the main function from main-mem2reg-jlmopt.ll:

define i32 @main() #0 {
bb1:
  br label %bb2

bb2:                                 ; preds = %bb5, %bb1
  %0 = phi i32 [ 0, %bb1 ], [ %4, %bb5 ]
  %1 = icmp slt i32 %0, 1
  br i1 %1, label %bb4, label %bb3

bb3:                                 ; preds = %bb2
  br label %bb5

bb4:                                 ; preds = %bb2
  %2 = add i32 %0, 1
  br label %bb5

bb5:                                 ; preds = %bb4, %bb3
  %3 = phi i1 [ false, %bb3 ], [ true, %bb4 ]
  %4 = phi i32 [ %0, %bb3 ], [ %2, %bb4 ]
  br i1 %3, label %bb2, label %bb6

bb6:                                 ; preds = %bb5
  ret i32 0
}

And here is the main function from main-mem2reg-jlmopt-twice.ll (this is the one that never terminates):

define i32 @main() #0 {
bb1:
  br label %bb2

bb2:                                 ; preds = %bb8, %bb1
  %0 = phi i32 [ 0, %bb1 ], [ %8, %bb8 ]
  %1 = icmp slt i32 %0, 1
  %2 = add i32 %0, 1
  br i1 %1, label %bb4, label %bb3

bb3:                                 ; preds = %bb2
  br label %bb5

bb4:                                 ; preds = %bb2
  %3 = add i32 %0, 1
  br label %bb5

bb5:                                 ; preds = %bb4, %bb3
  %4 = phi i1 [ false, %bb3 ], [ true, %bb4 ]
  br i1 %4, label %bb7, label %bb6

bb6:                                 ; preds = %bb5
  %5 = select i1 %1, i32 %0, i32 %2
  br label %bb8

bb7:                                 ; preds = %bb5
  %6 = select i1 %1, i32 %0, i32 %2
  br label %bb8

bb8:                                 ; preds = %bb7, %bb6
  %7 = phi i1 [ false, %bb6 ], [ true, %bb7 ]
  %8 = phi i32 [ %0, %bb6 ], [ %6, %bb7 ]
  br i1 %7, label %bb2, label %bb9

bb9:                                 ; preds = %bb8
  ret i32 0
}
@haved haved added the bug label Aug 20, 2024
@haved
Copy link
Collaborator Author

haved commented Aug 21, 2024

@phate Just a heads up, this bug is possibly a bit gnarly. I was curious and had a look at the RVSDG, and it might look like it is not the NodePullIn pass that is at fault, but instead the conversion to LLVM IR afterwards.

I'm not 100% certain, as I might have mixed up some boolean values in my head, but it appears as if a trivial delta in the RVSDG was turned into a select with the parameters in the wrong order or something.

@phate
Copy link
Owner

phate commented Sep 2, 2024

I can reproduce it.

phate added a commit that referenced this issue Sep 10, 2024
1. Cleans up the basic block conversion in the LLVM backend
2. Assigns name "bb[0...]" to the basic block in the output

This PR is required for #586 in order to better follow the output. It is
also part of the effort in #529.

Example output:

```
; Function Attrs: noinline nounwind optnone uwtable
define i64 @fac(i64 noundef %0) #0 {
bb0:
  %1 = alloca i64, align 8
  %2 = alloca i64, align 8
  store i64 %0, ptr %2, align 8
  store i64 1, ptr %1, align 8
  br label %bb1

bb1:                                              ; preds = %bb4, %bb0
  %3 = load i64, ptr %1, align 8
  %4 = load i64, ptr %2, align 8
  %5 = icmp ugt i64 %4, 1
  br i1 %5, label %bb3, label %bb2

bb2:                                              ; preds = %bb1
  br label %bb4

bb3:                                              ; preds = %bb1
  %6 = mul i64 %3, %4
  store i64 %6, ptr %1, align 8
  %7 = load i64, ptr %2, align 8
  %8 = add i64 %7, -1
  store i64 %8, ptr %2, align 8
  br label %bb4

bb4:                                              ; preds = %bb3, %bb2
  %9 = phi i1 [ false, %bb2 ], [ true, %bb3 ]
  br i1 %9, label %bb1, label %bb5

bb5:                                              ; preds = %bb4
  %10 = load i64, ptr %1, align 8
  ret i64 %10
}
```
@phate
Copy link
Owner

phate commented Sep 14, 2024

The node pullin optimization only seems to be the trigger, but not the culprit. When I comment out the convert_empty_gamma_node() function in rvsdg2jlm.cpp and convert the RVSDG without the special handling for empty gammas, then the programs created from both assemblies (with and without pullin) terminate.

phate added a commit that referenced this issue Sep 16, 2024
1. Moves all the gamma tests of the RVSDG to CFG pass into a single file
2. Cleans them up a bit
3. Fix indentation of test files in Makefile

This is part of the fix for #586.
phate added a commit that referenced this issue Sep 28, 2024
The ascii output for the control flow graph is not easily readable. This
is the first PR on improving it. It does the following:

1. Moves the functions for converting a CFG to ascii to the cfg class
2. Adds a unit test for the conversion of three address codes

In order to add a unit test for the CFG conversion, I first need to make
the output more deterministic. This will happen in a follow up PR. This
PR is part of issue #586
phate added a commit that referenced this issue Oct 6, 2024
The PR does the following:
1. Ensure that CFG arguments are labeled after RVSDG to CFG conversion
2. Give readable labels to CFG nodes
3. Add missing newline after CFG printing

This PR is part of issue #586
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants