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:
Differences from Alive
Alive-NJ adds these features:
- Support for floating-point
- Symbolic constants, integer literals, and expressions using
%may be integer or floating point
- Floating-point literals
- Special values
- Precondition comparisons use IEEE semantics for floats (thus,
C == 0.0is satisfied when
Cis positive or negative zero, and
C == nanis never satisfied)
fpsame(C1,C2)is satisfied when
C2are structurally equal (meaning
fpsame(nan,nan)is true, but
fpsame(0.0, -0.0)is not)
- Full replaceability of
xor %x, %xis
- New constant symbols may be defined in the target, for example,
C2 = trunc(C1). These symbols are in scope in the precondition and target, so
zext(C2) == C1is a valid precondition. Note that, unlike
trunc(C1), all uses of
C2will have the same type.
- Checks for compile-time undefined behavior. For example, a precondition
C1 % C2 == 0will be rejected unless
C2is guaranteed to be nonzero.
- An explicit
- Support for the recently-proposed
- Choice of semantics for verification, using the
--translatoroption. Available translators include:
undefwhen the conditions of fast-math attributes are violated.
poisonwhen the conditions of fast-math attributes are violated.
freezeinstruction, and prevents
poisonfrom propagating through the unchosen branch of a
We have found the following bugs with the floating point support in Alive-NJ:
Alive-NJ does not include, or does not fully implement, these features:
- C++ code generation
- Flag inference
- Memory operations (
- 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:
Most options can be negated. For example,
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.
--incompletes is set, Alive-Infer will also generate valid and succinct
preconditions which may exclude some input programs where the optimization is
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:
--pre-featuresis set, Alive-Infer will treat the predicates in
Pre:as if they had been suggested using
--assume-preis set, Alive-Infer will treat
Pre:as if it were
--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
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
MaskedValueIsZero, or type casts, such as
trunc. Alive-Infer does not currently support floating-point.