go/src/cmd/compile/internal/ssa
Joel Sing 9439a7d87f cmd/compile: use SEQZ pseudo instruction in RISCV64 boolean rules
This makes the intent clearer, allows for another ellipsis and will aid
in future rewriting. While here, document boolean loads to explain register
contents.

Change-Id: I933db2813826d88819366191fbbea8fcee5e4dda
Reviewed-on: https://go-review.googlesource.com/c/go/+/230120
Reviewed-by: Keith Randall <khr@golang.org>
2020-05-02 18:10:49 +00:00
..
gen cmd/compile: use SEQZ pseudo instruction in RISCV64 boolean rules 2020-05-02 18:10:49 +00:00
testdata cmd/compile: change the "bogus line" to be 1 2020-01-17 23:14:48 +00:00
addressingmodes.go cmd/compile: add indexed memory modification ops to amd64 2020-04-30 17:21:31 +00:00
biasedsparsemap.go cmd/compile: index line number tables by source file to improve sparsity 2019-05-14 18:48:16 +00:00
block.go cmd/compile: add ssa.Block.truncateValues 2020-04-23 14:59:55 +00:00
branchelim.go cmd/compile: optimize integer-in-range checks 2020-03-03 14:30:26 +00:00
branchelim_test.go cmd/compile/internal/ssa: add Op{SP,SB} type checks to check.go 2018-04-24 15:51:15 +00:00
cache.go cmd/compile: re-use regalloc's []valState 2019-05-10 00:14:40 +00:00
check.go cmd/compile: make some s390x rules use strongly typed aux values 2020-04-17 14:54:05 +00:00
checkbce.go cmd/compile: enable optimizer logging for bounds checking 2019-11-10 17:12:35 +00:00
compile.go cmd/compile: restore missing columns in ssa.html 2020-04-05 23:23:03 +00:00
config.go cmd/compile: start implementing strongly typed aux and auxint fields 2020-04-09 21:18:55 +00:00
copyelim.go cmd/compile: allow multiple SSA block control values 2019-10-02 09:56:36 +00:00
copyelim_test.go cmd/compile: change ssa.Type into *types.Type 2017-05-09 23:01:51 +00:00
critical.go cmd/compile: move Frontend field from ssa.Config to ssa.Func 2017-03-17 23:18:57 +00:00
cse.go cmd/compile: fix liveness for open-coded defer args for infinite loops 2019-11-05 17:19:16 +00:00
cse_test.go cmd/compile: add GCNode to VarDef in tests 2019-05-29 01:10:13 +00:00
deadcode.go cmd/compile: add ssa.Block.truncateValues 2020-04-23 14:59:55 +00:00
deadcode_test.go cmd/compile: change ssa.Type into *types.Type 2017-05-09 23:01:51 +00:00
deadstore.go cmd/compile, cmd/link, runtime: make defers low-cost through inline code and extra funcdata 2019-10-24 13:54:11 +00:00
deadstore_test.go cmd/compile/internal/ssa: add Op{SP,SB} type checks to check.go 2018-04-24 15:51:15 +00:00
debug.go cmd/link: revert/revise CL 98075 because LLDB is very picky now 2019-04-23 20:52:23 +00:00
debug_test.go cmd/compile/internal/ssa: skip TestNexting 2020-04-08 18:59:28 +00:00
decompose.go cmd/compile: decompose composite OpArg before decomposeUser 2018-11-23 22:59:32 +00:00
dom.go cmd/compile: simplify postorder 2019-08-29 19:54:46 +00:00
dom_test.go cmd/compile: change ssa.Type into *types.Type 2017-05-09 23:01:51 +00:00
export_test.go cmd/compile: start implementing strongly typed aux and auxint fields 2020-04-09 21:18:55 +00:00
flagalloc.go cmd/compile: remove dead values after flagalloc 2020-04-23 19:40:07 +00:00
func.go cmd/compile: fix liveness for open-coded defer args for infinite loops 2019-11-05 17:19:16 +00:00
func_test.go cmd/compile: index line number tables by source file to improve sparsity 2019-05-14 18:48:16 +00:00
fuse.go cmd/compile: use fuse to implement shortcircuit loop 2020-04-20 19:36:50 +00:00
fuse_comparisons.go cmd/compile: optimize integer-in-range checks 2020-03-03 14:30:26 +00:00
fuse_test.go cmd/compile: optimize integer-in-range checks 2020-03-03 14:30:26 +00:00
html.go cmd/compile: fix misalignment in sources column of generated ssa.html 2020-04-24 02:13:27 +00:00
id.go cmd/compile: in a Tarjan algorithm, DFS should really be DFS 2016-04-22 19:21:16 +00:00
layout.go cmd/compile: lay out exit post-dominated blocks at the end 2020-04-06 16:08:18 +00:00
lca.go cmd/compile: remove redundant function idom 2016-10-11 16:43:12 +00:00
lca_test.go cmd/compile: remove a few unused bits of code 2019-09-01 14:25:12 +00:00
likelyadjust.go cmd/compile: fix liveness for open-coded defer args for infinite loops 2019-11-05 17:19:16 +00:00
location.go cmd/compile: dense numbering for GP registers 2018-05-22 15:55:01 +00:00
loopbce.go cmd/compile: remove Greater* and Geq* generic integer ops 2020-02-26 13:11:53 +00:00
loopreschedchecks.go cmd/compile: simplify postorder 2019-08-29 19:54:46 +00:00
looprotate.go cmd/compile: make loop finder more aware of irreducible loops 2017-10-05 18:49:10 +00:00
lower.go cmd/compile,runtime: redo mid-stack inlining tracebacks 2018-12-28 20:55:36 +00:00
magic.go cmd/compile: convert constant divide strength reduction rules to typed aux 2020-04-29 16:45:32 +00:00
magic_test.go cmd/compile: add signed divisibility rules 2019-04-30 22:02:07 +00:00
nilcheck.go cmd/compile: add ssa.Block.truncateValues 2020-04-23 14:59:55 +00:00
nilcheck_test.go cmd/compile: optimize integer-in-range checks 2020-03-03 14:30:26 +00:00
numberlines.go cmd/compile: add specialized Value reset for OpCopy 2020-03-02 16:24:47 +00:00
op.go cmd/compile: convert splitload rules to typed aux 2020-04-23 17:52:48 +00:00
opGen.go cmd/compile: switch to typed auxint for arm64 TBZ/TBNZ block 2020-04-30 17:30:54 +00:00
opt.go cmd/compile: repair name propagation into aggregate parts 2017-11-05 17:30:11 +00:00
passbm_test.go cmd/compile/internal/ssa: add Op{SP,SB} type checks to check.go 2018-04-24 15:51:15 +00:00
phielim.go cmd/compile: move Frontend field from ssa.Config to ssa.Func 2017-03-17 23:18:57 +00:00
phiopt.go cmd/compile: use correct types in phiopt 2020-02-29 17:02:29 +00:00
poset.go cmd/compile: in poset, implement path collapsing 2019-10-26 07:52:35 +00:00
poset_test.go cmd/compile: in poset, implement path collapsing 2019-10-26 07:52:35 +00:00
print.go cmd/compile: combine ssa.html columns with identical contents 2020-04-01 03:54:44 +00:00
prove.go cmd/compile: use only bit patterns in isNonNegative 2020-03-09 20:19:25 +00:00
README.md cmd/compile: allow multiple SSA block control values 2019-10-02 09:56:36 +00:00
redblack32.go cmd/compile: remove some redundant types in decls 2017-09-17 09:51:38 +00:00
redblack32_test.go cmd/compile/internal/ssa: simplify s = s <op> x to s <op>= x 2019-08-31 02:52:13 +00:00
regalloc.go cmd/compile: error if register is reused when setting edge state 2020-04-14 19:04:38 +00:00
regalloc_test.go cmd/compile: do not allow regalloc to LoadReg G register 2018-05-30 16:39:21 +00:00
rewrite.go cmd/compile: switch to typed auxint for arm64 TBZ/TBNZ block 2020-04-30 17:30:54 +00:00
rewrite386.go cmd/compile: CSE the RHS of rewrite rules 2020-04-24 16:44:20 +00:00
rewrite386splitload.go cmd/compile: convert splitload rules to typed aux 2020-04-23 17:52:48 +00:00
rewrite_test.go cmd/compile: remove nlz function 2020-04-21 18:15:14 +00:00
rewriteAMD64.go cmd/compile: move duffcopy auxint calculation out of rewrite rules 2020-04-24 23:56:54 +00:00
rewriteAMD64splitload.go cmd/compile: splitload (CMPconst [0] x) into (TEST x x) on amd64 2020-04-23 19:39:25 +00:00
rewriteARM.go cmd/compile: CSE the RHS of rewrite rules 2020-04-24 16:44:20 +00:00
rewriteARM64.go cmd/compile: switch to typed auxint for arm64 TBZ/TBNZ block 2020-04-30 17:30:54 +00:00
rewritedec.go cmd/compile: rewrite dec rules to use typed aux field 2020-04-23 17:25:43 +00:00
rewritedec64.go cmd/compile: CSE the RHS of rewrite rules 2020-04-24 16:44:20 +00:00
rewritedecArgs.go cmd/compile: rewrite decArgs rules to use typed aux field 2020-04-27 16:18:30 +00:00
rewritegeneric.go cmd/compile: move last of the generic rules to typed aux 2020-04-29 22:40:20 +00:00
rewriteMIPS.go cmd/compile: CSE the RHS of rewrite rules 2020-04-24 16:44:20 +00:00
rewriteMIPS64.go cmd/compile: adjust MIPS64x rewrite rules to use typed aux fields 2020-04-29 04:16:08 +00:00
rewritePPC64.go cmd/compile,cmd/internal/obj/ppc64: use mod instructions on power9 2020-04-29 14:45:56 +00:00
rewriteRISCV64.go cmd/compile: use SEQZ pseudo instruction in RISCV64 boolean rules 2020-05-02 18:10:49 +00:00
rewriteS390X.go cmd/compile: adopt strong aux typing for some s390x rules 2020-04-30 13:46:20 +00:00
rewriteWasm.go cmd/compile: CSE the RHS of rewrite rules 2020-04-24 16:44:20 +00:00
schedule.go cmd/compile: add more non-ID comparisons to schedule 2020-04-24 21:13:36 +00:00
schedule_test.go cmd/compile: change ssa.Type into *types.Type 2017-05-09 23:01:51 +00:00
shift_test.go cmd/compile/internal/ssa: add Op{SP,SB} type checks to check.go 2018-04-24 15:51:15 +00:00
shortcircuit.go cmd/compile: use fuse to implement shortcircuit loop 2020-04-20 19:36:50 +00:00
shortcircuit_test.go cmd/compile: change ssa.Type into *types.Type 2017-05-09 23:01:51 +00:00
sizeof_test.go all: remove the nacl port (part 1) 2019-10-09 06:14:44 +00:00
softfloat.go cmd/compile: use correct store types in softfloat 2018-11-23 17:16:36 +00:00
sparsemap.go all: update comment URLs from HTTP to HTTPS, where possible 2018-06-01 21:52:00 +00:00
sparseset.go all: update comment URLs from HTTP to HTTPS, where possible 2018-06-01 21:52:00 +00:00
sparsetree.go cmd/compile: fix liveness for open-coded defer args for infinite loops 2019-11-05 17:19:16 +00:00
sparsetreemap.go cmd/compile: remove redundant function idom 2016-10-11 16:43:12 +00:00
stackalloc.go all: fix a bunch of misspellings 2018-10-06 15:40:03 +00:00
stackframe.go cmd/compile: move Frontend field from ssa.Config to ssa.Func 2017-03-17 23:18:57 +00:00
stmtlines_test.go cmd/compile: print lines missing is_stmt if testing.Verbose() 2019-08-30 20:11:59 +00:00
tighten.go cmd/compile: allow multiple SSA block control values 2019-10-02 09:56:36 +00:00
TODO cmd/compile: update SSA TODO file 2018-04-24 23:35:13 +00:00
trim.go cmd/compile: attempt to preserve statement marks when empty blocks are trimmed. 2019-10-04 17:31:51 +00:00
value.go cmd/compile: make some s390x rules use strongly typed aux values 2020-04-17 14:54:05 +00:00
writebarrier.go cmd/compile: simplify readonly sym checks in writebarrier pass 2020-04-28 19:49:53 +00:00
writebarrier_test.go cmd/compile/internal/ssa: add Op{SP,SB} type checks to check.go 2018-04-24 15:51:15 +00:00
xposmap.go cmd/compile: index line number tables by source file to improve sparsity 2019-05-14 18:48:16 +00:00
zcse.go cmd/compile: simplify zcse 2020-04-23 14:59:41 +00:00
zeroextension_test.go cmd/compile/internal/ssa: refactor zeroUpper32Bits 2018-02-27 20:38:32 +00:00

Introduction to the Go compiler's SSA backend

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

Key concepts

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

Memory types

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

Functions

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in gen/generic.rules and gen/rulegen.go.

Similarly, the code to manage operators is also code generated from gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, see gen/README for instructions on how to generate the Go code again.