Technical Documentation: llvm/llvm-project
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (README.md)
The LLVM Compiler Infrastructure
Welcome to the LLVM project!
This repository contains the source code for LLVM, a toolkit for the
construction of highly optimized compilers, optimizers, and run-time
environments.
The LLVM project has multiple components. The core of the project is
itself called "LLVM". This contains all of the tools, libraries, and header
files needed to process intermediate representations and convert them into
object files. Tools include an assembler, disassembler, bitcode analyzer, and
bitcode optimizer.
C-like languages use the Clang frontend. This
component compiles C, C++, Objective-C, and Objective-C++ code into LLVM bitcode
-- and from there into object files, using LLVM.
Other components include:
the libc++ C++ standard library,
the LLD linker, and more.
Getting the Source Code and Building LLVM
Consult the
Getting Started with LLVM
page for information on building and running LLVM.
For information on how to contribute to the LLVM project, please take a look at
the Contributing to LLVM guide.
Getting in touch
Join the LLVM Discourse forums, Discord
chat,
LLVM Office Hours or
Regular sync-ups.
The LLVM project has adopted a code of conduct for
participants to all modes of communication within the project.
Bolt Docs Index (`bolt/docs/index.md`)
BOLT
:hidden: true
BOLTAArch64OptimizationStatusBOLT is a post-link optimizer developed to speed up large applications.
It achieves the improvements by optimizing application’s code layout
based on execution profile gathered by sampling profiler, such as Linuxperf tool. An overview of the ideas implemented in BOLT along with a
discussion of its potential and current results is available in CGO’19
paper.
User Guides
:maxdepth: 1
GettingStarted
OptimizingClang
OptimizingLinux
HeatmapsReference
:maxdepth: 1
CommandLineArgumentReference
profiles
BOLTAArch64OptimizationStatusDesign Documentation
:maxdepth: 1
BAT
BinaryAnalysis
NewBackend
PointerAuthDesign
RuntimeLibraryClang Tools Extra Docs Index (`clang-tools-extra/docs/index.md`)
Introduction
Welcome to the clang-tools-extra project which contains extra tools built using
Clang's tooling APIs.
:maxdepth: 1
ReleaseNotesContents
:maxdepth: 2
clang-tidy/index
clang-include-fixer
clang-change-namespace
clang-reorder-fields
modularize
pp-trace
clangd <https://clangd.llvm.org/>
clang-doc
MaintainersDoxygen Documentation
The Doxygen documentation describes the internal software that makes up the
tools of clang-tools-extra, not the external use of these tools. The Doxygen
documentation contains no instructions about how to use the tools, only the APIs
that make up the software. For usage instructions, please see the user's guide
or reference manual for each tool.
:::{note}
This documentation is generated directly from the source code with doxygen.
Since the tools of clang-tools-extra are constantly under active
development, what you're about to read is out of date!
:::
Indices and tables
- {ref}
genindex - {ref}
search
Clang Docs DataFlowAnalysisIntro (`clang/docs/DataFlowAnalysisIntro.md`)
Data flow analysis: an informal introduction
Abstract
This document introduces data flow analysis in an informal way. The goal is to
give the reader an intuitive understanding of how it works, and show how it
applies to a range of refactoring and bug finding problems.
Data flow analysis is a well-established technique; it is described in many
papers, books, and videos. If you would like a more formal, or a more thorough
explanation of the concepts mentioned in this document, please refer to the
following resources:
- The Lattice article in Wikipedia.
- Videos on the PacketPrep YouTube channel that introduce lattices and the
necessary background information:
#20,
#21,
#22,
#23,
#24,
#25. - Introduction to Dataflow Analysis
- Introduction to abstract interpretation.
- Introduction to symbolic execution.
- Static Program Analysis by Anders Møller and Michael I. Schwartzbach.
- EXE: automatically generating inputs of death
(a paper that successfully applies symbolic execution to real-world
software).
Data flow analysis
The purpose of data flow analysis
Data flow analysis is a static analysis technique that proves facts about a
program or its fragment. It can make conclusions about all paths through the
program, while taking control flow into account and scaling to large programs.
The basic idea is propagating facts about the program through the edges of the
control flow graph (CFG) until a fixpoint is reached.
Sample problem and an ad-hoc solution
We would like to explain data flow analysis while discussing an example. Let's
imagine that we want to track possible values of an integer variable in our
program. Here is how a human could annotate the code:
void Example(int n) {
int x = 0;
// x is {0}
if (n > 0) {
x = 5;
// x is {5}
} else {
x = 42;
// x is {42}
}
// x is {5; 42}
print(x);
}We use sets of integers to represent possible values of x. Local variables
have unambiguous values between statements, so we annotate program points
between statements with sets of possible values.
Here is how we arrived at these annotations. Assigning a constant to x allows
us to make a conclusion that x can only have one value. When control flow from
the "then" and "else" branches joins, x can have either value.
Abstract algebra provides a nice formalism that models this kind of structure,
namely, a lattice. A join-semilattice is a partially ordered set, in which every
two elements have a least upper bound (called a join).
join(a, b) ⩾ a and join(a, b) ⩾ b and join(x, x) = xFor this problem we will use the lattice of subsets of integers, with set
inclusion relation as ordering and set union as a join.
Lattices are often represented visually as Hasse diagrams. Here is a Hasse
diagram for our lattice that tracks subsets of integers:
:::{figure} DataFlowAnalysisIntroImages/IntegerSetsInfiniteLattice.svg
:class: light-background
:alt: Hasse diagram for a lattice of integer sets
:::
Computing the join in the lattice corresponds to finding the lowest common
ancestor (LCA) between two nodes in its Hasse diagram. There is a vast amount of
literature on efficiently implementing LCA queries for a DAG, however Efficient
Implementation of Lattice Operations (1989)
(CiteSeerX,
doi) describes a scheme that
particularly well-suited for programmatic implementation.
Too much information and "top" values
Let's try to find the possible sets of values of x in a function that modifiesx in a loop:
void ExampleOfInfiniteSets() {
int x = 0; // x is {0}
while (condition()) {
x += 1; // x is {0; 1; 2; …}
}
print(x); // x is {0; 1; 2; …}
}We have an issue: x can have any value greater than zero; that's an infinite
set of values, if the program operated on mathematical integers. In C++ int is
limited by INT_MAX so technically we have a set {0; 1; …; INT_MAX} which is
still really big.
To make our analysis practical to compute, we have to limit the amount of
information that we track. In this case, we can, for example, arbitrarily limit
the size of sets to 3 elements. If at a certain program point x has more than
3 possible values, we stop tracking specific values at that program point.
Instead, we denote possible values of x with the symbol ⊤ (pronounced "top"
according to a convention in abstract algebra).
void ExampleOfTopWithALoop() {
int x = 0; // x is {0}
while (condition()) {
x += 1; // x is ⊤
}
print(x); // x is ⊤
}The statement "at this program point, x's possible values are ⊤" is
understood as "at this program point x can have any value because we have too
much information, or the information is conflicting".
Note that we can get more than 3 possible values even without a loop:
void ExampleOfTopWithoutLoops(int n) {
int x = 0; // x is {0}
switch(n) {
case 0: x = 1; break; // x is {1}
case 1: x = 9; break; // x is {9}
case 2: x = 7; break; // x is {7}
default: x = 3; break; // x is {3}
}
// x is ⊤
}Uninitialized variables and "bottom" values
When x is declared but not initialized, it has no possible values. We
represent this fact symbolically as ⊥ (pronounced "bottom").
void ExampleOfBottom() {
int x; // x is ⊥
x = 42; // x is {42}
print(x);
}Note that using values read from uninitialized variables is undefined behaviour
in C++. Generally, compilers and static analysis tools can assume undefined
behavior does not happen. We must model uninitialized variables only when we are
implementing a checker that specifically is trying to find uninitialized reads.
In this example we show how to model uninitialized variables only to demonstrate
the concept of "bottom", and how it applies to possible value analysis. We
describe an analysis that finds uninitialized reads in a section below.
A practical lattice that tracks sets of concrete values
Taking into account all corner cases covered above, we can put together a
lattice that we can use in practice to track possible values of integer
variables. This lattice represents sets of integers with 1, 2, or 3 elements, as
well as top and bottom. Here is a Hasse diagram for it:
:::{figure} DataFlowAnalysisIntroImages/IntegerSetsFiniteLattice.svg
:class: light-background
:alt: Hasse diagram for a lattice of integer sets
:::
Formalization
Let's consider a slightly more complex example, and think about how we can
compute the sets of possible values algorithmically.
void Example(int n) {
int x; // x is ⊥
if (n > 0) {
if (n == 42) {
x = 44; // x is {44}
} else {
x = 5; // x is {5}
}
print(x); // x is {44; 5}
} else {
x = n; // x is ⊤
}
print(x); // x is ⊤
}As humans, we understand the control flow from the program text. We used our
understanding of control flow to find program points where two flows join.
Formally, control flow is represented by a CFG (control flow graph):
:::{figure} DataFlowAnalysisIntroImages/CFGExample.svg
:class: light-background
:alt: CFG for the code above
:::
We can compute sets of possible values by propagating them through the CFG of
the function:
When
xis declared but not initialized, its possible values are{}. The
empty set plays the role of⊥in this lattice.When
xis assigned a concrete value, its possible set of values contains
just that specific value.When
xis assigned some unknown value, it can have any value. We represent
this fact as⊤.When two control flow paths join, we compute the set union of incoming
values (limiting the number of elements to 3, representing larger sets as⊤).
The sets of possible values are influenced by:
Statements, for example, assignments.
Joins in control flow, for example, ones that appear at the end of "if"
statements.
Effects of statements are modeled by what is formally known as a transfer
function. A transfer function takes two arguments: the statement, and the state
of x at the previous program point. It produces the state of x at the next
program point. For example, the transfer function for assignment ignores the
state at the previous program point:
// GIVEN: x is {42; 44}
x = 0;
// CONCLUSION: x is {0}The transfer function for + performs arithmetic on every set member:
// GIVEN: x is {42, 44}
x = x + 100;
// CONCLUSION: x is {142, 144}Effects of control flow are modeled by joining the knowledge from all
possible previous program points.
if (...) {
...
// GIVEN: x is {42}
} else {
...
// GIVEN: x is {44}
}
// CONCLUSION: x is {42; 44}// GIVEN: x is {42}
while (...) {
...
// GIVEN: x is {44}
}
// CONCLUSION: {42; 44}The predicate that we marked "given" is usually called a precondition, and the
conclusion is called a postcondition.
In terms of the CFG, we join the information from all predecessor basic blocks.
:::{figure} DataFlowAnalysisIntroImages/CFGJoinRule.svg
:class: light-background
:alt: Modeling the effects of a CFG basic block
:::
Putting it all together, to model the effects of a basic block we compute:
out = transfer(basic_block, join(in_1, in_2, ..., in_n))(Note that there are other ways to write this equation that produce higher
precision analysis results. The trick is to keep exploring the execution paths
separately and delay joining until later. However, we won't discuss those
variations here.)
To make a conclusion about all paths through the program, we repeat this
computation on all basic blocks until we reach a fixpoint. In other words, we
keep propagating information through the CFG until the computed sets of values
stop changing.
If the lattice has a finite height and transfer functions are monotonic the
algorithm is guaranteed to terminate. Each iteration of the algorithm can
change computed values only to larger values from the lattice. In the worst
case, all computed values become ⊤, which is not very useful, but at least the
analysis terminates at that point, because it can't change any of the values.
Fixpoint iteration can be optimised by only reprocessing basic blocks which had
one of their inputs changed on the previous iteration. This is typically
implemented using a worklist queue. With this optimisation the time complexity
becomes O(m * |L|), where m is the number of basic blocks in the CFG and|L| is the size of lattice used by the analysis.
Symbolic execution: a very short informal introduction
Symbolic values
In the previous example where we tried to figure out what values a variable can
have, the analysis had to be seeded with a concrete value. What if there are no
assignments of concrete values in the program? We can still deduce some
interesting information by representing unknown input values symbolically, and
computing results as symbolic expressions:
void PrintAbs(int x) {
int result;
if (x >= 0) {
result = x; // result is {x}
} else {
result = -x; // result is {-x}
}
print(result); // result is {x; -x}
}We can't say what specific value gets printed, but we know that it is either x
or -x.
Dataflow analysis is an instance of abstract interpretation, and does not dictate
how exactly the lattice and transfer functions should be designed, beyond the
necessary conditions for the analysis to converge. Nevertheless, we can use
symbolic execution ideas to guide our design of the lattice and transfer
functions: lattice values can be symbolic expressions, and transfer functions
can construct more complex symbolic expressions from symbolic expressions that
represent arguments. See this StackOverflow
discussion
for a further comparison of abstract interpretation and symbolic execution.
Flow condition
A human can say about the previous example that the function returns x whenx >= 0, and -x when x < 0. We can make this conclusion programmatically by
tracking a flow condition. A flow condition is a predicate written in terms of
the program state that is true at a specific program point regardless of the
execution path that led to this statement. For example, the flow condition for
the program point right before evaluating result = x is x >= 0.
If we enhance the lattice to be a set of pairs of values and predicates, the
dataflow analysis computes the following values:
void PrintAbs(int x) {
int result;
if (x >= 0) {
// Flow condition: x >= 0.
result = x; // result is {x if x >= 0}
} else {
// Flow condition: x < 0.
result = -x; // result is {-x if x < 0}
}
print(result); // result is {x if x >= 0; -x if x < 0}
}Of course, in a program with loops, symbolic expressions for flow conditions can
grow unbounded. A practical static analysis system must control this growth to
keep the symbolic representations manageable and ensure that the data flow
analysis terminates. For example, it can use a constraint solver to prune
impossible flow conditions, and/or it can abstract them, losing precision, after
their symbolic representations grow beyond some threshold. This is similar to
how we had to limit the sizes of computed sets of possible values to 3 elements.
Symbolic pointers
This approach proves to be particularly useful for modeling pointer values,
since we don't care about specific addresses but just want to give a unique
identifier to a memory location.
void ExampleOfSymbolicPointers(bool b) {
int x = 0; // x is {0}
int* ptr = &x; // x is {0} ptr is {&x}
if (b) {
*ptr = 42; // x is {42} ptr is {&x}
}
print(x); // x is {0; 42} ptr is {&x}
}Example: finding output parameters
Let's explore how data flow analysis can help with a problem that is hard to
solve with other tools in Clang.
Problem description
Output parameters are function parameters of pointer or reference type whose
pointee is completely overwritten by the function, and not read before it is
overwritten. They are common in pre-C++11 code due to the absence of move
semantics. In modern C++ output parameters are non-idiomatic, and return values
are used instead.
Imagine that we would like to refactor output parameters to return values to
modernize old code. The first step is to identify refactoring candidates through
static analysis.
For example, in the following code snippet the pointer c is an output
parameter:
struct Customer {
int account_id;
std::string name;
}
void GetCustomer(Customer *c) {
c->account_id = ...;
if (...) {
c->name = ...;
} else {
c->name = ...;
}
}We would like to refactor this code into:
Customer GetCustomer() {
Customer c;
c.account_id = ...;
if (...) {
c.name = ...;
} else {
c.name = ...;
}
return c;
}However, in the function below the parameter c is not an output parameter
because its field name is not overwritten on every path through the function.
void GetCustomer(Customer *c) {
c->account_id = ...;
if (...) {
c->name = ...;
}
}The code also cannot read the value of the parameter before overwriting it:
void GetCustomer(Customer *c) {
use(c->account_id);
c->name = ...;
c->account_id = ...;
}Functions that escape the pointer also block the refactoring:
Customer* kGlobalCustomer;
void GetCustomer(Customer *c) {
c->name = ...;
c->account_id = ...;
kGlobalCustomer = c;
}To identify a candidate function for refactoring, we need to do the following:
Find a function with a non-const pointer or reference parameter.
Find the definition of that function.
Prove that the function completely overwrites the pointee on all paths
before returning.Prove that the function reads the pointee only after overwriting it.
Prove that the function does not persist the pointer in a data structure
that is live after the function returns.
There are also requirements that all usage sites of the candidate function must
satisfy, for example, that function arguments do not alias, that users are not
taking the address of the function, and so on. Let's consider verifying usage
site conditions to be a separate static analysis problem.
Lattice design
To analyze the function body we can use a lattice which consists of normal
states and failure states. A normal state describes program points where we are
sure that no behaviors that block the refactoring have occurred. Normal states
keep track of all parameter's member fields that are known to be overwritten on
every path from function entry to the corresponding program point. Failure
states accumulate observed violations (unsafe reads and pointer escapes) that
block the refactoring.
In the partial order of the lattice failure states compare greater than normal
states, which guarantees that they "win" when joined with normal states. Order
between failure states is determined by inclusion relation on the set of
accumulated violations (lattice's ⩽ is ⊆ on the set of violations). Order
between normal states is determined by reversed inclusion relation on the set of
overwritten parameter's member fields (lattice's ⩽ is ⊇ on the set of
overwritten fields).
:::{figure} DataFlowAnalysisIntroImages/OutputParameterIdentificationLattice.svg
:class: light-background
:alt: Lattice for data flow analysis that identifies output parameters
:::
To determine whether a statement reads or writes a field we can implement
symbolic evaluation of DeclRefExprs, LValueToRValue casts, pointer
dereference operator and MemberExprs.
Using data flow results to identify output parameters
Let's take a look at how we use data flow analysis to identify an output
parameter. The refactoring can be safely done when the data flow algorithm
computes a normal state with all of the fields proven to be overwritten in the
exit basic block of the function.
struct Customer {
int account_id;
std::string name;
};
void GetCustomer(Customer* c) {
// Overwritten: {}
c->account_id = ...; // Overwritten: {c->account_id}
if (...) {
c->name = ...; // Overwritten: {c->account_id, c->name}
} else {
c->name = ...; // Overwritten: {c->account_id, c->name}
}
// Overwritten: {c->account_id, c->name}
}When the data flow algorithm computes a normal state, but not all fields are
proven to be overwritten we can't perform the refactoring.
void target(bool b, Customer* c) {
// Overwritten: {}
if (b) {
c->account_id = 42; // Overwritten: {c->account_id}
} else {
c->name = "Konrad"; // Overwritten: {c->name}
}
// Overwritten: {}
}Similarly, when the data flow algorithm computes a failure state, we also can't
perform the refactoring.
Customer* kGlobalCustomer;
void GetCustomer(Customer* c) {
// Overwritten: {}
c->account_id = ...; // Overwritten: {c->account_id}
if (...) {
print(c->name); // Unsafe read
} else {
kGlobalCustomer = c; // Pointer escape
}
// Unsafe read, Pointer escape
}Example: finding dead stores
Let's say we want to find redundant stores, because they indicate potential
bugs.
x = GetX();
x = GetY();The first store to x is never read, probably there is a bug.
The implementation of dead store analysis is very similar to output parameter
analysis: we need to track stores and loads, and find stores that were never
read.
Liveness analysis is a
generalization of this idea, which is often used to answer many related
questions, for example:
- finding dead stores,
- finding uninitialized variables,
- finding a good point to deallocate memory,
- finding out if it would be safe to move an object.
Example: definitive initialization
Definitive initialization proves that variables are known to be initialized when
read. If we find a variable which is read when not initialized then we generate
a warning.
void Init() {
int x; // x is uninitialized
if (cond()) {
x = 10; // x is initialized
} else {
x = 20; // x is initialized
}
print(x); // x is initialized
}void Uninit() {
int x; // x is uninitialized
if (cond()) {
x = 10; // x is initialized
}
print(x); // x is maybe uninitialized, x is being read, report a bug.
}For this purpose we can use lattice in a form of a mapping from variable
declarations to initialization states; each initialization state is represented
by the following lattice:
:::{figure} DataFlowAnalysisIntroImages/DefinitiveInitializationLattice.svg
:class: light-background
:alt: Lattice for definitive initialization analysis
:::
A lattice element could also capture the source locations of the branches that
lead us to the corresponding program point. Diagnostics would use this
information to show a sample buggy code path to the user.
Example: refactoring raw pointers to `unique_ptr`
Modern idiomatic C++ uses smart pointers to express memory ownership, however in
pre-C++11 code one can often find raw pointers that own heap memory blocks.
Imagine that we would like to refactor raw pointers that own memory tounique_ptr. There are multiple ways to design a data flow analysis for this
problem; let's look at one way to do it.
For example, we would like to refactor the following code that uses raw
pointers:
void UniqueOwnership1() {
int *pi = new int;
if (...) {
Borrow(pi);
delete pi;
} else {
TakeOwnership(pi);
}
}into code that uses unique_ptr:
void UniqueOwnership1() {
auto pi = std::make_unique<int>();
if (...) {
Borrow(pi.get());
} else {
TakeOwnership(pi.release());
}
}This problem can be solved with a lattice in form of map from value declarations
to pointer states:
:::{figure} DataFlowAnalysisIntroImages/UniquePtrLattice.svg
:class: light-background
:alt: Lattice that identifies candidates for unique_ptr refactoring
:::
We can perform the refactoring if at the exit of a function pi isCompatible.
void UniqueOwnership1() {
int *pi; // pi is Compatible
pi = new int; // pi is Defined
if (...) {
Borrow(pi); // pi is Defined
delete pi; // pi is Compatible
} else {
TakeOwnership(pi); // pi is Compatible
}
// pi is Compatible
}Let's look at an example where the raw pointer owns two different memory blocks:
void UniqueOwnership2() {
int *pi = new int; // pi is Defined
Borrow(pi);
delete pi; // pi is Compatible
if (smth) {
pi = new int; // pi is Defined
Borrow(pi);
delete pi; // pi is Compatible
}
// pi is Compatible
}It can be refactored to use unique_ptr like this:
void UniqueOwnership2() {
auto pi = make_unique<int>();
Borrow(pi);
if (smth) {
pi = make_unique<int>();
Borrow(pi);
}
}In the following example, the raw pointer is used to access the heap object
after the ownership has been transferred.
void UniqueOwnership3() {
int *pi = new int; // pi is Defined
if (...) {
Borrow(pi);
delete pi; // pi is Compatible
} else {
vector<unique_ptr<int>> v = {std::unique_ptr(pi)}; // pi is Compatible
print(*pi);
use(v);
}
// pi is Compatible
}We can refactor this code to use unique_ptr, however we would have to
introduce a non-owning pointer variable, since we can't use the moved-fromunique_ptr to access the object:
void UniqueOwnership3() {
std::unique_ptr<int> pi = std::make_unique<int>();
if (...) {
Borrow(pi);
} else {
int *pi_non_owning = pi.get();
vector<unique_ptr<int>> v = {std::move(pi)};
print(*pi_non_owning);
use(v);
}
}If the original code didn't call delete at the very end of the function, then
our refactoring may change the point at which we run the destructor and release
memory. Specifically, if there is some user code after delete, then extending
the lifetime of the object until the end of the function may hold locks for
longer than necessary, introduce memory overhead etc.
One solution is to always replace delete with a call to reset(), and then
perform another analysis that removes unnecessary reset() calls.
void AddedMemoryOverhead() {
HugeObject *ho = new HugeObject();
use(ho);
delete ho; // Release the large amount of memory quickly.
LongRunningFunction();
}This analysis will refuse to refactor code that mixes borrowed pointer values
and unique ownership. In the following code, GetPtr() returns a borrowed
pointer, which is assigned to pi. Then, pi is used to hold a uniquely-owned
pointer. We don't distinguish between these two assignments, and we want each
assignment to be paired with a corresponding sink; otherwise, we transition the
pointer to a Conflicting state, like in this example.
void ConflictingOwnership() {
int *pi; // pi is Compatible
pi = GetPtr(); // pi is Defined
Borrow(pi); // pi is Defined
pi = new int; // pi is Conflicting
Borrow(pi);
delete pi;
// pi is Conflicting
}We could still handle this case by finding a maximal range in the code wherepi could be in the Compatible state, and only refactoring that part.
void ConflictingOwnership() {
int *pi;
pi = GetPtr();
Borrow(pi);
std::unique_ptr<int> pi_unique = std::make_unique<int>();
Borrow(pi_unique.get());
}Example: finding redundant branch conditions
In the code below b1 should not be checked in both the outer and inner "if"
statements. It is likely there is a bug in this code.
int F(bool b1, bool b2) {
if (b1) {
f();
if (b1 && b2) { // Check `b1` again -- unnecessary!
g();
}
}
}A checker that finds this pattern syntactically is already implemented in
ClangTidy using AST matchers (bugprone-redundant-branch-condition).
To implement it using the data flow analysis framework, we can produce a warning
if any part of the branch condition is implied by the flow condition.
int F(bool b1, bool b2) {
// Flow condition: true.
if (b1) {
// Flow condition: b1.
f();
if (b1 && b2) { // `b1` is implied by the flow condition.
g();
}
}
}One way to check this implication is to use a SAT solver. Without a SAT solver,
we could keep the flow condition in the CNF form and then it would be easy to
check the implication.
Example: finding unchecked `std::optional` unwraps
Calling optional::value() is only valid if optional::has_value() is true. We
want to show that when x.value() is executed, the flow condition impliesx.has_value().
In the example below x.value() is accessed safely because it is guarded by thex.has_value() check.
void Example(std::optional<int> &x) {
if (x.has_value()) {
use(x.value());
}
}While entering the if branch we deduce that x.has_value() is implied by the
flow condition.
void Example(std::optional<int> x) {
// Flow condition: true.
if (x.has_value()) {
// Flow condition: x.has_value() == true.
use(x.value());
}
// Flow condition: true.
}We also need to prove that x is not modified between check and value access.
The modification of x may be very subtle:
void F(std::optional<int> &x);
void Example(std::optional<int> &x) {
if (x.has_value()) {
// Flow condition: x.has_value() == true.
unknown_function(x); // may change x.
// Flow condition: true.
use(x.value());
}
}Example: finding dead code behind A/B experiment flags
Finding dead code is a classic application of data flow analysis.
Unused flags for A/B experiment hide dead code. However, this flavor of dead
code is invisible to the compiler because the flag can be turned on at any
moment.
We could make a tool that deletes experiment flags. The user tells us which flag
they want to delete, and we assume that the it's value is a given constant.
For example, the user could use the tool to remove example_flag from this
code:
DEFINE_FLAG(std::string, example_flag, "", "A sample flag.");
void Example() {
bool x = GetFlag(FLAGS_example_flag).empty();
f();
if (x) {
g();
} else {
h();
}
}The tool would simplify the code to:
void Example() {
f();
g();
}We can solve this problem with a classic constant propagation lattice combined
with symbolic evaluation.
Example: finding inefficient usages of associative containers
Real-world code often accidentally performs repeated lookups in associative
containers:
map<int, Employee> xs;
xs[42]->name = "...";
xs[42]->title = "...";To find the above inefficiency we can use the available expressions analysis to
understand that m[42] is evaluated twice.
map<int, Employee> xs;
Employee &e = xs[42];
e->name = "...";
e->title = "...";We can also track the m.contains() check in the flow condition to find
redundant checks, like in the example below.
std::map<int, Employee> xs;
if (!xs.contains(42)) {
xs.insert({42, someEmployee});
}Example: refactoring types that implicitly convert to each other
Refactoring one strong type to another is difficult, but the compiler can help:
once you refactor one reference to the type, the compiler will flag other places
where this information flows with type mismatch errors. Unfortunately this
strategy does not work when you are refactoring types that implicitly convert to
each other, for example, replacing int32_t with int64_t.
Imagine that we want to change user IDs from 32 to 64-bit integers. In other
words, we need to find all integers tainted with user IDs. We can use data flow
analysis to implement taint analysis.
void UseUser(int32_t user_id) {
int32_t id = user_id;
// Variable `id` is tainted with a user ID.
...
}Taint analysis is very well suited to this problem because the program rarely
branches on user IDs, and almost certainly does not perform any computation
(like arithmetic).
Clang Docs Index (`clang/docs/index.md`)
% Clang documentation master file, created by
% sphinx-quickstart on Sun Dec 9 20:01:55 2012.
% You can adapt this file completely to your liking, but it should at least
% contain the root toctree directive.
/* Detailed source-code truncated for AI context efficiency. */- {doc}
ReleaseNotes
Using Clang as a Compiler
- {doc}
UsersManual - {doc}
Toolchain - {doc}
LanguageExtensions - {doc}
ClangCommandLineReference - {doc}
AttributeReference - {doc}
DiagnosticsReference - {doc}
AMDGPUBuiltinReference - {doc}
WarningSuppressionMappings - {doc}
CrossCompilation - {doc}
ClangStaticAnalyzer - {doc}
ThreadSafetyAnalysis - {doc}
LifetimeSafety - {doc}
SafeBuffers - {doc}
ScalableStaticAnalysis/index - {doc}
DataFlowAnalysisIntro - {doc}
FunctionEffectAnalysis - {doc}
AddressSanitizer - {doc}
HardwareAssistedAddressSanitizer - {doc}
ThreadSanitizer - {doc}
MemorySanitizer - {doc}
UndefinedBehaviorSanitizer - {doc}
DataFlowSanitizer - {doc}
LeakSanitizer - {doc}
TypeSanitizer - {doc}
RealtimeSanitizer - {doc}
SanitizerCoverage - {doc}
SanitizerStats - {doc}
SanitizerSpecialCaseList - {doc}
AllocToken - {doc}
OverflowBehaviorTypes - {doc}
BoundsSafety - {doc}
BoundsSafetyAdoptionGuide - {doc}
BoundsSafetyImplPlans - {doc}
ControlFlowIntegrity - {doc}
LTOVisibility - {doc}
PointerAuthentication - {doc}
ReturnAddressAuthenticationHardening - {doc}
SafeStack - {doc}
ShadowCallStack - {doc}
StructureProtection - {doc}
SourceBasedCodeCoverage - {doc}
StandardCPlusPlusModules - {doc}
Modules - {doc}
MSVCCompatibility - {doc}
MisExpect - {doc}
OpenCLSupport - {doc}
OpenMPSupport - {doc}
SYCLSupport - {doc}
HIPSupport - {doc}
HLSL/HLSLDocs - {doc}
ThinLTO - {doc}
APINotes - {doc}
DebuggingCoroutines - {doc}
AMDGPUSupport - {doc}
RISCVSupport - {doc}
CXXTypeAwareAllocators - {doc}
CIR/index - {doc}
CommandGuide/index - {doc}
FAQ
Using Clang as a Library
- {doc}
Tooling - {doc}
ExternalClangExamples - {doc}
IntroductionToTheClangAST - {doc}
LibTooling - {doc}
LibClang - {doc}
LibFormat - {doc}
ClangPlugins - {doc}
RAVFrontendAction - {doc}
LibASTMatchersTutorial - {doc}
LibASTMatchers - {doc}
ClangTransformerTutorial - {doc}
LibASTImporter - {doc}
HowToSetupToolingForLLVM - {doc}
JSONCompilationDatabase - {doc}
RefactoringEngine
Using Clang Tools
- {doc}
ClangTools - {doc}
ClangCheck - {doc}
ClangFormat - {doc}
ClangFormatStyleOptions - {doc}
ClangLinkerWrapper - {doc}
ClangNVLinkWrapper - {doc}
ClangOffloadBundler - {doc}
ClangRepl - {doc}
ClangSYCLLinker
Design Documents
- {doc}
Maintainers - {doc}
InternalsManual - {doc}
DriverInternals - {doc}
Multilib - {doc}
OffloadingDesign - {doc}
PCHInternals - {doc}
ItaniumMangleAbiTags - {doc}
ControlFlowIntegrityDesign - {doc}
HardwareAssistedAddressSanitizerDesign - {doc}
ConstantInterpreter - {doc}
LLVMExceptionHandlingCodeGen
Indices and tables
- {ref}
genindex - {ref}
search
Flang Docs Overview (`flang/docs/Overview.md`)
Overview of Compiler Phases
The Flang compiler transforms Fortran source code into an executable file.
This transformation proceeds in three high level phases -- analysis, lowering,
and code generation/linking.
The first high level phase (analysis) transforms Fortran source code into a
decorated parse tree and a symbol table. During this phase, all user
related errors are detected and reported.
The second high level phase (lowering), changes the decorated parse tree and
symbol table into the Fortran Intermediate Representation (FIR), which is a
dialect of LLVM's Multi-Level Intermediate Representation or MLIR. It then
runs a series of passes on the FIR code which verify its validity, perform a
series of optimizations, and finally transform it into LLVM's Intermediate
Representation, or LLVM IR
The third high level phase generates machine code and invokes a linker to
produce an executable file.
This document describes the first two high level phases. Each of these is
described in more detailed phases.
Each detailed phase is described -- its inputs and outputs along with how to
produce a readable version of the outputs.
Each detailed phase produces either correct output or fatal errors.
Analysis
This high level phase validates that the program is correct and creates all of
the information needed for lowering.
Prescan and Preprocess
See Preprocessing.md.
Input: Fortran source and header files, command line macro definitions,
set of enabled compiler directives (to be treated as directives rather than
comments).
Output:
- A "cooked" character stream: the entire program as a contiguous stream of
normalized Fortran source.
Extraneous whitespace and comments are removed (except comments that are
compiler directives that are not disabled) and case is normalized. Also,
directives are processed and macros expanded. - Provenance information mapping each character back to the source it came from.
This is used in subsequent phases that need source locations. This includes
error messages, optimization reports, and debugging information.
Entry point: parser::Parsing::Prescan
Commands:
flang -fc1 -E src.f90dumps the cooked character streamflang -fc1 -fdebug-dump-provenance src.f90dumps provenance
information
Parsing
Input: Cooked character stream
Output: A parse tree for each Fortran program unit in the source code
representing a syntactically correct program, rooted at the program unit. See:
Parsing.md and ParserCombinators.md.
Entry point: parser::Parsing::Parse
Commands:
flang -fc1 -fdebug-dump-parse-tree-no-sema src.f90dumps the parse treeflang -fc1 -fdebug-unparse src.f90converts the parse tree to normalized Fortranflang -fc1 -fdebug-dump-parsing-log src.f90runs an instrumented parse and dumps the logflang -fc1 -fdebug-measure-parse-tree src.f90measures the parse tree
Semantic processing
Input: the parse tree, the cooked character stream, and provenance
information
Output:
- a symbol table
- modified parse tree
- module files, (see: ModFiles.md)
- the intrinsic procedure table
- the target characteristics
- the runtime derived type derived type tables (see: RuntimeTypeInfo.md)
Entry point: semantics::Semantics::Perform
For more detail on semantic analysis, see: Semantics.md.
Semantic processing performs several tasks:
- validates labels, see: LabelResolution.md.
- canonicalizes DO statements,
- canonicalizes OpenACC and OpenMP code
- resolves names, building a tree of scopes and symbols
- rewrites the parse tree to correct parsing mistakes (when needed) once semantic information is available to clarify the program's meaning
- checks the validity of declarations
- analyzes expressions and statements, emitting error messages where appropriate
- creates module files if the source code contains modules,
see ModFiles.md.
In the course of semantic analysis, the compiler:
- creates the symbol table
- decorates the parse tree with semantic information (such as pointers into the symbol table)
- creates the intrinsic procedure table
- folds constant expressions
At the end of semantic processing, all validation of the user's program is complete. This is the last detailed phase of analysis processing.
Commands:
flang -fc1 -fdebug-dump-parse-tree src.f90dumps the parse tree after semantic analysisflang -fc1 -fdebug-dump-symbols src.f90dumps the symbol tableflang -fc1 -fdebug-dump-all src.f90dumps both the parse tree and the symbol table
Lowering
Lowering takes the parse tree and symbol table produced by analysis and
produces LLVM IR.
Create the lowering bridge
Inputs:
- the parse tree
- the symbol table
- The default KINDs for intrinsic types (specified by default or command line option)
- The intrinsic procedure table (created in semantics processing)
- The target characteristics (created during semantics processing)
- The cooked character stream
- The target triple -- CPU type, vendor, operating system
- The mapping between Fortran KIND values to FIR KIND values
The lowering bridge is a container that holds all of the information needed for lowering.
Output: A container with all of the information needed for lowering
Entry point: lower::LoweringBridge::create
Initial lowering
Input: the lowering bridge
Output: A Fortran IR (FIR) representation of the program.
Entry point: lower::LoweringBridge::lower
The compiler then takes the information in the lowering bridge and creates a
pre-FIR tree or PFT. The PFT is a list of programs and modules. The programs
and modules contain lists of function-like units. The function-like units
contain a list of evaluations. All of these contain pointers back into the
parse tree. The compiler walks the PFT generating FIR.
Commands:
flang -fc1 -fdebug-dump-pft src.f90dumps the pre-FIR treeflang -fc1 -emit-mlir src.f90dumps the FIR to the files src.mlir
Transformation passes
Input: initial version of the FIR code
Output: An LLVM IR representation of the program
Entry point: mlir::PassManager::run
The compiler then runs a series of passes over the FIR code. The first is a
verification pass. It's followed by a series of transformation passes that
perform various optimizations and transformations. The final pass creates an
LLVM IR representation of the program.
Commands:
flang -fc1 -emit-llvm src.f90dumps the LLVM IR to src.llflang -mmlir --mlir-print-ir-after-all -S src.f90dumps the FIR code after each pass to standard errorflang -mmlir --mlir-print-ir-before= -S src.f90dumps the FIR code before a specific pass to standard errorflang -mmlir --mlir-print-ir-after= -S src.f90dumps the FIR code after a specific pass to standard error
Note: The exact set of passes depends on compilation options. For example,stack-arrays only appears when -fstack-arrays is enabled. To see the
complete list for a given compilation, use --mlir-print-ir-before-all and
look at the IR Dump Before headers.
Object code generation and linking
After the LLVM IR is created, the flang driver invokes LLVM's existing
infrastructure to generate object code and invoke a linker to create the
executable file.
Flang Docs Index (`flang/docs/index.md`)
Welcome to Flang's documentation
Flang is LLVM's Fortran frontend that can be found
here. It is often
referred to as "LLVM Flang" to differentiate itself from "Classic
Flang" - these are two separate and
independent Fortran compilers. LLVM Flang is under active development. While it
is capable of generating executables for a number of examples, some
functionality is still missing. See Getting Involved for tips
on how to get in touch with us and to learn more about the current status.
.. toctree::
:titlesonly:
ReleaseNotesUsing Flang
.. toctree::
:titlesonly:
GettingStarted
FlangCommandLineReference
FortranStandardsSupport
Extensions
Directives
OpenMPSupport
OpenMP-extensions
Real16MathSupport
Unsigned
FAQContributing to Flang
.. toctree::
:titlesonly:
C++17
C++style
DesignGuideline
FortranForCProgrammers
GettingInvolved
ImplementingASemanticCheck
PullRequestChecklistDesign Documents
.. toctree::
:titlesonly:
Aliasing
AliasingAnalysisFIR
ArrayComposition
ArrayRepacking
AssumedRank
BijectiveInternalNameUniquing
Calls
Character
ComplexOperations
ControlFlowGraph
DebugGeneration
DoConcurrent
DoConcurrentConversionToOpenMP
F202X
FIRLangRef
FlangDriver
FortranFeatureHistory
FortranIR
FortranLLVMTestSuite
HighLevelFIR
IORuntimeInternals
InlineRuntimeChecks
InternalProcedureTrampolines
Intrinsics
IntrinsicTypes
LabelResolution
ModFiles
OpenACC
OpenACC-extensions
OpenACC-descriptor-management.md
OpenMP-4.5-grammar.md
OpenMP-declare-target
OpenMP-descriptor-management
OpenMP-semantics
OptionComparison
Overview
ParallelMultiImageFortranRuntime
ParameterizedDerivedTypes
ParserCombinators
Parsing
PolymorphicEntities
Preprocessing
ProcedurePointer
RuntimeDescriptor
RuntimeEnvironment
RuntimeTypeInfo
Semantics
f2018-grammar.md
fstack-arraysHistorical References
.. toctree::
:titlesonly:
HLFIRTransitionIndices and tables
* :ref:`genindex`
* :ref:`search`Libc Docs Index (`libc/docs/index.md`)
:alt: The LLVM-libc logo, drawn by Jordan Abrahams-Whitehead
:align: right
:width: 25%The LLVM C Library
LLVM-libc is a from-scratch implementation of the C standard library, built as
part of the LLVM project. It is designed to be modular (any piece can be
used independently), multiplatform (Linux, GPU, baremetal embedded, UEFI,
macOS, Windows), and written in modern C++ for correctness, performance, and
safety.
The logo was drawn by Jordan Abrahams-Whitehead
What Works Today
LLVM-libc is actively used in production for a targeted set of use cases,
though coverage is still growing and many programs that depend on the full C
standard library (regex, locale, wide-character I/O, etc.) will not yet compile
against it:
- Static-linked Linux servers and containers — used in production at Google
(servers and Pixel Buds) on x86-64 and AArch64. - GPU compute (AMDGPU, NVPTX) — libc functions available in GPU kernels
via LLVM's offloading runtime. {doc}GPU docs <gpu/index> - Baremetal embedded (ARM, RISC-V, AArch64) — minimal footprint builds
for microcontrollers and custom hardware. - UEFI applications — experimental support for firmware development.
{doc}UEFI docs - LLVM ecosystem internals — libc++ and the offloading runtime consume
LLVM-libc directly via {doc}Hand-in-Hand <hand_in_hand>. - Toolchain integrations — pieces of LLVM-libc are used in Android Bionic,
Fuchsia, Emscripten, and the ARM embedded toolchain.
Coverage is still growing. See the {doc}implementation status <headers/index>
pages for per-header detail, and the
{doc}platform support page for OS/architecture coverage.
Getting Started
If you are new to LLVM-libc, {doc}getting_started is the right first stop.
It covers cloning, building, testing, and verifying your installation in one
place.
Want to use LLVM-libc alongside your system libc instead of replacing it?
See {doc}overlay_mode.
Get Involved
LLVM-libc is an active project and welcomes contributors of all experience
levels. See {doc}contributing to learn how to help.
:caption: Using LLVM-libc
:hidden: true
:maxdepth: 1
getting_started
build_concepts
overlay_mode
full_host_build
full_cross_build
configure
hand_in_hand:caption: Platforms
:hidden: true
:maxdepth: 1
gpu/index
uefi/index:caption: Implementation Status
:hidden: true
:maxdepth: 1
headers/index
arch_support
platform_support
compiler_support:caption: Development
:hidden: true
:maxdepth: 1
contributing
build_and_test
dev/index
porting
Maintainers:caption: Links
:hidden: true
:maxdepth: 1
talks
Source Code <https://github.com/llvm/llvm-project/tree/main/libc>
Bug Reports <https://github.com/llvm/llvm-project/labels/libc>
Discourse <https://discourse.llvm.org/c/runtimes/libc>
Join the Discord <https://discord.gg/xS7Z362>
Discord Channel <https://discord.com/channels/636084430946959380/636732994891284500>
Buildbot <https://lab.llvm.org/buildbot/#/builders?tags=libc>Clang Docs CIR Index (`clang/docs/CIR/index.md`)
ClangIR
:::{warning}
The project of upstreaming ClangIR support from the incubator repository is
still in progress, and ClangIR is not included in a default clang build. The
documentation may be incomplete and out-of-date.
:::
ClangIR is a high-level representation in Clang that reflects aspects of the
C/C++ languages and their extensions. It is implemented using MLIR and occupies
a position between Clang's AST and LLVM IR.
ClangIR Design Documents
:maxdepth: 1
:numbered:
ABILowering
CleanupAndEHDesign
CodeDuplicationClang Docs CommandGuide Index (`clang/docs/CommandGuide/index.md`)
Clang "man" pages
The following documents are command descriptions for all of the Clang tools.
These pages describe how to use the Clang commands and what their options are.
Note that these pages do not describe all of the options available for all
tools. To get a complete listing, pass the --help (general options) or--help-hidden (general and debugging options) arguments to the tool you are
interested in.
Basic Commands
:maxdepth: 1
clang
diagtoolClang Docs ScalableStaticAnalysis Index (`clang/docs/ScalableStaticAnalysis/index.md`)
Scalable Static Analysis Framework
This is a framework for writing cross-translation unit analyses in a scalable and extensible setting.
:glob: true
:maxdepth: 2
:numbered:
user-docs/*{doc}Developer documentation <developer-docs/index>
:hidden: true
developer-docs/indexFlang Docs CommandGuide Index (`flang/docs/CommandGuide/index.md`)
flang - the Flang Fortran compiler
SYNOPSIS
{program}flang [options] filename ...
DESCRIPTION
{program}flang is a Fortran compiler which supports all of the Fortran 95 and
many newer language features. Flang supports OpenMP and has some support for
OpenACC and CUDA. It encompasses preprocessing, parsing, optimization, code
generation, assembly, and linking. Depending on the options passed in, Flang
will perform only some, or all, of these actions. While Flang is highly
integrated, it is important to understand the stages of compilation in order to
understand how to invoke it. These stages are:
Driver
: The flang executable is actually a small driver that orchestrates the
execution of other tools such as the compiler, assembler and linker.
Typically you do not need to interact with the driver, but you
transparently use it to run the other tools.
Preprocessing
: This stage performs tokenization of the input source file, macro expansion,
#include expansion and handles other preprocessor directives.
Parsing and Semantic Analysis
: This stage parses the input file, translating preprocessor tokens into a
parse tree. Once in the form of a parse tree, it applies semantic
analysis to compute types for expressions and determine whether
the code is well formed. Parse errors and most compiler warnings
are generated by this stage.
Code Generation and Optimization
: This stage translates the parse tree into intermediate code (known as
"LLVM IR") and, ultimately, machine code. It also optimizes this
intermediate code and handles target-specific code generation. The output
of this stage is typically a ".s" file, referred to as an "assembly" file.
Flang also supports the use of an integrated assembler, in which the code
generator produces object files directly. This avoids the overhead of
generating the ".s" file and calling the target assembler explicitly.
Assembler
: This stage runs the target assembler to translate the output of the
compiler into a target object file. The output of this stage is typically
a ".o" file, referred to as an "object" file.
Linker
: This stage runs the target linker to merge multiple object files into an
executable or dynamic library. The output of this stage is typically
an "a.out", ".dylib" or ".so" file.
OPTIONS
:::{toctree}
:maxdepth: 1
FlangCommandLineOptions
:::
Flang Docs MeetingNotes README (`flang/docs/MeetingNotes/README.md`)
Combined Flang Community Call Notes
About these notes
This directory combines the notes from the Flang Technical and Community Calls. This was done to make the call notes more easily available than before. All references to Flang in these documents refer to the Fortran compiler being developed as part of the LLVM project and located in the LLVM monorepo (github.com/llvm/llvm-project). Information about "Classic Flang", a separate and independent Fortran compiler, can be found at github.com/flang-compiler/flang.
Call Information
- Details about the call can be found in the Getting Involved page in flang's documentation.
Flang Community
Websites:
- Flang: https://flang.llvm.org
Issues:
- Issues for Flang should be reported in GitHub: https://github.com/llvm/llvm-project/issues. Please use the "flang" tag for flang-related issues.
LLVM mailing lists and Discourse channels: There are now two communication channels hosted by llvm.org:
- The Flang Discourse channel (used primarily for discussions): https://discourse.llvm.org/c/subprojects/flang/33
- The flang-commits mailing list (used primarily for routine notifications): subscribe at https://lists.llvm.org/cgi-bin/mailman/listinfo/flang-commits
Slack: The flang-compiler.slack.com
workspace is used for informal real-time communication in the Flang community.
See the Getting Involved page in flang's documentation for instructions on joining the workspace.Flang Development:
- On GitHub: The source code for Flang is available on https://github.com/llvm/llvm-project/tree/main/flang. It is recommended to start in the "docs" directory.
Libc Docs Dev Index (`libc/docs/dev/index.md`)
(developer_guides)=
Developer Guides
Navigate to the links below for information on the respective topics:
:maxdepth: 1
building_docs
code_style
source_tree_layout
entrypoints
implementing_a_function
config_options
date_and_time
fuzzing
header_generation
implementation_standard
undefined_behavior
printf_behavior
builtin_compatibility
syscall_wrapper_refactor
modular_format
code_coverageClang Docs ScalableStaticAnalysis Developer Docs Index (`clang/docs/ScalableStaticAnalysis/developer-docs/index.md`)
Developer Documentation
:glob: true
:maxdepth: 2
:numbered:
*Clang Tools Extra Docs Clang Tidy Checks Cppcoreguidelines Pro Bounds Constant Array Index (`clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/pro-bounds-constant-array-index.md`)
cppcoreguidelines-pro-bounds-constant-array-index
This check flags all array subscript expressions on static arrays andstd::arrays that either do not have a constant integer expression index or
are out of bounds (for std::array). For out-of-bounds checking of static
arrays, see the -Warray-bounds Clang diagnostic.
This rule is part of the Bounds safety (Bounds 2)
profile from the C++ Core Guidelines.
Optionally, this check can generate fixes using gsl::at for indexing.
Options
The check can generate fixes after this option has been set to the name of
the include file that contains `gsl::at()`, e.g. `"gsl/gsl.h"`.
Default is an empty string.A string specifying which include-style is used, `llvm` or `google`. Default
is `llvm`.Clang Tools Extra Docs Clang Tidy Checks Readability Misplaced Array Index (`clang-tools-extra/docs/clang-tidy/checks/readability/misplaced-array-index.md`)
readability-misplaced-array-index
This check warns for unusual array index syntax.
The following code has unusual array index syntax:
void f(int *X, int Y) {
Y[X] = 0;
}becomes
void f(int *X, int Y) {
X[Y] = 0;
}The check warns about such unusual syntax for readability reasons:
- There are programmers that are not familiar with this unusual syntax.
- It is possible that variables are mixed up.
Bolt Docs BAT (`bolt/docs/BAT.md`)
BOLT Address Translation (BAT)
Purpose
A regular profile collection for BOLT involves collecting samples from
unoptimized binary. BOLT Address Translation allows collecting profile
from BOLT-optimized binary and using it for optimizing the input (pre-BOLT)
binary.
Overview
BOLT Address Translation is an extra section (.note.bolt_bat) inserted by BOLT
into the output binary containing translation tables and split functions linkage
information. This information enables mapping the profile back from optimized
binary onto the original binary.
Usage
--enable-bat flag controls the generation of BAT section. Sampled profile
needs to be passed along with the optimized binary containing BAT section toperf2bolt which reads BAT section and produces profile for the original
binary.
Internals
Section contents
The section is organized as follows:
- Hot functions table
- Address translation tables
- Cold functions table
Construction and parsing
BAT section is created from BoltAddressTranslation class which captures
address translation information provided by BOLT linker. It is then encoded as a
note section in the output binary.
During profile conversion when BAT-enabled binary is passed to perf2bolt,BoltAddressTranslation class is populated from BAT section. The class is then
queried by DataAggregator during sample processing to reconstruct addresses/
offsets in the input binary.
Encoding format
The encoding is specified in
BoltAddressTranslation.h
and BoltAddressTranslation.cpp.
Layout
The general layout is as follows:
Hot functions table
Cold functions table
Functions table:
|------------------|
| Function entry |
| |
| Address |
| translation |
| table |
| |
| Secondary entry |
| points |
|------------------|Functions table
Hot and cold functions tables share the encoding except differences marked below.
Header:
| Entry | Encoding | Description |
|---|---|---|
NumFuncs |
ULEB128 | Number of functions in the functions table |
The header is followed by Functions table with NumFuncs entries.
Output binary addresses are delta encoded, meaning that only the difference with
the last previous output address is stored. Addresses implicitly start at zero.
Output addresses are continuous through function start addresses and function
internal offsets, and between hot and cold fragments, to better spread deltas
and save space.
Hot indices are delta encoded, implicitly starting at zero.
| Entry | Encoding | Description | Hot/Cold |
|---|---|---|---|
Address |
Continuous, Delta, ULEB128 | Function address in the output binary | Both |
HotIndex |
Delta, ULEB128 | Index of corresponding hot function in hot functions table | Cold |
FuncHash |
8b | Function hash for input function | Hot |
NumBlocks |
ULEB128 | Number of basic blocks in the original function | Hot |
NumSecEntryPoints |
ULEB128 | Number of secondary entry points in the original function | Hot |
ColdInputSkew |
ULEB128 | Skew to apply to all input offsets | Cold |
NumEntries |
ULEB128 | Number of address translation entries for a function | Both |
EqualElems |
ULEB128 | Number of equal offsets in the beginning of a function | Both |
BranchEntries |
Bitmask, alignTo(EqualElems, 8) bits |
If EqualElems is non-zero, bitmask denoting entries with BRANCHENTRY bit |
Both |
Function header is followed by Address Translation Table with NumEntries
total entries, and Secondary Entry Points table with NumSecEntryPoints
entries (hot functions only).
Address translation table
Delta encoding means that only the difference with the previous corresponding
entry is encoded. Input offsets implicitly start at zero.
| Entry | Encoding | Description | Branch/BB |
|---|---|---|---|
OutputOffset |
Continuous, Delta, ULEB128 | Function offset in output binary | Both |
InputOffset |
Optional, Delta, SLEB128 | Function offset in input binary with BRANCHENTRY LSB bit |
Both |
BBHash |
Optional, 8b | Basic block hash in input binary | BB |
BBIdx |
Optional, Delta, ULEB128 | Basic block index in input binary | BB |
The table omits the first EqualElems input offsets where the input offset
equals output offset.
BRANCHENTRY bit denotes whether a given offset pair is a control flow source
(branch or call instruction). If not set, it signifies a control flow target
(basic block offset).
InputAddr is omitted for equal offsets in input and output function. In this
case, BRANCHENTRY bits are encoded separately in a BranchEntries bitvector.
Deleted basic blocks are emitted as having OutputOffset equal to the size of
the function. They don't affect address translation and only participate in
input basic block mapping.
Secondary Entry Points table
The table is emitted for hot fragments only. It contains NumSecEntryPoints
offsets denoting secondary entry points, delta encoded, implicitly starting at zero.
| Entry | Encoding | Description |
|---|---|---|
SecEntryPoint |
Delta, ULEB128 | Secondary entry point offset |
Bolt Docs BOLTAArch64OptimizationStatus (`bolt/docs/BOLTAArch64OptimizationStatus.md`)
AArch64 Optimization and Flags Status
Overview
This page summarizes default-off BOLT optimization flags that users may
explicitly enable when optimizing AArch64 binaries.
BOLT is to be used with binaries linked with
relocations (--emit-relocs or -Wl,-q) and representative profile data.
Main Code-Layout Optimizations
The following code-layout optimizations are typically the first options to
consider when optimizing AArch64 binaries with representative profile data.
They typically provide the largest performance gains among BOLT optimizations.
:::{list-table}
:header-rows: 1
:widths: 34 42
:align: left
- Flag
- Optimization
--reorder-functions=exec-count|hfsort|cdsort|pettis-hansen|random|user--function-order=<file>- Reorder functions
--reorder-blocks=normal|ext-tsp|cache|branch-predictor|reverse|cluster-shuffle- Reorder basic blocks
--split-functions--split-strategy=profile2|random2|randomN|all--split-all-cold--split-eh- Split hot and cold code
:::
Other Supported Optimizations
The following optimizations are also supported for AArch64.
:::{list-table}
:header-rows: 1
:widths: 34 42
:align: left
- Flag
- Optimization
--align-blocks--block-alignment=- Align basic blocks
--tail-duplication=aggressive|moderate|cache- Duplicate branch tails
--peepholes=double-jumps|tailcall-traps|useless-branches|all- Run peephole optimizations
--inline-all--inline-small-functions
Related options:--inline-ap--inline-limit=--inline-small-functions-bytes=- Inline functions
--icf=safe|all- Fold identical functions
:::
Supported Flags With Limitations
The following flags are implemented for AArch64, but require specific runtime
or option conditions. Enabling them without the required conditions may report
an error or perform no transformation.
:::{list-table}
:header-rows: 1
:widths: 30 28 44
:align: left
- Flag
- Optimization
- Notes
--inline-memcpy- Inline fixed-size
memcpycalls - Only applies when the copy size is a known constant; AArch64 skips sizes over 64 bytes.
--plt=hot|all- Optimize PLT calls
- Requires immediate binding. If BOLT cannot update the binary, relink with
-znow.
--hugify- Place hot code on huge pages
- Applies to binaries with a recognized entry point; skipped when
--instrumentis used.
--reorder-data=<section1,section2,...>--reorder-data-algo=count|funcs- Reorder data sections
move,splitandaggressivedisable data reordering.
--split-strategy=cdsplit- Split functions using cache-directed splitting
- Requires
--compact-code-modelon AArch64.
:::
Unsupported Flags
The following flags are not available for AArch64. Not applicable to AArch64 means the optimization targets architectural features or mechanisms
that do not apply to AArch64. Not implemented for AArch64 means the
optimization could be relevant, but is not currently implemented for this
target.
:::{list-table}
:header-rows: 1
:widths: 30 28 42
:align: left
- Flag
- Optimization
- Notes
--jt-footprint-reduction- Reduce jump-table footprint
- Not implemented for AArch64.
--three-way-branch- Reorder three-way branches
- Not implemented for AArch64.
--simplify-rodata-loads- Replace read-only data loads with constants
- Not implemented for AArch64.
--frame-opt=hot|all- Optimize stack-frame accesses
- Not implemented for AArch64.
--indirect-call-promotion=calls|jump-tables|all- Promote indirect calls
- Not implemented for AArch64.
--memcpy1-spec=<func1,func2:cs1:cs2,...>- Specialize one-byte
memcpycalls - Not implemented for AArch64.
--reg-reassign- Reassign registers to reduce encoding size
- Not applicable to AArch64.
--cmov-conversion- Convert branches to conditional moves
- Not applicable to AArch64.
--stoke--stoke-outEmit STOKE optimization data
Not applicable to AArch64.
--insert-retpolines- Insert retpolines
- Not applicable to AArch64.
:::
Bolt Docs BinaryAnalysis (`bolt/docs/BinaryAnalysis.md`)
BOLT-based binary analysis
As part of post-link-time optimization, BOLT needs to perform a range of analyses
on binaries such as reconstructing control flow graphs, and more.
The llvm-bolt-binary-analysis tool enables running requested analyses
on binaries, and generating reports. It does this by building on top of the
analyses implemented in the BOLT libraries.
For now, the only analysis implemented is validation of Pointer Authentication
hardening on AArch64, more types of analyses can be added later.
Contents
Background and motivation
Security scanners
For the past 25 years, a large numbers of exploits have been built and used in
the wild to undermine computer security. The majority of these exploits abuse
memory vulnerabilities in programs, see evidence from
Microsoft,
Chromium and
Android.
It is not surprising therefore, that a large number of mitigations have been
added to instruction sets and toolchains to make it harder to build an exploit
using a memory vulnerability. Examples are: stack canaries, stack clash,
pac-ret, shadow stacks, arm64e, and many more.
These mitigations guarantee a so-called "security property" on the binaries they
produce. For example, for stack canaries, the security property is roughly that
a canary is located on the stack between the set of saved registers and the set
of local variables. For pac-ret, it is roughly that either the return address is
never stored/retrieved to/from memory; or, there are no writes to the register
containing the return address between an instruction authenticating it and a
return instruction using it.
From time to time, however, a bug gets found in the implementation of such
mitigations in toolchains. Also, code that is manually written in assembly
requires the developer to ensure these security properties by hand.
In short, it is sometimes found that a few places in the binary code are not
protected as well as expected given the requested mitigations. Attackers could
make use of those places (sometimes called gadgets) to circumvent the protection
that the mitigation should give.
One of the reasons that such gadgets, or holes in the mitigation implementation,
exist is that typically the amount of testing and verification for these
security properties is limited to checking results on specific examples.
In comparison, for testing functional correctness, or for testing performance,
toolchain and software in general typically get tested with large test suites
and benchmarks. In contrast, this typically does not get done for testing the
security properties of binary code.
Unlike functional correctness where compilation errors result in test failures,
and performance where speed and size differences are measurable, broken security
properties cannot be easily observed using existing testing and benchmarking
tools.
The security scanners implemented in llvm-bolt-binary-analysis aim to enable
the testing of security hardening in arbitrary programs and not just specific
examples.
Pointer Authentication
Pointer Authentication
is intended to make it harder for an attacker to replace pointers at run time.
This is achieved by making it possible for the compiler or the programmer to
produce a signed pointer from a raw one, and then to probabilistically
authenticate the signature at another site in the program.
On AArch64 this is achieved by injecting a cryptographic hash, called a
"Pointer Authentication Code" (PAC),
to the upper bits of the pointer.
While this approach can be applied to any pointers in the program, the most
frequent use case, at least in C and C++, is protecting the code pointers.
The language rules for such pointers are more restrictive, thus allowing the
compiler to implement various hardenings transparently to the programmer.
Probably the most simple variant of hardening based on Pointer Authentication ispac-ret, a security
hardening scheme implemented in compilers such as GCC and Clang, which can be
enabled with a command line option like -mbranch-protection=pac-ret.
On AArch64, pac-ret hardening is enabled by default on most widely used Linux
distributions. The hardening scheme mitigates
Return-Oriented Programming (ROP)
attacks by making sure that return addresses are only ever stored to memory
in a signed form. This makes it substantially harder for attackers to divert
control flow by overwriting a return address with a different value.
It is possible to link object files with different pac-ret hardening modes together,
as each particular function is responsible for signing the LR value in the prologue
and authenticating it in the epilogue. Other hardening variants exist that break
ABI compatibility, see the description of
PAuth ABI Extensions
for details. While pac-ret hardens (signs and authenticates) return addresses,
PAuth ABI may harden most other code pointers as well, such as function pointers
and labels used as the destination of computed goto.
The approach to validation of Pointer Authentication hardening implemented inllvm-bolt-binary-analysis is tracking register properties using dataflow analysis.
At each program point it is computed whether the particular register can be
controlled and whether it can be inspected by an attacker under the
Pointer Authentication threat model.
Then, for a number of sensitive instruction kinds (such as function calls and
pointer signing instructions), the properties of input or output operands are
inspected to check if the particular instruction is emitted in a safe manner.
As an example, for a return instruction this usually means that either the link
register (x30, which is also referred to as LR on AArch64) was never clobbered
in the function, or that it was authenticated at some point and never written to
since then:
foo:
; x30 is assumed to be trusted on function entry
cbnz x0, .L1
; For every possible execution path leading to this point, x30 was never
; clobbered and is thus safe to be used by `ret`.
ret
.L1:
paciasp
stp x29, x30, [sp, #-16]!
mov x29, sp
bl bar
ldp x29, x30, [sp], #16
autiasp
; At this point, x30 was never written to since `autiasp`.
retThe real rules being used by llvm-bolt-binary-analysis are somewhat more
complex, see the below sections for the detailed explanation.
Usage
llvm-bolt-binary-analysis --scanners=<list> [options] <binary>The --scanners= option accepts a comma-separated list of analyses to run on
the provided binary. The binary to be analyzed can be either an ELF executable or
a shared object. Similar to other BOLT tools, llvm-bolt-binary-analysis expectsbinary to be unstripped and preferably linked with --emit-relocs linker option.
In addition to options printed by llvm-bolt-binary-analysis --help-hidden,
other relevant BOLT options can generally be passed, see llvm-bolt --help-hidden.
Incomplete help message is a known issue and is tracked in
#176969.
The only analysis which is currently implemented is validation of a number of
Pointer Authentication-based hardening schemes such as pac-ret and PAuth ABI.
The specific set of gadget kinds which are searched for depends on command line
options. Each gadget found by PtrAuth gadget scanner results in a plain text
report printed at the end of the analysis. Furthermore, an attempt is made to
provide additional information on the instructions that made the register unsafe.
Please note that this extra information is provided on a best-effort basis and
is not expected to be as accurate as the reports themselves.
Here is an example of the report:
GS-PAUTH: signing oracle found in function function_name, basic block .LBB08, at address 102b8
The instruction is 000102b8: pacda x0, x1
The 1 instructions that write to the affected registers after any authentication are:
1. 000102b4: ldr x0, [x1]
This happens in the following basic block:
000102b4: ldr x0, [x1]
000102b8: pacda x0, x1
000102bc: retA similar report without the associated extra information looks like this:
GS-PAUTH: signing oracle found in function function_name, basic block .LBB016, at address 10384
The instruction is 00010384: pacda x0, x1
The 0 instructions that write to the affected registers after any authentication are:Pointer Authentication validator
Pointer Authentication analysis is able to search for a number of gadget kinds,
with the specific set depending on command line options:
ptrauth-pac-ret-
non-protected return instructionptrauth-tail-calls-
performing a tail call with an untrusted value in the link registerptrauth-forward-cf-
non-protected destination of branch or call instructionptrauth-sign-oracles-
signing of untrusted value (signing oracle)ptrauth-auth-oracles-
revealing the result of authentication without crashing the program on failure
(authentication oracle)
Validation is performed by llvm-bolt-binary-analysis on a per-function basis.
First, the register properties are computed by analyzing the function as a whole.
Then, the instructions are considered in isolation. For each kind of gadget,
the set of susceptible instructions is computed. The properties of input or
output registers of each such instruction are analyzed and reports are produced
for each detected gadget.
Each gadget kind that is searched for can be characterized by the combination of
- the set of instructions to analyze
- the properties of input or output operands to check
Currently, three properties can be computed for each register at any given
program point:
- "trusted" - the register is known not to be attacker-controlled, either because
it successfully passed authentication or because its value was materialized
using an instruction sequence that an attacker cannot tamper with- "safe-to-dereference" (sometimes referred to as "s-t-d" below) -
a weaker property that the register can be controlled by an attacker to some
extent, but any memory access using a value crafted by an attacker is known
to result in an access to an unmapped memory ("segmentation fault").
This makes it possible for authentication instructions to return an invalid
address on failure as long as it is known to crash the program on accessing
memory, but may require extra care to be taken when implementing operations
like re-signing a pointer with a different signing schema without accessing
that address in-between. If any failed authentication instruction is
guaranteed to terminate the program abnormally, then "safe-to-dereference"
and "trusted" properties are equivalent.
- "safe-to-dereference" (sometimes referred to as "s-t-d" below) -
- "cannot escape unchecked" - at every possible execution path after this point,
it is known to be impossible for an attacker to determine that the value is
a result of a failed authentication operation (for example, the register is
zeroed, or its value is checked to be valid, so that failure results in
immediate abnormal program termination).
Generally, BOLT strives to reconstruct the control-flow graph of each function,
which is important for dataflow analysis. However, when BOLT fails to recognize
some control flow in the particular function, that function ends up being
represented as a flat list of instructions - in such casesllvm-bolt-binary-analysis computes register properties using a fallback
analysis implementation, which is less precise.
The below sub-sections describe the particular detectors. Please note that while
the descriptions refer to AArch64 for simplicity, the implementation of gadget
detectors in llvm-bolt-binary-analysis attempts to be target-neutral by
isolating AArch64 specifics in target-dependent hooks.
Return address protection (`ptrauth-pac-ret`)
Instructions: Return instructions without built-in authentication:
either ret (implicit x30 register) or ret <reg>, but not retaa and
similar instructions.
Property: The register holding the return address must be safe-to-dereference.
Notes: Cross-exception-level return instructions (eret) are not analyzed yet.
A report is generated for a return instruction whose destination is possibly
attacker-controlled.
Examples:
authenticated_return:
pacibsp
; ...
; ... some code here ...
; ...
retab ; Built-in authentication, thus out of scope.
good_leaf_function:
; x30 is implicitly safe-to-dereference (s-t-d) and trusted at function entry.
mov x0, #42
; x30 was not written to by this function, thus remains s-t-d.
ret
good_non_leaf_function:
pacibsp
; Spilling signed return address.
stp x29, x30, [sp, #-16]!
mov x29, sp
bl callee
; Re-loading signed return address.
; LDP writes to x30 and thus resets it to neither s-t-d nor trusted state.
ldp x29, x30, [sp], #16
; Checking that signature is valid.
; AUTIBSP sets "s-t-d" property of x30, but not "trusted" (unless FEAT_FPAC
; is known to be implemented).
autibsp
; x30 is s-t-d at this point.
ret
bad_spill:
; x30 is implicitly s-t-d at function entry.
stp x29, x30, [sp, #-16]!
mov x29, sp
bl callee ; Spilled x30 may have been overwritten on stack.
; Writing to x30 resets its s-t-d property.
ldp x29, x30, [sp], #16
; x30 is unsafe by the time it is used by ret, thus generating a report.
ret
bad_clobber:
pacibsp
; ...
; ... some code here ...
; ...
autibsp
mov x30, x1
; The value in LR is unsafe, even though there was autibsp above.
retReturn address protection before tail call (`ptrauth-tail-calls`)
Instructions: Branch instructions (both direct and indirect, regular or
with built-in authentication), classified as tail calls either by BOLT or by
PtrAuth gadget scanner's heuristic.
Property: link register (x30 on AArch64) must be trusted.
Notes: Heuristics are involved to classify instructions either as a tail
call or as another kind of branch (such as jump table or computed goto).
A report is generated if a tail call is performed with an untrusted link register.
This basically means that the tail-called function would have the link register
untrusted on its entry (unlike the inherently correct address placed in the link
register by one of the bl* instructions when a non-tail call is performed).
non_protected_tail_call:
stp x29, x30, [sp, #-16]!
mov x29, sp
bl callee
ldp x29, x30, [sp], #16
; x30 is neither trusted nor safe-to-dereference at this point.
b tail_callee
non_checked_tail_call:
pacibsp
stp x29, x30, [sp, #-16]!
mov x29, sp
bl callee
ldp x29, x30, [sp], #16
autibsp
; x30 is safe-to-dereference, but not fully trusted at this point.
b tail_callee
tail_callee:
pacibsp
; ...Even though x30 is likely to be safe-to-dereference before exit from a function
(whether via return or tail call) in a consistently pac-ret-protected program,
with respect to this gadget kind it further must be fully "trusted".
This corresponds to the way tail calls are emitted when --aarch64-authenticated-lr-check-method=
option is specified with an argument other than none, or when such checks are
requested for the particular subtarget by default.
Properly mitigating this issue would usually require inserting an explicit
check after a regular authentication instruction, which may be either too
expensive (if a fully-generic XPAC-based sequence is being used) on one hand,
or not required at all (if FEAT_FPAC is known to be implemented) on the other hand.
(ptrauth-forward-cf)=
Indirect branch / call target protection (`ptrauth-forward-cf`)
Instructions: Indirect call and branch instructions without built-in
authentication: either blr <reg> or br <reg>, but not blraa, braa
and similar instructions.
Property: The call or branch target register must be safe-to-dereference.
A report is generated for an indirect branch or call instruction whose destination
is possibly attacker-controlled.
Examples:
direct_call:
; ...
bl callee ; Direct call, thus out of scope.
; ...
authenticated_call:
; ...
ldr x2, [x1]
blraa x2, x1 ; Built-in authentication, thus out of scope.
; ...
good_call:
; ...
ldr x2, [x1]
autia x2, x1
blr x2
; ...
bad_call:
; ...
ldr x2, [x1]
autia x2, x1
; Store unprotected address.
str x2, [x3]
; ...
; The callee address may have been overwritten in memory.
ldr x2, [x3]
blr x2
; ...
good_call_dataflow:
cbz x0, .L1
ldr x2, [x1]
autia x2, x1
b .L2
.L1:
adrp x2, callee
add x2, x2, :lo12:callee
.L2:
; Dataflow analysis can deduce that x2 is s-t-d on any possible execution
; path leading to the below "br x2" instruction.
br x2
bad_call_dataflow:
cbz x0, .L3
adrp x2, callee
add x2, x2, :lo12:callee
.L3:
; x2 is untrusted if x0 is 0.
br x2Signing oracles (`ptrauth-sign-oracles`)
Instructions: Address-signing instructions.
Property: The address being signed must be trusted.
Reports signing of untrusted values, as this could make arbitrary and possibly
attacker-controlled values indistinguishable from perfectly trusted and protected ones.
Note that in absence of --auth-traps-on-failure command line option, this detector
reports auth+sign pairs unless an explicit check is emitted to make sure the
authentication operation succeeded. This corresponds to the way LLVM emits
instructions on AArch64.
Examples:
good_sign_constant:
; ...
adrp x0, sym
add x0, x0, :lo12:sym
pacda x0, x1
; ...
good_resign:
; ...
autda x0, x1
; x0 is s-t-d here.
ldr x2, [x0]
; If we got here without crashing on the above ldr, x0 is fully trusted.
pacdb x0, x1
; ...
bad_resign_if_not_fpac:
; ...
autda x0, x1
; x0 is only s-t-d, but not trusted here, unless autda raises an error on failure.
pacdb x0, x1
; ...
very_bad_function:
pacda x0, x1
retAuthentication oracles (`ptrauth-auth-oracles`)
Instructions: Standalone authentication instructions: autda, autdb, etc.
(i.e. not those built into corresponding memory-accessing instructions, such asldraa or blraa).
Property: The result of authentication must be written to a register
that cannot escape unchecked.
Reports authentication instructions, whose result (success or failure) can be
observed by the attacker and used to guess the correct PAC field by trial-and-error.
The authentication oracles searched for by this detector are impossible if all
authentication instructions are known to generate an irrecoverable error on
failure. On AArch64 this is the case if CPU is known to implement FEAT_FPAC
(though recoverability depends on the OS: for example, on Linux such errors
result in signals that can be handled if configured accordingly).
This check is disabled by --auth-traps-on-failure command line option.
Examples:
; The descriptions assume FEAT_FPAC is not implemented.
good_auth_call:
paciasp
stp x29, x30, [sp, #-16]!
mov x29, sp
; The result of authentication is inevitably dereferenced by blr.
; If autia returns a non-canonical address due to incorrect signature,
; the program crashes when the resulting address is jumped-to by blr.
cbz x2, .L1
autia x0, x1
blr x0
.L1:
ldp x29, x30, [sp], #16
autiasp
ret
bad_auth_call:
paciasp
stp x29, x30, [sp, #-16]!
mov x29, sp
; x0 may be observed by the caller of bad_auth_call if x2 is 0.
autia x0, x1
cbz x2, .L2
blr x0
.L2:
ldp x29, x30, [sp], #16
autiasp
ret
bad_leaks_to_callee:
paciasp
stp x29, x30, [sp, #-16]!
mov x29, sp
ldr x20, [x0]
autda x20, x0
; The result of authentication is leaked to the called function.
bl callee
; The below ldr instruction would properly check x20 if placed above the call.
ldr x0, [x20]
ldp x29, x30, [sp], #16
autiasp
retKnown issues and missing features
Control-flow graph availability
The analysis quality is degraded if BOLT is unable to reconstruct control-flow
graph of the function correctly.
When reconstructing CFG for a particular function, it is possible that BOLT
finds a code pattern that it is unable to handle. In that case PtrAuth validator
processes a function containing a flat list of instructions (as opposed to
interlinked basic blocks) and the reports (if any) are printed without the, basic block <name> part in the first line.
Furthermore, it is possible that CFG information is returned by BOLT for a
function, even though the graph is imprecise. Inaccurate CFG information may
result in false positives and false negatives, thus PtrAuth gadget scanner
produces warning messages (at most once per function) for non-entry basic blocks
without any predecessors in CFG: while unreachable basic blocks are technically
correct, truly unreachable blocks are unlikely to exist in an optimized code.
Known gadget scanner issues related to CFG reconstruction are tracked in
#177761,
#178058,
#178232.
Last writing instructions
Some of the reports contain extra information along these lines
The 1 instructions that write to the affected registers after any authentication are:
1. 000102b4: ldr x0, [x1]
This happens in the following basic block:
000102b4: ldr x0, [x1]
000102b8: pacda x0, x1
000102bc: retThis information is provided on a best-effort basis and is less reliable than
gadget reports themselves.
Feature: scan for unsafe computation of discriminators
On AArch64, signing and authentication operations are parameterized by the pair
of the key identifier and a 64-bit discriminator value. The key is one ofIA, IB, DA, or DB, and its identifier is directly encoded into the
instruction (the GA key differs significantly and is thus out of scope here).
The discriminator, on the other hand, is computed at run-time and is intended
to be derived independently
at the signing and authentication sites.
There is a common pattern on AArch64 to compute the discriminator as a blend
of an address and an integer modifier by inserting a compile-time constant
value into the top 16 bits of the storage address. It is not necessarily
possible to prevent an attacker from modifying the storage address, but the
insertion of 16-bit constant modifier can always be performed immediately before
the discriminator value is used by signing or authentication instruction.
While not as bad as signing an arbitrary untrusted pointer, spilling a
"ready-to-use" discriminator and then reloading a potentially modified value
later is something we would rather avoid. On the other hand, using an arbitrary
value as the discriminator should probably be allowed, making it hard to
distinguish the below patterns:
; Valid and not reported.
good_store_with_address_and_constant_diversity:
mov x16, x1
movk x16, #1234, lsl #48
pacda x0, x16
str x0, [x1]
ret
; Valid and not reported.
good_store_with_address_diversity:
pacda x0, x1
str x0, [x1]
ret
; Spilled discriminator. Not critically wrong, but could rather be avoided.
; Not reported, but probably should (false negative).
bad_spilling:
mov x16, x1
movk x16, #1234, lsl #48
; Spilling the discriminator to memory.
str x16, [x2]
; Reloaded value could have been modified by an attacker.
ldr x16, [x2]
pacda x0, x16
str x0, [x1]
ret
; Not reported (and should probably not).
better_spilling:
mov x16, x1
str x16, [x2]
; Reloaded value could have been modified by an attacker.
ldr x16, [x2]
movk x16, #1234, lsl #48
pacda x0, x16
str x0, [x1]
retHandling of constants
While (PC-relative) address constants are tracked as "trusted" register state
by SrcSafetyAnalysis, constant values are not generally accounted for.
This results in false-positives like reporting
; Let assume FEAT_FPAC is implemented.
autda x16, x22
mov x17, #0x128
add x16, x16, x17
pacda x16, x22as a signing oracle, even though
; Let assume FEAT_FPAC is implemented.
autda x16, x22
add x16, x16, #8
pacda x16, x22is not reported, because add Xdst, Xsrc, #imm is recognized as a safe address
computation.
As an example of false-negative, it is possible that an instruction likeadd x0, x0, #1 could be called in a loop with an attacker-controlled number
of iterations, making it technically possible for an attacker to replace a
valid pointer with a pointer to an arbitrary address.
Tail call detection
ptrauth-tail-calls detector uses a heuristic to guess which branch instructions
(both direct and indirect) correspond to performing tail calls, as opposed to
other kinds of control flow.
Most other parts of BOLT should not break code when rewriting. Unlike them,
this analyzer tries to keep reasonable balance between false positive reports
and missed issues. For this reason, it inspects some other branch instructions
in addition to those BOLT is sure about.
While it should provide reasonable balance between false positives and false
negatives on general code, this may not be the case for some specific patterns.
An example of a generally uncommon code pattern that is likely to yield false
positives is labels-as-values GCC extension
which is also supported by Clang.
Other known issues
- No lightweight variant of
ptrauth-tail-calls,
see issue #186204
for the details. - Not handling "no-return" functions. See issue
#115154 for details and
pointers to open PRs to fix this. - Scanning of binaries compiled by Clang at
-Ozoptimization level produces a
lot of reports due to outlining. Many such reports could probably be considered
false negatives as long as an attacker is unable to callOUTLINED_FUNCTIONs
as ROP gadgets in the first place. - False positives are possible due to multi-instruction pointer-checking sequences
not being detected without CFG. - While obviously "checking" the result of pointer authentication, store
instructions do not transition their address operand register from safe-to-
dereference to trusted state yet. This does not affect scanning regular code
hardened neither bypac-ret, nor byarm64eorpauthtest.
How to add your own analysis
Pointer Authentication validator
To implement the detection of a new gadget kind, add newshouldReport*Gadget function to bolt/lib/Passes/PAuthGadgetScanner.cpp and
call it either from FunctionAnalysisContext::findUnsafeUses orFunctionAnalysisContext::findUnsafeDefs.
To improve overall analysis quality by better computing register properties,
either modify one of *SafetyAnalysis classes in PAuthGadgetScanner.cpp
(if the improvement is target-neutral), or one of target-specific hooks in
the subclass of MCPlusBuilder corresponding to your target (if an analysis of
target-specific instruction patterns is to be improved).
To add support for a new target, if one eventually implements similar pointer
protection technique, implement PtrAuth-related hooks in the subclass ofMCPlusBuilder corresponding to your target. Ideally, no changes should be
needed in PAuthGadgetScanner.cpp, as it is intended to be reasonably target-
independent, though it is possible that some amount of further generalization
may be required.
New types of analyses
TODO: this section needs to be written. Ideally, we should have a simple
"example" or "template" analysis that can be the starting point for implementing
custom analyses
Bolt Docs CMakeLists (`bolt/docs/CMakeLists.txt`)
if (DOXYGEN_FOUND)
if (LLVM_ENABLE_DOXYGEN)
set(abs_top_srcdir ${CMAKE_CURRENT_SOURCE_DIR})
set(abs_top_builddir ${CMAKE_CURRENT_BINARY_DIR})
if (HAVE_DOT)
set(DOT ${LLVM_PATH_DOT})
endif()
if (LLVM_DOXYGEN_EXTERNAL_SEARCH)
set(enable_searchengine "YES")
set(searchengine_url "${LLVM_DOXYGEN_SEARCHENGINE_URL}")
set(enable_server_based_search "YES")
set(enable_external_search "YES")
set(extra_search_mappings "${LLVM_DOXYGEN_SEARCH_MAPPINGS}")
else()
set(enable_searchengine "NO")
set(searchengine_url "")
set(enable_server_based_search "NO")
set(enable_external_search "NO")
set(extra_search_mappings "")
endif()
If asked, configure doxygen for the creation of a Qt Compressed Help file.
if (LLVM_ENABLE_DOXYGEN_QT_HELP)
set(BOLT_DOXYGEN_QCH_FILENAME "org.llvm.bolt.qch" CACHE STRING
"Filename of the Qt Compressed help file")
set(BOLT_DOXYGEN_QHP_NAMESPACE "org.llvm.bolt" CACHE STRING
"Namespace under which the intermediate Qt Help Project file lives")
set(BOLT_DOXYGEN_QHP_CUST_FILTER_NAME "Clang ${BOLT_VERSION}" CACHE STRING
"See http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-filters")
set(BOLT_DOXYGEN_QHP_CUST_FILTER_ATTRS "Clang,${BOLT_VERSION}" CACHE STRING
"See http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes")
set(bolt_doxygen_generate_qhp "YES")
set(bolt_doxygen_qch_filename "${BOLT_DOXYGEN_QCH_FILENAME}")
set(bolt_doxygen_qhp_namespace "${BOLT_DOXYGEN_QHP_NAMESPACE}")
set(bolt_doxygen_qhelpgenerator_path "${LLVM_DOXYGEN_QHELPGENERATOR_PATH}")
set(bolt_doxygen_qhp_cust_filter_name "${BOLT_DOXYGEN_QHP_CUST_FILTER_NAME}")
set(bolt_doxygen_qhp_cust_filter_attrs "${BOLT_DOXYGEN_QHP_CUST_FILTER_ATTRS}")
else()
set(bolt_doxygen_generate_qhp "NO")
set(bolt_doxygen_qch_filename "")
set(bolt_doxygen_qhp_namespace "")
set(bolt_doxygen_qhelpgenerator_path "")
set(bolt_doxygen_qhp_cust_filter_name "")
set(bolt_doxygen_qhp_cust_filter_attrs "")
endif()
llvm_configure_doxygen()
set(abs_top_srcdir)
set(abs_top_builddir)
set(DOT)
set(enable_searchengine)
set(searchengine_url)
set(enable_server_based_search)
set(enable_external_search)
set(extra_search_mappings)
set(bolt_doxygen_generate_qhp)
set(bolt_doxygen_qch_filename)
set(bolt_doxygen_qhp_namespace)
set(bolt_doxygen_qhelpgenerator_path)
set(bolt_doxygen_qhp_cust_filter_name)
set(bolt_doxygen_qhp_cust_filter_attrs)
add_custom_target(doxygen-bolt
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating bolt doxygen documentation." VERBATIM)
set_target_properties(doxygen-bolt PROPERTIES FOLDER "BOLT/Docs")
if (LLVM_BUILD_DOCS)
add_dependencies(doxygen doxygen-bolt)
endif()
if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY AND LLVM_BUILD_DOCS)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doxygen/html
DESTINATION docs/html)
endif()
endif()
endif()
if (LLVM_ENABLE_SPHINX)
include(AddSphinxTarget)
if (SPHINX_FOUND)
if (${SPHINX_OUTPUT_HTML})
add_sphinx_target(html bolt)
endif()
endif()
endif()
Bolt Docs CommandLineArgumentReference (`bolt/docs/CommandLineArgumentReference.md`)
BOLT - a post-link optimizer developed to speed up large applications
SYNOPSIS
llvm-bolt <executable> [-o outputfile] <executable>.bolt [-data=perf.fdata] [options]
OPTIONS
Generic options:
-hAlias for --help
--helpDisplay available options (--help-hidden for more)
--help-hiddenDisplay all available options
--help-listDisplay list of available options (--help-list-hidden for more)
--help-list-hiddenDisplay list of all available options
--versionDisplay the version of this program
Output options:
--bolt-infoWrite bolt info section in the output binary
-o <string>output file
-w <string>Save recorded profile to a file
BOLT generic options:
--align-text=Alignment of .text section
--allow-strippedAllow processing of stripped binaries
--alt-inst-feature-size=Size of feature field in .altinstructions
--alt-inst-has-padlenSpecify that .altinstructions has padlen field
--asm-dump[=<dump folder>]Dump function into assembly
-bAlias for -data
--bolt-id=<string>Add any string to tag this execution in the output binary via bolt info section
--break-funcs=<func1,func2,func3,...>List of functions to core dump on (debugging)
--check-encodingPerform verification of LLVM instruction encoding/decoding. Every instruction
in the input is decoded and re-encoded. If the resulting bytes do not match
the input, a warning message is printed.--comp-dir-override=<string>Overrides DW_AT_comp_dir, and provides an alternative base location, which is
used with DW_AT_dwo_name to construct a path to *.dwo files.--create-debug-names-sectionCreates .debug_names section, if the input binary doesn't have it already, for
DWARF5 CU/TUs.--cu-processing-batch-size=Specifies the size of batches for processing CUs. Higher number has better
performance, but more memory usage. Default value is 1.--data=<string>data file
--data2=<string>data file
--debug-skeleton-cuPrints out offsets for abbrev and debug_info of Skeleton CUs that get patched.
--debug-thread-count=Specifies the number of threads to be used when processing DWO debug information.
--dot-tooltip-codeAdd basic block instructions as tool tips on nodes
--dump-alt-instructionsDump Linux alternative instructions info
--dump-cg=<string>Dump callgraph to the given file
--dump-dataDump parsed bolt data for debugging
--dump-dot-allDump function CFGs to graphviz format after each stage;enable '-print-loops'
for color-coded blocks--dump-dot-func=<func1,func2,func3...>Dump function CFGs to graphviz format for specified functions only;
takes function name patterns (regex supported). Note: C++ function names
must be passed using their mangled names--dump-linux-exceptionsDump Linux kernel exception table
--dump-orcDump raw ORC unwind information (sorted)
--dump-para-sitesDump Linux kernel paravitual patch sites
--dump-pci-fixupsDump Linux kernel PCI fixup table
--dump-smp-locksDump Linux kernel SMP locks
--dump-static-callsDump Linux kernel static calls
--dump-static-keysDump Linux kernel static keys jump table
--dwarf-output-path=<string>Path to where .dwo files or dwp file will be written out to.
--dwp=<string>Path and name to DWP file.
--dyno-statsPrint execution info based on profile
--dyno-stats-allPrint dyno stats after each stage
--dyno-stats-scale=Scale to be applied while reporting dyno stats
--enable-batWrite BOLT Address Translation tables
--fix-branches-with-livenessUse liveness analysis to decide whether AArch64 branch inversion must
preserve condition flags--force-data-relocationsForce relocations to data sections to always be processed
--force-patchForce patching of original entry points
--funcs=<func1,func2,func3,...>Limit optimizations to functions from the list
--funcs-file=<string>File with list of functions to optimize
--funcs-file-no-regex=<string>File with list of functions to optimize (non-regex)
--funcs-no-regex=<func1,func2,func3,...>Limit optimizations to functions from the list (non-regex)
--hot-dataHot data symbols support (relocation mode)
--hot-functions-at-endIf reorder-functions is used, order functions putting hottest last
--hot-textGenerate hot text symbols. Apply this option to a precompiled binary that
manually calls into hugify, such that at runtime hugify call will put hot code
into 2M pages. This requires relocation.--hot-text-move-sections=<sec1,sec2,sec3,...>List of sections containing functions used for hugifying hot text. BOLT makes
sure these functions are not placed on the same page as the hot text.
(default='.stub,.mover').--insert-retpolinesRun retpoline insertion pass
--keep-arangesKeep or generate .debug_aranges section if .gdb_index is written
--keep-tmpPreserve intermediate .o file
--liteSkip processing of cold functions
--log-file=<string>Redirect journaling to a file instead of stdout/stderr
--long-jump-labelsAlways use long jumps/nops for Linux kernel static keys
--match-profile-with-function-hashMatch profile with function hash
--max-data-relocations=Maximum number of data relocations to process
--max-funcs=Maximum number of functions to process
--no-huge-pagesUse regular size pages for code alignment
--no-threadsDisable multithreading
--pad-funcs=<func1:pad1,func2:pad2,func3:pad3,...>List of functions to pad with amount of bytes
--print-mappingsPrint mappings in the legend, between characters/blocks and text sections
(default false).--profile-format=<value>Format to dump profile output in aggregation mode, default is fdata
fdata: offset-based plaintext formatyaml: dense YAML representation
--r11-availability=<value>Determine the availability of r11 before indirect branches
never: r11 not availablealways: r11 available before calls and jumpsabi: r11 available before calls but not before jumps
--relocsUse relocations in the binary (default=autodetect)
--remove-symtabRemove .symtab section
--reorder-skip-symbols=<symbol1,symbol2,symbol3,...>List of symbol names that cannot be reordered
--reorder-symbols=<symbol1,symbol2,symbol3,...>List of symbol names that can be reordered
--retpoline-lfenceDetermine if lfence instruction should exist in the retpoline
--skip-funcs=<func1,func2,func3,...>List of functions to skip
--skip-funcs-file=<string>File with list of functions to skip
--strictTrust the input to be from a well-formed source
--tasks-per-thread=Number of tasks to be created per thread
--terminal-trapAssume that execution stops at trap instruction
--thread-count=Number of threads
--top-called-limit=Maximum number of functions to print in top called functions section
--trap-avx512In relocation mode trap upon entry to any function that uses AVX-512
instructions--trap-old-codeInsert traps in old function bodies (relocation mode)
--update-debug-sectionsUpdate DWARF debug sections of the executable
--use-gnu-stackUse GNU_STACK program header for new segment (workaround for issues with
strip/objcopy)--use-old-textReuse space in old .text if possible (relocation mode)
-vSet verbosity level for diagnostic output
BOLT optimization options:
--align-blocksAlign basic blocks
--align-blocks-min-size=Minimal size of the basic block that should be aligned
--align-blocks-threshold=Align only blocks with frequency larger than containing function execution
frequency specified in percent. E.g. 1000 means aligning blocks that are 10
times more frequently executed than the containing function.--align-functions=Align functions at a given value (relocation mode)
--align-functions-max-bytes=Maximum number of bytes to use to align functions
--assume-abiAssume the ABI is never violated
--block-alignment=Boundary to use for alignment of basic blocks
--bolt-seed=Seed for randomization
--cg-from-perf-dataUse perf data directly when constructing the call graph for stale functions
--cg-ignore-recursive-callsIgnore recursive calls when constructing the call graph
--cg-use-split-hot-sizeUse hot/cold data on basic blocks to determine hot sizes for call graph
functions--cold-threshold=Tenths of percents of main entry frequency to use as a threshold when
evaluating whether a basic block is cold (0 means it is only considered cold
if the block has zero samples). Default: 0--elim-link-veneersRun veneer elimination pass
--eliminate-unreachableEliminate unreachable code
--equalize-bb-countsUse same count for BBs that should have equivalent count (used in non-LBR and
shrink wrapping)--execution-count-threshold=Perform profiling accuracy-sensitive optimizations only if function execution
count >= the threshold (default: 0)--fix-block-countsAdjust block counts based on outgoing branch counts
--fix-func-countsAdjust function counts based on basic blocks execution count
--force-inline=<func1,func2,func3,...>List of functions to always consider for inlining
--frame-opt=<value>Optimize stack frame accesses
none: do not perform frame optimizationhot: perform FOP on hot functionsall: perform FOP on all functions
--frame-opt-rm-storesApply additional analysis to remove stores (experimental)
--function-order=<string>File containing an ordered list of functions to use for function reordering
--generate-function-order=<string>File to dump the ordered list of functions to use for function reordering
--generate-link-sections=<string>Generate a list of function sections in a format suitable for inclusion in a
linker script--group-stubsShare stubs across functions
--hugifyAutomatically put hot code on 2MB page(s) (hugify) at runtime. No manual call
to hugify is needed in the binary (which is what --hot-text relies on).--icf=<value>Fold functions with identical code
all: Enable identical code foldingnone: Disable identical code folding (default)safe: Enable safe identical code folding
--icpAlias for --indirect-call-promotion
--icp-calls-remaining-percent-threshold=The percentage threshold against remaining unpromoted indirect call count for
the promotion for calls--icp-calls-topnAlias for --indirect-call-promotion-calls-topn
--icp-calls-total-percent-threshold=The percentage threshold against total count for the promotion for calls
--icp-eliminate-loadsEnable load elimination using memory profiling data when performing ICP
--icp-funcs=<func1,func2,func3,...>List of functions to enable ICP for
--icp-inlineOnly promote call targets eligible for inlining
--icp-jt-remaining-percent-threshold=The percentage threshold against remaining unpromoted indirect call count for
the promotion for jump tables--icp-jt-targetsAlias for --icp-jump-tables-targets
--icp-jt-topnAlias for --indirect-call-promotion-jump-tables-topn
--icp-jt-total-percent-threshold=The percentage threshold against total count for the promotion for jump tables
--icp-jump-tables-targetsFor jump tables, optimize indirect jmp targets instead of indices
--icp-mp-thresholdAlias for --indirect-call-promotion-mispredict-threshold
--icp-old-code-sequenceUse old code sequence for promoted calls
--icp-top-callsites=Optimize hottest calls until at least this percentage of all indirect calls
frequency is covered. 0 = all callsites--icp-topnAlias for --indirect-call-promotion-topn
--icp-use-mpAlias for --indirect-call-promotion-use-mispredicts
--indirect-call-promotion=<value>Indirect call promotion
none: do not perform indirect call promotioncalls: perform ICP on indirect callsjump-tables: perform ICP on jump tablesall: perform ICP on calls and jump tables
--indirect-call-promotion-calls-topn=Limit number of targets to consider when doing indirect call promotion on
calls. 0 = no limit--indirect-call-promotion-jump-tables-topn=Limit number of targets to consider when doing indirect call promotion on jump
tables. 0 = no limit--indirect-call-promotion-topn=Limit number of targets to consider when doing indirect call promotion. 0 = no
limit--indirect-call-promotion-use-mispredictsUse misprediction frequency for determining whether or not ICP should be
applied at a callsite. The -indirect-call-promotion-mispredict-threshold
value will be used by this heuristic--infer-fall-throughsInfer execution count for fall-through blocks
--infer-stale-profileInfer counts from stale profile data.
--inline-allInline all functions
--inline-apAdjust function profile after inlining
--inline-limit=Maximum number of call sites to inline
--inline-max-iters=Maximum number of inline iterations
--inline-memcpyInline memcpy using optimized instruction sequences (X86: 'rep movsb', AArch64: width-optimized register operations)
--inline-small-functionsInline functions if increase in size is less than defined by -inline-small-
functions-bytes--inline-small-functions-bytes=Max number of bytes for the function to be considered small for inlining
purposes--instrumentInstrument code to generate accurate profile data
--iterative-guessIn non-LBR mode, guess edge counts using iterative technique
--jt-footprint-optimize-for-icacheWith jt-footprint-reduction, only process PIC jumptables and turn off other
transformations that increase code size--jt-footprint-reductionMake jump tables size smaller at the cost of using more instructions at jump
sites--jump-tables=<value>Jump tables support (default=basic)
none: do not optimize functions with jump tablesbasic: optimize functions with jump tablesmove: move jump tables to a separate sectionsplit: split jump tables section into hot and cold based on function
execution frequencyaggressive: aggressively split jump tables section based on usage of the
tables
--keep-nopsKeep no-op instructions. By default they are removed.
--lite-threshold-count=Similar to '-lite-threshold-pct' but specify threshold using absolute function
call count. I.e. limit processing to functions executed at least the specified
number of times.--lite-threshold-pct=Threshold (in percent) for selecting functions to process in lite mode. Higher
threshold means fewer functions to process. E.g threshold of 90 means only top
10 percent of functions with profile will be processed.--match-with-call-graphMatch functions with call graph
--memcpy1-spec=<func1,func2:cs1:cs2,func3:cs1,...>List of functions with call sites for which to specialize memcpy() for size 1
--min-branch-clustersUse a modified clustering algorithm geared towards minimizing branches
--name-similarity-function-matching-threshold=Match functions using namespace and edit distance.
--no-inlineDisable all inlining (overrides other inlining options)
--no-scanDo not scan cold functions for external references (may result in slower binary)
--peepholes=<value>Enable peephole optimizations
none: disable peepholesdouble-jumps: remove double jumps when abletailcall-traps: insert tail call trapsuseless-branches: remove useless conditional branchesall: enable all peephole optimizations
--plt=<value>Optimize PLT calls (requires linking with -znow on non-x86 architectures)
none: do not optimize PLT callshot: optimize executed (hot) PLT callsall: optimize all PLT calls
--preserve-blocks-alignmentTry to preserve basic block alignment
--profile-ignore-hashIgnore hash while reading function profile
--profile-use-dfsUse DFS order for YAML profile
--reg-reassignReassign registers so as to avoid using REX prefixes in hot code
--reorder-blocks=<value>Change layout of basic blocks in a function
none: do not reorder basic blocksreverse: layout blocks in reverse ordernormal: perform optimal layout based on profilebranch-predictor: perform optimal layout prioritizing branch predictionscache: perform optimal layout prioritizing I-cache behaviorcache+: perform layout optimizing I-cache behaviorext-tsp: perform layout optimizing I-cache behaviorcluster-shuffle: perform random layout of clusters
--reorder-data=<section1,section2,section3,...>List of sections to reorder
--reorder-data-algo=<value>Algorithm used to reorder data sections
count: sort hot data by read countsfuncs: sort hot data by hot function usage and count
--reorder-data-inplaceReorder data sections in place
--reorder-data-max-bytes=Maximum number of bytes to reorder
--reorder-data-max-symbols=Maximum number of symbols to reorder
--reorder-functions=<value>Reorder and cluster functions (works only with relocations)
none: do not reorder functionsexec-count: order by execution counthfsort: use hfsort algorithmhfsort+: use cache-directed sortcdsort: use cache-directed sortpettis-hansen: use Pettis-Hansen algorithmrandom: reorder functions randomlyuser: use function order specified by -function-order
--reorder-functions-use-hot-sizeUse a function's hot size when doing clustering
--report-bad-layout=Print top functions with suboptimal code layout on input
--report-stalePrint the list of functions with stale profile
--runtime-hugify-lib=<string>Specify file name of the runtime hugify library
--runtime-instrumentation-lib=<string>Specify file name of the runtime instrumentation library
--runtime-lib-init-hook=<value>Primary target for hooking runtime library initialization, used in
fallback order of availability in input binary (entry_point -> init
-> init_array) (default: entry_point)entry_point: use ELF Header Entry Pointinit: use ELF DT_INIT entryinit_array: use ELF .init_array entry
--sctc-mode=<value>Mode for simplify conditional tail calls
always: always perform sctcpreserve: only perform sctc when branch direction is preservedheuristic: use branch prediction data to control sctc
--sequential-disassemblyPerforms disassembly sequentially
--shrink-wrapping-threshold=Percentage of prologue execution count to use as threshold when evaluating
whether a block is cold enough to be profitable to move eligible spills there--simplify-conditional-tail-callsSimplify conditional tail calls by removing unnecessary jumps
--simplify-rodata-loadsSimplify loads from read-only sections by replacing the memory operand with
the constant found in the corresponding section--split-align-threshold=When deciding to split a function, apply this alignment while doing the size
comparison (see -split-threshold). Default value: 2.--split-all-coldOutline as many cold basic blocks as possible
--split-ehSplit C++ exception handling code
--split-functionsSplit functions into fragments
--split-strategy=<value>Strategy used to partition blocks into fragments
profile2: split each function into a hot and cold fragment using profiling
informationcdsplit: split each function into a hot, warm, and cold fragment using
profiling informationrandom2: split each function into a hot and cold fragment at a randomly
chosen split point (ignoring any available profiling information)randomN: split each function into N fragments at a randomly chosen split
points (ignoring any available profiling information)all: split all basic blocks of each function into fragments such that each
fragment contains exactly a single basic block
--split-threshold=Split function only if its main size is reduced by more than given amount of
bytes. Default value: 0, i.e. split iff the size is reduced. Note that on some
architectures the size can increase after splitting.--stale-matching-max-func-size=The maximum size of a function to consider for inference.
--stale-matching-min-matched-block=Percentage threshold of matched basic blocks at which stale profile inference
is executed.--stale-threshold=Maximum percentage of stale functions to tolerate (default: 100)
--stokeTurn on the stoke analysis
--strip-rep-retStrip 'repz' prefix from 'repz retq' sequence (on by default)
--tail-duplication=<value>Duplicate unconditional branches that cross a cache line
none: do not applyaggressive: aggressive strategymoderate: moderate strategycache: cache-aware duplication strategy
--tsp-threshold=Maximum number of hot basic blocks in a function for which to use a precise
TSP solution while re-ordering basic blocks--use-aggr-reg-reassignUse register liveness analysis to try to find more opportunities for -reg-
reassign optimization--use-compact-alignerUse compact approach for aligning functions
--use-edge-countsUse edge count data when doing clustering
--verify-cfgVerify the CFG after every pass
--x86-align-branch-boundary-hot-onlyOnly apply branch boundary alignment in hot code
--x86-strip-redundant-address-sizeRemove redundant Address-Size override prefix
BOLT instrumentation options:
llvm-bolt <executable> -instrument [-o outputfile]
--conservative-instrumentationDisable instrumentation optimizations that sacrifice profile accuracy (for
debugging, default: false)--instrument-callsRecord profile for inter-function control flow activity (default: true)
--instrument-hot-onlyOnly insert instrumentation on hot functions (needs profile, default: false)
--instrumentation-binpath=<string>Path to instrumented binary in case if /proc/self/map_files is not accessible
due to access restriction issues--instrumentation-file=<string>File name where instrumented profile will be saved (default: /tmp/prof.fdata)
--instrumentation-file-append-pidAppend PID to saved profile file name (default: false)
--instrumentation-no-counters-clearDon't clear counters across dumps (use with instrumentation-sleep-time option)
--instrumentation-sleep-time=Interval between profile writes (default: 0 = write only at program end).
This is useful for service workloads when you want to dump profile every X
minutes or if you are killing the program and the profile is not being dumped
at the end.--instrumentation-wait-forksWait until all forks of instrumented process will finish (use with
instrumentation-sleep-time option)
BOLT printing options:
--print-aliasesPrint aliases when printing objects
--print-allPrint functions after each stage
--print-cfgPrint functions after CFG construction
--print-debug-infoPrint debug info when printing functions
--print-disasmPrint function after disassembly
--print-dyno-opcode-stats=Print per instruction opcode dyno stats and the functionnames:BB offsets of
the nth highest execution counts--print-dyno-stats-onlyWhile printing functions output dyno-stats and skip instructions
--print-exceptionsPrint exception handling data
--print-globalsPrint global symbols after disassembly
--print-jump-tablesPrint jump tables
--print-loopsPrint loop related information
--print-mem-dataPrint memory data annotations when printing functions
--print-normalizedPrint functions after CFG is normalized
--print-only=<func1,func2,func3,...>List of functions to print
--print-orcPrint ORC unwind information for instructions
--print-profilePrint functions after attaching profile
--print-profile-statsPrint profile quality/bias analysis
--print-pseudo-probes=<value>Print pseudo probe info
decode: decode probes section from binaryaddress_conversion: update address2ProbesMap with output block addressencoded_probes: display the encoded probes in binary sectionall: enable all debugging printout
--print-relocationsPrint relocations when printing functions/objects
--print-reordered-dataPrint section contents after reordering
--print-retpoline-insertionPrint functions after retpoline insertion pass
--print-sdtPrint all SDT markers
--print-sectionsPrint all registered sections
--print-unknownPrint names of functions with unknown control flow
--time-buildPrint time spent constructing binary functions
--time-rewritePrint time spent in rewriting passes
--print-after-branch-fixupPrint function after fixing local branches
--print-after-jt-footprint-reductionPrint function after jt-footprint-reduction pass
--print-after-loweringPrint function after instruction lowering
--print-cache-metricsCalculate and print various metrics for instruction cache
--print-clustersPrint clusters
--print-estimate-edge-countsPrint function after edge counts are set for no-LBR profile
--print-finalizedPrint function after CFG is finalized
--print-fix-relaxationsPrint functions after fix relaxations pass
--print-fix-riscv-callsPrint functions after fix RISCV calls pass
--print-fopPrint functions after frame optimizer pass
--print-function-statistics=Print statistics about basic block ordering
--print-icfPrint functions after ICF optimization
--print-icpPrint functions after indirect call promotion
--print-inlinePrint functions after inlining optimization
--print-large-functionsPrint functions that could not be overwritten due to excessive size
--print-longjmpPrint functions after longjmp pass
--print-optimize-bodylessPrint functions after bodyless optimization
--print-output-address-rangePrint output address range for each basic block in the function
whenBinaryFunction::print is called--print-peepholesPrint functions after peephole optimization
--print-pltPrint functions after PLT optimization
--print-regreassignPrint functions after regreassign pass
--print-reorderedPrint functions after layout optimization
--print-reordered-functionsPrint functions after clustering
--print-sctcPrint functions after conditional tail call simplification
--print-simplify-rodata-loadsPrint functions after simplification of RO data loads
--print-sorted-by=<value>Print functions sorted by order of dyno stats
executed-forward-branches: executed forward branchestaken-forward-branches: taken forward branchesexecuted-backward-branches: executed backward branchestaken-backward-branches: taken backward branchesexecuted-unconditional-branches: executed unconditional branchesall-function-calls: all function callsindirect-calls: indirect callsPLT-calls: PLT callsexecuted-instructions: executed instructionsexecuted-load-instructions: executed load instructionsexecuted-store-instructions: executed store instructionstaken-jump-table-branches: taken jump table branchestaken-unknown-indirect-branches: taken unknown indirect branchestotal-branches: total branchestaken-branches: taken branchesnon-taken-conditional-branches: non-taken conditional branchestaken-conditional-branches: taken conditional branchesall-conditional-branches: all conditional brancheslinker-inserted-veneer-calls: linker-inserted veneer callsall: sorted by all names
--print-sorted-by-order=<value>Use ascending or descending order when printing functions ordered by dyno stats
--print-splitPrint functions after code splitting
--print-stokePrint functions after stoke analysis
--print-ucePrint functions after unreachable code elimination
--print-veneer-eliminationPrint functions after veneer elimination pass
--time-optsPrint time spent in each optimization
--print-all-optionsPrint all option values after command line parsing
--print-optionsPrint non-default options after command line parsing
Bolt Docs GettingStarted (`bolt/docs/GettingStarted.md`)
Getting Started with BOLT
Input Binary Requirements
BOLT operates on X86-64 and AArch64 ELF binaries. At the minimum, the
binaries should have an unstripped symbol table, and, to get maximum
performance gains, they should be linked with relocations
(--emit-relocs or -q linker flag).
BOLT disassembles functions and reconstructs the control flow graph
(CFG) before it runs optimizations. Since this is a nontrivial task,
especially when indirect branches are present, we rely on certain
heuristics to accomplish it. These heuristics have been tested on a code
generated with Clang and GCC compilers. The main requirement for C/C++
code is not to rely on code layout properties, such as function pointer
deltas. Assembly code can be processed too. Requirements for it include
a clear separation of code and data, with data objects being placed into
data sections/segments. If indirect jumps are used for intra-function
control transfer (e.g., jump tables), the code patterns should be
matching those generated by Clang/GCC.
NOTE: BOLT is currently incompatible with the-freorder-blocks-and-partition compiler option. Since GCC8 enables
this option by default, you have to explicitly disable it by adding-fno-reorder-blocks-and-partition flag if you are compiling with
GCC8 or above.
NOTE2: DWARF v5 is the new debugging format generated by the latest LLVM
and GCC compilers. It offers several benefits over the previous DWARF
v4. Currently, the support for v5 is a work in progress for BOLT. While
you will be able to optimize binaries produced by the latest compilers,
until the support is complete, you will not be able to update the debug
info with -update-debug-sections. To temporarily work around the
issue, we recommend compiling binaries with -gdwarf-4 option that
forces DWARF v4 output.
PIE and .so support has been added recently. Please report bugs if you
encounter any issues.
Installation
Docker Image
You can build and use the docker image containing BOLT using our docker
file.
Alternatively, you can build BOLT manually using the steps below.
Manual Build
BOLT heavily uses LLVM libraries, and by design, it is built as one of
LLVM tools. The build process is not much different from a regular LLVM
build. The following instructions are assuming that you are running
under Linux.
Start with cloning LLVM repo:
> git clone https://github.com/llvm/llvm-project.git
> mkdir build
> cd build
> cmake -G Ninja ../llvm-project/llvm -DLLVM_TARGETS_TO_BUILD="X86;AArch64" -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_ENABLE_PROJECTS="bolt"
> ninja boltllvm-bolt will be available under bin/. Add this directory to
your path to ensure the rest of the commands in this tutorial work.
Optimizing BOLT’s Performance
BOLT runs many internal passes in parallel. If you foresee heavy usage
of BOLT, you can improve the processing time by linking against one of
memory allocation libraries with good support for concurrency. E.g. to
use jemalloc:
> sudo yum install jemalloc-devel
> LD_PRELOAD=/usr/lib64/libjemalloc.so llvm-bolt ....Or if you rather use tcmalloc:
> sudo yum install gperftools-devel
> LD_PRELOAD=/usr/lib64/libtcmalloc_minimal.so llvm-bolt ....Usage
For a complete practical guide of using BOLT see {doc}OptimizingClang.
Step 0
In order to allow BOLT to re-arrange functions (in addition to
re-arranging code within functions) in your program, it needs a little
help from the linker. Add --emit-relocs to the final link step of
your application. You can verify the presence of relocations by checking
for .rela.text section in the binary. BOLT will also report if it
detects relocations while processing the binary.
Step 1: Collect Profile
This step is different for different kinds of executables. If you can
invoke your program to run on a representative input from a command
line, then check For Applications section below. If your program
typically runs as a server/service, then skip to For Services
section.
The version of perf command used for the following steps has to
support -F brstack option. We recommend using perf version 4.5
or later.
For Applications
This assumes you can run your program from a command line with a typical
input. In this case, simply prepend the command line invocation withperf:
$ perf record -e cycles:u -j any,u -o perf.data -- <executable> <args> ...For Services
Once you get the service deployed and warmed-up, it is time to collect
perf data with LBR (branch information). The exact perf command to use
will depend on the service. E.g., to collect the data for all processes
running on the server for the next 3 minutes use:
$ perf record -e cycles:u -j any,u -a -o perf.data -- sleep 180Depending on the application, you may need more samples to be included
with your profile. It’s hard to tell upfront what would be a sweet spot
for your application. We recommend the profile to cover 1B instructions
as reported by BOLT -dyno-stats option. If you need to increase the
number of samples in the profile, you can either run the sleep
command for longer and use -F<N> option with perf to increase
sampling frequency.
Note that for profile collection we recommend using cycle events and notBR_INST_RETIRED.*. Empirically we found it to produce better
results.
If the collection of a profile with branches is not available, e.g.,
when you run on a VM or on hardware that does not support it, then you
can use only sample events, such as cycles. In this case, the quality of
the profile information would not be as good, and performance gains with
BOLT are expected to be lower.
With instrumentation
If perf record is not available to you, you may collect profile by first
instrumenting the binary with BOLT and then running it.
llvm-bolt <executable> -instrument -o <instrumented-executable>After you run instrumented-executable with the desired workload, its
BOLT profile should be ready for you in /tmp/prof.fdata and you can
skip Step 2.
Run BOLT with the -help option and check the category “BOLT
instrumentation options” for a quick reference on instrumentation knobs.
Step 2: Convert Profile to BOLT Format
NOTE: you can skip this step and feed perf.data directly to BOLT
using experimental -p perf.data option.
For this step, you will need perf.data file collected from the
previous step and a copy of the binary that was running. The binary has
to be either unstripped, or should have a symbol table intact (i.e.,
running strip -g is okay).
Make sure perf is in your PATH, and execute perf2bolt:
$ perf2bolt -p perf.data -o perf.fdata <executable>This command will aggregate branch data from perf.data and store it
in a format that is both more compact and more resilient to binary
modifications.
If the profile was collected without brstacks, you will need to add -ba flag to
the command line above.
Step 3: Optimize with BOLT
Once you have perf.fdata ready, you can use it for optimizations
with BOLT. Assuming your environment is setup to include the right path,
execute llvm-bolt:
$ llvm-bolt <executable> -o <executable>.bolt -data=perf.fdata -reorder-blocks=ext-tsp -reorder-functions=hfsort -split-functions -split-all-cold -split-eh -dyno-statsIf you do need an updated debug info, then add-update-debug-sections option to the command above. The processing
time will be slightly longer.
For a full list of options see -help/-help-hidden output.
The input binary for this step does not have to 100% match the binary
used for profile collection in Step 1. This could happen when you
are doing active development, and the source code constantly changes,
yet you want to benefit from profile-guided optimizations. However,
since the binary is not precisely the same, the profile information
could become invalid or stale, and BOLT will report the number of
functions with a stale profile. The higher the number, the less
performance improvement should be expected. Thus, it is crucial to
update .fdata for release branches.
Multiple Profiles
Suppose your application can run in different modes, and you can
generate multiple profiles for each one of them. To generate a single
binary that can benefit all modes (assuming the profiles don’t
contradict each other) you can use merge-fdata tool:
$ merge-fdata *.fdata > combined.fdataUse combined.fdata for Step 3 above to generate a universally
optimized binary.
Profile Formats
See {doc}profiles for comprehensive documentation of all profile formats
accepted by BOLT: perf.data, fdata, YAML, and pre-aggregated.
License
BOLT is licensed under the Apache License v2.0 with LLVM
Exceptions.
Bolt Docs Heatmaps (`bolt/docs/Heatmaps.md`)
Code Heatmaps
BOLT has gained the ability to print code heatmaps based on
sampling-based profiles generated by perf, either with brstack data or not.
The output is produced in colored ASCII to be displayed in a color-capable
terminal. It looks something like this:
Heatmaps can be generated for BOLTed and non-BOLTed binaries. You can
use them to compare the code layout before and after optimizations.
To generate a heatmap, start with running your app under perf:
$ perf record -e cycles:u -j any,u -- <executable with args>or if you want to monitor the existing process(es):
$ perf record -e cycles:u -j any,u [-p PID|-a] -- sleep <interval>Running with brstack (-j any,u or -b) is recommended. Heatmaps can be generated
from basic events by using the llvm-bolt-heatmap option -ba (basic events) but
such heatmaps do not have the coverage provided by brstack and may only be useful
for finding event hotspots at larger code block granularities.
Once the run is complete, and perf.data is generated, run llvm-bolt-heatmap:
$ llvm-bolt-heatmap -p perf.data <executable>By default the heatmap will be dumped to stdout. You can change it
with -o <heatmapfile> option.
If you prefer to look at the data in a browser (or would like to share
it that way), then you can use an HTML conversion tool. E.g.:
$ aha -b -f <heatmapfile> > <heatmapfile>.htmlBackground on heatmaps:
A heatmap is effectively a histogram that is rendered into a grid for better
visualization.
In theory we can generate a heatmap using any binary and a perf profile.
Each block/character in the heatmap shows the execution data accumulated for
corresponding 64 bytes of code. You can change this granularity with a-block-size option.
E.g. set it to 4096 to see code usage grouped by 4K pages.
When a block is shown as a dot, it means that no samples were found for that
address.
When it is shown as a letter, it indicates a captured sample on a particular
text section of the binary.
To show a mapping between letters and text sections in the legend, use-print-mappings.
When a sampled address does not belong to any of the text sections, the
characters 'o' or 'O' will be shown.
The legend shows by default the ranges in the heatmap according to the number
of samples per block.
A color is assigned per range, except the first two ranges that distinguished by
lower and upper case letters.
On the Y axis, each row/line starts with an actual address of the binary.
Consecutive lines in the heatmap advance by the same amount, with the binary
size covered by a line dependent on the block size and the line size.
An empty new line is inserted for larger gaps between samples.
On the X axis, the horizontally emitted hex numbers can help estimate where
in the line the samples lie, but they cannot be combined to provide a full
address, as they are relative to both the bucket and line sizes.
In the example below, the highlighted 0x100 column is not an offset to each
row's address, but instead, it points to the middle of the line.
For the generation, the default bucket size was used with a line size of 128.
Some useful options are:
-line-size=<uint> - number of entries per line (default 256).
Use a smaller value (e.g. 128) if the heatmap doesn't fit
the screen horizontally.
-block-size=<initial size>[,<zoom-out size>,...] - heatmap bucket size,
optionally followed by zoom-out sizes to produce coarse-
grained heatmaps. Size can be specified in human-readable
format with [kKmMgG][i][B] suffix. Default 64B, 4K, 256K.
-max-address=<uint> - maximum address considered valid for heatmap (default 4GB)
-print-mappings - print mappings in the legend, between characters/blocks and text sections (default false)Bolt Docs NewBackend (`bolt/docs/NewBackend.md`)
Adding a New BOLT Backend
A new BOLT backend should follow LLVM's
policy for adding a new target.
New BOLT targets are experimental and are not built by default.
Most target-specific work should be isolated in an MCPlusBuilder
implementation under bolt/lib/Target/<Target>, together with
platform-specific relocation handling in bolt/lib/Core/Relocation.cpp.
Changes to common infrastructure, particularly MCPlusBuilder, must be
separate from the target changes and land first. Such changes require thorough
tests and must state their processing-time and peak-memory impact on existing
targets. Measurements should use large binaries, including at least Clang
itself.
A backend does not need to implement every MCPlusBuilder interface initially.
However, omitted interfaces must be checked against their callers. Analyses and
passes that depend on missing functionality, such as stack frame analysis or
register liveness, must remain disabled.
BOLT passes are not cleanly partitioned into common and target-specific code.
Enable them for a new target only after auditing their target assumptions and
proving them stable with target-specific and end-to-end tests. Otherwise, keep
them disabled.
RISC targets generally need the LongJmp pass for branch relaxation. This
requires correct branch-range checks and short- and long-jump construction,
with tests covering range boundaries, calls, tail calls, and linker relaxation.
The initial backend should include focused tests for instruction analysis,
relocations, CFG construction, rewriting, and each enabled pass. Validation
must include rewriting and executing large real-world binaries, with Clang as
the minimum large-binary test.
Bolt Docs OptimizingClang (`bolt/docs/OptimizingClang.md`)
Optimizing Clang : A Practical Example of Applying BOLT
Preface
BOLT (Binary Optimization and Layout Tool) is designed to improve the application
performance by laying out code in a manner that helps CPU better utilize its caching and
branch predicting resources.
The most obvious candidates for BOLT optimizations
are programs that suffer from many instruction cache and iTLB misses, such as
large applications measuring over hundreds of megabytes in size. However, medium-sized
programs can benefit too. Clang, one of the most popular open-source C/C++ compilers,
is a good example of the latter. Its code size could easily be in the order of tens of megabytes.
As we will see, the Clang binary suffers from many instruction cache
misses and can be significantly improved with BOLT, even on top of profile-guided and
link-time optimizations.
In this tutorial we will first build Clang with PGO and LTO, and then will show steps on how to
apply BOLT optimizations to make Clang up to 15% faster. We will also analyze where
the compile-time performance gains are coming from, and verify that the speed-ups are
sustainable while building other applications.
Building Clang
The process of getting Clang sources and performing the build is very similar to the
one described at http://clang.llvm.org/get_started.html. For completeness, we provide the detailed steps
on how to obtain and build Clang in Bootstrapping Clang-7 with PGO and LTO section.
The only difference from the standard Clang build is that we require the -Wl,-q flag to be present during
the final link. This option saves relocation metadata in the executable file, but does not affect
the generated code in any way.
Optimizing Clang with BOLT
We will use the setup described in Bootstrapping Clang-7 with PGO and LTO.
Adjust the steps accordingly if you skipped that section. We will also assume that llvm-bolt is present in your $PATH.
Before we can run BOLT optimizations, we need to collect the profile for Clang, and we will use
Clang/LLVM sources for that.
Collecting accurate profile requires running perf on a hardware that
implements taken branch sampling (-b/-j flag). For that reason, it may not be possible to
collect the accurate profile in a virtualized environment, e.g. in the cloud.
We do support regular sampling profiles, but the performance
improvements are expected to be more modest.
$ mkdir ${TOPLEV}/stage3
$ cd ${TOPLEV}/stage3
$ CPATH=${TOPLEV}/stage2-prof-use-lto/install/bin/
$ cmake -G Ninja ${TOPLEV}/llvm -DLLVM_TARGETS_TO_BUILD=X86 -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_COMPILER=$CPATH/clang -DCMAKE_CXX_COMPILER=$CPATH/clang++ \
-DLLVM_ENABLE_PROJECTS="clang" \
-DLLVM_USE_LINKER=lld -DCMAKE_INSTALL_PREFIX=${TOPLEV}/stage3/install
$ perf record -e cycles:u -j any,u -- ninja clangOnce the last command is finished, it will create a perf.data file larger than 10GiB.
We will first convert this profile into a more compact aggregated
form suitable to be consumed by BOLT:
$ perf2bolt $CPATH/clang-7 -p perf.data -o clang-7.fdata -w clang-7.yamlNotice that we are passing clang-7 to perf2bolt which is the real binary thatclang and clang++ are symlinking to. The next step will optimize Clang using
the generated profile:
$ llvm-bolt $CPATH/clang-7 -o $CPATH/clang-7.bolt -b clang-7.yaml \
-reorder-blocks=ext-tsp -reorder-functions=hfsort+ -split-functions \
-split-all-cold -dyno-stats -icf=1 -use-gnu-stackThe output will look similar to the one below:
...
BOLT-INFO: enabling relocation mode
BOLT-INFO: 11415 functions out of 104526 simple functions (10.9%) have non-empty execution profile.
...
BOLT-INFO: ICF folded 29144 out of 105177 functions in 8 passes. 82 functions had jump tables.
BOLT-INFO: Removing all identical functions will save 5466.69 KB of code space. Folded functions were called 2131985 times based on profile.
BOLT-INFO: basic block reordering modified layout of 7848 (10.32%) functions
...
660155947 : executed forward branches (-2.3%)
48252553 : taken forward branches (-57.2%)
129897961 : executed backward branches (+13.8%)
52389551 : taken backward branches (-19.5%)
35650038 : executed unconditional branches (-33.2%)
128338874 : all function calls (=)
19010563 : indirect calls (=)
9918250 : PLT calls (=)
6113398840 : executed instructions (-0.6%)
1519537463 : executed load instructions (=)
943321306 : executed store instructions (=)
20467109 : taken jump table branches (=)
825703946 : total branches (-2.1%)
136292142 : taken branches (-41.1%)
689411804 : non-taken conditional branches (+12.6%)
100642104 : taken conditional branches (-43.4%)
790053908 : all conditional branches (=)
...The statistics in the output is based on the brstack profile (LBR) collected with perf, and since we were using
the cycles counter, its accuracy is affected. However, the relative improvement in taken conditional branches is a good indication that BOLT was able to straighten out the code even after PGO.
Measuring Compile-time Improvement
clang-7.bolt can be used as a replacement for PGO+LTO Clang:
$ mv $CPATH/clang-7 $CPATH/clang-7.org
$ ln -fs $CPATH/clang-7.bolt $CPATH/clang-7Doing a new build of Clang using the new binary shows a significant overall
build time reduction on a 48-core Haswell system:
$ ln -fs $CPATH/clang-7.org $CPATH/clang-7
$ ninja clean && /bin/time -f %e ninja clang -j48
202.72
$ ln -fs $CPATH/clang-7.bolt $CPATH/clang-7
$ ninja clean && /bin/time -f %e ninja clang -j48
180.11That's 22.61 seconds (or 12%) faster compared to the PGO+LTO build.
Notice that we are measuring an improvement of the total build time, which includes the time spent in the linker.
Compilation time improvements for individual files differ, and speedups over 15% are not uncommon.
If we run BOLT on a Clang binary compiled without PGO+LTO (in which case the build is finished in 253.32 seconds),
the gains we see are over 50 seconds (25%),
but, as expected, the result is still slower than PGO+LTO+BOLT build.
Source of the Wins
We mentioned that Clang suffers from considerable instruction cache misses. This can be measured with perf:
$ ln -fs $CPATH/clang-7.org $CPATH/clang-7
$ ninja clean && perf stat -e instructions,L1-icache-misses -- ninja clang -j48
...
16,366,101,626,647 instructions
359,996,216,537 L1-icache-missesThat's about 22 instruction cache misses per thousand instructions. As a rule of thumb, if the application
has over 10 misses per thousand instructions, it is a good indication that it will be improved by BOLT.
Now let's see how many misses are in the BOLTed binary:
$ ln -fs $CPATH/clang-7.bolt $CPATH/clang-7
$ ninja clean && perf stat -e instructions,L1-icache-misses -- ninja clang -j48
...
16,319,818,488,769 instructions
244,888,677,972 L1-icache-missesThe number of misses per thousand instructions went down from 22 to 15, significantly reducing
the number of stalls in the CPU front-end.
Notice how the number of executed instructions stayed roughly the same. That's because we didn't
run any optimizations beyond the ones affecting the code layout. Other than instruction cache misses,
BOLT also improves branch mispredictions, iTLB misses, and misses in L2 and L3.
Using Clang for Other Applications
We have collected profile for Clang using its own source code. Would it be enough to speed up
the compilation of other projects? We picked mysqld, an open-source database, to do the test.
On our 48-core Haswell system using the PGO+LTO Clang, the build finished in 136.06 seconds, while using the PGO+LTO+BOLT Clang, 126.10 seconds.
That's a noticeable improvement, but not as significant as the one we saw on Clang itself.
This is partially because the number of instruction cache misses is slightly lower on this scenario : 19 vs 22.
Another reason is that Clang is run with a different set of options while building mysqld compared
to the training run.
Different options exercise different code paths, and
if we trained without a specific option, we may have misplaced parts of the code responsible for handling it.
To test this theory, we have collected another perf profile while building mysqld, and merged it with an existing profile
using the merge-fdata utility that comes with BOLT. Optimized with that profile, the PGO+LTO+BOLT Clang was able
to perform the mysqld build in 124.74 seconds, i.e. 11 seconds or 9% faster compared to PGO+LGO Clang.
The merged profile didn't make the original Clang compilation slower either, while the number of profiled functions in Clang increased from 11,415 to 14,025.
Ideally, the profile run has to be done with a superset of all commonly used options. However, the main improvement is expected with just the basic set.
Summary
In this tutorial we demonstrated how to use BOLT to improve the
performance of the Clang compiler. Similarly, BOLT could be used to improve the performance
of GCC, or any other application suffering from a high number of instruction
cache misses.
Appendix
Bootstrapping Clang-7 with PGO and LTO
Below we describe detailed steps to build Clang, and make it ready for BOLT
optimizations. If you already have the build setup, you can skip this section,
except for the last step that adds -Wl,-q linker flag to the final build.
Getting Clang-7 Sources
Set $TOPLEV to the directory of your preference where you would like to do
builds. E.g. TOPLEV=~/clang-7/. Follow with commands to clone the release_70
branch of LLVM monorepo:
$ mkdir ${TOPLEV}
$ cd ${TOPLEV}
$ git clone --branch=release/7.x https://github.com/llvm/llvm-project.gitBuilding Stage 1 Compiler
Stage 1 will be the first build we are going to do, and we will be using the
default system compiler to build Clang. If your system lacks a compiler, use
your distribution package manager to install one that supports C++11. In this
example we are going to use GCC. In addition to the compiler, you will need thecmake and ninja packages. Note that we disable the build of certain
compiler-rt components that are known to cause build issues at release/7.x.
$ mkdir ${TOPLEV}/stage1
$ cd ${TOPLEV}/stage1
$ cmake -G Ninja ${TOPLEV}/llvm-project/llvm -DLLVM_TARGETS_TO_BUILD=X86 \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_ASM_COMPILER=gcc \
-DLLVM_ENABLE_PROJECTS="clang;lld" \
-DLLVM_ENABLE_RUNTIMES="compiler-rt" \
-DCOMPILER_RT_BUILD_SANITIZERS=OFF -DCOMPILER_RT_BUILD_XRAY=OFF \
-DCOMPILER_RT_BUILD_LIBFUZZER=OFF \
-DCMAKE_INSTALL_PREFIX=${TOPLEV}/stage1/install
$ ninja installBuilding Stage 2 Compiler With Instrumentation
Using the freshly-baked stage 1 Clang compiler, we are going to build Clang with
profile generation capabilities:
$ mkdir ${TOPLEV}/stage2-prof-gen
$ cd ${TOPLEV}/stage2-prof-gen
$ CPATH=${TOPLEV}/stage1/install/bin/
$ cmake -G Ninja ${TOPLEV}/llvm-project/llvm -DLLVM_TARGETS_TO_BUILD=X86 \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_COMPILER=$CPATH/clang -DCMAKE_CXX_COMPILER=$CPATH/clang++ \
-DLLVM_ENABLE_PROJECTS="clang;lld" \
-DLLVM_USE_LINKER=lld -DLLVM_BUILD_INSTRUMENTED=ON \
-DCMAKE_INSTALL_PREFIX=${TOPLEV}/stage2-prof-gen/install
$ ninja installGenerating Profile for PGO
While there are many ways to obtain the profile data, we are going to use the
source code already at our disposal, i.e. we are going to collect the profile
while building Clang itself:
$ mkdir ${TOPLEV}/stage3-train
$ cd ${TOPLEV}/stage3-train
$ CPATH=${TOPLEV}/stage2-prof-gen/install/bin
$ cmake -G Ninja ${TOPLEV}/llvm-project/llvm -DLLVM_TARGETS_TO_BUILD=X86 \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_COMPILER=$CPATH/clang -DCMAKE_CXX_COMPILER=$CPATH/clang++ \
-DLLVM_ENABLE_PROJECTS="clang" \
-DLLVM_USE_LINKER=lld -DCMAKE_INSTALL_PREFIX=${TOPLEV}/stage3-train/install
$ ninja clangOnce the build is completed, the profile files will be saved under${TOPLEV}/stage2-prof-gen/profiles. We will merge them before they can be
passed back into Clang:
$ cd ${TOPLEV}/stage2-prof-gen/profiles
$ ${TOPLEV}/stage1/install/bin/llvm-profdata merge -output=clang.profdata *Building Clang with PGO and LTO
Now the profile can be used to guide optimizations to produce better code for
our scenario, i.e. building Clang. We will also enable link-time optimizations
to allow cross-module inlining and other optimizations. Finally, we are going to
add one extra step that is useful for BOLT: a linker flag instructing it to
preserve relocations in the output binary. Note that this flag does not affect
the generated code or data used at runtime, it only writes metadata to the file
on disk:
$ mkdir ${TOPLEV}/stage2-prof-use-lto
$ cd ${TOPLEV}/stage2-prof-use-lto
$ CPATH=${TOPLEV}/stage1/install/bin/
$ export LDFLAGS="-Wl,-q"
$ cmake -G Ninja ${TOPLEV}/llvm-project/llvm -DLLVM_TARGETS_TO_BUILD=X86 \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_COMPILER=$CPATH/clang -DCMAKE_CXX_COMPILER=$CPATH/clang++ \
-DLLVM_ENABLE_PROJECTS="clang;lld" \
-DLLVM_ENABLE_LTO=Full \
-DLLVM_PROFDATA_FILE=${TOPLEV}/stage2-prof-gen/profiles/clang.profdata \
-DLLVM_USE_LINKER=lld \
-DCMAKE_INSTALL_PREFIX=${TOPLEV}/stage2-prof-use-lto/install
$ ninja installNow we have a Clang compiler that can build itself much faster. As we will see,
it builds other applications faster as well, and, with BOLT, the compile time
can be improved even further.
Bolt Docs OptimizingLinux (`bolt/docs/OptimizingLinux.md`)
Optimizing Linux Kernel with BOLT
Introduction
Many Linux applications spend a significant amount of their execution time in the kernel. Thus, when we consider code optimization for system performance, it is essential to improve the CPU utilization not only in the user-space applications and libraries but also in the kernel. BOLT has demonstrated double-digit gains while being applied to user-space programs. This guide shows how to apply BOLT to the x86-64 Linux kernel and enhance your system's performance. In our experiments, BOLT boosted database TPS by 2 percent when applied to the kernel compiled with the highest level optimizations, including PGO and LTO. The database spent ~40% of the time in the kernel and was quite sensitive to kernel performance.
BOLT optimizes code layout based on a low-level execution profile collected with the Linux perf tool. The best quality profile should include branch history (brstack), such as Intel's last branch records (LBR) or AArch64's Branch Record Buffer Extension (BRBE). BOLT runs on a linked binary and reorders the code while combining frequently executed blocks of instructions in a manner best suited for the hardware. Other than branch instructions, most of the code is left unchanged. Additionally, BOLT updates all metadata associated with the modified code, including DWARF debug information and Linux ORC unwind information.
While BOLT optimizations are not specific to the Linux kernel, certain quirks distinguish the kernel from user-level applications.
BOLT has been successfully applied to and tested with several flavors of the x86-64 Linux kernel.
QuickStart Guide
BOLT operates on a statically-linked kernel executable, a.k.a. vmlinux binary. However, most Linux distributions use a vmlinuz compressed image for system booting. To use BOLT on the kernel, you must either repackage vmlinuz after BOLT optimizations or add steps for running BOLT into the kernel build and rebuild vmlinuz. Uncompressing vmlinuz and repackaging it with a new vmlinux binary falls beyond the scope of this guide, and at some point, we may add the capability to run BOLT directly on vmlinuz. Meanwhile, this guide focuses on steps for integrating BOLT into the kernel build process.
Building the Kernel
After downloading the kernel sources and configuration for your distribution, you should be able to build vmlinuz using the make bzImage command. Ideally, the kernel should binary match the kernel on the system you are about to optimize (the target system). The binary matching part is critical as BOLT performs profile matching and optimizations at the binary level. We recommend installing a freshly built kernel on the target system to avoid any discrepancies.
Note that the kernel build will produce several artifacts besides bzImage. The most important of them is the uncompressed vmlinux binary, which will be used in the next steps. Make sure to save this file.
Build and target systems should have a perf tool installed for collecting and processing profiles. If your build system differs from the target, make sure perf versions are compatible. The build system should also have the latest BOLT binary and tools (llvm-bolt, perf2bolt).
Once the target system boots with the freshly-built kernel, start your workload, such as a database benchmark. While the system is under load, collect the kernel profile using perf:
$ sudo perf record -a -e cycles -j any,k -F 5000 -- sleep 600Convert perf profile into a format suitable for BOLT passing the vmlinux binary to perf2bolt:
$ sudo chown $USER perf.data
$ perf2bolt -p perf.data -o perf.fdata vmlinuxUnder a high load, perf.data should be several gigabytes in size and you should expect the converted perf.fdata not to exceed 100 MB.
Profiles collected from multiple workloads could be joined into a single profile using merge-fdata utility:
$ merge-fdata perf.1.fdata perf.2.fdata ... perf.<N>.fdata > perf.merged.fdataTwo changes are required for the kernel build. The first one is optional but highly recommended. It introduces a BOLT-reserved space into vmlinux code section:
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -139,6 +139,11 @@ SECTIONS
STATIC_CALL_TEXT
*(.gnu.warning)
+ /* Allocate space for BOLT */
+ __bolt_reserved_start = .;
+ . += 2048 * 1024;
+ __bolt_reserved_end = .;
+
#ifdef CONFIG_RETPOLINE
__indirect_thunk_start = .;
*(.text.__x86.*)The second patch adds a step that runs BOLT on vmlinux binary:
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -340,5 +340,13 @@ if is_enabled CONFIG_KALLSYMS; then
fi
fi
+# Apply BOLT
+BOLT=llvm-bolt
+BOLT_PROFILE=perf.fdata
+BOLT_OPTS="--dyno-stats --eliminate-unreachable=0 --reorder-blocks=ext-tsp --simplify-conditional-tail-calls=0 --skip-funcs=__entry_text_start,irq_entries_start --split-functions"
+mv vmlinux vmlinux.pre-bolt
+echo BOLTing vmlinux
+${BOLT} vmlinux.pre-bolt -o vmlinux --data ${BOLT_PROFILE} ${BOLT_OPTS}
+
# For fixdep
echo "vmlinux: $0" > .vmlinux.dIf you skipped the first step or are running BOLT on a pre-built vmlinux binary, drop the --split-functions option.
Performance Expectations
By improving the code layout, BOLT can boost the kernel's performance by up to 5% by reducing instruction cache misses and branch mispredictions. When measuring total system performance, you should scale this number accordingly based on the time your application spends in the kernel (excluding I/O time).
Profile Quality
The timing and duration of the profiling may have a significant effect on the performance of the BOLTed kernel. If you don't know your workload well, it's recommended that you profile for the whole duration of the benchmark run. As longer times will result in larger perf.data files, you can lower the profiling frequency by providing a smaller value of -F flag. E.g., to record the kernel profile for half an hour, use the following command:
$ sudo perf record -a -e cycles -j any,k -F 1000 -- sleep 1800BOLT Disassembly
BOLT annotates the disassembly with control-flow information and attaches Linux-specific metadata to the code. To view annotated disassembly, run:
$ llvm-bolt vmlinux -o /dev/null --print-cfgIf you want to limit the disassembly to a set of functions, add --print-only=<func1regex>,<func2regex>,..., where a function name is specified using regular expressions.
Bolt Docs PointerAuthDesign (`bolt/docs/PointerAuthDesign.md`)
Optimizing binaries with pac-ret hardening
This is a design document about processing the DW_CFA_AARCH64_negate_ra_state
DWARF instruction in BOLT. As it describes internal design decisions, the
intended audience is BOLT developers. The document is an updated version of the
RFC posted on the LLVM Discourse.
DW_CFA_AARCH64_negate_ra_state is also referred to as .cfi_negate_ra_state
in assembly, or OpNegateRAState in BOLT sources. In this document, I will use
negate-ra-state as a shorthand.
Note: there are two resolutions for CFI:
- Call Frame Instruction: individual DWARF instruction, e.g. negate-ra-state
- Control Flow Integrity: a security mechanism, e.g. pointer authentication.
Introduction
Pointer Authentication
For more information, see the pac-ret section of the BOLT-binary-analysis document.
DW_CFA_AARCH64_negate_ra_state
The negate-ra-state CFI is a vendor-specific Call Frame Instruction defined in
the Arm ABI.
The DW_CFA_AARCH64_negate_ra_state operation negates bit[0] of the RA_SIGN_STATE pseudo-register.This bit indicates to the unwinder whether the current return address is signed
or not (hence the name). The unwinder uses this information to authenticate the
pointer, and remove the Pointer Authentication Code (PAC) bits.
Incorrect placement of negate-ra-state CFIs causes the unwinder to either attempt
to authenticate an unsigned pointer (resulting in a segmentation fault), or skip
authentication on a signed pointer, which can also cause a fault.
Note: some unwinders use the xpac instruction to strip the PAC bits without
authenticating the pointer. This is an incorrect (incomplete) implementation,
as it allows control-flow modification in the case of unwinding.
There are no DWARF instructions to directly set or clear the RA State. However,
two other CFIs can also affect the RA state:
DW_CFA_remember_state: this CFI stores register rules onto an implicit stack.DW_CFA_restore_state: this CFI pops rules from this stack.
Example:
| CFI | Effect on RA state |
|---|---|
| (default) | 0 |
| DW_CFA_AARCH64_negate_ra_state | 0 -> 1 |
| DW_CFA_remember_state | 1 pushed to the stack |
| DW_CFA_AARCH64_negate_ra_state | 1 -> 0 |
| DW_CFA_restore_state | 0 -> 1 (popped from the stack) |
The Arm ABI also defines the DW_CFA_AARCH64_negate_ra_state_with_pc CFI, but it
is not widely used, and is likely to become deprecated.
Where are these CFIs needed?
Whenever two consecutive instructions have different RA states, the unwinder must
be informed of the change. This typically occurs during pointer signing or
authentication. If adjacent instructions differ in RA state but neither signs
nor authenticates the return address, they must belong to different control flow
paths. One is part of an execution path with signed RA, the other is part of a
path with an unsigned RA.
In the example below, the first BasicBlock ends in a conditional branch, and
jumps to two different BasicBlocks, each with their own authentication, and
return. The instructions on the border of the second and third BasicBlock have
different RA states. The ret at the end of the second BasicBlock is in unsigned
state. The start of the third BasicBlock is after the paciasp in the control
flow, but before the authentication. In this case, a negate-ra-state is needed
at the end of the second BasicBlock.
+----------------+
| paciasp |
| |
| b.cc |
+--------+-------+
|
+----------------+
| |
| +--------v-------+
| | |
| | autiasp |
| | ret | // RA: unsigned
| +----------------+
+----------------+
|
+--------v-------+ // RA: signed
| |
| autiasp |
| ret |
+----------------+[!important]
The unwinder does not follow the control flow graph. It reads unwind
information in the layout order.
Because these locations are dependent on how the function layout looks,
negate-ra-state CFIs will become invalid during BasicBlock reordering.
Solution design
The implementation introduces two new passes:
PointerAuthCFIAnalyzer: assigns the RA state to each instruction based on
the CFIs in the input binaryPointerAuthCFIFixup: reads those assigned instruction RA states after
optimizations, and emitsDW_CFA_AARCH64_negate_ra_stateCFIs at the correct
places: wherever there is a state change between two consecutive instructions
in the layout order.
To track metadata on individual instructions, the MCAnnotation class was
extended. These also have helper functions in MCPlusBuilder.
Saving annotations at CFI reading
CFIs are read and added to BinaryFunctions in CFIReaderWriter::FillCFIInfoFor.
At this point, we add MCAnnotations about negate-ra-state, remember-state and
restore-state CFIs to the instructions they refer to. This is to not interfere
with the CFI processing that already happens in BOLT (e.g. remember-state and
restore-state CFIs are removed in normalizeCFIState for reasons unrelated to PAC).
As we add the MCAnnotations to instructions, we have to account for the case
where the function starts with a CFI altering the RA state. As CFIs modify the RA
state of the instructions before them, we cannot add the annotation to the first
instruction.
This special case is handled by adding an initialRAState bool to each BinaryFunction.
If the Offset the CFI refers to is zero, we don't store an annotation, but set
the initialRAState in FillCFIInfoFor. This information is then used inPointerAuthCFIAnalyzer.
Binaries without DWARF info
In some cases, the DWARF tables are stripped from the binary. These programs
usually have some other unwind-mechanism.
These passes only run on functions that include at least one negate-ra-state CFI.
This avoids processing functions that do not use Pointer Authentication, or on
functions that use Pointer Authentication, but do not have DWARF info.
In summary:
- pointer auth is not used: no change, the new passes do not run.
- pointer auth is used, but DWARF info is stripped: no change, the new passes
do not run. - pointer auth is used, and we have DWARF CFIs: passes run, and rewrite the
negate-ra-state CFI.
PointerAuthCFIAnalyzer pass
This pass runs before optimizations reorder anything.
It processes MCAnnotations generated during the CFI reading stage to check if
instructions have either of the three CFIs that can modify RA state:
- negate-ra-state,
- remember-state,
- restore-state.
Then it adds new MCAnnotations to each instruction, indicating their RA state.
Those annotations are:
- Signed,
- Unsigned.
Below is a simple example, that shows the two different type of annotations:
what we have before the pass, and after it.
| Instruction | Before | After |
|---|---|---|
| paciasp | negate-ra-state | unsigned |
| stp x29, x30, [sp, #-0x10]! | signed | |
| mov x29, sp | signed | |
| ldp x29, x30, [sp], #0x10 | signed | |
| autiasp | negate-ra-state | signed |
| ret | unsigned |
Error handling in PointerAuthCFIAnalyzer pass:
Whenever the PointerAuthCFIAnalyzer pass finds inconsistencies in the current
BinaryFunction, it marks the function as ignored using BF.setIgnored(). BOLT
will not optimize this function but will emit it unchanged in the original section
(.bolt.org.text).
The inconsistencies are as follows:
- finding a
pac*instruction when already in signed state - finding an
aut*instruction when already in unsigned state - finding
pac*andaut*instructions without.cfi_negate_ra_state.
Users will be informed about the number of ignored functions in the pass, the
exact functions ignored, and the found inconsistency.
PointerAuthCFIFixup
This pass runs after optimizations. It performs the inverse of PointerAuthCFIAnalyzer
pass:
- it reads the RA state annotations attached to the instructions, and
- whenever the state changes, it adds a PseudoInstruction that holds an
OpNegateRAState CFI.
Covering newly generated instructions:
Some BOLT passes can add new Instructions. In PointerAuthCFIFixup, we have
to know what RA state these have.
[!important]
As issue #160989 explains, unwind info is missing from stubs.
For this same reason, we cannot generate correct pac-specific unwind info: the
signedness of the incorrect return address is meaningless.
Assignment of RAStates to newly generated instructions is done in inferUnknownStates.
We have two different cases to cover:
If a BasicBlock has some instructions with known RA state, and some without, we
can copy the RAState of known instructions to the unknown ones. As the control
flow only changes between BasicBlocks, instructions in the same BasicBlock have
the same return address. (The exception is noreturn calls, but these would only
cause problems, if the newly inserted instruction is right after the call.)If a BasicBlock has no instructions with known RAState, we have to copy the
RAState of the previous BasicBlock in layout order.
Optimizations requiring special attention
Marking states before optimizations ensure that instructions can be moved around
freely. The only special case is function splitting. When a function is split,
the split part becomes a new function in the emitted binary. For unwinding to
work, it needs to "replay" all CFIs that lead up to the split point. BOLT does
this for other CFIs. As negate-ra-state is not read (only stored as an Annotation),
we have to do this manually in PointerAuthCFIFixup. Here, if the split part
starts with an instruction that has Signed RA state, we add a negate-ra-state CFI
to indicate this.
Option to disallow the feature
The feature can be guarded with the --update-branch-prediction flag, which is
on by default. If the flag is set to false, and a functioncontainedNegateRAState() after FillCFIInfoFor(), BOLT exits with an error.
Bolt Docs README (`bolt/docs/README.txt`)
BOLT Documentation
The BOLT documentation is written using the Sphinx documentation generator. It
is currently tested with Sphinx 1.1.3.
To build the documents into html configure BOLT with the following cmake options:
- -DLLVM_ENABLE_SPHINX=ON
- -DBOLT_INCLUDE_DOCS=ON
After configuring BOLT with these options the make rule docs-bolt-html should
be available.
Bolt Docs RuntimeLibrary (`bolt/docs/RuntimeLibrary.md`)
BOLT ORC-based linker
A high-level view on the simple linker used to insert auxiliary/library code into the final binary produced by BOLT. This is built on top of LLVM's ORC infra (the newest iteration on JITting for LLVM).
Several levels of code injection
When BOLT starts processing an input executable, its first task is to raise the binary to a low-level IR with CFG. After this is done, we are ready to change code in this binary. Throughout BOLT's pipeline of code transformations, there are plenty of situations when we need to insert new code or fix existing code.
If operating with small code changes inside a basic block, we typically defer this work to MCPlusBuilder. This is our target-independent interface to create new instructions, but it also contains some functions that may create code spanning multiple basic blocks (for instance, when doing indirect call promotion and unrolling an indirect call into a ladder of comparisons/direct calls). The implementation here usually boils down to programmatically creating new MCInst instructions while setting their opcodes according to the target list (see X86GenInstOpcodes.inc generated by tablegen in an LLVM build).
However, this approach quickly becomes awkward if we want to insert a lot of code, especially if this code is frozen and never changes. In these situations, it is more convenient to have a runtime library with all the code you need to insert. This library defines some symbols and can be linked into the final binary. In this case, all you need to do in a BOLT transformation is to insert a call to your library.
The runtime library
Currently, our runtime library is written in C++ and contains code that helps us instrument a binary.
Limitations
Our library is not written with regular C++ code as it is not linked against any other libraries (this means we cannot rely on anything defined on libstdc++, glibc, libgcc etc), but is self sufficient. In runtime/CMakeLists.txt, we can see it is built with -ffreestanding, which requires the compiler to avoid using a runtime library by itself.
While this requires us to make our own syscalls, it does simplify our linker a lot, which is very limited and can only do basic function name resolving. However, this is a big improvement in comparison with programmatically generating the code in assembly language using MCInsts.
A few more quirks:
- No BSS section: don't use uninitialized globals
- No dependencies on foreign code: self sufficient
- You should closely watch the generated bolt_rt object files, anything requiring fancy linker features will break. We only support bare bones .text, .data and nothing else.
Read instr.cpp opening comment for more details.
Linking
While RewriteInstance::emitAndLink() will perform an initial link step to resolve all references of the input program, it will not start linking the runtime library right away. The input program lives in its own module that may end up with unresolved references to the runtime library.
RewriteInstance::linkRuntime() has the job of actually reading individual .o files and adding them to the binary. We currently have a single .o file, so after it is read, ORC can finally resolve references from the first module to the newly inserted .o objects.
This sequence of steps is done by calls to addObject() and emitAndFinalize(). The latter will trigger symbol resolution, relying on the symbol resolver provided by us when calling createLegacyLookupResolver().
Bolt Docs Profiles (`bolt/docs/profiles.md`)
BOLT Profile Formats
BOLT accepts profile data in several formats. This document describes each
format, how to generate it, and how BOLT consumes it.
The general recommended workflow is to convert unsymbolized profiles (perf.data
or pre-aggregated) into symbolized (fdata or YAML):
$ perf2bolt executable \
# perf.data is consumed directly:
-p perf.data
# OR pre-aggregated requires `--pa` switch:
-p preagg --pa
# fdata is the default output format, YAML is optionally emitted using `-w` flag:
-o perf.fdata [-w perf.yaml]
# the output format for `-o` can be switched with `--profile-format`:
-o perf.yaml --profile-format=yaml
# perf.data can also be cached as pre-aggregated trace data:
-o perf.preagg --profile-format=preaggUnsymbolized profiles
Sample or trace profiles without symbol information accepted by
perf2bolt, to be converted into symbolized profile formats, used by llvm-bolt.
Linux perf data
Collection
Example with brstack:
perf record -j any,u -e cycles:u -o perf.data -- ./binaryConsumption modes
- Branch samples (default): Branch stack samples from capable hardware
(Intel LBR, AMD LBRv2/BRS, ARM BRBE).
Used by default withperf2boltandllvm-bolt -p perf.data. - Basic aggregation (
-ba): Sample-based profile without branch stacks.
Lower quality but works on hardware/VMs without branch sampling support. - Tracing (
--itrace): Synthesizing branch stacks from trace profile (Intel PT, ARM ETM).
Requires a value (e.g.i10usl), see
perf documentation
for details. - ARM SPE (
--spe): Statistical Profiling Extension on supported ARM
platforms providing short (1-deep) branch stacks.
Build-id verification
BOLT verifies that the build-id in perf.data matches the input binary.
Use --ignore-build-id to skip this check.
Pre-aggregated format
Pre-aggregated profile for direct consumption by perf2bolt --pa orllvm-bolt --pa. Enables external tools to generate BOLT-compatible profiles
without going through perf.data.
Entry types
E <event>
S <start> <count>
[TR] <branch> <ft_start> <ft_end> <count> [<mispred_count>]
B <start> <end> <count> [<mispred_count>]
[Ff] <start> <end> <count>
r <start> <end> <count>Where:
E— Name of the sampling event used for subsequent entries.S— Aggregated basic sample at<start>.T— Aggregated trace: branch from `` to<ft_start>with a
fall-through to<ft_end>.R— Aggregated trace originating at a return.B— Aggregated branch from<start>to<end>.F— Aggregated fall-through from<start>to<end>.f— Aggregated fall-through with external origin.r— Aggregated fall-through with external return as origin.
Trace mapping
Internally, all branch/fall-through formats are represented as traces, with the
following field mapping:
B <start> <end>->T <start> <end> BR_ONLYF <start> <end>->T FT_ONLY <start> <end>f <start> <end>->T FT_EXTERNAL_ORIGIN <start> <end>r <start> <end>->R FT_EXTERNAL_RETURN <start> <end>
The constants have the following values and can be specified directly:
BR_ONLY/FT_ONLY: UINT64_MAX: ffffffffffffffff or -1FT_EXTERNAL_ORIGIN: UINT64_MAX-1: fffffffffffffffe or -2FT_EXTERNAL_RETURN: UINT64_MAX-2: fffffffffffffffd or -3
Call continuation fall-throughs
Where applicable, call continuation fall-throughs are extended back to cover
the call site, improving profile continuity:
f: check that the fall-through start is a block start, not an entry point
or a landing pad.R/r: unconditional.T: local origin: extend only if it's a return. External origin: usef.
Return hint is only needed for external returns (e.g. from a PLT call), where
the address can't be disassembled to distinguish a return from a jump.
Best practices
Use T/R format. To capture branch type using Linux perf events, set
the PERF_SAMPLE_BRANCH_TYPE_SAVE flag in perf_event_attr::branch_sample_type.
To represent top of brstack entry with no fall-through, use BR_ONLY as ft_end.
Such traces can be extended with an average fall-through length by passing--impute-trace-fall-through.
Location format
Locations have the format [:]<addr>:
<addr>— Hex vaddr (non-PIE) or offset from the object base load address (PIE).:<offset>— Offset within the object identified by ``.X:<addr>— External address (outside the profiled binary).
Base load address is the address of the first PT_LOAD segment in the binary, which
may not be the same as the segment containing code address.
Examples
Basic samples profile:
E cycles
S 41be50 3
E br_inst_retired.near_taken
S 41be60 6Trace profile combining branches and fall-throughs:
T 4b196f 4b19e0 4b19ef 2Trace with unknown fall-through:
T 4b196f 4b19e0 -1 2Legacy branch profile with separate branches and fall-throughs:
F 41be50 41be50 3
F 41be90 41be90 4
B 4b1942 39b57f0 3 0
B 4b196f 4b19e0 2 0Generation
Pre-aggregated profiles can be generated by external tools. See
ebpf-bolt for a reference
implementation using eBPF-based collection.
perf2bolt can generate a pre-aggregated profile fromperf.data:
perf2bolt ./binary -p perf.data -o perf.preagg --profile-format=preaggSymbolized profiles
The profiles accepted by llvm-bolt. fdata is the legacy format, YAML is the rich (metadata-enabled) format.
As local symbol names are not guaranteed to be unique, BOLT disambiguates them with suffixes:
- short form
<name>/, - long form
<name>/<file>/, where<file>is the containing debug file symbol.
(Debug file symbols can be preserved when stripping debug information using --keep-file-symbols).
As such, symbolized profiles are not expected to be produced by external tools, except the symbols fdata
format mode which accepts names without suffixes but matches all aliases.
fdata format
Plaintext, space-separated branch profile format written by perf2bolt and
consumed by llvm-bolt -data <file>. Also produced by BOLT instrumentation.
LBR mode format
Each line records a branch:
<is_sym_from> <sym_from> <off_from> <is_sym_to> <sym_to> <off_to> <mispreds> <branches>Where:
,:1if the name is an ELF symbol,0if
it is a DSO name. Special values:2for local symbols (includes
filename),3/4/5for memory events.<sym_from>,<sym_to>: Symbol name or DSO name.<off_from>,<off_to>: Hex offset relative to the symbol/DSO.<mispreds>: Number of branch mispredictions.- ``: Total number of branches.
Example:
1 main 3fb 0 /lib/ld-2.21.so 12 4 221No-LBR mode format
Requires no_lbr header followed by an optional event name:
no_lbr <event_name>
<is_sym> <sym> <off> <count>Symbols mode format
Requires symbols header, followed by a list of (mangled) symbol names:
symbols
main
BZ2_compressBlockEach line matches all aliases of the symbol, each matching function is assigned
an execution count of 1.
Special headers
boltedcollection: Indicates profile collected on a BOLTed binary.
Requires BAT (BOLT Address Translation) tables for remapping.
Memory events format
Memory event types use `` values 3, 4, 5 to record load address
information alongside the instruction location.
YAML format
Structured profile format with block-level granularity. More resilient to
binary changes and supports stale profile matching.
Schema
Defined in ProfileYAMLMapping.h:
header:
profile-version: <uint32>
binary-name: <string>
binary-build-id: <string> # optional
profile-flags: [lbr|sample|memevent]
profile-origin: <string> # optional, how profile was obtained
profile-events: <string> # optional, event names
dfs-order: <bool> # optional, default true
hash-func: <std-hash|xxh3> # optional, default std-hash
functions:
- name: <string>
fid: <uint32>
hash: <hex64>
exec: <uint64>
nblocks: <uint32>
blocks:
- bid: <uint32>
insns: <uint32>
hash: <hex64> # optional
exec: <uint64> # optional
succ: [{bid, cnt, mis}] # optional
calls: [{off, fid, cnt}] # optional
inline_tree: [...] # optional, pseudo probe infoHash functions
std-hash: Standard hash function (default for backward compatibility).xxh3: XXH3 hash function (recommended, better distribution).
Stale profile matching
BOLT supports matching profiles to modified binaries using block hashes and
call graph matching. When the binary changes between profile collection and
optimization, BOLT uses the hash values to find corresponding blocks in the
new binary.
Clang Tools Extra Docs CMakeLists (`clang-tools-extra/docs/CMakeLists.txt`)
if (DOXYGEN_FOUND)
if (LLVM_ENABLE_DOXYGEN)
set(abs_srcdir ${CMAKE_CURRENT_SOURCE_DIR})
set(abs_builddir ${CMAKE_CURRENT_BINARY_DIR})
if (HAVE_DOT)
set(DOT ${LLVM_PATH_DOT})
endif()
if (LLVM_DOXYGEN_EXTERNAL_SEARCH)
set(enable_searchengine "YES")
set(searchengine_url "${LLVM_DOXYGEN_SEARCHENGINE_URL}")
set(enable_server_based_search "YES")
set(enable_external_search "YES")
set(extra_search_mappings "${LLVM_DOXYGEN_SEARCH_MAPPINGS}")
else()
set(enable_searchengine "NO")
set(searchengine_url "")
set(enable_server_based_search "NO")
set(enable_external_search "NO")
set(extra_search_mappings "")
endif()
# If asked, configure doxygen for the creation of a Qt Compressed Help file.
if (LLVM_ENABLE_DOXYGEN_QT_HELP)
set(CLANG_TOOLS_DOXYGEN_QCH_FILENAME "org.llvm.clang.qch" CACHE STRING
"Filename of the Qt Compressed help file")
set(CLANG_TOOLS_DOXYGEN_QHP_NAMESPACE "org.llvm.clang" CACHE STRING
"Namespace under which the intermediate Qt Help Project file lives")
set(CLANG_TOOLS_DOXYGEN_QHP_CUST_FILTER_NAME "Clang ${CLANG_VERSION}" CACHE STRING
"See http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-filters")
set(CLANG_TOOLS_DOXYGEN_QHP_CUST_FILTER_ATTRS "Clang,${CLANG_VERSION}" CACHE STRING
"See http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes")
set(clang_tools_doxygen_generate_qhp "YES")
set(clang_tools_doxygen_qch_filename "${CLANG_DOXYGEN_QCH_FILENAME}")
set(clang_tools_doxygen_qhp_namespace "${CLANG_DOXYGEN_QHP_NAMESPACE}")
set(clang_tools_doxygen_qhelpgenerator_path "${LLVM_DOXYGEN_QHELPGENERATOR_PATH}")
set(clang_tools_doxygen_qhp_cust_filter_name "${CLANG_DOXYGEN_QHP_CUST_FILTER_NAME}")
set(clang_tools_doxygen_qhp_cust_filter_attrs "${CLANG_DOXYGEN_QHP_CUST_FILTER_ATTRS}")
else()
set(clang_tools_doxygen_generate_qhp "NO")
set(clang_tools_doxygen_qch_filename "")
set(clang_tools_doxygen_qhp_namespace "")
set(clang_tools_doxygen_qhelpgenerator_path "")
set(clang_tools_doxygen_qhp_cust_filter_name "")
set(clang_tools_doxygen_qhp_cust_filter_attrs "")
endif()
llvm_configure_doxygen()
set(abs_top_srcdir)
set(abs_top_builddir)
set(DOT)
set(enable_searchengine)
set(searchengine_url)
set(enable_server_based_search)
set(enable_external_search)
set(extra_search_mappings)
set(clang_tools_doxygen_generate_qhp)
set(clang_tools_doxygen_qch_filename)
set(clang_tools_doxygen_qhp_namespace)
set(clang_tools_doxygen_qhelpgenerator_path)
set(clang_tools_doxygen_qhp_cust_filter_name)
set(clang_tools_doxygen_qhp_cust_filter_attrs)
add_custom_target(doxygen-clang-tools
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating clang doxygen documentation." VERBATIM)
set_target_properties(doxygen-clang-tools PROPERTIES FOLDER "Clang Tools Extra/Docs")
if (LLVM_BUILD_DOCS)
add_dependencies(doxygen doxygen-clang-tools)
endif()
if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doxygen/html
DESTINATION docs/html)
endif() endif()
endif()
if (LLVM_ENABLE_SPHINX)
include(AddSphinxTarget)
if (SPHINX_FOUND)
if (${SPHINX_OUTPUT_HTML})
add_sphinx_target(html clang-tools)
endif()
if (${SPHINX_OUTPUT_MAN})
add_sphinx_target(man clang-tools)
endif()
endif()
endif()
Clang Tools Extra Docs ModularizeUsage (`clang-tools-extra/docs/ModularizeUsage.md`)
Modularize Usage
modularize [<modularize-options>] [<module-map>|]* [<front-end-options>...]
<modularize-options> is a place-holder for options
specific to modularize, which are described below inModularize Command Line Options.
<module-map> specifies the path of a file name for an
existing module map. The module map must be well-formed in
terms of syntax. Modularize will extract the header file names
from the map. Only normal headers are checked, assuming headers
marked "private", "textual", or "exclude" are not to be checked
as a top-level include, assuming they either are included by
other headers which are checked, or they are not suitable for
modules.
`` specifies the path of a file name for a
file containing the newline-separated list of headers to check
with respect to each other. Lines beginning with '#' and empty
lines are ignored. Header file names followed by a colon and
other space-separated file names will include those extra files
as dependencies. The file names can be relative or full paths,
but must be on the same line. For example:
header1.h
header2.h
header3.h: header1.h header2.hNote that unless a -prefix (header path) option is specified,
non-absolute file paths in the header list file will be relative
to the header list file directory. Use -prefix to specify a different
directory.
<front-end-options> is a place-holder for regular Clang
front-end arguments, which must follow the ``.
Note that by default, modularize assumes .h files
contain C++ source, so if you are using a different language,
you might need to use a -x option to tell Clang that the
header contains another language, i.e.: -x c
Note also that because modularize does not use the clang driver,
you will likely need to pass in additional compiler front-end
arguments to match those passed in by default by the driver.
Modularize Command Line Options
:::{option} -prefix=
Prepend the given path to non-absolute file paths in the header list file.
By default, headers are assumed to be relative to the header list file
directory. Use -prefix to specify a different directory.
:::
:::{option} -module-map-path=
Generate a module map and output it to the given file. See the description
in {ref}module-map-generation.
:::
:::{option} -problem-files-list=
For use only with module map assistant. Input list of files that
have problems with respect to modules. These will still be
included in the generated module map, but will be marked as
"excluded" headers.
:::
:::{option} -root-module=
Put modules generated by the -module-map-path option in an enclosing
module with the given name. See the description in {ref}module-map-generation.
:::
:::{option} -block-check-header-list-only
Limit the #include-inside-extern-or-namespace-block
check to only those headers explicitly listed in the header list.
This is a work-around for avoiding error messages for private includes that
purposefully get included inside blocks.
:::
:::{option} -no-coverage-check
Don't do the coverage check for a module map.
:::
:::{option} -coverage-check-only
Only do the coverage check for a module map.
:::
:::{option} -display-file-lists
Display lists of good files (no compile errors), problem files,
and a combined list with problem files preceded by a '#'.
This can be used to quickly determine which files have problems.
The latter combined list might be useful in starting to modularize
a set of headers. You can start with a full list of headers,
use -display-file-lists option, and then use the combined list as
your intermediate list, uncommenting-out headers as you fix them.
:::
Clang Tools Extra Docs README (`clang-tools-extra/docs/README.txt`)
Documentation in clang-tools-extra
To generate documentation in HTML format from files in clang-tools-extra/docs,
build the docs-clang-tools-html target.
To generate documentation from the source code using Doxygen, build the
doxygen-clang-tools target.
Clang Tools Extra Docs ReleaseNotes (`clang-tools-extra/docs/ReleaseNotes.md`)
myst:
enable_extensions:
- attrs_block
- colon_fence
- substitution
% If you want to modify sections/contents permanently, you should modify both
% ReleaseNotes.md and ReleaseNotesTemplate.txt.
{#extra-clang-tools-release-releasenotestitle}
Extra Clang Tools {{env.config.release}} {{ (('(In-Progress) ' if env.app.tags.has('PreRelease') else '') ~ 'Release Notes') }}
Written by the LLVM Team
::::{only} PreRelease
:::{warning}
These are in-progress notes for the upcoming Extra Clang Tools {{env.config.version}} release.
Release notes for previous releases can be found on
the Download Page.
:::
::::
Introduction
This document contains the release notes for the Extra Clang Tools, part of the
Clang release {{env.config.release}}. Here we describe the status of the Extra Clang Tools in
some detail, including major improvements from the previous release and new
feature work. All LLVM releases may be downloaded from the LLVM releases web
site.
For more information about Clang or LLVM, including information about
the latest release, please see the Clang Web Site or
the LLVM Web Site.
Note that if you are reading this file from a Git checkout or the
main Clang web page, this document applies to the next release, not
the current one. To see the release notes for a specific release, please
see the releases page.
{#what-s-new-in-extra-clang-tools-release}
What's New in Extra Clang Tools {{env.config.release}}?
Some of the major new features and improvements to Extra Clang Tools are listed
here. Generic improvements to Extra Clang Tools as a whole or to its underlying
infrastructure are described first, followed by tool-specific sections.
Major New Features
Potentially Breaking Changes
The deprecated
zirconclang-tidy module has been removed. Users ofzircon-temporary-objectsshould migrate to {doc}fuchsia-temporary-objects <clang-tidy/checks/fuchsia/temporary-objects>.In 22nd release, The
clang-tidy/ClangTidyModuleRegistry.hheader was deprecated.
All of the symbols it used to define were moved intoclang-tidy/ClangTidyModule.h.
The deprecated header has been removed in this release.
Improvements to clangd
Inlay hints
Diagnostics
- Query-based custom clang-tidy checks can now be enabled with the
Diagnostics.ClangTidy.ExperimentalCustomChecksclangd configuration option.
Custom checks are subject toFastCheckFilter, and therefore requireFastCheckFilter: LooseorNoneto run.
Semantic Highlighting
Compile flags
Hover
- The type a
decltyperesolves to is now also shown for composite types,
e.g.decltype(x)&is displayed asint&. Qualifiers applied to adecltypeare no longer dropped, soconst decltype(x)is displayed asconst intrather thanint.
Code completion
- Parameters declared with a
decltypeare now displayed as the type thedecltyperesolves to, e.g.set_x(int val)rather thanset_x(decltype(x) val).
Code actions
clangd now applies clang-tidy fix-it post-processing before exposing fixes.
The
Extract to functiontweak is now offered for selections consisting of
a single expression-statement (e.g. a lone function call or an overloaded
operator call such asstream << 42;), which it previously refused to
extract.The
Extract to functiontweak is now also available in C files, where
it previously always refused to apply. Captured variables are passed by
value when possible, and otherwise via a pointer parameter, since C has
no references.
Signature help
- Parameters declared with a
decltypeare now displayed as the type thedecltyperesolves to, as for code completion.
Cross-references
Objective-C
Miscellaneous
Fixed
clangd-indexer --format=yamlemitting invalid YAML when indexing
C++20 concepts.
(#206875)clangd-indexernow reads clangd configuration files (.clangdand the
user config) by default. Pass--enable-config=falseto restore the previous
behavior.Added support for loading dynamic plugins via the
-loadcommand-line
option.
Improvements to clang-doc
Improvements to clang-query
Improvements to clang-tidy
Improved {program}
check_clang_tidy.pyby adding support of-std=cXX-or-earliervalues, mirroring the existing-std=cXX-or-later.
New construct expands to the given standard and every earlier one.On Windows, {program}
clang-tidywill now better match mixes of forwards and
backwards slashes when using the--line-filteroption. This also improves
piping from {program}gitto {program}clang-tidy-diff.py, where slashes
will now be automatically normalized.
New checks
New {doc}
llvm-invalid-regex-pattern <clang-tidy/checks/llvm/invalid-regex-pattern>check.Detects malformed regex patterns defined in a single string literal.
New {doc}
modernize-use-to-underlying <clang-tidy/checks/modernize/use-to-underlying>check.Finds casts from a scoped enumeration (
enum class) to an integer type and
replaces them with a call tostd::to_underlying(introduced in C++23).New {doc}
performance-expensive-value-or <clang-tidy/checks/performance/expensive-value-or>check.Finds calls to
value_or(and alternative spellingsvalueOr,ValueOr) on optional types where the return type is expensive to copy.New {doc}
portability-avoid-pragma-comment <clang-tidy/checks/portability/avoid-pragma-comment>check.Finds uses of
#pragma commentand, forliborlinkercomments, suggests
using the build system for improved portability.New {doc}
readability-redundant-zero-initializer <clang-tidy/checks/readability/redundant-zero-initializer>check.Finds explicit zero initializers of arrays that can be replaced with empty
braces.
New check aliases
Changes in existing checks
Improved {doc}
bugprone-easily-swappable-parameters <clang-tidy/checks/bugprone/easily-swappable-parameters>check by no longer
emitting empty notes for type aliases when {option}ModelImplicitConversions
is enabled.Improved {doc}
bugprone-implicit-widening-of-multiplication-result <clang-tidy/checks/bugprone/implicit-widening-of-multiplication-result>check
by suggesting a wider type of the same signedness as the original operands,
instead of forcing a signed type, when a multiplication of two unsigned
operands narrower thanintis only signed due to integer promotion.Improved {doc}
bugprone-macro-parentheses <clang-tidy/checks/bugprone/macro-parentheses>by fixing invalid fixes for
macro arguments used as names in alias declarations.Fixed a crash in {doc}
bugprone-misplaced-operator-in-strlen-in-alloc <clang-tidy/checks/bugprone/misplaced-operator-in-strlen-in-alloc>when
checking an array new expression without a size expression.Fixed a crash in {doc}
bugprone-misplaced-pointer-arithmetic-in-alloc <clang-tidy/checks/bugprone/misplaced-pointer-arithmetic-in-alloc>when
pointer arithmetic is applied to a non-arraynewexpression whose
constructor has no arguments.Fixed a crash in {doc}
bugprone-pointer-arithmetic-on-polymorphic-object <clang-tidy/checks/bugprone/pointer-arithmetic-on-polymorphic-object>when
the pointer points to an incomplete (forward-declared) type.Improved {doc}
bugprone-redundant-branch-condition <clang-tidy/checks/bugprone/redundant-branch-condition>check by fixing
false positives when the condition variable is changed later in a loop that
encloses the innerif.Fixed a crash in {doc}
bugprone-std-namespace-modification <clang-tidy/checks/bugprone/std-namespace-modification>when checking
lambda closure types used as template arguments.Improved {doc}
bugprone-unchecked-optional-access <clang-tidy/checks/bugprone/unchecked-optional-access>by fixing false
positives onbsl::optionalandbdlb::NullableValueconstructed from a
value or returned bybsl::make_optional.Improved {doc}
cppcoreguidelines-missing-std-forward <clang-tidy/checks/cppcoreguidelines/missing-std-forward>check by diagnosing
unforwardedauto&¶meters in C++20 abbreviated function templates.Improved {doc}
cppcoreguidelines-pro-type-member-init <clang-tidy/checks/cppcoreguidelines/pro-type-member-init>check by treatingstd::arraythe same as built-in arrays whenIgnoreArraysoption is enabled.Improved {doc}
cppcoreguidelines-use-enum-class <clang-tidy/checks/cppcoreguidelines/use-enum-class>check by omitting unnamed enums from theenum classrequirement, as previously the check suggested users an ill-formed fix.Improved {doc}
cppcoreguidelines-virtual-class-destructor <clang-tidy/checks/cppcoreguidelines/virtual-class-destructor>check by
emitting the diagnostic and its fix-it notes at the destructor's location
instead of the class name, whenever the destructor is user-declared. The
diagnostics are still emitted at the class name for implicitly declared
destructors.Improved {doc}
misc-const-correctness <clang-tidy/checks/misc/const-correctness>check:Fixed false positives when the pointee is written through a pointer that
is incremented, decremented or adjusted with+=or-=, such as*p++ = 0.Fixed false positives when the pointee is written through a pointer
assignment, such as*(p = q) = 0.No longer diagnoses variables declared with
decltype(auto), where the
suggestedconstdoes not compile.No longer diagnoses parameters of
main, whose signature is fixed by the
standard.
Fixed an infinite loop in {doc}
misc-multiple-inheritance <clang-tidy/checks/misc/multiple-inheritance>when checking a class that
inherits from itself or has a circular inheritance graph.Improved {doc}
misc-redundant-expression <clang-tidy/checks/misc/redundant-expression>by fixing false positives in
nested expressions involving different macros or a mix of macro and
non-macro operands.Fixed a crash in {doc}
modernize-raw-string-literal <clang-tidy/checks/modernize/raw-string-literal>on synthetic string
literals created for raw user-defined literal operators, such as12_w.Improved {doc}
modernize-return-braced-init-list <clang-tidy/checks/modernize/return-braced-init-list>check to no longer
rewrite the return value when the constructed type has astd::initializer_listconstructor, as the braced form could select a
different constructor.Fixed a crash in {doc}
modernize-use-designated-initializers <clang-tidy/checks/modernize/use-designated-initializers>when analyzing
malformed code with nested classes and ambiguous initializer.Improved {doc}
modernize-use-equals-default <clang-tidy/checks/modernize/use-equals-default>check to also capture
default constructor with explicit default-initialization of parents.Fixed a crash in {doc}
modernize-use-noexcept <clang-tidy/checks/modernize/use-noexcept>when analyzing malformed template
code with an unparsed exception specification.Extend {doc}
modernize-use-nullptr <clang-tidy/checks/modernize/use-nullptr>to turndecltype(nullptr)intostd::nullptr_tfrom<cstdef>.Improved {doc}
modernize-use-nullptr <clang-tidy/checks/modernize/use-nullptr>check to avoid replacing0
withnullptrin comparisons with ordering types such asstd::strong_ordering.Improved {doc}
modernize-use-ranges <clang-tidy/checks/modernize/use-ranges>check by preserving used output
iterator results when replacing output algorithms such asstd::copy.Preserved used callable results when replacing
std::for_eachand
structured binding results when replacing algorithms such asstd::equal_range.Kept diagnostics but suppressed unsafe fix-its when no safe
result-preserving rewrite is available.
Improved {doc}
performance-inefficient-algorithm <clang-tidy/checks/performance/inefficient-algorithm>check to no longer
produce a fix with the container or the searched-for value missing, such as.find(43)ors.find(), when either comes from a macro. The value is
copied as written rather than with its parentheses stripped, and no fix is
offered when an argument covers only part of a macro expansion, as it then
has no source text of its own.Improved {doc}
performance-inefficient-vector-operation <clang-tidy/checks/performance/inefficient-vector-operation>by adding the
{option}ForRangeLoopClassesto configure container classes that can be used
as sources in range-basedforloops.Improved {doc}
performance-no-automatic-move <clang-tidy/checks/performance/no-automatic-move>check by no longer
warning when returning a variable captured by a lambda or a block, which is
never implicitly moved from.Improved {doc}
performance-prefer-single-char-overloads <clang-tidy/checks/performance/prefer-single-char-overloads>check to
avoid offering fix-its for string literals originating from macro
expansions.Improved {doc}
readability-convert-member-functions-to-static <clang-tidy/checks/readability/convert-member-functions-to-static>check by
fixing a crash when checking a const-qualified method declared with thelifetimeboundattribute.Improved {doc}
readability-enum-initial-value <clang-tidy/checks/readability/enum-initial-value>check by adding
the {option}AllowReferencedInitialValuesto support theINT09-C-EX1exception, allowing enumerators initialized by referencing
another enumerator in the same enum (e.g.,last = first).Improved {doc}
readability-function-cognitive-complexity <clang-tidy/checks/readability/function-cognitive-complexity>check by fixing
a crash when checking a function declared with thealiasattribute.Improved {doc}
readability-identifier-naming <clang-tidy/checks/readability/identifier-naming>check:Fixed a crash when a class inherits from a forward-declared base class.
Fixed a crash when checking forward-declared classes with
{option}DefaultHungarianPrefixenabled.Fixed {option}
DefaultHungarianPrefixbeing incorrectly diagnosed as an
invalid option.Fixed invalid fixes when renaming parameter packs used in
sizeof...
expressions.Added the {option}
TypedefInheritAnonTagConfig, which checks a
typedef or type alias that provides the only name of an otherwise unnamed
tag, such astypedef enum {} MyEnum;, against the style configured for
that tag kind instead of the typedef or type alias style.Added support for naming lambda init-captures (e.g.
[Captured = Var]) via
the newLambdaCaptureoptions. Simple, non-init captures continue to follow
the naming style of the variable they capture.Added the {option}
AllowTrailingUnderscore, which permits a
single trailing underscore on any identifier.
Improved {doc}
readability-named-parameter <clang-tidy/checks/readability/named-parameter>check by ignoring
standard tag types (e.g.std::in_place_t,std::allocator_arg_t,std::nothrow_t, iterator tags, lock tags, etc.) that are used
exclusively for overload resolution. Added the {option}IgnoredTypes
option to allow customizing the set of ignored types.Improved {doc}
readability-non-const-parameter <clang-tidy/checks/readability/non-const-parameter>check by fixing false
positives on pointers passed to atomic builtins, whose operands may be
written to, such as theexpectedparameter ofatomic_compare_exchange_strong().Improved {doc}
readability-redundant-parentheses <clang-tidy/checks/readability/redundant-parentheses>check:Fixed a false positive on the required parentheses of
typeofandtypeof_unqualoperands.Fixed false positives and incorrect fixes caused by synthetic parentheses
in reference non-type template parameter uses,__builtin_dump_structcalls,
and OpenMPlinearclauses.
Fixed {doc}
readability-simplify-boolean-expr <clang-tidy/checks/readability/simplify-boolean-expr>producing invalid
fixes when applying De Morgan's theorem to overloaded comparison operators.Improved {doc}
readability-suspicious-call-argument <clang-tidy/checks/readability/suspicious-call-argument>check by fixing the
defaultdistanddstabbreviations ofdistancenot being recognized.Improved {doc}
readability-trailing-comma <clang-tidy/checks/readability/trailing-comma>check:Fixed false positives on designated initializers, where initializer lists
synthesized for intermediate subobjects caused the trailing comma of the
enclosing list to be incorrectly rewritten.Ignored preprocessor directives such as
#endifthat appear immediately
before an enum's closing brace, which previously produced a false positive
and a fix-it that inserted a comma after the directive.Fixed a false positive on empty brace initializers of types with default
member initializers.
Improved {doc}
readability-use-std-min-max <clang-tidy/checks/readability/use-std-min-max>check by fixing spurious
trailing semicolons and lost comments when theifbody has no braces.
Removed checks
- Removed the deprecated
zircon-temporary-objectscheck. Users should migrate to
{doc}fuchsia-temporary-objects <clang-tidy/checks/fuchsia/temporary-objects>.
Miscellaneous
- Fixed {program}
clang-tidyadding an unwanted blank line when automatically
inserting a missing#include, which stopped {program}clang-formatand
thellvm-include-ordercheck from being able to sort it correctly afterwards.
Improvements to include-fixer
Improvements to clang-include-fixer
Improvements to modularize
Improvements to pp-trace
Clang-tidy Visual Studio plugin
Clang Tools Extra Docs ReleaseNotesTemplate (`clang-tools-extra/docs/ReleaseNotesTemplate.txt`)
myst:
enable_extensions:
- attrs_block
- colon_fence
- substitution
% If you want to modify sections/contents permanently, you should modify both
% ReleaseNotes.md and ReleaseNotesTemplate.txt.
{#extra-clang-tools-release-releasenotestitle}
Extra Clang Tools {{env.config.release}} {{ (('(In-Progress) ' if env.app.tags.has('PreRelease') else '') ~ 'Release Notes') }}
:depth: 3
:local: trueWritten by the LLVM Team
::::{only} PreRelease
:::{warning}
These are in-progress notes for the upcoming Extra Clang Tools {{env.config.version}} release.
Release notes for previous releases can be found on
the Download Page.
:::
::::
Introduction
This document contains the release notes for the Extra Clang Tools, part of the
Clang release {{env.config.release}}. Here we describe the status of the Extra Clang Tools in
some detail, including major improvements from the previous release and new
feature work. All LLVM releases may be downloaded from the LLVM releases web
site.
For more information about Clang or LLVM, including information about
the latest release, please see the Clang Web Site or
the LLVM Web Site.
Note that if you are reading this file from a Git checkout or the
main Clang web page, this document applies to the next release, not
the current one. To see the release notes for a specific release, please
see the releases page.
{#what-s-new-in-extra-clang-tools-release}
What's New in Extra Clang Tools {{env.config.release}}?
Some of the major new features and improvements to Extra Clang Tools are listed
here. Generic improvements to Extra Clang Tools as a whole or to its underlying
infrastructure are described first, followed by tool-specific sections.
Major New Features
Potentially Breaking Changes
Improvements to clangd
Inlay hints
Diagnostics
Semantic Highlighting
Compile flags
Hover
Code completion
Code actions
Signature help
Cross-references
Objective-C
Miscellaneous
Improvements to clang-doc
Improvements to clang-query
Improvements to clang-tidy
New checks
New check aliases
Changes in existing checks
Removed checks
Miscellaneous
Improvements to include-fixer
Improvements to clang-include-fixer
Improvements to modularize
Improvements to pp-trace
Clang-tidy Visual Studio plugin
Clang Tools Extra Docs Clang Change Namespace (`clang-tools-extra/docs/clang-change-namespace.md`)
Clang-Change-Namespace
{program}clang-change-namespace can be used to change the surrounding
namespaces of class/function definitions.
Classes/functions in the moved namespace will have new namespaces while
references to symbols (e.g. types, functions) which are not defined in the
changed namespace will be correctly qualified by prepending namespace specifiers
before them. This will try to add shortest namespace specifiers possible.
When a symbol reference needs to be fully-qualified, this adds a :: prefix to
the namespace specifiers unless the new namespace is the global namespace. For
classes, only classes that are declared/defined in the given namespace in
specified files will be moved: forward declarations will remain in the old
namespace. The will be demonstrated in the next example.
Example usage
For example, consider this test.cc example here with the forward declared
class FWD and the defined class A, both in the namespace a.
namespace a {
class FWD;
class A {
FWD *fwd;
};
} // namespace aAnd now let's change the namespace a to x.
clang-change-namespace \
--old_namespace "a" \
--new_namespace "x" \
--file_pattern "test.cc" \
--i \
test.ccNote that in the code below there's still the forward decalred class FWD that
stayed in the namespace a. It wasn't moved to the new namespace because it
wasn't defined/declared here in a but only forward declared.
namespace a {
class FWD;
} // namespace a
namespace x {
class A {
a::FWD *fwd;
};
} // namespace xAnother example
Consider this test.cc file:
namespace na {
class X {};
namespace nb {
class Y {
X x;
};
} // namespace nb
} // namespace naTo move the definition of class Y from namespace na::nb to x::y, run:
clang-change-namespace \
--old_namespace "na::nb" \
--new_namespace "x::y" \
--file_pattern "test.cc" \
--i \
test.ccThis will overwrite test.cc to look like this:
namespace na {
class X {};
} // namespace na
namespace x {
namespace y {
class Y {
na::X x;
};
} // namespace y
} // namespace xNote, that we've successfully moved the class Y from namespace na::nb to
namespace x::y.
Caveats
Content already exists in new namespace
Consider this test.cc example that defines two class A one inside the
namespace a and one in namespace b:
namespace a {
class A {
int classAFromWithinNamespace_a;
};
} // namespace a
namespace b {
class A {
int classAFromWithinNamespace_b;
};
} //namespace bLet's move everything from namespace a to namespace b:
clang-change-namespace \
--old_namespace "a" \
--new_namespace "b" \
--file_pattern test.cc \
test.ccAs expected we now have to definitions of class A inside the namespace b:
namespace b {
class A {
int classAFromWithinNamespace_a;
};
} // namespace b
namespace b {
class A {
int classAFromWithinNamespace_b;
};
} //namespace bThe re-factoring looks correct but the code will not compile due to the name
duplication. It is not up to the tool to ensure compilability in that sense.
But one has to be aware of that.
Inline namespace doesn't work
Consider this usage of two versions of implementations for a greet function:
#include <cstdio>
namespace Greeter {
inline namespace Version1 {
const char* greet() { return "Hello from version 1!"; }
} // namespace Version1
namespace Version2 {
const char* greet() { return "Hello from version 2!"; }
} // namespace Version2
} // namespace Greeter
int main(int argc, char* argv[]) {
printf("%s\n", Greeter::greet());
return 0;
}Note, that currently Greeter::greet() will result in a call toGreeter::Version1::greet() because that's the inlined namespace.
Let's say you want to move one and make Version2 the default now and remove
the inline from the Version1. First let's try to turn namespace Version2
into inline namespace Version2:
clang-change-namespace \
--old_namespace "Greeter::Version2" \
--new_namespace "inline Version2" \
--file_pattern main.cc main.ccBut this will put the inline keyword in the wrong place resulting in:
#include <cstdio>
namespace Greeter {
inline namespace Version1 {
const char* greet() { return "Hello from version 1!"; }
} // namespace Version1
} // namespace Greeter
namespace inline Greeter {
namespace Version2 {
const char *greet() { return "Hello from version 2!"; }
} // namespace Version2
} // namespace inline Greeter
int main(int argc, char* argv[]) {
printf("%s\n", Greeter::greet());
return 0;
}One cannot use {program}clang-change-namespace to inline a namespace.
Symbol references not updated
Consider this test.cc file:
namespace old {
struct foo {};
} // namespace old
namespace b {
old::foo g_foo;
} // namespace bNotice that namespace b defines a global variable of type old::foo. If we
now change the name of the old namespace to modern, the reference will not
be updated:
clang-change-namespace \
--old_namespace "old" \
--new_namespace "modern" \
--file_pattern test.cc \
test.ccnamespace modern {
struct foo {};
} // namespace modern
namespace b {
old::foo g_foo;
} // namespace bg_foo is still of the no longer existing type old::foo while instead it
should use modern::foo.
Only symbol references in the moved namespace are updated, not outside of it.
{program}`clang-change-namespace` Command Line Options
:::{option} --allowed_file=
A file containing regexes of symbol names that are not expected to be updated
when changing namespaces around them.
:::
:::{option} --dump_result
Dump new file contents in YAML, if specified.
:::
:::{option} --extra-arg=
Additional argument to append to the compiler command line
:::
:::{option} --extra-arg-before=
Additional argument to prepend to the compiler command line
:::
:::{option} --file_pattern=
Only rename namespaces in files that match the given regular expression
pattern.
:::
:::{option} -i
Inplace edit <file>s, if specified.
:::
:::{option} --new_namespace=
New namespace. Use "" when you target the global namespace.
:::
:::{option} --old_namespace=
Old namespace.
:::
:::{option} -p
Build path
:::
:::{option} --style=
The style name used for reformatting.
:::
Clang Tools Extra Docs Clang Doc (`clang-tools-extra/docs/clang-doc.md`)
Clang-Doc
{program}clang-doc is a tool for generating C and C++ documentation from
source code and comments.
The tool is in a very early development stage, so you might encounter bugs and
crashes. Submitting reports with information about how to reproduce the issue
to the LLVM bug tracker will definitely help the
project. If you have any ideas or suggestions, please to put a feature request
there.
Use
{program}clang-doc is a LibTooling-based tool, and so requires a
compile command database for your project (for an example of how to do this
see How To Setup Tooling For LLVM).
The tool will process a list of files by default:
$ clang-doc File1.cpp File2.cpp ... FileN.cppThe tool can be also used with a compile commands database:
$ clang-doc --executor=all-TUs compile_commands.jsonTo select only a subset of files from the database, use the --filter flag:
$ clang-doc --executor=all-TUs --filter=File[0-9]+.cpp compile_commands.jsonOutput
{program}clang-doc produces a directory of documentation. One file is produced
for each namespace and record in the project source code, containing all
documentation (including contained functions, methods, and enums) for that item.
The top-level directory is configurable through the output flag:
$ clang-doc --output=output/directory/ compile_commands.jsonConfiguration
Configuration for {program}clang-doc is currently limited to command-line options.
In the future, it may develop the ability to use a configuration file, but no such
efforts are currently in progress.
Options
{program}clang-doc offers the following options:
/* Detailed source-code truncated for AI context efficiency. */The following flags should only be used if format is set to html:
repositorysource-rootstylesheets
Clang Tools Extra Docs Clang Include Fixer (`clang-tools-extra/docs/clang-include-fixer.md`)
Clang-Include-Fixer
One of the major nuisances of C++ compared to other languages is the manual
management of #include directives in any file.
{program}clang-include-fixer addresses one aspect of this problem by providing
an automated way of adding #include directives for missing symbols in one
translation unit.
While inserting missing #include, {program}clang-include-fixer adds
missing namespace qualifiers to all instances of an unidentified symbol if
the symbol is missing some prefix namespace qualifiers.
Setup
To use {program}clang-include-fixer two databases are required. Both can be
generated with existing tools.
- Compilation database. Contains the compiler commands for any given file in a
project and can be generated by CMake, see How To Setup Tooling For LLVM. - Symbol index. Contains all symbol information in a project to match a given
identifier to a header file.
Ideally both databases (compile_commands.json andfind_all_symbols_db.yaml) are linked into the root of the source tree they
correspond to. Then the {program}clang-include-fixer can automatically pick
them up if called with a source file from that tree. Note that by defaultcompile_commands.json as generated by CMake does not include header files,
so only implementation files can be handled by tools.
Creating a Symbol Index From a Compilation Database
The include fixer contains {program}find-all-symbols, a tool to create a
symbol database in YAML format from a compilation database by parsing all
source files listed in it. The following list of commands shows how to set up a
database for LLVM, any project built by CMake should follow similar steps.
$ cd path/to/llvm-build
$ ninja find-all-symbols // build find-all-symbols tool.
$ ninja clang-include-fixer // build clang-include-fixer tool.
$ ls compile_commands.json # Make sure compile_commands.json exists.
compile_commands.json
$ path/to/llvm/source/clang-tools-extra/clang-include-fixer/find-all-symbols/tool/run-find-all-symbols.py
... wait as clang indexes the code base ...
$ ln -s $PWD/find_all_symbols_db.yaml path/to/llvm/source/ # Link database into the source tree.
$ ln -s $PWD/compile_commands.json path/to/llvm/source/ # Also link compilation database if it's not there already.
$ cd path/to/llvm/source
$ /path/to/clang-include-fixer -db=yaml path/to/file/with/missing/include.cpp
Added #include "foo.h"Integrate with Vim
To run clang-include-fixer on a potentially unsaved buffer in Vim. Add the
following key binding to your .vimrc:
noremap <leader>cf :pyf path/to/llvm/source/clang-tools-extra/clang-include-fixer/tool/clang-include-fixer.py<cr>This enables clang-include-fixer for NORMAL and VISUAL mode. Change<leader>cf to another binding if you need clang-include-fixer on a different
key. The <leader> key
is a reference to a specific key defined by the mapleader variable and is bound
to backslash by default.
Make sure vim can find {program}clang-include-fixer:
- Add the path to {program}
clang-include-fixerto the PATH environment variable. - Or set
g:clang_include_fixer_pathin vimrc:let g:clang_include_fixer_path=path/to/clang-include-fixer
You can customize the number of headers being shown by settinglet g:clang_include_fixer_maximum_suggested_headers=5
Customized settings in .vimrc:
let g:clang_include_fixer_path = "clang-include-fixer"Set clang-include-fixer binary file path.
let g:clang_include_fixer_maximum_suggested_headers = 3Set the maximum number of
#includesto show. Default is 3.let g:clang_include_fixer_increment_num = 5Set the increment number of #includes to show every time when pressing
m.
Default is 5.let g:clang_include_fixer_jump_to_include = 0Set to 1 if you want to jump to the new inserted
#includeline. Default islet g:clang_include_fixer_query_mode = 0Set to 1 if you want to insert
#includefor the symbol under the cursor.
Default is 0. Compared to normal mode, this mode won't parse the source file
and only search the symbol from database, which is faster than normal mode.
See clang-include-fixer.py for more details.
Integrate with Emacs
To run clang-include-fixer on a potentially unsaved buffer in Emacs.
Ensure that Emacs finds clang-include-fixer.el by adding the directory
containing the file to the load-path and requiring the clang-include-fixer
in your .emacs:
(add-to-list 'load-path "path/to/llvm/source/clang-tools-extra/clang-include-fixer/tool/"
(require 'clang-include-fixer)Within Emacs the tool can be invoked with the commandM-x clang-include-fixer. This will insert the header that defines the
first undefined symbol; if there is more than one header that would define the
symbol, the user is prompted to select one.
To include the header that defines the symbol at point, runM-x clang-include-fixer-at-point.
Make sure Emacs can find {program}clang-include-fixer:
- Either add the parent directory of {program}
clang-include-fixerto the PATH
environment variable, or customize the Emacs user optionclang-include-fixer-executableto point to the file name of the program.
How it Works
To get the most information out of Clang at parse time,
{program}clang-include-fixer runs in tandem with the parse and receives
callbacks from Clang's semantic analysis. In particular it reuses the existing
support for typo corrections. Whenever Clang tries to correct a potential typo
it emits a callback to the include fixer which then looks for a corresponding
file. At this point rich lookup information is still available, which is not
available in the AST at a later stage.
The identifier that should be typo corrected is then sent to the database, if a
header file is returned it is added as an include directive at the top of the
file.
Currently {program}clang-include-fixer only inserts a single include at a
time to avoid getting caught in follow-up errors. If multiple #include
additions are desired the program can be rerun until a fix-point is reached.
Clang Tools Extra Docs Clang Modernize (`clang-tools-extra/docs/clang-modernize.md`)
orphan: true
All {program}clang-modernize transforms have moved to {doc}clang-tidy/index
(see the modernize module).
Clang Tools Extra Docs Clang Reorder Fields (`clang-tools-extra/docs/clang-reorder-fields.md`)
Clang-Reorder-Fields
{program}clang-reorder-fields is a refactoring tool to reorder fields in
C/C++ structs and classes. This tool automatically updates:
- Field declarations in the record definition
- Constructor initializer lists in C++ classes
- Aggregate initialization expressions (both C and C++)
- Designated initializer lists (C++20)
This can be useful for optimizing memory layout, improving cache performance,
or conforming to coding standards that require specific field orderings.
Example usage
Basic struct reordering
Consider this simple struct in example.c:
struct Foo {
const int *x;
int y;
double z;
int w;
};
int main() {
const int val = 42;
struct Foo foo = { &val, 0, 1.5, 17 };
return 0;
}To reorder the fields to z, w, y, x, run:
clang-reorder-fields -record-name Foo -fields-order z,w,y,x example.c --This will reorder both the struct definition and the initialization:
struct Foo {
double z;
int w;
int y;
const int *x;
};
int main() {
const int val = 42;
struct Foo foo = { 1.5, 17, 0, &val };
return 0;
}Namespaced structs
For C++ code with namespaces, use the fully-qualified name:
namespace bar {
struct Foo {
const int *x;
int y;
double z;
int w;
};
}clang-reorder-fields -record-name ::bar::Foo -fields-order z,w,y,x example.cpp --For classes defined in the global namespace (without any namespace), you can
use either the simple class name or prefix it with :::
clang-reorder-fields -record-name Foo -fields-order z,w,y,x example.cpp --
# or
clang-reorder-fields -record-name ::Foo -fields-order z,w,y,x example.cpp --C++ constructor initializer lists
The tool also reorders constructor initializer lists. Given:
class Foo {
public:
Foo();
private:
int x;
const char *s1;
const char *s2;
double z;
};
Foo::Foo():
x(12),
s1("abc"),
s2("def"),
z(3.14)
{}Running:
clang-reorder-fields -record-name Foo -fields-order s1,x,z,s2 example.cpp --Will reorder both the field declarations and the constructor initializers:
class Foo {
public:
Foo();
private:
const char *s1;
int x;
double z;
const char *s2;
};
Foo::Foo():
s1("abc"),
x(12),
z(3.14),
s2("def")
{}Designated initializers
For C++20 code using designated initializers:
struct Bar {
char a;
int b;
int c;
};
int main() {
Bar bar1 = { 'a', 0, 123 };
Bar bar2 = { .a = 'a', .b = 0, .c = 123 };
return 0;
}clang-reorder-fields --extra-arg="-std=c++20" -record-name Bar \
-fields-order c,a,b example.cpp --Will produce:
struct Bar {
int c;
char a;
int b;
};
int main() {
Bar bar1 = { 123, 'a', 0 };
Bar bar2 = { .c = 123, .a = 'a', .b = 0 };
return 0;
}In-place editing
Use the -i flag to modify files in-place:
clang-reorder-fields -record-name Foo -fields-order z,w,y,x -i example.c --Limitations and Caveats
Different access specifiers
The tool cannot reorder fields with different access specifiers
(public/private/protected). All fields being reordered must have the same
access level.
class Example {
private:
int x;
public:
int y; // Cannot reorder x and y - different access levels
};Multiple field declarations
Declarations with multiple fields in one statement are not supported:
struct Example {
int a, b; // Not supported - multiple fields in one declaration
};Macro-expanded fields
Macros that expand to multiple field declarations are not supported. However,
macros that expand to a single field declaration work correctly:
#define INT_FIELD(NAME) int NAME // Supported - expands to one field
#define TWO_FIELDS int a; int b; // Not supported - expands to two fields
struct Supported {
INT_FIELD(x); // OK - this is a single field
int y;
INT_FIELD(z); // OK - this is a single field
};
struct NotSupported {
TWO_FIELDS // Not OK - expands to multiple fields
int c;
};The tool can reorder fields declared via macros as long as each macro invocation
expands to exactly one field declaration.
Preprocessor directives
Structs with preprocessor directives between fields cannot be reordered:
struct Example {
int a;
#ifdef FEATURE
int b;
#endif
int c; // Not supported - preprocessor directives present
};Flexible array members
In C, a flexible array member is an incomplete array type that must be the last
member of a struct (as specified by C99 and later standards). This allows the
struct to have a variable-length array at the end. Since this is a language
requirement, the tool enforces that flexible array members remain in the last
position:
struct Example {
int count;
int data[]; // Flexible array member - must remain last
};Attempting to reorder fields such that the flexible array member is no longer
last will result in an error:
clang-reorder-fields -record-name Example -fields-order data,count example.c --Will produce:
Flexible array member must remain the last field in the structThis ensures the generated code remains valid C.
Field dependencies in initializers
The tool will issue a warning if reordering causes a field to be used in an
initializer before it's initialized. Consider this example:
class Foo {
public:
Foo(int x, char c);
int x;
char c;
Dummy z;
};
Foo::Foo(int x, char c) :
x(x),
c(c),
z(this->x, c) // z's initializer uses x and c
{}If you reorder the fields to z, c, x:
clang-reorder-fields -record-name Foo -fields-order z,c,x example.cpp --The tool will produce warnings:
example.cpp:10:3: warning: reordering field x after z makes x uninitialized when used in init expression
example.cpp:10:3: warning: reordering field c after z makes c uninitialized when used in init expressionThis warns you that in C++, member initializers are executed in the order that
fields are declared in the class, not the order they appear in the initializer
list. After reordering, z would be initialized first, but its initializer
tries to use x and c which haven't been initialized yet.
The tool will still perform the reordering but warns about the potential issue.
You should review these warnings and adjust your code accordingly.
{program}`clang-reorder-fields` Command Line Options
:::{option} --record-name=
The fully-qualified name of the struct or class to reorder. Required.
For C structs, use the struct name directly (e.g., Foo).
For C++ classes/structs in namespaces, use the fully-qualified name including
namespaces (e.g., ::namespace::ClassName).
For C++ classes/structs in the global namespace, you can use either the simple
name (e.g., Foo) or prefix with :: (e.g., ::Foo).
:::
:::{option} --fields-order=
Comma-separated list of field names in the desired order. Required.
All field names must exactly match the fields in the struct/class definition.
The number of fields must match the number in the definition.
:::
:::{option} -i
Overwrite edited files in-place. If not specified, the rewritten code is
printed to stdout.
:::
:::{option} --extra-arg=
Additional argument to append to the compiler command line.
Useful for specifying language standards (e.g., --extra-arg="-std=c++20").
:::
:::{option} --extra-arg-before=
Additional argument to prepend to the compiler command line.
:::
:::{option} -p
Build path. Specifies the directory containing compile_commands.json for
compilation database support.
:::
Use Cases
Memory layout optimization
Reorder fields to minimize padding and improve cache locality:
// Before: 24 bytes (with padding)
struct Data {
char a; // 1 byte + 7 padding
double b; // 8 bytes
char c; // 1 byte + 7 padding
};clang-reorder-fields -record-name Data -fields-order b,a,c data.c --// After: 16 bytes (less padding)
struct Data {
double b; // 8 bytes
char a; // 1 byte
char c; // 1 byte + 6 padding
};Coding standard compliance
Ensure fields are ordered according to project conventions (e.g., alphabetically,
by type, or by access pattern).
Field grouping
Group related fields together for better code organization and readability.
Clang Tools Extra Docs Clang Tidy (`clang-tools-extra/docs/clang-tidy.md`)
orphan: true
.. meta::
:http-equiv=refresh: 0;URL='clang-tidy/'clang-tidy documentation has moved here: clang-tidy
Clang Tools Extra Docs Clangd (`clang-tools-extra/docs/clangd.md`)
orphan: true redirect_target: https://clangd.llvm.org/ template: clangd_redirect.html
Clang Tools Extra Docs Cpp11 Migrate (`clang-tools-extra/docs/cpp11-migrate.md`)
orphan: true
All {program}clang-modernize transforms have moved to {doc}clang-tidy/index
(see the modernize module).
Clang Tools Extra Docs Modularize (`clang-tools-extra/docs/modularize.md`)
Modularize User's Manual
:hidden: true
ModularizeUsage{program}modularize is a standalone tool that checks whether a set of headers
provides the consistent definitions required to use modules. For example, it
detects whether the same entity (say, a NULL macro or size_t typedef) is
defined in multiple headers or whether a header produces different definitions
under different circumstances. These conditions cause modules built from the
headers to behave poorly, and should be fixed before introducing a module
map.
{program}modularize also has an assistant mode option for generating
a module map file based on the provided header list. The generated file
is a functional module map that can be used as a starting point for a
module.modulemap file.
Getting Started
To build from source:
Read Getting Started with the LLVM System and Clang Tools
Documentation for information on getting sources for LLVM, Clang, and
Clang Extra Tools.Getting Started with the LLVM System and Building LLVM with CMake give
directions for how to build. With sources all checked out into the
right place the LLVM build will build Clang Extra Tools and their
dependencies automatically.- If using CMake, you can also use the
modularizetarget to build
just the modularize tool and its dependencies.
- If using CMake, you can also use the
Before continuing, take a look at {doc}ModularizeUsage to see how to invoke
modularize.
What Modularize Checks
Modularize will check for the following:
- Duplicate global type and variable definitions
- Duplicate macro definitions
- Macro instances, 'defined(macro)', or #if, #elif, #ifdef, #ifndef conditions
that evaluate differently in a header - #include directives inside 'extern "C/C++" {}' or 'namespace (name) {}' blocks
- Module map header coverage completeness (in the case of a module map input
only)
Modularize will do normal C/C++ parsing, reporting normal errors and warnings,
but will also report special error messages like the following:
error: '(symbol)' defined at multiple locations:
(file):(row):(column)
(file):(row):(column)
error: header '(file)' has different contents depending on how it was includedThe latter might be followed by messages like the following:
note: '(symbol)' in (file) at (row):(column) not always providedChecks will also be performed for macro expansions, defined(macro)
expressions, and preprocessor conditional directives that evaluate
inconsistently, and can produce error messages like the following:
(...)/SubHeader.h:11:5:
#if SYMBOL == 1
^
error: Macro instance 'SYMBOL' has different values in this header,
depending on how it was included.
'SYMBOL' expanded to: '1' with respect to these inclusion paths:
(...)/Header1.h
(...)/SubHeader.h
(...)/SubHeader.h:3:9:
#define SYMBOL 1
^
Macro defined here.
'SYMBOL' expanded to: '2' with respect to these inclusion paths:
(...)/Header2.h
(...)/SubHeader.h
(...)/SubHeader.h:7:9:
#define SYMBOL 2
^
Macro defined here.Checks will also be performed for '#include' directives that are
nested inside 'extern "C/C++" {}' or 'namespace (name) {}' blocks,
and can produce error message like the following:
IncludeInExtern.h:2:3:
#include "Empty.h"
^
error: Include directive within extern "C" {}.
IncludeInExtern.h:1:1:
extern "C" {
^
The "extern "C" {}" block is here.(module-map-coverage)=
Module Map Coverage Check
The coverage check uses the Clang library to read and parse the
module map file. Starting at the module map file directory, or just the
include paths, if specified, it will collect the names of all the files it
considers headers (no extension, .h, or .inc--if you need more, modify the
isHeader function). It then compares the headers against those referenced
in the module map, either explicitly named, or implicitly named via an
umbrella directory or umbrella file, as parsed by the ModuleMap object.
If headers are found which are not referenced or covered by an umbrella
directory or file, warning messages will be produced, and this program
will return an error code of 1. If no problems are found, an error code of
0 is returned.
Note that in the case of umbrella headers, this tool invokes the compiler
to preprocess the file, and uses a callback to collect the header files
included by the umbrella header or any of its nested includes. If any
front end options are needed for these compiler invocations, these
can be included on the command line after the module map file argument.
Warning message have the form:
warning: module.modulemap does not account for file: Level3A.h
Note that for the case of the module map referencing a file that does
not exist, the module map parser in Clang will (at the time of this
writing) display an error message.
To limit the checks {program}modularize does to just the module
map coverage check, use the -coverage-check-only option.
For example:
modularize -coverage-check-only module.modulemap(module-map-generation)=
Module Map Generation
If you specify the -module-map-path=<module map file>,
{program}modularize will output a module map based on the input header list.
A module will be created for each header. Also, if the header in the header
list is a partial path, a nested module hierarchy will be created in which a
module will be created for each subdirectory component in the header path,
with the header itself represented by the innermost module. If other headers
use the same subdirectories, they will be enclosed in these same modules also.
For example, for the header list:
SomeTypes.h
SomeDecls.h
SubModule1/Header1.h
SubModule1/Header2.h
SubModule2/Header3.h
SubModule2/Header4.h
SubModule2.hThe following module map will be generated:
// Output/NoProblemsAssistant.txt
// Generated by: modularize -module-map-path=Output/NoProblemsAssistant.txt \
-root-module=Root NoProblemsAssistant.modularize
module SomeTypes {
header "SomeTypes.h"
export *
}
module SomeDecls {
header "SomeDecls.h"
export *
}
module SubModule1 {
module Header1 {
header "SubModule1/Header1.h"
export *
}
module Header2 {
header "SubModule1/Header2.h"
export *
}
}
module SubModule2 {
module Header3 {
header "SubModule2/Header3.h"
export *
}
module Header4 {
header "SubModule2/Header4.h"
export *
}
header "SubModule2.h"
export *
}An optional -root-module=<root-name> option can be used to cause a root module
to be created which encloses all the modules.
An optional -problem-files-list= can be used to input
a list of files to be excluded, perhaps as a temporary stop-gap measure until
problem headers can be fixed.
For example, with the same header list from above:
// Output/NoProblemsAssistant.txt
// Generated by: modularize -module-map-path=Output/NoProblemsAssistant.txt \
-root-module=Root NoProblemsAssistant.modularize
module Root {
module SomeTypes {
header "SomeTypes.h"
export *
}
module SomeDecls {
header "SomeDecls.h"
export *
}
module SubModule1 {
module Header1 {
header "SubModule1/Header1.h"
export *
}
module Header2 {
header "SubModule1/Header2.h"
export *
}
}
module SubModule2 {
module Header3 {
header "SubModule2/Header3.h"
export *
}
module Header4 {
header "SubModule2/Header4.h"
export *
}
header "SubModule2.h"
export *
}
}Note that headers with dependents will be ignored with a warning, as the
Clang module mechanism doesn't support headers that rely on other headers
being included first.
The module map format defines some keywords which can't be used in module
names. If a header has one of these names, an underscore ('_') will be
prepended to the name. For example, if the header name is header.h,
because header is a keyword, the module name will be _header.
For a list of the module map keywords, please see:
Lexical structure
Clang Tools Extra Docs Pp Trace (`clang-tools-extra/docs/pp-trace.md`)
pp-trace User's Manual
{program}pp-trace is a standalone tool that traces preprocessor
activity. It's also used as a test of Clang's PPCallbacks interface.
It runs a given source file through the Clang preprocessor, displaying
selected information from callback functions overridden in a
PPCallbacks
derivation. The output is in a high-level YAML format, described in
{ref}OutputFormat.
(usage)=
pp-trace Usage
Command Line Format
pp-trace [] [-- <front-end-options>]
`` is a place-holder for options
specific to pp-trace, which are described below in
{ref}CommandLineOptions.
`` specifies the source file to run through the preprocessor.
<front-end-options> is a place-holder for regular
Clang Compiler Options,
which must follow the .
(commandlineoptions)=
Command Line Options
:::{option} -callbacks
This option specifies a comma-separated list of globs describing the list of
callbacks that should be traced. Globs are processed in order of appearance.
Positive globs add matched callbacks to the set, negative globs (those with
the '-' prefix) remove callacks from the set.
- FileChanged
- FileSkipped
- InclusionDirective
- moduleImport
- EndOfMainFile
- Ident
- PragmaDirective
- PragmaComment
- PragmaDetectMismatch
- PragmaDebug
- PragmaMessage
- PragmaDiagnosticPush
- PragmaDiagnosticPop
- PragmaDiagnostic
- PragmaOpenCLExtension
- PragmaWarning
- PragmaWarningPush
- PragmaWarningPop
- MacroExpands
- MacroDefined
- MacroUndefined
- Defined
- SourceRangeSkipped
- If
- Elif
- Ifdef
- Ifndef
- Else
- Endif
:::
:::{option} -output
By default, pp-trace outputs the trace information to stdout. Use this
option to output the trace information to a file.
:::
(outputformat)=
pp-trace Output Format
The pp-trace output is formatted as YAML. See https://yaml.org/ for general
YAML information. It's arranged as a sequence of information about the
callback call, including the callback name and argument information, for
example:
---
- Callback: Name
Argument1: Value1
Argument2: Value2
(etc.)
...With real data:
---
- Callback: FileChanged
Loc: "c:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-include.cpp:1:1"
Reason: EnterFile
FileType: C_User
PrevFID: (invalid)
(etc.)
- Callback: FileChanged
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-include.cpp:5:1"
Reason: ExitFile
FileType: C_User
PrevFID: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/Input/Level1B.h"
- Callback: EndOfMainFile
...In all but one case (MacroDirective) the "Argument" scalars have the same
name as the argument in the corresponding PPCallbacks callback function.
Callback Details
The following sections describe the purpose and output format for each callback.
Click on the callback name in the section heading to see the Doxygen
documentation for the callback.
The argument descriptions table describes the callback argument information
displayed.
The Argument Name field in most (but not all) cases is the same name as the
callback function parameter.
The Argument Value Syntax field describes the values that will be displayed
for the argument value. It uses an ad hoc representation that mixes literal
and symbolic representations. Enumeration member symbols are shown as the
actual enum member in a (member1|member2|...) form. A name in parentheses
can either represent a place holder for the described value, or confusingly,
it might be a literal, such as (null), for a null pointer.
Locations are shown as quoted only to avoid confusing the documentation generator.
The Clang C++ Type field is the type from the callback function declaration.
The description describes the argument or what is displayed for it.
Note that in some cases, such as when a structure pointer is an argument
value, only some key member or members are shown to represent the value,
instead of trying to display all members of the structure.
[FileChanged](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a7cc8cfaf34114fc65e92af621cd6464e) Callback
FileChanged is called when the preprocessor enters or exits a file, both the
top level file being compiled, as well as any #include directives. It will
also be called as a result of a system header pragma or in internal renaming
of a file.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the directive. |
| Reason | (EnterFile|ExitFile|SystemHeaderPragma|RenameFile) | PPCallbacks::FileChangeReason | Reason for change. |
| FileType | (C_User|C_System|C_ExternCSystem) | SrcMgr::CharacteristicKind | Include type. |
| PrevFID | ((file)|(invalid)) | FileID | Previous file, if any. |
Example:
- Callback: FileChanged
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-include.cpp:1:1"
Reason: EnterFile
FileType: C_User
PrevFID: (invalid)[FileSkipped](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ab5b338a0670188eb05fa7685bbfb5128) Callback
FileSkipped is called when a source file is skipped as the result of header
guard optimization.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| ParentFile | ("(file)" or (null)) | const FileEntry | The file that #included the skipped file. |
| FilenameTok | (token) | const Token | The token in ParentFile that indicates the skipped file. |
| FileType | (C_User|C_System|C_ExternCSystem) | SrcMgr::CharacteristicKind | The file type. |
Example:
- Callback: FileSkipped
ParentFile: "/path/filename.h"
FilenameTok: "filename.h"
FileType: C_User[InclusionDirective](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a557d9738c329793513a6f57d6b60de52) Callback
InclusionDirective is called when an inclusion directive of any kind (#include</code>, #import</code>, etc.) has been processed, regardless of whether the inclusion will actually result in an inclusion.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| HashLoc | "(file):(line):(col)" | SourceLocation | The location of the '#' that starts the inclusion directive. |
| IncludeTok | (token) | const Token | The token that indicates the kind of inclusion directive, e.g., 'include' or 'import'. |
| FileName | "(file)" | StringRef | The name of the file being included, as written in the source code. |
| IsAngled | (true|false) | bool | Whether the file name was enclosed in angle brackets; otherwise, it was enclosed in quotes. |
| FilenameRange | "(file)" | CharSourceRange | The character range of the quotes or angle brackets for the written file name. |
| File | "(file)" | const FileEntry | The actual file that may be included by this inclusion directive. |
| SearchPath | "(path)" | StringRef | Contains the search path which was used to find the file in the file system. |
| RelativePath | "(path)" | StringRef | The path relative to SearchPath, at which the include file was found. |
| Imported | ((module name)|(null)) | const Module | The module, whenever an inclusion directive was automatically turned into a module import or null otherwise. |
Example:
- Callback: InclusionDirective
HashLoc: "D:/Clang/llvmnewmod/clang-tools-extra/test/pp-trace/pp-trace-include.cpp:4:1"
IncludeTok: include
FileName: "Input/Level1B.h"
IsAngled: false
FilenameRange: "Input/Level1B.h"
File: "D:/Clang/llvmnewmod/clang-tools-extra/test/pp-trace/Input/Level1B.h"
SearchPath: "D:/Clang/llvmnewmod/clang-tools-extra/test/pp-trace"
RelativePath: "Input/Level1B.h"
Imported: (null)[moduleImport](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#af32dcf1b8b7c179c7fcd3e24e89830fe) Callback
moduleImport is called when there was an explicit module-import syntax.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| ImportLoc | "(file):(line):(col)" | SourceLocation | The location of import directive token. |
| Path | "(path)" | ModuleIdPath | The identifiers (and their locations) of the module "path". |
| Imported | ((module name)|(null)) | const Module | The imported module; can be null if importing failed. |
Example:
- Callback: moduleImport
ImportLoc: "d:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-modules.cpp:4:2"
Path: [{Name: Level1B, Loc: "d:/Clang/llvmnewmod/clang-tools-extra/test/pp-trace/pp-trace-modules.cpp:4:9"}, {Name: Level2B, Loc: "d:/Clang/llvmnewmod/clang-tools-extra/test/pp-trace/pp-trace-modules.cpp:4:17"}]
Imported: Level2B[EndOfMainFile](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a63e170d069e99bc1c9c7ea0f3bed8bcc) Callback
EndOfMainFile is called when the end of the main file is reached.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| (no arguments) |
Example:
- Callback: EndOfMainFile[Ident](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a3683f1d1fa513e9b6193d446a5cc2b66) Callback
Ident is called when a #ident or #sccs directive is read.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the directive. |
| str | (name) | const std::string | The text of the directive. |
Example:
- Callback: Ident
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-ident.cpp:3:1"
str: "$Id$"[PragmaDirective](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a0a2d7a72c62184b3cbde31fb62c6f2f7) Callback
PragmaDirective is called when start reading any pragma directive.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the directive. |
| Introducer | (PIK_HashPragma|PIK__Pragma|PIK___pragma) | PragmaIntroducerKind | The type of the pragma directive. |
Example:
- Callback: PragmaDirective
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
Introducer: PIK_HashPragma[PragmaComment](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ace0d940fc2c12ab76441466aab58dc37) Callback
PragmaComment is called when a #pragma comment directive is read.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the directive. |
| Kind | ((name)|(null)) | const IdentifierInfo | The comment kind symbol. |
| Str | (message directive) | const std::string | The comment message directive. |
Example:
- Callback: PragmaComment
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
Kind: library
Str: kernel32.lib[PragmaDetectMismatch](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ab11158c9149fb8ad8af1903f4a6cd65d) Callback
PragmaDetectMismatch is called when a #pragma detect_mismatch directive is read.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the directive. |
| Name | "(name)" | const std::string | The name. |
| Value | (string) | const std::string | The value. |
Example:
- Callback: PragmaDetectMismatch
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
Name: name
Value: value[PragmaDebug](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a57cdccb6dcc07e926513ac3d5b121466) Callback
PragmaDebug is called when a #pragma clang __debug directive is read.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the directive. |
| DebugType | (string) | StringRef | Indicates type of debug message. |
Example:
- Callback: PragmaDebug
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
DebugType: warning[PragmaMessage](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#abb42935d9a9fd8e2c4f51cfdc4ea2ae1) Callback
PragmaMessage is called when a #pragma message directive is read.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the directive. |
| Namespace | (name) | StringRef | The namespace of the message directive. |
| Kind | (PMK_Message|PMK_Warning|PMK_Error) | PPCallbacks::PragmaMessageKind | The type of the message directive. |
| Str | (string) | StringRef | The text of the message directive. |
Example:
- Callback: PragmaMessage
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
Namespace: "GCC"
Kind: PMK_Message
Str: The message text.[PragmaDiagnosticPush](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a0f3ff19762baa38fe6c5c58022d32979) Callback
PragmaDiagnosticPush is called when a #pragma gcc diagnostic push directive is read.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the directive. |
| Namespace | (name) | StringRef | Namespace name. |
Example:
- Callback: PragmaDiagnosticPush
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
Namespace: "GCC"[PragmaDiagnosticPop](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ac94d789873122221fba8d76f6c5ea45e) Callback
PragmaDiagnosticPop is called when a #pragma gcc diagnostic pop directive is read.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the directive. |
| Namespace | (name) | StringRef | Namespace name. |
Example:
- Callback: PragmaDiagnosticPop
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
Namespace: "GCC"[PragmaDiagnostic](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#afe7938f38a83cb7b4b25a13edfdd7bdd) Callback
PragmaDiagnostic is called when a #pragma gcc diagnostic directive is read.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the directive. |
| Namespace | (name) | StringRef | Namespace name. |
| mapping | (0|MAP_IGNORE|MAP_WARNING|MAP_ERROR|MAP_FATAL) | diag::Severity | Mapping type. |
| Str | (string) | StringRef | Warning/error name. |
Example:
- Callback: PragmaDiagnostic
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
Namespace: "GCC"
mapping: MAP_WARNING
Str: WarningName[PragmaOpenCLExtension](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a92a20a21fadbab4e2c788f4e27fe07e7) Callback
PragmaOpenCLExtension is called when OpenCL extension is either disabled or enabled with a pragma.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| NameLoc | "(file):(line):(col)" | SourceLocation | The location of the name. |
| Name | (name) | const IdentifierInfo | Name symbol. |
| StateLoc | "(file):(line):(col)" | SourceLocation | The location of the state. |
| State | (1|0) | unsigned | Enabled/disabled state. |
Example:
- Callback: PragmaOpenCLExtension
NameLoc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:10"
Name: Name
StateLoc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:18"
State: 1[PragmaWarning](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#aa17169d25fa1cf0a6992fc944d1d8730) Callback
PragmaWarning is called when a #pragma warning directive is read.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the directive. |
| WarningSpec | (string) | StringRef | The warning specifier. |
| Ids | [(number)[, ...]] | ArrayRef\ | The warning numbers. |
Example:
- Callback: PragmaWarning
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
WarningSpec: disable
Ids: 1,2,3[PragmaWarningPush](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ae5626ef70502687a859f323a809ed0b6) Callback
PragmaWarningPush is called when a #pragma warning(push) directive is read.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the directive. |
| Level | (number) | int | Warning level. |
Example:
- Callback: PragmaWarningPush
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
Level: 1[PragmaWarningPop](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ac98d502af8811b8a6e7342d7cd2b3b95) Callback
PragmaWarningPop is called when a #pragma warning(pop) directive is read.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the directive. |
Example:
- Callback: PragmaWarningPop
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1"[MacroExpands](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a9bc725209d3a071ea649144ab996d515) Callback
MacroExpands is called when ::HandleMacroExpandedIdentifier when a macro invocation is found.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| MacroNameTok | (token) | const Token | The macro name token. |
| MacroDirective | (MD_Define|MD_Undefine|MD_Visibility) | const MacroDirective | The kind of macro directive from the MacroDirective structure. |
| Range | ["(file):(line):(col)", "(file):(line):(col)"] | SourceRange | The source range for the expansion. |
| Args | [(name)|(number)|<(token name)>[, ...]] | const MacroArgs | The argument tokens. Names and numbers are literal, everything else is of the form '<' tokenName '>'. |
Example:
- Callback: MacroExpands
MacroNameTok: X_IMPL
MacroDirective: MD_Define
Range: [(nonfile), (nonfile)]
Args: [a <plus> y, b][MacroDefined](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a8448fc9f96f22ad1b93ff393cffc5a76) Callback
MacroDefined is called when a macro definition is seen.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| MacroNameTok | (token) | const Token | The macro name token. |
| MacroDirective | (MD_Define|MD_Undefine|MD_Visibility) | const MacroDirective | The kind of macro directive from the MacroDirective structure. |
Example:
- Callback: MacroDefined
MacroNameTok: X_IMPL
MacroDirective: MD_Define[MacroUndefined](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#acb80fc6171a839db8e290945bf2c9d7a) Callback
MacroUndefined is called when a macro #undef is seen.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| MacroNameTok | (token) | const Token | The macro name token. |
| MacroDirective | (MD_Define|MD_Undefine|MD_Visibility) | const MacroDirective | The kind of macro directive from the MacroDirective structure. |
Example:
- Callback: MacroUndefined
MacroNameTok: X_IMPL
MacroDirective: MD_Define[Defined](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a3cc2a644533d0e4088a13d2baf90db94) Callback
Defined is called when the 'defined' operator is seen.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| MacroNameTok | (token) | const Token | The macro name token. |
| MacroDirective | (MD_Define|MD_Undefine|MD_Visibility) | const MacroDirective | The kind of macro directive from the MacroDirective structure. |
| Range | ["(file):(line):(col)", "(file):(line):(col)"] | SourceRange | The source range for the directive. |
Example:
- Callback: Defined
MacroNameTok: MACRO
MacroDirective: (null)
Range: ["D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:5", "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:19"][SourceRangeSkipped](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#abdb4ebe11610f079ac33515965794b46) Callback
SourceRangeSkipped is called when a source range is skipped.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Range | ["(file):(line):(col)", "(file):(line):(col)"] | SourceRange | The source range skipped. |
Example:
- Callback: SourceRangeSkipped
Range: [":/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:2", ":/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:9:2"][If](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a645edcb0d6becbc6f256f02fd1287778) Callback
If is called when an #if is seen.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the directive. |
| ConditionRange | ["(file):(line):(col)", "(file):(line):(col)"] | SourceRange | The source range for the condition. |
| ConditionValue | (true|false) | bool | The condition value. |
Example:
- Callback: If
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:2"
ConditionRange: ["D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:4", "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:9:1"]
ConditionValue: false[Elif](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a180c9e106a28d60a6112e16b1bb8302a) Callback
Elif is called when an #elif is seen.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the directive. |
| ConditionRange | ["(file):(line):(col)", "(file):(line):(col)"] | SourceRange | The source range for the condition. |
| ConditionValue | (true|false) | bool | The condition value. |
| IfLoc | "(file):(line):(col)" | SourceLocation | The location of the directive. |
Example:
- Callback: Elif
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:10:2"
ConditionRange: ["D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:10:4", "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:11:1"]
ConditionValue: false
IfLoc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:2"[Ifdef](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a0ce79575dda307784fd51a6dd4eec33d) Callback
Ifdef is called when an #ifdef is seen.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the directive. |
| MacroNameTok | (token) | const Token | The macro name token. |
| MacroDirective | (MD_Define|MD_Undefine|MD_Visibility) | const MacroDirective | The kind of macro directive from the MacroDirective structure. |
Example:
- Callback: Ifdef
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-conditional.cpp:3:1"
MacroNameTok: MACRO
MacroDirective: MD_Define[Ifndef](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a767af69f1cdcc4cd880fa2ebf77ad3ad) Callback
Ifndef is called when an #ifndef is seen.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the directive. |
| MacroNameTok | (token) | const Token | The macro name token. |
| MacroDirective | (MD_Define|MD_Undefine|MD_Visibility) | const MacroDirective | The kind of macro directive from the MacroDirective structure. |
Example:
- Callback: Ifndef
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-conditional.cpp:3:1"
MacroNameTok: MACRO
MacroDirective: MD_Define[Else](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ad57f91b6d9c3cbcca326a2bfb49e0314) Callback
Else is called when an #else is seen.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the else directive. |
| IfLoc | "(file):(line):(col)" | SourceLocation | The location of the if directive. |
Example:
- Callback: Else
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:10:2"
IfLoc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:2"[Endif](https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#afc62ca1401125f516d58b1629a2093ce) Callback
Endif is called when an #endif is seen.
Argument descriptions:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
|---|---|---|---|
| Loc | "(file):(line):(col)" | SourceLocation | The location of the endif directive. |
| IfLoc | "(file):(line):(col)" | SourceLocation | The location of the if directive. |
Example:
- Callback: Endif
Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:10:2"
IfLoc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:2"Building pp-trace
To build from source:
Read Getting Started with the LLVM System and Clang Tools
Documentation for information on getting sources for LLVM, Clang, and
Clang Extra Tools.Getting Started with the LLVM System and Building LLVM with CMake give
directions for how to build. With sources all checked out into the
right place the LLVM build will build Clang Extra Tools and their
dependencies automatically.- If using CMake, you can also use the
pp-tracetarget to build
just the pp-trace tool and its dependencies.
- If using CMake, you can also use the
Clang Docs AMDGPUSupport (`clang/docs/AMDGPUSupport.md`)
AMDGPU Support
Clang supports OpenCL, HIP and OpenMP on AMD GPU targets.
Predefined Macros
| Macro | Description |
|---|---|
__AMDGPU__ |
Indicates that the code is being compiled for an AMD GPU. |
__AMDGCN__ |
Defined if the GPU target is AMDGCN. |
__R600__ |
Defined if the GPU target is R600. |
__<ArchName>__ |
Defined with the name of the architecture (e.g., __gfx906__ for the gfx906 architecture). |
__<GFXN>__ |
Defines the GFX family (e.g., for gfx906, this macro would be __GFX9__). |
__amdgcn_processor__ |
Defined with the processor name as a string (e.g., "gfx906"). |
__amdgcn_target_id__ |
Defined with the target ID as a string. |
__amdgcn_feature_<feature-name>__ |
Defined for each supported target feature. The value is 1 if the feature is enabled and 0 if it is disabled. Allowed feature names are sramecc and xnack. |
__AMDGCN_CUMODE__ |
Defined as 1 if the CU mode is enabled and 0 if the WGP mode is enabled. |
__AMDGCN_UNSAFE_FP_ATOMICS__ |
Defined if unsafe floating-point atomics are allowed. |
__HAS_FMAF__ |
Defined if FMAF instruction is available (deprecated). |
__HAS_LDEXPF__ |
Defined if LDEXPF instruction is available (deprecated). |
__HAS_FP64__ |
Defined if FP64 instruction is available (deprecated). |
Please note that the specific architecture and feature names will vary depending on the GPU. Also, some macros are deprecated and may be removed in future releases.
Target-Specific Builtins
Clang exposes AMDGPU hardware intrinsics as target-specific builtins with the__builtin_amdgcn_ prefix. These are documented in {doc}AMDGPUBuiltinReference.
Target-Specific Types
Named Workgroup Barrier Type
The __amdgpu_named_workgroup_barrier_t type is used to represent the GFX12.5 named barriers.
This type is subject to certain restrictions when used to declare non-static fields of classes.
See the {ref}named barrier wrappers<namedbarrierwrappers> section below for more information.
Example usage:
__amdgpu_named_workgroup_barrier_t x;
__amdgpu_named_workgroup_barrier_t arr[2]; // Arrays are also fine
struct SimpleWrapper {
__amdgpu_named_workgroup_barrier_t foo;
};
void foo(int a)
{
__builtin_amdgcn_s_barrier_init(&x, a);
}(namedbarrierwrappers)=
Named Barrier Wrappers
A "named barrier wrapper" is a class that contains exactly one non-static field
of one of the following types:
__amdgpu_named_workgroup_barrier_t, or an array thereof.- Another named barrier wrapper, or an array thereof.
In C++, a class that inherits from a named barrier wrapper is also considered a
named barrier wrapper, and all named barrier wrappers must be standard-layout
types (see std::is_standard_layout). This means that named barrier wrappers:
- May not have a virtual table: they cannot declare or inherit any virtual
functions, or inherit from a virtual base class. - May not have any extra fields, either declared by the class or inherited
from a base class.
Clang Docs APINotes (`clang/docs/APINotes.md`)
API Notes: Annotations Without Modifying Headers
The Problem: You have headers you want to use, but you also want to add
extra information to the API. You don't want to put that information in the
headers themselves --- perhaps because you want to keep them clean for other
clients, or perhaps because they're from some open source project and you don't
want to modify them at all.
Incomplete solution: Redeclare all the interesting parts of the API in your
own header and add the attributes you want. Unfortunately, this:
- doesn't work with attributes that must be present on a definition
- doesn't allow changing the definition in other ways
- requires your header to be included in any client code to take effect
Better solution: Provide a "sidecar" file with the information you want to
add, and have that automatically get picked up by the module-building logic in
the compiler.
That's API notes.
API notes use a YAML-based file format. YAML is a format best explained by
example, so here is a small example
from the compiler test suite of API
notes for a hypothetical "SomeKit" framework.
Usage
API notes files are found relative to the module map that defines a module,
under the name "SomeKit.apinotes" for a module named "SomeKit". Additionally, a
file named "SomeKit_private.apinotes" will also be picked up to go with a
private module map. For bare modules these two files will be in the same
directory as the corresponding module map; for framework modules, they should
be placed in the Headers and PrivateHeaders directories, respectively. The
module map for a private top-level framework module should be placed in the
PrivateHeaders directory as well, though it does not need an additional
"_private" suffix on its name.
Clang will search for API notes files next to module maps only when passed the-fapinotes-modules option.
Limitations
- Since they're identified by module name, API notes cannot be used to modify
arbitrary textual headers.
"Versioned" API Notes
Many API notes affect how a C API is imported into Swift. In order to change
that behavior while still remaining backwards-compatible, API notes can be
selectively applied based on the Swift compatibility version provided to the
compiler (e.g. -fapi-notes-swift-version=5). The rule is that an
explicitly-versioned API note applies to that version and all earlier
versions, and any applicable explicitly-versioned API note takes precedence
over an unversioned API note.
Reference
An API notes file contains a YAML dictionary with the following top-level
entries:
/* Detailed source-code truncated for AI context efficiency. */Clang Docs AddressSanitizer (`clang/docs/AddressSanitizer.md`)
AddressSanitizer
Introduction
AddressSanitizer is a fast memory error detector. It consists of a compiler
instrumentation module and a run-time library. The tool can detect the
following types of bugs:
- Out-of-bounds accesses to heap, stack and globals
- Use-after-free
- Use-after-return (clang flag
-fsanitize-address-use-after-return=(never|runtime|always)default:runtime)
: - Enable with:ASAN_OPTIONS=detect_stack_use_after_return=1(already enabled on Linux).
- Disable with:ASAN_OPTIONS=detect_stack_use_after_return=0. - Use-after-scope (clang flag
-fsanitize-address-use-after-scope) - Double-free, invalid free
- Memory leaks (experimental)
Typical slowdown introduced by AddressSanitizer is 2x.
How to build
Build LLVM/Clang with CMake and enable
the compiler-rt runtime. An example CMake configuration that will allow
for the use/testing of AddressSanitizer:
$ cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang" -DLLVM_ENABLE_RUNTIMES="compiler-rt" <path to source>/llvmUsage
Simply compile and link your program with -fsanitize=address flag. The
AddressSanitizer run-time library should be linked to the final executable, so
make sure to use clang (not ld) for the final link step. When linking
shared libraries, the AddressSanitizer run-time is not linked, so-Wl,-z,defs may cause link errors (don't use it with AddressSanitizer). To
get a reasonable performance add -O1 or higher. To get nicer stack traces
in error messages add -fno-omit-frame-pointer. To get perfect stack traces
you may need to disable inlining (just use -O1) and tail call elimination
(-fno-optimize-sibling-calls).
% cat example_UseAfterFree.cc
int main(int argc, char **argv) {
int *array = new int[100];
delete [] array;
return array[argc]; // BOOM
}
# Compile and link
% clang++ -O1 -g -fsanitize=address -fno-omit-frame-pointer example_UseAfterFree.ccor:
# Compile
% clang++ -O1 -g -fsanitize=address -fno-omit-frame-pointer -c example_UseAfterFree.cc
# Link
% clang++ -g -fsanitize=address example_UseAfterFree.oIf a bug is detected, the program will print an error message to stderr and
exit with a non-zero exit code. AddressSanitizer exits on the first detected error.
This is by design:
- This approach allows AddressSanitizer to produce faster and smaller generated code
(both by ~5%). - Fixing bugs becomes unavoidable. AddressSanitizer does not produce
false alarms. Once a memory corruption occurs, the program is in an inconsistent
state, which could lead to confusing results and potentially misleading
subsequent reports.
If your process is sandboxed and you are running on OS X 10.10 or earlier, you
will need to set DYLD_INSERT_LIBRARIES environment variable and point it to
the ASan library that is packaged with the compiler used to build the
executable. (You can find the library by searching for dynamic libraries withasan in their name.) If the environment variable is not set, the process will
try to re-exec. Also keep in mind that when moving the executable to another machine,
the ASan library will also need to be copied over.
Symbolizing the Reports
To make AddressSanitizer symbolize its output
you need to set the ASAN_SYMBOLIZER_PATH environment variable to point to
the llvm-symbolizer binary (or make sure llvm-symbolizer is in your$PATH):
% ASAN_SYMBOLIZER_PATH=/usr/local/bin/llvm-symbolizer ./a.out
==9442== ERROR: AddressSanitizer heap-use-after-free on address 0x7f7ddab8c084 at pc 0x403c8c bp 0x7fff87fb82d0 sp 0x7fff87fb82c8
READ of size 4 at 0x7f7ddab8c084 thread T0
#0 0x403c8c in main example_UseAfterFree.cc:4
#1 0x7f7ddabcac4d in __libc_start_main ??:0
0x7f7ddab8c084 is located 4 bytes inside of 400-byte region [0x7f7ddab8c080,0x7f7ddab8c210)
freed by thread T0 here:
#0 0x404704 in operator delete[](void*) ??:0
#1 0x403c53 in main example_UseAfterFree.cc:4
#2 0x7f7ddabcac4d in __libc_start_main ??:0
previously allocated by thread T0 here:
#0 0x404544 in operator new[](unsigned long) ??:0
#1 0x403c43 in main example_UseAfterFree.cc:2
#2 0x7f7ddabcac4d in __libc_start_main ??:0
==9442== ABORTINGIf that does not work for you (e.g. your process is sandboxed), you can use a
separate script to symbolize the result offline (online symbolization can be
force disabled by setting ASAN_OPTIONS=symbolize=0):
% ASAN_OPTIONS=symbolize=0 ./a.out 2> log
% projects/compiler-rt/lib/asan/scripts/asan_symbolize.py / < log | c++filt
==9442== ERROR: AddressSanitizer heap-use-after-free on address 0x7f7ddab8c084 at pc 0x403c8c bp 0x7fff87fb82d0 sp 0x7fff87fb82c8
READ of size 4 at 0x7f7ddab8c084 thread T0
#0 0x403c8c in main example_UseAfterFree.cc:4
#1 0x7f7ddabcac4d in __libc_start_main ??:0
...Note that on macOS you may need to run dsymutil on your binary to have the
file:line info in the AddressSanitizer reports.
Additional Checks
Initialization order checking
AddressSanitizer can optionally detect dynamic initialization order problems,
when initialization of globals defined in one translation unit uses
globals defined in another translation unit. To enable this check at runtime,
you should set environment variableASAN_OPTIONS=check_initialization_order=1.
Note that this option is not supported on macOS.
Stack Use After Return (UAR)
AddressSanitizer can optionally detect stack use after return problems.
This is available by default, or explicitly
(-fsanitize-address-use-after-return=runtime).
To disable this check at runtime, set the environment variableASAN_OPTIONS=detect_stack_use_after_return=0.
Enabling this check (-fsanitize-address-use-after-return=always) will
reduce code size. The code size may be reduced further by completely
eliminating this check (-fsanitize-address-use-after-return=never).
To summarize: -fsanitize-address-use-after-return=<mode>
: - never: Completely disables detection of UAR errors (reduces code size).
- runtime: Adds the code for detection, but it can be disabled via the
runtime environment (ASAN_OPTIONS=detect_stack_use_after_return=0).
- always: Enables detection of UAR errors in all cases. (reduces code
size, but not as much as never).
Container Overflow Detection
AddressSanitizer can detect overflows in containers with custom allocators
(such as std::vector) where the library developers have added calls into the
AddressSanitizer runtime to indicate which memory is poisoned etc.
Note that this feature is prone to false positives:
: - Partially poisoning objects on stack, e.g. for small string optimization, can
cause both false positives and negatives.
- If the binary is partially AddressSanitizer instrumented, these
checks can cause false positives.
See Disabling container overflow checks for details on suppressing checks.
Memory leak detection
For more information on leak detector in AddressSanitizer, see
{doc}LeakSanitizer. The leak detection is turned on by default on Linux,
and can be enabled using ASAN_OPTIONS=detect_leaks=1 on macOS;
however, it is not yet supported on other platforms.
Flags and Options
Runtime flags can be passed to AddressSanitizer via the ASAN_OPTIONS environment
variable:
$ ASAN_OPTIONS="verbosity=1:detect_stack_use_after_return=1" ./a.outTo see the full list of available flags, run an instrumented binary withASAN_OPTIONS="help=1".
Flags passed via the ASAN_OPTIONS environment variable take precedence over
compile-time default options.
Compile-time Default Options
Default options can be specified at compile/link time by defining
the __asan_default_options function in your source code:
#include <sanitizer/asan_interface.h>
extern "C" const char *__asan_default_options() {
return "verbosity=1:detect_stack_use_after_return=1";
}Options Evaluation with Integrated Sanitizers
When running AddressSanitizer with integrated {doc}LeakSanitizer or {doc}UndefinedBehaviorSanitizer:
__asan_default_options(),__lsan_default_options(), and__ubsan_default_options()are all evaluated independently by the runtime.- The environment variables
ASAN_OPTIONS,LSAN_OPTIONS, andUBSAN_OPTIONSare also parsed independently.
LSan and UBSan flags should be passed via their own environment variables (LSAN_OPTIONS, UBSAN_OPTIONS) or default option hooks (__lsan_default_options(), __ubsan_default_options()) rather than packed into ASAN_OPTIONS or __asan_default_options().
Issue Suppression
AddressSanitizer is not expected to produce false positives. If you see one,
look again; most likely it is a true positive!
Suppressing Reports in External Libraries
Runtime interposition allows AddressSanitizer to find bugs in code that is
not being recompiled. If you run into an issue in external libraries, we
recommend immediately reporting it to the library maintainer so that it
gets addressed. However, you can use the following suppression mechanism
to unblock yourself and continue on with the testing. This suppression
mechanism should only be used for suppressing issues in external code; it
does not work on code recompiled with AddressSanitizer. To suppress errors
in external libraries, set the ASAN_OPTIONS environment variable to point
to a suppression file. You can either specify the full path to the file or the
path of the file relative to the location of your executable.
ASAN_OPTIONS=suppressions=MyASan.suppEach non-empty line of the suppression file represents one suppression of the
form suppression_type:suppression_pattern. Supported types are:
interceptor_via_fun: Suppress errors when the given function is in the caller stack trace.interceptor_via_lib: Suppress errors when the call originates from the given library.interceptor_name: Suppress an interceptor by function name directly (e.g.,interceptor_name:memcpy).odr_violation: Suppress One Definition Rule violation reports for global variables.alloc_dealloc_mismatch: Suppress allocation/deallocation mismatch errors for specific functions in the stack trace.
interceptor_via_fun:NameOfCFunctionToSuppress
interceptor_via_fun:-[ClassName objCMethodToSuppress:]
interceptor_via_lib:NameOfTheLibraryToSuppress
interceptor_name:memcpy
odr_violation:my_global_var
alloc_dealloc_mismatch:my_allocator_fnAlternatively, you can provide default suppressions at compile time by defining
the __asan_default_suppressions function in your source code:
#include <sanitizer/asan_interface.h>
extern "C" const char *__asan_default_suppressions() {
return "interceptor_via_lib:NameOfTheLibraryToSuppress\n"
"interceptor_name:memcpy\n";
}Conditional Compilation with `__has_feature(address_sanitizer)`
In some cases one may need to execute different code depending on whether
AddressSanitizer is enabled.
{ref}__has_feature <langext-__has_feature-__has_extension> can be used for
this purpose.
#if defined(__has_feature)
# if __has_feature(address_sanitizer)
// code that builds only under AddressSanitizer
# endif
#endifDisabling Instrumentation with `__attribute__((no_sanitize("address")))`
Some code should not be instrumented by AddressSanitizer. One may use
the attribute __attribute__((no_sanitize("address"))) (which has
deprecated synonyms no_sanitize_address andno_address_safety_analysis) to disable instrumentation of a
particular function. This attribute may not be supported by other
compilers, so we suggest to use it together with__has_feature(address_sanitizer).
The same attribute used on a global variable prevents AddressSanitizer
from adding redzones around it and detecting out of bounds accesses.
AddressSanitizer also supports__attribute__((disable_sanitizer_instrumentation)). This attribute
works similarly to __attribute__((no_sanitize("address"))), but it also
prevents instrumentation performed by other sanitizers.
Interaction of Inlining with Disabling Sanitizer Instrumentation
- A
no_sanitizefunction will not be inlined heuristically by the compiler into a sanitized function. - An
always_inlinefunction will adopt the instrumentation status of the function it is inlined into. - Forcibly combining
no_sanitizeand__attribute__((always_inline))is not supported, and will often lead to unexpected results. To avoid mixing these attributes, use:
// Note, __has_feature test for sanitizers is deprecated, and Clang will support __SANITIZE_<sanitizer>__ similar to GCC.
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) || ... <other sanitizers>
#define ALWAYS_INLINE_IF_UNINSTRUMENTED
#else
#define ALWAYS_INLINE_IF_UNINSTRUMENTED __attribute__((always_inline))
#endifExplicit Sanitizer Checks with `__builtin_allow_sanitize_check`
The __builtin_allow_sanitize_check("address") builtin can be used to
conditionally execute code depending on whether AddressSanitizer checks are
enabled and permitted by the current policy (after inlining). This is
particularly useful for inserting explicit, sanitizer-specific checks around
operations like syscalls or inline assembly, which might otherwise be unchecked
by the sanitizer.
Example:
void __asan_load8(void *);
inline __attribute__((always_inline))
void my_helper(void *addr) {
if (__builtin_allow_sanitize_check("address"))
__asan_load8(addr);
// ... actual logic, e.g. inline assembly ...
asm volatile ("..." : : "r" (addr) : "memory");
}
void instrumented_function() {
...
my_helper(buf); // checks are active
...
}
__attribute__((no_sanitize("address")))
void uninstrumented_function() {
...
my_helper(buf); // checks are skipped
...
}Disabling container overflow checks
Runtime suppression
Container overflow checks can be disabled at runtime using theASAN_OPTIONS=detect_container_overflow=0 environment variable.
Compile time suppression
-D__SANITIZER_DISABLE_CONTAINER_OVERFLOW__ can be used at compile time to
disable container overflow checks if the container library has added support
for this define.
To support a standard way to disable container overflow checks at compile time,
library developers should use this definition in conjunction with the
AddressSanitizer feature test to conditionally include container overflow
related code compiled into user code:
The recommended form is
// include the sanitizer common interfaces
#include <sanitizer/common_interface_defs.h>
#if __has_feature(address_sanitizer)
// Container overflow detection enabled - include annotations
__sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid);
#endifThis pattern ensures that:
- Container overflow annotations are only included when AddressSanitizer is
enabled - Container overflow detection can be disabled by passing
-D__SANITIZER_DISABLE_CONTAINER_OVERFLOW__to the compiler
Suppressing Errors in Recompiled Code (Ignorelist)
AddressSanitizer supports src and fun entity types in
{doc}SanitizerSpecialCaseList, that can be used to suppress error reports
in the specified source files or functions. Additionally, AddressSanitizer
introduces global and type entity types that can be used to
suppress error reports for out-of-bound access to globals with certain
names and types (you may only specify class or struct types).
You may use an init category to suppress reports about initialization-order
problems happening in certain source files or with certain global variables.
# Suppress error reports for code in a file or in a function:
src:bad_file.cpp
# Ignore all functions with names containing MyFooBar:
fun:*MyFooBar*
# Disable out-of-bound checks for global:
global:bad_array
# Disable out-of-bound checks for global instances of a given class ...
type:Namespace::BadClassName
# ... or a given struct. Use wildcard to deal with anonymous namespace.
type:Namespace2::*::BadStructName
# Disable initialization-order checks for globals:
global:bad_init_global=init
type:*BadInitClassSubstring*=init
src:bad/init/files/*=initSuppressing memory leaks
Memory leak reports produced by {doc}LeakSanitizer (when run as part of
AddressSanitizer) can be suppressed at runtime via LSAN_OPTIONS=suppressions=...
or at compile time via __lsan_default_suppressions(). See {doc}LeakSanitizer
for full details on suppression rules, default options, and programmatic APIs.
Code generation control
Instrumentation code outlining
By default AddressSanitizer inlines the instrumentation code to improve the
run-time performance, which leads to increased binary size. Using the
(clang flag -fsanitize-address-outline-instrumentation default: false)
flag forces all code instrumentation to be outlined, which reduces the size
of the generated code, but also reduces the run-time performance.
Limitations
- AddressSanitizer uses more real memory than a native run. Exact overhead
depends on the allocation sizes. The smaller the allocations you make the
bigger the overhead is. - AddressSanitizer uses more stack memory. We have seen up to 3x increase.
- On 64-bit platforms AddressSanitizer maps (but not reserves) 16+ Terabytes of
virtual address space. This means that tools likeulimitmay not work as
usually expected. - Static linking of executables is not supported.
Security Considerations
AddressSanitizer is a bug detection tool and its runtime is not meant to be
linked against production executables. While it may be useful for testing,
AddressSanitizer's runtime was not developed with security-sensitive
constraints in mind and may compromise the security of the resulting executable.
Supported Platforms
AddressSanitizer is supported on:
- Linux
- macOS
- iOS Simulator
- Android
- NetBSD
- FreeBSD
- Windows 8.1+
Current Status
AddressSanitizer is fully functional on supported platforms starting from LLVM
3.1. The test suite is integrated into CMake build and can be run with make check-asan command.
The Windows port is functional and is used by Chrome and Firefox, but it is not
as well supported as the other ports.
More Information
https://github.com/google/sanitizers/wiki/AddressSanitizer
Clang Docs AllocToken (`clang/docs/AllocToken.md`)
Allocation Tokens
Introduction
Clang provides support for allocation tokens to enable allocator-level heap
organization strategies. Clang assigns mode-dependent token IDs to allocation
calls; the runtime behavior depends entirely on the implementation of a
compatible memory allocator.
Possible allocator strategies include:
- Security Hardening: Placing allocations into separate, isolated heap
partitions. For example, separating pointer-containing types from raw data
can mitigate exploits that rely on overflowing a primitive buffer to corrupt
object metadata. - Memory Layout Optimization: Grouping related allocations to improve data
locality and cache utilization. - Custom Allocation Policies: Applying different management strategies to
different partitions.
Token Assignment Mode
The default mode to calculate tokens is:
typehashpointersplit: This mode assigns a token ID based on the hash of
the allocated type's name, where the top half ID-space is reserved for types
that contain pointers and the bottom half for types that do not contain
pointers.
Other token ID assignment modes are supported, but they may be subject to
change or removal. These may (experimentally) be selected with -Xclang -falloc-token-mode=<mode>:
typehash: This mode assigns a token ID based on the hash of the allocated
type's name.typefunchashpointersplit: This mode assigns a token ID based on the hash of
the allocated type's name and the hash of the name of the function containing
the allocation. The upper half of the token ID bits is the type name hash,
the lower half of the token ID bits is the function name hash, and the most
significant bit is a flag indicating whether the type contains pointers.typefunchash: Liketypefunchashpointersplit, but without the pointer flag.random: This mode assigns a statically-determined random token ID to each
allocation site.increment: This mode assigns a simple, incrementally increasing token ID
to each allocation site.
The following command-line options affect generated token IDs:
-falloc-token-max=<N>
: Configures the maximum number of token IDs. By default the number of tokens
is bounded bySIZE_MAX.- In the
typefunchashpointersplitandtypefunchashmodes, the number of
token IDs isNrounded down to a power of two. As an exception, ifN
is of the form2^k-1(e.g. the defaultSIZE_MAX), allkbits are
used and token IDs are in[0, N], i.e. a token ID may be equal toN.
- In the
Querying Token IDs with `__builtin_infer_alloc_token`
For use cases where the token ID must be known at compile time, Clang provides
a builtin function:
size_t __builtin_infer_alloc_token(<args>, ...);This builtin returns the token ID inferred from its argument expressions, which
mirror arguments normally passed to any allocation function. The argument
expressions are unevaluated, so it can be used with expressions that would
have side effects without any runtime impact. In the typefunchashpointersplit,typefunchash, random, and increment modes, the builtin cannot be used in
constant expressions.
For example, it can be used as follows:
struct MyType { ... };
void *__partition_alloc(size_t size, size_t partition);
#define partition_alloc(...) __partition_alloc(__VA_ARGS__, __builtin_infer_alloc_token(__VA_ARGS__))
void foo(void) {
MyType *x = partition_alloc(sizeof(*x));
}Allocation Token Instrumentation
To enable instrumentation of allocation functions, code can be compiled with
the -fsanitize=alloc-token flag:
% clang++ -fsanitize=alloc-token example.ccThe instrumentation transforms allocation calls to include a token ID. For
example:
// Original:
ptr = malloc(size);
// Instrumented:
ptr = __alloc_token_malloc(size, <token id>);Runtime Interface
A compatible runtime must be provided that implements the token-enabled
allocation functions. The instrumentation generates calls to functions that
take a final size_t token_id argument.
// C standard library functions
void *__alloc_token_malloc(size_t size, size_t token_id);
void *__alloc_token_calloc(size_t count, size_t size, size_t token_id);
void *__alloc_token_realloc(void *ptr, size_t size, size_t token_id);
// ...
// C++ operators (mangled names)
// operator new(size_t, size_t)
void *__alloc_token__Znwm(size_t size, size_t token_id);
// operator new[](size_t, size_t)
void *__alloc_token__Znam(size_t size, size_t token_id);
// ... other variants like nothrow, etc., are also instrumented.Fast ABI
An alternative ABI can be enabled with -fsanitize-alloc-token-fast-abi,
which encodes the token ID in the allocation function name.
void *__alloc_token_0_malloc(size_t size);
void *__alloc_token_1_malloc(size_t size);
void *__alloc_token_2_malloc(size_t size);
...
void *__alloc_token_0_Znwm(size_t size);
void *__alloc_token_1_Znwm(size_t size);
void *__alloc_token_2_Znwm(size_t size);
...This ABI provides a more efficient alternative where-falloc-token-max is small.
Instrumenting Non-Standard Allocation Functions
By default, AllocToken only instruments standard library allocation functions.
This simplifies adoption, as a compatible allocator only needs to provide
token-enabled variants for a well-defined set of standard functions.
To extend instrumentation to custom allocation functions, enable broader
coverage with -fsanitize-alloc-token-extended. Such functions require being
marked with the malloc or
alloc_size attributes
(or a combination).
For example:
void *custom_malloc(size_t size) __attribute__((malloc));
void *my_malloc(size_t size) __attribute__((alloc_size(1)));
// Original:
ptr1 = custom_malloc(size);
ptr2 = my_malloc(size);
// Instrumented:
ptr1 = __alloc_token_custom_malloc(size, token_id);
ptr2 = __alloc_token_my_malloc(size, token_id);Note: Even in the default mode (without -fsanitize-alloc-token-extended),
an inline allocation wrapper marked with the malloc or
alloc_size attribute is
supported if it is inlined into its caller: the inferred token is propagated to
the allocation call the wrapper returns, which is then instrumented normally.
Wrappers that are not inlined still require-fsanitize-alloc-token-extended.
Disabling Instrumentation
To exclude specific functions from instrumentation, you can use theno_sanitize("alloc-token") attribute:
__attribute__((no_sanitize("alloc-token")))
void* custom_allocator(size_t size) {
return malloc(size); // Uses original malloc
}Note: Independent of any given allocator support, the instrumentation aims to
remain performance neutral. As such, no_sanitize("alloc-token")
functions may be inlined into instrumented functions and vice-versa. If
correctness is affected, such functions should explicitly be markednoinline.
The __attribute__((disable_sanitizer_instrumentation)) is also supported to
disable this and other sanitizer instrumentations.
Suppressions File (Ignorelist)
AllocToken respects the src and fun entity types in the
{doc}SanitizerSpecialCaseList, which can be used to omit specified source
files or functions from instrumentation.
[alloc-token]
# Exclude specific source files
src:third_party/allocator.c
# Exclude function name patterns
fun:*custom_malloc*
fun:LowLevel::*% clang++ -fsanitize=alloc-token -fsanitize-ignorelist=my_ignorelist.txt example.ccConditional Compilation with `__SANITIZE_ALLOC_TOKEN__`
In some cases, one may need to execute different code depending on whether
AllocToken instrumentation is enabled. The __SANITIZE_ALLOC_TOKEN__ macro
can be used for this purpose.
#ifdef __SANITIZE_ALLOC_TOKEN__
// Code specific to -fsanitize=alloc-token builds
#endifClang Docs AutomaticReferenceCounting (`clang/docs/AutomaticReferenceCounting.md`)
% FIXME: move to the stylesheet or Sphinx plugin
<style>
.arc-term { font-style: italic; font-weight: bold; }
.revision { font-style: italic; }
.when-revised { font-weight: bold; font-style: normal; }
/*
* Automatic numbering is described in this article:
* https://dev.opera.com/articles/view/automatic-numbering-with-css-counters/
*/
/*
* Automatic numbering for the TOC.
* This is wrong from the semantics point of view, since it is an ordered
* list, but uses "ul" tag.
*/
div#contents.contents.local ul {
counter-reset: toc-section;
list-style-type: none;
}
div#contents.contents.local ul li {
counter-increment: toc-section;
background: none; // Remove bullets
}
div#contents.contents.local ul li a.reference:before {
content: counters(toc-section, ".") " ";
}
/* Automatic numbering for the body. */
body {
counter-reset: section subsection subsubsection;
}
.section h2 {
counter-reset: subsection subsubsection;
counter-increment: section;
}
.section h2 a.toc-backref:before {
content: counter(section) " ";
}
.section h3 {
counter-reset: subsubsection;
counter-increment: subsection;
}
.section h3 a.toc-backref:before {
content: counter(section) "." counter(subsection) " ";
}
.section h4 {
counter-increment: subsubsection;
}
.section h4 a.toc-backref:before {
content: counter(section) "." counter(subsection) "." counter(subsubsection) " ";
}
</style>Objective-C Automatic Reference Counting (ARC)
(arc.meta)=
About this document
(arc.meta.purpose)=
Purpose
The first and primary purpose of this document is to serve as a complete
technical specification of Automatic Reference Counting. Given a core
Objective-C compiler and runtime, it should be possible to write a compiler and
runtime which implements these new semantics.
The secondary purpose is to act as a rationale for why ARC was designed in this
way. This should remain tightly focused on the technical design and should not
stray into marketing speculation.
(arc.meta.background)=
Background
This document assumes a basic familiarity with C.
{arc-term}Blocks are a C language extension for creating anonymous functions.
Users interact with and transfer block objects using {arc-term}block pointers, which are represented like a normal pointer. A block may capture
values from local variables; when this occurs, memory must be dynamically
allocated. The initial allocation is done on the stack, but the runtime
provides a Block_copy function which, given a block pointer, either copies
the underlying block object to the heap, setting its reference count to 1 and
returning the new block pointer, or (if the block object is already on the
heap) increases its reference count by 1. The paired function isBlock_release, which decreases the reference count by 1 and destroys the
object if the count reaches zero and is on the heap.
Objective-C is a set of language extensions, significant enough to be
considered a different language. It is a strict superset of C. The extensions
can also be imposed on C++, producing a language called Objective-C++. The
primary feature is a single-inheritance object system; we briefly describe the
modern dialect.
Objective-C defines a new type kind, collectively called the {arc-term}object pointer types. This kind has two notable builtin members, id andClass; id is the final supertype of all object pointers. The validity
of conversions between object pointer types is not checked at runtime. Users
may define {arc-term}classes; each class is a type, and the pointer to that
type is an object pointer type. A class may have a superclass; its pointer
type is a subtype of its superclass's pointer type. A class has a set of
{arc-term}ivars, fields which appear on all instances of that class. For
every class T there's an associated metaclass; it has no fields, its
superclass is the metaclass of T's superclass, and its metaclass is a global
class. Every class has a global object whose class is the class's metaclass;
metaclasses have no associated type, so pointers to this object have typeClass.
A class declaration (@interface) declares a set of {arc-term}methods. A
method has a return type, a list of argument types, and a {arc-term}selector:
a name like foo:bar:baz:, where the number of colons corresponds to the
number of formal arguments. A method may be an instance method, in which case
it can be invoked on objects of the class, or a class method, in which case it
can be invoked on objects of the metaclass. A method may be invoked by
providing an object (called the {arc-term}receiver) and a list of formal
arguments interspersed with the selector, like so:
[receiver foo: fooArg bar: barArg baz: bazArg]This looks in the dynamic class of the receiver for a method with this name,
then in that class's superclass, etc., until it finds something it can execute.
The receiver "expression" may also be the name of a class, in which case the
actual receiver is the class object for that class, or (within method
definitions) it may be super, in which case the lookup algorithm starts
with the static superclass instead of the dynamic class. The actual methods
dynamically found in a class are not those declared in the @interface, but
those defined in a separate @implementation declaration; however, when
compiling a call, typechecking is done based on the methods declared in the@interface.
Method declarations may also be grouped into {arc-term}protocols, which are not
inherently associated with any class, but which classes may claim to follow.
Object pointer types may be qualified with additional protocols that the object
is known to support.
{arc-term}Class extensions are collections of ivars and methods, designed to
allow a class's @interface to be split across multiple files; however,
there is still a primary implementation file which must see the@interfaces of all class extensions. {arc-term}Categories allow
methods (but not ivars) to be declared post hoc on an arbitrary class; the
methods in the category's @implementation will be dynamically added to that
class's method tables which the category is loaded at runtime, replacing those
methods in case of a collision.
In the standard environment, objects are allocated on the heap, and their
lifetime is manually managed using a reference count. This is done using two
instance methods which all classes are expected to implement: retain
increases the object's reference count by 1, whereas release decreases it
by 1 and calls the instance method dealloc if the count reaches 0. To
simplify certain operations, there is also an {arc-term}autorelease pool, a
thread-local list of objects to call release on later; an object can be
added to this pool by calling autorelease on it.
Block pointers may be converted to type id; block objects are laid out in a
way that makes them compatible with Objective-C objects. There is a builtin
class that all block objects are considered to be objects of; this class
implements retain by adjusting the reference count, not by callingBlock_copy.
(arc.meta.evolution)=
Evolution
ARC is under continual evolution, and this document must be updated as the
language progresses.
If a change increases the expressiveness of the language, for example by
lifting a restriction or by adding new syntax, the change will be annotated
with a revision marker, like so:
ARC applies to Objective-C pointer types, block pointer types, and
{when-revised}[beginning Apple 8.0, LLVM 3.8]{revision}BPTRs declared withinextern "BCPL"blocks.
For now, it is sensible to version this document by the releases of its sole
implementation (and its host project), clang. "LLVM X.Y" refers to an
open-source release of clang from the LLVM project. "Apple X.Y" refers to an
Apple-provided release of the Apple LLVM Compiler. Other organizations that
prepare their own, separately-versioned clang releases and wish to maintain
similar information in this document should send requests to cfe-dev.
If a change decreases the expressiveness of the language, for example by
imposing a new restriction, this should be taken as an oversight in the
original specification and something to be avoided in all versions. Such
changes are generally to be avoided.
(arc.general)=
General
Automatic Reference Counting implements automatic memory management for
Objective-C objects and blocks, freeing the programmer from the need to
explicitly insert retains and releases. It does not provide a cycle collector;
users must explicitly manage the lifetime of their objects, breaking cycles
manually or with weak or unsafe references.
ARC may be explicitly enabled with the compiler flag -fobjc-arc. It may
also be explicitly disabled with the compiler flag -fno-objc-arc. The last
of these two flags appearing on the compile line "wins".
If ARC is enabled, __has_feature(objc_arc) will expand to 1 in the
preprocessor. For more information about __has_feature, see the
{ref}language extensions <langext-__has_feature-__has_extension> document.
(arc.objects)=
Retainable object pointers
This section describes retainable object pointers, their basic operations, and
the restrictions imposed on their use under ARC. Note in particular that it
covers the rules for pointer values (patterns of bits indicating the location
of a pointed-to object), not pointer objects (locations in memory which store
pointer values). The rules for objects are covered in the next section.
A {arc-term}retainable object pointer (or "retainable pointer") is a value of
a {arc-term}retainable object pointer type ("retainable type"). There are
three kinds of retainable object pointer types:
- block pointers (formed by applying the caret (
^) declarator sigil to a
function type) - Objective-C object pointers (
id,Class,NSFoo*, etc.) - typedefs marked with
__attribute__((NSObject))
Other pointer types, such as int* and CFStringRef, are not subject to
ARC's semantics and restrictions.
:::{admonition} Rationale
We are not at liberty to require all code to be recompiled with ARC;
therefore, ARC must interoperate with Objective-C code which manages retains
and releases manually. In general, there are three requirements in order for
a compiler-supported reference-count system to provide reliable
interoperation:
- The type system must reliably identify which objects are to be managed. An
int*might be a pointer to amalloc'ed array, or it might be an
interior pointer to such an array, or it might point to some field or local
variable. In contrast, values of the retainable object pointer types are
never interior. - The type system must reliably indicate how to manage objects of a type.
This usually means that the type must imply a procedure for incrementing
and decrementing retain counts. Supporting single-ownership objects
requires a lot more explicit mediation in the language. - There must be reliable conventions for whether and when "ownership" is
passed between caller and callee, for both arguments and return values.
Objective-C methods follow such a convention very reliably, at least for
system libraries on macOS, and functions always pass objects at +0. The
C-based APIs for Core Foundation objects, on the other hand, have much more
varied transfer semantics.
:::
The use of __attribute__((NSObject)) typedefs is not recommended. If it's
absolutely necessary to use this attribute, be very explicit about using the
typedef, and do not assume that it will be preserved by language features like__typeof and C++ template argument substitution.
:::{admonition} Rationale
Any compiler operation which incidentally strips type "sugar" from a type
will yield a type without the attribute, which may result in unexpected
behavior.
:::
(arc.objects.retains)=
Retain count semantics
A retainable object pointer is either a {arc-term}null pointer or a pointer
to a valid object. Furthermore, if it has block pointer type and is notnull then it must actually be a pointer to a block object, and if it hasClass type (possibly protocol-qualified) then it must actually be a pointer
to a class object. Otherwise ARC does not enforce the Objective-C type system
as long as the implementing methods follow the signature of the static type.
It is undefined behavior if ARC is exposed to an invalid pointer.
For ARC's purposes, a valid object is one with "well-behaved" retaining
operations. Specifically, the object must be laid out such that the
Objective-C message send machinery can successfully send it the following
messages:
retain, taking no arguments and returning a pointer to the object.release, taking no arguments and returningvoid.autorelease, taking no arguments and returning a pointer to the object.
The behavior of these methods is constrained in the following ways. The term
{arc-term}high-level semantics is an intentionally vague term; the intent is
that programmers must implement these methods in a way such that the compiler,
modifying code in ways it deems safe according to these constraints, will not
violate their requirements. For example, if the user puts logging statements
in retain, they should not be surprised if those statements are executed
more or less often depending on optimization settings. These constraints are
not exhaustive of the optimization opportunities: values held in local
variables are subject to additional restrictions, described later in this
document.
It is undefined behavior if a computation history featuring a send ofretain followed by a send of release to the same object, with no
intervening release on that object, is not equivalent under the high-level
semantics to a computation history in which these sends are removed. Note that
this implies that these methods may not raise exceptions.
It is undefined behavior if a computation history features any use whatsoever
of an object following the completion of a send of release that is not
preceded by a send of retain to the same object.
The behavior of autorelease must be equivalent to sending release when
one of the autorelease pools currently in scope is popped. It may not throw an
exception.
When the semantics call for performing one of these operations on a retainable
object pointer, if that pointer is null then the effect is a no-op.
All of the semantics described in this document are subject to additional
{ref}optimization rules <arc.optimization> which permit the removal or
optimization of operations based on local knowledge of data flow. The
semantics describe the high-level behaviors that the compiler implements, not
an exact sequence of operations that a program will be compiled into.
(arc.objects.operands)=
Retainable object pointers as operands and arguments
In general, ARC does not perform retain or release operations when simply using
a retainable object pointer as an operand within an expression. This includes:
- loading a retainable pointer from an object with non-weak {ref}
ownership <arc.ownership>, - passing a retainable pointer as an argument to a function or method, and
- receiving a retainable pointer as the result of a function or method call.
:::{admonition} Rationale
While this might seem uncontroversial, it is actually unsafe when multiple
expressions are evaluated in "parallel", as with binary operators and calls,
because (for example) one expression might load from an object while another
writes to it. However, C and C++ already call this undefined behavior
because the evaluations are unsequenced, and ARC simply exploits that here to
avoid needing to retain arguments across a large number of calls.
:::
The remainder of this section describes exceptions to these rules, how those
exceptions are detected, and what those exceptions imply semantically.
(arc.objects.operands.consumed)=
Consumed parameters
A function or method parameter of retainable object pointer type may be marked
as {arc-term}consumed, signifying that the callee expects to take ownership
of a +1 retain count. This is done by adding the ns_consumed attribute to
the parameter declaration, like so:
void foo(__attribute((ns_consumed)) id x);
- (void) foo: (id) __attribute((ns_consumed)) x;This attribute is part of the type of the function or method, not the type of
the parameter. It controls only how the argument is passed and received.
When passing such an argument, ARC retains the argument prior to making the
call.
When receiving such an argument, ARC releases the argument at the end of the
function, subject to the usual optimizations for local values.
:::{admonition} Rationale
This formalizes direct transfers of ownership from a caller to a callee. The
most common scenario here is passing the self parameter to init, but
it is useful to generalize. Typically, local optimization will remove any
extra retains and releases: on the caller side the retain will be merged with
a +1 source, and on the callee side the release will be rolled into the
initialization of the parameter.
:::
The implicit self parameter of a method may be marked as consumed by adding__attribute__((ns_consumes_self)) to the method declaration. Methods in
the init {ref}family <arc.method-families> are treated as if they were
implicitly marked with this attribute.
It is undefined behavior if an Objective-C message send to a method withns_consumed parameters (other than self) is made with a null receiver. It
is undefined behavior if the method to which an Objective-C message send
statically resolves to has a different set of ns_consumed parameters than
the method it dynamically resolves to. It is undefined behavior if a block or
function call is made through a static type with a different set ofns_consumed parameters than the implementation of the called block or
function.
:::{admonition} Rationale
Consumed parameters with null receiver are a guaranteed leak. Mismatches
with consumed parameters will cause over-retains or over-releases, depending
on the direction. The rule about function calls is really just an
application of the existing C/C++ rule about calling functions through an
incompatible function type, but it's useful to state it explicitly.
:::
(arc.object.operands.retained-return-values)=
Retained return values
A function or method which returns a retainable object pointer type may be
marked as returning a retained value, signifying that the caller expects to take
ownership of a +1 retain count. This is done by adding thens_returns_retained attribute to the function or method declaration, like
so:
id foo(void) __attribute((ns_returns_retained));
- (id) foo __attribute((ns_returns_retained));This attribute is part of the type of the function or method.
When returning from such a function or method, ARC retains the value at the
point of evaluation of the return statement, before leaving all local scopes.
When receiving a return result from such a function or method, ARC releases the
value at the end of the full-expression it is contained within, subject to the
usual optimizations for local values.
:::{admonition} Rationale
This formalizes direct transfers of ownership from a callee to a caller. The
most common scenario this models is the retained return from init,alloc, new, and copy methods, but there are other cases in the
frameworks. After optimization there are typically no extra retains and
releases required.
:::
Methods in the alloc, copy, init, mutableCopy, and new
{ref}families <arc.method-families> are implicitly marked__attribute__((ns_returns_retained)). This may be suppressed by explicitly
marking the method __attribute__((ns_returns_not_retained)).
It is undefined behavior if the method to which an Objective-C message send
statically resolves has different retain semantics on its result from the
method it dynamically resolves to. It is undefined behavior if a block or
function call is made through a static type with different retain semantics on
its result from the implementation of the called block or function.
:::{admonition} Rationale
Mismatches with returned results will cause over-retains or over-releases,
depending on the direction. Again, the rule about function calls is really
just an application of the existing C/C++ rule about calling functions
through an incompatible function type.
:::
(arc.objects.operands.unretained-returns)=
Unretained return values
A method or function which returns a retainable object type but does not return
a retained value must ensure that the object is still valid across the return
boundary.
When returning from such a function or method, ARC retains the value at the
point of evaluation of the return statement, then leaves all local scopes, and
then balances out the retain while ensuring that the value lives across the
call boundary. In the worst case, this may involve an autorelease, but
callers must not assume that the value is actually in the autorelease pool.
ARC performs no extra mandatory work on the caller side, although it may elect
to do something to shorten the lifetime of the returned value.
:::{admonition} Rationale
It is common in non-ARC code to not return an autoreleased value; therefore
the convention does not force either path. It is convenient to not be
required to do unnecessary retains and autoreleases; this permits
optimizations such as eliding retain/autoreleases when it can be shown that
the original pointer will still be valid at the point of return.
:::
A method or function may be marked with__attribute__((ns_returns_autoreleased)) to indicate that it returns a
pointer which is guaranteed to be valid at least as long as the innermost
autorelease pool. There are no additional semantics enforced in the definition
of such a method; it merely enables optimizations in callers.
(arc.objects.operands.casts)=
Bridged casts
A {arc-term}bridged cast is a C-style cast annotated with one of three
keywords:
(__bridge T) opcasts the operand to the destination typeT. IfTis a retainable object pointer type, thenopmust have a
non-retainable pointer type. IfTis a non-retainable pointer type,
thenopmust have a retainable object pointer type. Otherwise the cast
is ill-formed. There is no transfer of ownership, and ARC inserts no retain
operations.(__bridge_retained T) opcasts the operand, which must have retainable
object pointer type, to the destination type, which must be a non-retainable
pointer type. ARC retains the value, subject to the usual optimizations on
local values, and the recipient is responsible for balancing that +1.(__bridge_transfer T) opcasts the operand, which must have
non-retainable pointer type, to the destination type, which must be a
retainable object pointer type. ARC will release the value at the end of
the enclosing full-expression, subject to the usual optimizations on local
values.
These casts are required in order to transfer objects in and out of ARC
control; see the rationale in the section on {ref}conversion of retainable object pointers <arc.objects.restrictions.conversion>.
Using a __bridge_retained or __bridge_transfer cast purely to convince
ARC to emit an unbalanced retain or release, respectively, is poor form.
(arc.objects.restrictions)=
Restrictions
(arc.objects.restrictions.conversion)=
Conversion of retainable object pointers
In general, a program which attempts to implicitly or explicitly convert a
value of retainable object pointer type to any non-retainable type, or
vice-versa, is ill-formed. For example, an Objective-C object pointer shall
not be converted to void*. As an exception, cast to intptr_t is
allowed because such casts are not transferring ownership. The {ref}bridged casts <arc.objects.operands.casts> may be used to perform these conversions
where necessary.
:::{admonition} Rationale
We cannot ensure the correct management of the lifetime of objects if they
may be freely passed around as unmanaged types. The bridged casts are
provided so that the programmer may explicitly describe whether the cast
transfers control into or out of ARC.
:::
However, the following exceptions apply.
(arc.objects.restrictions.conversion.with.known.semantics)=
Conversion to retainable object pointer type of expressions with known semantics
{when-revised}[beginning Apple 4.0, LLVM 3.1]
{revision}These exceptions have been greatly expanded; they previously applied only to a much-reduced subset which is difficult to categorize but which included null pointers, message sends (under the given rules), and the various global constants.
An unbridged conversion to a retainable object pointer type from a type other
than a retainable object pointer type is ill-formed, as discussed above, unless
the operand of the cast has a syntactic form which is known retained, known
unretained, or known retain-agnostic.
An expression is {arc-term}known retain-agnostic if it is:
- an Objective-C string literal,
- a load from a
constsystem global variable of {ref}C retainable pointer type <arc.misc.c-retainable>, or - a null pointer constant.
An expression is {arc-term}known unretained if it is an rvalue of {ref}C retainable pointer type <arc.misc.c-retainable> and it is:
- a direct call to a function, and either that function has the
cf_returns_not_retainedattribute or it is an {ref}audited <arc.misc.c-retainable.audit>function that does not have thecf_returns_retainedattribute and does not follow the create/copy naming
convention, - a message send, and the declared method either has the
cf_returns_not_retainedattribute or it has neither thecf_returns_retainedattribute nor a {ref}selector family <arc.method-families>that implies a retained result, or - {when-revised}
[beginning LLVM 3.6]{revision}a load from aconst
{revision}non-system global variable.
An expression is {arc-term}known retained if it is an rvalue of {ref}C retainable pointer type <arc.misc.c-retainable> and it is:
- a message send, and the declared method either has the
cf_returns_retainedattribute, or it does not have thecf_returns_not_retainedattribute but it does have a {ref}selector family <arc.method-families>that implies a retained result.
Furthermore:
- a comma expression is classified according to its right-hand side,
- a statement expression is classified according to its result expression, if
it has one, - an lvalue-to-rvalue conversion applied to an Objective-C property lvalue is
classified according to the underlying message send, and - a conditional operator is classified according to its second and third
operands, if they agree in classification, or else the other if one is known
retain-agnostic.
If the cast operand is known retained, the conversion is treated as a__bridge_transfer cast. If the cast operand is known unretained or known
retain-agnostic, the conversion is treated as a __bridge cast.
:::{admonition} Rationale
Bridging casts are annoying. Absent the ability to completely automate the
management of CF objects, however, we are left with relatively poor attempts
to reduce the need for a glut of explicit bridges. Hence these rules.
We've so far consciously refrained from implicitly turning retained CF
results from function calls into __bridge_transfer casts. The worry is
that some code patterns --- for example, creating a CF value, assigning it
to an ObjC-typed local, and then calling CFRelease when done --- are a
bit too likely to be accidentally accepted, leading to mysterious behavior.
For loads from const global variables of {ref}C retainable pointer type <arc.misc.c-retainable>, it is reasonable to assume that global system
constants were initialized with true constants (e.g. string literals), but
user constants might have been initialized with something dynamically
allocated, using a global initializer.
:::
(arc.objects.restrictions.conversion-exception-contextual)=
Conversion from retainable object pointer type in certain contexts
{when-revised}[beginning Apple 4.0, LLVM 3.1]
If an expression of retainable object pointer type is explicitly cast to a
{ref}C retainable pointer type <arc.misc.c-retainable>, the program is
ill-formed as discussed above unless the result is immediately used:
- to initialize a parameter in an Objective-C message send where the parameter
is not marked with thecf_consumedattribute, or - to initialize a parameter in a direct call to an
{ref}audited <arc.misc.c-retainable.audit>function where the parameter is
not marked with thecf_consumedattribute.
:::{admonition} Rationale
Consumed parameters are left out because ARC would naturally balance them
with a retain, which was judged too treacherous. This is in part because
several of the most common consuming functions are in the Release family,
and it would be quite unfortunate for explicit releases to be silently
balanced out in this way.
:::
(arc.ownership)=
Ownership qualification
This section describes the behavior of objects of retainable object pointer
type; that is, locations in memory which store retainable object pointers.
A type is a {arc-term}retainable object owner type if it is a retainable
object pointer type or an array type whose element type is a retainable object
owner type.
An {arc-term}ownership qualifier is a type qualifier which applies only to
retainable object owner types. An array type is ownership-qualified according
to its element type, and adding an ownership qualifier to an array type so
qualifies its element type.
A program is ill-formed if it attempts to apply an ownership qualifier to a
type which is already ownership-qualified, even if it is the same qualifier.
There is a single exception to this rule: an ownership qualifier may be applied
to a substituted template type parameter, which overrides the ownership
qualifier provided by the template argument.
When forming a function type, the result type is adjusted so that any
top-level ownership qualifier is deleted.
Except as described under the {ref}inference rules <arc.ownership.inference>,
a program is ill-formed if it attempts to form a pointer or reference type to a
retainable object owner type which lacks an ownership qualifier.
:::{admonition} Rationale
These rules, together with the inference rules, ensure that all objects and
lvalues of retainable object pointer type have an ownership qualifier. The
ability to override an ownership qualifier during template substitution is
required to counteract the {ref}inference of __strong for template type arguments <arc.ownership.inference.template.arguments>. Ownership qualifiers
on return types are dropped because they serve no purpose there except to
cause spurious problems with overloading and templates.
:::
There are four ownership qualifiers:
__autoreleasing__strong__unsafe_unretained__weak
A type is {arc-term}nontrivially ownership-qualified if it is qualified with__autoreleasing, __strong, or __weak.
(arc.ownership.spelling)=
Spelling
The names of the ownership qualifiers are reserved for the implementation. A
program may not assume that they are or are not implemented with macros, or
what those macros expand to.
An ownership qualifier may be written anywhere that any other type qualifier
may be written.
If an ownership qualifier appears in the declaration-specifiers, the
following rules apply:
- if the type specifier is a retainable object owner type, the qualifier
initially applies to that type; - otherwise, if the outermost non-array declarator is a pointer
or block pointer declarator, the qualifier initially applies to
that type; - otherwise the program is ill-formed.
- If the qualifier is so applied at a position in the declaration
where the next-innermost declarator is a function declarator, and
there is a block declarator within that function declarator, then
the qualifier applies instead to that block declarator and this rule
is considered afresh beginning from the new position.
If an ownership qualifier appears on the declarator name, or on the declared
object, it is applied to the innermost pointer or block-pointer type.
If an ownership qualifier appears anywhere else in a declarator, it applies to
the type there.
:::{admonition} Rationale
Ownership qualifiers are like const and volatile in the sense
that they may sensibly apply at multiple distinct positions within a
declarator. However, unlike those qualifiers, there are many
situations where they are not meaningful, and so we make an effort
to "move" the qualifier to a place where it will be meaningful. The
general goal is to allow the programmer to write, say, __strong
before the entire declaration and have it apply in the leftmost
sensible place.
:::
(arc.ownership.spelling.property)=
Property declarations
A property of retainable object pointer type may have ownership. If the
property's type is ownership-qualified, then the property has that ownership.
If the property has one of the following modifiers, then the property has the
corresponding ownership. A property is ill-formed if it has conflicting
sources of ownership, or if it has redundant ownership modifiers, or if it has__autoreleasing ownership.
assignimplies__unsafe_unretainedownership.copyimplies__strongownership, as well as the usual behavior of
copy semantics on the setter.retainimplies__strongownership.strongimplies__strongownership.unsafe_unretainedimplies__unsafe_unretainedownership.weakimplies__weakownership.
With the exception of weak, these modifiers are available in non-ARC
modes.
A property's specified ownership is preserved in its metadata, but otherwise
the meaning is purely conventional unless the property is synthesized. If a
property is synthesized, then the {arc-term}associated instance variable is
the instance variable which is named, possibly implicitly, by the@synthesize declaration. If the associated instance variable already
exists, then its ownership qualification must equal the ownership of the
property; otherwise, the instance variable is created with that ownership
qualification.
A property of retainable object pointer type which is synthesized without a
source of ownership has the ownership of its associated instance variable, if it
already exists; otherwise, {when-revised}[beginning Apple 3.1, LLVM 3.1]
{revision}its ownership is implicitly strong. Prior to this revision, it
was ill-formed to synthesize such a property.
:::{admonition} Rationale
Using strong by default is safe and consistent with the generic ARC rule
about {ref}inferring ownership <arc.ownership.inference.variables>. It is,
unfortunately, inconsistent with the non-ARC rule which states that such
properties are implicitly assign. However, that rule is clearly
untenable in ARC, since it leads to default-unsafe code. The main merit to
banning the properties is to avoid confusion with non-ARC practice, which did
not ultimately strike us as sufficient to justify requiring extra syntax and
(more importantly) forcing novices to understand ownership rules just to
declare a property when the default is so reasonable. Changing the rule away
from non-ARC practice was acceptable because we had conservatively banned the
synthesis in order to give ourselves exactly this leeway.
:::
Applying __attribute__((NSObject)) to a property not of retainable object
pointer type has the same behavior it does outside of ARC: it requires the
property type to be some sort of pointer and permits the use of modifiers other
than assign. These modifiers only affect the synthesized getter and
setter; direct accesses to the ivar (even if synthesized) still have primitive
semantics, and the value in the ivar will not be automatically released during
deallocation.
(arc.ownership.semantics)=
Semantics
There are five {arc-term}managed operations which may be performed on an
object of retainable object pointer type. Each qualifier specifies different
semantics for each of these operations. It is still undefined behavior to
access an object outside of its lifetime.
A load or store with "primitive semantics" has the same semantics as the
respective operation would have on an void* lvalue with the same alignment
and non-ownership qualification.
{arc-term}Reading occurs when performing a lvalue-to-rvalue conversion on an
object lvalue.
For
__weakobjects, the current pointee is retained and then released at
the end of the current full-expression. In particular, messaging a__weak
object keeps the object retained until the end of the full expression.objc__weak MyObject *weakObj; void foo() { // weakObj is retained before the message send and released at the end of // the full expression. [weakObj m]; }This must execute atomically with respect to assignments and to the final
release of the pointee.For all other objects, the lvalue is loaded with primitive semantics.
{arc-term}Assignment occurs when evaluating an assignment operator. The
semantics vary based on the qualification:
- For
__strongobjects, the new pointee is first retained; second, the
lvalue is loaded with primitive semantics; third, the new pointee is stored
into the lvalue with primitive semantics; and finally, the old pointee is
released. This is not performed atomically; external synchronization must be
used to make this safe in the face of concurrent loads and stores. - For
__weakobjects, the lvalue is updated to point to the new pointee,
unless the new pointee is an object currently undergoing deallocation, in
which case the lvalue is updated to a null pointer. This must execute
atomically with respect to other assignments to the object, to reads from the
object, and to the final release of the new pointee. - For
__unsafe_unretainedobjects, the new pointee is stored into the
lvalue using primitive semantics. - For
__autoreleasingobjects, the new pointee is retained, autoreleased,
and stored into the lvalue using primitive semantics.
{arc-term}Initialization occurs when an object's lifetime begins, which
depends on its storage duration. Initialization proceeds in two stages:
- First, a null pointer is stored into the lvalue using primitive semantics.
This step is skipped if the object is__unsafe_unretained. - Second, if the object has an initializer, that expression is evaluated and
then assigned into the object using the usual assignment semantics.
{arc-term}Destruction occurs when an object's lifetime ends. In all cases it
is semantically equivalent to assigning a null pointer to the object, with the
proviso that of course the object cannot be legally read after the object's
lifetime ends.
{arc-term}Moving occurs in specific situations where an lvalue is "moved
from", meaning that its current pointee will be used but the object may be left
in a different (but still valid) state. This arises with __block variables
and rvalue references in C++. For __strong lvalues, moving is equivalent
to loading the lvalue with primitive semantics, writing a null pointer to it
with primitive semantics, and then releasing the result of the load at the end
of the current full-expression. For all other lvalues, moving is equivalent to
reading the object.
(arc.ownership.restrictions)=
Restrictions
(arc.ownership.restrictions.weak)=
Weak-unavailable types
It is explicitly permitted for Objective-C classes to not support __weak
references. It is undefined behavior to perform an operation with weak
assignment semantics with a pointer to an Objective-C object whose class does
not support __weak references.
:::{admonition} Rationale
Historically, it has been possible for a class to provide its own
reference-count implementation by overriding retain, release, etc.
However, weak references to an object require coordination with its class's
reference-count implementation because, among other things, weak loads and
stores must be atomic with respect to the final release. Therefore, existing
custom reference-count implementations will generally not support weak
references without additional effort. This is unavoidable without breaking
binary compatibility.
:::
A class may indicate that it does not support weak references by providing theobjc_arc_weak_reference_unavailable attribute on the class's interface declaration. A
retainable object pointer type is weak-unavailable if it
is a pointer to an (optionally protocol-qualified) Objective-C class T whereT or one of its superclasses has the objc_arc_weak_reference_unavailable
attribute. A program is ill-formed if it applies the __weak ownership
qualifier to a weak-unavailable type or if the value operand of a weak
assignment operation has a weak-unavailable type.
(arc.ownership.restrictions.autoreleasing)=
Storage duration of `__autoreleasing` objects
A program is ill-formed if it declares an __autoreleasing object of
non-automatic storage duration. A program is ill-formed if it captures an__autoreleasing object in a block or, unless by reference, in a C++11
lambda.
:::{admonition} Rationale
Autorelease pools are tied to the current thread and scope by their nature.
While it is possible to have temporary objects whose instance variables are
filled with autoreleased objects, there is no way that ARC can provide any
sort of safety guarantee there.
:::
It is undefined behavior if a non-null pointer is assigned to an__autoreleasing object while an autorelease pool is in scope and then that
object is read after the autorelease pool's scope is left.
(arc.ownership.restrictions.conversion.indirect)=
Conversion of pointers to ownership-qualified types
A program is ill-formed if an expression of type T* is converted,
explicitly or implicitly, to the type U*, where T and U have
different ownership qualification, unless:
Tis qualified with__strong,__autoreleasing, or__unsafe_unretained, andUis qualified with bothconstand__unsafe_unretained; or- either
TorUiscv void, wherecvis an optional sequence
of non-ownership qualifiers; or - the conversion is requested with a
reinterpret_castin Objective-C++; or - the conversion is a well-formed {ref}
pass-by-writeback <arc.ownership.restrictions.pass_by_writeback>.
The analogous rule applies to T& and U& in Objective-C++.
:::{admonition} Rationale
These rules provide a reasonable level of type-safety for indirect pointers,
as long as the underlying memory is not deallocated. The conversion toconst __unsafe_unretained is permitted because the semantics of reads are
equivalent across all these ownership semantics, and that's a very useful and
common pattern. The interconversion with void* is useful for allocating
memory or otherwise escaping the type system, but use it carefully.reinterpret_cast is considered to be an obvious enough sign of taking
responsibility for any problems.
:::
It is undefined behavior to access an ownership-qualified object through an
lvalue of a differently-qualified type, except that any non-__weak object
may be read through an __unsafe_unretained lvalue.
It is undefined behavior if the storage of a __strong or __weak
object is not properly initialized before the first managed operation
is performed on the object, or if the storage of such an object is freed
or reused before the object has been properly deinitialized. Storage for
a __strong or __weak object may be properly initialized by filling
it with the representation of a null pointer, e.g. by acquiring the memory
with calloc or using bzero to zero it out. A __strong or__weak object may be properly deinitialized by assigning a null pointer
into it. A __strong object may also be properly initialized
by copying into it (e.g. with memcpy) the representation of a
different __strong object whose storage has been properly initialized;
doing this properly deinitializes the source object and causes its storage
to no longer be properly initialized. A __weak object may not be
representation-copied in this way.
These requirements are followed automatically for objects whose
initialization and deinitialization are under the control of ARC:
- objects of static, automatic, and temporary storage duration
- instance variables of Objective-C objects
- elements of arrays where the array object's initialization and
deinitialization are under the control of ARC - fields of Objective-C struct types where the struct object's
initialization and deinitialization are under the control of ARC - non-static data members of Objective-C++ non-union class types
- Objective-C++ objects and arrays of dynamic storage duration created
with thenewornew[]operators and destroyed with the
correspondingdeleteordelete[]operator
They are not followed automatically for these objects:
- objects of dynamic storage duration created in other memory, such as
that returned bymalloc - union members
:::{admonition} Rationale
ARC must perform special operations when initializing an object and
when destroying it. In many common situations, ARC knows when an
object is created and when it is destroyed and can ensure that these
operations are performed correctly. Otherwise, however, ARC requires
programmer cooperation to establish its initialization invariants
because it is infeasible for ARC to dynamically infer whether they
are intact. For example, there is no syntactic difference in C between
an assignment that is intended by the programmer to initialize a variable
and one that is intended to replace the existing value stored there,
but ARC must perform one operation or the other. ARC chooses to always
assume that objects are initialized (except when it is in charge of
initializing them) because the only workable alternative would be to
ban all code patterns that could potentially be used to access
uninitialized memory, and that would be too limiting. In practice,
this is rarely a problem because programmers do not generally need to
work with objects for which the requirements are not handled
automatically.
:::
Note that dynamically-allocated Objective-C++ arrays of
nontrivially-ownership-qualified type are not ABI-compatible with non-ARC
code because the non-ARC code will consider the element type to be POD.
Such arrays that are new[]'d in ARC translation units cannot bedelete[]'d in non-ARC translation units and vice-versa.
(arc.ownership.restrictions.pass_by_writeback)=
Passing to an out parameter by writeback
If the argument passed to a parameter of type T __autoreleasing * has typeU oq *, where oq is an ownership qualifier, then the argument is a
candidate for {arc-term}`pass-by-writeback`` if:
oqis__strongor__weak, and- it would be legal to initialize a
T __strong *with aU __strong *.
For purposes of overload resolution, an implicit conversion sequence requiring
a pass-by-writeback is always worse than an implicit conversion sequence not
requiring a pass-by-writeback.
The pass-by-writeback is ill-formed if the argument expression does not have a
legal form:
&var, wherevaris a scalar variable of automatic storage duration
with retainable object pointer type- a conditional expression where the second and third operands are both legal
forms - a cast whose operand is a legal form
- a null pointer constant
:::{admonition} Rationale
The restriction in the form of the argument serves two purposes. First, it
makes it impossible to pass the address of an array to the argument, which
serves to protect against an otherwise serious risk of mis-inferring an
"array" argument as an out-parameter. Second, it makes it much less likely
that the user will see confusing aliasing problems due to the implementation,
below, where their store to the writeback temporary is not immediately seen
in the original argument variable.
:::
A pass-by-writeback is evaluated as follows:
- The argument is evaluated to yield a pointer
pof typeU oq *. - If
pis a null pointer, then a null pointer is passed as the argument,
and no further work is required for the pass-by-writeback. - Otherwise, a temporary of type
T __autoreleasingis created and
initialized to a null pointer. - If the parameter is not an Objective-C method parameter marked
out,
then*pis read, and the result is written into the temporary with
primitive semantics. - The address of the temporary is passed as the argument to the actual call.
- After the call completes, the temporary is loaded with primitive
semantics, and that value is assigned into*p.
:::{admonition} Rationale
This is all admittedly convoluted. In an ideal world, we would see that a
local variable is being passed to an out-parameter and retroactively modify
its type to be __autoreleasing rather than __strong. This would be
remarkably difficult and not always well-founded under the C type system.
However, it was judged unacceptably invasive to require programmers to write__autoreleasing on all the variables they intend to use for
out-parameters. This was the least bad solution.
:::
(arc.ownership.restrictions.records)=
Ownership-qualified fields of structs and unions
A member of a struct or union may be declared to have ownership-qualified
type. If the type is qualified with __unsafe_unretained, the semantics
of the containing aggregate are unchanged from the semantics of an unqualified type in a non-ARC mode. If the type is qualified with __autoreleasing, the program is ill-formed. Otherwise, if the type is nontrivially ownership-qualified, additional rules apply.
Both Objective-C and Objective-C++ support nontrivially ownership-qualified
fields. Due to formal differences between the standards, the formal
treatment is different; however, the basic language model is intended to
be the same for identical code.
:::{admonition} Rationale
Permitting __strong and __weak references in aggregate types
allows programmers to take advantage of the normal language tools of
C and C++ while still automatically managing memory. While it is
usually simpler and more idiomatic to use Objective-C objects for
secondary data structures, doing so can introduce extra allocation
and message-send overhead, which can cause unacceptable
performance. Using structs can resolve some of this tension.
__autoreleasing is forbidden because it is treacherous to rely
on autoreleases as an ownership tool outside of a function-local
contexts.
Earlier releases of Clang permitted __strong and __weak only
references in Objective-C++ classes, not in Objective-C. This
restriction was an undesirable short-term constraint arising from the
complexity of adding support for non-trivial struct types to C.
:::
In Objective-C++, nontrivially ownership-qualified types are treated
for nearly all purposes as if they were class types with non-trivial
default constructors, copy constructors, move constructors, copy assignment
operators, move assignment operators, and destructors. This includes the
determination of the triviality of special members of classes with a
non-static data member of such a type.
In Objective-C, the definition cannot be so succinct: because the C
standard lacks rules for non-trivial types, those rules must first be
developed. They are given in the next section. The intent is that these
rules are largely consistent with the rules of C++ for code expressible
in both languages.
Formal rules for non-trivial types in C
The following are base rules which can be added to C to support
implementation-defined non-trivial types.
A type in C is said to be non-trivial to copy, non-trivial to destroy,
or non-trivial to default-initialize if:
- it is a struct or union containing a member whose type is non-trivial
to (respectively) copy, destroy, or default-initialize; - it is a qualified type whose unqualified type is non-trivial to
(respectively) copy, destroy, or default-initialize (for at least
the standard C qualifiers); or - it is an array type whose element type is non-trivial to (respectively)
copy, destroy, or default-initialize.
A type in C is said to be illegal to copy, illegal to destroy, or
illegal to default-initialize if:
- it is a union which contains a member whose type is either illegal
or non-trivial to (respectively) copy, destroy, or initialize; - it is a qualified type whose unqualified type is illegal to
(respectively) copy, destroy, or default-initialize (for at least
the standard C qualifiers); or - it is an array type whose element type is illegal to (respectively)
copy, destroy, or default-initialize.
No type describable under the rules of the C standard shall be either
non-trivial or illegal to copy, destroy, or default-initialize.
An implementation may provide additional types which have one or more
of these properties.
An expression calls for a type to be copied if it:
- passes an argument of that type to a function call,
- defines a function which declares a parameter of that type,
- calls or defines a function which returns a value of that type,
- assigns to an l-value of that type, or
- converts an l-value of that type to an r-value.
A program calls for a type to be destroyed if it:
- passes an argument of that type to a function call,
- defines a function which declares a parameter of that type,
- calls or defines a function which returns a value of that type,
- creates an object of automatic storage duration of that type,
- assigns to an l-value of that type, or
- converts an l-value of that type to an r-value.
A program calls for a type to be default-initialized if it:
- declares a variable of that type without an initializer.
An expression is ill-formed if calls for a type to be copied,
destroyed, or default-initialized and that type is illegal to
(respectively) copy, destroy, or default-initialize.
A program is ill-formed if it contains a function type specifier
with a parameter or return type that is illegal to copy or
destroy. If a function type specifier would be ill-formed for this
reason except that the parameter or return type was incomplete at
that point in the translation unit, the program is ill-formed but
no diagnostic is required.
A goto or switch is ill-formed if it jumps into the scope of
an object of automatic storage duration whose type is non-trivial to
destroy.
C specifies that it is generally undefined behavior to access an l-value
if there is no object of that type at that location. Implementations
are often lenient about this, but non-trivial types generally require
it to be enforced more strictly. The following rules apply:
The static subobjects of a type T at a location L are:
- an object of type
Tspanning fromLtoL + sizeof(T); - if
Tis a struct type, then for each fieldfof that struct,
the static subobjects ofTat locationL + offsetof(T, .f); and - if
Tis the array typeE[N], then for eachisatisfying0 <= i < N, the static subobjects ofEat locationL + i * sizeof(E).
If an l-value is converted to an r-value, then all static subobjects
whose types are non-trivial to copy are accessed. If an l-value is
assigned to, or if an object of automatic storage duration goes out of
scope, then all static subobjects of types that are non-trivial to destroy
are accessed.
A dynamic object is created at a location if an initialization initializes
an object of that type there. A dynamic object ceases to exist at a
location if the memory is repurposed. Memory is repurposed if it is
freed or if a different dynamic object is created there, for example by
assigning into a different union member. An implementation may provide
additional rules for what constitutes creating or destroying a dynamic
object.
If an object is accessed under these rules at a location where no such
dynamic object exists, the program has undefined behavior.
If memory for a location is repurposed while a dynamic object that is
non-trivial to destroy exists at that location, the program has
undefined behavior.
:::{admonition} Rationale
While these rules are far less fine-grained than C++, they are
nonetheless sufficient to express a wide spectrum of types.
Types that express some sort of ownership will generally be non-trivial
to both copy and destroy and either non-trivial or illegal to
default-initialize. Types that don't express ownership may still
be non-trivial to copy because of some sort of address sensitivity;
for example, a relative reference. Distinguishing default
initialization allows types to impose policies about how they are
created.
These rules assume that assignment into an l-value is always a
modification of an existing object rather than an initialization.
Assignment is then a compound operation where the old value is
read and destroyed, if necessary, and the new value is put into
place. These are the natural semantics of value propagation, where
all basic operations on the type come down to copies and destroys,
and everything else is just an optimization on top of those.
The most glaring weakness of programming with non-trivial types in C
is that there are no language mechanisms (akin to C++'s placementnew and explicit destructor calls) for explicitly creating and
destroying objects. Clang should consider adding builtins for this
purpose, as well as for common optimizations like destructive
relocation.
:::
Application of the formal C rules to nontrivial ownership qualifiers
Nontrivially ownership-qualified types are considered non-trivial
to copy, destroy, and default-initialize.
A dynamic object of nontrivially ownership-qualified type contingently
exists at a location if the memory is filled with a zero pattern, e.g.
by calloc or bzero. Such an object can be safely accessed in
all of the cases above, but its memory can also be safely repurposed.
Assigning a null pointer into an l-value of __weak or__strong-qualified type accesses the dynamic object there (and thus
may have undefined behavior if no such object exists), but afterwards
the object's memory is guaranteed to be filled with a zero pattern
and thus may be either further accessed or repurposed as needed.
The upshot is that programs may safely initialize dynamically-allocated
memory for nontrivially ownership-qualified types by ensuring it is zero-initialized, and they may safely deinitialize memory before
freeing it by storing nil into any __strong or __weak
references previously created in that memory.
C/C++ compatibility for structs and unions with non-trivial members
Structs and unions with non-trivial members are compatible in
different language modes (e.g. between Objective-C and Objective-C++,
or between ARC and non-ARC modes) under the following conditions:
The types must be compatible ignoring ownership qualifiers according
to the baseline, non-ARC rules (e.g. C struct compatibility or C++'s
ODR). This condition implies a pairwise correspondence between
fields.Note that an Objective-C++ class with base classes, a user-provided
copy or move constructor, or a user-provided destructor is never
compatible with an Objective-C type.If two fields correspond as above, and at least one of the fields is
ownership-qualified, then:- the fields must be identically qualified, or else
- one type must be unqualified (and thus declared in a non-ARC mode),
and the other type must be qualified with__unsafe_unretained
or__strong.
Note that
__weakfields must always be declared__weakbecause
of the need to pin those fields in memory and keep them properly
registered with the Objective-C runtime. Non-ARC modes may still
declare fields__weakby enabling-fobjc-weak.
These compatibility rules permit a function that takes a parameter
of non-trivial struct type to be written in ARC and called from
non-ARC or vice-versa. The convention for this always transfers
ownership of objects stored in __strong fields from the caller
to the callee, just as for an ns_consumed argument. Therefore,
non-ARC callers must ensure that such fields are initialized to a +1
reference, and non-ARC callees must balance that +1 by releasing the
reference or transferring it as appropriate.
Likewise, a function returning a non-trivial struct may be written in
ARC and called from non-ARC or vice-versa. The convention for this
always transfers ownership of objects stored in __strong fields
from the callee to the caller, and so callees must initialize such
fields with +1 references, and callers must balance that +1 by releasing
or transferring them.
Similar transfers of responsibility occur for __weak fields, but
since both sides must use native __weak support to ensure
calling convention compatibility, this transfer is always handled
automatically by the compiler.
:::{admonition} Rationale
In earlier releases, when non-trivial ownership was only permitted
on fields in Objective-C++, the ABI used for such classes was the
ordinary ABI for non-trivial C++ classes, which passes arguments and
returns indirectly and does not transfer responsibility for arguments.
When support for Objective-C structs was added, it was decided to
change to the current ABI for three reasons:
- It permits ARC / non-ARC compatibility for structs containing only
__strongreferences, as long as the non-ARC side is careful about
transferring ownership. - It avoids unnecessary indirection for sufficiently small types that
the C ABI would prefer to pass in registers. - Given that struct arguments must be produced at +1 to satisfy C's
semantics of initializing the local parameter variable, transferring
ownership of that copy to the callee is generally better for ARC
optimization, since otherwise there will be releases in the caller
that are much harder to pair with transfers in the callee.
Breaking compatibility with existing Objective-C++ structures was
considered an acceptable cost, as most Objective-C++ code does not have
binary-compatibility requirements. Any existing code which cannot accept
this compatibility break, which is necessarily Objective-C++, should
force the use of the standard C++ ABI by declaring an empty (but
non-defaulted) destructor.
:::
(arc.ownership.inference)=
Ownership inference
(arc.ownership.inference.variables)=
Objects
If an object is declared with retainable object owner type, but without an
explicit ownership qualifier, its type is implicitly adjusted to have__strong qualification.
As a special case, if the object's base type is Class (possibly
protocol-qualified), the type is adjusted to have __unsafe_unretained
qualification instead.
(arc.ownership.inference.indirect_parameters)=
Indirect parameters
If a function or method parameter has type T*, where T is an
ownership-unqualified retainable object pointer type, then:
- if
Tisconst-qualified orClass, then it is implicitly
qualified with__unsafe_unretained; - otherwise, it is implicitly qualified with
__autoreleasing.
:::{admonition} Rationale__autoreleasing exists mostly for this case, the Cocoa convention for
out-parameters. Since a pointer to const is obviously not an
out-parameter, we instead use a type more useful for passing arrays. If the
user instead intends to pass in a mutable array, inferring__autoreleasing is the wrong thing to do; this directs some of the
caution in the following rules about writeback.
:::
Such a type written anywhere else would be ill-formed by the general rule
requiring ownership qualifiers.
This rule does not apply in Objective-C++ if a parameter's type is dependent in
a template pattern and is only instantiated to a type which would be a
pointer to an unqualified retainable object pointer type. Such code is still
ill-formed.
:::{admonition} Rationale
The convention is very unlikely to be intentional in template code.
:::
(arc.ownership.inference.template.arguments)=
Template arguments
If a template argument for a template type parameter is a retainable object
owner type that does not have an explicit ownership qualifier, it is adjusted
to have __strong qualification. This adjustment occurs regardless of
whether the template argument was deduced or explicitly specified.
:::{admonition} Rationale__strong is a useful default for containers (e.g., std::vector),
which would otherwise require explicit qualification. Moreover, unqualified
retainable object pointer types are unlikely to be useful within templates,
since they generally need to have a qualifier applied to the before being
used.
:::
(arc.method-families)=
Method families
An Objective-C method may fall into a {arc-term}method family, which is a
conventional set of behaviors ascribed to it by the Cocoa conventions.
A method is in a certain method family if:
- it has a
objc_method_familyattribute placing it in that family; or if
not that, - it does not have an
objc_method_familyattribute placing it in a
different or no family, and - its selector falls into the corresponding selector family, and
- its signature obeys the added restrictions of the method family.
A selector is in a certain selector family if, ignoring any leading
underscores, the first component of the selector either consists entirely of
the name of the method family or it begins with that name followed by a
character other than a lowercase letter. For example, _perform:with: andperformWith: would fall into the perform family (if we recognized one),
but performing:with would not.
The families and their added restrictions are:
allocmethods must return a retainable object pointer type.copymethods must return a retainable object pointer type.mutableCopymethods must return a retainable object pointer type.newmethods must return a retainable object pointer type.initmethods must be instance methods and must return an Objective-C
pointer type. Additionally, a program is ill-formed if it declares or
contains a call to aninitmethod whose return type is neitheridnor
a pointer to a super-class or sub-class of the declaring class (if the method
was declared on a class) or the static receiver type of the call (if it was
declared on a protocol).:::{admonition} Rationale
There are a fair number of existing methods withinit-like selectors
which nonetheless don't follow theinitconventions. Typically these
are either accidental naming collisions or helper methods called during
initialization. Because of the peculiar retain/release behavior ofinitmethods, it's very important not to treat these methods asinitmethods if they aren't meant to be. It was felt that implicitly
defining these methods out of the family based on the exact relationship
between the return type and the declaring class would be much too subtle
and fragile. Therefore we identify a small number of legitimate-seeming
return types and call everything else an error. This serves the secondary
purpose of encouraging programmers not to accidentally give methods names
in theinitfamily.Note that a method with an
init-family selector which returns a
non-Objective-C type (e.g.void) is perfectly well-formed; it simply
isn't in theinitfamily.
:::
A program is ill-formed if a method's declarations, implementations, and
overrides do not all have the same method family.
(arc.family.attribute)=
Explicit method family control
A method may be annotated with the objc_method_family attribute to
precisely control which method family it belongs to. If a method in an@implementation does not have this attribute, but there is a method
declared in the corresponding @interface that does, then the attribute is
copied to the declaration in the @implementation. The attribute is
available outside of ARC, and may be tested for with the preprocessor query__has_attribute(objc_method_family).
The attribute is spelled__attribute__((objc_method_family( family ))). If family isnone, the method has no family, even if it would otherwise be considered to
have one based on its selector and type. Otherwise, family must be one ofalloc, copy, init, mutableCopy, or new, in which case the
method is considered to belong to the corresponding family regardless of its
selector. It is an error if a method that is explicitly added to a family in
this way does not meet the requirements of the family other than the selector
naming convention.
:::{admonition} Rationale
The rules codified in this document describe the standard conventions of
Objective-C. However, as these conventions have not heretofore been enforced
by an unforgiving mechanical system, they are only imperfectly kept,
especially as they haven't always even been precisely defined. While it is
possible to define low-level ownership semantics with attributes likens_returns_retained, this attribute allows the user to communicate
semantic intent, which is of use both to ARC (which, e.g., treats calls toinit specially) and the static analyzer.
:::
(arc.family.semantics)=
Semantics of method families
A method's membership in a method family may imply non-standard semantics for
its parameters and return type.
Methods in the alloc, copy, mutableCopy, and new families ---
that is, methods in all the currently-defined families except init ---
implicitly {ref}return a retained object <arc.object.operands.retained-return-values> as if they were annotated with
the ns_returns_retained attribute. This can be overridden by annotating
the method with either of the ns_returns_autoreleased orns_returns_not_retained attributes.
Properties also follow same naming rules as methods. This means that those in
the alloc, copy, mutableCopy, and new families provide access
to {ref}retained objects <arc.object.operands.retained-return-values>. This
can be overridden by annotating the property with ns_returns_not_retained
attribute.
(arc.family.semantics.init)=
Semantics of `init`
Methods in the init family implicitly {ref}consume <arc.objects.operands.consumed> their self parameter and {ref}return a retained object <arc.object.operands.retained-return-values>. Neither of
these properties can be altered through attributes.
A call to an init method with a receiver that is either self (possibly
parenthesized or casted) or super is called a {arc-term}delegate init call. It is an error for a delegate init call to be made except from aninit method, and excluding blocks within such methods.
As an exception to the {ref}usual rule <arc.misc.self>, the variable self
is mutable in an init method and has the usual semantics for a __strong
variable. However, it is undefined behavior and the program is ill-formed, no
diagnostic required, if an init method attempts to use the previous value
of self after the completion of a delegate init call. It is conventional,
but not required, for an init method to return self.
It is undefined behavior for a program to cause two or more calls to init
methods on the same object, except that each init method invocation may
perform at most one delegate init call.
(arc.family.semantics.result_type)=
Related result types
Certain methods are candidates to have {arc-term}related result types:
- class methods in the
allocandnewmethod families - instance methods in the
initfamily - the instance method
self - outside of ARC, the instance methods
retainandautorelease
If the formal result type of such a method is id or protocol-qualifiedid, or a type equal to the declaring class or a superclass, then it is said
to have a related result type. In this case, when invoked in an explicit
message send, it is assumed to return a type related to the type of the
receiver:
- if it is a class method, and the receiver is a class name
T, the message
send expression has typeT*; otherwise - if it is an instance method, and the receiver has type
T, the message
send expression has typeT; otherwise - the message send expression has the normal result type of the method.
This is a new rule of the Objective-C language and applies outside of ARC.
:::{admonition} Rationale
ARC's automatic code emission is more prone than most code to signature
errors, i.e. errors where a call was emitted against one method signature,
but the implementing method has an incompatible signature. Having more
precise type information helps drastically lower this risk, as well as
catching a number of latent bugs.
:::
(arc.optimization)=
Optimization
Within this section, the word {arc-term}function will be used to
refer to any structured unit of code, be it a C function, an
Objective-C method, or a block.
This specification describes ARC as performing specific retain andrelease operations on retainable object pointers at specific
points during the execution of a program. These operations make up a
non-contiguous subsequence of the computation history of the program.
The portion of this sequence for a particular retainable object
pointer for which a specific function execution is directly
responsible is the {arc-term}formal local retain history of the
object pointer. The corresponding actual sequence executed is thedynamic local retain history.
However, under certain circumstances, ARC is permitted to re-order and
eliminate operations in a manner which may alter the overall
computation history beyond what is permitted by the general "as if"
rule of C/C++ and the {ref}restrictions <arc.objects.retains> on
the implementation of retain and release.
:::{admonition} Rationale
Specifically, ARC is sometimes permitted to optimize release
operations in ways which might cause an object to be deallocated
before it would otherwise be. Without this, it would be almost
impossible to eliminate any retain/release pairs. For
example, consider the following code:
id x = _ivar;
[x foo];If we were not permitted in any event to shorten the lifetime of the
object in x, then we would not be able to eliminate this retain
and release unless we could prove that the message send could not
modify _ivar (or deallocate self). Since message sends are
opaque to the optimizer, this is not possible, and so ARC's hands
would be almost completely tied.
:::
ARC makes no guarantees about the execution of a computation history
which contains undefined behavior. In particular, ARC makes no
guarantees in the presence of race conditions.
ARC may assume that any retainable object pointers it receives or
generates are instantaneously valid from that point until a point
which, by the concurrency model of the host language, happens-after
the generation of the pointer and happens-before a release of that
object (possibly via an aliasing pointer or indirectly due to
destruction of a different object).
:::{admonition} Rationale
There is very little point in trying to guarantee correctness in the
presence of race conditions. ARC does not have a stack-scanning
garbage collector, and guaranteeing the atomicity of every load and
store operation would be prohibitive and preclude a vast amount of
optimization.
:::
ARC may assume that non-ARC code engages in sensible balancing
behavior and does not rely on exact or minimum retain count values
except as guaranteed by __strong object invariants or +1 transfer
conventions. For example, if an object is provably double-retained
and double-released, ARC may eliminate the inner retain and release;
it does not need to guard against code which performs an unbalanced
release followed by a "balancing" retain.
(arc.optimization.liveness)=
Object liveness
ARC may not allow a retainable object X to be deallocated at a
time T in a computation history if:
Xis the value stored in a__strongobjectSwith
{ref}precise lifetime semantics <arc.optimization.precise>, orXis the value stored in a__strongobjectSwith
imprecise lifetime semantics and, at some point afterTbut
before the next store toS, the computation history features a
load fromSand in some way depends on the value loaded, orXis a value described as being released at the end of the
current full-expression and, at some point afterTbut before
the end of the full-expression, the computation history depends
on that value.
:::{admonition} Rationale
The intent of the second rule is to say that objects held in normal__strong local variables may be released as soon as the value in
the variable is no longer being used: either the variable stops
being used completely or a new value is stored in the variable.
The intent of the third rule is to say that return values may be
released after they've been used.
:::
A computation history depends on a pointer value P if it:
- performs a pointer comparison with
P, - loads from
P, - stores to
P, - depends on a pointer value
Qderived via pointer arithmetic
fromP(including an instance-variable or field access), or - depends on a pointer value
Qloaded fromP.
Dependency applies only to values derived directly or indirectly from
a particular expression result and does not occur merely because a
separate pointer value dynamically aliases P. Furthermore, this
dependency is not carried by values that are stored to objects.
:::{admonition} Rationale
The restrictions on dependency are intended to make this analysis
feasible by an optimizer with only incomplete information about a
program. Essentially, dependence is carried to "obvious" uses of a
pointer. Merely passing a pointer argument to a function does not
itself cause dependence, but since generally the optimizer will not
be able to prove that the function doesn't depend on that parameter,
it will be forced to conservatively assume it does.
Dependency propagates to values loaded from a pointer because those
values might be invalidated by deallocating the object. For
example, given the code __strong id x = p->ivar;, ARC must not
move the release of p to between the load of p->ivar and the
retain of that value for storing into x.
Dependency does not propagate through stores of dependent pointer
values because doing so would allow dependency to outlive the
full-expression which produced the original value. For example, the
address of an instance variable could be written to some global
location and then freely accessed during the lifetime of the local,
or a function could return an inner pointer of an object and store
it to a local. These cases would be potentially impossible to
reason about and so would basically prevent any optimizations based
on imprecise lifetime. There are also uncommon enough to make it
reasonable to require the precise-lifetime annotation if someone
really wants to rely on them.
Dependency does propagate through return values of pointer type.
The compelling source of need for this rule is a property accessor
which returns an un-autoreleased result; the calling function must
have the chance to operate on the value, e.g. to retain it, before
ARC releases the original pointer. Note again, however, that
dependence does not survive a store, so ARC does not guarantee the
continued validity of the return value past the end of the
full-expression.
:::
(arc.optimization.object_lifetime)=
No object lifetime extension
If, in the formal computation history of the program, an object X
has been deallocated by the time of an observable side-effect, then
ARC must cause X to be deallocated by no later than the occurrence
of that side-effect, except as influenced by the re-ordering of the
destruction of objects.
:::{admonition} Rationale
This rule is intended to prohibit ARC from observably extending the
lifetime of a retainable object, other than as specified in this
document. Together with the rule limiting the transformation of
releases, this rule requires ARC to eliminate retains and release
only in pairs.
ARC's power to reorder the destruction of objects is critical to its
ability to do any optimization, for essentially the same reason that
it must retain the power to decrease the lifetime of an object.
Unfortunately, while it's generally poor style for the destruction
of objects to have arbitrary side-effects, it's certainly possible.
Hence the caveat.
:::
(arc.optimization.precise)=
Precise lifetime semantics
In general, ARC maintains an invariant that a retainable object pointer held in
a __strong object will be retained for the full formal lifetime of the
object. Objects subject to this invariant have {arc-term}precise lifetime semantics.
By default, local variables of automatic storage duration do not have precise
lifetime semantics. Such objects are simply strong references which hold
values of retainable object pointer type, and these values are still fully
subject to the optimizations on values under local control.
:::{admonition} Rationale
Applying these precise-lifetime semantics strictly would be prohibitive.
Many useful optimizations that might theoretically decrease the lifetime of
an object would be rendered impossible. Essentially, it promises too much.
:::
A local variable of retainable object owner type and automatic storage duration
may be annotated with the objc_precise_lifetime attribute to indicate that
it should be considered to be an object with precise lifetime semantics.
:::{admonition} Rationale
Nonetheless, it is sometimes useful to be able to force an object to be
released at a precise time, even if that object does not appear to be used.
This is likely to be uncommon enough that the syntactic weight of explicitly
requesting these semantics will not be burdensome, and may even make the code
clearer.
:::
(arc.misc)=
Miscellaneous
(arc.misc.special_methods)=
Special methods
(arc.misc.special_methods.retain)=
Memory management methods
A program is ill-formed if it contains a method definition, message send, or@selector expression for any of the following selectors:
autoreleasereleaseretainretainCount
:::{admonition} RationaleretainCount is banned because ARC robs it of consistent semantics. The
others were banned after weighing three options for how to deal with message
sends:
Honoring them would work out very poorly if a programmer naively or
accidentally tried to incorporate code written for manual retain/release code
into an ARC program. At best, such code would do twice as much work as
necessary; quite frequently, however, ARC and the explicit code would both
try to balance the same retain, leading to crashes. The cost is losing the
ability to perform "unrooted" retains, i.e. retains not logically
corresponding to a strong reference in the object graph.
Ignoring them would badly violate user expectations about their code.
While it would make it easier to develop code simultaneously for ARC and
non-ARC, there is very little reason to do so except for certain library
developers. ARC and non-ARC translation units share an execution model and
can seamlessly interoperate. Within a translation unit, a developer who
faithfully maintains their code in non-ARC mode is suffering all the
restrictions of ARC for zero benefit, while a developer who isn't testing the
non-ARC mode is likely to be unpleasantly surprised if they try to go back to
it.
Banning them has the disadvantage of making it very awkward to migrate
existing code to ARC. The best answer to that, given a number of other
changes and restrictions in ARC, is to provide a specialized tool to assist
users in that migration.
Implementing these methods was banned because they are too integral to the
semantics of ARC; many tricks which worked tolerably under manual reference
counting will misbehave if ARC performs an ephemeral extra retain or two. If
absolutely required, it is still possible to implement them in non-ARC code,
for example in a category; the implementations must obey the {ref}semantics <arc.objects.retains> laid out elsewhere in this document.
:::
(arc.misc.special_methods.dealloc)=
`dealloc`
A program is ill-formed if it contains a message send or @selector
expression for the selector dealloc.
:::{admonition} Rationale
There are no legitimate reasons to call dealloc directly.
:::
A class may provide a method definition for an instance method nameddealloc. This method will be called after the final release of the
object but before it is deallocated or any of its instance variables are
destroyed. The superclass's implementation of dealloc will be called
automatically when the method returns.
:::{admonition} Rationale
Even though ARC destroys instance variables automatically, there are still
legitimate reasons to write a dealloc method, such as freeing
non-retainable resources. Failing to call [super dealloc] in such a
method is nearly always a bug. Sometimes, the object is simply trying to
prevent itself from being destroyed, but dealloc is really far too late
for the object to be raising such objections. Somewhat more legitimately, an
object may have been pool-allocated and should not be deallocated withfree; for now, this can only be supported with a dealloc
implementation outside of ARC. Such an implementation must be very careful
to do all the other work that NSObject's dealloc would, which is
outside the scope of this document to describe.
:::
The instance variables for an ARC-compiled class will be destroyed at some
point after control enters the dealloc method for the root class of the
class. The ordering of the destruction of instance variables is unspecified,
both within a single class and between subclasses and superclasses.
:::{admonition} Rationale
The traditional, non-ARC pattern for destroying instance variables is to
destroy them immediately before calling [super dealloc]. Unfortunately,
message sends from the superclass are quite capable of reaching methods in
the subclass, and those methods may well read or write to those instance
variables. Making such message sends from dealloc is generally discouraged,
since the subclass may well rely on other invariants that were broken duringdealloc, but it's not so inescapably dangerous that we felt comfortable
calling it undefined behavior. Therefore we chose to delay destroying the
instance variables to a point at which message sends are clearly disallowed:
the point at which the root class's deallocation routines take over.
In most code, the difference is not observable. It can, however, be observed
if an instance variable holds a strong reference to an object whose
deallocation will trigger a side-effect which must be carefully ordered with
respect to the destruction of the super class. Such code violates the design
principle that semantically important behavior should be explicit. A simple
fix is to clear the instance variable manually during dealloc; a more
holistic solution is to move semantically important side-effects out ofdealloc and into a separate teardown phase which can rely on working with
well-formed objects.
:::
(arc.misc.autoreleasepool)=
`@autoreleasepool`
To simplify the use of autorelease pools, and to bring them under the control
of the compiler, a new kind of statement is available in Objective-C. It is
written @autoreleasepool followed by a compound-statement, i.e. by a new
scope delimited by curly braces. Upon entry to this block, the current state
of the autorelease pool is captured. When the block is exited normally,
whether by fallthrough or directed control flow (such as return orbreak), the autorelease pool is restored to the saved state, releasing all
the objects in it. When the block is exited with an exception, the pool is not
drained.
@autoreleasepool may be used in non-ARC translation units, with equivalent
semantics.
A program is ill-formed if it refers to the NSAutoreleasePool class.
:::{admonition} Rationale
Autorelease pools are clearly important for the compiler to reason about, but
it is far too much to expect the compiler to accurately reason about control
dependencies between two calls. It is also very easy to accidentally forget
to drain an autorelease pool when using the manual API, and this can
significantly inflate the process's high-water-mark. The introduction of a
new scope is unfortunate but basically required for sane interaction with the
rest of the language. Not draining the pool during an unwind is apparently
required by the Objective-C exceptions implementation.
:::
(arc.misc.externally_retained)=
Externally-Retained Variables
In some situations, variables with strong ownership are considered
externally-retained by the implementation. This means that the variable is
retained elsewhere, and therefore the implementation can elide retaining and
releasing its value. Such a variable is implicitly const for safety. In
contrast with __unsafe_unretained, an externally-retained variable still
behaves as a strong variable outside of initialization and destruction. For
instance, when an externally-retained variable is captured in a block the value
of the variable is retained and released on block capture and destruction. It
also affects C++ features such as lambda capture, decltype, and template
argument deduction.
Implicitly, the implementation assumes that the {ref}self parameter in a non-init method <arc.misc.self> and the {ref}variable in a for-in loop <arc.misc.enumeration> are externally-retained.
Externally-retained semantics can also be opted into with theobjc_externally_retained attribute. This attribute can apply to strong local
variables, functions, methods, or blocks:
@class WobbleAmount;
@interface Widget : NSObject
-(void)wobble:(WobbleAmount *)amount;
@end
@implementation Widget
-(void)wobble:(WobbleAmount *)amount
__attribute__((objc_externally_retained)) {
// 'amount' and 'alias' aren't retained on entry, nor released on exit.
__attribute__((objc_externally_retained)) WobbleAmount *alias = amount;
}
@endAnnotating a function with this attribute makes every parameter with strong
retainable object pointer type externally-retained, unless the variable was
explicitly qualified with __strong. For instance, first_param is
externally-retained (and therefore const) below, but not second_param:
__attribute__((objc_externally_retained))
void f(NSArray *first_param, __strong NSArray *second_param) {
// ...
}You can test if your compiler has support for objc_externally_retained with__has_attribute:
#if __has_attribute(objc_externally_retained)
// Use externally retained...
#endif(arc.misc.self)=
`self`
The self parameter variable of a non-init Objective-C method is considered
{ref}externally-retained <arc.misc.externally_retained> by the implementation.
It is undefined behavior, or at least dangerous, to cause an object to be
deallocated during a message send to that object. In an init method, self
follows the {ref}init family rules <arc.family.semantics.init>.
:::{admonition} Rationale
The cost of retaining self in all methods was found to be prohibitive, as
it tends to be live across calls, preventing the optimizer from proving that
the retain and release are unnecessary --- for good reason, as it's quite
possible in theory to cause an object to be deallocated during its execution
without this retain and release. Since it's extremely uncommon to actually
do so, even unintentionally, and since there's no natural way for the
programmer to remove this retain/release pair otherwise (as there is for
other parameters by, say, making the variable objc_externally_retained or
qualifying it with __unsafe_unretained), we chose to make this optimizing
assumption and shift some amount of risk to the user.
:::
(arc.misc.enumeration)=
Fast enumeration iteration variables
If a variable is declared in the condition of an Objective-C fast enumeration
loop, and the variable has no explicit ownership qualifier, then it is
implicitly {ref}externally-retained <arc.misc.externally_retained> so that
objects encountered during the enumeration are not actually retained and
released.
:::{admonition} Rationale
This is an optimization made possible because fast enumeration loops promise
to keep the objects retained during enumeration, and the collection itself
cannot be synchronously modified. It can be overridden by explicitly
qualifying the variable with __strong, which will make the variable
mutable again and cause the loop to retain the objects it encounters.
:::
(arc.misc.blocks)=
Blocks
The implicit const capture variables created when evaluating a block
literal expression have the same ownership semantics as the local variables
they capture. The capture is performed by reading from the captured variable
and initializing the capture variable with that value; the capture variable is
destroyed when the block literal is, i.e. at the end of the enclosing scope.
The {ref}inference <arc.ownership.inference> rules apply equally to__block variables, which is a shift in semantics from non-ARC, where__block variables did not implicitly retain during capture.
__block variables of retainable object owner type are moved off the stack
by initializing the heap copy with the result of moving from the stack copy.
With the exception of retains done as part of initializing a __strong
parameter variable or reading a __weak variable, whenever these semantics
call for retaining a value of block-pointer type, it has the effect of aBlock_copy. The optimizer may remove such copies when it sees that the
result is used only as an argument to a call.
When a block pointer type is converted to a non-block pointer type (such asid), Block_copy is called. This is necessary because a block allocated
on the stack won't get copied to the heap when the non-block pointer escapes.
A block pointer is implicitly converted to id when it is passed to a
function as a variadic argument.
(arc.misc.exceptions)=
Exceptions
By default in Objective C, ARC is not exception-safe for normal releases:
- It does not end the lifetime of
__strongvariables when their scopes are
abnormally terminated by an exception. - It does not perform releases which would occur at the end of a
full-expression if that full-expression throws an exception.
A program may be compiled with the option -fobjc-arc-exceptions in order to
enable these, or with the option -fno-objc-arc-exceptions to explicitly
disable them, with the last such argument "winning".
:::{admonition} Rationale
The standard Cocoa convention is that exceptions signal programmer error and
are not intended to be recovered from. Making code exceptions-safe by
default would impose severe runtime and code size penalties on code that
typically does not actually care about exceptions safety. Therefore,
ARC-generated code leaks by default on exceptions, which is just fine if the
process is going to be immediately terminated anyway. Programs which do care
about recovering from exceptions should enable the option.
:::
In Objective-C++, -fobjc-arc-exceptions is enabled by default.
:::{admonition} Rationale
C++ already introduces pervasive exceptions-cleanup code of the sort that ARC
introduces. C++ programmers who have not already disabled exceptions are
much more likely to actual require exception-safety.
:::
ARC does end the lifetimes of __weak objects when an exception terminates
their scope unless exceptions are disabled in the compiler.
:::{admonition} Rationale
The consequence of a local __weak object not being destroyed is very
likely to be corruption of the Objective-C runtime, so we want to be safer
here. Of course, potentially massive leaks are about as likely to take down
the process as this corruption is if the program does try to recover from
exceptions.
:::
(arc.misc.interior)=
Interior pointers
An Objective-C method returning a non-retainable pointer may be annotated with
the objc_returns_inner_pointer attribute to indicate that it returns a
handle to the internal data of an object, and that this reference will be
invalidated if the object is destroyed. When such a message is sent to an
object, the object's lifetime will be extended until at least the earliest of:
- the last use of the returned pointer, or any pointer derived from it, in the
calling function or - the autorelease pool is restored to a previous state.
:::{admonition} Rationale
Rationale: not all memory and resources are managed with reference counts; it
is common for objects to manage private resources in their own, private way.
Typically these resources are completely encapsulated within the object, but
some classes offer their users direct access for efficiency. If ARC is not
aware of methods that return such "interior" pointers, its optimizations can
cause the owning object to be reclaimed too soon. This attribute informs ARC
that it must tread lightly.
The extension rules are somewhat intentionally vague. The autorelease pool
limit is there to permit a simple implementation to simply retain and
autorelease the receiver. The other limit permits some amount of
optimization. The phrase "derived from" is intended to encompass the results
both of pointer transformations, such as casts and arithmetic, and of loading
from such derived pointers; furthermore, it applies whether or not such
derivations are applied directly in the calling code or by other utility code
(for example, the C library routine strchr). However, the implementation
never need account for uses after a return from the code which calls the
method returning an interior pointer.
:::
As an exception, no extension is required if the receiver is loaded directly
from a __strong object with {ref}precise lifetime semantics <arc.optimization.precise>.
:::{admonition} Rationale
Implicit autoreleases carry the risk of significantly inflating memory use,
so it's important to provide users a way of avoiding these autoreleases.
Tying this to precise lifetime semantics is ideal, as for local variables
this requires a very explicit annotation, which allows ARC to trust the user
with good cheer.
:::
(arc.misc.c-retainable)=
C retainable pointer types
A type is a {arc-term}C retainable pointer type if it is a pointer to
(possibly qualified) void or a pointer to a (possibly qualifier) struct
or class type.
:::{admonition} Rationale
ARC does not manage pointers of CoreFoundation type (or any of the related
families of retainable C pointers which interoperate with Objective-C for
retain/release operation). In fact, ARC does not even know how to
distinguish these types from arbitrary C pointer types. The intent of this
concept is to filter out some obviously non-object types while leaving a hook
for later tightening if a means of exhaustively marking CF types is made
available.
:::
(arc.misc.c-retainable.audit)=
Auditing of C retainable pointer interfaces
{when-revised}[beginning Apple 4.0, LLVM 3.1]
A C function may be marked with the cf_audited_transfer attribute to
express that, except as otherwise marked with attributes, it obeys the
parameter (consuming vs. non-consuming) and return (retained vs. non-retained)
conventions for a C function of its name, namely:
- A parameter of C retainable pointer type is assumed to not be consumed
unless it is marked with thecf_consumedattribute, and - A result of C retainable pointer type is assumed to not be returned retained
unless the function is either markedcf_returns_retainedor it follows
the create/copy naming convention and is not markedcf_returns_not_retained.
A function obeys the {arc-term}create/copy naming convention if its name
contains as a substring:
- either "Create" or "Copy" not followed by a lowercase letter, or
- either "create" or "copy" not followed by a lowercase letter and
not preceded by any letter, whether uppercase or lowercase.
A second attribute, cf_unknown_transfer, signifies that a function's
transfer semantics cannot be accurately captured using any of these
annotations. A program is ill-formed if it annotates the same function with
both cf_audited_transfer and cf_unknown_transfer.
A pragma is provided to facilitate the mass annotation of interfaces:
#pragma clang arc_cf_code_audited begin
...
#pragma clang arc_cf_code_audited endAll C functions declared within the extent of this pragma are treated as if
annotated with the cf_audited_transfer attribute unless they otherwise have
the cf_unknown_transfer attribute. The pragma is accepted in all language
modes. A program is ill-formed if it attempts to change files, whether by
including a file or ending the current file, within the extent of this pragma.
It is possible to test for all the features in this section with__has_feature(arc_cf_code_audited).
:::{admonition} Rationale
A significant inconvenience in ARC programming is the necessity of
interacting with APIs based around C retainable pointers. These features are
designed to make it relatively easy for API authors to quickly review and
annotate their interfaces, in turn improving the fidelity of tools such as
the static analyzer and ARC. The single-file restriction on the pragma is
designed to eliminate the risk of accidentally annotating some other header's
interfaces.
:::
(arc.runtime)=
Runtime support
This section describes the interaction between the ARC runtime and the code
generated by the ARC compiler. This is not part of the ARC language
specification; instead, it is effectively a language-specific ABI supplement,
akin to the "Itanium" generic ABI for C++.
Ownership qualification does not alter the storage requirements for objects,
except that it is undefined behavior if a __weak object is inadequately
aligned for an object of type id. The other qualifiers may be used on
explicitly under-aligned memory.
The runtime tracks __weak objects which holds non-null values. It is
undefined behavior to directly modify a __weak object which is being tracked
by the runtime except through an
{ref}objc_storeWeak <arc.runtime.objc_storeWeak>,
{ref}objc_destroyWeak <arc.runtime.objc_destroyWeak>, or
{ref}objc_moveWeak <arc.runtime.objc_moveWeak> call.
The runtime must provide a number of new entrypoints which the compiler may
emit, which are described in the remainder of this section.
:::{admonition} Rationale
Several of these functions are semantically equivalent to a message send; we
emit calls to C functions instead because:
- the machine code to do so is significantly smaller,
- it is much easier to recognize the C functions in the ARC optimizer, and
- a sufficiently sophisticated runtime may be able to avoid the message send in
common cases.
Several other of these functions are "fused" operations which can be
described entirely in terms of other operations. We use the fused operations
primarily as a code-size optimization, although in some cases there is also a
real potential for avoiding redundant operations in the runtime.
:::
(arc.runtime.objc_autorelease)=
`id objc_autorelease(id value);`
Precondition: value is null or a pointer to a valid object.
If value is null, this call has no effect. Otherwise, it adds the object
to the innermost autorelease pool exactly as if the object had been sent theautorelease message.
Always returns value.
(arc.runtime.objc_autoreleasePoolPop)=
`void objc_autoreleasePoolPop(void *pool);`
Precondition: pool is the result of a previous call to
{ref}objc_autoreleasePoolPush <arc.runtime.objc_autoreleasePoolPush> on the
current thread, where neither pool nor any enclosing pool have previously
been popped.
Releases all the objects added to the given autorelease pool and any
autorelease pools it encloses, then sets the current autorelease pool to the
pool directly enclosing pool.
(arc.runtime.objc_autoreleasePoolPush)=
`void *objc_autoreleasePoolPush(void);`
Creates a new autorelease pool that is enclosed by the current pool, makes that
the current pool, and returns an opaque "handle" to it.
:::{admonition} Rationale
While the interface is described as an explicit hierarchy of pools, the rules
allow the implementation to just keep a stack of objects, using the stack
depth as the opaque pool handle.
:::
(arc.runtime.objc_autoreleaseReturnValue)=
`id objc_autoreleaseReturnValue(id value);`
Precondition: value is null or a pointer to a valid object.
If value is null, this call has no effect. Otherwise, it makes a best
effort to hand off ownership of a retain count on the object to a call to
{ref}objc_retainAutoreleasedReturnValue <arc.runtime.objc_retainAutoreleasedReturnValue> (or
{ref}objc_unsafeClaimAutoreleasedReturnValue <arc.runtime.objc_unsafeClaimAutoreleasedReturnValue>) for the same object in
an enclosing call frame. If this is not possible, the object is autoreleased as
above.
Always returns value.
(arc.runtime.objc_copyWeak)=
`void objc_copyWeak(id *dest, id *src);`
Precondition: src is a valid pointer which either contains a null pointer
or has been registered as a __weak object. dest is a valid pointer
which has not been registered as a __weak object.
dest is initialized to be equivalent to src, potentially registering it
with the runtime. Equivalent to the following code:
void objc_copyWeak(id *dest, id *src) {
objc_release(objc_initWeak(dest, objc_loadWeakRetained(src)));
}Must be atomic with respect to calls to objc_storeWeak on src.
(arc.runtime.objc_destroyWeak)=
`void objc_destroyWeak(id *object);`
Precondition: object is a valid pointer which either contains a null
pointer or has been registered as a __weak object.
object is unregistered as a weak object, if it ever was. The current value
of object is left unspecified; otherwise, equivalent to the following code:
void objc_destroyWeak(id *object) {
objc_storeWeak(object, nil);
}Does not need to be atomic with respect to calls to objc_storeWeak onobject.
(arc.runtime.objc_initWeak)=
`id objc_initWeak(id *object, id value);`
Precondition: object is a valid pointer which has not been registered as
a __weak object. value is null or a pointer to a valid object.
If value is a null pointer or the object to which it points has begun
deallocation, object is zero-initialized. Otherwise, object is
registered as a __weak object pointing to value. Equivalent to the
following code:
id objc_initWeak(id *object, id value) {
*object = nil;
return objc_storeWeak(object, value);
}Returns the value of object after the call.
Does not need to be atomic with respect to calls to objc_storeWeak onobject.
(arc.runtime.objc_loadWeak)=
`id objc_loadWeak(id *object);`
Precondition: object is a valid pointer which either contains a null
pointer or has been registered as a __weak object.
If object is registered as a __weak object, and the last value stored
into object has not yet been deallocated or begun deallocation, retains and
autoreleases that value and returns it. Otherwise returns null. Equivalent to
the following code:
id objc_loadWeak(id *object) {
return objc_autorelease(objc_loadWeakRetained(object));
}Must be atomic with respect to calls to objc_storeWeak on object.
:::{admonition} Rationale
Loading weak references would be inherently prone to race conditions without
the retain.
:::
(arc.runtime.objc_loadWeakRetained)=
`id objc_loadWeakRetained(id *object);`
Precondition: object is a valid pointer which either contains a null
pointer or has been registered as a __weak object.
If object is registered as a __weak object, and the last value stored
into object has not yet been deallocated or begun deallocation, retains
that value and returns it. Otherwise returns null.
Must be atomic with respect to calls to objc_storeWeak on object.
(arc.runtime.objc_moveWeak)=
`void objc_moveWeak(id *dest, id *src);`
Precondition: src is a valid pointer which either contains a null pointer
or has been registered as a __weak object. dest is a valid pointer
which has not been registered as a __weak object.
dest is initialized to be equivalent to src, potentially registering it
with the runtime. src may then be left in its original state, in which
case this call is equivalent to {ref}objc_copyWeak <arc.runtime.objc_copyWeak>, or it may be left as null.
Must be atomic with respect to calls to objc_storeWeak on src.
(arc.runtime.objc_release)=
`void objc_release(id value);`
Precondition: value is null or a pointer to a valid object.
If value is null, this call has no effect. Otherwise, it performs a
release operation exactly as if the object had been sent the release
message.
(arc.runtime.objc_retain)=
`id objc_retain(id value);`
Precondition: value is null or a pointer to a valid object.
If value is null, this call has no effect. Otherwise, it performs a retain
operation exactly as if the object had been sent the retain message.
Always returns value.
(arc.runtime.objc_retainAutorelease)=
`id objc_retainAutorelease(id value);`
Precondition: value is null or a pointer to a valid object.
If value is null, this call has no effect. Otherwise, it performs a retain
operation followed by an autorelease operation. Equivalent to the following
code:
id objc_retainAutorelease(id value) {
return objc_autorelease(objc_retain(value));
}Always returns value.
(arc.runtime.objc_retainAutoreleaseReturnValue)=
`id objc_retainAutoreleaseReturnValue(id value);`
Precondition: value is null or a pointer to a valid object.
If value is null, this call has no effect. Otherwise, it performs a retain
operation followed by the operation described in
{ref}objc_autoreleaseReturnValue <arc.runtime.objc_autoreleaseReturnValue>.
Equivalent to the following code:
id objc_retainAutoreleaseReturnValue(id value) {
return objc_autoreleaseReturnValue(objc_retain(value));
}Always returns value.
(arc.runtime.objc_retainAutoreleasedReturnValue)=
`id objc_retainAutoreleasedReturnValue(id value);`
Precondition: value is null or a pointer to a valid object.
If value is null, this call has no effect. Otherwise, it attempts to
accept a hand off of a retain count from a call to
{ref}objc_autoreleaseReturnValue <arc.runtime.objc_autoreleaseReturnValue> onvalue in a recently-called function or something it tail-calls. If that
fails, it performs a retain operation exactly like {ref}objc_retain <arc.runtime.objc_retain>.
Always returns value.
(arc.runtime.objc_retainBlock)=
`id objc_retainBlock(id value);`
Precondition: value is null or a pointer to a valid block object.
If value is null, this call has no effect. Otherwise, if the block pointed
to by value is still on the stack, it is copied to the heap and the address
of the copy is returned. Otherwise a retain operation is performed on the
block exactly as if it had been sent the retain message.
(arc.runtime.objc_storeStrong)=
`void objc_storeStrong(id *object, id value);`
Precondition: object is a valid pointer to a __strong object which is
adequately aligned for a pointer. value is null or a pointer to a valid
object.
Performs the complete sequence for assigning to a __strong object of
non-block type [^arc-store-strong-block]. Equivalent to the following code:
void objc_storeStrong(id *object, id value) {
id oldValue = *object;
value = [value retain];
*object = value;
[oldValue release];
}[^arc-store-strong-block]: This does not imply that a __strong object of block
type is an invalid argument to this function. Rather it implies that an
objc_retain and not an objc_retainBlock operation will be emitted if the
argument is a block.
(arc.runtime.objc_storeWeak)=
`id objc_storeWeak(id *object, id value);`
Precondition: object is a valid pointer which either contains a null
pointer or has been registered as a __weak object. value is null or a
pointer to a valid object.
If value is a null pointer or the object to which it points has begun
deallocation, object is assigned null and unregistered as a __weak
object. Otherwise, object is registered as a __weak object or has its
registration updated to point to value.
Returns the value of object after the call.
(arc.runtime.objc_unsafeClaimAutoreleasedReturnValue)=
`id objc_unsafeClaimAutoreleasedReturnValue(id value);`
Precondition: value is null or a pointer to a valid object.
If value is null, this call has no effect. Otherwise, it attempts to
accept a hand off of a retain count from a call to
{ref}objc_autoreleaseReturnValue <arc.runtime.objc_autoreleaseReturnValue> onvalue in a recently-called function or something it tail-calls (in a manner
similar to {ref}objc_retainAutoreleasedReturnValue <arc.runtime.objc_retainAutoreleasedReturnValue>). If that succeeds,
it performs a release operation exactly like {ref}objc_release <arc.runtime.objc_release>. If the handoff fails, this call has no effect.
Always returns value.
Clang Docs Block ABI Apple (`clang/docs/Block-ABI-Apple.md`)
Block Implementation Specification
History
- 2008/7/14 - created.
- 2008/8/21 - revised, C++.
- 2008/9/24 - add
NULLisafield to__blockstorage. - 2008/10/1 - revise block layout to use a
staticdescriptor structure. - 2008/10/6 - revise block layout to use an unsigned long int flags.
- 2008/10/28 - specify use of
_Block_object_assignand_Block_object_disposefor all "Object" types in helper functions. - 2008/10/30 - revise new layout to have invoke function in same place.
- 2008/10/30 - add
__weaksupport. - 2010/3/16 - rev for stret return, signature field.
- 2010/4/6 - improved wording.
- 2013/1/6 - improved wording and converted to rst.
This document describes the Apple ABI implementation specification of Blocks.
The first shipping version of this ABI is found in Mac OS X 10.6, and shall be
referred to as 10.6.ABI. As of 2010/3/16, the following describes the ABI
contract with the runtime and the compiler, and, as necessary, will be referred
to as ABI.2010.3.16.
Since the Apple ABI references symbols from other elements of the system, any
attempt to use this ABI on systems prior to SnowLeopard is undefined.
High Level
The ABI of Blocks consists of their layout and the runtime functions required
by the compiler. A Block of type R (^)(P...) consists of a structure of
the following form:
struct Block_literal_1 {
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
int flags;
int reserved;
R (*invoke)(struct Block_literal_1 *, P...);
struct Block_descriptor_1 {
unsigned long int reserved; // NULL
unsigned long int size; // sizeof(struct Block_literal_1)
// optional helper functions
void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
void (*dispose_helper)(void *src); // IFF (1<<25)
// required ABI.2010.3.16
const char *signature; // IFF (1<<30)
} *descriptor;
// imported variables
};The following flags bits are in use thusly for a possible ABI.2010.3.16:
enum {
// Set to true on blocks that have captures (and thus are not true
// global blocks) but are known not to escape for various other
// reasons. For backward compatibility with old runtimes, whenever
// BLOCK_IS_NOESCAPE is set, BLOCK_IS_GLOBAL is set too. Copying a
// non-escaping block returns the original block and releasing such a
// block is a no-op, which is exactly how global blocks are handled.
BLOCK_IS_NOESCAPE = (1 << 23),
BLOCK_HAS_COPY_DISPOSE = (1 << 25),
BLOCK_HAS_CTOR = (1 << 26), // helpers have C++ code
BLOCK_IS_GLOBAL = (1 << 28),
BLOCK_HAS_STRET = (1 << 29), // IFF BLOCK_HAS_SIGNATURE
BLOCK_HAS_SIGNATURE = (1 << 30),
};In 10.6.ABI the (1<<29) was usually set and was always ignored by the runtime -
it had been a transitional marker that did not get deleted after the
transition. This bit is now paired with (1<<30), and represented as the pair
(3<<29), for the following combinations of valid bit settings, and their
meanings:
switch (flags & (3<<29)) {
case (0<<29): 10.6.ABI, no signature field available
case (1<<29): 10.6.ABI, no signature field available
case (2<<29): ABI.2010.3.16, regular calling convention, presence of signature field
case (3<<29): ABI.2010.3.16, stret calling convention, presence of signature field,
}The signature field is not always populated.
The following discussions are presented as 10.6.ABI otherwise.
Block literals may occur within functions where the structure is created in
stack local memory. They may also appear as initialization expressions forBlock variables of global or static local variables.
When a Block literal expression is evaluated the stack based structure is
initialized as follows:
A
staticdescriptor structure is declared and initialized as follows:a. The
invokefunction pointer is set to a function that takes the
Blockstructure as its first argument and the rest of the arguments (if
any) to theBlockand executes theBlockcompound statement.b. The
sizefield is set to the size of the followingBlockliteral
structure.c. The
copy_helperanddispose_helperfunction pointers are set to
respective helper functions if they are required by theBlockliteral.A stack (or global)
Blockliteral data structure is created and
initialized as follows:a. The
isafield is set to the address of the external_NSConcreteStackBlock, which is a block of uninitialized memory supplied
inlibSystem, or_NSConcreteGlobalBlockif this is a static or file
levelBlockliteral.b. The
flagsfield is set to zero unless there are variables imported
into theBlockthat need helper functions for program levelBlock_copy()andBlock_release()operations, in which case the
(1<<25)flags bit is set.
As an example, the Block literal expression:
^ { printf("hello world\n"); }would cause the following to be created on a 32-bit system:
struct __block_literal_1 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_1 *);
struct __block_descriptor_1 *descriptor;
};
void __block_invoke_1(struct __block_literal_1 *_block) {
printf("hello world\n");
}
static struct __block_descriptor_1 {
unsigned long int reserved;
unsigned long int Block_size;
} __block_descriptor_1 = { 0, sizeof(struct __block_literal_1) };and where the Block literal itself appears:
struct __block_literal_1 _block_literal = {
&_NSConcreteStackBlock,
(1<<29), <uninitialized>,
__block_invoke_1,
&__block_descriptor_1
};A Block imports other Block references, const copies of other
variables, and variables marked __block. In Objective-C, variables may
additionally be objects.
When a Block literal expression is used as the initial value of a global
or static local variable, it is initialized as follows:
struct __block_literal_1 __block_literal_1 = {
&_NSConcreteGlobalBlock,
(1<<28)|(1<<29), <uninitialized>,
__block_invoke_1,
&__block_descriptor_1
};that is, a different address is provided as the first value and a particular
(1<<28) bit is set in the flags field, and otherwise it is the same as for
stack based Block literals. This is an optimization that can be used for
any Block literal that imports no const or __block storage
variables.
Imported Variables
Variables of auto storage class are imported as const copies. Variables
of __block storage class are imported as a pointer to an enclosing data
structure. Global variables are simply referenced and not considered as
imported.
Imported `const` copy variables
Automatic storage variables not marked with __block are imported asconst copies.
The simplest example is that of importing a variable of type int:
int x = 10;
void (^vv)(void) = ^{ printf("x is %d\n", x); }
x = 11;
vv();which would be compiled to:
struct __block_literal_2 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_2 *);
struct __block_descriptor_2 *descriptor;
const int x;
};
void __block_invoke_2(struct __block_literal_2 *_block) {
printf("x is %d\n", _block->x);
}
static struct __block_descriptor_2 {
unsigned long int reserved;
unsigned long int Block_size;
} __block_descriptor_2 = { 0, sizeof(struct __block_literal_2) };and:
struct __block_literal_2 __block_literal_2 = {
&_NSConcreteStackBlock,
(1<<29), <uninitialized>,
__block_invoke_2,
&__block_descriptor_2,
x
};In summary, scalars, structures, unions, and function pointers are generally
imported as const copies with no need for helper functions.
Imported `const` copy of `Block` reference
The first case where copy and dispose helper functions are required is for the
case of when a Block itself is imported. In this case both acopy_helper function and a dispose_helper function are needed. Thecopy_helper function is passed both the existing stack based pointer and the
pointer to the new heap version and should call back into the runtime to
actually do the copy operation on the imported fields within the Block. The
runtime functions are all described in {ref}RuntimeHelperFunctions.
A quick example:
void (^existingBlock)(void) = ...;
void (^vv)(void) = ^{ existingBlock(); }
vv();
struct __block_literal_3 {
...; // existing block
};
struct __block_literal_4 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_4 *);
struct __block_literal_3 *const existingBlock;
};
void __block_invoke_4(struct __block_literal_2 *_block) {
__block->existingBlock->invoke(__block->existingBlock);
}
void __block_copy_4(struct __block_literal_4 *dst, struct __block_literal_4 *src) {
//_Block_copy_assign(&dst->existingBlock, src->existingBlock, 0);
_Block_object_assign(&dst->existingBlock, src->existingBlock, BLOCK_FIELD_IS_BLOCK);
}
void __block_dispose_4(struct __block_literal_4 *src) {
// was _Block_destroy
_Block_object_dispose(src->existingBlock, BLOCK_FIELD_IS_BLOCK);
}
static struct __block_descriptor_4 {
unsigned long int reserved;
unsigned long int Block_size;
void (*copy_helper)(struct __block_literal_4 *dst, struct __block_literal_4 *src);
void (*dispose_helper)(struct __block_literal_4 *);
} __block_descriptor_4 = {
0,
sizeof(struct __block_literal_4),
__block_copy_4,
__block_dispose_4,
};and where said Block is used:
struct __block_literal_4 _block_literal = {
&_NSConcreteStackBlock,
(1<<25)|(1<<29), <uninitialized>
__block_invoke_4,
& __block_descriptor_4
existingBlock,
};Importing `__attribute__((NSObject))` variables
GCC introduces __attribute__((NSObject)) on structure pointers to mean "this
is an object". This is useful because many low level data structures are
declared as opaque structure pointers, e.g. CFStringRef, CFArrayRef,
etc. When used from C, however, these are still really objects and are the
second case where that requires copy and dispose helper functions to be
generated. The copy helper functions generated by the compiler should use the_Block_object_assign runtime helper function and in the dispose helper the_Block_object_dispose runtime helper function should be called.
For example, Block foo in the following:
struct Opaque *__attribute__((NSObject)) objectPointer = ...;
...
void (^foo)(void) = ^{ CFPrint(objectPointer); };would have the following helper functions generated:
void __block_copy_foo(struct __block_literal_5 *dst, struct __block_literal_5 *src) {
_Block_object_assign(&dst->objectPointer, src-> objectPointer, BLOCK_FIELD_IS_OBJECT);
}
void __block_dispose_foo(struct __block_literal_5 *src) {
_Block_object_dispose(src->objectPointer, BLOCK_FIELD_IS_OBJECT);
}Imported `__block` marked variables
Layout of `__block` marked variables
The compiler must embed variables that are marked __block in a specialized
structure of the form:
struct _block_byref_foo {
void *isa;
struct Block_byref *forwarding;
int flags; //refcount;
int size;
typeof(marked_variable) marked_variable;
};Variables of certain types require helper functions for when Block_copy()
and Block_release() are performed upon a referencing Block. At the "C"
level only variables that are of type Block or ones that have__attribute__((NSObject)) marked require helper functions. In Objective-C
objects require helper functions and in C++ stack based objects require helper
functions. Variables that require helper functions use the form:
struct _block_byref_foo {
void *isa;
struct _block_byref_foo *forwarding;
int flags; //refcount;
int size;
// helper functions called via Block_copy() and Block_release()
void (*byref_keep)(void *dst, void *src);
void (*byref_dispose)(void *);
typeof(marked_variable) marked_variable;
};The structure is initialized such that:
a. The forwarding pointer is set to the beginning of its enclosing
structure.
b. The size field is initialized to the total size of the enclosing
structure.
c. The flags field is set to either 0 if no helper functions are needed
or (1<<25) if they are.
d. The helper functions are initialized (if present).
e. The variable itself is set to its initial value.
f. The isa field is set to NULL.
Access to `__block` variables from within its lexical scope
In order to "move" the variable to the heap upon a copy_helper operation the
compiler must rewrite access to such a variable to be indirect through the
structures forwarding pointer. For example:
int __block i = 10;
i = 11;would be rewritten to be:
struct _block_byref_i {
void *isa;
struct _block_byref_i *forwarding;
int flags; //refcount;
int size;
int captured_i;
} i = { NULL, &i, 0, sizeof(struct _block_byref_i), 10 };
i.forwarding->captured_i = 11;In the case of a Block reference variable being marked __block the
helper code generated must use the _Block_object_assign and_Block_object_dispose routines supplied by the runtime to make the
copies. For example:
__block void (voidBlock)(void) = blockA;
voidBlock = blockB;would translate into:
struct _block_byref_voidBlock {
void *isa;
struct _block_byref_voidBlock *forwarding;
int flags; //refcount;
int size;
void (*byref_keep)(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src);
void (*byref_dispose)(struct _block_byref_voidBlock *);
void (^captured_voidBlock)(void);
};
void _block_byref_keep_helper(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src) {
//_Block_copy_assign(&dst->captured_voidBlock, src->captured_voidBlock, 0);
_Block_object_assign(&dst->captured_voidBlock, src->captured_voidBlock, BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER);
}
void _block_byref_dispose_helper(struct _block_byref_voidBlock *param) {
//_Block_destroy(param->captured_voidBlock, 0);
_Block_object_dispose(param->captured_voidBlock, BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER)}and:
struct _block_byref_voidBlock voidBlock = {( .forwarding=&voidBlock, .flags=(1<<25), .size=sizeof(struct _block_byref_voidBlock *),
.byref_keep=_block_byref_keep_helper, .byref_dispose=_block_byref_dispose_helper,
.captured_voidBlock=blockA )};
voidBlock.forwarding->captured_voidBlock = blockB;Importing `__block` variables into `Blocks`
A Block that uses a __block variable in its compound statement body must
import the variable and emit copy_helper and dispose_helper helper
functions that, in turn, call back into the runtime to actually copy or release
the byref data block using the functions _Block_object_assign and_Block_object_dispose.
For example:
int __block i = 2;
functioncall(^{ i = 10; });would translate to:
struct _block_byref_i {
void *isa; // set to NULL
struct _block_byref_voidBlock *forwarding;
int flags; //refcount;
int size;
void (*byref_keep)(struct _block_byref_i *dst, struct _block_byref_i *src);
void (*byref_dispose)(struct _block_byref_i *);
int captured_i;
};
struct __block_literal_5 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_5 *);
struct __block_descriptor_5 *descriptor;
struct _block_byref_i *i_holder;
};
void __block_invoke_5(struct __block_literal_5 *_block) {
_block->forwarding->captured_i = 10;
}
void __block_copy_5(struct __block_literal_5 *dst, struct __block_literal_5 *src) {
//_Block_byref_assign_copy(&dst->captured_i, src->captured_i);
_Block_object_assign(&dst->captured_i, src->captured_i, BLOCK_FIELD_IS_BYREF | BLOCK_BYREF_CALLER);
}
void __block_dispose_5(struct __block_literal_5 *src) {
//_Block_byref_release(src->captured_i);
_Block_object_dispose(src->captured_i, BLOCK_FIELD_IS_BYREF | BLOCK_BYREF_CALLER);
}
static struct __block_descriptor_5 {
unsigned long int reserved;
unsigned long int Block_size;
void (*copy_helper)(struct __block_literal_5 *dst, struct __block_literal_5 *src);
void (*dispose_helper)(struct __block_literal_5 *);
} __block_descriptor_5 = { 0, sizeof(struct __block_literal_5) __block_copy_5, __block_dispose_5 };and:
struct _block_byref_i i = {( .isa=NULL, .forwarding=&i, .flags=0, .size=sizeof(struct _block_byref_i), .captured_i=2 )};
struct __block_literal_5 _block_literal = {
&_NSConcreteStackBlock,
(1<<25)|(1<<29), <uninitialized>,
__block_invoke_5,
&__block_descriptor_5,
&i,
};Importing `__attribute__((NSObject))` `__block` variables
A __block variable that is also marked __attribute__((NSObject)) should
have byref_keep and byref_dispose helper functions that use_Block_object_assign and _Block_object_dispose.
`__block` escapes
Because Blocks referencing __block variables may have Block_copy()
performed upon them the underlying storage for the variables may move to the
heap. In Objective-C Garbage Collection Only compilation environments the heap
used is the garbage collected one and no further action is required. Otherwise
the compiler must issue a call to potentially release any heap storage for__block variables at all escapes or terminations of their scope. The call
should be:
_Block_object_dispose(&_block_byref_foo, BLOCK_FIELD_IS_BYREF);Nesting
Blocks may contain Block literal expressions. Any variables used within
inner blocks are imported into all enclosing Block scopes even if the
variables are not used. This includes const imports as well as __block
variables.
Objective C Extensions to `Blocks`
Importing Objects
Objects should be treated as __attribute__((NSObject)) variables; allcopy_helper, dispose_helper, byref_keep, and byref_dispose
helper functions should use _Block_object_assign and_Block_object_dispose. There should be no code generated that uses*-retain or *-release methods.
`Blocks` as Objects
The compiler will treat Blocks as objects when synthesizing property setters
and getters, will characterize them as objects when generating garbage
collection strong and weak layout information in the same manner as objects, and
will issue strong and weak write-barrier assignments in the same manner as
objects.
`__weak __block` Support
Objective-C (and Objective-C++) support the __weak attribute on __block
variables. Under normal circumstances the compiler uses the Objective-C runtime
helper support functions objc_assign_weak and objc_read_weak. Both
should continue to be used for all reads and writes of __weak __block
variables:
objc_read_weak(&block->byref_i->forwarding->i)The __weak variable is stored in a _block_byref_foo structure and theBlock has copy and dispose helpers for this structure that call:
_Block_object_assign(&dest->_block_byref_i, src-> _block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BYREF);and:
_Block_object_dispose(src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BYREF);In turn, the block_byref copy support helpers distinguish between whether
the __block variable is a Block or not and should either call:
_Block_object_assign(&dest->_block_byref_i, src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_OBJECT | BLOCK_BYREF_CALLER);for something declared as an object or:
_Block_object_assign(&dest->_block_byref_i, src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER);for something declared as a Block.
A full example follows:
__block __weak id obj = <initialization expression>;
functioncall(^{ [obj somemessage]; });would translate to:
struct _block_byref_obj {
void *isa; // uninitialized
struct _block_byref_obj *forwarding;
int flags; //refcount;
int size;
void (*byref_keep)(struct _block_byref_i *dst, struct _block_byref_i *src);
void (*byref_dispose)(struct _block_byref_i *);
id captured_obj;
};
void _block_byref_obj_keep(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src) {
//_Block_copy_assign(&dst->captured_obj, src->captured_obj, 0);
_Block_object_assign(&dst->captured_obj, src->captured_obj, BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER);
}
void _block_byref_obj_dispose(struct _block_byref_voidBlock *param) {
//_Block_destroy(param->captured_obj, 0);
_Block_object_dispose(param->captured_obj, BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER);
};for the block byref part and:
struct __block_literal_5 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_5 *);
struct __block_descriptor_5 *descriptor;
struct _block_byref_obj *byref_obj;
};
void __block_invoke_5(struct __block_literal_5 *_block) {
[objc_read_weak(&_block->byref_obj->forwarding->captured_obj) somemessage];
}
void __block_copy_5(struct __block_literal_5 *dst, struct __block_literal_5 *src) {
//_Block_byref_assign_copy(&dst->byref_obj, src->byref_obj);
_Block_object_assign(&dst->byref_obj, src->byref_obj, BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK);
}
void __block_dispose_5(struct __block_literal_5 *src) {
//_Block_byref_release(src->byref_obj);
_Block_object_dispose(src->byref_obj, BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK);
}
static struct __block_descriptor_5 {
unsigned long int reserved;
unsigned long int Block_size;
void (*copy_helper)(struct __block_literal_5 *dst, struct __block_literal_5 *src);
void (*dispose_helper)(struct __block_literal_5 *);
} __block_descriptor_5 = { 0, sizeof(struct __block_literal_5), __block_copy_5, __block_dispose_5 };and within the compound statement:
truct _block_byref_obj obj = {( .forwarding=&obj, .flags=(1<<25), .size=sizeof(struct _block_byref_obj),
.byref_keep=_block_byref_obj_keep, .byref_dispose=_block_byref_obj_dispose,
.captured_obj = <initialization expression> )};
truct __block_literal_5 _block_literal = {
&_NSConcreteStackBlock,
(1<<25)|(1<<29), <uninitialized>,
__block_invoke_5,
&__block_descriptor_5,
&obj, // a reference to the on-stack structure containing "captured_obj"
};
functioncall(_block_literal->invoke(&_block_literal));C++ Support
Within a block stack based C++ objects are copied into const copies using
the copy constructor. It is an error if a stack based C++ object is used within
a block if it does not have a copy constructor. In addition both copy and
destroy helper routines must be synthesized for the block to support theBlock_copy() operation, and the flags work marked with the (1<<26) bit in
addition to the (1<<25) bit. The copy helper should call the constructor using
appropriate offsets of the variable within the supplied stack based block source
and heap based destination for all const constructed copies, and similarly
should call the destructor in the destroy routine.
As an example, suppose a C++ class FOO existed with a copy constructor.
Within a code block a stack version of a FOO object is declared and used
within a Block literal expression:
{
FOO foo;
void (^block)(void) = ^{ printf("%d\n", foo.value()); };
}The compiler would synthesize:
struct __block_literal_10 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_10 *);
struct __block_descriptor_10 *descriptor;
const FOO foo;
};
void __block_invoke_10(struct __block_literal_10 *_block) {
printf("%d\n", _block->foo.value());
}
void __block_copy_10(struct __block_literal_10 *dst, struct __block_literal_10 *src) {
FOO_ctor(&dst->foo, &src->foo);
}
void __block_dispose_10(struct __block_literal_10 *src) {
FOO_dtor(&src->foo);
}
static struct __block_descriptor_10 {
unsigned long int reserved;
unsigned long int Block_size;
void (*copy_helper)(struct __block_literal_10 *dst, struct __block_literal_10 *src);
void (*dispose_helper)(struct __block_literal_10 *);
} __block_descriptor_10 = { 0, sizeof(struct __block_literal_10), __block_copy_10, __block_dispose_10 };and the code would be:
{
FOO foo;
comp_ctor(&foo); // default constructor
struct __block_literal_10 _block_literal = {
&_NSConcreteStackBlock,
(1<<25)|(1<<26)|(1<<29), <uninitialized>,
__block_invoke_10,
&__block_descriptor_10,
};
comp_ctor(&_block_literal->foo, &foo); // const copy into stack version
struct __block_literal_10 &block = &_block_literal; // assign literal to block variable
block->invoke(block); // invoke block
comp_dtor(&_block_literal->foo); // destroy stack version of const block copy
comp_dtor(&foo); // destroy original version
}C++ objects stored in __block storage start out on the stack in ablock_byref data structure as do other variables. Such objects (if notconst objects) must support a regular copy constructor. The block_byref
data structure will have copy and destroy helper routines synthesized by the
compiler. The copy helper will have code created to perform the copy
constructor based on the initial stack block_byref data structure, and will
also set the (1<<26) bit in addition to the (1<<25) bit. The destroy helper
will have code to do the destructor on the object stored within the suppliedblock_byref heap data structure. For example,
__block FOO blockStorageFoo;requires the normal constructor for the embedded blockStorageFoo object:
FOO_ctor(& _block_byref_blockStorageFoo->blockStorageFoo);and at scope termination the destructor:
FOO_dtor(& _block_byref_blockStorageFoo->blockStorageFoo);Note that the forwarding indirection is NOT used.
The compiler would need to generate (if used from a block literal) the following
copy/dispose helpers:
void _block_byref_obj_keep(struct _block_byref_blockStorageFoo *dst, struct _block_byref_blockStorageFoo *src) {
FOO_ctor(&dst->blockStorageFoo, &src->blockStorageFoo);
}
void _block_byref_obj_dispose(struct _block_byref_blockStorageFoo *src) {
FOO_dtor(&src->blockStorageFoo);
}for the appropriately named constructor and destructor for the class/structFOO.
To support member variable and function access the compiler will synthesize aconst pointer to a block version of the this pointer.
(RuntimeHelperFunctions)=
Runtime Helper Functions
The runtime helper functions are described in/usr/local/include/Block_private.h. To summarize their use, a Block
requires copy/dispose helpers if it imports any block variables, __block
storage variables, __attribute__((NSObject)) variables, or C++ const
copied objects with constructor/destructors. The (1<<26) bit is set and
functions are generated.
The block copy helper function should, for each of the variables of the type
mentioned above, call:
_Block_object_assign(&dst->target, src->target, BLOCK_FIELD_<apropos>);in the copy helper and:
_Block_object_dispose(->target, BLOCK_FIELD_<apropos>);in the dispose helper where <apropos> is:
enum {
BLOCK_FIELD_IS_OBJECT = 3, // id, NSObject, __attribute__((NSObject)), block, ...
BLOCK_FIELD_IS_BLOCK = 7, // a block variable
BLOCK_FIELD_IS_BYREF = 8, // the on stack structure holding the __block variable
BLOCK_FIELD_IS_WEAK = 16, // declared __weak
BLOCK_BYREF_CALLER = 128, // called from byref copy/dispose helpers
};and of course the constructors/destructors for const copied C++ objects.
The block_byref data structure similarly requires copy/dispose helpers for
block variables, __attribute__((NSObject)) variables, or C++ const
copied objects with constructor/destructors, and again the (1<<26) bit is set
and functions are generated in the same manner.
Under ObjC we allow __weak as an attribute on __block variables, and
this causes the addition of BLOCK_FIELD_IS_WEAK orred onto theBLOCK_FIELD_IS_BYREF flag when copying the block_byref structure in theBlock copy helper, and onto the BLOCK_FIELD_<apropos> field within theblock_byref copy/dispose helper calls.
The prototypes, and summary, of the helper functions are:
/* Certain field types require runtime assistance when being copied to the
heap. The following function is used to copy fields of types: blocks,
pointers to byref structures, and objects (including
__attribute__((NSObject)) pointers. BLOCK_FIELD_IS_WEAK is orthogonal to
the other choices which are mutually exclusive. Only in a Block copy
helper will one see BLOCK_FIELD_IS_BYREF.
*/
void _Block_object_assign(void *destAddr, const void *object, const int flags);
/* Similarly a compiler generated dispose helper needs to call back for each
field of the byref data structure. (Currently the implementation only
packs one field into the byref structure but in principle there could be
more). The same flags used in the copy helper should be used for each
call generated to this function:
*/
void _Block_object_dispose(const void *object, const int flags);Copyright
Copyright 2008-2010 Apple, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Clang Docs BlockLanguageSpec (`clang/docs/BlockLanguageSpec.md`)
Language Specification for Blocks
Revisions
- 2008/2/25 --- created
- 2008/7/28 --- revised,
__blocksyntax - 2008/8/13 --- revised, Block globals
- 2008/8/21 --- revised, C++ elaboration
- 2008/11/1 --- revised,
__weaksupport - 2009/1/12 --- revised, explicit return types
- 2009/2/10 --- revised,
__blockobjects need retain
Overview
A new derived type is introduced to C and, by extension, Objective-C,
C++, and Objective-C++
The Block Type
Like function types, the {block-term}Block type is a pair consisting
of a result value type and a list of parameter types very similar to a
function type. Blocks are intended to be used much like functions with
the key distinction being that in addition to executable code they
also contain various variable bindings to automatic (stack) or managed
(heap) memory.
The abstract declarator,
int (^)(char, float)describes a reference to a Block that, when invoked, takes two
parameters, the first of type char and the second of type float, and
returns a value of type int. The Block referenced is of opaque data
that may reside in automatic (stack) memory, global memory, or heap
memory.
Block Variable Declarations
A {block-term}variable with Block type is declared using function
pointer style notation substituting ^ for *. The following are
valid Block variable declarations:
void (^blockReturningVoidWithVoidArgument)(void);
int (^blockReturningIntWithIntAndCharArguments)(int, char);
void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);Variadic ... arguments are supported. [variadic.c] A Block that
takes no arguments must specify void in the argument list [voidarg.c].
An empty parameter list does not represent, as K&R provide, an
unspecified argument list. Note: both gcc and clang support K&R style
as a convenience.
A Block reference may be cast to a pointer of arbitrary type and vice
versa. [cast.c] A Block reference may not be dereferenced via the
pointer dereference operator *, and thus a Block's size may not be
computed at compile time. [sizeof.c]
Block Literal Expressions
A {block-term}Block literal expression produces a reference to a
Block. It is introduced by the use of the ^ token as a unary
operator.
Block_literal_expression ::= ^ block_decl compound_statement_body
block_decl ::=
block_decl ::= parameter_list
block_decl ::= type_expressionwhere type expression is extended to allow ^ as a Block reference
(pointer) where * is allowed as a function reference (pointer).
The following Block literal:
^ void (void) { printf("hello world\n"); }produces a reference to a Block with no arguments with no return value.
The return type is optional and is inferred from the return
statements. If the return statements return a value, they all must
return a value of the same type. If there is no value returned the
inferred type of the Block is void; otherwise it is the type of the
return statement value.
If the return type is omitted and the argument list is ( void ),
the ( void ) argument list may also be omitted.
So:
^ ( void ) { printf("hello world\n"); }and:
^ { printf("hello world\n"); }are exactly equivalent constructs for the same expression.
The type_expression extends C expression parsing to accommodate Block
reference declarations as it accommodates function pointer
declarations.
Given:
typedef int (*pointerToFunctionThatReturnsIntWithCharArg)(char);
pointerToFunctionThatReturnsIntWithCharArg functionPointer;
^ pointerToFunctionThatReturnsIntWithCharArg (float x) { return functionPointer; }and:
^ int ((*)(float x))(char) { return functionPointer; }are equivalent expressions, as is:
^(float x) { return functionPointer; }[returnfunctionptr.c]
The compound statement body establishes a new lexical scope within
that of its parent. Variables used within the scope of the compound
statement are bound to the Block in the normal manner with the
exception of those in automatic (stack) storage. Thus one may access
functions and global variables as one would expect, as well as static
local variables. [testme]
Local automatic (stack) variables referenced within the compound
statement of a Block are imported and captured by the Block as const
copies. The capture (binding) is performed at the time of the Block
literal expression evaluation.
The compiler is not required to capture a variable if it can prove
that no references to the variable will actually be evaluated.
Programmers can force a variable to be captured by referencing it in a
statement at the beginning of the Block, like so:
(void) foo;This matters when capturing the variable has side-effects, as it can
in Objective-C or C++.
The lifetime of variables declared in a Block is that of a function;
each activation frame contains a new copy of variables declared within
the local scope of the Block. Such variable declarations should be
allowed anywhere [testme] rather than only when C99 parsing is
requested, including for statements. [testme]
Block literal expressions may occur within Block literal expressions
(nest) and all variables captured by any nested blocks are implicitly
also captured in the scopes of their enclosing Blocks.
A Block literal expression may be used as the initialization value for
Block variables at global or local static scope.
The Invoke Operator
Blocks are {block-term}invoked using function call syntax with a
list of expression parameters of types corresponding to the
declaration and returning a result type also according to the
declaration. Given:
int (^x)(char);
void (^z)(void);
int (^(*y))(char) = &x;the following are all legal Block invocations:
x('a');
(*y)('a');
(true ? x : *y)('a')The Copy and Release Operations
The compiler and runtime provide {block-term}copy and
{block-term}release operations for Block references that create and,
in matched use, release allocated storage for referenced Blocks.
The copy operation Block_copy() is styled as a function that takes
an arbitrary Block reference and returns a Block reference of the same
type. The release operation, Block_release(), is styled as a
function that takes an arbitrary Block reference and, if dynamically
matched to a Block copy operation, allows recovery of the referenced
allocated memory.
The `__block` Storage Qualifier
In addition to the new Block type we also introduce a new storage
qualifier, {block-term}__block, for local variables. [testme: a
__block declaration within a block literal] The __block storage
qualifier is mutually exclusive to the existing local storage
qualifiers auto, register, and static. [testme] Variables qualified by__block act as if they were in allocated storage and this storage
is automatically recovered after last use of said variable. An
implementation may choose an optimization where the storage is
initially automatic and only "moved" to allocated (heap) storage upon
a Block_copy of a referencing Block. Such variables may be mutated as
normal variables are.
In the case where a __block variable is a Block one must assume
that the __block variable resides in allocated storage and as such
is assumed to reference a Block that is also in allocated storage
(that it is the result of a Block_copy operation). Despite this
there is no provision to do a Block_copy or a Block_release if
an implementation provides initial automatic storage for Blocks. This
is due to the inherent race condition of potentially several threads
trying to update the shared variable and the need for synchronization
around disposing of older values and copying new ones. Such
synchronization is beyond the scope of this language specification.
Control Flow
The compound statement of a Block is treated much like a function body
with respect to control flow in that goto, break, and continue do not
escape the Block. Exceptions are treated normally in that when
thrown they pop stack frames until a catch clause is found.
Objective-C Extensions
Objective-C extends the definition of a Block reference type to be
that also of id. A variable or expression of Block type may be
messaged or used as a parameter wherever an id may be. The converse is
also true. Block references may thus appear as properties and are
subject to the assign, retain, and copy attribute logic that is
reserved for objects.
All Blocks are constructed to be Objective-C objects regardless of
whether the Objective-C runtime is operational in the program or
not. Blocks using automatic (stack) memory are objects and may be
messaged, although they may not be assigned into __weak locations
if garbage collection is enabled.
Within a Block literal expression within a method definition
references to instance variables are also imported into the lexical
scope of the compound statement. These variables are implicitly
qualified as references from self, and so self is imported as a const
copy. The net effect is that instance variables can be mutated.
The {block-term}Block_copy operator retains all objects held in
variables of automatic storage referenced within the Block expression
(or forms strong references if running under garbage collection).
Object variables of __block storage type are assumed to hold
normal pointers with no provision for retain and release messages.
Foundation defines (and supplies) -copy and -release methods for
Blocks.
In the Objective-C and Objective-C++ languages, we allow the__weak specifier for __block variables of object type. If
garbage collection is not enabled, this qualifier causes these
variables to be kept without retain messages being sent. This
knowingly leads to dangling pointers if the Block (or a copy) outlives
the lifetime of this object.
In garbage collected environments, the __weak variable is set to
nil when the object it references is collected, as long as the__block variable resides in the heap (either by default or viaBlock_copy()). The initial Apple implementation does in fact
start __block variables on the stack and migrate them to the heap
only as a result of a Block_copy() operation.
It is a runtime error to attempt to assign a reference to a
stack-based Block into any storage marked __weak, including__weak __block variables.
C++ Extensions
Block literal expressions within functions are extended to allow const
use of C++ objects, pointers, or references held in automatic storage.
As usual, within the block, references to captured variables become
const-qualified, as if they were references to members of a const
object. Note that this does not change the type of a variable of
reference type.
For example, given a class Foo:
Foo foo;
Foo &fooRef = foo;
Foo *fooPtr = &foo;A Block that referenced these variables would import the variables as
const variations:
const Foo block_foo = foo;
Foo &block_fooRef = fooRef;
Foo *const block_fooPtr = fooPtr;Captured variables are copied into the Block at the instant of
evaluating the Block literal expression. They are also copied when
calling Block_copy() on a Block allocated on the stack. In both
cases, they are copied as if the variable were const-qualified, and
it's an error if there's no such constructor.
Captured variables in Blocks on the stack are destroyed when control
leaves the compound statement that contains the Block literal
expression. Captured variables in Blocks on the heap are destroyed
when the reference count of the Block drops to zero.
Variables declared as residing in __block storage may be initially
allocated in the heap or may first appear on the stack and be copied
to the heap as a result of a Block_copy() operation. When copied
from the stack, __block variables are copied using their normal
qualification (i.e. without adding const). In C++11, __block
variables are copied as x-values if that is possible, then as l-values
if not; if both fail, it's an error. The destructor for any initial
stack-based version is called at the variable's normal end of scope.
References to this, as well as references to non-static members of
any enclosing class, are evaluated by capturing this just like a
normal variable of C pointer type.
Member variables that are Blocks may not be overloaded by the types of
their arguments.
Clang Docs BoundsSafety (`clang/docs/BoundsSafety.md`)
`-fbounds-safety`: Enforcing bounds safety for C
Overview
NOTE: This is a design document and the feature is not available for users yet.
Please see {doc}BoundsSafetyImplPlans for more details.
-fbounds-safety is a C extension to enforce bounds safety to prevent
out-of-bounds (OOB) memory accesses, which remain a major source of security
vulnerabilities in C. -fbounds-safety aims to eliminate this class of bugs
by turning OOB accesses into deterministic traps.
The -fbounds-safety extension offers bounds annotations that programmers can
use to attach bounds to pointers. For example, programmers can add the__counted_by(N) annotation to parameter ptr, indicating that the pointer
has N valid elements:
void foo(int *__counted_by(N) ptr, size_t N);Using this bounds information, the compiler inserts bounds checks on every
pointer dereference, ensuring that the program does not access memory outside
the specified bounds. The compiler requires programmers to provide enough bounds
information so that the accesses can be checked at either run time or compile
time — and it rejects code if it cannot.
The most important contribution of -fbounds-safety is how it reduces the
programmer's annotation burden by reconciling bounds annotations at ABI
boundaries with the use of implicit wide pointers (a.k.a. "fat" pointers) that
carry bounds information on local variables without the need for annotations. We
designed this model so that it preserves ABI compatibility with C while
minimizing adoption effort.
The -fbounds-safety extension has been adopted on millions of lines of
production C code and proven to work in a consumer operating system setting. The
extension was designed to enable incremental adoption — a key requirement in
real-world settings where modifying an entire project and its dependencies all
at once is often not possible. It also addresses multiple of other practical
challenges that have made existing approaches to safer C dialects difficult to
adopt, offering these properties that make it widely adoptable in practice:
- It is designed to preserve the Application Binary Interface (ABI).
- It interoperates well with plain C code.
- It can be adopted partially and incrementally while still providing safety
benefits. - It is a conforming extension to C.
- Consequently, source code that adopts the extension can continue to be
compiled by toolchains that do not support the extension (CAVEAT: this still
requires inclusion of a header file macro-defining bounds annotations to
empty). - It has a relatively low adoption cost.
This document discusses the key designs of -fbounds-safety. The document is
subject to active updates with a more detailed specification.
Programming Model
Overview
-fbounds-safety ensures that pointers are not used to access memory beyond
their bounds by performing bounds checking. If a bounds check fails, the program
will deterministically trap before out-of-bounds memory is accessed.
In our model, every pointer has an explicit or implicit bounds attribute that
determines its bounds and ensures guaranteed bounds checking. Consider the
example below where the __counted_by(count) annotation indicates that
parameter p points to a buffer of integers containing count elements. An
off-by-one error is present in the loop condition, leading to p[i] being
out-of-bounds access during the loop's final iteration. The compiler inserts a
bounds check before p is dereferenced to ensure that the access remains
within the specified bounds.
void fill_array_with_indices(int *__counted_by(count) p, unsigned count) {
// off-by-one error (i < count)
for (unsigned i = 0; i <= count; ++i) {
// bounds check inserted:
// if (i >= count) trap();
p[i] = i;
}
}A bounds annotation defines an invariant for the pointer type, and the model
ensures that this invariant remains true. In the example below, pointer p
annotated with __counted_by(count) must always point to a memory buffer
containing at least count elements of the pointee type. Changing the value
of count, like in the example below, may violate this invariant and permit
out-of-bounds access to the pointer. To avoid this, the compiler employs
compile-time restrictions and emits run-time checks as necessary to ensure the
new count value doesn't exceed the actual length of the buffer. Section
Maintaining correctness of bounds annotations
provides more details about this programming model.
int g;
void foo(int *__counted_by(count) p, size_t count) {
count++; // may violate the invariant of __counted_by
count--; // may violate the invariant of __counted_by if count was 0.
count = g; // may violate the invariant of __counted_by
// depending on the value of `g`.
}The requirement to annotate all pointers with explicit bounds information could
present a significant adoption burden. To tackle this issue, the model
incorporates the concept of a "wide pointer" (a.k.a. fat pointer) – a larger
pointer that carries bounds information alongside the pointer value. Utilizing
wide pointers can potentially reduce the adoption burden, as it contains bounds
information internally and eliminates the need for explicit bounds annotations.
However, wide pointers differ from standard C pointers in their data layout,
which may result in incompatibilities with the application binary interface
(ABI). Breaking the ABI complicates interoperability with external code that has
not adopted the same programming model.
-fbounds-safety harmonizes the wide pointer and the bounds annotation
approaches to reduce the adoption burden while maintaining the ABI. In this
model, local variables of pointer type are implicitly treated as wide pointers,
allowing them to carry bounds information without requiring explicit bounds
annotations. Please note that this approach doesn't apply to function parameters
which are considered ABI-visible. As local variables are typically hidden from
the ABI, this approach has a marginal impact on it. In addition,-fbounds-safety employs compile-time restrictions to prevent implicit wide
pointers from silently breaking the ABI (see ABI implications of default bounds
annotations). Pointers associated with any other variables, including function
parameters, are treated as single object pointers (i.e., __single), ensuring
that they always have the tightest bounds by default and offering a strong
bounds safety guarantee.
By implementing default bounds annotations based on ABI visibility, a
considerable portion of C code can operate without modifications within this
programming model, reducing the adoption burden.
The rest of the section will discuss individual bounds annotations and the
programming model in more detail.
Bounds annotations
Annotation for pointers to a single object
The C language allows pointer arithmetic on arbitrary pointers and this has been
a source of many bounds safety issues. In practice, many pointers are merely
pointing to a single object and incrementing or decrementing such a pointer
immediately makes the pointer go out-of-bounds. To prevent this unsafety,-fbounds-safety provides the annotation __single that causes pointer
arithmetic on annotated pointers to be a compile time error.
__single: indicates that the pointer is either pointing to a single
object or null. Hence, pointers with__singledo not permit pointer
arithmetic nor being subscripted with a non-zero index. Dereferencing a__singlepointer is allowed but it requires a null check. Upper and lower
bounds checks are not required because the__singlepointer should point
to a valid object unless it's null.
__single is the default annotation for ABI-visible pointers. This
gives strong security guarantees in that these pointers cannot be incremented or
decremented unless they have an explicit, overriding bounds annotation that can
be used to verify the safety of the operation. The compiler issues an error when
a __single pointer is utilized for pointer arithmetic or array access, as
these operations would immediately cause the pointer to exceed its bounds.
Consequently, this prompts programmers to provide sufficient bounds information
to pointers. In the following example, the pointer on parameter p is
single-by-default, and is employed for array access. As a result, the compiler
generates an error suggesting to add __counted_by to the pointer.
void fill_array_with_indices(int *p, unsigned count) {
for (unsigned i = 0; i < count; ++i) {
p[i] = i; // error
}
}External bounds annotations
"External" bounds annotations provide a way to express a relationship between a
pointer variable and another variable (or expression) containing the bounds
information of the pointer. In the following example, __counted_by(count)
annotation expresses the bounds of parameter p using another parameter count.
This model works naturally with many C interfaces and structs because the bounds
of a pointer is often available adjacent to the pointer itself, e.g., at another
parameter of the same function prototype, or at another field of the same struct
declaration.
void fill_array_with_indices(int *__counted_by(count) p, size_t count) {
// off-by-one error
for (size_t i = 0; i <= count; ++i)
p[i] = i;
}External bounds annotations include __counted_by, __sized_by, and__ended_by. These annotations do not change the pointer representation,
meaning they do not have ABI implications.
__counted_by(N): The pointer points to memory that containsN
elements of pointee type.Nis an expression of integer type which can be
a simple reference to declaration, a constant including calls to constant
functions, or an arithmetic expression that does not have side effect. The__counted_byannotation cannot apply to pointers to incomplete types or
types without size such asvoid *. Instead,__sized_bycan be used to
describe the byte count.__sized_by(N): The pointer points to memory that containsNbytes.
Just like the argument of__counted_by,Nis an expression of integer
type which can be a constant, a simple reference to a declaration, or an
arithmetic expression that does not have side effects. This is mainly used for
pointers to incomplete types or types without size such asvoid *.__ended_by(P): The pointer has the upper bound of valueP, which is
one past the last element of the pointer. In other words, this annotation
describes a range that starts with the pointer that has this annotation and
ends withPwhich is the argument of the annotation.Pitself may be
annotated with__ended_by(Q). In this case, the end of the range extends
to the pointerQ. This is used for "iterator" support in C where you're
iterating from one pointer value to another until a final pointer value is
reached (and the final pointer value is not dereferenceable).
Accessing a pointer outside the specified bounds causes a run-time trap or a
compile-time error. Also, the model maintains correctness of bounds annotations
when the pointer and/or the related value containing the bounds information are
updated or passed as arguments. This is done by compile-time restrictions or
run-time checks (see Maintaining correctness of bounds annotations
for more detail). For instance, initializing buf with null while
assigning non-zero value to count, as shown in the following example, would
violate the __counted_by annotation because a null pointer does not point to
any valid memory location. To avoid this, the compiler produces either a
compile-time error or run-time trap.
void null_with_count_10(int *__counted_by(count) buf, unsigned count) {
buf = 0;
// This is not allowed as it creates a null pointer with non-zero length
count = 10;
}However, there are use cases where a pointer is either a null pointer or is
pointing to memory of the specified size. To support this idiom,-fbounds-safety provides *_or_null variants,__counted_by_or_null(N), __sized_by_or_null(N), and__ended_by_or_null(P). Accessing a pointer with any of these bounds
annotations will require an extra null check to avoid a null pointer
dereference.
Internal bounds annotations
A wide pointer (sometimes known as a "fat" pointer) is a pointer that carries
additional bounds information internally (as part of its data). The bounds
require additional storage space making wide pointers larger than normal
pointers, hence the name "wide pointer". The memory layout of a wide pointer is
equivalent to a struct with the pointer, upper bound, and (optionally) lower
bound as its fields as shown below.
struct wide_pointer_datalayout {
void* pointer; // Address used for dereferences and pointer arithmetic
void* upper_bound; // Points one past the highest address that can be
// accessed
void* lower_bound; // (Optional) Points to lowest address that can be
// accessed
};Even with this representational change, wide pointers act syntactically as
normal pointers to allow standard pointer operations, such as pointer
dereference (*p), array subscript (p[i]), member access (p->), and
pointer arithmetic, with some restrictions on bounds-unsafe uses.
-fbounds-safety has a set of "internal" bounds annotations to turn pointers
into wide pointers. These are __bidi_indexable and __indexable. When a
pointer has either of these annotations, the compiler changes the pointer to the
corresponding wide pointer. This means these annotations will break the ABI and
will not be compatible with plain C, and thus they should generally not be used
in ABI surfaces.
__bidi_indexable: A pointer with this annotation becomes a wide pointer
to carry the upper bound and the lower bound, the layout of which is
equivalent tostruct { T *ptr; T *upper_bound; T *lower_bound; };. As the
name indicates, pointers with this annotation are "bidirectionally indexable",
meaning that they can be indexed with either a negative or a positive offset
and the pointers can be incremented or decremented using pointer arithmetic. A__bidi_indexablepointer is allowed to hold an out-of-bounds pointer
value. While creating an OOB pointer is undefined behavior in C,-fbounds-safetymakes it well-defined behavior. That is, pointer
arithmetic overflow with__bidi_indexableis defined as equivalent of
two's complement integer computation, and at the LLVM IR level this meansgetelementptrwon't getinboundskeyword. Accessing memory using the
OOB pointer is prevented via a run-time bounds check.__indexable: A pointer with this annotation becomes a wide pointer
carrying the upper bound (but no explicit lower bound), the layout of which is
equivalent tostruct { T *ptr; T *upper_bound; };. Since__indexable
pointers do not have a separate lower bound, the pointer value itself acts as
the lower bound. An__indexablepointer can only be incremented or indexed
in the positive direction. Indexing it in the negative direction will trigger
a compile-time error. Otherwise, the compiler inserts a run-time
check to ensure pointer arithmetic doesn't make the pointer smaller than the
original__indexablepointer (Note that__indexabledoesn't have a
lower bound so the pointer value is effectively the lower bound). As pointer
arithmetic overflow will make the pointer smaller than the original pointer,
it will cause a trap at runtime. Similar to__bidi_indexable, an__indexablepointer is allowed to have a pointer value above the upper
bound and creating such a pointer is well-defined behavior. Dereferencing such
a pointer, however, will cause a run-time trap.__bidi_indexableoffers the best flexibility out of all the pointer
annotations in this model, as__bidi_indexablepointers can be used for
any pointer operation. However, this comes with the largest code size and
memory cost out of the available pointer annotations in this model. In some
cases, use of the__bidi_indexableannotation may be duplicating bounds
information that exists elsewhere in the program. In such cases, using
external bounds annotations may be a better choice.
__bidi_indexable is the default annotation for non-ABI visible pointers,
such as local pointer variables — that is, if the programmer does not specify
another bounds annotation, a local pointer variable is implicitly__bidi_indexable. Since __bidi_indexable pointers automatically carry
bounds information and have no restrictions on kinds of pointer operations that
can be used with these pointers, most code inside a function works as is without
modification. In the example below, int *buf doesn't require manual
annotation as it's implicitly int *__bidi_indexable buf, carrying the bounds
information passed from the return value of malloc, which is necessary to insert
bounds checking for buf[i].
void *__sized_by(size) malloc(size_t size);
int *__counted_by(n) get_array_with_0_to_n_1(size_t n) {
int *buf = malloc(sizeof(int) * n);
for (size_t i = 0; i < n; ++i)
buf[i] = i;
return buf;
}Annotations for sentinel-delimited arrays
A C string is an array of characters. The null terminator — the first null
character ('\0') element in the array — marks the end of the string.-fbounds-safety provides __null_terminated to annotate C strings and the
generalized form __terminated_by(T) to annotate pointers and arrays with an
end marked by a sentinel value. The model prevents dereferencing a__terminated_by pointer beyond its end. Calculating the location of the end
(i.e., the address of the sentinel value), requires reading the entire array in
memory and would have some performance costs. To avoid an unintended performance
hit, the model puts some restrictions on how these pointers can be used.__terminated_by pointers cannot be indexed and can only be incremented one
element at a time. To allow these operations, the pointers must be explicitly
converted to __indexable pointers using the intrinsic function__unsafe_terminated_by_to_indexable(P, T) (or__unsafe_null_terminated_to_indexable(P)) which converts the__terminated_by pointer P to an __indexable pointer.
__null_terminated: The pointer or array is terminated byNULLor0. Modifying the terminator or incrementing the pointer beyond it is
prevented at run time.__terminated_by(T): The pointer or array is terminated byTwhich is
a constant expression. Accessing or incrementing the