diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -2812,6 +2812,15 @@ } assert(FreeInstrBB->size() == 1 && "Only the branch instruction should remain"); + + // Now that we've moved the call, its previous attributes might not apply + // (e.g. the parameter might have been annotated as nonnull because of the + // null check, but we've now hoisted the call above the null check). Remove + // any atttributes which may result in undef or poison since they might not + // apply anymore and would result in miscompiles if kept. + FI.dropUndefImplyingAttrsAndUnknownMetadata(); + FI.removeParamAttr(0, Attribute::NonNull); + return &FI; } diff --git a/llvm/test/Transforms/InstCombine/malloc-free.ll b/llvm/test/Transforms/InstCombine/malloc-free.ll --- a/llvm/test/Transforms/InstCombine/malloc-free.ll +++ b/llvm/test/Transforms/InstCombine/malloc-free.ll @@ -170,6 +170,55 @@ ret void } +;; Test that undef and poison-implying attributes are stripped from the call +;; when it's moved, since they may no longer be valid and result in miscompiles +;; if not dropped. +define void @test_nonnull_free_move(i8* %foo) minsize { +; CHECK-LABEL: @test_nonnull_free_move( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i8* [[FOO:%.*]], null +; CHECK-NEXT: tail call void @free(i8* [[FOO]]) +; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]] +; CHECK: if.then: +; CHECK-NEXT: br label [[IF_END]] +; CHECK: if.end: +; CHECK-NEXT: ret void +; +entry: + %tobool = icmp eq i8* %foo, null + br i1 %tobool, label %if.end, label %if.then + +if.then: ; preds = %entry + tail call void @free(i8* nonnull %foo) + br label %if.end + +if.end: ; preds = %entry, %if.then + ret void +} + +define void @test_dereferenceable_free_move(i8* %foo) minsize { +; CHECK-LABEL: @test_dereferenceable_free_move( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i8* [[FOO:%.*]], null +; CHECK-NEXT: tail call void @free(i8* [[FOO]]) +; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]] +; CHECK: if.then: +; CHECK-NEXT: br label [[IF_END]] +; CHECK: if.end: +; CHECK-NEXT: ret void +; +entry: + %tobool = icmp eq i8* %foo, null + br i1 %tobool, label %if.end, label %if.then + +if.then: ; preds = %entry + tail call void @free(i8* dereferenceable(1) %foo) + br label %if.end + +if.end: ; preds = %entry, %if.then + ret void +} + ; The next four tests cover the semantics of the nofree attributes. These ; are thought to be legal transforms, but an implementation thereof has ; been reverted once due to difficult to isolate fallout.