[Attributor] Check violation of returned position nonnull and noundef attribute in...
authorShinji Okumura <okuraofvegetable@gmail.com>
Fri, 7 Aug 2020 02:40:53 +0000 (11:40 +0900)
committerShinji Okumura <okuraofvegetable@gmail.com>
Fri, 7 Aug 2020 03:02:42 +0000 (12:02 +0900)
This patch is a follow up of D84733.
If a function has noundef attribute in returned position, instructions that return undef or poison value cause UB.

Reviewed By: jdoerfert

Differential Revision: https://reviews.llvm.org/D85178

llvm/lib/Transforms/IPO/AttributorAttributes.cpp
llvm/test/Transforms/Attributor/undefined_behavior.ll

index 89d359b..88195af 100644 (file)
@@ -2038,6 +2038,37 @@ struct AAUndefinedBehaviorImpl : public AAUndefinedBehavior {
       return true;
     };
 
+    auto InspectReturnInstForUB =
+        [&](Value &V, const SmallSetVector<ReturnInst *, 4> RetInsts) {
+          // Check if a return instruction always cause UB or not
+          // Note: It is guaranteed that the returned position of the anchor
+          //       scope has noundef attribute when this is called.
+
+          // When the returned position has noundef attriubte, UB occur in the
+          // following cases.
+          //   (1) Returned value is known to be undef.
+          //   (2) The value is known to be a null pointer and the returned
+          //       position has nonnull attribute (because the returned value is
+          //       poison).
+          // Note: This callback is not called for a dead returned value because
+          //       such values are ignored in
+          //       checkForAllReturnedValuesAndReturnedInsts.
+          bool FoundUB = false;
+          if (isa<UndefValue>(V)) {
+            FoundUB = true;
+          } else {
+            auto &NonNullAA = A.getAAFor<AANonNull>(
+                *this, IRPosition::returned(*getAnchorScope()));
+            if (NonNullAA.isKnownNonNull() && isa<ConstantPointerNull>(V))
+              FoundUB = true;
+          }
+
+          if (FoundUB)
+            for (ReturnInst *RI : RetInsts)
+              KnownUBInsts.insert(RI);
+          return true;
+        };
+
     A.checkForAllInstructions(InspectMemAccessInstForUB, *this,
                               {Instruction::Load, Instruction::Store,
                                Instruction::AtomicCmpXchg,
@@ -2046,6 +2077,13 @@ struct AAUndefinedBehaviorImpl : public AAUndefinedBehavior {
     A.checkForAllInstructions(InspectBrInstForUB, *this, {Instruction::Br},
                               /* CheckBBLivenessOnly */ true);
     A.checkForAllCallLikeInstructions(InspectCallSiteForUB, *this);
+
+    // If the returned position of the anchor scope has noundef attriubte, check
+    // all returned instructions.
+    // TODO: If AANoUndef is implemented, ask it here.
+    if (IRPosition::returned(*getAnchorScope()).hasAttr({Attribute::NoUndef}))
+      A.checkForAllReturnedValuesAndReturnInsts(InspectReturnInstForUB, *this);
+
     if (NoUBPrevSize != AssumedNoUBInsts.size() ||
         UBPrevSize != KnownUBInsts.size())
       return ChangeStatus::CHANGED;
index b5bf4c4..498b683 100644 (file)
@@ -580,7 +580,9 @@ define i32 @foo() {
   ret i32 %X
 }
 
-; Tests for nonnull attribute violation.
+; Tests for nonnull noundef attribute violation.
+;
+; Tests for argument position
 
 define void @arg_nonnull_1(i32* nonnull %a) {
 ; IS__TUNIT____: Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly
@@ -874,3 +876,131 @@ f:
 ret:
   ret void
 }
+
+; Tests for returned position
+
+define nonnull i32* @returned_nonnnull(i32 %c) {
+; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn
+; IS__TUNIT____-LABEL: define {{[^@]+}}@returned_nonnnull
+; IS__TUNIT____-SAME: (i32 [[C:%.*]])
+; IS__TUNIT____-NEXT:    switch i32 [[C]], label [[ONDEFAULT:%.*]] [
+; IS__TUNIT____-NEXT:    i32 0, label [[ONZERO:%.*]]
+; IS__TUNIT____-NEXT:    i32 1, label [[ONONE:%.*]]
+; IS__TUNIT____-NEXT:    ]
+; IS__TUNIT____:       onzero:
+; IS__TUNIT____-NEXT:    [[PTR:%.*]] = alloca i32, align 4
+; IS__TUNIT____-NEXT:    ret i32* [[PTR]]
+; IS__TUNIT____:       onone:
+; IS__TUNIT____-NEXT:    ret i32* null
+; IS__TUNIT____:       ondefault:
+; IS__TUNIT____-NEXT:    ret i32* undef
+;
+; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
+; IS__CGSCC____-LABEL: define {{[^@]+}}@returned_nonnnull
+; IS__CGSCC____-SAME: (i32 [[C:%.*]])
+; IS__CGSCC____-NEXT:    switch i32 [[C]], label [[ONDEFAULT:%.*]] [
+; IS__CGSCC____-NEXT:    i32 0, label [[ONZERO:%.*]]
+; IS__CGSCC____-NEXT:    i32 1, label [[ONONE:%.*]]
+; IS__CGSCC____-NEXT:    ]
+; IS__CGSCC____:       onzero:
+; IS__CGSCC____-NEXT:    [[PTR:%.*]] = alloca i32, align 4
+; IS__CGSCC____-NEXT:    ret i32* [[PTR]]
+; IS__CGSCC____:       onone:
+; IS__CGSCC____-NEXT:    ret i32* null
+; IS__CGSCC____:       ondefault:
+; IS__CGSCC____-NEXT:    ret i32* undef
+;
+  switch i32 %c, label %ondefault [ i32 0, label %onzero
+  i32 1, label %onone ]
+onzero:
+  %ptr = alloca i32
+  ret i32* %ptr
+onone:
+  ret i32* null
+ondefault:
+  ret i32* undef
+}
+
+define noundef i32* @returned_noundef(i32 %c) {
+; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn
+; IS__TUNIT____-LABEL: define {{[^@]+}}@returned_noundef
+; IS__TUNIT____-SAME: (i32 [[C:%.*]])
+; IS__TUNIT____-NEXT:    switch i32 [[C]], label [[ONDEFAULT:%.*]] [
+; IS__TUNIT____-NEXT:    i32 0, label [[ONZERO:%.*]]
+; IS__TUNIT____-NEXT:    i32 1, label [[ONONE:%.*]]
+; IS__TUNIT____-NEXT:    ]
+; IS__TUNIT____:       onzero:
+; IS__TUNIT____-NEXT:    [[PTR:%.*]] = alloca i32, align 4
+; IS__TUNIT____-NEXT:    ret i32* [[PTR]]
+; IS__TUNIT____:       onone:
+; IS__TUNIT____-NEXT:    ret i32* null
+; IS__TUNIT____:       ondefault:
+; IS__TUNIT____-NEXT:    unreachable
+;
+; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
+; IS__CGSCC____-LABEL: define {{[^@]+}}@returned_noundef
+; IS__CGSCC____-SAME: (i32 [[C:%.*]])
+; IS__CGSCC____-NEXT:    switch i32 [[C]], label [[ONDEFAULT:%.*]] [
+; IS__CGSCC____-NEXT:    i32 0, label [[ONZERO:%.*]]
+; IS__CGSCC____-NEXT:    i32 1, label [[ONONE:%.*]]
+; IS__CGSCC____-NEXT:    ]
+; IS__CGSCC____:       onzero:
+; IS__CGSCC____-NEXT:    [[PTR:%.*]] = alloca i32, align 4
+; IS__CGSCC____-NEXT:    ret i32* [[PTR]]
+; IS__CGSCC____:       onone:
+; IS__CGSCC____-NEXT:    ret i32* null
+; IS__CGSCC____:       ondefault:
+; IS__CGSCC____-NEXT:    unreachable
+;
+  switch i32 %c, label %ondefault [ i32 0, label %onzero
+  i32 1, label %onone ]
+onzero:
+  %ptr = alloca i32
+  ret i32* %ptr
+onone:
+  ret i32* null
+ondefault:
+  ret i32* undef
+}
+
+define nonnull noundef i32* @returned_nonnnull_noundef(i32 %c) {
+; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn
+; IS__TUNIT____-LABEL: define {{[^@]+}}@returned_nonnnull_noundef
+; IS__TUNIT____-SAME: (i32 [[C:%.*]])
+; IS__TUNIT____-NEXT:    switch i32 [[C]], label [[ONDEFAULT:%.*]] [
+; IS__TUNIT____-NEXT:    i32 0, label [[ONZERO:%.*]]
+; IS__TUNIT____-NEXT:    i32 1, label [[ONONE:%.*]]
+; IS__TUNIT____-NEXT:    ]
+; IS__TUNIT____:       onzero:
+; IS__TUNIT____-NEXT:    [[PTR:%.*]] = alloca i32, align 4
+; IS__TUNIT____-NEXT:    ret i32* [[PTR]]
+; IS__TUNIT____:       onone:
+; IS__TUNIT____-NEXT:    unreachable
+; IS__TUNIT____:       ondefault:
+; IS__TUNIT____-NEXT:    unreachable
+;
+; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
+; IS__CGSCC____-LABEL: define {{[^@]+}}@returned_nonnnull_noundef
+; IS__CGSCC____-SAME: (i32 [[C:%.*]])
+; IS__CGSCC____-NEXT:    switch i32 [[C]], label [[ONDEFAULT:%.*]] [
+; IS__CGSCC____-NEXT:    i32 0, label [[ONZERO:%.*]]
+; IS__CGSCC____-NEXT:    i32 1, label [[ONONE:%.*]]
+; IS__CGSCC____-NEXT:    ]
+; IS__CGSCC____:       onzero:
+; IS__CGSCC____-NEXT:    [[PTR:%.*]] = alloca i32, align 4
+; IS__CGSCC____-NEXT:    ret i32* [[PTR]]
+; IS__CGSCC____:       onone:
+; IS__CGSCC____-NEXT:    unreachable
+; IS__CGSCC____:       ondefault:
+; IS__CGSCC____-NEXT:    unreachable
+;
+  switch i32 %c, label %ondefault [ i32 0, label %onzero
+  i32 1, label %onone ]
+onzero:
+  %ptr = alloca i32
+  ret i32* %ptr
+onone:
+  ret i32* null
+ondefault:
+  ret i32* undef
+}