This is an archive of the discontinued LLVM Phabricator instance.

[SimplifyCFG][LICM] Preserve nonnull, range and align metadata when speculating
ClosedPublic

Authored by nikic on Mar 22 2023, 6:46 AM.

Details

Summary

After D141386, violation of nonnull, range and align metadata results in poison rather than immediate undefined behavior, which means that these are now safe to retain when speculating. We only need to remove UB-implying metadata like noundef.

This is done by adding a dropUBImplyingAttrsAndMetadata() helper, which lists the metadata which is known safe to retain on speculation.

Diff Detail

Event Timeline

nikic created this revision.Mar 22 2023, 6:46 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 22 2023, 6:46 AM
nikic requested review of this revision.Mar 22 2023, 6:46 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 22 2023, 6:46 AM
jdoerfert added a comment.EditedMar 22 2023, 9:12 AM

This is valid, I think.
That said, I went to try what could go wrong and it was easy.
At least early-cse is buggy so this will cause issues until those bugs are fixed:
https://godbolt.org/z/Tz98hYa9E

FWIW, instcombine gets it right regardless of the order. Might be luck or logic to properly merge the attributes.

nikic planned changes to this revision.Mar 22 2023, 9:20 AM

@jdoerfert Yeah, there is a pending patch that fixes this for !range here: https://reviews.llvm.org/D142687 We'll need similar changes for nonnull/align as well.

I'll take this off the review queue until those changes have all landed.

@jdoerfert Yeah, there is a pending patch that fixes this for !range here: https://reviews.llvm.org/D142687 We'll need similar changes for nonnull/align as well.

I'll take this off the review queue until those changes have all landed.

Just a thought:
Can we create a test that we somehow run against all transformations (one by one) to verify they don't combine good metadata?

This is valid, I think.
That said, I went to try what could go wrong and it was easy.
At least early-cse is buggy so this will cause issues until those bugs are fixed:
https://godbolt.org/z/Tz98hYa9E

FWIW, instcombine gets it right regardless of the order. Might be luck or logic to properly merge the attributes.

Does it seem that we even haven't called combineMetadata in early-cse?

nikic updated this revision to Diff 510493.Apr 3 2023, 7:22 AM

Rebase. I believe all the known metadata handling issues are now fixed. https://github.com/llvm/llvm-project/commit/9b5ff4436e446e9df97ee37b3bcf9ba029c7d9aa is the fix for EarlyCSE.

This revision is now accepted and ready to land.Apr 3 2023, 7:23 AM
This revision was landed with ongoing or failed builds.Apr 4 2023, 1:03 AM
This revision was automatically updated to reflect the committed changes.
durin42 added a subscriber: durin42.Apr 4 2023, 1:00 PM

FYI, we're investigating a pretty big regression in Rust when built with LLVM containing this change. I don't have much yet, but

io::buffered::tests::line_buffer_write0_normal
io::tests::chain_zero_length_read_is_not_eof
io::tests::read_exact
net::tcp::tests::timeouts
net::udp::tests::timeouts
os::unix::net::tests::timeouts

in library/std tests are all broken by this change. I'm working on getting something more now.

I got a funny sense of things and decided to try again with

diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp
index f14737e73fde..849a22ec9c2f 100644
--- a/llvm/lib/IR/Instruction.cpp
+++ b/llvm/lib/IR/Instruction.cpp
@@ -249,7 +249,7 @@ void Instruction::dropUBImplyingAttrsAndMetadata() {
   // !noundef and various AA metadata must be dropped, as it generally produces
   // immediate undefined behavior.
   unsigned KnownIDs[] = {LLVMContext::MD_annotation, LLVMContext::MD_range,
-                         LLVMContext::MD_nonnull, LLVMContext::MD_align};
+                         LLVMContext::MD_align};
   dropUBImplyingAttrsAndUnknownMetadata(KnownIDs);
 }

and the tests all pass. I hope that's useful insight, I'm going to try and keep working on getting some IR out of one of the broken tests.

nikic added a comment.Apr 6 2023, 9:18 AM

Reduced to a miscompile in GVN:

declare void @use(i64)

define void @test(ptr %p) {
  %val = load ptr, ptr %p, align 8, !nonnull !{}
  %val.int = ptrtoint ptr %val to i64
  %val2 = load i64, ptr %p, align 8
  call void @use(i64 %val.int)
  call void @use(i64 %val2)
  ret void
}

; RUN: opt -passes=gvn < %s

define void @test(ptr %p) {
  %val = load ptr, ptr %p, align 8, !nonnull !0
  %val.int = ptrtoint ptr %val to i64
  call void @use(i64 %val.int)
  call void @use(i64 %val.int)
  ret void
}

I believe the problem here is that this goes through VNCoercion and the replacement we do is not with a plain load, but a ptrtoint cast of the load. As such, we end up not performing the combineMetadataForCSE adjustment.

TimNN added a subscriber: TimNN.Apr 6 2023, 9:21 AM
nikic added a comment.Apr 20 2023, 2:44 AM

Looks like there's another bug in GVN:

declare void @use(i32)

define void @test(i1 %c, ptr %p) {
  %v1 = load i32, ptr %p, !range !{i32 0, i32 10}
  br i1 %c, label %if, label %join

if:
  br label %join

join:
  %v2 = load i32, ptr %p, !range !{i32 20, i32 30}
  call void @use(i32 %v1)
  call void @use(i32 %v2)
  ret void
}

; RUN: opt -S -passes=gvn < %s

declare void @use(i32)

define void @test(i1 %c, ptr %p) {
  %v1 = load i32, ptr %p, align 4, !range !0
  br i1 %c, label %if, label %join

if:                                               ; preds = %0
  br label %join

join:                                             ; preds = %if, %0
  call void @use(i32 %v1)
  call void @use(i32 %v1)
  ret void
}

!0 = !{i32 0, i32 10}

Apparently we don't perform metadata combining when replacing non-local loads :(