Original article address: Workshop. SSA is an efficient data stream analysis technology. Currently, almost all modern compilers, such as GCC, open64, and llvm, support the SSA technology, not just the compiler, jikes rvm, hotspot JVM ,. net mono, Python pypy, and andoroid Dalvik. Just-in-time compiler in these virtual machines/interpreters also supports SSA.
The next-generation JavaScript Engine ionmonkey of Firefox will also introduce SSA to its JIT.
As you can see, SSA is available in almost all popular compilers, interpreters, and virtual machines for popular languages.
Table of contents
- 1. What is SSA?
- 2. Role of SSA
- 2.1 SSA and register allocation
- 3. Ssa Conversion
- 3.1 from the normal middle to the SSA
- 3.2 From SSA to normal intermediate representation
- 4. SSA in more complex situations
- 4.1 aliases such as arrays and pointers
- 4.2 storage on stacks
- 4.3 Composite Structure-struct
- 5. SSA in GCC
- 6. SSA in open64
- 7. Related Documents
1. What is SSA?
SSA is the static single assignment, static single-assignment, which is an intermediate representation. It is called single assignment because each name is assigned only once in SSA.
For example, the control flow diagram of a program in. From this figure, we can see that the definition of the Y value in the last basic block is either from the left branch or from the right branch.
Ssa_example1.1
After a variable in each value assignment statement is assigned a unique name, the new name is generally in the form of the original variable + version. The preceding control flow diagram is in the following format:
Ssa_example1.2
There is a problem in this figure. When there is a branch, if there is a variable operation in the branch, you cannot determine which version of the variable is used. Therefore, Phi nodes are introduced. As shown in:
Ssa_example1.3
Phi connects Y1 and Y2 in the Branch and generates a new definition y3. With the Phi node, the Y3 definition in the last basic block comes from the previous Phi node. The two operands Y1 and Y2 in the Phi node come from the left and right branches respectively.
2. Role of SSA
In the SSA intermediate representation, each used variable can have a unique definition, that is, SSA can bring accurate use-definition relationships. The Y value definition in Figure ssa_example1.1 is very vague.
In summary, SSA has four benefits:
- Because each variable has a unique definition, the data stream analysis and optimization algorithm can be simpler.
- Use-defines the space consumed by the relationship chain from exponential growth to linear growth. If a variable has n usages and M definitions, and SSA is not used, m × n usage-definition relationships exist.
- Because of the more precise relationship between the use and definition, SSA can simplify the algorithm for constructing interference graphs.
- In the source program, the use of the same variable is changed to the use of different variables in the form of SSA. Therefore, many unnecessary dependencies can be eliminated.
With precise object usage-definition relationships, many optimization using-definition relationships can be more precise, thorough, and efficient. For example
- Constant Propagation
- Delete dead code
- Global
- Partial redundancy Deletion
- Strength weakened
- Register Allocation
2.1 SSA and register allocation
Because SSA makes dependency analysis simpler and more accurate, and variables in the Phi node cannot be active at the same time. Therefore, it can assist in completing register allocation in the form of SSA. In fact, the earliest SSA in GCC is the RTL stage in GCC 3.
3. Ssa Conversion
After talking about the advantages of SSA, we will introduce the general method for the compiler to build SSA.
3.1 from the normal middle to the SSA
Two-step strategy:
- Insert Phi node: the Phi node must be inserted at the aggregation point (joint point) of the control flow chart. As long as there is a change to a variable in the branch before the aggregation point, you need to insert the Phi node for this variable in this aggregation point. The operand of the Phi node is a new variable in the branch path.
- Rename a variable: After the Phi node is inserted, all the definitions for the variables in SSA are ready. Next, rename the variables in the definition and replace the corresponding variables.
In addition, to save memory space and simplify the SSA algorithm, we need to minimize the number of inserted Phi nodes. Because Phi nodes are only conceptual nodes, if too many unnecessary Phi nodes are inserted, the algorithm needs to analyze each branch at the aggregation point of the control flow chart. The dominance frontier can be used to digest the maximum number of Phi nodes. Generally, Phi nodes are inserted by directly calculating the control boundary.
3.2 From SSA to normal intermediate representation
Why do we need to switch back from SSA? The processor cannot directly perform the operations corresponding to the Phi node. The simplest method is to copy data directly, as shown in:
Out of SSA
But there is a problem, such. A simple copy algorithm may change the semantics of the Code:
Out of SSA Problem
Correct practice:
- Rename the operand and result of the Phi node so that the node name is the same.
- Insert the copy operation to the branch.
4. More complex ssa4.1 arrays, pointers, and other aliases
The above discussion about SSA is basically about the SSA operation for a single simple variable. What should SSA do for complex access to pointers, arrays, and so on? Arrays and pointers make the compiler unable to determine the specific variables of define and use.
Reference 7 provides a definition method. By introducing maydef, mayuse, and zero versions, the compiler can also perform SSA Analysis on programs with aliases (pointers and arrays. If a value is assigned to the region indicated by the pointer, maydef is inserted here, which indicates that the variable may be defined. Similarly, a mayuse is inserted for the value that points to the region using the pointer. The variable to which the pointer is directed cannot be determined. For correctness, You need to insert the maydef action to all variables. Mayuse also applies to all variables.
When there are many pointer operations, this method will introduce too many new variable versions. Therefore, zero version is added. The role of zero version is to minimize the number of versions brought by maydef. Use the same zero version for those that are likely not to be aliases. For example, if a variable uses maydef to generate a new version, if there is a new maydef operation, zero version is generated directly without generating a new version.
4.2 storage on stacks
The storage space allocated on the heap. Generally, the compiler regards the whole heap as an object for SSA.
4.3 Composite Structure-struct
Because struct is composed of many elements, there are two ways to handle it: Think of struct as a whole for SSA, and think of each element of struct as an object for SSA. Compared with the former, the latter achieves better optimization in programs with frequent struct operations because of its finer scores.
5. SSA in GCC
GCC SSA
- Tree-ssa.c
- Tree-into-ssa.c: converts the function to the form of SSA, inserts the Phi node, and gives warnings for uninitialized variables.
- Tree-ssa-dce.c: scans the entire function, marking statements that have no side effects and the results are not used, and all storage operations are considered to have side effects.
- Tree-ssa-dom.c: dominated relationship related optimization: re-writing propagation, constant propagation, expression simplification, Redundancy Elimination, jump threading?
- Tree-ssa-forwprop.c: the Forward propagation of a single referenced variable attempts to easily Delete by replacing a variable that is used only once with a corresponding expression.
- Tree-ssa-copyrename.c: Try to replace the SSA variable generated by the copy action with the original variable to optimize the symbol table.
- Tree-ssa-phiopt.c: identifies the Phi node that expresses the conditional expression and overwrites it to line code.
- Tree-ssa-alias.c: Based on SSA, the possible-alias, certain-alias and escape analysis information is obtained. This information will be used to promote variables from available objects in the memory address to non-alias variables so that these variables can be analyzed and optimized in the form of SSA.
- Tree-ssa-structalias.c: used for inter-process pointing analysis.
- Tree-sra.c: converts a suitable local composite variable without alias to a scalar set and then to the SSA form.
- Tree-ssa-dse.c: removing unwanted storage operations
- Tree-ssa-sink.c: Sink storage and assignment statements as close as possible to their usage points.
- Tree-ssa-pre.c: Partial redundancy deletion, load statement movement, full redundancy Deletion
- Tree-ssa-loop.c: cyclic optimization in the form of SSA
- Tree-ssa-loop-im.c: loop-independent statement Movement
- Tree-ssa-loop-ivcanon.c: Cycle Standardization
- Tree-ssa-loop-ivopts.c: Index Variable Optimization
- Tree-ssa-loop-unswitch.c: Jump loop-independent conditions out of the loop
- Tree-vectorizer.c, tree-vect-analyze.c, tree-vect-transform.c: auto vectorization
- Tree-ssa-ccp.c: Conditional constant propagation
- Tree-ssa-copy.c: Conditional reproduction Propagation
- Tree-vrp.c: value range propagation
- Tree-outof-ssa.c: Convert from SSA form back to normal form
6. SSA in open64
The SSA in open64 is mainly used for loop nested optimization, inter-process optimization, and common function optimization. All machine-independent optimizations except Cyclic Transformation and inline optimization are based on SSA. This part can be said to be an important selling point of open64, and the corresponding code is under Osprey/be/OPT.
When there is no inter-process optimization, open64 mainly uses functions as the unit to construct the SSA Based on the information obtained by the control flow diagram and alias analysis.
- Opt_goto.cxx: Convert the GOTO statement to facilitate SSA
- Opt_loop.cxx: cyclic Regularization
- Opt_sym.cxx: Create a table of related symbols
- Opt_alias_class.cxx: alias classification for convenient alias Analysis
- Opt_cfg.cxx: Build a control flow chart, including a control tree, which cannot be recognized by code, and if statement Conversion
- Opt_tail.cxx: Elimination of tail recursion
- Opt_alias_analysis.cxx: Stream-independent alias Analysis
- Opt_ssa.cxx: Build a whirl-based SSA
- Opt_dse.cxx: deleted from the dead store
- Opt_htable.cxx: Build HSSA-hash-based global value serial number SSA
- Opt_ivr.cxx: standardized index Variables
- Opt_prop.cxx: reproduction Propagation
- Opt_revise_ssa.cxx: expands a non-direct variable into a direct variable.
- Opt_dce.cxx: delete dead code
- Opt_cfg_trans.cxx: control flow Conversion
- Opt_rename.cxx: Rename and update the SSA variable
- Opt_du.cxx: Build define-use information
- Opt_etable.cxx: expression-based redundant Deletion
- Opt_estr.cxx: strength weakened
- Opt_ehoist.cxx: code upgrade
- Opt_lftr2.cxx: linear code test and replacement
- Opt_vn.cxx: delete full redundancy based on value numbers
- Opt_ltable.cxx: Partial redundancy deletion for Load
- Opt_stable.cxx: store partial Redundancy Elimination partially deletes store Redundancy
- Opt_bdce.cxx: bitwise dead code elimination-for struct
- Opt_htable_emit.cxx: indicates the conversion from SSA to whirl.
7. Related Documents
- Http://en.wikipedia.org/wiki/Static_single_assignment_form
- Http://en.wikipedia.org/wiki/SpiderMonkey_%28JavaScript_engine%29
- Advanced compiler design and implementation, by Steven S. muchnick, page 252-265
- Modern compiler implementation in C, by Andrew W. Appel, page 433-473
- Crafting a compiler, by Charles N. Fischer, etc, page 410-414
- 8. Static Single Assignment form, by Marcus Denker,
Http://marcusdenker.de/talks/08CC/08IntroSSA.pdf
- Valid tive representation of aliases and indirect memory operations in SSA form, Fred Chow, Sun Chan, etc. Compiler Construction 1996