mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-26 15:11:00 +00:00
[Attributor][FIX] Do not create UB by introducing a noundef undef
This was reported as PR49104. The reproducer uses varargs but the issue is the same, we know an argument is dead but can't change the signature for some reason. The PR49104 situation was: We are in an CG-SCC traversal and we remove all the uses of an argument and proof it thereby dead. However, if we do not remove the argument, via signature rewrite, we need to ensure that the `undef` we introduce at the call site doesn't clash with a `noundef` attribute.
This commit is contained in:
parent
2aa01ccec3
commit
81429abd83
@ -24,6 +24,7 @@
|
||||
#include "llvm/Analysis/MemorySSAUpdater.h"
|
||||
#include "llvm/Analysis/MustExecute.h"
|
||||
#include "llvm/Analysis/ValueTracking.h"
|
||||
#include "llvm/IR/Attributes.h"
|
||||
#include "llvm/IR/GlobalValue.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/NoFolder.h"
|
||||
@ -1252,6 +1253,17 @@ ChangeStatus Attributor::cleanupIR() {
|
||||
isInstructionTriviallyDead(I))
|
||||
DeadInsts.push_back(I);
|
||||
}
|
||||
if (isa<UndefValue>(NewV) && isa<CallBase>(U->getUser())) {
|
||||
auto *CB = cast<CallBase>(U->getUser());
|
||||
if (CB->isArgOperand(U)) {
|
||||
unsigned Idx = CB->getArgOperandNo(U);
|
||||
CB->removeParamAttr(Idx, Attribute::NoUndef);
|
||||
Function *Fn = CB->getCalledFunction();
|
||||
assert(Fn && "Expected callee when call argument is replaced!");
|
||||
if (Fn->arg_size() > Idx)
|
||||
Fn->removeParamAttr(Idx, Attribute::NoUndef);
|
||||
}
|
||||
}
|
||||
if (isa<Constant>(NewV) && isa<BranchInst>(U->getUser())) {
|
||||
Instruction *UserI = cast<Instruction>(U->getUser());
|
||||
if (isa<UndefValue>(NewV)) {
|
||||
|
@ -167,7 +167,7 @@ define i32 @caller2b(%T* %g) {
|
||||
; IS__CGSCC____: Function Attrs: argmemonly nofree norecurse nosync nounwind willreturn
|
||||
; IS__CGSCC____-LABEL: define {{[^@]+}}@caller2b
|
||||
; IS__CGSCC____-SAME: (%T* nocapture nofree readonly [[G:%.*]]) [[ATTR3]] {
|
||||
; IS__CGSCC____-NEXT: [[V:%.*]] = call i32 @test2b(%T* nocapture nofree readonly [[G]], i32 noundef undef) [[ATTR6:#.*]]
|
||||
; IS__CGSCC____-NEXT: [[V:%.*]] = call i32 @test2b(%T* nocapture nofree readonly [[G]], i32 undef) [[ATTR6:#.*]]
|
||||
; IS__CGSCC____-NEXT: ret i32 0
|
||||
;
|
||||
%v = call i32 @test2b(%T* %g, i32 0)
|
||||
|
@ -157,7 +157,7 @@ define i32* @test6_2() #0 {
|
||||
define internal i8* @f1(i8* readnone %0) local_unnamed_addr #0 {
|
||||
; IS__TUNIT____: Function Attrs: nofree noinline nosync nounwind readnone uwtable willreturn
|
||||
; IS__TUNIT____-LABEL: define {{[^@]+}}@f1
|
||||
; IS__TUNIT____-SAME: (i8* noalias nofree noundef nonnull readnone returned align 8 dereferenceable(1) "no-capture-maybe-returned" [[TMP0:%.*]]) local_unnamed_addr [[ATTR0]] {
|
||||
; IS__TUNIT____-SAME: (i8* noalias nofree nonnull readnone returned align 8 dereferenceable(1) "no-capture-maybe-returned" [[TMP0:%.*]]) local_unnamed_addr [[ATTR0]] {
|
||||
; IS__TUNIT____-NEXT: br label [[TMP3:%.*]]
|
||||
; IS__TUNIT____: 2:
|
||||
; IS__TUNIT____-NEXT: unreachable
|
||||
@ -166,7 +166,7 @@ define internal i8* @f1(i8* readnone %0) local_unnamed_addr #0 {
|
||||
;
|
||||
; IS__CGSCC_OPM: Function Attrs: nofree noinline nosync nounwind readnone uwtable willreturn
|
||||
; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@f1
|
||||
; IS__CGSCC_OPM-SAME: (i8* noalias nofree noundef nonnull readnone returned align 8 dereferenceable(1) "no-capture-maybe-returned" [[TMP0:%.*]]) local_unnamed_addr [[ATTR2:#.*]] {
|
||||
; IS__CGSCC_OPM-SAME: (i8* noalias nofree nonnull readnone returned align 8 dereferenceable(1) "no-capture-maybe-returned" [[TMP0:%.*]]) local_unnamed_addr [[ATTR2:#.*]] {
|
||||
; IS__CGSCC_OPM-NEXT: br label [[TMP3:%.*]]
|
||||
; IS__CGSCC_OPM: 2:
|
||||
; IS__CGSCC_OPM-NEXT: unreachable
|
||||
@ -251,7 +251,7 @@ define align 4 i8* @test7() #0 {
|
||||
define internal i8* @f1b(i8* readnone %0) local_unnamed_addr #0 {
|
||||
; IS__CGSCC_OPM: Function Attrs: nofree noinline nosync nounwind readnone uwtable willreturn
|
||||
; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@f1b
|
||||
; IS__CGSCC_OPM-SAME: (i8* noalias nofree noundef nonnull readnone returned align 8 dereferenceable(1) "no-capture-maybe-returned" [[TMP0:%.*]]) local_unnamed_addr [[ATTR2]] {
|
||||
; IS__CGSCC_OPM-SAME: (i8* noalias nofree nonnull readnone returned align 8 dereferenceable(1) "no-capture-maybe-returned" [[TMP0:%.*]]) local_unnamed_addr [[ATTR2]] {
|
||||
; IS__CGSCC_OPM-NEXT: br label [[TMP3:%.*]]
|
||||
; IS__CGSCC_OPM: 2:
|
||||
; IS__CGSCC_OPM-NEXT: unreachable
|
||||
|
@ -62,6 +62,65 @@ define void @callback_caller() {
|
||||
ret void
|
||||
}
|
||||
|
||||
; Drop the noundef if when we replace the call argument with `undef`. We use a
|
||||
; varargs function as we cannot (yet) rewrite their signature. If we ever can,
|
||||
; try to come up with a different scheme to verify the `noundef` is dropped if
|
||||
; signature rewriting is not happening.
|
||||
define internal void @callee_with_dead_noundef_arg(i1 noundef %create, ...) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@callee_with_dead_noundef_arg
|
||||
; CHECK-SAME: (i1 [[CREATE:%.*]], ...) {
|
||||
; CHECK-NEXT: call void @unknown()
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
call void @unknown()
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @caller_with_unused_arg(i1 %c) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@caller_with_unused_arg
|
||||
; CHECK-SAME: (i1 [[C:%.*]]) {
|
||||
; CHECK-NEXT: call void (i1, ...) @callee_with_dead_noundef_arg(i1 undef)
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
call void (i1, ...) @callee_with_dead_noundef_arg(i1 %c)
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @callee_with_dead_arg(i1 %create, ...) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@callee_with_dead_arg
|
||||
; CHECK-SAME: (i1 [[CREATE:%.*]], ...) {
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br label [[IF_THEN3:%.*]]
|
||||
; CHECK: if.then:
|
||||
; CHECK-NEXT: unreachable
|
||||
; CHECK: if.then3:
|
||||
; CHECK-NEXT: call void @unknown()
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
entry:
|
||||
br i1 %create, label %if.then3, label %if.then
|
||||
|
||||
if.then: ; preds = %entry
|
||||
ret void
|
||||
|
||||
if.then3: ; preds = %entry
|
||||
call void @unknown()
|
||||
ret void
|
||||
}
|
||||
|
||||
; Drop the noundef if when we replace the call argument with `undef`. We use a
|
||||
; varargs function as we cannot (yet) rewrite their signature. If we ever can,
|
||||
; try to come up with a different scheme to verify the `noundef` is dropped if
|
||||
; signature rewriting is not happening.
|
||||
define void @caller_with_noundef_arg() {
|
||||
; CHECK-LABEL: define {{[^@]+}}@caller_with_noundef_arg() {
|
||||
; CHECK-NEXT: call void (i1, ...) @callee_with_dead_arg(i1 undef)
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
call void (i1, ...) @callee_with_dead_arg(i1 noundef true)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare !callback !0 void @callback_broker(void (i8*)*, i8*)
|
||||
!1 = !{i64 0, i64 1, i1 false}
|
||||
!0 = !{!1}
|
||||
|
Loading…
Reference in New Issue
Block a user