diff --git a/llvm/lib/Transforms/Scalar/SCCP.cpp b/llvm/lib/Transforms/Scalar/SCCP.cpp --- a/llvm/lib/Transforms/Scalar/SCCP.cpp +++ b/llvm/lib/Transforms/Scalar/SCCP.cpp @@ -1104,11 +1104,21 @@ 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 @@ ValueLatticeElement &IV = ValueState[&I]; - if (!isConstant(PtrVal) || I.isVolatile()) - return (void)markOverdefined(IV, &I); - - Constant *Ptr = getConstant(PtrVal); - - // load null is undefined. - if (isa(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(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(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(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(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(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 @@ 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 Operands; for (auto AI = CB.arg_begin(), E = CB.arg_end(); AI != E; ++AI) { if (AI->get()->getType()->isStructTy()) @@ -1202,8 +1213,8 @@ } } - // 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 --- a/llvm/test/Transforms/SCCP/metadata.ll +++ b/llvm/test/Transforms/SCCP/metadata.ll @@ -7,12 +7,10 @@ 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 @@ 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 @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 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 @@ ; 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)