Static Single Assignment (SSA) Form (New!)
This language extension lets you represent Bril programs in static single assignment (SSA) form. An SSA-form Bril program contains only one assignment per variable, globally—that is, variables within a function cannot be reassigned.
This extension uses Filip Pizlo’s “phi/upsilon” variant of SSA.
In particular, it splits the traditional φ-node into a set
instruction and a get
instruction for communicating conditionally across control-flow edges.
(In the “Pizlo form” description, set
is called “upsilon” and get
is called “phi.”)
This extension is a successor to an older, deprecated “classic” SSA extension, which has a more standard-looking phi
instruction.
set
and get
Operations
Semantically, the instructions in this extension adds “shadow variables” to the state of the program.
Imagine a separate environment (a map from shadow variable names to ordinary values);
the set
instruction writes to shadow variables and get
reads from them.
Shadow variables obey a static single use (SSU) restriction, i.e., there may be only one get
in a function that reads from each shadow variable.
Here are the two main instructions in detail:
set
: An effect instruction that takes two arguments: a destination and a source. The instruction copies a value from the source, a “regular” variable, to the destination, a shadow variable. For example,set x y
takes the value from the ordinary variabley
and copies into the shadow variablex
.get
: A value instruction with zero arguments. The instruction gets the value from the shadow variable named by the destination and copies it to the regular variable of the same name. So, for example,x: int = get
copies the value from the shadow variablex
and puts it into the ordinary variablex
.
Examples
Here’s a simple example that uses set
and get
to copy something through a shadow variable:
one: int = const 1;
set shadow one;
shadow: int = get;
print shadow;
While set
and get
can appear anywhere in a program, the actual purpose is to put them around control flow edges.
Put set
at the end of one basic block and get
at the beginning of a successor block.
Here’s a small example:
a: int = const 5;
set c a;
br cond .here .there;
.here:
b: int = const 7;
set c b;
.there:
c: int = get;
print c;
You can think of these two set
instructions as “sending” two different values to the get
instruction for c
.
Because SSA allows only one assignment to every variable, get
s are also globally unique:
that is, every function can have at most one get
for each variable.
Undefined Values
When working with SSA form, it also turns out to be useful to explicitly propagate undefined values. So there is one more instruction in this extension:
undef
: A value instruction with zero arguments. It sets the destination variable, which may be of any type, to a special undef value.
The only thing you can do with this undef value is copy it to other variables with id
, set
, and get
.
It is an error to feed undef into any other operation.
In other words, in well-formed programs, all undef
instructions are transitively dead.
For example, this is legal:
x: float = undef;
y: float = id x;
But this is not:
x: float = undef;
print x;
Support
The reference interpreter supports programs in SSA form because it can faithfully execute set
, get
, and undef
.
This extension is also supported in the fast Rust-based interpreter and the type checker.