Value Range Analysis of source code variables

Hello,

is there a way to query the range analysis results for source-code level variables?
For example, given the following piece of code:

static int a = 0;

int main(){
  int b = 42;
  if (b > 0){
    a = 2;
  }
  b = 1;
  return 0;
}

Is there a way to “ask” LLVM what the value ranges (or sets) of a and b are?
The result would be, assuming LLVM can figure this out, a = {0,2} and b = {1, 42} or a in [0,2] and b in [1,42].

If not, would it make sense to write a pass that directly queries the relevant passes and is run before machine code is emitted?

Thanks,
Theo

See the code here for intervals datastructure: llvm-project/ConstantRange.cpp at main · llvm/llvm-project · GitHub
And here for the code that does the analysis itself: llvm-project/LazyValueInfo.cpp at main · llvm/llvm-project · GitHub

Following up on these utilities (ConstantRange and LazyValueInfo): they’re for LLVM values not for source variables. I don’t think there is a direct way to answer such queries for source variables using LLVM (though you can always use debug info to map these analysis results back, I think). But maybe clang static analyzer can help you.

Thanks for the suggestions!

I wanted to test LazyValueInfo, but LazyValueInfoPrinter is an old pass manager pass (is it possible to run such passes with the new pm?). So I wrote a very simple wrapper around LazyValueAnalysis and registered it with the pass manager:

PreservedAnalyses LazyValueInfoPrinterNPM::run(Function &F, FunctionAnalysisManager &AM) {
  auto *LVI = &AM.getResult<LazyValueAnalysis>(F);
  auto &DTree = AM.getResult<DominatorTreeAnalysis>(F);
  LVI->printLVI(F, DTree, dbgs());
  return PreservedAnalyses::all();
}

It’s essentially the same as the original printer.

However if I run this on the following IR (generated with -O3) :

; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn uwtable
define dso_local i32 @foo(i32 noundef %0) local_unnamed_addr #0 {
  %2 = icmp sgt i32 %0, 7
  %3 = sub i32 3, %0
  %4 = select i1 %2, i32 %3, i32 5
  ret i32 %4
}

(source:

int foo(int a) {
  int b = 5;
  if (a > 7)
    b = -a + 3;

  return b;
}

)

I see no relevant output:

./bin/opt --passes="print-lvi" test.ll --debug-pass-manager  -S                                      
Running pass: VerifierPass on [module]
Running analysis: VerifierAnalysis on [module]
Running analysis: InnerAnalysisManagerProxy<llvm::FunctionAnalysisManager, llvm::Module> on [module]
Running pass: LazyValueInfoPrinterNPM on foo
Running analysis: LazyValueAnalysis on foo
Running analysis: AssumptionAnalysis on foo
Running analysis: TargetIRAnalysis on foo
Running analysis: TargetLibraryAnalysis on foo
Running analysis: DominatorTreeAnalysis on foo
Running pass: VerifierPass on [module]
Running pass: PrintModulePass on [module]
; ModuleID = 'test.ll'
source_filename = "test.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn uwtable
define dso_local i32 @foo(i32 noundef %0) local_unnamed_addr #0 {
  %2 = icmp sgt i32 %0, 7
  %3 = sub i32 3, %0
  %4 = select i1 %2, i32 %3, i32 5
  ret i32 %4
}
...

Am I supposed to generated the LVI in a different manner? Or is the input a bad example?

Thanks in advance!

It is a fine example. LVI can handle it.
It just seems your new printing pass is not working correctly, but I’m not an expert in the NPM to help with that. If you manage to get it working, please do submit a patch to replace the old printer. Thanks!

1 Like