[llvm] 8691544 - [SCCP] Use range metadata for loads and calls

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 7 12:11:32 PDT 2020


Author: Nikita Popov
Date: 2020-07-07T21:09:21+02:00
New Revision: 8691544a276744474ff04b71d7e220069435c7fe

URL: https://github.com/llvm/llvm-project/commit/8691544a276744474ff04b71d7e220069435c7fe
DIFF: https://github.com/llvm/llvm-project/commit/8691544a276744474ff04b71d7e220069435c7fe.diff

LOG: [SCCP] Use range metadata for loads and calls

When all else fails, use range metadata to constrain the result
of loads and calls. It should also be possible to use !nonnull,
but that would require some general support for inequalities in
SCCP first.

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

Added: 
    

Modified: 
    llvm/lib/Transforms/Scalar/SCCP.cpp
    llvm/test/Transforms/SCCP/metadata.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Scalar/SCCP.cpp b/llvm/lib/Transforms/Scalar/SCCP.cpp
index d28846d6e6de..8ba118291206 100644
--- a/llvm/lib/Transforms/Scalar/SCCP.cpp
+++ b/llvm/lib/Transforms/Scalar/SCCP.cpp
@@ -1104,11 +1104,21 @@ void SCCPSolver::visitStoreInst(StoreInst &SI) {
     TrackedGlobals.erase(I);      // No need to keep tracking this!
 }
 
