This is an archive of the discontinued LLVM Phabricator instance.

[GVN] Patch for invalid GVN replacement
ClosedPublic

Authored by alexgatea on Oct 17 2022, 10:25 AM.

Details

Summary

The way in which GVNPass is currently replacing uses of the instruction being processed with a value already computed is incorrect because it's not considering the possibility that both the replacement and the use are in the same block with the replacement coming after the use. This happens for example when both the blocks we branch to contain identical shl values, as in the LIT test provided. Prior to this patch, this test case results in an invalid dominator tree.

More details in the issue https://github.com/llvm/llvm-project/issues/58418

Diff Detail

Event Timeline

alexgatea created this revision.Oct 17 2022, 10:25 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 17 2022, 10:25 AM
Herald added a subscriber: hiraditya. · View Herald Transcript
alexgatea requested review of this revision.Oct 17 2022, 10:25 AM
alexgatea edited the summary of this revision. (Show Details)Oct 17 2022, 10:57 AM
nikic added a subscriber: nikic.Oct 17 2022, 10:57 AM

Please upload the patch with full context (-U99999). It would also be helpful to provide some more information on the issue, like which uses are being replaced by what instruction and why does GVN try to do that.

alexgatea updated this revision to Diff 468263.EditedOct 17 2022, 11:14 AM

Changed to full context differential and provided more information in the description

alexgatea edited the summary of this revision. (Show Details)Oct 17 2022, 11:25 AM
alexgatea added a comment.EditedOct 24 2022, 1:20 PM

Fyi the build fails due to clang-format, but I checked locally and the improperly formatted lines are not related to my patch

nikic added a comment.EditedOct 28 2022, 7:42 AM

Haven't looked into this deeply yet, but here is a reduced test case:

declare void @use(i32)

define void @test1(i1 %c, i32 %arg) {
  br i1 %c, label %bb1, label %bb2

bb1:
  %shl1 = shl i32 %arg, 2
  br label %bb3

bb2:
  %shl2 = shl i32 %arg, 2
  call void @use(i32 %shl2)
  br label %bb3

bb3:
  %shl3 = shl i32 %arg, 2
  %gep = getelementptr i32, ptr null, i32 %shl3
  %v = load i32, ptr %gep, align 4
  call void @use(i32 %v)
  br label %bb2
}

Probably the presence of irreducible control-flow is the important bit.

Edit: For reference, this is the output we currently get with disabled verifier:

declare void @use(i32)

define void @test1(i1 %c, i32 %arg) {
  br i1 %c, label %bb1, label %bb2

bb1:                                              ; preds = %0
  %shl1 = shl i32 %arg, 2
  br label %bb3

bb2:                                              ; preds = %bb3, %0
  call void @use(i32 %.pre)
  %.pre = shl i32 %arg, 2
  br label %bb3

bb3:                                              ; preds = %bb2, %bb1
  %shl3.pre-phi = phi i32 [ %.pre, %bb2 ], [ %shl1, %bb1 ]
  %gep = getelementptr i32, ptr null, i32 %shl3.pre-phi
  %v = load i32, ptr %gep, align 4
  call void @use(i32 %v)
  br label %bb2
}
alexgatea updated this revision to Diff 471559.EditedOct 28 2022, 8:16 AM
alexgatea edited the summary of this revision. (Show Details)

Updated LIT test to the suggested reduced test case.

nikic added a comment.Oct 28 2022, 8:27 AM

Here is my current understanding of the issue: For loads of GEPs, we try to perform scalar PRE during the main GVN pass, while for everything else it is done afterwards, see: https://github.com/llvm/llvm-project/blob/524c640090a8463305acbafd7606f1db3c1256e2/llvm/lib/Transforms/Scalar/GVN.cpp#L1777-L1783

Scalar PRE will insert the new instruction into the leader table for the predecessor here: https://github.com/llvm/llvm-project/blob/524c640090a8463305acbafd7606f1db3c1256e2/llvm/lib/Transforms/Scalar/GVN.cpp#L2768-L2769

In this example, we process the blocks in the order entry, bb1, bb3, bb2. When we process bb2, we add the PRE'd instruction to the leader table of bb2, so when we visit bb2, it thinks that the instruction is already available.

I don't think that the fix implemented here is the right one -- I think we need to either prevent PRE from happening in this case, or prevent the incorrect insertion into the leader table, or something along those lines.

nikic added a comment.Oct 28 2022, 8:45 AM

Two more notes:

I think this is the correct way to fix the issue. This will prevent PRE across backedges in all cases, and avoid inserting incorrect values into the leader table.

alexgatea updated this revision to Diff 471583.Oct 28 2022, 9:47 AM
alexgatea edited the summary of this revision. (Show Details)

Updated the patch with the suggested fix, and updated my LIT test as well as the LIT test from the previous patch. I agree that this is a better approach than my initial try.

nikic accepted this revision.Oct 31 2022, 8:28 AM

LGTM

llvm/test/Transforms/GVN/PRE/load-pre-identical-branches.ll
1 ↗(On Diff #471583)

I'd rename this to load-pre-across-backedge.ll or something, I don't think this has much to do with "identical branches".

This revision is now accepted and ready to land.Oct 31 2022, 8:28 AM

@nikic Thanks for approving! Since this is my first approved patch, I don't think I have commit access. So can you please push it for me (or let me know if I'm wrong)?

alexgatea updated this revision to Diff 472036.Oct 31 2022, 9:01 AM

Updated test name

nikic added a comment.Nov 1 2022, 1:29 AM

@alexgatea Can you please share Name <email> to use for the commit?

@alexgatea Can you please share Name <email> to use for the commit?

@nikic Sure: Alex Gatea <alexgatea@gmail.com>

@nikic I'm just wondering if you had a chance to push my commit through

This revision was automatically updated to reflect the committed changes.