aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO45
-rw-r--r--docs/passes.txt7
-rw-r--r--docs/range_analysis.txt108
-rw-r--r--docs/sccvn.txt37
4 files changed, 197 insertions, 0 deletions
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..d4d9add
--- /dev/null
+++ b/TODO
@@ -0,0 +1,45 @@
+Conversion to SSA form, could maybe be done in two phases:
+1. Build up initial tree of block parameters, something like
+get_in(block b) {
+ b->visited = true
+ bitset gen = 0
+ bitset in = 0
+
+ for i in b->insns {
+ if i.in1 not in gen {
+ add i.in1 to in
+ }
+
+ if i.in2 not in gen {
+ add i.in2 to in
+ }
+
+ add i.out to gen
+ }
+
+ /* recursion, but presumably the user's programs are reasonable
+ * in size. Otherwise we can probably build up some piecewise
+ * construct that iterates normally until it converges or something
+ */
+ bitset s1 = get_in(b->s1)
+ bitset s2 = get_in(b->s2)
+
+ add from s1 not in gen to in
+ add from s2 not in gen to in
+}
+
+2. Within each block, allocate unique temporary variables.
+
+This should result in SSA form with block arguments?
+
+In SSA form, calculate (approximate) value ranges. Assign each location
+some value range, [min, max], and propagate functions that calculate the ranges
+wherever possible. Where min==max, the location can be replaced by min and the
+constants propagated. Generally already checked blocks would not need to be
+checked, unless one of the input parameters' range was undefined but became
+defined, or was a constant but became a range, or was positive/negative but the
+range expanded to be negative/positive as well.
+
+Do register allocation
+
+Do instruction generation
diff --git a/docs/passes.txt b/docs/passes.txt
new file mode 100644
index 0000000..7d527a2
--- /dev/null
+++ b/docs/passes.txt
@@ -0,0 +1,7 @@
+1. Input -> SSA block parameter form
+2. First ABI lowering
+3. Boolean expression to branch conditions wherever possible (also sort operands
+wherever possible? so instead of 5 + i2 it becomes i2 + 5 or something?)
+4. SCCVN (+ weak value range instead of just constant propagation?)
+5. Register allocation
+6. Instruction selection (pattern matching?)
diff --git a/docs/range_analysis.txt b/docs/range_analysis.txt
new file mode 100644
index 0000000..752a8ff
--- /dev/null
+++ b/docs/range_analysis.txt
@@ -0,0 +1,108 @@
+Here's an approximate order of steps that I'm thinking about:
+
+Block0():
+ n0 = 4
+ goto Block1(n0, 0)
+
+Block1(n1, i0):
+ j0 = i0 + n0
+ i1 = i0 + 1
+ i1 < 5 Block1(n1, i1) else Block2()
+
+Block2():
+ nop
+
+=>
+n0 = U
+n1 = U
+i0 = U
+j0 = U
+i1 = U
+
+=>
+n0 = [4, 4]
+n1 = U
+i0 = U
+j0 = U
+i1 = U
+
+=>
+n0 = [4, 4]
+n1 = [4, 4]
+i0 = [0, 0]
+j0 = U
+i1 = U
+
+=>
+n0 = [4, 4]
+n1 = [4, 4]
+i0 = [0, 0]
+j0 = [4, 4]
+i1 = U
+
+=>
+n0 = [4, 4]
+n1 = [4, 4]
+i0 = [0, 0]
+j0 = [4, 4]
+i1 = [1, 1]
+
+/* here's the branch */
+n1 = minmax([4, 4], [4, 4]) = [4, 4]
+i0 = minmax(i0, maxmin(i1, [-inf, 4])) = minmax(i0, [1, 4]) = [0, 4]
+i0 changed, so recalculate block?
+maxmin() here is to signify that the absolute highest value i0 could have
+is the branch condition value, with i1 giving the lowest possible value.
+Not entirely sure if that's all it takes, but at least this fairly trivial
+example seems to work. Should check how comparison results fare, like
+"b0 = r1 < 5
+ b0 == 1 Block0 ..."
+
+One (radical) option would be to ban comparisons completely and just force the
+compiler to output branches everywhere, at which point it would then probably be
+necessary to pattern match the hardware instructions or something to
+'reimplement' comparisons.
+
+=>
+n0 = [4, 4]
+n1 = [4, 4]
+i0 = [0, 4]
+j0 = [4, 4]
+i1 = [1, 1]
+
+=>
+n0 = [4, 4]
+n1 = [4, 4]
+i0 = [0, 4]
+j0 = [4, 8]
+i1 = [1, 5]
+
+/* here's the branch again */
+n1 = minmax([4, 4], [4, 4]) = [4, 4] // no change
+i0 = minmax(i0, maxmin(i1, [-inf, 4])) = minmax(i0, [1, 4]) = [0, 4]
+no change so don't recalculate block?
+
+
+===
+
+The above works alright, but the compiler is likely easier to implement if the
+optimizer accepts stuff like
+
+ b0 = i5 < 5
+ bnz b0 Block1(...) else Block2(...)
+
+bnz/bez are the weakest for of branch and doesn't provide any good info on
+limits (full blown SCEV as in GCC would likely be required for that), but we can
+add a pass that tries to move 'trivial' branches to
+
+ i5 < 5 Block1(...) else Block2(...)
+
+as this gives more context to the blocks that follow, even if just a little bit.
+Could still be useful for the range analysis.
+
+In the weak case, bnz/beq, we must perform a weak widening of all parameters in
+the blocks, i.e. if the new range max is larger, it must be increased to
+infinity, and if the new range min is smaller, it must be decreased to negative
+infinity. If any ranges changed, mark the block as not done yet and should be
+recomputed. Note that the weak widening applies to the stronger conditonal
+branches as well for all parameters except where the condition register is used.
diff --git a/docs/sccvn.txt b/docs/sccvn.txt
new file mode 100644
index 0000000..aea7d23
--- /dev/null
+++ b/docs/sccvn.txt
@@ -0,0 +1,37 @@
+Strongly connected components could be a cool pass to implement, it
+handles constant folding (seems like most passes have some support for constant
+folding?) and certain kinds of common subexpression elimination.
+
+The paper I'm mainly looking at is https://www.cs.rice.edu/~keith/Publications/SCCVN.pdf
+
+The algorithm (in short) is
+
+for all SSA names i
+ VN [i] <- T
+repeat
+ done <- TRUE
+ for all blocks b in reverse postorder
+ for all definitions x in b
+ temp <- lookup(x:op; VN[x[1]]; VN[x[2]]; x)
+ if VN[x] != temp
+ done <- FALSE
+ VN[x] <- temp
+ Remove all entries from the hash table
+until done
+
+Where lookup computes a unique hash for x, in C I guess it could just be
+op VN1 VN2
+32 64 64
+
+The lookup returns the name of the expression if found, otherwise adds the name x to
+the map. The paper extends this by adding a map where each SSA name also uses
+the constant propagation lattice (TODO: can we use a range lattice? or is that
+too much in one pass?)
+
+To make sure a + b and b + a are the same, I guess each expression node should
+sort its inputs.
+
+SCC is in the name, but I wonder if being strongly connected is necessary for
+the algorithm or if it only speeds up the computation as doing it in that order
+ensures values are 'final' when the block (or block group) is finished, whereas
+otherwise the whole graph seems to be iterated on excessively?