-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[IPSCCP] Add range attribute handling. #86747
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -428,6 +428,13 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> { | |
return markConstant(ValueState[V], V, C); | ||
} | ||
|
||
/// markConstantRange - Mark the object as constant range with \p CR. If the | ||
/// object is not a constant range with the range \p CR, add it to the | ||
/// instruction work list so that the users of the instruction are updated | ||
/// later. | ||
bool markConstantRange(ValueLatticeElement &IV, Value *V, | ||
const ConstantRange &CR); | ||
|
||
// markOverdefined - Make a value be marked as "overdefined". If the | ||
// value is not already overdefined, add it to the overdefined instruction | ||
// work list so that the users of the instruction are updated later. | ||
|
@@ -788,6 +795,17 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> { | |
markOverdefined(ValueState[V], V); | ||
} | ||
|
||
void trackValueOfArgument(Argument *A) { | ||
if (A->getType()->isIntegerTy()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need the restriction to non-vectors here? IIRC ipsccp can handle vectors though it mostly doesn't. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I only assumed that vectors was not handled as there was a restriction to only non-vectors for the range metadata. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, we can relax this separately from this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tried to add a test for vector like below but was not optimized when I remove the check. But maybe I did something wrong.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. think that my test fails as ConstantVector is not handled in https://github.com/andjo403/llvm-project/blob/fa71860e1937b916758693e268d5a16021c8b5c3/llvm/include/llvm/Analysis/ValueLattice.h#L301-L339 |
||
if (std::optional<ConstantRange> Range = A->getRange()) { | ||
markConstantRange(ValueState[A], A, *Range); | ||
return; | ||
} | ||
} | ||
// Assume nothing about the incoming arguments without range. | ||
markOverdefined(A); | ||
} | ||
|
||
bool isStructLatticeConstant(Function *F, StructType *STy); | ||
|
||
Constant *getConstant(const ValueLatticeElement &LV, Type *Ty) const; | ||
|
@@ -873,6 +891,15 @@ bool SCCPInstVisitor::markConstant(ValueLatticeElement &IV, Value *V, | |
return true; | ||
} | ||
|
||
bool SCCPInstVisitor::markConstantRange(ValueLatticeElement &IV, Value *V, | ||
const ConstantRange &CR) { | ||
if (!IV.markConstantRange(CR)) | ||
return false; | ||
LLVM_DEBUG(dbgs() << "markConstantRange: " << CR << ": " << *V << '\n'); | ||
pushToWorkList(IV, V); | ||
return true; | ||
} | ||
|
||
bool SCCPInstVisitor::markOverdefined(ValueLatticeElement &IV, Value *V) { | ||
if (!IV.markOverdefined()) | ||
return false; | ||
|
@@ -1581,10 +1608,15 @@ void SCCPInstVisitor::visitStoreInst(StoreInst &SI) { | |
} | ||
|
||
static ValueLatticeElement getValueFromMetadata(const Instruction *I) { | ||
if (MDNode *Ranges = I->getMetadata(LLVMContext::MD_range)) | ||
if (I->getType()->isIntegerTy()) | ||
if (I->getType()->isIntegerTy()) { | ||
if (MDNode *Ranges = I->getMetadata(LLVMContext::MD_range)) | ||
return ValueLatticeElement::getRange( | ||
getConstantRangeFromMetadata(*Ranges)); | ||
|
||
if (const auto *CB = dyn_cast<CallBase>(I)) | ||
if (std::optional<ConstantRange> Range = CB->getRange()) | ||
return ValueLatticeElement::getRange(*Range); | ||
} | ||
if (I->hasMetadata(LLVMContext::MD_nonnull)) | ||
return ValueLatticeElement::getNot( | ||
ConstantPointerNull::get(cast<PointerType>(I->getType()))); | ||
|
@@ -2090,6 +2122,10 @@ const SmallPtrSet<Function *, 16> SCCPSolver::getMRVFunctionsTracked() { | |
|
||
void SCCPSolver::markOverdefined(Value *V) { Visitor->markOverdefined(V); } | ||
|
||
void SCCPSolver::trackValueOfArgument(Argument *V) { | ||
Visitor->trackValueOfArgument(V); | ||
} | ||
|
||
bool SCCPSolver::isStructLatticeConstant(Function *F, StructType *STy) { | ||
return Visitor->isStructLatticeConstant(F, STy); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py | ||
; RUN: opt < %s -passes=ipsccp -S | FileCheck %s | ||
|
||
declare void @use(i1) | ||
declare i32 @get_i32() | ||
|
||
define void @range_attribute(i32 range(i32 0, 10) %v) { | ||
; CHECK-LABEL: @range_attribute( | ||
; CHECK-NEXT: call void @use(i1 true) | ||
; CHECK-NEXT: [[C2:%.*]] = icmp ult i32 [[V:%.*]], 9 | ||
; CHECK-NEXT: call void @use(i1 [[C2]]) | ||
; CHECK-NEXT: call void @use(i1 false) | ||
; CHECK-NEXT: [[C4:%.*]] = icmp ugt i32 [[V]], 8 | ||
; CHECK-NEXT: call void @use(i1 [[C4]]) | ||
; CHECK-NEXT: ret void | ||
; | ||
%c1 = icmp ult i32 %v, 10 | ||
call void @use(i1 %c1) | ||
%c2 = icmp ult i32 %v, 9 | ||
call void @use(i1 %c2) | ||
%c3 = icmp ugt i32 %v, 9 | ||
call void @use(i1 %c3) | ||
%c4 = icmp ugt i32 %v, 8 | ||
call void @use(i1 %c4) | ||
ret void | ||
} | ||
|
||
define i32 @range_attribute_single(i32 range(i32 0, 1) %v) { | ||
; CHECK-LABEL: @range_attribute_single( | ||
; CHECK-NEXT: ret i32 0 | ||
; | ||
ret i32 %v | ||
} | ||
|
||
define void @call_range_attribute() { | ||
; CHECK-LABEL: @call_range_attribute( | ||
; CHECK-NEXT: [[V:%.*]] = call range(i32 0, 10) i32 @get_i32() | ||
; CHECK-NEXT: call void @use(i1 true) | ||
; CHECK-NEXT: [[C2:%.*]] = icmp ult i32 [[V]], 9 | ||
; CHECK-NEXT: call void @use(i1 [[C2]]) | ||
; CHECK-NEXT: call void @use(i1 false) | ||
; CHECK-NEXT: [[C4:%.*]] = icmp ugt i32 [[V]], 8 | ||
; CHECK-NEXT: call void @use(i1 [[C4]]) | ||
; CHECK-NEXT: ret void | ||
; | ||
%v = call range(i32 0, 10) i32 @get_i32() | ||
%c1 = icmp ult i32 %v, 10 | ||
call void @use(i1 %c1) | ||
%c2 = icmp ult i32 %v, 9 | ||
call void @use(i1 %c2) | ||
%c3 = icmp ugt i32 %v, 9 | ||
call void @use(i1 %c3) | ||
%c4 = icmp ugt i32 %v, 8 | ||
call void @use(i1 %c4) | ||
ret void | ||
} | ||
|
||
|
||
declare range(i32 0, 10) i32 @get_i32_in_range() | ||
|
||
define void @call_range_result() { | ||
; CHECK-LABEL: @call_range_result( | ||
; CHECK-NEXT: [[V:%.*]] = call i32 @get_i32_in_range() | ||
; CHECK-NEXT: call void @use(i1 true) | ||
; CHECK-NEXT: [[C2:%.*]] = icmp ult i32 [[V]], 9 | ||
; CHECK-NEXT: call void @use(i1 [[C2]]) | ||
; CHECK-NEXT: call void @use(i1 false) | ||
; CHECK-NEXT: [[C4:%.*]] = icmp ugt i32 [[V]], 8 | ||
; CHECK-NEXT: call void @use(i1 [[C4]]) | ||
; CHECK-NEXT: ret void | ||
; | ||
%v = call i32 @get_i32_in_range() | ||
%c1 = icmp ult i32 %v, 10 | ||
call void @use(i1 %c1) | ||
%c2 = icmp ult i32 %v, 9 | ||
call void @use(i1 %c2) | ||
%c3 = icmp ugt i32 %v, 9 | ||
call void @use(i1 %c3) | ||
%c4 = icmp ugt i32 %v, 8 | ||
call void @use(i1 %c4) | ||
ret void | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please also check that range() does not pessimize inter-procedural inference? I.e. if we get better info from call-sites, we should use it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added test for this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't quite what I was looking for, or maybe I'm misunderstanding the test. What I had in mind if a function with range(0, 10) that has icmp 5 and is only called with value 5, or something like that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok I thought that you was thinking of the test ip_load_range that exist for range metadata So I think I did kind of the opposite of the test you was thinking of, that the range is propagated in to a function without a range and you want a constant to be propagated in to a function with a argument that have a range if I understand correctly. |
||
|
||
define internal i1 @ip_cmp_range_attribute(i32 %v) { | ||
; CHECK-LABEL: @ip_cmp_range_attribute( | ||
; CHECK-NEXT: ret i1 undef | ||
; | ||
%c = icmp ult i32 %v, 10 | ||
ret i1 %c | ||
} | ||
|
||
define i1 @ip_range_attribute(i32 range(i32 0, 10) %v) { | ||
; CHECK-LABEL: @ip_range_attribute( | ||
; CHECK-NEXT: [[C:%.*]] = call i1 @ip_cmp_range_attribute(i32 [[V:%.*]]) | ||
; CHECK-NEXT: ret i1 true | ||
; | ||
%c = call i1 @ip_cmp_range_attribute(i32 %v) | ||
ret i1 %c | ||
} | ||
|
||
define internal i1 @ip_cmp_range_call(i32 %v) { | ||
; CHECK-LABEL: @ip_cmp_range_call( | ||
; CHECK-NEXT: ret i1 undef | ||
; | ||
%c = icmp ult i32 %v, 10 | ||
ret i1 %c | ||
} | ||
|
||
define i1 @ip_range_call() { | ||
; CHECK-LABEL: @ip_range_call( | ||
; CHECK-NEXT: [[V:%.*]] = call range(i32 0, 10) i32 @get_i32() | ||
; CHECK-NEXT: [[C:%.*]] = call i1 @ip_cmp_range_call(i32 [[V]]) | ||
; CHECK-NEXT: ret i1 true | ||
; | ||
%v = call range(i32 0, 10) i32 @get_i32() | ||
%c = call i1 @ip_cmp_range_call(i32 %v) | ||
ret i1 %c | ||
} | ||
|
||
define internal i1 @ip_cmp_range_result(i32 %v) { | ||
; CHECK-LABEL: @ip_cmp_range_result( | ||
; CHECK-NEXT: ret i1 undef | ||
; | ||
%c = icmp ult i32 %v, 10 | ||
ret i1 %c | ||
} | ||
|
||
define i1 @ip_range_result() { | ||
; CHECK-LABEL: @ip_range_result( | ||
; CHECK-NEXT: [[V:%.*]] = call range(i32 0, 10) i32 @get_i32() | ||
; CHECK-NEXT: [[C:%.*]] = call i1 @ip_cmp_range_result(i32 [[V]]) | ||
; CHECK-NEXT: ret i1 true | ||
; | ||
%v = call range(i32 0, 10) i32 @get_i32() | ||
%c = call i1 @ip_cmp_range_result(i32 %v) | ||
ret i1 %c | ||
} | ||
|
||
define internal i1 @ip_cmp_with_range_attribute(i32 range(i32 0, 10) %v) { | ||
; CHECK-LABEL: @ip_cmp_with_range_attribute( | ||
; CHECK-NEXT: ret i1 undef | ||
; | ||
%c = icmp eq i32 %v, 5 | ||
ret i1 %c | ||
} | ||
|
||
define i1 @ip_range_attribute_constant() { | ||
; CHECK-LABEL: @ip_range_attribute_constant( | ||
; CHECK-NEXT: [[C:%.*]] = call i1 @ip_cmp_with_range_attribute(i32 5) | ||
; CHECK-NEXT: ret i1 true | ||
; | ||
%c = call i1 @ip_cmp_with_range_attribute(i32 5) | ||
ret i1 %c | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand why it works with structs. Doesn't it only work with integer scalars?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as it calls the markOverdefined if it is not a integer with a range attribute and that function have that text I selected to have it for this function also.