mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 13:56:07 +00:00
441 lines
14 KiB
ReStructuredText
441 lines
14 KiB
ReStructuredText
======================================
|
|
LLVM IR Undefined Behavior (UB) Manual
|
|
======================================
|
|
|
|
.. contents::
|
|
:local:
|
|
:depth: 2
|
|
|
|
Abstract
|
|
========
|
|
This document describes the undefined behavior (UB) in LLVM's IR, including
|
|
undef and poison values, as well as the ``freeze`` instruction.
|
|
We also provide guidelines on when to use each form of UB.
|
|
|
|
|
|
Introduction
|
|
============
|
|
Undefined behavior (UB) is used to specify the behavior of corner cases for
|
|
which we don't wish to specify the concrete results. UB is also used to provide
|
|
additional constraints to the optimizers (e.g., assumptions that the frontend
|
|
guarantees through the language type system or the runtime).
|
|
For example, we could specify the result of division by zero as zero, but
|
|
since we are not really interested in the result, we say it is UB.
|
|
|
|
There exist two forms of undefined behavior in LLVM: immediate UB and deferred
|
|
UB. The latter comes in two flavors: undef and poison values.
|
|
There is also a ``freeze`` instruction to tame the propagation of deferred UB.
|
|
The lattice of values in LLVM is:
|
|
immediate UB > poison > undef > freeze(poison) > concrete value.
|
|
|
|
We explain each of the concepts in detail below.
|
|
|
|
|
|
Immediate UB
|
|
============
|
|
Immediate UB is the most severe form of UB. It should be avoided whenever
|
|
possible.
|
|
Immediate UB should be used only for operations that trap in most CPUs supported
|
|
by LLVM.
|
|
Examples include division by zero, dereferencing a null pointer, etc.
|
|
|
|
The reason that immediate UB should be avoided is that it makes optimizations
|
|
such as hoisting a lot harder.
|
|
Consider the following example:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i32 @f(i1 %c, i32 %v) {
|
|
br i1 %c, label %then, label %else
|
|
|
|
then:
|
|
%div = udiv i32 3, %v
|
|
br label %ret
|
|
|
|
else:
|
|
br label %ret
|
|
|
|
ret:
|
|
%r = phi i32 [ %div, %then ], [ 0, %else ]
|
|
ret i32 %r
|
|
}
|
|
|
|
We might be tempted to simplify this function by removing the branching and
|
|
executing the division speculatively because ``%c`` is true most of times.
|
|
We would obtain the following IR:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i32 @f(i1 %c, i32 %v) {
|
|
%div = udiv i32 3, %v
|
|
%r = select i1 %c, i32 %div, i32 0
|
|
ret i32 %r
|
|
}
|
|
|
|
However, this transformation is not correct! Since division triggers UB
|
|
when the divisor is zero, we can only execute speculatively if we are sure we
|
|
don't hit that condition.
|
|
The function above, when called as ``f(false, 0)``, would return 0 before the
|
|
optimization, and triggers UB after being optimized.
|
|
|
|
This example highlights why we minimize the cases that trigger immediate UB
|
|
as much as possible.
|
|
As a rule of thumb, use immediate UB only for the cases that trap the CPU for
|
|
most of the supported architectures.
|
|
|
|
|
|
Time Travel
|
|
-----------
|
|
Immediate UB in LLVM IR allows the so-called time travelling. What this means
|
|
is that if a program triggers UB, then we are not required to preserve any of
|
|
its observable behavior, including I/O.
|
|
For example, the following function triggers UB after calling ``printf``:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define void @fn() {
|
|
call void @printf(...) willreturn
|
|
unreachable
|
|
}
|
|
|
|
Since we know that ``printf`` will always return, and because LLVM's UB can
|
|
time-travel, it is legal to remove the call to ``printf`` altogether and
|
|
optimize the function to simply:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define void @fn() {
|
|
unreachable
|
|
}
|
|
|
|
|
|
Deferred UB
|
|
===========
|
|
Deferred UB is a lighter form of UB. It enables instructions to be executed
|
|
speculatively while marking some corner cases as having erroneous values.
|
|
Deferred UB should be used for cases where the semantics offered by common
|
|
CPUs differ, but the CPU does not trap.
|
|
|
|
As an example, consider the shift instructions. The x86 and ARM architectures
|
|
offer different semantics when the shift amount is equal to or greater than
|
|
the bitwidth.
|
|
We could solve this tension in one of two ways: 1) pick one of the x86/ARM
|
|
semantics for LLVM, which would make the code emitted for the other architecture
|
|
slower; 2) define that case as yielding ``poison``.
|
|
LLVM chose the latter option. For frontends for languages like C or C++
|
|
(e.g., clang), they can map shifts in the source program directly to a shift in
|
|
LLVM IR, since the semantics of C and C++ define such shifts as UB.
|
|
For languages that offer strong semantics, they must use the value of the shift
|
|
conditionally, e.g.:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i32 @x86_shift(i32 %a, i32 %b) {
|
|
%mask = and i32 %b, 31
|
|
%shift = shl i32 %a, %mask
|
|
ret i32 %shift
|
|
}
|
|
|
|
|
|
There are two deferred UB values in LLVM: ``undef`` and ``poison``, which we
|
|
describe next.
|
|
|
|
|
|
Undef Values
|
|
------------
|
|
.. warning::
|
|
Undef values are deprecated and should be used only when strictly necessary.
|
|
Uses of undef values should be restricted to representing loads of
|
|
uninitialized memory. This is the only part of the IR semantics that cannot
|
|
be replaced with alternatives yet (work in ongoing).
|
|
|
|
An undef value represents any value of a given type. Moreover, each use of
|
|
an instruction that depends on undef can observe a different value.
|
|
For example:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i32 @fn() {
|
|
%add = add i32 undef, 0
|
|
%ret = add i32 %add, %add
|
|
ret i32 %ret
|
|
}
|
|
|
|
Unsurprisingly, the first addition yields ``undef``.
|
|
However, the result of the second addition is more subtle. We might be tempted
|
|
to think that it yields an even number. But it might not be!
|
|
Since each (transitive) use of ``undef`` can observe a different value,
|
|
the second addition is equivalent to ``add i32 undef, undef``, which is
|
|
equivalent to ``undef``.
|
|
Hence, the function above is equivalent to:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i32 @fn() {
|
|
ret i32 undef
|
|
}
|
|
|
|
Each call to this function may observe a different value, namely any 32-bit
|
|
number (even and odd).
|
|
|
|
Because each use of undef can observe a different value, some optimizations
|
|
are wrong if we are not sure a value is not undef.
|
|
Consider a function that multiplies a number by 2:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i32 @fn(i32 %v) {
|
|
%mul2 = mul i32 %v, 2
|
|
ret i32 %mul2
|
|
}
|
|
|
|
This function is guaranteed to return an even number, even if ``%v`` is
|
|
undef.
|
|
However, as we've seen above, the following function does not:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i32 @fn(i32 %v) {
|
|
%mul2 = add i32 %v, %v
|
|
ret i32 %mul2
|
|
}
|
|
|
|
This optimization is wrong just because undef values exist, even if they are
|
|
not used in this part of the program as LLVM has no way to tell if ``%v`` is
|
|
undef or not.
|
|
|
|
Looking at the value lattice, ``undef`` values can only be replaced with either
|
|
a ``freeze`` instruction or a concrete value.
|
|
A consequence is that giving undef as an operand to an instruction that triggers
|
|
UB for some values of that operand makes the program UB. For example,
|
|
``udiv %x, undef`` is UB since we replace undef with 0 (``udiv %x, 0``),
|
|
becoming obvious that it is UB.
|
|
|
|
|
|
Poison Values
|
|
-------------
|
|
Poison values are a stronger form of deferred UB than undef. They still
|
|
allow instructions to be executed speculatively, but they taint the whole
|
|
expression DAG (with some exceptions), akin to floating point NaN values.
|
|
|
|
Example:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i32 @fn(i32 %a, i32 %b, i32 %c) {
|
|
%add = add nsw i32 %a, %b
|
|
%ret = add nsw i32 %add, %c
|
|
ret i32 %ret
|
|
}
|
|
|
|
The ``nsw`` attribute in the additions indicates that the operation yields
|
|
poison if there is a signed overflow.
|
|
If the first addition overflows, ``%add`` is poison and thus ``%ret`` is also
|
|
poison since it taints the whole expression DAG.
|
|
|
|
Poison values can be replaced with any value of type (undef, concrete values,
|
|
or a ``freeze`` instruction).
|
|
|
|
|
|
Propagation of Poison Through Select
|
|
------------------------------------
|
|
Most instructions return poison if any of their inputs is poison.
|
|
A notable exception is the ``select`` instruction, which is poison if and
|
|
only if the condition is poison or the selected value is poison.
|
|
This means that ``select`` acts as a barrier for poison propagation, which
|
|
impacts which optimizations can be performed.
|
|
|
|
For example, consider the following function:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i1 @fn(i32 %x, i32 %y) {
|
|
%cmp1 = icmp ne i32 %x, 0
|
|
%cmp2 = icmp ugt i32 %x, %y
|
|
%and = select i1 %cmp1, i1 %cmp2, i1 false
|
|
ret i1 %and
|
|
}
|
|
|
|
It is not correct to optimize the ``select`` into an ``and`` because when
|
|
``%cmp1`` is false, the ``select`` is only poison if ``%x`` is poison, while
|
|
the ``and`` below is poison if either ``%x`` or ``%y`` are poison.
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i1 @fn(i32 %x, i32 %y) {
|
|
%cmp1 = icmp ne i32 %x, 0
|
|
%cmp2 = icmp ugt i32 %x, %y
|
|
%and = and i1 %cmp1, %cmp2 ;; poison if %x or %y are poison
|
|
ret i1 %and
|
|
}
|
|
|
|
However, the optimization is possible if all operands of the values are used in
|
|
the condition (notice the flipped operands in the ``select``):
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i1 @fn(i32 %x, i32 %y) {
|
|
%cmp1 = icmp ne i32 %x, 0
|
|
%cmp2 = icmp ugt i32 %x, %y
|
|
%and = select i1 %cmp2, i1 %cmp1, i1 false
|
|
; ok to replace with:
|
|
%and = and i1 %cmp1, %cmp2
|
|
ret i1 %and
|
|
}
|
|
|
|
|
|
The Freeze Instruction
|
|
======================
|
|
Both undef and poison values sometimes propagate too much down an expression
|
|
DAG. Undef values because each transitive use can observe a different value,
|
|
and poison values because they make the whole DAG poison.
|
|
There are some cases where it is important to stop such propagation.
|
|
This is where the ``freeze`` instruction comes in.
|
|
|
|
Take the following example function:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i32 @fn(i32 %n, i1 %c) {
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
%i = phi i32 [ 0, %entry ], [ %i2, %loop.end ]
|
|
%cond = icmp ule i32 %i, %n
|
|
br i1 %cond, label %loop.cont, label %exit
|
|
|
|
loop.cont:
|
|
br i1 %c, label %then, label %else
|
|
|
|
then:
|
|
...
|
|
br label %loop.end
|
|
|
|
else:
|
|
...
|
|
br label %loop.end
|
|
|
|
loop.end:
|
|
%i2 = add i32 %i, 1
|
|
br label %loop
|
|
|
|
exit:
|
|
...
|
|
}
|
|
|
|
Imagine we want to perform loop unswitching on the loop above since the branch
|
|
condition inside the loop is loop invariant.
|
|
We would obtain the following IR:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i32 @fn(i32 %n, i1 %c) {
|
|
entry:
|
|
br i1 %c, label %then, label %else
|
|
|
|
then:
|
|
%i = phi i32 [ 0, %entry ], [ %i2, %then.cont ]
|
|
%cond = icmp ule i32 %i, %n
|
|
br i1 %cond, label %then.cont, label %exit
|
|
|
|
then.cont:
|
|
...
|
|
%i2 = add i32 %i, 1
|
|
br label %then
|
|
|
|
else:
|
|
%i3 = phi i32 [ 0, %entry ], [ %i4, %else.cont ]
|
|
%cond = icmp ule i32 %i3, %n
|
|
br i1 %cond, label %else.cont, label %exit
|
|
|
|
else.cont:
|
|
...
|
|
%i4 = add i32 %i3, 1
|
|
br label %else
|
|
|
|
exit:
|
|
...
|
|
}
|
|
|
|
There is a subtle catch: when the function is called with ``%n`` being zero,
|
|
the original function did not branch on ``%c``, while the optimized one does.
|
|
Branching on a deferred UB value is immediate UB, hence the transformation is
|
|
wrong in general because ``%c`` may be undef or poison.
|
|
|
|
Cases like this need a way to tame deferred UB values. This is exactly what the
|
|
``freeze`` instruction is for!
|
|
When given a concrete value as argument, ``freeze`` is a no-op, returning the
|
|
argument as-is. When given an undef or poison value, ``freeze`` returns a
|
|
non-deterministic value of the type.
|
|
This is not the same as undef: the value returned by ``freeze`` is the same
|
|
for all users.
|
|
|
|
Branching on a value returned by ``freeze`` is always safe since it either
|
|
evaluates to true or false consistently.
|
|
We can make the loop unswitching optimization above correct as follows:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i32 @fn(i32 %n, i1 %c) {
|
|
entry:
|
|
%c2 = freeze i1 %c
|
|
br i1 %c2, label %then, label %else
|
|
|
|
|
|
Writing Tests Without Undefined Behavior
|
|
========================================
|
|
|
|
When writing tests, it is important to ensure that they don't trigger UB
|
|
unnecessarily. Some automated test reduces sometimes use undef or poison
|
|
values as dummy values, but this is considered a bad practice if this leads
|
|
to triggering UB.
|
|
|
|
For example, imagine that we want to write a test and we don't care about the
|
|
particular divisor value because our optimization kicks in regardless:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i32 @fn(i8 %a) {
|
|
%div = udiv i8 %a, poison
|
|
...
|
|
}
|
|
|
|
The issue with this test is that it triggers immediate UB. This prevents
|
|
verification tools like Alive from validating the correctness of the
|
|
optimization. Hence, it is considered a bad practice to have tests with
|
|
unnecessary immediate UB (unless that is exactly what the test is for).
|
|
The test above should use a dummy function argument instead of using poison:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i32 @fn(i8 %a, i8 %dummy) {
|
|
%div = udiv i8 %a, %dummy
|
|
...
|
|
}
|
|
|
|
Common sources of immediate UB in tests include branching on undef/poison
|
|
conditions and dereferencing undef/poison/null pointers.
|
|
|
|
.. note::
|
|
If you need a placeholder value to pass as an argument to an instruction
|
|
that may trigger UB, add a new argument to the function rather than using
|
|
undef or poison.
|
|
|
|
|
|
Summary
|
|
=======
|
|
Undefined behavior (UB) in LLVM IR consists of two well-defined concepts:
|
|
immediate and deferred UB (undef and poison values).
|
|
Passing deferred UB values to certain operations leads to immediate UB.
|
|
This can be avoided in some cases through the use of the ``freeze``
|
|
instruction.
|
|
|
|
The lattice of values in LLVM is:
|
|
immediate UB > poison > undef > freeze(poison) > concrete value.
|
|
It is only valid to transform values from the left to the right (e.g., a poison
|
|
value can be replaced with a concrete value, but not the other way around).
|
|
|
|
Undef is now deprecated and should be used only to represent loads of
|
|
uninitialized memory.
|