[SCCP] Infer return attributes in SCCP as well (#106732)

We can infer the range/nonnull attributes in non-interprocedural SCCP as
well. The results may be better after the function has been simplified.
This commit is contained in:
Nikita Popov 2024-09-02 11:44:37 +02:00 committed by GitHub
parent 87d904871f
commit 24fe1d4fd6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 57 additions and 47 deletions

View File

@ -639,7 +639,7 @@ void test6(struct anon_struct *p, int index) {
p->array[index] = __builtin_dynamic_object_size(p->array, 1);
}
// SANITIZE-WITH-ATTR-LABEL: define dso_local i64 @test6_bdos(
// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, -3) i64 @test6_bdos(
// SANITIZE-WITH-ATTR-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR2]] {
// SANITIZE-WITH-ATTR-NEXT: entry:
// SANITIZE-WITH-ATTR-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
@ -649,7 +649,7 @@ void test6(struct anon_struct *p, int index) {
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = select i1 [[DOTINV]], i64 0, i64 [[TMP0]]
// SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP1]]
//
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local i64 @test6_bdos(
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, -3) i64 @test6_bdos(
// NO-SANITIZE-WITH-ATTR-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR2]] {
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
// NO-SANITIZE-WITH-ATTR-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
@ -955,7 +955,7 @@ void test10(struct union_of_fams *p, int index) {
p->bytes[index] = (unsigned char)__builtin_dynamic_object_size(p->bytes, 1);
}
// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -2147483648, 2147483648) i64 @test10_bdos(
// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test10_bdos(
// SANITIZE-WITH-ATTR-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR2]] {
// SANITIZE-WITH-ATTR-NEXT: entry:
// SANITIZE-WITH-ATTR-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
@ -964,7 +964,7 @@ void test10(struct union_of_fams *p, int index) {
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = zext nneg i32 [[NARROW]] to i64
// SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP0]]
//
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -2147483648, 2147483648) i64 @test10_bdos(
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test10_bdos(
// NO-SANITIZE-WITH-ATTR-SAME: ptr nocapture noundef readonly [[P:%.*]]) local_unnamed_addr #[[ATTR2]] {
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
// NO-SANITIZE-WITH-ATTR-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8

View File

@ -137,7 +137,7 @@ public:
const ValueLatticeElement &getLatticeValueFor(Value *V) const;
/// getTrackedRetVals - Get the inferred return value map.
const MapVector<Function *, ValueLatticeElement> &getTrackedRetVals();
const MapVector<Function *, ValueLatticeElement> &getTrackedRetVals() const;
/// getTrackedGlobals - Get and return the set of inferred initializers for
/// global variables.
@ -190,6 +190,8 @@ public:
bool removeNonFeasibleEdges(BasicBlock *BB, DomTreeUpdater &DTU,
BasicBlock *&NewUnreachableBB) const;
void inferReturnAttributes() const;
bool tryToReplaceWithConstant(Value *V);
// Helper to check if \p LV is either a constant or a constant

View File

@ -277,34 +277,10 @@ static bool runIPSCCP(
// whether other functions are optimizable.
SmallVector<ReturnInst*, 8> ReturnsToZap;
for (const auto &I : Solver.getTrackedRetVals()) {
Function *F = I.first;
const ValueLatticeElement &ReturnValue = I.second;
// If there is a known constant range for the return value, add range
// attribute to the return value.
if (ReturnValue.isConstantRange() &&
!ReturnValue.getConstantRange().isSingleElement()) {
// Do not add range metadata if the return value may include undef.
if (ReturnValue.isConstantRangeIncludingUndef())
continue;
// Take the intersection of the existing attribute and the inferred range.
ConstantRange CR = ReturnValue.getConstantRange();
if (F->hasRetAttribute(Attribute::Range))
CR = CR.intersectWith(F->getRetAttribute(Attribute::Range).getRange());
F->addRangeRetAttr(CR);
continue;
}
// Infer nonnull return attribute.
if (F->getReturnType()->isPointerTy() && ReturnValue.isNotConstant() &&
ReturnValue.getNotConstant()->isNullValue() &&
!F->hasRetAttribute(Attribute::NonNull)) {
F->addRetAttr(Attribute::NonNull);
continue;
}
if (F->getReturnType()->isVoidTy())
continue;
Solver.inferReturnAttributes();
for (const auto &[F, ReturnValue] : Solver.getTrackedRetVals()) {
assert(!F->getReturnType()->isVoidTy() &&
"should not track void functions");
if (SCCPSolver::isConstant(ReturnValue) || ReturnValue.isUnknownOrUndef())
findReturnsToZap(*F, ReturnsToZap, Solver);
}

View File

@ -24,6 +24,7 @@
#include "llvm/Analysis/DomTreeUpdater.h"
#include "llvm/Analysis/GlobalsModRef.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/ValueLatticeUtils.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constant.h"
@ -66,6 +67,11 @@ static bool runSCCP(Function &F, const DataLayout &DL,
DL, [TLI](Function &F) -> const TargetLibraryInfo & { return *TLI; },
F.getContext());
// While we don't do any actual inter-procedural analysis, still track
// return values so we can infer attributes.
if (canTrackReturnsInterprocedurally(&F))
Solver.addTrackedFunction(&F);
// Mark the first block of the function as being executable.
Solver.markBlockExecutable(&F.front());
@ -115,6 +121,8 @@ static bool runSCCP(Function &F, const DataLayout &DL,
if (!DeadBB->hasAddressTaken())
DTU.deleteBB(DeadBB);
Solver.inferReturnAttributes();
return MadeChanges;
}

View File

@ -354,6 +354,34 @@ bool SCCPSolver::removeNonFeasibleEdges(BasicBlock *BB, DomTreeUpdater &DTU,
return true;
}
void SCCPSolver::inferReturnAttributes() const {
for (const auto &[F, ReturnValue] : getTrackedRetVals()) {
// If there is a known constant range for the return value, add range
// attribute to the return value.
if (ReturnValue.isConstantRange() &&
!ReturnValue.getConstantRange().isSingleElement()) {
// Do not add range metadata if the return value may include undef.
if (ReturnValue.isConstantRangeIncludingUndef())
continue;
// Take the intersection of the existing attribute and the inferred range.
ConstantRange CR = ReturnValue.getConstantRange();
if (F->hasRetAttribute(Attribute::Range))
CR = CR.intersectWith(F->getRetAttribute(Attribute::Range).getRange());
F->addRangeRetAttr(CR);
continue;
}
// Infer nonnull return attribute.
if (F->getReturnType()->isPointerTy() && ReturnValue.isNotConstant() &&
ReturnValue.getNotConstant()->isNullValue() &&
!F->hasRetAttribute(Attribute::NonNull)) {
F->addRetAttr(Attribute::NonNull);
continue;
}
}
}
/// Helper class for SCCPSolver. This implements the instruction visitor and
/// holds all the state.
class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
@ -2168,7 +2196,7 @@ const ValueLatticeElement &SCCPSolver::getLatticeValueFor(Value *V) const {
}
const MapVector<Function *, ValueLatticeElement> &
SCCPSolver::getTrackedRetVals() {
SCCPSolver::getTrackedRetVals() const {
return Visitor->getTrackedRetVals();
}

View File

@ -2,7 +2,7 @@
; RUN: opt -O1 -S < %s | FileCheck %s
define i32 @testa(i32 %mul) {
; CHECK-LABEL: define range(i32 -65536, 65536) i32 @testa(
; CHECK-LABEL: define range(i32 -65536, 32768) i32 @testa(
; CHECK-SAME: i32 [[MUL:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: [[SHR:%.*]] = ashr i32 [[MUL]], 15
; CHECK-NEXT: [[SPEC_SELECT_I:%.*]] = tail call i32 @llvm.smin.i32(i32 [[SHR]], i32 32767)
@ -16,7 +16,7 @@ define i32 @testa(i32 %mul) {
}
define i32 @testb(i32 %mul) {
; CHECK-LABEL: define range(i32 -16777216, 16777216) i32 @testb(
; CHECK-LABEL: define range(i32 -128, 128) i32 @testb(
; CHECK-SAME: i32 [[MUL:%.*]]) local_unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: [[SHR102:%.*]] = ashr i32 [[MUL]], 7
; CHECK-NEXT: [[TMP1:%.*]] = tail call i32 @llvm.smax.i32(i32 [[SHR102]], i32 -128)

View File

@ -2,7 +2,7 @@
; RUN: opt -passes=sccp < %s -S | FileCheck %s
define i8 @ashr_to_lshr(i8 %x, i8 %y) {
; CHECK-LABEL: define i8 @ashr_to_lshr(
; CHECK-LABEL: define range(i8 0, -128) i8 @ashr_to_lshr(
; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
; CHECK-NEXT: [[P:%.*]] = and i8 [[X]], 127
; CHECK-NEXT: [[R:%.*]] = lshr exact i8 [[P]], [[Y]]
@ -14,7 +14,7 @@ define i8 @ashr_to_lshr(i8 %x, i8 %y) {
}
define i8 @sdiv_to_udiv(i8 %x, i8 %y) {
; CHECK-LABEL: define i8 @sdiv_to_udiv(
; CHECK-LABEL: define range(i8 0, -128) i8 @sdiv_to_udiv(
; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
; CHECK-NEXT: [[X1:%.*]] = and i8 [[X]], 127
; CHECK-NEXT: [[Y1:%.*]] = and i8 [[Y]], 127

View File

@ -100,7 +100,7 @@ end:
}
define <2 x i16> @phi_vector_merge1(i1 %c, <2 x i8> %a) {
; CHECK-LABEL: define <2 x i16> @phi_vector_merge1(
; CHECK-LABEL: define range(i16 2, 259) <2 x i16> @phi_vector_merge1(
; CHECK-SAME: i1 [[C:%.*]], <2 x i8> [[A:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: [[ZEXT:%.*]] = zext <2 x i8> [[A]] to <2 x i16>
@ -126,7 +126,7 @@ join:
}
define <2 x i16> @phi_vector_merge2(i1 %c, <2 x i8> %a) {
; CHECK-LABEL: define <2 x i16> @phi_vector_merge2(
; CHECK-LABEL: define range(i16 2, 259) <2 x i16> @phi_vector_merge2(
; CHECK-SAME: i1 [[C:%.*]], <2 x i8> [[A:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: [[ZEXT:%.*]] = zext <2 x i8> [[A]] to <2 x i16>

View File

@ -232,13 +232,9 @@ define i1 @ip_test_nonnull_caller(ptr %p) {
}
define ptr @ret_nonnull_pointer(ptr nonnull %p) {
; SCCP-LABEL: define ptr @ret_nonnull_pointer(
; SCCP-SAME: ptr nonnull [[P:%.*]]) {
; SCCP-NEXT: ret ptr [[P]]
;
; IPSCCP-LABEL: define nonnull ptr @ret_nonnull_pointer(
; IPSCCP-SAME: ptr nonnull [[P:%.*]]) {
; IPSCCP-NEXT: ret ptr [[P]]
; CHECK-LABEL: define nonnull ptr @ret_nonnull_pointer(
; CHECK-SAME: ptr nonnull [[P:%.*]]) {
; CHECK-NEXT: ret ptr [[P]]
;
ret ptr %p
}