+static ValueLatticeElement getValueFromMetadata(const Instruction *I) {
+  if (MDNode *Ranges = I->getMetadata(LLVMContext::MD_range))
+    if (I->getType()->isIntegerTy())
+      return ValueLatticeElement::getRange(
+          getConstantRangeFromMetadata(*Ranges));
+  // TODO: Also handle MD_nonnull.
+  return ValueLatticeElement::getOverdefined();
+}
+
 // Handle load instructions.  If the operand is a constant pointer to a constant
 // global, we can replace the load with the loaded constant value!
 void SCCPSolver::visitLoadInst(LoadInst &I) {
-  // If this load is of a struct, just mark the result overdefined.
-  if (I.getType()->isStructTy())
+  // If this load is of a struct or the load is volatile, just mark the result
+  // as overdefined.
+  if (I.getType()->isStructTy() || I.isVolatile())
     return (void)markOverdefined(&I);
 
   // ResolvedUndefsIn might mark I as overdefined. Bail out, even if we would
@@ -1122,41 +1132,39 @@ void SCCPSolver::visitLoadInst(LoadInst &I) {
 
   ValueLatticeElement &IV = ValueState[&I];
 
-  if (!isConstant(PtrVal) || I.isVolatile())
-    return (void)markOverdefined(IV, &I);
-
-  Constant *Ptr = getConstant(PtrVal);
-
-  // load null is undefined.
-  if (isa<ConstantPointerNull>(Ptr)) {
-    if (NullPointerIsDefined(I.getFunction(), I.getPointerAddressSpace()))
-      return (void)markOverdefined(IV, &I);
-    else
-      return;
-  }
+  if (isConstant(PtrVal)) {
+    Constant *Ptr = getConstant(PtrVal);
 
-  // Transform load (constant global) into the value loaded.
-  if (auto *GV = dyn_cast<GlobalVariable>(Ptr)) {
-    if (!TrackedGlobals.empty()) {
-      // If we are tracking this global, merge in the known value for it.
-      auto It = TrackedGlobals.find(GV);
-      if (It != TrackedGlobals.end()) {
-        mergeInValue(IV, &I, It->second, getMaxWidenStepsOpts());
+    // load null is undefined.
+    if (isa<ConstantPointerNull>(Ptr)) {
+      if (NullPointerIsDefined(I.getFunction(), I.getPointerAddressSpace()))
+        return (void)markOverdefined(IV, &I);
+      else
         return;
+    }
+
+    // Transform load (constant global) into the value loaded.
+    if (auto *GV = dyn_cast<GlobalVariable>(Ptr)) {
+      if (!TrackedGlobals.empty()) {
+        // If we are tracking this global, merge in the known value for it.
+        auto It = TrackedGlobals.find(GV);
+        if (It != TrackedGlobals.end()) {
+          mergeInValue(IV, &I, It->second, getMaxWidenStepsOpts());
+          return;
+        }
       }
     }
-  }
 
-  // Transform load from a constant into a constant if possible.
-  if (Constant *C = ConstantFoldLoadFromConstPtr(Ptr, I.getType(), DL)) {
-    if (isa<UndefValue>(C))
-      return;
-    return (void)markConstant(IV, &I, C);
+    // Transform load from a constant into a constant if possible.
+    if (Constant *C = ConstantFoldLoadFromConstPtr(Ptr, I.getType(), DL)) {
+      if (isa<UndefValue>(C))
+        return;
+      return (void)markConstant(IV, &I, C);
+    }
   }
 
-  // Otherwise we cannot say for certain what value this load will produce.
-  // Bail out.
-  markOverdefined(IV, &I);
+  // Fall back to metadata.
+  mergeInValue(&I, getValueFromMetadata(&I));
 }
 
 void SCCPSolver::visitCallBase(CallBase &CB) {
@@ -1171,10 +1179,13 @@ void SCCPSolver::handleCallOverdefined(CallBase &CB) {
   if (CB.getType()->isVoidTy())
     return;
 
+  // Always mark struct return as overdefined.
+  if (CB.getType()->isStructTy())
+    return (void)markOverdefined(&CB);
+
   // Otherwise, if we have a single return value case, and if the function is
   // a declaration, maybe we can constant fold it.
-  if (F && F->isDeclaration() && !CB.getType()->isStructTy() &&
-      canConstantFoldCallTo(&CB, F)) {
+  if (F && F->isDeclaration() && canConstantFoldCallTo(&CB, F)) {
     SmallVector<Constant *, 8> Operands;
     for (auto AI = CB.arg_begin(), E = CB.arg_end(); AI != E; ++AI) {
       if (AI->get()->getType()->isStructTy())
@@ -1202,8 +1213,8 @@ void SCCPSolver::handleCallOverdefined(CallBase &CB) {
     }
   }
 
-  // Otherwise, we don't know anything about this call, mark it overdefined.
-  return (void)markOverdefined(&CB);
+  // Fall back to metadata.
+  mergeInValue(&CB, getValueFromMetadata(&CB));
 }
 
 void SCCPSolver::handleCallArguments(CallBase &CB) {

diff  --git a/llvm/test/Transforms/SCCP/metadata.ll b/llvm/test/Transforms/SCCP/metadata.ll
index afc66df58676..43e4c59571e9 100644
--- a/llvm/test/Transforms/SCCP/metadata.ll
+++ b/llvm/test/Transforms/SCCP/metadata.ll
@@ -7,12 +7,10 @@ declare i32 @get_i32()
 define void @load_range(i32* %p) {
 ; CHECK-LABEL: @load_range(
 ; CHECK-NEXT:    [[V:%.*]] = load i32, i32* [[P:%.*]], align 4, !range !0
-; CHECK-NEXT:    [[C1:%.*]] = icmp ult i32 [[V]], 10
-; CHECK-NEXT:    call void @use(i1 [[C1]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[C2:%.*]] = icmp ult i32 [[V]], 9
 ; CHECK-NEXT:    call void @use(i1 [[C2]])
-; CHECK-NEXT:    [[C3:%.*]] = icmp ugt i32 [[V]], 9
-; CHECK-NEXT:    call void @use(i1 [[C3]])
+; 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
@@ -29,9 +27,26 @@ define void @load_range(i32* %p) {
   ret void
 }
 
+define i32 @load_range_single(i32* %p) {
+; CHECK-LABEL: @load_range_single(
+; CHECK-NEXT:    ret i32 0
+;
+  %v = load i32, i32* %p, !range !{i32 0, i32 1}
+  ret i32 %v
+}
+
+define i32 @load_range_single_volatile(i32* %p) {
+; CHECK-LABEL: @load_range_single_volatile(
+; CHECK-NEXT:    [[V:%.*]] = load volatile i32, i32* [[P:%.*]], align 4, !range !1
+; CHECK-NEXT:    ret i32 [[V]]
+;
+  %v = load volatile i32, i32* %p, !range !{i32 0, i32 1}
+  ret i32 %v
+}
+
 define void @load_nonnull(i32** %p) {
 ; CHECK-LABEL: @load_nonnull(
-; CHECK-NEXT:    [[V:%.*]] = load i32*, i32** [[P:%.*]], align 8, !nonnull !1
+; CHECK-NEXT:    [[V:%.*]] = load i32*, i32** [[P:%.*]], align 8, !nonnull !2
 ; CHECK-NEXT:    [[C1:%.*]] = icmp ne i32* [[V]], null
 ; CHECK-NEXT:    call void @use(i1 [[C1]])
 ; CHECK-NEXT:    ret void
@@ -45,12 +60,10 @@ define void @load_nonnull(i32** %p) {
 define void @call_range(i32* %p) {
 ; CHECK-LABEL: @call_range(
 ; CHECK-NEXT:    [[V:%.*]] = call i32 @get_i32(), !range !0
-; CHECK-NEXT:    [[C1:%.*]] = icmp ult i32 [[V]], 10
-; CHECK-NEXT:    call void @use(i1 [[C1]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[C2:%.*]] = icmp ult i32 [[V]], 9
 ; CHECK-NEXT:    call void @use(i1 [[C2]])
-; CHECK-NEXT:    [[C3:%.*]] = icmp ugt i32 [[V]], 9
-; CHECK-NEXT:    call void @use(i1 [[C3]])
+; 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
@@ -69,8 +82,7 @@ define void @call_range(i32* %p) {
 
 define internal i1 @ip_cmp_range(i32 %v) {
 ; CHECK-LABEL: @ip_cmp_range(
-; CHECK-NEXT:    [[C:%.*]] = icmp ult i32 [[V:%.*]], 10
-; CHECK-NEXT:    ret i1 [[C]]
+; CHECK-NEXT:    ret i1 undef
 ;
   %c = icmp ult i32 %v, 10
   ret i1 %c
@@ -80,7 +92,7 @@ define i1 @ip_load_range(i32* %p) {
 ; CHECK-LABEL: @ip_load_range(
 ; CHECK-NEXT:    [[V:%.*]] = load i32, i32* [[P:%.*]], align 4, !range !0
 ; CHECK-NEXT:    [[C:%.*]] = call i1 @ip_cmp_range(i32 [[V]])
-; CHECK-NEXT:    ret i1 [[C]]
+; CHECK-NEXT:    ret i1 true
 ;
   %v = load i32, i32* %p, !range !{i32 0, i32 10}
   %c = call i1 @ip_cmp_range(i32 %v)


        


More information about the llvm-commits mailing list