Alive-NJ is a reimplementation of the Automated LLVM's InstCombine Verifier, written by Nuno Lopes, David Menendez, Santosh Nagarakatte, and John Regehr and detailed in the paper "Provably Correct Peephole Optimizations with Alive", presented at PLDI 2015.
Alive-NJ is intended to facilitate experimentation with Alive semantics and extension of Alive into new areas.
Alive requires Python 2.7 and Z3 4.3.2 or later.
Z3 can be obtained from https://github.com/Z3Prover/z3
To verify all the optimizations in a file:
./run.py [file [file...]]
Alive-NJ reads from standard input if no arguments are given.
To get a list of options:
./run.py --help
Alive-NJ adds these features:
- Support for floating-point
half,float,double,fp128, andx86_fp80types- Instructions:
fadd,fsub,fmul,fdiv,frem,fcmp,fptosi,fptoui,sitofp,uitofp,fpext,fptrunc - Symbolic constants, integer literals, and expressions using
+,-,*,/, and%may be integer or floating point - Floating-point literals
- Special values
nan,inf,-inf, and-0.0 - Precondition comparisons use IEEE semantics for floats (thus,
C == 0.0is satisfied whenCis positive or negative zero, andC == nanis never satisfied) - Predicate
fpsame(C1,C2)is satisfied whenC1andC2are structurally equal (meaningfpsame(nan,nan)is true, butfpsame(0.0, -0.0)is not)
- Full replaceability of
undef: If%xisundef, thenxor %x, %xisundef - New constant symbols may be defined in the target, for example,
C2 = trunc(C1). These symbols are in scope in the precondition and target, sozext(C2) == C1is a valid precondition. Note that, unliketrunc(C1), all uses ofC2will have the same type. - Checks for compile-time undefined behavior. For example, a precondition
C1 % C2 == 0will be rejected unlessC2is guaranteed to be nonzero. - An explicit
poisonvalue. - Support for the recently-proposed
freezeinstruction. - Choice of semantics for verification, using the
--translatoroption. Available translators include:smtundefUsesundefwhen the conditions of fast-math attributes are violated.smtpoisonUsespoisonwhen the conditions of fast-math attributes are violated.poisononlyAllows thefreezeinstruction, and preventspoisonfrom propagating through the unchosen branch of aselectinstruction.
We have found the following bugs with the floating point support in Alive-NJ:
- https://llvm.org/bugs/show_bug.cgi?id=26863
- https://llvm.org/bugs/show_bug.cgi?id=27151
- https://llvm.org/bugs/show_bug.cgi?id=27153
- https://llvm.org/bugs/show_bug.cgi?id=26862
- https://llvm.org/bugs/show_bug.cgi?id=26746
Alive-NJ does not include, or does not fully implement, these features:
- C++ code generation
- Flag inference
- Memory operations (
alloca,store,load,getelementpointer) - Pointer types
- Composition of optimizations and non-termination checking
Alive-NJ includes a tool for inferring preconditions for Alive optimizations, detailed in the paper "Alive-Infer: Data-Driven Precondition Inference for Peephole Optimizations in LLVM". You might use this tool if an optimization you have developed is invalid, and you need to find a stronger precondition, or if you want to weaken the precondition of an optimization so that it can be used on more programs.
To infer preconditions for all optimizations given in a file:
./infer.py [file [file...]]
Alive-Infer reads from standard input if no files are given.
To get a list of options:
./infer.py --help
Most options can be negated. For example, --incompletes vs --no-incompletes.
In case of a conflict, the last option wins.
Alive-Infer only returns preconditions which are valid, meaning they reject all input programs where the optimization would change the semantics. Alive-Infer attempts to find preconditions which accept all input programs where the optimization is valid. This may result in too-complex preconditions, or require too much time to run.
If --incompletes is set, Alive-Infer will also generate valid and succinct
preconditions which may exclude some input programs where the optimization is
valid.
Alive-Infer extends the Alive language with headers which provide more information to the inference engine. To illustrate:
Name: AndOrXor:1628-1
Feature: isPowerOf2(-C2 ^ -C1)
Feature: -C2 ^ -C1 == (C3-C2) ^ (C3-C1)
Feature: abs(C1-C2) u> C3
Assume: C1 != 0 && C2 != 0
Pre: C1 u> C3 && C2 u> C3 && isPowerOf2(C1 ^ C2)
%a1 = add i29 %A, C1
%a2 = add %A, C2
%cmp1 = icmp ult %a1, C3
%cmp2 = icmp ult %a2, C3
%r = or %cmp1, %cmp2
=>
%newand = and %A, ~(C1^C2)
%newadd = add %newand, umax(C1, C2)
%r = icmp ult %newadd, C3
Feature: headers suggest predicates to the inference engine. Use
--no-features to ignore these headers.
Assume: headers indicate conditions that should never occur. The
precondition is not required to accept or reject input programs which violate
the assumptions. Use --no-assumptions to ignore these headers.
Pre: headers are normally ignored during inference. However, certain options
tell Alive-Infer to use this specified precondition:
- If
--pre-featuresis set, Alive-Infer will treat the predicates inPre:as if they had been suggested usingFeature:. - If
--assume-preis set, Alive-Infer will treatPre:as if it wereAssume:. - If
--strengthenis set, Alive-Infer will attempt to find a precondition which makes the optimization valid and implies the given precondition.
If you find yourself using the same options frequently, you can customize
infer.py by creating a copy and adding keyword arguments to its call to
main().
For example, to make --pre-features set by default, use:
main(pre_features = True)
Alive-Infer does not infer predicates involving dataflow analysis, such as
WillNotOverflowSignedAdd or MaskedValueIsZero, or type casts, such as
zext and trunc. Alive-Infer does not currently support floating-point.