diff options
| author | Dibyendu Majumdar <mobile@majumdar.org.uk> | 2023-10-20 15:21:08 -0700 |
|---|---|---|
| committer | Dibyendu Majumdar <mobile@majumdar.org.uk> | 2024-10-23 17:51:09 -0700 |
| commit | 83fdf059150e37c335a86e3dd4547a76a1c588db (patch) | |
| tree | 7df24a5366647c8f9cf083ead43ca106f1d23430 | |
| parent | 0e8916e1a4ae550357401b37df6fdca683c221f7 (diff) | |
| download | Simple-83fdf059150e37c335a86e3dd4547a76a1c588db.tar.gz Simple-83fdf059150e37c335a86e3dd4547a76a1c588db.zip | |
Chapter 4: External Argument and Comparisonslinear-chapter04
Co-authored-by: Cliff Click <cliffc@acm.org>
Co-authored-by: Yichen Yan <oraluben@outlook.com>
Co-authored-by: Thalia Archibald <thalia@archibald.dev>
Co-authored-by: Tamas Hadhazy <helsgithub15@gmail.com>
Co-authored-by: Hels15 <helsgithub15@gmail.com>
Co-authored-by: XmiliaH <45106915+XmiliaH@users.noreply.github.com>
39 files changed, 2424 insertions, 73 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..c7724bc7 --- /dev/null +++ b/Makefile @@ -0,0 +1,98 @@ +SHELL := /bin/bash +.DELETE_ON_ERROR: + +# for printing variable values +# usage: make print-VARIABLE +# > VARIABLE = value_of_variable +print-% : ; @echo $* = $($*) + +# literal space +space := $() $() + +# Decide OS-specific questions +# jar-file seperator +ifeq ($(OS),Windows_NT) + SEP = ; +else + SEP = : +endif +# Find a reasonable ctags. +CTAGS = $(shell which ctags) +# Hack for MacOS: /usr/bin/ctags is unfriendly, so look for ctags from brew +ifeq ($(UNAME),Darwin) + CTAGS = $(shell brew list ctags 2> /dev/null | grep bin/ctags) +endif + +# Fun Args to javac. Mostly limit to java8 source definitions, and fairly +# aggressive lint warnings. +JAVAC_ARGS = -g + +# Source code +SIMPLE := com/seaofnodes/simple +SRC := src/main/java +TST := src/test/java +CLZDIR:= build/classes +main_javas := $(wildcard $(SRC)/$(SIMPLE)/*java $(SRC)/$(SIMPLE)/*/*java) +test_javas := $(wildcard $(TST)/$(SIMPLE)/*java $(TST)/$(SIMPLE)/*/*java) +main_classes := $(patsubst $(SRC)/%java,$(CLZDIR)/main/%class,$(main_javas)) +test_classes := $(patsubst $(TST)/%java,$(CLZDIR)/test/%class,$(test_javas)) +test_cp := $(patsubst $(TST)/$(SIMPLE)/%.java,com.seaofnodes.simple.%,$(wildcard $(TST)/$(SIMPLE)/*Test.java)) +classes = $(main_classes) $(test_classes) +# All the libraries +libs = $(wildcard lib/*jar) +jars = $(subst $(space),$(SEP),$(libs)) + + +default_targets := $(main_classes) $(test_classes) +# Optionally add ctags to the default target if a reasonable one was found. +ifneq ($(CTAGS),) +default_targets += tags +endif + +default: $(default_targets) + +# Compile just the out-of-date files +$(main_classes): build/classes/main/%class: $(SRC)/%java + @echo "compiling " $@ " because " $? + @[ -d $(CLZDIR)/main ] || mkdir -p $(CLZDIR)/main + @javac $(JAVAC_ARGS) -cp "$(CLZDIR)/main$(SEP)$(jars)" -sourcepath $(SRC) -d $(CLZDIR)/main $(main_javas) + +$(test_classes): $(CLZDIR)/test/%class: $(TST)/%java $(main_classes) + @echo "compiling " $@ " because " $? + @[ -d $(CLZDIR)/test ] || mkdir -p $(CLZDIR)/test + @javac $(JAVAC_ARGS) -cp "$(CLZDIR)/test$(SEP)$(CLZDIR)/main$(SEP)$(jars)" -sourcepath $(TST) -d $(CLZDIR)/test $(test_javas) + +# Base launch line for JVM tests +JVM=nice java -ea -cp "build/classes/main${SEP}${jars}${SEP}$(CLZDIR)/test" + +tests: $(default_targets) + @echo "testing " $(test_cp) + @$(JVM) org.junit.runner.JUnitCore $(test_cp) + +fuzzer: $(default_targets) + @echo "fuzzing " $(test_cp) + @$(JVM) org.junit.runner.JUnitCore com.seaofnodes.simple.FuzzerWrap + + +.PHONY: clean +clean: + rm -rf build + rm -f TAGS + (find . -name "*~" -exec rm {} \; 2>/dev/null; exit 0) + +# Download libs from maven +lib: lib/junit-4.12.jar lib/hamcrest-core-1.3.jar + +# Jars +lib/junit-4.12.jar: + @[ -d lib ] || mkdir -p lib + @(cd lib; wget https://repo1.maven.org/maven2/junit/junit/4.12/junit-4.12.jar) + +lib/hamcrest-core-1.3.jar: + @[ -d lib ] || mkdir -p lib + @(cd lib; wget https://repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar) + +# Build emacs tags (part of a tasty emacs ide experience) +tags: $(main_javas) $(test_javas) + @rm -f TAGS + @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST) @@ -31,7 +31,7 @@ The Simple language will be styled after a subset of C or Java with constants. Peephole optimization / simple constant folding. * [Chapter 3](docs/chapter03/README.md): Local variables, and assignment statements. Read on RHS, SSA, more peephole optimization if local is a constant. -* Chapter 4: A non-constant external variable input named `arg`. Binary and Comparison operators involving constants and `arg`. Non-zero values will be truthy. Peephole optimizations involving algebraic simplifications. +* [Chapter 4](docs/chapter04/README.md): A non-constant external variable input named `arg`. Binary and Comparison operators involving constants and `arg`. Non-zero values will be truthy. Peephole optimizations involving algebraic simplifications. * Chapter 5: `if` statement. CFG construction. * Chapter 6: Peephole optimization around dead control flow. * Chapter 7: `while` statement. Looping construct - eager phi approach. diff --git a/appendix/dce.md b/appendix/dce.md new file mode 100644 index 00000000..ad5be2d0 --- /dev/null +++ b/appendix/dce.md @@ -0,0 +1,166 @@ +# Appendix +## Dead Code Elimination(DCE) +Dead Code Elimination is the process or removing nodes that are dead(unused). + +There are two kinds of dead code eliminations present in this chapter. +- Delete unused nodes when popping the scope. +- Delete old nodes after finding a better replacement(after idealize call). + +Both of the methods mentioned above delete the nodes recursively. However, the first case happens +regardless of whether `peephole` is called or not. + +### Delete unused nodes when popping the scope +A node is unused if it has no outputs(users). + +### Example 1: + ``` + int a = 12; // the only output it has is the scope itself + return 1; + ``` +We clearly see `a` is unused. +This gives us the following graph(without DCE): + + + +To remove `a` we introduce the following technique when popping the scope. + + ```java + var ret = (ReturnNode) parseBlock(); + _scope.pop(); + ``` +As of now the end of the program is hard-wired to return, after finishing parsing we pop the scope. +The state of the scope before popping it: + ``` + _scope = {ScopeNode@1422} "Scope[$ctrl:null, arg:arg][a:12]" + 1 = {ProjNode@1528} "arg" + 2 = {ConstantNode@1529} "12" + _inputs = {ArrayList@1523} size = 3 + ``` +The output(s) of `a` + ``` + _outputs = {ArrayList@1544} size = 1 + 0 = {ScopeNode@1422} "Scope[$ctrl:null, arg:arg][a:12]" + ``` +Notice how the only output of `a`is the scope itself. When `a` is introduced, it is added to the +inputs of the scope, and the scope is added to the outputs of `a`(recall use-def chains). + +Since `a` is not being returned - it is unused and can be killed. + +`_scope.pop`: +```java +public void pop() { popN(_scopes.pop().size()); } +``` +`_scope.popN`(responsible for killing the unused nodes *recursively*): +```java +void popN(int n) { + for (int i = 0; i < n; i++) { + Node old_def = _inputs.removeLast(); // start from the back + if (old_def != null && // If it exists and + old_def.delUse(this)) { // If we removed the last use, the old def is now dead + old_def.kill(); // Kill the old def + } + } +} +``` +First, we start off by looping through the inputs of the scope(*remember*: scope is just a node like any other, the symbols of the scope are the inputs). +If the node returned by `removeLast` is not null, and if the only user of `old_def` is the current Node then we can delete it. + +`old_def.delUse(this)` - delete *this* from `old_def`'s outputs. +```java +protected boolean delUse( Node use ) { + Utils.del(_outputs, Utils.find(_outputs, use)); + return _outputs.isEmpty(); + } +``` +Returns true if `_outputs` becomes empty after deleting `use`. +`old_def.kill()` starts the killing process recursively. + +`old_def.kill()` +```java +public void kill( ) { + assert isUnused(); // Has no uses, so it is dead + popN(nIns()); // Set all inputs to null, recursively killing unused Nodes + _type=null; // Flag as dead + assert isDead(); // Really dead now + } +``` +`popN` is called again, our goal is to look for nodes that might also get deleted once `a` is freed. +The only input `a` has is the constant node with value 12. As every other node a `ConstantNode` takes in the control node first(`Start`), once we +drop the `Start` node from the inputs of `ConstantNode`: +``` +Node old_def = _inputs.removeLast(); +``` +we can also get rid of `ConstantNode`(`this`) from the outputs of `Start`: +``` +old_def.delUse(this) +``` +### Example 2: +``` +int a = 12; +int b = a + 1; +return 1; +``` +- Call `popN` on current scope. +- Realize that `b = a + 1;` has no uses - so we can kill it. +- In the kill function we can call `popN` but this time with the inputs of `b = a + 1;`(start killing them recursively) +- We start the killing process from the back and examine "1" first. This has no output other than the `AddNode`. Since `this = AddNode` calling `delUse` will mark "1" as dead. +- We now can kill "1" and call the `popN` function with one input(the control node). +- Now we examine "a" which has 2 uses at this point, the scope and `AddNode`. Since `this = AddNode` we will remove `AddNode` from its outputs, it will still have the scope as a user - so we can't delete it just yet. +- We move onto`a = 12`, since "a" has only one user left(the scope) and `this = scope` it can get killed easily. +- Same as before, we go through the inputs of "a" and will delete them from the outputs of the control node. + +*A constant node pointing to "Start"(ignored by the graph visualizer) but still maintains a connection:* + + + +## Delete old nodes after finding a better replacement +Consider the following code: +``` +return (2+1)+3; +``` +Without DCE after the `idealize` call we would end up with: + + +After identifying a more optimal replacement value (6) for the expression (2 + 1) + 3, +we created a constant node to store this value. +However, we did not remove the original expression, +resulting in dangling nodes in our structure. + +To fix this, we introduce an extra `deadCodeElim` call that is responsible for deleting unused nodes. + + +```java +public final Node peephole( ) { + ... + if (!(this instanceof ConstantNode) && type.isConstant()) + return new deadCodeElim(ConstantNode(type).peephole()); + + ... + + Node n = idealize(); + if( n != null ) // Something changed + // Recursively optimize + return deadCodeElim(n.peephole()); + + ... +} +``` + +```java +// m is the new Node, self is the old. +// Return 'm', which may have zero uses but is alive nonetheless. +// If self has zero uses (and is not 'm'), {@link #kill} self. +private Node deadCodeElim(Node m) { + // If self is going dead and not being returned here (Nodes returned + // from peephole commonly have no uses (yet)), then kill self. + if( m != this && isUnused() ) { + // Killing self - and since self recursively kills self's inputs we + // might end up killing 'm', which we are returning as a live Node. + // So we add a bogus extra null output edge to stop kill(). + m.addUse(null); // Add bogus null use to keep m alive + kill(); // Kill self because replacing with 'm' + m.delUse(null); // Remove bogus null. + } + return m; +} +```
\ No newline at end of file diff --git a/docs/chapter04/04-arg1.svg b/docs/chapter04/04-arg1.svg new file mode 100644 index 00000000..cf4599c5 --- /dev/null +++ b/docs/chapter04/04-arg1.svg @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.40.1 (20161225.0304) + --> +<!-- Title: chapter04 Pages: 1 --> +<svg width="358pt" height="186pt" viewBox="0.00 0.00 358.00 186.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 182)"> +<title>chapter04</title> +<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-182 354,-182 354,4 -4,4"/> +<g id="clust1" class="cluster"> +<title>cluster_Nodes</title> +<polygon fill="none" stroke="#000000" points="8,-16 8,-170 154,-170 154,-16 8,-16"/> +</g> +<g id="clust2" class="cluster"> +<title>cluster_Scope1_0</title> +<polygon fill="none" stroke="#000000" points="162,-8 162,-76 342,-76 342,-8 162,-8"/> +</g> +<g id="clust3" class="cluster"> +<title>cluster_Scope1_1</title> +<polygon fill="none" stroke="#000000" points="264,-16 264,-68 334,-68 334,-16 264,-16"/> +</g> +<!-- Start2 --> +<g id="node1" class="node"> +<title>Start2</title> +<polygon fill="#ffff00" stroke="transparent" points="25,-132 25,-158 66,-158 66,-132 25,-132"/> +<polygon fill="none" stroke="#000000" points="25,-132 25,-158 66,-158 66,-132 25,-132"/> +<text text-anchor="start" x="32.2805" y="-140.4" font-family="Times,serif" font-size="14.00" fill="#000000">Start</text> +<polygon fill="none" stroke="#000000" points="25,-100 25,-132 66,-132 66,-100 25,-100"/> +<polygon fill="#ffff00" stroke="transparent" points="30,-105 30,-127 61,-127 61,-105 30,-105"/> +<polygon fill="none" stroke="#000000" points="30,-105 30,-127 61,-127 61,-105 30,-105"/> +<text text-anchor="start" x="32.6718" y="-111.4" font-family="Times,serif" font-size="14.00" fill="#000000">$ctrl</text> +</g> +<!-- Con_5 --> +<g id="node2" class="node"> +<title>Con_5</title> +<ellipse fill="none" stroke="#000000" cx="119" cy="-129" rx="27" ry="18"/> +<text text-anchor="middle" x="119" y="-124.8" font-family="Times,serif" font-size="14.00" fill="#000000">2</text> +</g> +<!-- Return6 --> +<g id="node3" class="node"> +<title>Return6</title> +<polygon fill="#ffff00" stroke="#000000" points="109.1025,-60 54.8975,-60 54.8975,-24 109.1025,-24 109.1025,-60"/> +<text text-anchor="middle" x="82" y="-37.8" font-family="Times,serif" font-size="14.00" fill="#000000">Return</text> +</g> +<!-- Return6->Start2 --> +<g id="edge1" class="edge"> +<title>Return6->Start2:p0</title> +<path fill="none" stroke="#ff0000" d="M65.0542,-60.052C57.6135,-69.4899 49.9318,-81.7894 47.1181,-94.8747"/> +<polygon fill="#ff0000" stroke="#ff0000" points="43.6189,-94.6762 46,-105 50.5766,-95.4446 43.6189,-94.6762"/> +<text text-anchor="middle" x="62.8306" y="-62.452" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text> +</g> +<!-- Return6->Con_5 --> +<g id="edge2" class="edge"> +<title>Return6->Con_5</title> +<path fill="none" stroke="#000000" d="M89.6664,-60.0265C94.8444,-72.2017 101.7707,-88.4878 107.5926,-102.1772"/> +<polygon fill="#000000" stroke="#000000" points="104.4613,-103.7577 111.5959,-111.5903 110.903,-101.0181 104.4613,-103.7577"/> +<text text-anchor="middle" x="87.4428" y="-62.4265" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">1</text> +</g> +<!-- Scope1_0 --> +<g id="node4" class="node"> +<title>Scope1_0</title> +<polygon fill="#00ffff" stroke="transparent" points="179,-31 179,-53 192,-53 192,-31 179,-31"/> +<polygon fill="none" stroke="#000000" points="179,-31 179,-53 192,-53 192,-31 179,-31"/> +<text text-anchor="start" x="182" y="-37.4" font-family="Times,serif" font-size="14.00" fill="#000000">0</text> +<polygon fill="none" stroke="#000000" points="192,-31 192,-53 223,-53 223,-31 192,-31"/> +<text text-anchor="start" x="194.6718" y="-37.4" font-family="Times,serif" font-size="14.00" fill="#000000">$ctrl</text> +<polygon fill="none" stroke="#000000" points="223,-31 223,-53 246,-53 246,-31 223,-31"/> +<text text-anchor="start" x="225.5624" y="-37.4" font-family="Times,serif" font-size="14.00" fill="#000000">arg</text> +</g> +<!-- Scope1_0->Con_5 --> +<g id="edge3" class="edge"> +<title>Scope1_0:Scope1_0_arg->Con_5</title> +<path fill="none" stroke="#6495ed" stroke-dasharray="5,2" d="M235,-53C235,-91.8959 188.8038,-111.9666 154.717,-121.4819"/> +<polygon fill="#6495ed" stroke="#6495ed" points="153.8305,-118.0959 145.0327,-123.9992 155.5916,-124.8708 153.8305,-118.0959"/> +</g> +<!-- Scope1_1 --> +<g id="node5" class="node"> +<title>Scope1_1</title> +<polygon fill="#00ffff" stroke="transparent" points="293,-31 293,-53 306,-53 306,-31 293,-31"/> +<polygon fill="none" stroke="#000000" points="293,-31 293,-53 306,-53 306,-31 293,-31"/> +<text text-anchor="start" x="296" y="-37.4" font-family="Times,serif" font-size="14.00" fill="#000000">1</text> +</g> +</g> +</svg>
\ No newline at end of file diff --git a/docs/chapter04/04-arg2.svg b/docs/chapter04/04-arg2.svg new file mode 100644 index 00000000..2b3b47a3 --- /dev/null +++ b/docs/chapter04/04-arg2.svg @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.40.1 (20161225.0304) + --> +<!-- Title: chapter04 Pages: 1 --> +<svg width="308pt" height="186pt" viewBox="0.00 0.00 308.00 186.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 182)"> +<title>chapter04</title> +<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-182 304,-182 304,4 -4,4"/> +<g id="clust1" class="cluster"> +<title>cluster_Nodes</title> +<polygon fill="none" stroke="#000000" points="8,-16 8,-170 104,-170 104,-16 8,-16"/> +</g> +<g id="clust2" class="cluster"> +<title>cluster_Scope1_0</title> +<polygon fill="none" stroke="#000000" points="112,-8 112,-76 292,-76 292,-8 112,-8"/> +</g> +<g id="clust3" class="cluster"> +<title>cluster_Scope1_1</title> +<polygon fill="none" stroke="#000000" points="214,-16 214,-68 284,-68 284,-16 214,-16"/> +</g> +<!-- Start2 --> +<g id="node1" class="node"> +<title>Start2</title> +<polygon fill="#ffff00" stroke="transparent" points="24,-132 24,-158 88,-158 88,-132 24,-132"/> +<polygon fill="none" stroke="#000000" points="24,-132 24,-158 88,-158 88,-132 24,-132"/> +<text text-anchor="start" x="42.7805" y="-140.4" font-family="Times,serif" font-size="14.00" fill="#000000">Start</text> +<polygon fill="none" stroke="#000000" points="24,-100 24,-132 88,-132 88,-100 24,-100"/> +<polygon fill="#ffff00" stroke="transparent" points="29,-105 29,-127 60,-127 60,-105 29,-105"/> +<polygon fill="none" stroke="#000000" points="29,-105 29,-127 60,-127 60,-105 29,-105"/> +<text text-anchor="start" x="31.6718" y="-111.4" font-family="Times,serif" font-size="14.00" fill="#000000">$ctrl</text> +<polygon fill="none" stroke="#000000" points="60,-105 60,-127 83,-127 83,-105 60,-105"/> +<text text-anchor="start" x="62.5624" y="-111.4" font-family="Times,serif" font-size="14.00" fill="#000000">arg</text> +</g> +<!-- Return5 --> +<g id="node2" class="node"> +<title>Return5</title> +<polygon fill="#ffff00" stroke="#000000" points="83.1025,-60 28.8975,-60 28.8975,-24 83.1025,-24 83.1025,-60"/> +<text text-anchor="middle" x="56" y="-37.8" font-family="Times,serif" font-size="14.00" fill="#000000">Return</text> +</g> +<!-- Return5->Start2 --> +<g id="edge1" class="edge"> +<title>Return5->Start2:p0</title> +<path fill="none" stroke="#ff0000" d="M50.7017,-60.0225C48.1903,-69.7926 45.5126,-82.3709 44.4612,-94.733"/> +<polygon fill="#ff0000" stroke="#ff0000" points="40.9524,-94.853 44,-105 47.9454,-95.1672 40.9524,-94.853"/> +<text text-anchor="middle" x="48.4781" y="-62.4225" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text> +</g> +<!-- Return5->Start2 --> +<g id="edge2" class="edge"> +<title>Return5->Start2:p1</title> +<path fill="none" stroke="#000000" d="M63.1576,-60.1614C66.4858,-69.8668 70.0107,-82.3387 71.3935,-94.7027"/> +<polygon fill="#000000" stroke="#000000" points="67.918,-95.2231 72,-105 74.9059,-94.8115 67.918,-95.2231"/> +<text text-anchor="middle" x="60.934" y="-62.5614" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">1</text> +</g> +<!-- Scope1_0 --> +<g id="node3" class="node"> +<title>Scope1_0</title> +<polygon fill="#00ffff" stroke="transparent" points="129,-31 129,-53 142,-53 142,-31 129,-31"/> +<polygon fill="none" stroke="#000000" points="129,-31 129,-53 142,-53 142,-31 129,-31"/> +<text text-anchor="start" x="132" y="-37.4" font-family="Times,serif" font-size="14.00" fill="#000000">0</text> +<polygon fill="none" stroke="#000000" points="142,-31 142,-53 173,-53 173,-31 142,-31"/> +<text text-anchor="start" x="144.6718" y="-37.4" font-family="Times,serif" font-size="14.00" fill="#000000">$ctrl</text> +<polygon fill="none" stroke="#000000" points="173,-31 173,-53 196,-53 196,-31 173,-31"/> +<text text-anchor="start" x="175.5624" y="-37.4" font-family="Times,serif" font-size="14.00" fill="#000000">arg</text> +</g> +<!-- Scope1_0->Start2 --> +<g id="edge3" class="edge"> +<title>Scope1_0:Scope1_0_arg->Start2:p1</title> +<path fill="none" stroke="#6495ed" stroke-dasharray="5,2" d="M185,-53C185,-102.849 142.3608,-114.5665 93.2341,-115.8698"/> +<polygon fill="#6495ed" stroke="#6495ed" points="92.9546,-112.373 83,-116 93.0437,-119.3724 92.9546,-112.373"/> +</g> +<!-- Scope1_1 --> +<g id="node4" class="node"> +<title>Scope1_1</title> +<polygon fill="#00ffff" stroke="transparent" points="243,-31 243,-53 256,-53 256,-31 243,-31"/> +<polygon fill="none" stroke="#000000" points="243,-31 243,-53 256,-53 256,-31 243,-31"/> +<text text-anchor="start" x="246" y="-37.4" font-family="Times,serif" font-size="14.00" fill="#000000">1</text> +</g> +</g> +</svg>
\ No newline at end of file diff --git a/docs/chapter04/04-dce1.svg b/docs/chapter04/04-dce1.svg new file mode 100644 index 00000000..c63a85f7 --- /dev/null +++ b/docs/chapter04/04-dce1.svg @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.40.1 (20161225.0304) + --> +<!-- Title: chapter04 Pages: 1 --> +<svg width="204pt" height="258pt" viewBox="0.00 0.00 204.00 258.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 254)"> +<title>chapter04</title> +<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-254 200,-254 200,4 -4,4"/> +<g id="clust1" class="cluster"> +<title>cluster_Nodes</title> +<polygon fill="none" stroke="#000000" points="19,-88 19,-242 187,-242 187,-88 19,-88"/> +</g> +<g id="clust2" class="cluster"> +<title>cluster_Scope1_0</title> +<polygon fill="none" stroke="#000000" points="8,-8 8,-76 188,-76 188,-8 8,-8"/> +</g> +<g id="clust3" class="cluster"> +<title>cluster_Scope1_1</title> +<polygon fill="none" stroke="#000000" points="110,-16 110,-68 180,-68 180,-16 110,-16"/> +</g> +<!-- Start2 --> +<g id="node1" class="node"> +<title>Start2</title> +<polygon fill="#ffff00" stroke="transparent" points="35,-204 35,-230 99,-230 99,-204 35,-204"/> +<polygon fill="none" stroke="#000000" points="35,-204 35,-230 99,-230 99,-204 35,-204"/> +<text text-anchor="start" x="53.7805" y="-212.4" font-family="Times,serif" font-size="14.00" fill="#000000">Start</text> +<polygon fill="none" stroke="#000000" points="35,-172 35,-204 99,-204 99,-172 35,-172"/> +<polygon fill="#ffff00" stroke="transparent" points="40,-177 40,-199 71,-199 71,-177 40,-177"/> +<polygon fill="none" stroke="#000000" points="40,-177 40,-199 71,-199 71,-177 40,-177"/> +<text text-anchor="start" x="42.6718" y="-183.4" font-family="Times,serif" font-size="14.00" fill="#000000">$ctrl</text> +<polygon fill="none" stroke="#000000" points="71,-177 71,-199 94,-199 94,-177 71,-177"/> +<text text-anchor="start" x="73.5624" y="-183.4" font-family="Times,serif" font-size="14.00" fill="#000000">arg</text> +</g> +<!-- Con_5 --> +<g id="node2" class="node"> +<title>Con_5</title> +<ellipse fill="none" stroke="#000000" cx="152" cy="-114" rx="27" ry="18"/> +<text text-anchor="middle" x="152" y="-109.8" font-family="Times,serif" font-size="14.00" fill="#000000">12</text> +</g> +<!-- Con_6 --> +<g id="node3" class="node"> +<title>Con_6</title> +<ellipse fill="none" stroke="#000000" cx="152" cy="-201" rx="27" ry="18"/> +<text text-anchor="middle" x="152" y="-196.8" font-family="Times,serif" font-size="14.00" fill="#000000">1</text> +</g> +<!-- Return7 --> +<g id="node4" class="node"> +<title>Return7</title> +<polygon fill="#ffff00" stroke="#000000" points="107.1025,-132 52.8975,-132 52.8975,-96 107.1025,-96 107.1025,-132"/> +<text text-anchor="middle" x="80" y="-109.8" font-family="Times,serif" font-size="14.00" fill="#000000">Return</text> +</g> +<!-- Return7->Start2 --> +<g id="edge1" class="edge"> +<title>Return7->Start2:p0</title> +<path fill="none" stroke="#ff0000" d="M68.6703,-132.0016C63.4509,-141.64 57.957,-154.1206 55.8715,-166.8193"/> +<polygon fill="#ff0000" stroke="#ff0000" points="52.3657,-166.7379 55,-177 59.3402,-167.335 52.3657,-166.7379"/> +<text text-anchor="middle" x="66.4467" y="-134.4016" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text> +</g> +<!-- Return7->Con_6 --> +<g id="edge2" class="edge"> +<title>Return7->Con_6</title> +<path fill="none" stroke="#000000" d="M94.9184,-132.0265C105.7302,-145.0906 120.4588,-162.8877 132.2466,-177.1313"/> +<polygon fill="#000000" stroke="#000000" points="129.7233,-179.572 138.7954,-185.0444 135.1161,-175.109 129.7233,-179.572"/> +<text text-anchor="middle" x="92.6948" y="-134.4265" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">1</text> +</g> +<!-- Scope1_0 --> +<g id="node5" class="node"> +<title>Scope1_0</title> +<polygon fill="#00ffff" stroke="transparent" points="25,-31 25,-53 38,-53 38,-31 25,-31"/> +<polygon fill="none" stroke="#000000" points="25,-31 25,-53 38,-53 38,-31 25,-31"/> +<text text-anchor="start" x="28" y="-37.4" font-family="Times,serif" font-size="14.00" fill="#000000">0</text> +<polygon fill="none" stroke="#000000" points="38,-31 38,-53 69,-53 69,-31 38,-31"/> +<text text-anchor="start" x="40.6718" y="-37.4" font-family="Times,serif" font-size="14.00" fill="#000000">$ctrl</text> +<polygon fill="none" stroke="#000000" points="69,-31 69,-53 92,-53 92,-31 69,-31"/> +<text text-anchor="start" x="71.5624" y="-37.4" font-family="Times,serif" font-size="14.00" fill="#000000">arg</text> +</g> +<!-- Scope1_0->Start2 --> +<g id="edge3" class="edge"> +<title>Scope1_0:Scope1_0_arg->Start2:p1</title> +<path fill="none" stroke="#6495ed" stroke-dasharray="5,2" d="M81,-53C81,-75.6361 52.952,-67.2092 44,-88 36.2663,-105.9613 37.1652,-113.6777 44,-132 51.8047,-152.9223 74.2036,-151.7329 81.0127,-166.7479"/> +<polygon fill="#6495ed" stroke="#6495ed" points="77.6609,-167.8488 83,-177 84.533,-166.5167 77.6609,-167.8488"/> +</g> +<!-- Scope1_1 --> +<g id="node6" class="node"> +<title>Scope1_1</title> +<polygon fill="#00ffff" stroke="transparent" points="133,-31 133,-53 146,-53 146,-31 133,-31"/> +<polygon fill="none" stroke="#000000" points="133,-31 133,-53 146,-53 146,-31 133,-31"/> +<text text-anchor="start" x="136" y="-37.4" font-family="Times,serif" font-size="14.00" fill="#000000">1</text> +<polygon fill="none" stroke="#000000" points="146,-31 146,-53 158,-53 158,-31 146,-31"/> +<text text-anchor="start" x="148.8927" y="-37.4" font-family="Times,serif" font-size="14.00" fill="#000000">a</text> +</g> +<!-- Scope1_1->Con_5 --> +<g id="edge4" class="edge"> +<title>Scope1_1:Scope1_1_a->Con_5</title> +<path fill="none" stroke="#6495ed" stroke-dasharray="5,2" d="M152,-53C152,-63.6896 152,-75.4328 152,-85.737"/> +<polygon fill="#6495ed" stroke="#6495ed" points="148.5001,-85.8496 152,-95.8497 155.5001,-85.8497 148.5001,-85.8496"/> +</g> +</g> +</svg>
\ No newline at end of file diff --git a/docs/chapter04/04-dce2.svg b/docs/chapter04/04-dce2.svg new file mode 100644 index 00000000..e3719bcc --- /dev/null +++ b/docs/chapter04/04-dce2.svg @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.40.1 (20161225.0304) + --> +<!-- Title: chapter04 Pages: 1 --> +<svg width="114pt" height="250pt" viewBox="0.00 0.00 114.00 250.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 246)"> +<title>chapter04</title> +<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-246 110,-246 110,4 -4,4"/> +<g id="clust1" class="cluster"> +<title>cluster_Nodes</title> +<polygon fill="none" stroke="#000000" points="8,-8 8,-234 98,-234 98,-8 8,-8"/> +</g> +<!-- Start2 --> +<g id="node1" class="node"> +<title>Start2</title> +<polygon fill="#ffff00" stroke="transparent" points="33,-196 33,-222 74,-222 74,-196 33,-196"/> +<polygon fill="none" stroke="#000000" points="33,-196 33,-222 74,-222 74,-196 33,-196"/> +<text text-anchor="start" x="40.2805" y="-204.4" font-family="Times,serif" font-size="14.00" fill="#000000">Start</text> +<polygon fill="none" stroke="#000000" points="33,-164 33,-196 74,-196 74,-164 33,-164"/> +<polygon fill="#ffff00" stroke="transparent" points="38,-169 38,-191 69,-191 69,-169 38,-169"/> +<polygon fill="none" stroke="#000000" points="38,-169 38,-191 69,-191 69,-169 38,-169"/> +<text text-anchor="start" x="40.6718" y="-175.4" font-family="Times,serif" font-size="14.00" fill="#000000">$ctrl</text> +</g> +<!-- Con_6 --> +<g id="node2" class="node"> +<title>Con_6</title> +<ellipse fill="none" stroke="#000000" cx="43" cy="-106" rx="27" ry="18"/> +<text text-anchor="middle" x="43" y="-101.8" font-family="Times,serif" font-size="14.00" fill="#000000">1</text> +</g> +<!-- Con_6->Start2 --> +<g id="edge1" class="edge"> +<title>Con_6->Start2</title> +<path fill="none" stroke="#ff0000" d="M45.072,-124.0265C45.9366,-131.5481 46.9815,-140.6387 48.0249,-149.7167"/> +<polygon fill="#ff0000" stroke="#ff0000" points="44.5736,-150.3415 49.1927,-159.8764 51.5278,-149.5421 44.5736,-150.3415"/> +<text text-anchor="middle" x="42.8484" y="-126.4265" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text> +</g> +<!-- Return7 --> +<g id="node3" class="node"> +<title>Return7</title> +<polygon fill="#ffff00" stroke="#000000" points="80.1025,-52 25.8975,-52 25.8975,-16 80.1025,-16 80.1025,-52"/> +<text text-anchor="middle" x="53" y="-29.8" font-family="Times,serif" font-size="14.00" fill="#000000">Return</text> +</g> +<!-- Return7->Start2 --> +<g id="edge2" class="edge"> +<title>Return7->Start2:p0</title> +<path fill="none" stroke="#ff0000" d="M64.3196,-52.2322C69.9396,-62.3585 76.1294,-75.4211 79,-88 82.5597,-103.599 83.0135,-108.5115 79,-124 74.2474,-142.3411 60.2215,-145.475 55.5405,-159.004"/> +<polygon fill="#ff0000" stroke="#ff0000" points="52.0641,-158.5835 54,-169 58.9824,-159.6498 52.0641,-158.5835"/> +<text text-anchor="middle" x="62.096" y="-54.6322" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text> +</g> +<!-- Return7->Con_6 --> +<g id="edge3" class="edge"> +<title>Return7->Con_6</title> +<path fill="none" stroke="#000000" d="M50.4766,-52.1686C49.4071,-59.869 48.1353,-69.0257 46.9468,-77.5834"/> +<polygon fill="#000000" stroke="#000000" points="43.4664,-77.2003 45.5574,-87.5867 50.3999,-78.1633 43.4664,-77.2003"/> +<text text-anchor="middle" x="48.253" y="-54.5686" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">1</text> +</g> +</g> +</svg>
\ No newline at end of file diff --git a/docs/chapter04/04-dce3.svg b/docs/chapter04/04-dce3.svg new file mode 100644 index 00000000..86fa6f0d --- /dev/null +++ b/docs/chapter04/04-dce3.svg @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.40.1 (20161225.0304) + --> +<!-- Title: chapter04 Pages: 1 --> +<svg width="458pt" height="178pt" viewBox="0.00 0.00 458.00 178.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 174)"> +<title>chapter04</title> +<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-174 454,-174 454,4 -4,4"/> +<g id="clust1" class="cluster"> +<title>cluster_Nodes</title> +<polygon fill="none" stroke="#000000" points="8,-8 8,-162 442,-162 442,-8 8,-8"/> +</g> +<!-- Start2 --> +<g id="node1" class="node"> +<title>Start2</title> +<polygon fill="#ffff00" stroke="transparent" points="25,-124 25,-150 66,-150 66,-124 25,-124"/> +<polygon fill="none" stroke="#000000" points="25,-124 25,-150 66,-150 66,-124 25,-124"/> +<text text-anchor="start" x="32.2805" y="-132.4" font-family="Times,serif" font-size="14.00" fill="#000000">Start</text> +<polygon fill="none" stroke="#000000" points="25,-92 25,-124 66,-124 66,-92 25,-92"/> +<polygon fill="#ffff00" stroke="transparent" points="30,-97 30,-119 61,-119 61,-97 30,-97"/> +<polygon fill="none" stroke="#000000" points="30,-97 30,-119 61,-119 61,-97 30,-97"/> +<text text-anchor="start" x="32.6718" y="-103.4" font-family="Times,serif" font-size="14.00" fill="#000000">$ctrl</text> +</g> +<!-- Con_5 --> +<g id="node2" class="node"> +<title>Con_5</title> +<ellipse fill="none" stroke="#000000" cx="335" cy="-121" rx="27" ry="18"/> +<text text-anchor="middle" x="335" y="-116.8" font-family="Times,serif" font-size="14.00" fill="#000000">2</text> +</g> +<!-- Con_6 --> +<g id="node3" class="node"> +<title>Con_6</title> +<ellipse fill="none" stroke="#000000" cx="407" cy="-121" rx="27" ry="18"/> +<text text-anchor="middle" x="407" y="-116.8" font-family="Times,serif" font-size="14.00" fill="#000000">1</text> +</g> +<!-- Add7 --> +<g id="node4" class="node"> +<title>Add7</title> +<ellipse fill="none" stroke="#000000" cx="371" cy="-34" rx="27" ry="18"/> +<text text-anchor="middle" x="371" y="-29.8" font-family="Times,serif" font-size="14.00" fill="#000000">+</text> +</g> +<!-- Add7->Con_5 --> +<g id="edge1" class="edge"> +<title>Add7->Con_5</title> +<path fill="none" stroke="#000000" d="M363.7149,-51.6057C358.6889,-63.7518 351.9107,-80.1326 346.2018,-93.9289"/> +<polygon fill="#000000" stroke="#000000" points="342.8638,-92.842 342.2743,-103.4204 349.3319,-95.5185 342.8638,-92.842"/> +<text text-anchor="middle" x="361.4913" y="-54.0057" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">1</text> +</g> +<!-- Add7->Con_6 --> +<g id="edge2" class="edge"> +<title>Add7->Con_6</title> +<path fill="none" stroke="#000000" d="M378.2851,-51.6057C383.3111,-63.7518 390.0893,-80.1326 395.7982,-93.9289"/> +<polygon fill="#000000" stroke="#000000" points="392.6681,-95.5185 399.7257,-103.4204 399.1362,-92.842 392.6681,-95.5185"/> +<text text-anchor="middle" x="376.0615" y="-54.0057" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">2</text> +</g> +<!-- Con_8 --> +<g id="node5" class="node"> +<title>Con_8</title> +<ellipse fill="none" stroke="#000000" cx="191" cy="-121" rx="27" ry="18"/> +<text text-anchor="middle" x="191" y="-116.8" font-family="Times,serif" font-size="14.00" fill="#000000">3</text> +</g> +<!-- Con_9 --> +<g id="node6" class="node"> +<title>Con_9</title> +<ellipse fill="none" stroke="#000000" cx="263" cy="-121" rx="27" ry="18"/> +<text text-anchor="middle" x="263" y="-116.8" font-family="Times,serif" font-size="14.00" fill="#000000">3</text> +</g> +<!-- Add10 --> +<g id="node7" class="node"> +<title>Add10</title> +<ellipse fill="none" stroke="#000000" cx="227" cy="-34" rx="27" ry="18"/> +<text text-anchor="middle" x="227" y="-29.8" font-family="Times,serif" font-size="14.00" fill="#000000">+</text> +</g> +<!-- Add10->Con_8 --> +<g id="edge3" class="edge"> +<title>Add10->Con_8</title> +<path fill="none" stroke="#000000" d="M219.7149,-51.6057C214.6889,-63.7518 207.9107,-80.1326 202.2018,-93.9289"/> +<polygon fill="#000000" stroke="#000000" points="198.8638,-92.842 198.2743,-103.4204 205.3319,-95.5185 198.8638,-92.842"/> +<text text-anchor="middle" x="217.4913" y="-54.0057" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">1</text> +</g> +<!-- Add10->Con_9 --> +<g id="edge4" class="edge"> +<title>Add10->Con_9</title> +<path fill="none" stroke="#000000" d="M234.2851,-51.6057C239.3111,-63.7518 246.0893,-80.1326 251.7982,-93.9289"/> +<polygon fill="#000000" stroke="#000000" points="248.6681,-95.5185 255.7257,-103.4204 255.1362,-92.842 248.6681,-95.5185"/> +<text text-anchor="middle" x="232.0615" y="-54.0057" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">2</text> +</g> +<!-- Con_11 --> +<g id="node8" class="node"> +<title>Con_11</title> +<ellipse fill="none" stroke="#000000" cx="119" cy="-121" rx="27" ry="18"/> +<text text-anchor="middle" x="119" y="-116.8" font-family="Times,serif" font-size="14.00" fill="#000000">6</text> +</g> +<!-- Return12 --> +<g id="node9" class="node"> +<title>Return12</title> +<polygon fill="#ffff00" stroke="#000000" points="109.1025,-52 54.8975,-52 54.8975,-16 109.1025,-16 109.1025,-52"/> +<text text-anchor="middle" x="82" y="-29.8" font-family="Times,serif" font-size="14.00" fill="#000000">Return</text> +</g> +<!-- Return12->Start2 --> +<g id="edge5" class="edge"> +<title>Return12->Start2:p0</title> +<path fill="none" stroke="#ff0000" d="M65.0542,-52.052C57.6135,-61.4899 49.9318,-73.7894 47.1181,-86.8747"/> +<polygon fill="#ff0000" stroke="#ff0000" points="43.6189,-86.6762 46,-97 50.5766,-87.4446 43.6189,-86.6762"/> +<text text-anchor="middle" x="62.8306" y="-54.452" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text> +</g> +<!-- Return12->Con_11 --> +<g id="edge6" class="edge"> +<title>Return12->Con_11</title> +<path fill="none" stroke="#000000" d="M89.6664,-52.0265C94.8444,-64.2017 101.7707,-80.4878 107.5926,-94.1772"/> +<polygon fill="#000000" stroke="#000000" points="104.4613,-95.7577 111.5959,-103.5903 110.903,-93.0181 104.4613,-95.7577"/> +<text text-anchor="middle" x="87.4428" y="-54.4265" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">1</text> +</g> +</g> +</svg>
\ No newline at end of file diff --git a/docs/chapter04/04-grammar.md b/docs/chapter04/04-grammar.md new file mode 100644 index 00000000..48facd4c --- /dev/null +++ b/docs/chapter04/04-grammar.md @@ -0,0 +1,76 @@ +# Grammar for Chapter 1 + +```antlrv4 +grammar SimpleLanguage; + +program + : statement+ EOF + ; + +statement + : returnStatement + | declStatement + | blockStatment + | expressionStatement + | metaStatement + ; + +metaStatement + : '#showGraph' ';' + ; + +expressionStatement + : IDENTIFIER '=' expression ';' + ; + +blockStatement + : '{' statement+ '}' + ; + +declStatement + : 'int' IDENTIFIER '=' expression ';' + ; + +returnStatement + : 'return' expression ';' + ; + +expression + : comparisonExpression + ; + +comparisonExpression + : additiveExpression (('==' | '!='| '>'| '<'| '>='| '<=') additiveExpression)* + ; + +additiveExpression + : multiplicativeExpression (('+' | '-') multiplicativeExpression)* + ; + +multiplicativeExpression + : unaryExpression (('*' | '/') unaryExpression)* + ; + +unaryExpression + : ('-') unaryExpression + | primaryExpression + ; + +primaryExpression + : IDENTIFIER + | INTEGER_LITERAL + | '(' expression ')' + ; + +INTEGER_LITERAL + : [1-9][0-9]* + | [0] + ; + +IDENTIFIER + : NON_DIGIT (NON_DIGIT | DIGIT)* + ; + +NON_DIGIT: [a-zA-Z_]; +DEC_DIGIT: [0-9]; +``` diff --git a/docs/chapter04/04-post-peephole.gv b/docs/chapter04/04-post-peephole.gv new file mode 100644 index 00000000..0a9f8e93 --- /dev/null +++ b/docs/chapter04/04-post-peephole.gv @@ -0,0 +1,43 @@ +digraph chapter04 { +/* +return 1+arg+2; #showGraph; +*/ + rankdir=BT; + ordering="in"; + concentrate="true"; + subgraph cluster_Nodes { + Start2 [ shape=plaintext label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4"> + <TR><TD BGCOLOR="yellow">Start</TD></TR> + <TR><TD> + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TR><TD PORT="p0" BGCOLOR="yellow">$ctrl</TD><TD PORT="p1">arg</TD></TR> + </TABLE> + </TD></TR> + </TABLE>> + ]; + Con_10 [ label="#3" ]; + Add11 [ label="+" ]; + Return12 [ shape=box style=filled fillcolor=yellow label="Return" ]; + } + node [shape=plaintext]; + subgraph cluster_Scope1_0 { + Scope1_0 [label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TR><TD BGCOLOR="cyan">0</TD><TD PORT="Scope1_0_$ctrl">$ctrl</TD><TD PORT="Scope1_0_arg">arg</TD></TR> + </TABLE>>]; + subgraph cluster_Scope1_1 { + Scope1_1 [label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TR><TD BGCOLOR="cyan">1</TD></TR> + </TABLE>>]; + } + } + edge [ fontname=Helvetica, fontsize=8 ]; + Add11 -> Start2:p1[taillabel=1]; + Add11 -> Con_10[taillabel=2]; + Return12 -> Start2:p0[taillabel=0; color=red]; + Return12 -> Add11[taillabel=1]; + edge [style=dashed color=cornflowerblue]; + Scope1_0:"Scope1_0_arg" -> Start2:p1; +} diff --git a/docs/chapter04/04-post-peephole.svg b/docs/chapter04/04-post-peephole.svg new file mode 100644 index 00000000..550c16ef --- /dev/null +++ b/docs/chapter04/04-post-peephole.svg @@ -0,0 +1,108 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.43.0 (0) + --> +<!-- Title: chapter04 Pages: 1 --> +<svg width="417pt" height="248pt" + viewBox="0.00 0.00 417.00 248.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 244)"> +<title>chapter04</title> +<polygon fill="white" stroke="transparent" points="-4,4 -4,-244 413,-244 413,4 -4,4"/> +<g id="clust1" class="cluster"> +<title>cluster_Nodes</title> +<polygon fill="none" stroke="black" points="8,-8 8,-232 193,-232 193,-8 8,-8"/> +</g> +<g id="clust2" class="cluster"> +<title>cluster_Scope1_0</title> +<polygon fill="none" stroke="black" points="201,-72 201,-140 401,-140 401,-72 201,-72"/> +</g> +<g id="clust3" class="cluster"> +<title>cluster_Scope1_1</title> +<polygon fill="none" stroke="black" points="323,-80 323,-132 393,-132 393,-80 323,-80"/> +</g> +<!-- Start2 --> +<g id="node1" class="node"> +<title>Start2</title> +<polygon fill="yellow" stroke="transparent" points="97,-195 97,-220 178,-220 178,-195 97,-195"/> +<polygon fill="none" stroke="black" points="97,-195 97,-220 178,-220 178,-195 97,-195"/> +<text text-anchor="start" x="119" y="-203.8" font-family="Times,serif" font-size="14.00">Start</text> +<polygon fill="none" stroke="black" points="97,-164 97,-195 178,-195 178,-164 97,-164"/> +<polygon fill="yellow" stroke="transparent" points="102,-169 102,-190 142,-190 142,-169 102,-169"/> +<polygon fill="none" stroke="black" points="102,-169 102,-190 142,-190 142,-169 102,-169"/> +<text text-anchor="start" x="105" y="-175.8" font-family="Times,serif" font-size="14.00">$ctrl</text> +<polygon fill="none" stroke="black" points="142,-169 142,-190 173,-190 173,-169 142,-169"/> +<text text-anchor="start" x="145" y="-175.8" font-family="Times,serif" font-size="14.00">arg</text> +</g> +<!-- Con_10 --> +<g id="node2" class="node"> +<title>Con_10</title> +<ellipse fill="none" stroke="black" cx="43" cy="-192" rx="27" ry="18"/> +<text text-anchor="middle" x="43" y="-188.3" font-family="Times,serif" font-size="14.00">#3</text> +</g> +<!-- Add11 --> +<g id="node3" class="node"> +<title>Add11</title> +<ellipse fill="none" stroke="black" cx="158" cy="-106" rx="27" ry="18"/> +<text text-anchor="middle" x="158" y="-102.3" font-family="Times,serif" font-size="14.00">+</text> +</g> +<!-- Add11->Start2 --> +<g id="edge1" class="edge"> +<title>Add11->Start2:p1</title> +<path fill="none" stroke="black" d="M158,-124.09C158,-133.55 158,-145.77 158,-157.69"/> +<polygon fill="black" stroke="black" points="154.5,-158 158,-168 161.5,-158 154.5,-158"/> +<text text-anchor="middle" x="155" y="-126.69" font-family="Helvetica,sans-Serif" font-size="8.00">1</text> +</g> +<!-- Add11->Con_10 --> +<g id="edge2" class="edge"> +<title>Add11->Con_10</title> +<path fill="none" stroke="black" d="M139.03,-119.12C123,-129.54 99.59,-145.19 80,-160 75.68,-163.26 71.19,-166.85 66.88,-170.4"/> +<polygon fill="black" stroke="black" points="64.34,-167.96 58.92,-177.06 68.84,-173.32 64.34,-167.96"/> +<text text-anchor="middle" x="136.03" y="-121.72" font-family="Helvetica,sans-Serif" font-size="8.00">2</text> +</g> +<!-- Return12 --> +<g id="node4" class="node"> +<title>Return12</title> +<polygon fill="yellow" stroke="black" points="165,-52 99,-52 99,-16 165,-16 165,-52"/> +<text text-anchor="middle" x="132" y="-30.3" font-family="Times,serif" font-size="14.00">Return</text> +</g> +<!-- Return12->Start2 --> +<g id="edge3" class="edge"> +<title>Return12->Start2:p0</title> +<path fill="none" stroke="red" d="M126.36,-52.13C124.59,-58.32 122.88,-65.4 122,-72 116.87,-110.66 121.12,-123.21 121.89,-157.66"/> +<polygon fill="red" stroke="red" points="118.39,-158.04 122,-168 125.39,-157.96 118.39,-158.04"/> +<text text-anchor="middle" x="123.36" y="-54.73" font-family="Helvetica,sans-Serif" font-size="8.00">0</text> +</g> +<!-- Return12->Add11 --> +<g id="edge4" class="edge"> +<title>Return12->Add11</title> +<path fill="none" stroke="black" d="M138.43,-52.3C141.4,-60.3 144.99,-69.98 148.29,-78.85"/> +<polygon fill="black" stroke="black" points="145.06,-80.22 151.83,-88.38 151.63,-77.78 145.06,-80.22"/> +<text text-anchor="middle" x="135.43" y="-54.9" font-family="Helvetica,sans-Serif" font-size="8.00">1</text> +</g> +<!-- Scope1_0 --> +<g id="node5" class="node"> +<title>Scope1_0</title> +<polygon fill="cyan" stroke="transparent" points="218,-95 218,-116 234,-116 234,-95 218,-95"/> +<polygon fill="none" stroke="black" points="218,-95 218,-116 234,-116 234,-95 218,-95"/> +<text text-anchor="start" x="221" y="-101.8" font-family="Times,serif" font-size="14.00">0</text> +<polygon fill="none" stroke="black" points="234,-95 234,-116 274,-116 274,-95 234,-95"/> +<text text-anchor="start" x="237" y="-101.8" font-family="Times,serif" font-size="14.00">$ctrl</text> +<polygon fill="none" stroke="black" points="274,-95 274,-116 305,-116 305,-95 274,-95"/> +<text text-anchor="start" x="277" y="-101.8" font-family="Times,serif" font-size="14.00">arg</text> +</g> +<!-- Scope1_0->Start2 --> +<g id="edge5" class="edge"> +<title>Scope1_0:Scope1_0_arg->Start2:p1</title> +<path fill="none" stroke="cornflowerblue" stroke-dasharray="5,2" d="M290,-117C290,-172.03 239,-178.4 184.25,-178.95"/> +<polygon fill="cornflowerblue" stroke="cornflowerblue" points="183.98,-175.45 174,-179 184.02,-182.45 183.98,-175.45"/> +</g> +<!-- Scope1_1 --> +<g id="node6" class="node"> +<title>Scope1_1</title> +<polygon fill="cyan" stroke="transparent" points="350,-95 350,-116 366,-116 366,-95 350,-95"/> +<polygon fill="none" stroke="black" points="350,-95 350,-116 366,-116 366,-95 350,-95"/> +<text text-anchor="start" x="353" y="-101.8" font-family="Times,serif" font-size="14.00">1</text> +</g> +</g> +</svg> diff --git a/docs/chapter04/04-pre-peephole.gv b/docs/chapter04/04-pre-peephole.gv new file mode 100644 index 00000000..aefd81c4 --- /dev/null +++ b/docs/chapter04/04-pre-peephole.gv @@ -0,0 +1,47 @@ +digraph chapter04 { +/* +return 1+arg+2; #showGraph; +*/ + rankdir=BT; + ordering="in"; + concentrate="true"; + subgraph cluster_Nodes { + Start2 [ shape=plaintext label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4"> + <TR><TD BGCOLOR="yellow">Start</TD></TR> + <TR><TD> + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TR><TD PORT="p0" BGCOLOR="yellow">$ctrl</TD><TD PORT="p1">arg</TD></TR> + </TABLE> + </TD></TR> + </TABLE>> + ]; + Con_5 [ label="#1" ]; + Con_6 [ label="#2" ]; + Add7 [ label="+" ]; + Add8 [ label="+" ]; + Return9 [ shape=box style=filled fillcolor=yellow label="Return" ]; + } + node [shape=plaintext]; + subgraph cluster_Scope1_0 { + Scope1_0 [label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TR><TD BGCOLOR="cyan">0</TD><TD PORT="Scope1_0_$ctrl">$ctrl</TD><TD PORT="Scope1_0_arg">arg</TD></TR> + </TABLE>>]; + subgraph cluster_Scope1_1 { + Scope1_1 [label=< + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> + <TR><TD BGCOLOR="cyan">1</TD></TR> + </TABLE>>]; + } + } + edge [ fontname=Helvetica, fontsize=8 ]; + Add7 -> Start2:p1[taillabel=1]; + Add7 -> Con_6[taillabel=2]; + Add8 -> Con_5[taillabel=1]; + Add8 -> Add7[taillabel=2]; + Return9 -> Start2:p0[taillabel=0; color=red]; + Return9 -> Add8[taillabel=1]; + edge [style=dashed color=cornflowerblue]; + Scope1_0:"Scope1_0_arg" -> Start2:p1; +} diff --git a/docs/chapter04/04-pre-peephole.svg b/docs/chapter04/04-pre-peephole.svg new file mode 100644 index 00000000..9ec38d27 --- /dev/null +++ b/docs/chapter04/04-pre-peephole.svg @@ -0,0 +1,134 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.43.0 (0) + --> +<!-- Title: chapter04 Pages: 1 --> +<svg width="417pt" height="320pt" + viewBox="0.00 0.00 417.00 320.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 316)"> +<title>chapter04</title> +<polygon fill="white" stroke="transparent" points="-4,4 -4,-316 413,-316 413,4 -4,4"/> +<g id="clust1" class="cluster"> +<title>cluster_Nodes</title> +<polygon fill="none" stroke="black" points="8,-8 8,-304 193,-304 193,-8 8,-8"/> +</g> +<g id="clust2" class="cluster"> +<title>cluster_Scope1_0</title> +<polygon fill="none" stroke="black" points="201,-144 201,-212 401,-212 401,-144 201,-144"/> +</g> +<g id="clust3" class="cluster"> +<title>cluster_Scope1_1</title> +<polygon fill="none" stroke="black" points="323,-152 323,-204 393,-204 393,-152 323,-152"/> +</g> +<!-- Start2 --> +<g id="node1" class="node"> +<title>Start2</title> +<polygon fill="yellow" stroke="transparent" points="97,-267 97,-292 178,-292 178,-267 97,-267"/> +<polygon fill="none" stroke="black" points="97,-267 97,-292 178,-292 178,-267 97,-267"/> +<text text-anchor="start" x="119" y="-275.8" font-family="Times,serif" font-size="14.00">Start</text> +<polygon fill="none" stroke="black" points="97,-236 97,-267 178,-267 178,-236 97,-236"/> +<polygon fill="yellow" stroke="transparent" points="102,-241 102,-262 142,-262 142,-241 102,-241"/> +<polygon fill="none" stroke="black" points="102,-241 102,-262 142,-262 142,-241 102,-241"/> +<text text-anchor="start" x="105" y="-247.8" font-family="Times,serif" font-size="14.00">$ctrl</text> +<polygon fill="none" stroke="black" points="142,-241 142,-262 173,-262 173,-241 142,-241"/> +<text text-anchor="start" x="145" y="-247.8" font-family="Times,serif" font-size="14.00">arg</text> +</g> +<!-- Con_5 --> +<g id="node2" class="node"> +<title>Con_5</title> +<ellipse fill="none" stroke="black" cx="115" cy="-178" rx="27" ry="18"/> +<text text-anchor="middle" x="115" y="-174.3" font-family="Times,serif" font-size="14.00">#1</text> +</g> +<!-- Con_6 --> +<g id="node3" class="node"> +<title>Con_6</title> +<ellipse fill="none" stroke="black" cx="43" cy="-264" rx="27" ry="18"/> +<text text-anchor="middle" x="43" y="-260.3" font-family="Times,serif" font-size="14.00">#2</text> +</g> +<!-- Add7 --> +<g id="node4" class="node"> +<title>Add7</title> +<ellipse fill="none" stroke="black" cx="43" cy="-178" rx="27" ry="18"/> +<text text-anchor="middle" x="43" y="-174.3" font-family="Times,serif" font-size="14.00">+</text> +</g> +<!-- Add7->Start2 --> +<g id="edge1" class="edge"> +<title>Add7->Start2:p1</title> +<path fill="none" stroke="black" d="M56.63,-193.93C62.95,-200.18 70.86,-207.11 79,-212 107.43,-229.09 147.88,-208.4 156.39,-229.99"/> +<polygon fill="black" stroke="black" points="152.96,-230.68 158,-240 159.87,-229.57 152.96,-230.68"/> +<text text-anchor="middle" x="53.63" y="-196.53" font-family="Helvetica,sans-Serif" font-size="8.00">1</text> +</g> +<!-- Add7->Con_6 --> +<g id="edge2" class="edge"> +<title>Add7->Con_6</title> +<path fill="none" stroke="black" d="M43,-196.4C43,-207.74 43,-222.77 43,-235.68"/> +<polygon fill="black" stroke="black" points="39.5,-235.9 43,-245.9 46.5,-235.9 39.5,-235.9"/> +<text text-anchor="middle" x="40" y="-199" font-family="Helvetica,sans-Serif" font-size="8.00">2</text> +</g> +<!-- Add8 --> +<g id="node5" class="node"> +<title>Add8</title> +<ellipse fill="none" stroke="black" cx="115" cy="-106" rx="27" ry="18"/> +<text text-anchor="middle" x="115" y="-102.3" font-family="Times,serif" font-size="14.00">+</text> +</g> +<!-- Add8->Con_5 --> +<g id="edge3" class="edge"> +<title>Add8->Con_5</title> +<path fill="none" stroke="black" d="M115,-124.3C115,-132.02 115,-141.29 115,-149.89"/> +<polygon fill="black" stroke="black" points="111.5,-149.9 115,-159.9 118.5,-149.9 111.5,-149.9"/> +<text text-anchor="middle" x="112" y="-126.9" font-family="Helvetica,sans-Serif" font-size="8.00">1</text> +</g> +<!-- Add8->Add7 --> +<g id="edge4" class="edge"> +<title>Add8->Add7</title> +<path fill="none" stroke="black" d="M100.43,-121.17C90.25,-131.06 76.48,-144.45 64.97,-155.64"/> +<polygon fill="black" stroke="black" points="62.53,-153.13 57.8,-162.62 67.41,-158.15 62.53,-153.13"/> +<text text-anchor="middle" x="97.43" y="-123.77" font-family="Helvetica,sans-Serif" font-size="8.00">2</text> +</g> +<!-- Return9 --> +<g id="node6" class="node"> +<title>Return9</title> +<polygon fill="yellow" stroke="black" points="166,-52 100,-52 100,-16 166,-16 166,-52"/> +<text text-anchor="middle" x="133" y="-30.3" font-family="Times,serif" font-size="14.00">Return</text> +</g> +<!-- Return9->Start2 --> +<g id="edge5" class="edge"> +<title>Return9->Start2:p0</title> +<path fill="none" stroke="red" d="M140.48,-52.3C144.47,-62.48 148.97,-75.73 151,-88 159.98,-142.37 172.54,-161.27 151,-212 145.75,-224.37 132.06,-223.86 125.55,-230.39"/> +<polygon fill="red" stroke="red" points="122.18,-229.41 122,-240 128.74,-231.83 122.18,-229.41"/> +<text text-anchor="middle" x="137.48" y="-54.9" font-family="Helvetica,sans-Serif" font-size="8.00">0</text> +</g> +<!-- Return9->Add8 --> +<g id="edge6" class="edge"> +<title>Return9->Add8</title> +<path fill="none" stroke="black" d="M128.55,-52.3C126.55,-60.1 124.13,-69.49 121.9,-78.17"/> +<polygon fill="black" stroke="black" points="118.5,-77.34 119.4,-87.9 125.28,-79.08 118.5,-77.34"/> +<text text-anchor="middle" x="125.55" y="-54.9" font-family="Helvetica,sans-Serif" font-size="8.00">1</text> +</g> +<!-- Scope1_0 --> +<g id="node7" class="node"> +<title>Scope1_0</title> +<polygon fill="cyan" stroke="transparent" points="218,-167 218,-188 234,-188 234,-167 218,-167"/> +<polygon fill="none" stroke="black" points="218,-167 218,-188 234,-188 234,-167 218,-167"/> +<text text-anchor="start" x="221" y="-173.8" font-family="Times,serif" font-size="14.00">0</text> +<polygon fill="none" stroke="black" points="234,-167 234,-188 274,-188 274,-167 234,-167"/> +<text text-anchor="start" x="237" y="-173.8" font-family="Times,serif" font-size="14.00">$ctrl</text> +<polygon fill="none" stroke="black" points="274,-167 274,-188 305,-188 305,-167 274,-167"/> +<text text-anchor="start" x="277" y="-173.8" font-family="Times,serif" font-size="14.00">arg</text> +</g> +<!-- Scope1_0->Start2 --> +<g id="edge7" class="edge"> +<title>Scope1_0:Scope1_0_arg->Start2:p1</title> +<path fill="none" stroke="cornflowerblue" stroke-dasharray="5,2" d="M290,-189C290,-244.03 239,-250.4 184.25,-250.95"/> +<polygon fill="cornflowerblue" stroke="cornflowerblue" points="183.98,-247.45 174,-251 184.02,-254.45 183.98,-247.45"/> +</g> +<!-- Scope1_1 --> +<g id="node8" class="node"> +<title>Scope1_1</title> +<polygon fill="cyan" stroke="transparent" points="350,-167 350,-188 366,-188 366,-167 350,-167"/> +<polygon fill="none" stroke="black" points="350,-167 350,-188 366,-188 366,-167 350,-167"/> +<text text-anchor="start" x="353" y="-173.8" font-family="Times,serif" font-size="14.00">1</text> +</g> +</g> +</svg> diff --git a/docs/chapter04/Makefile b/docs/chapter04/Makefile new file mode 100644 index 00000000..12659b8f --- /dev/null +++ b/docs/chapter04/Makefile @@ -0,0 +1,12 @@ +GV := $(wildcard *.gv) +SVG := $(patsubst %.gv,%.svg,$(GV)) + +.PHONY: all +all: $(SVG) + +%.svg: %.gv + dot -Tsvg $< > $@ + +.PHONY: clean +clean: + @rm -f $(SVG) diff --git a/docs/chapter04/README.md b/docs/chapter04/README.md new file mode 100644 index 00000000..bfb52487 --- /dev/null +++ b/docs/chapter04/README.md @@ -0,0 +1,397 @@ +# Chapter 4: External Argument and Comparisons + +In this chapter we extend the language grammar with following features: + +* The program receives a single argument named `arg` of integer type from external environment. +* Expressions support comparison operators. +* We introduce a scope sensitive name binding `$ctrl` for the current incoming + control node. Thus, our Return is no longer hard-wired to Start, instead it + tracks the in scope `$ctrl` binding. + +Here is the [complete language grammar](04-grammar.md) for this chapter. + +You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter04) in a linear Git revision history on the [linear](https://github.com/SeaOfNodes/Simple/tree/linear) branch and [compare](https://github.com/SeaOfNodes/Simple/compare/linear-chapter03...linear-chapter04) it to the previous chapter. + +## Extensions to Intermediate Representation + +In this chapter we add some new nodes and revise some existing nodes. +Following are revised or new nodes + +| Node Name | Type | Description | Inputs | Value | +|-----------|----------------|------------------------------------------------|-----------------------|----------------------------------------------------------------------------| +| MultiNode | Abstract class | A node that has a tuple result | | A tuple | +| Start | Control | Start of function, now a MultiNode | | A tuple with a ctrl token and an `arg` data node | +| Proj | Data | Projection nodes extract values from MultiNode | A MultiNode and index | Result is the extracted value from the input MultiNode at offset index | +| Bool | Data | Represents results of a comparison operator | Two data nodes | Result is a comparison, represented as integer value where 1=true, 0=false | +| Not | Data | Logical not | One data node | Result converts 0 to 1 and vice versa | + +Below is our list of Nodes from [Chapter 3](../chapter03/README.md): + +| Node Name | Type | Description | Inputs | Value | +|-----------|--------------|------------------------------------|------------------------------------------------------------------|------------------------------| +| Return | Control | End of function | Predecessor control node, Data node value | Return value of the function | +| Constant | Data | Constants such as integer literals | None, however Start node is set as input to enable graph walking | Value of the constant | +| Add | Data | Add two values | Two data nodes, values are added, order not important | Result of the add operation | +| Sub | Data | Subtract a value from another | Two data nodes, values are subtracted, order matters | Result of the subtract | +| Mul | Data | Multiply two values | Two data nodes, values are multiplied, order not important | Result of the multiply | +| Div | Data | Divide a value by another | Two data nodes, values are divided, order matters | Result of the division | +| Minus | Data | Negate a value | One data node, value is negated | Result of the unary minus | +| Scope | Symbol Table | Represents scopes in the graph | All nodes that define variables | None | + +## Projection Nodes + +We add projection nodes that are used to extract a specific tuple member from a multi-valued node. +Each projection node contains an index of the field to extract from its input node. +Projection nodes allow us to maintain labeled use-def edges as simple Node references. + +In the visuals, projection nodes are shown as rectangular boxes inside the node to which they are attached. + +### Visualisation + + + +In the visual above, the projection nodes have been tagged by the names +associated with the outputs of the Start node. Both MultiNode that contain +Control and Control nodes are yellow. `$ctrl` and `arg` are outputs(users) of +StartNode(`START`) and have their MultiNode as their only input in slot 0. The +`label` has no semantics and is used during debug printing. + +```java +public ProjNode(MultiNode ctrl, int idx, String label) { + super(ctrl); +``` + +```java +_scope.define(ScopeNode.CTRL, new ProjNode(START, 0, ScopeNode.CTRL).peephole()); +_scope.define(ScopeNode.ARG0, new ProjNode(START, 1, ScopeNode.ARG0).peephole()); +``` + +These projection nodes are going to extract the types for `CTRL` and `ARG0` from the `START` MultiNode. +The MultiNode holds the types as a TypeTuple: + +```java +public StartNode(Type[] args) { + super(); + _args = new TypeTuple(args); +``` + +Extracting the types is based on the `_idx`: +```java +return t instanceof TypeTuple tt ? tt._types[_idx] : Type.BOTTOM; +``` + +### Initial values + +An argument is always provided, and defaults to `TypeInteger.BOT`. Individual +tests can (and do) pass in other integer values by calling `new Parser` with +an argument: + +```java +Parser parser = new Parser("return arg; #showGraph;", TypeInteger.constant(2)); +``` + +The graph: + + + +- `$ctrl` and `arg` are both defined in the outermost symbol table, at lexical depth 0. +- There is an empty global-scope symbol table created at depth 1; in the future + global variables will be defined here. +- There is no line for the scope `$ctrl` because control is dead after the + return. It is manually set to null, which calls `setDef` to kill the node + (and killed nodes are not part of the program and not visualized). + +``` + ctrl(null); // Kill control +``` + +- The `arg` projection node is not included in the cluster because arg is a + constant. The `arg` was originally defined by a ProjNode; but when peephole + is called the computed type is a constant, and the ProjNode gets replaced + with a ConstantNode: + +```java +public final Node peephole( ) { + Type type = _type = compute(); + /* type = {TypeInteger@1527} "2" */ + + if (_disablePeephole) + return this; + + // ProjNode is not a constant unlike its type. + if (!(this instanceof ConstantNode) && type.isConstant()) + /* Create Constant(2) */ + return deadCodeElim(new ConstantNode(type).peephole()); + + /* won't get called */ + ... +``` + +If you don't pass an argument you get `TypeInteger.BOT` + +```java + Parser parser = new Parser("return arg; #showGraph;"); + ... + +public Parser(String source) { + this(source, TypeInteger.BOT); +} + +``` + +We clearly see this is not a constant: + +```java +public final static TypeInteger BOT = new TypeInteger(false, 1); + /* _is_con = false */ +``` + +So the `arg` projection node remains in the cluster: + + + + +#### When Is Control Not-Null? + +In later chapters, `$ctrl` will point to other control nodes, such as +`If` and `Region` (and `Loop`), but for now with no other control flow it always gets set to null after a `Return`. + +#### Note Regarding Visualizations + +From this chapter onwards we omit the edges from Constants to Start node, mainly to reduce +clutter and help draw reader's attention to the more important aspects of the graph. + + + +## Changes to Type System + +In [Chapter 2](../chapter02/README.md) we introduced the Type System. + +We have annotated Nodes with Types and the Type annotation serves two purposes: + +* It defines the set of operations allowed on the Node, and +* it defines the set of values the Node takes on. + +We mentioned in [Chapter 2](../chapter02/README.md) that the set of values +associated with a Type at a specific Node can be conveniently represented as a +[lattice](https://en.wikipedia.org/wiki/Lattice_(order)). + +The type itself is identified by the Java class `Type` and subtypes. The type +implementation uses Java classes as convenient to deal with a Types' internal +structure - but the Java classes have no relation to a Type's place in the +lattice. + +In this chapter we extend the Type hierarchy as follows: + +``` +Type ++-- TypeControl (New - represents control token) ++-- TypeInteger (Enhanced - now has Top and Bottom types) ++-- TypeBot ++-- TypeTuple (New - represents multi-valued result) +``` + +Our enhanced Lattice looks like this: + + + + +In this chapter we introduce the possibility of a program input variable named +`arg`; all we know about this variable is that it is some integer value, but we do not know whether it is a constant or not. + +To support the requirements for non-constant integer values, we enhance `TypeInteger` to +allow it to represent `IntTop` and `IntBot` integer types in addition to the earlier constant value. + +Now that integer values may be constants or non-constants, we need to introduce the "meet" operator +in our lattice. The `meet` operator describes rules that define the resulting type when we combine +integer values. + +| | IntBot | Con1 | Con2 | IntTop | +|--------|--------|--------|--------|--------| +| IntBot | IntBot | IntBot | IntBot | IntBot | +| Con1 | IntBot | Con1 | IntBot | Con1 | +| IntTop | IntBot | Con1 | Con2 | IntTop | + +In the table above `Con1` and `Con2` represent two distinct integer values, and serve to explain the rules below. + +* The `meet` of `IntTop` with any integer constant is that constant. +* The `meet` of `IntBot` with any integer value is `IntBot`. +* The `meet` of any integer with itself is that integer. +* The `meet` of two unrelated integer constants is `IntBot`. + +Currently, all our integer valued nodes are either a constant or a `IntBot` integer type. +When we start optimizing loops, we will start seeing `IntTop` values. + + +## `$ctrl` name binding + +In previous chapters, we had a hard coded control input edge from Start to +Return. In this chapter we no longer have such a hard-wired edge. Instead, we +track the current in-scope control node via the name `$ctrl`. This means that +when we need to create an edge to the predecessor control node, we simply +look up this name in the current scope. + +This introduces the idea that the control flow subgraph is a Petri net model.[^1] +The control token moves virtually from node to node as execution proceeds. The +initial control token is in Start, it then moves via the Proj node to Return. +In later chapters we will see how the token moves across branches. + + +## More Peephole Optimizations + +Now that we have non-constant integer values, we can do additional +optimizations, rearranging algebraic expressions to enable constant +folding. For example: + +``` +return 1 + arg + 2; +``` + +We would expect the compiler to output `arg+3` here, but as it stands what we get is: + + + +We need to perform some algebraic simplifications to enable better outcome. For +example, we need to rearrange the expression as follows: + +``` +arg + (1 + 2) +``` + +This then enables constant folding and the final outcome is shown below. By +canonicalizing expressions we fold common addressing math constants, remove +algebraic identities and generally simplify the code. + + + +Here is a list of peepholes introduced in this Chapter, more will be introduced in later chapters: + +| Before | After | Description | +|-----------------------|-----------------------|------------------------------------------------| +| (arg + 0 ) | arg | Add of zero identity | +| (arg * 1 ) | arg | Multiple of one identity | +| (con + arg) | (arg + con) | Move constants to right, to encourage folding | +| (con * arg) | (arg * con) | Move constants to right, to encourage folding | +| (con1 + (arg + con2)) | (arg + (con1 + con2)) | Move constants to right, to encourage folding | +| ((arg1 + con) + arg2) | ((arg1 + arg2) + con) | Move constants to right, to encourage folding | +| (arg + arg) | (arg * 2) | Sum-of-products form | +| (arg - arg) | 0 | Sub of same is 0 +| (con / 1) | con | Division by one +| (arg == arg) | 1 | Compare of same +| (arg != arg) | 0 | Compare of same +| (arg < arg) | 0 | Compare of same +| (arg > arg) | 0 | Compare of same +| (arg <= arg) | 1 | Compare of same +| (arg >= arg) | 1 | Compare of same + + +## Peephole Walkthrough + +The peephole optimizations introduced in this chapter are local. They are triggered during parsing +as new nodes are created, before the newly created node has any uses. + +For example, when we parse a unary expression, and create a Node for the parsed expression, +`peephole()` is invoked on the newly created `MinusNode`: + +```java +private Node parseUnary() { + if (match("-")) return new MinusNode(parseUnary()).peephole(); + return parsePrimary(); +} +``` + +As another example, here is the parsing of comparison operators, also introduced in this chapter: + +```java +private Node parseComparison() { + var lhs = parseAddition(); + if (match("==")) return new BoolNode.EQNode(lhs, parseComparison()).peephole(); + if (match("!=")) return new BoolNode.NENode(lhs, parseComparison()).peephole(); + if (match("<=")) return new BoolNode.LENode(lhs, parseComparison()).peephole(); + if (match("<" )) return new BoolNode.LTNode(lhs, parseComparison()).peephole(); + if (match(">=")) return new BoolNode.GENode(lhs, parseComparison()).peephole(); + if (match(">" )) return new BoolNode.GTNode(lhs, parseComparison()).peephole(); + return lhs; +} +``` + +The implementation of `peephole()` is in the main `Node` class, and is thus shared +by all subclasses of `Node`. + +```java +public final Node peephole( ) { + // Compute initial or improved Type + Type type = _type = compute(); + + // Replace constant computations from non-constants with a constant node + if (!(this instanceof ConstantNode) && type.isConstant()) + return deadCodeElim(new ConstantNode(type).peephole()); + + // Ask each node for a better replacement + Node n = idealize(); + if( n != null ) // Something changed + // Recursively optimize + return deadCodeElim(n.peephole()); + + return this; // No progress +} +``` + +The peephole method does following: + +- Compute a Type for the node. +- If the Type is a Constant and the node is not a ConstantNode, replace with a + ConstantNode, recursively invoking peephole on the new constant. +- Otherwise, ask the Node for a better replacement via a call to `idealize()`. + The "better replacement" is things like `(1+2)` becomes `3` and `1+(x+2))` + becomes `(x+(1+2))`. +- - Each Node subtype is responsible for deciding what the `idealize()` step + should do. If there is a better replacement then the node returns a non + `null` value. +- - If we see a non `null` value, something changed, so we invoke `peephole()` + again and then also run dead code elimination on the (now dead and replaced) + `this` node. + +`peephole()` calls `deadCodeElim()` which is shown below. + + +## Dead Code Elimination(DCE) + +We call `kill` every time a Node makes the 1-user-to-0-users transition during +graph editing. This commonly happens when replacing via peeps and exiting +scopes. This recursive `kill` amounts to Dead Code Elimination in many other +compilers; here we call it in a very fine-grained way. Many Nodes will be +created by the parser, only to be killed before the very next lexeme is parsed. + +For more information please read the [appendix section](appendix/dce.md#appendix) on dead code elimination. + +The suggestively called `deadCodeElim` function is a thin wrapper over `kill` +used by `peepholeOpt` to avoid prematurely killing a Node which only briefly +makes the 1->0 users transition. + +```java +// m is the new Node, self is the old. +// Return 'm', which may have zero uses but is alive nonetheless. +// If self has zero uses (and is not 'm'), {@link #kill} self. +private Node deadCodeElim(Node m) { + // If self is going dead and not being returned here (Nodes returned + // from peephole commonly have no uses (yet)), then kill self. + if( m != this && isUnused() ) { + // Killing self - and since self recursively kills self's inputs we + // might end up killing 'm', which we are returning as a live Node. + // So we add a bogus extra null output edge to stop kill(). + m.addUse(null); // Add bogus null use to keep m alive + kill(); // Kill self because replacing with 'm' + m.delUse(null); // Remove bogus null. + } + return m; +} +``` + +Note the temporary add of a bogus user to the Node `m`. The reason this is +done is that we know `m` is the new replacement and is alive, but since it is +not yet hooked into the graph, it has no users yet. By adding a bogus user we +prevent it being mistaken for dead and being killed. + +[^1]: Click, C. (1995). + Combining Analyses, Combining Optimizations, 131. diff --git a/docs/chapter04/lattice.gv b/docs/chapter04/lattice.gv new file mode 100644 index 00000000..1f286530 --- /dev/null +++ b/docs/chapter04/lattice.gv @@ -0,0 +1,63 @@ +digraph Lattice { + // Define nodes + splines=false; + node [shape=circle, style=filled, fillcolor=lightblue]; + nodesep=0.6; + + TOP [label="⊤"]; + BOT [label="⊥"]; + + CTRL [label="Ctrl"]; + + INT_TOP [label="IntTop"]; + INT_BOT [label="IntBot"]; + + INT_MIN [label="-n"]; + INT_MINDOTS [label="..." shape=none style=none]; + INT_MINUS [label="-1"]; + INT_ZERO [label="0"]; + INT_PLUS [label="1"]; + INT_MAXDOTS [label="..." shape=none style=none]; + INT_MAX [label="+n"]; + + TUPLE [label="TUPLE"]; + + // Define edges + edge [arrowhead=none]; + + TOP -> INT_TOP; + INT_TOP -> INT_MIN; + INT_TOP -> INT_MINDOTS; + INT_TOP -> INT_MINUS; + INT_TOP -> INT_ZERO; + INT_TOP -> INT_PLUS; + INT_TOP -> INT_MAXDOTS; + INT_TOP -> INT_MAX; + INT_MIN -> INT_BOT; + INT_MINDOTS -> INT_BOT; + INT_MINUS -> INT_BOT; + INT_ZERO -> INT_BOT; + INT_PLUS -> INT_BOT; + INT_MAXDOTS -> INT_BOT; + INT_MAX -> INT_BOT; + INT_BOT -> BOT; + + INT_MIN -> INT_MINDOTS -> INT_MINUS -> INT_ZERO -> INT_PLUS -> INT_MAXDOTS -> INT_MAX [style=invis]; + + TOP -> TUPLE; + TUPLE -> BOT; + + TOP -> CTRL; + CTRL -> BOT; + + + // Additional customization + {rank=same; TOP} + {rank=same; INT_TOP} + {rank=same; CTRL; INT_MIN; INT_MINDOTS; + INT_MINUS; INT_ZERO; + INT_PLUS; INT_MAXDOTS; + INT_MAX; TUPLE} + {rank=same; INT_BOT} + {rank=same; BOT;} +} diff --git a/docs/chapter04/lattice.svg b/docs/chapter04/lattice.svg new file mode 100644 index 00000000..0d403c20 --- /dev/null +++ b/docs/chapter04/lattice.svg @@ -0,0 +1,195 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.43.0 (0) + --> +<!-- Title: Lattice Pages: 1 --> +<svg width="802pt" height="478pt" + viewBox="0.00 0.00 802.14 478.27" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 474.27)"> +<title>Lattice</title> +<polygon fill="white" stroke="transparent" points="-4,4 -4,-474.27 798.14,-474.27 798.14,4 -4,4"/> +<!-- TOP --> +<g id="node1" class="node"> +<title>TOP</title> +<ellipse fill="lightblue" stroke="black" cx="377.6" cy="-450.77" rx="19.5" ry="19.5"/> +<text text-anchor="middle" x="377.6" y="-447.07" font-family="Times,serif" font-size="14.00">⊤</text> +</g> +<!-- CTRL --> +<g id="node3" class="node"> +<title>CTRL</title> +<ellipse fill="lightblue" stroke="black" cx="28.6" cy="-235.14" rx="28.7" ry="28.7"/> +<text text-anchor="middle" x="28.6" y="-231.44" font-family="Times,serif" font-size="14.00">Ctrl</text> +</g> +<!-- TOP->CTRL --> +<g id="edge25" class="edge"> +<title>TOP->CTRL</title> +<path fill="none" stroke="black" d="M360.76,-440.37C304.12,-405.37 120.24,-291.76 53.28,-250.38"/> +</g> +<!-- INT_TOP --> +<g id="node4" class="node"> +<title>INT_TOP</title> +<ellipse fill="lightblue" stroke="black" cx="377.6" cy="-354.98" rx="40.09" ry="40.09"/> +<text text-anchor="middle" x="377.6" y="-351.28" font-family="Times,serif" font-size="14.00">IntTop</text> +</g> +<!-- TOP->INT_TOP --> +<g id="edge1" class="edge"> +<title>TOP->INT_TOP</title> +<path fill="none" stroke="black" d="M377.6,-431.24C377.6,-420.99 377.6,-407.84 377.6,-395.42"/> +</g> +<!-- TUPLE --> +<g id="node13" class="node"> +<title>TUPLE</title> +<ellipse fill="lightblue" stroke="black" cx="750.6" cy="-235.14" rx="43.59" ry="43.59"/> +<text text-anchor="middle" x="750.6" y="-231.44" font-family="Times,serif" font-size="14.00">TUPLE</text> +</g> +<!-- TOP->TUPLE --> +<g id="edge23" class="edge"> +<title>TOP->TUPLE</title> +<path fill="none" stroke="black" d="M394.59,-440.95C451.03,-408.32 633.13,-303.05 712.75,-257.02"/> +</g> +<!-- BOT --> +<g id="node2" class="node"> +<title>BOT</title> +<ellipse fill="lightblue" stroke="black" cx="377.6" cy="-19.5" rx="19.5" ry="19.5"/> +<text text-anchor="middle" x="377.6" y="-15.8" font-family="Times,serif" font-size="14.00">⊥</text> +</g> +<!-- CTRL->BOT --> +<g id="edge26" class="edge"> +<title>CTRL->BOT</title> +<path fill="none" stroke="black" d="M53.14,-219.97C120.01,-178.65 304.22,-64.84 360.83,-29.86"/> +</g> +<!-- INT_MIN --> +<g id="node6" class="node"> +<title>INT_MIN</title> +<ellipse fill="lightblue" stroke="black" cx="119.6" cy="-235.14" rx="19.5" ry="19.5"/> +<text text-anchor="middle" x="119.6" y="-231.44" font-family="Times,serif" font-size="14.00">-n</text> +</g> +<!-- INT_TOP->INT_MIN --> +<g id="edge2" class="edge"> +<title>INT_TOP->INT_MIN</title> +<path fill="none" stroke="black" d="M341.18,-337.34C284.92,-311.65 179.02,-263.28 137.21,-244.18"/> +</g> +<!-- INT_MINDOTS --> +<g id="node7" class="node"> +<title>INT_MINDOTS</title> +<text text-anchor="middle" x="208.6" y="-231.44" font-family="Times,serif" font-size="14.00">...</text> +</g> +<!-- INT_TOP->INT_MINDOTS --> +<g id="edge3" class="edge"> +<title>INT_TOP->INT_MINDOTS</title> +<path fill="none" stroke="black" d="M345.01,-331.26C312.3,-308.45 262.65,-273.83 233.14,-253.25"/> +</g> +<!-- INT_MINUS --> +<g id="node8" class="node"> +<title>INT_MINUS</title> +<ellipse fill="lightblue" stroke="black" cx="297.6" cy="-235.14" rx="19.5" ry="19.5"/> +<text text-anchor="middle" x="297.6" y="-231.44" font-family="Times,serif" font-size="14.00">-1</text> +</g> +<!-- INT_TOP->INT_MINUS --> +<g id="edge4" class="edge"> +<title>INT_TOP->INT_MINUS</title> +<path fill="none" stroke="black" d="M355.31,-321.15C339.96,-298.54 320.25,-269.51 308.29,-251.89"/> +</g> +<!-- INT_ZERO --> +<g id="node9" class="node"> +<title>INT_ZERO</title> +<ellipse fill="lightblue" stroke="black" cx="377.6" cy="-235.14" rx="18" ry="18"/> +<text text-anchor="middle" x="377.6" y="-231.44" font-family="Times,serif" font-size="14.00">0</text> +</g> +<!-- INT_TOP->INT_ZERO --> +<g id="edge5" class="edge"> +<title>INT_TOP->INT_ZERO</title> +<path fill="none" stroke="black" d="M377.6,-314.41C377.6,-293.68 377.6,-269.36 377.6,-253.39"/> +</g> +<!-- INT_PLUS --> +<g id="node10" class="node"> +<title>INT_PLUS</title> +<ellipse fill="lightblue" stroke="black" cx="456.6" cy="-235.14" rx="18" ry="18"/> +<text text-anchor="middle" x="456.6" y="-231.44" font-family="Times,serif" font-size="14.00">1</text> +</g> +<!-- INT_TOP->INT_PLUS --> +<g id="edge6" class="edge"> +<title>INT_TOP->INT_PLUS</title> +<path fill="none" stroke="black" d="M399.6,-321.15C415.13,-298 435.17,-268.1 446.88,-250.63"/> +</g> +<!-- INT_MAXDOTS --> +<g id="node11" class="node"> +<title>INT_MAXDOTS</title> +<text text-anchor="middle" x="544.6" y="-231.44" font-family="Times,serif" font-size="14.00">...</text> +</g> +<!-- INT_TOP->INT_MAXDOTS --> +<g id="edge7" class="edge"> +<title>INT_TOP->INT_MAXDOTS</title> +<path fill="none" stroke="black" d="M410.19,-330.98C442.56,-308.14 491.43,-273.65 520.47,-253.16"/> +</g> +<!-- INT_MAX --> +<g id="node12" class="node"> +<title>INT_MAX</title> +<ellipse fill="lightblue" stroke="black" cx="639.6" cy="-235.14" rx="24.9" ry="24.9"/> +<text text-anchor="middle" x="639.6" y="-231.44" font-family="Times,serif" font-size="14.00">+n</text> +</g> +<!-- INT_TOP->INT_MAX --> +<g id="edge8" class="edge"> +<title>INT_TOP->INT_MAX</title> +<path fill="none" stroke="black" d="M414.03,-337.59C469.15,-312.8 572.09,-266.5 617.32,-246.16"/> +</g> +<!-- INT_BOT --> +<g id="node5" class="node"> +<title>INT_BOT</title> +<ellipse fill="lightblue" stroke="black" cx="377.6" cy="-115.29" rx="40.09" ry="40.09"/> +<text text-anchor="middle" x="377.6" y="-111.59" font-family="Times,serif" font-size="14.00">IntBot</text> +</g> +<!-- INT_BOT->BOT --> +<g id="edge16" class="edge"> +<title>INT_BOT->BOT</title> +<path fill="none" stroke="black" d="M377.6,-74.79C377.6,-62.37 377.6,-49.25 377.6,-39.01"/> +</g> +<!-- INT_MIN->INT_BOT --> +<g id="edge9" class="edge"> +<title>INT_MIN->INT_BOT</title> +<path fill="none" stroke="black" d="M137.33,-226.04C179.23,-206.9 285.02,-158.58 341.21,-132.91"/> +</g> +<!-- INT_MIN->INT_MINDOTS --> +<!-- INT_MINDOTS->INT_BOT --> +<g id="edge10" class="edge"> +<title>INT_MINDOTS->INT_BOT</title> +<path fill="none" stroke="black" d="M233.17,-217C262.65,-196.45 312.17,-161.91 344.87,-139.11"/> +</g> +<!-- INT_MINDOTS->INT_MINUS --> +<!-- INT_MINUS->INT_BOT --> +<g id="edge11" class="edge"> +<title>INT_MINUS->INT_BOT</title> +<path fill="none" stroke="black" d="M308.39,-218.24C320.33,-200.65 339.91,-171.81 355.2,-149.29"/> +</g> +<!-- INT_MINUS->INT_ZERO --> +<!-- INT_ZERO->INT_BOT --> +<g id="edge12" class="edge"> +<title>INT_ZERO->INT_BOT</title> +<path fill="none" stroke="black" d="M377.6,-217C377.6,-201.04 377.6,-176.66 377.6,-155.88"/> +</g> +<!-- INT_ZERO->INT_PLUS --> +<!-- INT_PLUS->INT_BOT --> +<g id="edge13" class="edge"> +<title>INT_PLUS->INT_BOT</title> +<path fill="none" stroke="black" d="M446.91,-219.69C435.21,-202.23 415.16,-172.33 399.63,-149.16"/> +</g> +<!-- INT_PLUS->INT_MAXDOTS --> +<!-- INT_MAXDOTS->INT_BOT --> +<g id="edge14" class="edge"> +<title>INT_MAXDOTS->INT_BOT</title> +<path fill="none" stroke="black" d="M520.31,-217C491.28,-196.51 442.55,-162.13 410.24,-139.33"/> +</g> +<!-- INT_MAXDOTS->INT_MAX --> +<!-- INT_MAX->INT_BOT --> +<g id="edge15" class="edge"> +<title>INT_MAX->INT_BOT</title> +<path fill="none" stroke="black" d="M617.26,-224.09C572.09,-203.77 469.52,-157.64 414.35,-132.82"/> +</g> +<!-- TUPLE->BOT --> +<g id="edge24" class="edge"> +<title>TUPLE->BOT</title> +<path fill="none" stroke="black" d="M712.78,-213.27C633.14,-167.23 450.85,-61.85 394.51,-29.27"/> +</g> +</g> +</svg> diff --git a/docs/chapter04/projnode.png b/docs/chapter04/projnode.png Binary files differnew file mode 100644 index 00000000..bf7493f2 --- /dev/null +++ b/docs/chapter04/projnode.png diff --git a/src/main/java/com/seaofnodes/simple/GraphVisualizer.java b/src/main/java/com/seaofnodes/simple/GraphVisualizer.java index 9dfac52d..62b434f3 100644 --- a/src/main/java/com/seaofnodes/simple/GraphVisualizer.java +++ b/src/main/java/com/seaofnodes/simple/GraphVisualizer.java @@ -17,7 +17,7 @@ public class GraphVisualizer { // nodes in the graph. Collection<Node> all = findAll(parser); StringBuilder sb = new StringBuilder(); - sb.append("digraph chapter03 {\n"); + sb.append("digraph chapter04 {\n"); sb.append("/*\n"); sb.append(parser.src()); sb.append("\n*/\n"); @@ -57,15 +57,45 @@ public class GraphVisualizer { // Just the Nodes first, in a cluster no edges sb.append("\tsubgraph cluster_Nodes {\n"); // Magic "cluster_" in the subgraph name for( Node n : all ) { - if( n instanceof ScopeNode ) - continue; // Do not emit, rolled into Scope cluster already + if( n instanceof ProjNode || n instanceof ScopeNode ) + continue; // Do not emit, rolled into MultiNode or Scope cluster already sb.append("\t\t").append(n.uniqueName()).append(" [ "); String lab = n.glabel(); - // control nodes have box shape - // other nodes are ellipses, i.e. default shape - if( n.isCFG() ) - sb.append("shape=box style=filled fillcolor=yellow "); - sb.append("label=\"").append(lab).append("\" "); + if( n instanceof MultiNode ) { + // Make a box with the MultiNode on top, and all the projections on the bottom + sb.append("shape=plaintext label=<\n"); + sb.append("\t\t\t<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">\n"); + sb.append("\t\t\t<TR><TD BGCOLOR=\"yellow\">").append(lab).append("</TD></TR>\n"); + sb.append("\t\t\t<TR>"); + boolean doProjTable = false; + for( Node use : n._outputs ) { + if( use instanceof ProjNode proj ) { + if (!doProjTable) { + doProjTable = true; + sb.append("<TD>").append("\n"); + sb.append("\t\t\t\t<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">").append("\n"); + sb.append("\t\t\t\t<TR>"); + } + sb.append("<TD PORT=\"p").append(proj._idx).append("\""); + if( proj.isCFG() ) sb.append(" BGCOLOR=\"yellow\""); + sb.append(">").append(proj.glabel()).append("</TD>"); + } + } + if (doProjTable) { + sb.append("</TR>").append("\n"); + sb.append("\t\t\t\t</TABLE>").append("\n"); + sb.append("\t\t\t</TD>"); + } + sb.append("</TR>\n"); + sb.append("\t\t\t</TABLE>>\n\t\t"); + + } else { + // control nodes have box shape + // other nodes are ellipses, i.e. default shape + if( n.isCFG() ) + sb.append("shape=box style=filled fillcolor=yellow "); + sb.append("label=\"").append(lab).append("\" "); + } sb.append("];\n"); } sb.append("\t}\n"); // End Node cluster @@ -101,22 +131,24 @@ public class GraphVisualizer { // All them edge labels sb.append("\tedge [ fontname=Helvetica, fontsize=8 ];\n"); for( Node n : all ) { - // In this chapter we do display the Constant->Start edge; + // Do not display the Constant->Start edge; + // ProjNodes handled by Multi; // ScopeNodes are done separately - if( n instanceof ScopeNode ) + if( n instanceof ConstantNode || n instanceof ProjNode || n instanceof ScopeNode ) continue; int i=0; for( Node def : n._inputs ) { if( def != null ) { // Most edges land here use->def sb.append('\t').append(n.uniqueName()).append(" -> "); - sb.append(def.uniqueName()); + if( def instanceof ProjNode proj ) { + String mname = proj.ctrl().uniqueName(); + sb.append(mname).append(":p").append(proj._idx); + } else sb.append(def.uniqueName()); // Number edges, so we can see how they track sb.append("[taillabel=").append(i); - if( n instanceof ConstantNode && def instanceof StartNode ) - sb.append(" style=dotted"); // control edges are colored red - else if( def.isCFG() ) + if( def.isCFG() ) sb.append(" color=red"); sb.append("];\n"); } @@ -138,7 +170,10 @@ public class GraphVisualizer { .append(scopeName).append(":") .append('"').append(makePortName(scopeName, name)).append('"') // wrap port name with quotes because $ctrl is not valid unquoted .append(" -> "); - sb.append(def.uniqueName()); + if( def instanceof ProjNode proj ) { + String mname = proj.ctrl().uniqueName(); + sb.append(mname).append(":p").append(proj._idx); + } else sb.append(def.uniqueName()); sb.append(";\n"); } level++; diff --git a/src/main/java/com/seaofnodes/simple/Parser.java b/src/main/java/com/seaofnodes/simple/Parser.java index fa1a852c..ff2c0504 100644 --- a/src/main/java/com/seaofnodes/simple/Parser.java +++ b/src/main/java/com/seaofnodes/simple/Parser.java @@ -14,6 +14,7 @@ import java.util.*; */ public class Parser { + /** * A Global Static, unique to each compilation. This is a public, so we * can make constants everywhere without having to thread the StartNode @@ -27,7 +28,7 @@ public class Parser { private final Lexer _lexer; /** - * Stack of lexical scopes, each scope is a symbol table that binds + * A ScopeNode contains a stack of lexical scopes, each scope is a symbol table that binds * variable names to Nodes. The top of this stack represents current scope. */ public ScopeNode _scope; @@ -41,18 +42,38 @@ public class Parser { }}; - public Parser(String source) { - _lexer = new Lexer(source); + public Parser(String source, TypeInteger arg) { Node.reset(); + _lexer = new Lexer(source); _scope = new ScopeNode(); - START = new StartNode(); + START = new StartNode(new Type[]{ Type.CONTROL, arg }); + START.peephole(); + } + + public Parser(String source) { + this(source, TypeInteger.BOT); } + @Override + public String toString() { return _lexer.toString(); } + String src() { return new String( _lexer._input ); } + // Debugging utility to find a Node by index + public static Node find(int nid) { return START.find(nid); } + + private Node ctrl() { return _scope.ctrl(); } + + private Node ctrl(Node n) { return _scope.ctrl(n); } + public ReturnNode parse() { return parse(false); } public ReturnNode parse(boolean show) { + // Enter a new scope for the initial control and arguments + _scope.push(); + _scope.define(ScopeNode.CTRL, new ProjNode(START, 0, ScopeNode.CTRL).peephole()); + _scope.define(ScopeNode.ARG0, new ProjNode(START, 1, ScopeNode.ARG0).peephole()); var ret = (ReturnNode) parseBlock(); + _scope.pop(); if (!_lexer.isEOF()) throw error("Syntax error, unexpected " + _lexer.getAnyNextToken()); if( show ) showGraph(); return ret; @@ -99,6 +120,7 @@ public class Parser { /** * Parses a return statement; "return" already parsed. + * The $ctrl edge is killed. * * <pre> * 'return' expr ; @@ -107,7 +129,9 @@ public class Parser { */ private Node parseReturn() { var expr = require(parseExpression(), ";"); - return new ReturnNode(START, expr).peephole(); + Node ret = new ReturnNode(ctrl(), expr).peephole(); + ctrl(null); // Kill control + return ret; } /** @@ -158,11 +182,30 @@ public class Parser { * Parse an expression of the form: * * <pre> - * expr : additiveExpr + * expr : compareExpr * </pre> * @return an expression {@link Node}, never {@code null} */ - private Node parseExpression() { return parseAddition(); } + private Node parseExpression() { return parseComparison(); } + + /** + * Parse an expression of the form: + * + * <pre> + * expr : additiveExpr op additiveExpr + * </pre> + * @return an comparator expression {@link Node}, never {@code null} + */ + private Node parseComparison() { + var lhs = parseAddition(); + if (match("==")) return new BoolNode.EQ(lhs, parseComparison()).peephole(); + if (match("!=")) return new NotNode(new BoolNode.EQ(lhs, parseComparison()).peephole()).peephole(); + if (match("<=")) return new BoolNode.LE(lhs, parseComparison()).peephole(); + if (match("<" )) return new BoolNode.LT(lhs, parseComparison()).peephole(); + if (match(">=")) return new BoolNode.LE(parseComparison(), lhs).peephole(); + if (match(">" )) return new BoolNode.LT(parseComparison(), lhs).peephole(); + return lhs; + } /** * Parse an additive expression diff --git a/src/main/java/com/seaofnodes/simple/node/AddNode.java b/src/main/java/com/seaofnodes/simple/node/AddNode.java index 7c1968c5..5720aca4 100644 --- a/src/main/java/com/seaofnodes/simple/node/AddNode.java +++ b/src/main/java/com/seaofnodes/simple/node/AddNode.java @@ -23,14 +23,76 @@ public class AddNode extends Node { in(2)._type instanceof TypeInteger i1 ) { if (i0.isConstant() && i1.isConstant()) return TypeInteger.constant(i0.value()+i1.value()); + return i0.meet(i1); } return Type.BOTTOM; } @Override public Node idealize () { - // TODO: add of 0, and many other peepholes added in Chaper04 + Node lhs = in(1); + Node rhs = in(2); + Type t1 = lhs._type; + Type t2 = rhs._type; + + // Already handled by peephole constant folding + assert !(t1.isConstant() && t2.isConstant()); + + // Add of 0. We do not check for (0+x) because this will already + // canonicalize to (x+0) + if( t2 instanceof TypeInteger i && i.value()==0 ) + return lhs; + + // Add of same to a multiply by 2 + if( lhs==rhs ) + return new MulNode(lhs,new ConstantNode(TypeInteger.constant(2)).peephole()); + + // Goal: a left-spine set of adds, with constants on the rhs (which then fold). + + // Move non-adds to RHS + if( !(lhs instanceof AddNode) && rhs instanceof AddNode ) + return swap12(); + + // Now we might see (add add non) or (add non non) or (add add add) but never (add non add) + + // Do we have x + (y + z) ? + // Swap to (x + y) + z + // Rotate (add add add) to remove the add on RHS + if( rhs instanceof AddNode add ) + return new AddNode(new AddNode(lhs,add.in(1)).peephole(), add.in(2)); + + // Now we might see (add add non) or (add non non) but never (add non add) nor (add add add) + if( !(lhs instanceof AddNode) ) + return spline_cmp(lhs,rhs) ? swap12() : null; + + // Now we only see (add add non) + + // Do we have (x + con1) + con2? + // Replace with (x + (con1+con2) which then fold the constants + if( lhs.in(2)._type.isConstant() && t2.isConstant() ) + return new AddNode(lhs.in(1),new AddNode(lhs.in(2),rhs).peephole()); + + // Now we sort along the spline via rotates, to gather similar things together. + + // Do we rotate (x + y) + z + // into (x + z) + y ? + if( spline_cmp(lhs.in(2),rhs) ) + return new AddNode(new AddNode(lhs.in(1),rhs).peephole(),lhs.in(2)); + return null; } + // Compare two off-spline nodes and decide what order they should be in. + // Do we rotate ((x + hi) + lo) into ((x + lo) + hi) ? + // Generally constants always go right, then others. + // Ties with in a category sort by node ID. + // TRUE if swapping hi and lo. + static boolean spline_cmp( Node hi, Node lo ) { + if( lo._type.isConstant() ) return false; + if( hi._type.isConstant() ) return true ; + + // Same category of "others" + return lo._nid > hi._nid; + } + } diff --git a/src/main/java/com/seaofnodes/simple/node/BoolNode.java b/src/main/java/com/seaofnodes/simple/node/BoolNode.java new file mode 100644 index 00000000..05495790 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/BoolNode.java @@ -0,0 +1,52 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; + +abstract public class BoolNode extends Node { + + public BoolNode(Node lhs, Node rhs) { + super(null, lhs, rhs); + } + + abstract String op(); // String opcode name + + @Override + public String label() { return getClass().getSimpleName(); } + + @Override + public String glabel() { return op(); } + + @Override + StringBuilder _print1(StringBuilder sb) { + in(1)._print0(sb.append("(")); + in(2)._print0(sb.append(op())); + return sb.append(")"); + } + + @Override + public Type compute() { + if( in(1)._type instanceof TypeInteger i0 && + in(2)._type instanceof TypeInteger i1 ) { + if (i0.isConstant() && i1.isConstant()) + return TypeInteger.constant(doOp(i0.value(), i1.value()) ? 1 : 0); + return i0.meet(i1); + } + return Type.BOTTOM; + } + + abstract boolean doOp(long lhs, long rhs); + + @Override + public Node idealize() { + // Compare of same + if( in(1)==in(2) ) + return new ConstantNode(TypeInteger.constant(doOp(3,3)?1:0)); + + return null; + } + + public static class EQ extends BoolNode { public EQ(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "=="; } boolean doOp(long lhs, long rhs) { return lhs == rhs; } } + public static class LT extends BoolNode { public LT(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "<" ; } boolean doOp(long lhs, long rhs) { return lhs < rhs; } } + public static class LE extends BoolNode { public LE(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "<="; } boolean doOp(long lhs, long rhs) { return lhs <= rhs; } } +} diff --git a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java index e4d7f412..1086037d 100644 --- a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java @@ -23,7 +23,7 @@ public class ConstantNode extends Node { } @Override - public String label() { return "#"+_con; } + public String label() { return ""+_con; } @Override public String uniqueName() { return "Con_" + _nid; } @@ -38,5 +38,4 @@ public class ConstantNode extends Node { @Override public Node idealize() { return null; } - } diff --git a/src/main/java/com/seaofnodes/simple/node/DivNode.java b/src/main/java/com/seaofnodes/simple/node/DivNode.java index e3c3616a..3b3a4436 100644 --- a/src/main/java/com/seaofnodes/simple/node/DivNode.java +++ b/src/main/java/com/seaofnodes/simple/node/DivNode.java @@ -25,10 +25,16 @@ public class DivNode extends Node { return i1.value() == 0 ? TypeInteger.ZERO : TypeInteger.constant(i0.value()/i1.value()); + return i0.meet(i1); } return Type.BOTTOM; } @Override - public Node idealize() { return null; } + public Node idealize() { + // Div of 1. + if( in(2)._type.isConstant() && in(2)._type instanceof TypeInteger i && i.value()==1 ) + return in(1); + return null; + } } diff --git a/src/main/java/com/seaofnodes/simple/node/MulNode.java b/src/main/java/com/seaofnodes/simple/node/MulNode.java index 26c28281..36d5eb3e 100644 --- a/src/main/java/com/seaofnodes/simple/node/MulNode.java +++ b/src/main/java/com/seaofnodes/simple/node/MulNode.java @@ -23,10 +23,27 @@ public class MulNode extends Node { in(2)._type instanceof TypeInteger i1) { if (i0.isConstant() && i1.isConstant()) return TypeInteger.constant(i0.value()*i1.value()); + return i0.meet(i1); } return Type.BOTTOM; } @Override - public Node idealize() { return null; } + public Node idealize() { + Node lhs = in(1); + Node rhs = in(2); + Type t1 = lhs._type; + Type t2 = rhs._type; + + // Mul of 1. We do not check for (1*x) because this will already + // canonicalize to (x*1) + if ( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==1 ) + return lhs; + + // Move constants to RHS: con*arg becomes arg*con + if ( t1.isConstant() && !t2.isConstant() ) + return swap12(); + + return null; + } } diff --git a/src/main/java/com/seaofnodes/simple/node/MultiNode.java b/src/main/java/com/seaofnodes/simple/node/MultiNode.java new file mode 100644 index 00000000..7ba1aa19 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/MultiNode.java @@ -0,0 +1,7 @@ +package com.seaofnodes.simple.node; + +public abstract class MultiNode extends Node { + + public MultiNode(Node... inputs) { super(inputs); } + +} diff --git a/src/main/java/com/seaofnodes/simple/node/Node.java b/src/main/java/com/seaofnodes/simple/node/Node.java index 82357f9b..9fa0a95a 100644 --- a/src/main/java/com/seaofnodes/simple/node/Node.java +++ b/src/main/java/com/seaofnodes/simple/node/Node.java @@ -81,14 +81,7 @@ public abstract class Node { // line (because this is what a debugger typically displays by default) and // has to be robust with broken graph/nodes. @Override - public final String toString() { - // TODO: The print evolves in later chapters. For now, a simple - // recursive print does well. Later with control flow and later still - // with loops, we need some serious algorithm work to get a decent - // print. So you should consider the "print" to always be a work-in- - // progress as we march through the chapters. - return print(); - } + public final String toString() { return print(); } // This is a *deep* print. This version will fail on cycles, which we will // correct later when we can parse programs with loops. We print with a @@ -125,8 +118,6 @@ public abstract class Node { public boolean isCFG() { return false; } - - /** * Change a <em>def</em> into a Node. Keeps the edges correct, by removing * the corresponding <em>use->def</em> edge. This may make the original @@ -184,11 +175,11 @@ public abstract class Node { // Error is 'use' does not exist; ok for 'use' to be null. protected boolean delUse( Node use ) { Utils.del(_outputs, Utils.find(_outputs, use)); - return _outputs.size() == 0; + return _outputs.isEmpty(); } // Shortcut for "popping" n nodes. A "pop" is basically a - // set_def(last,null) followed by lowering the nIns() count. + // setDef(last,null) followed by lowering the nIns() count. void popN(int n) { for( int i=0; i<n; i++ ) { Node old_def = _inputs.removeLast(); @@ -212,6 +203,15 @@ public abstract class Node { // Mostly used for asserts and printing. boolean isDead() { return isUnused() && nIns()==0 && _type==null; } + + // Shortcuts to stop DCE mid-parse + // Add bogus null use to keep node alive + public <N extends Node> N keep() { return addUse(null); } + // Remove bogus null. + public <N extends Node> N unkeep() { delUse(null); return (N)this; } + // ------------------------------------------------------------------------ + // Graph-based optimizations + /** * We allow disabling peephole opt so that we can observe the * full graph, vs the optimized graph. @@ -226,7 +226,11 @@ public abstract class Node { * <li>in a future chapter we will look for a * <a href="https://en.wikipedia.org/wiki/Common_subexpression_elimination">Common Subexpression</a> * to eliminate.</li> - * <li>we ask the Node for a better replacement (again, none enabled in this chapter)</li> + * <li>we ask the Node for a better replacement. The "better replacement" + * is things like {@code (1+2)} becomes {@code 3} and {@code (1+(x+2))} becomes + * {@code (x+(1+2))}. By canonicalizing expressions we fold common addressing + * math constants, remove algebraic identities and generally simplify the + * code. </li> * </ul> */ public final Node peephole( ) { @@ -237,20 +241,37 @@ public abstract class Node { return this; // Peephole optimizations turned off // Replace constant computations from non-constants with a constant node - if (!(this instanceof ConstantNode) && type.isConstant()) { - kill(); // Kill `this` because replacing with a Constant - return new ConstantNode(type).peephole(); - } + if (!(this instanceof ConstantNode) && type.isConstant()) + return deadCodeElim(new ConstantNode(type).peephole()); // Future chapter: Global Value Numbering goes here // Ask each node for a better replacement Node n = idealize(); - if( n != null ) return n; + if( n != null ) // Something changed + // Recursively optimize + return deadCodeElim(n.peephole()); return this; // No progress } + // m is the new Node, self is the old. + // Return 'm', which may have zero uses but is alive nonetheless. + // If self has zero uses (and is not 'm'), {@link #kill} self. + private Node deadCodeElim(Node m) { + // If self is going dead and not being returned here (Nodes returned + // from peephole commonly have no uses (yet)), then kill self. + if( m != this && isUnused() ) { + // Killing self - and since self recursively kills self's inputs we + // might end up killing 'm', which we are returning as a live Node. + // So we add a bogus extra null output edge to stop kill(). + m.keep(); // Keep m alive + kill(); // Kill self because replacing with 'm' + m.unkeep(); // Okay to peephole m + } + return m; + } + /** * This function needs to be * <a href="https://en.wikipedia.org/wiki/Monotonic_function">Monotonic</a> @@ -269,8 +290,65 @@ public abstract class Node { */ public abstract Type compute(); + /** + * This function rewrites the current Node into a more "idealized" form. + * This is the bulk of our peephole rewrite rules, and we use this to + * e.g. turn arbitrary collections of adds and multiplies with mixed + * constants into a normal form that's easy for hardware to implement. + * Example: An array addressing expression: + * <pre> ary[idx+1]</pre> + * might turn into Sea-of-Nodes IR: + * <pre> (ary+12)+((idx+1) * 4)</pre> + * This expression can then be idealized into: + * <pre> ary + ((idx*4) + (12 + (1*4)))</pre> + * And more folding: + * <pre> ary + ((idx<<2) + 16)</pre> + * And during code-gen: + * <pre> MOV4 Rary,Ridx,16 // or some such hardware-specific notation </pre> + * <p> + * {@link #idealize} has a very specific calling convention: + * <ul> + * <li>If NO change is made, return {@code null} + * <li>If ANY change is made, return not-null; this can be {@code this} + * <li>The returned Node does NOT call {@link #peephole} on itself; the {@link #peephole} call will recursively peephole it. + * <li>Any NEW nodes that are not directly returned DO call {@link #peephole}. + * </ul> + * <p> + * Examples: + * <table border="3"> + * <tr><th> before </th><th> after </th><th>return </th><th>comment </th></tr> + * <tr><td>{@code (x+5) }</td><td>{@code (x+5) }</td><td>{@code null }</td><td>No change</td></tr> + * <tr><td>{@code (5+x) }</td><td>{@code (x+5) }</td><td>{@code this }</td><td>Swapped arguments</td></tr> + * <tr><td>{@code ((x+1)+2)}</td><td>{@code (x+(1+2))}</td><td>{@code (x+_) }</td><td>Returns 2 new Nodes</td></tr> + * </table> + * + * The last entry deserves more discussion. The new Node {@code (1+2)} + * created in {@link #idealize} calls {@link #peephole} (which then folds + * into a constant). The other new Node {@code (x+3)} does not call + * peephole, because it is returned and peephole itself will recursively + * call peephole. + * <p> + * Since idealize calls peephole and peephole calls idealize, you must be + * careful that all idealizations are <em>monotonic</em>: all transforms remove + * some feature, so that the set of available transforms always shrinks. + * If you don't, you risk an infinite peephole loop! + * + * @return Either a new or changed node, or null for no changes. + */ public abstract Node idealize(); + + // ------------------------------------------------------------------------ + // Peephole utilities + + // Swap inputs without letting either input go dead during the swap. + Node swap12() { + Node tmp = in(1); + _inputs.set(1,in(2)); + _inputs.set(2,tmp); + return this; + } + /** * Used to allow repeating tests in the same JVM. This just resets the * Node unique id generator, and is done as part of making a new Parser. diff --git a/src/main/java/com/seaofnodes/simple/node/NotNode.java b/src/main/java/com/seaofnodes/simple/node/NotNode.java new file mode 100644 index 00000000..e3683646 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/NotNode.java @@ -0,0 +1,28 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; + +public class NotNode extends Node { + public NotNode(Node in) { super(null, in); } + + @Override public String label() { return "Not"; } + + @Override public String glabel() { return "!"; } + + @Override + StringBuilder _print1(StringBuilder sb) { + in(1)._print0(sb.append("(!")); + return sb.append(")"); + } + + @Override + public Type compute() { + if( in(1)._type instanceof TypeInteger i0 ) + return i0.isConstant() ? TypeInteger.constant(i0.value()==0 ? 1 : 0) : i0; + return Type.BOTTOM; + } + + @Override + public Node idealize() { return null; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/ProjNode.java b/src/main/java/com/seaofnodes/simple/node/ProjNode.java new file mode 100644 index 00000000..2f49fe91 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/ProjNode.java @@ -0,0 +1,40 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeTuple; + +public class ProjNode extends Node { + + // Which slice of the incoming multi-part value + public final int _idx; + + // Debugging label + public final String _label; + + public ProjNode(MultiNode ctrl, int idx, String label) { + super(ctrl); + _idx = idx; + _label = label; + } + + @Override + public String label() { return _label; } + + @Override + StringBuilder _print1(StringBuilder sb) { return sb.append(_label); } + + @Override public boolean isCFG() { return _idx==0; } + + public MultiNode ctrl() { return (MultiNode)in(0); } + + @Override + public Type compute() { + Type t = ctrl()._type; + return t instanceof TypeTuple tt ? tt._types[_idx] : Type.BOTTOM; + } + + @Override + public Node idealize() { + return null; + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java index 7fa470f8..c9d6c9c8 100644 --- a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java @@ -32,7 +32,7 @@ public class ReturnNode extends Node { @Override public Type compute() { - return Type.BOTTOM; + return new TypeTuple(ctrl()._type,expr()._type); } @Override diff --git a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java index 379ef033..8ffeac64 100644 --- a/src/main/java/com/seaofnodes/simple/node/ScopeNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java @@ -7,12 +7,18 @@ import java.util.*; /** * The Scope node is purely a parser helper - it tracks names to nodes with a * stack of scopes. - * @see <a href="file:../../../../../../../../chapter03/README.md">chapter03/README.md</a> */ public class ScopeNode extends Node { /** - * A Stack of symbol tables; a symbol table is a map from names to node indices. + * The control is a name that binds to the currently active control + * node in the graph + */ + public static final String CTRL = "$ctrl"; + public static final String ARG0 = "arg"; + + /** + * Names for every input edge */ public final Stack<HashMap<String, Integer>> _scopes; @@ -49,7 +55,7 @@ public class ScopeNode extends Node { * Recover the names for all variable bindings. * The result is an array of names that is aligned with the * inputs to the Node. - * <p> + * * This is an expensive operation. */ public String[] reverseNames() { @@ -78,21 +84,21 @@ public class ScopeNode extends Node { } /** * Lookup a name in all scopes starting from most deeply nested. - * <p> + * * @param name Name to be looked up * @see #update(String, Node, int) */ public Node lookup( String name ) { return update(name,null,_scopes.size()-1); } /** * If the name is present in any scope, then redefine else null - * <p> + * * @param name Name being redefined * @param n The node to bind to the name */ public Node update( String name, Node n ) { return update(name,n,_scopes.size()-1); } /** * Both recursive lookup and update. - * <p> + * * A shared implementation allows us to create lazy phis both during * lookups and updates; the lazy phi creation is part of chapter 8. * diff --git a/src/main/java/com/seaofnodes/simple/node/StartNode.java b/src/main/java/com/seaofnodes/simple/node/StartNode.java index 99e240ad..022e276d 100644 --- a/src/main/java/com/seaofnodes/simple/node/StartNode.java +++ b/src/main/java/com/seaofnodes/simple/node/StartNode.java @@ -1,6 +1,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeTuple; /** * The Start node represents the start of the function. For now, we do not @@ -9,10 +10,14 @@ import com.seaofnodes.simple.type.Type; * will require Projections to extract the values. We discuss this in detail * in Chapter 9: Functions and Calls. */ -public class StartNode extends Node { +public class StartNode extends MultiNode { - public StartNode(/*arguments go here*/) { + final TypeTuple _args; + + public StartNode(Type[] args) { super(); + _args = new TypeTuple(args); + _type = _args; } @Override @@ -26,7 +31,7 @@ public class StartNode extends Node { @Override public boolean isCFG() { return true; } @Override - public Type compute() { return Type.BOTTOM; } + public TypeTuple compute() { return _args; } @Override public Node idealize() { return null; } diff --git a/src/main/java/com/seaofnodes/simple/node/SubNode.java b/src/main/java/com/seaofnodes/simple/node/SubNode.java index d2e32c61..58d757ea 100644 --- a/src/main/java/com/seaofnodes/simple/node/SubNode.java +++ b/src/main/java/com/seaofnodes/simple/node/SubNode.java @@ -19,15 +19,22 @@ public class SubNode extends Node { @Override public Type compute() { + // Sub of same is 0 + if( in(1)==in(2) ) + return TypeInteger.ZERO; + if (in(1)._type instanceof TypeInteger i0 && in(2)._type instanceof TypeInteger i1) { if (i0.isConstant() && i1.isConstant()) return TypeInteger.constant(i0.value()-i1.value()); + return i0.meet(i1); } return Type.BOTTOM; } @Override - public Node idealize() { return null; } + public Node idealize() { + return null; + } } diff --git a/src/main/java/com/seaofnodes/simple/type/Type.java b/src/main/java/com/seaofnodes/simple/type/Type.java index 89f6c715..1687f65a 100644 --- a/src/main/java/com/seaofnodes/simple/type/Type.java +++ b/src/main/java/com/seaofnodes/simple/type/Type.java @@ -24,17 +24,29 @@ public class Type { // type hierarchy are simple, not that the Type is conceptually simple. static final byte TBOT = 0; // Bottom (ALL) static final byte TTOP = 1; // Top (ANY) - static final byte TSIMPLE = 2; // End of the Simple Types - static final byte TINT = 3; // All Integers; see TypeInteger + static final byte TCTRL = 2; // Ctrl flow bottom + static final byte TSIMPLE = 3; // End of the Simple Types + static final byte TINT = 4; // All Integers; see TypeInteger + static final byte TTUPLE = 5; // Tuples; finite collections of unrelated Types, kept in parallel + public final byte _type; public boolean is_simple() { return _type < TSIMPLE; } - private static final String[] STRS = new String[]{"Bot","Top"}; + private static final String[] STRS = new String[]{"Bot","Top","Ctrl"}; protected Type(byte type) { _type = type; } - public static final Type BOTTOM = new Type( TBOT ); // ALL + public static final Type BOTTOM = new Type( TBOT ); // ALL + public static final Type TOP = new Type( TTOP ); // ANY + public static final Type CONTROL = new Type( TCTRL ); // Ctrl public boolean isConstant() { return _type == TTOP; } public StringBuilder _print(StringBuilder sb) {return is_simple() ? sb.append(STRS[_type]) : sb;} + + public Type meet(Type other) { return Type.BOTTOM; } + + @Override + public final String toString() { + return _print(new StringBuilder()).toString(); + } } diff --git a/src/main/java/com/seaofnodes/simple/type/TypeInteger.java b/src/main/java/com/seaofnodes/simple/type/TypeInteger.java index 13f816bf..9fb4b853 100644 --- a/src/main/java/com/seaofnodes/simple/type/TypeInteger.java +++ b/src/main/java/com/seaofnodes/simple/type/TypeInteger.java @@ -5,37 +5,59 @@ package com.seaofnodes.simple.type; */ public class TypeInteger extends Type { - public final static TypeInteger ZERO= new TypeInteger(0); + public final static TypeInteger TOP = new TypeInteger(false, 0); + public final static TypeInteger BOT = new TypeInteger(false, 1); + public final static TypeInteger ZERO= new TypeInteger(true, 0); + + private final boolean _is_con; /** - * The constant value observed for this type + * The constant value or + * if not constant then 0=bottom, 1=top. */ private final long _con; - public TypeInteger(long con) { + public TypeInteger(boolean is_con, long con) { super(TINT); + _is_con = is_con; _con = con; } - public static TypeInteger constant(long con) { return new TypeInteger(con); } + public static TypeInteger constant(long con) { return new TypeInteger(true, con); } - @Override - public String toString() { - return _print(new StringBuilder()).toString(); - } + public boolean isTop() { return !_is_con && _con==0; } + public boolean isBot() { return !_is_con && _con==1; } @Override - public StringBuilder _print(StringBuilder sb) { return sb.append(_con); } + public StringBuilder _print(StringBuilder sb) { + if (isTop()) return sb.append("IntTop"); + if (isBot()) return sb.append("IntBot"); + return sb.append(_con); + } @Override - public boolean isConstant() { return true; } + public boolean isConstant() { return _is_con; } public long value() { return _con; } @Override + public Type meet(Type other) { + if( this==other ) return this; + if (!(other instanceof TypeInteger i)) return super.meet(other); + // BOT wins + if ( isBot() ) return this; + if ( i.isBot() ) return i ; + // TOP loses + if ( i.isTop() ) return this; + if ( isTop() ) return i ; + assert isConstant() && i.isConstant(); + return _con==i._con ? this : TypeInteger.BOT; + } + + @Override public boolean equals( Object o ) { if( o==this ) return true; if( !(o instanceof TypeInteger i) ) return false; - return _con==i._con; + return _con==i._con && _is_con==i._is_con; } } diff --git a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java new file mode 100644 index 00000000..25663dda --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java @@ -0,0 +1,26 @@ +package com.seaofnodes.simple.type; + +public class TypeTuple extends Type { + + public final Type[] _types; + + public TypeTuple(Type... _types) { + super(TTUPLE); + this._types = _types; + } + + @Override + public Type meet(Type other) { + throw new UnsupportedOperationException("Meet on Tuple Type not yet implemented"); + } + + @Override + public StringBuilder _print(StringBuilder sb) { + sb.append("[ "); + for( Type t : _types ) + t._print(sb).append(","); + sb.setLength(sb.length()-1); + sb.append("]"); + return sb; + } +} diff --git a/src/test/java/com/seaofnodes/simple/Chapter01Test.java b/src/test/java/com/seaofnodes/simple/Chapter01Test.java index 311ca23b..3d568337 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter01Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter01Test.java @@ -14,7 +14,7 @@ public class Chapter01Test { ReturnNode ret = parser.parse(); StartNode start = Parser.START; - assertEquals(start, ret.ctrl()); + assertTrue(ret.ctrl() instanceof ProjNode); Node expr = ret.expr(); if( expr instanceof ConstantNode con ) { assertEquals(start,con.in(0)); diff --git a/src/test/java/com/seaofnodes/simple/Chapter03Test.java b/src/test/java/com/seaofnodes/simple/Chapter03Test.java index bdb1d9ea..950d87fe 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter03Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter03Test.java @@ -1,6 +1,7 @@ package com.seaofnodes.simple; import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; import org.junit.Test; import static org.junit.Assert.*; diff --git a/src/test/java/com/seaofnodes/simple/Chapter04Test.java b/src/test/java/com/seaofnodes/simple/Chapter04Test.java new file mode 100644 index 00000000..9f1535b3 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/Chapter04Test.java @@ -0,0 +1,127 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeInteger; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class Chapter04Test { + + @Test + public void testPeephole() { + Parser parser = new Parser("return 1+arg+2; #showGraph;"); + ReturnNode ret = parser.parse(); + assertEquals("return (arg+3);", ret.print()); + } + + @Test + public void testPeephole2() { + Parser parser = new Parser("return (1+arg)+2;"); + ReturnNode ret = parser.parse(); + assertEquals("return (arg+3);", ret.print()); + } + + @Test + public void testAdd0() { + Parser parser = new Parser("return 0+arg;"); + ReturnNode ret = parser.parse(); + assertEquals("return arg;", ret.print()); + } + + @Test + public void testAddAddMul() { + Parser parser = new Parser("return arg+0+arg;"); + ReturnNode ret = parser.parse(); + assertEquals("return (arg*2);", ret.print()); + } + + @Test + public void testPeephole3() { + Parser parser = new Parser("return 1+arg+2+arg+3; #showGraph;"); + ReturnNode ret = parser.parse(); + assertEquals("return ((arg*2)+6);", ret.print()); + } + + @Test + public void testMul1() { + Parser parser = new Parser("return 1*arg;"); + ReturnNode ret = parser.parse(); + assertEquals("return arg;", ret.print()); + } + + @Test + public void testVarArg() { + Parser parser = new Parser("return arg; #showGraph;", TypeInteger.BOT); + ReturnNode ret = parser.parse(); + assertTrue(ret.in(0) instanceof ProjNode); + assertTrue(ret.in(1) instanceof ProjNode); + } + + @Test + public void testConstantArg() { + Parser parser = new Parser("return arg; #showGraph;", TypeInteger.constant(2)); + ReturnNode ret = parser.parse(); + assertEquals("return 2;", ret.print()); + } + + @Test + public void testCompEq() { + Parser parser = new Parser("return 3==3; #showGraph;"); + ReturnNode ret = parser.parse(); + assertEquals("return 1;", ret.print()); + } + + @Test + public void testCompEq2() { + Parser parser = new Parser("return 3==4; #showGraph;"); + ReturnNode ret = parser.parse(); + assertEquals("return 0;", ret.print()); + } + + @Test + public void testCompNEq() { + Parser parser = new Parser("return 3!=3; #showGraph;"); + ReturnNode ret = parser.parse(); + assertEquals("return 0;", ret.print()); + } + + @Test + public void testCompNEq2() { + Parser parser = new Parser("return 3!=4; #showGraph;"); + ReturnNode ret = parser.parse(); + assertEquals("return 1;", ret.print()); + } + + @Test + public void testBug1() { + Parser parser = new Parser("int a=arg+1; int b=a; b=1; return a+2; #showGraph;"); + ReturnNode ret = parser.parse(); + assertEquals("return (arg+3);", ret.print()); + } + + @Test + public void testBug2() { + Parser parser = new Parser("int a=arg+1; a=a; return a; #showGraph;"); + ReturnNode ret = parser.parse(); + assertEquals("return (arg+1);", ret.print()); + } + + @Test + public void testBug3() { + try { + new Parser("inta=1; return a;").parse(); + fail(); + } catch( RuntimeException e ) { + assertEquals("Undefined name 'inta'",e.getMessage()); + } + } + + @Test + public void testBug4() { + Parser parser = new Parser("return -arg;"); + ReturnNode ret = parser.parse(); + assertEquals("return (-arg);", ret.print()); + } + +} |
