diff options
29 files changed, 1768 insertions, 39 deletions
@@ -27,7 +27,7 @@ Sea Of Nodes intermediate representation. The Simple language will be styled after a subset of C or Java * [Chapter 1](docs/chapter01/README.md): Script that returns an integer literal, i.e., an empty function that takes no arguments and returns a single integer value. The `return` statement. -* Chapter 2: Simple binary arithmetic such as addition, subtraction, multiplication, division +* [Chapter 2](docs/chapter02/README.md): Simple binary arithmetic such as addition, subtraction, multiplication, division with constants. Peephole optimization / simple constant folding. * Chapter 3: Local variables, and assignment statements. Read on RHS, SSA, more peephole optimization if local is a constant. diff --git a/docs/chapter02/02-demonstration-peephole.svg b/docs/chapter02/02-demonstration-peephole.svg new file mode 100644 index 00000000..90ab6ca4 --- /dev/null +++ b/docs/chapter02/02-demonstration-peephole.svg @@ -0,0 +1,54 @@ +<?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: chapter02 Pages: 1 --> +<svg width="114pt" height="220pt" viewBox="0.00 0.00 114.00 220.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 216)"> +<title>chapter02</title> +<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-216 110,-216 110,4 -4,4"/> +<g id="clust1" class="cluster"> +<title>cluster_Nodes</title> +<polygon fill="none" stroke="#000000" points="8,-8 8,-204 98,-204 98,-8 8,-8"/> +</g> +<!-- Start1 --> +<g id="node1" class="node"> +<title>Start1</title> +<polygon fill="#ffff00" stroke="#000000" points="80,-196 26,-196 26,-160 80,-160 80,-196"/> +<text text-anchor="middle" x="53" y="-173.8" font-family="Times,serif" font-size="14.00" fill="#000000">Start</text> +</g> +<!-- Con_5 --> +<g id="node2" class="node"> +<title>Con_5</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">#3</text> +</g> +<!-- Con_5->Start1 --> +<g id="edge1" class="edge"> +<title>Con_5->Start1</title> +<path fill="none" stroke="#000000" stroke-dasharray="1,5" d="M45.5234,-124.1686C46.5929,-131.869 47.8647,-141.0257 49.0532,-149.5834"/> +<polygon fill="#000000" stroke="#000000" points="45.6001,-150.1633 50.4426,-159.5867 52.5336,-149.2003 45.6001,-150.1633"/> +<text text-anchor="middle" x="43.2998" y="-126.5686" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text> +</g> +<!-- Return6 --> +<g id="node3" class="node"> +<title>Return6</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> +<!-- Return6->Start1 --> +<g id="edge2" class="edge"> +<title>Return6->Start1</title> +<path fill="none" stroke="#ff0000" d="M64.3196,-52.2322C69.9396,-62.3585 76.1294,-75.4211 79,-88 82.5597,-103.599 82.5597,-108.401 79,-124 76.9144,-133.1394 73.0765,-142.534 68.9781,-150.8724"/> +<polygon fill="#ff0000" stroke="#ff0000" points="65.8583,-149.2853 64.3196,-159.7678 72.0595,-152.5328 65.8583,-149.2853"/> +<text text-anchor="middle" x="62.096" y="-54.6322" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text> +</g> +<!-- Return6->Con_5 --> +<g id="edge3" class="edge"> +<title>Return6->Con_5</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/chapter02/02-demonstration-pre-peephole-nokill.svg b/docs/chapter02/02-demonstration-pre-peephole-nokill.svg new file mode 100644 index 00000000..3d4f53df --- /dev/null +++ b/docs/chapter02/02-demonstration-pre-peephole-nokill.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: chapter02 Pages: 1 --> +<svg width="258pt" height="220pt" viewBox="0.00 0.00 258.00 220.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 216)"> +<title>chapter02</title> +<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-216 254,-216 254,4 -4,4"/> +<g id="clust1" class="cluster"> +<title>cluster_Nodes</title> +<polygon fill="none" stroke="#000000" points="8,-8 8,-204 242,-204 242,-8 8,-8"/> +</g> +<!-- Start1 --> +<g id="node1" class="node"> +<title>Start1</title> +<polygon fill="#ffff00" stroke="#000000" points="165,-196 111,-196 111,-160 165,-160 165,-196"/> +<text text-anchor="middle" x="138" y="-173.8" font-family="Times,serif" font-size="14.00" fill="#000000">Start</text> +</g> +<!-- Con_2 --> +<g id="node2" class="node"> +<title>Con_2</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_2->Start1 --> +<g id="edge1" class="edge"> +<title>Con_2->Start1</title> +<path fill="none" stroke="#000000" stroke-dasharray="1,5" d="M60.8688,-119.5426C73.5668,-129.1664 90.9688,-142.3553 106.03,-153.7701"/> +<polygon fill="#000000" stroke="#000000" points="104.028,-156.6444 114.1118,-159.8953 108.2562,-151.0656 104.028,-156.6444"/> +<text text-anchor="middle" x="58.6452" y="-121.9426" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text> +</g> +<!-- Con_3 --> +<g id="node3" class="node"> +<title>Con_3</title> +<ellipse fill="none" stroke="#000000" cx="115" cy="-106" rx="27" ry="18"/> +<text text-anchor="middle" x="115" y="-101.8" font-family="Times,serif" font-size="14.00" fill="#000000">#2</text> +</g> +<!-- Con_3->Start1 --> +<g id="edge2" class="edge"> +<title>Con_3->Start1</title> +<path fill="none" stroke="#000000" stroke-dasharray="1,5" d="M120.6854,-123.7978C123.237,-131.7854 126.3069,-141.3955 129.1486,-150.2913"/> +<polygon fill="#000000" stroke="#000000" points="125.8432,-151.4462 132.2203,-159.9069 132.5113,-149.316 125.8432,-151.4462"/> +<text text-anchor="middle" x="118.4618" y="-126.1978" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text> +</g> +<!-- Add4 --> +<g id="node4" class="node"> +<title>Add4</title> +<ellipse fill="none" stroke="#000000" cx="89" cy="-34" rx="27" ry="18"/> +<text text-anchor="middle" x="89" y="-29.8" font-family="Times,serif" font-size="14.00" fill="#000000">+</text> +</g> +<!-- Add4->Con_2 --> +<g id="edge3" class="edge"> +<title>Add4->Con_2</title> +<path fill="none" stroke="#000000" d="M78.3311,-50.6992C72.6414,-59.6048 65.5223,-70.7476 59.1684,-80.693"/> +<polygon fill="#000000" stroke="#000000" points="56.0456,-79.08 53.6111,-89.3913 61.9445,-82.8487 56.0456,-79.08"/> +<text text-anchor="middle" x="80.5547" y="-53.0992" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">1</text> +</g> +<!-- Add4->Con_3 --> +<g id="edge4" class="edge"> +<title>Add4->Con_3</title> +<path fill="none" stroke="#000000" d="M95.427,-51.7978C98.3866,-59.9936 101.963,-69.8976 105.2447,-78.9855"/> +<polygon fill="#000000" stroke="#000000" points="101.9585,-80.1902 108.647,-88.407 108.5424,-77.8126 101.9585,-80.1902"/> +<text text-anchor="middle" x="93.2034" y="-54.1978" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">2</text> +</g> +<!-- Con_5 --> +<g id="node5" class="node"> +<title>Con_5</title> +<ellipse fill="none" stroke="#000000" cx="207" cy="-106" rx="27" ry="18"/> +<text text-anchor="middle" x="207" y="-101.8" font-family="Times,serif" font-size="14.00" fill="#000000">#3</text> +</g> +<!-- Con_5->Start1 --> +<g id="edge5" class="edge"> +<title>Con_5->Start1</title> +<path fill="none" stroke="#000000" stroke-dasharray="1,5" d="M192.3669,-121.2693C183.6447,-130.3708 172.3525,-142.1539 162.3214,-152.6212"/> +<polygon fill="#000000" stroke="#000000" points="159.6869,-150.3117 155.2947,-159.9533 164.7408,-155.1551 159.6869,-150.3117"/> +<text text-anchor="middle" x="194.5905" y="-123.6693" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text> +</g> +<!-- Return6 --> +<g id="node6" class="node"> +<title>Return6</title> +<polygon fill="#ffff00" stroke="#000000" points="211.1025,-52 156.8975,-52 156.8975,-16 211.1025,-16 211.1025,-52"/> +<text text-anchor="middle" x="184" y="-29.8" font-family="Times,serif" font-size="14.00" fill="#000000">Return</text> +</g> +<!-- Return6->Start1 --> +<g id="edge6" class="edge"> +<title>Return6->Start1</title> +<path fill="none" stroke="#ff0000" d="M178.1741,-52.2377C170.2935,-76.9072 156.1635,-121.1402 146.9358,-150.0269"/> +<polygon fill="#ff0000" stroke="#ff0000" points="143.4876,-149.3195 143.7786,-159.9104 150.1557,-151.4497 143.4876,-149.3195"/> +<text text-anchor="middle" x="175.9505" y="-54.6377" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text> +</g> +<!-- Return6->Con_5 --> +<g id="edge7" class="edge"> +<title>Return6->Con_5</title> +<path fill="none" stroke="#000000" d="M189.8039,-52.1686C192.3565,-60.1594 195.41,-69.7181 198.2308,-78.5486"/> +<polygon fill="#000000" stroke="#000000" points="194.9006,-79.6259 201.2777,-88.0866 201.5687,-77.4957 194.9006,-79.6259"/> +<text text-anchor="middle" x="187.5803" 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/chapter02/02-demonstration-pre-peephole.svg b/docs/chapter02/02-demonstration-pre-peephole.svg new file mode 100644 index 00000000..49624652 --- /dev/null +++ b/docs/chapter02/02-demonstration-pre-peephole.svg @@ -0,0 +1,87 @@ +<?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: chapter02 Pages: 1 --> +<svg width="186pt" height="292pt" viewBox="0.00 0.00 186.00 292.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 288)"> +<title>chapter02</title> +<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-288 182,-288 182,4 -4,4"/> +<g id="clust1" class="cluster"> +<title>cluster_Nodes</title> +<polygon fill="none" stroke="#000000" points="8,-8 8,-276 170,-276 170,-8 8,-8"/> +</g> +<!-- Start1 --> +<g id="node1" class="node"> +<title>Start1</title> +<polygon fill="#ffff00" stroke="#000000" points="90,-268 36,-268 36,-232 90,-232 90,-268"/> +<text text-anchor="middle" x="63" y="-245.8" font-family="Times,serif" font-size="14.00" fill="#000000">Start</text> +</g> +<!-- Con_2 --> +<g id="node2" class="node"> +<title>Con_2</title> +<ellipse fill="none" stroke="#000000" cx="63" cy="-178" rx="27" ry="18"/> +<text text-anchor="middle" x="63" y="-173.8" font-family="Times,serif" font-size="14.00" fill="#000000">#1</text> +</g> +<!-- Con_2->Start1 --> +<g id="edge1" class="edge"> +<title>Con_2->Start1</title> +<path fill="none" stroke="#000000" stroke-dasharray="1,5" d="M63,-196.1686C63,-203.869 63,-213.0257 63,-221.5834"/> +<polygon fill="#000000" stroke="#000000" points="59.5001,-221.5867 63,-231.5867 66.5001,-221.5868 59.5001,-221.5867"/> +<text text-anchor="middle" x="60.7764" y="-198.5686" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text> +</g> +<!-- Con_3 --> +<g id="node3" class="node"> +<title>Con_3</title> +<ellipse fill="none" stroke="#000000" cx="135" cy="-178" rx="27" ry="18"/> +<text text-anchor="middle" x="135" y="-173.8" font-family="Times,serif" font-size="14.00" fill="#000000">#2</text> +</g> +<!-- Con_3->Start1 --> +<g id="edge2" class="edge"> +<title>Con_3->Start1</title> +<path fill="none" stroke="#000000" stroke-dasharray="1,5" d="M119.7307,-193.2693C110.6292,-202.3708 98.8461,-214.1539 88.3788,-224.6212"/> +<polygon fill="#000000" stroke="#000000" points="85.6429,-222.4073 81.0467,-231.9533 90.5927,-227.3571 85.6429,-222.4073"/> +<text text-anchor="middle" x="117.5071" y="-195.6693" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text> +</g> +<!-- Add4 --> +<g id="node4" class="node"> +<title>Add4</title> +<ellipse fill="none" stroke="#000000" cx="63" cy="-106" rx="27" ry="18"/> +<text text-anchor="middle" x="63" y="-101.8" font-family="Times,serif" font-size="14.00" fill="#000000">+</text> +</g> +<!-- Add4->Con_2 --> +<g id="edge3" class="edge"> +<title>Add4->Con_2</title> +<path fill="none" stroke="#000000" d="M63,-124.1686C63,-131.869 63,-141.0257 63,-149.5834"/> +<polygon fill="#000000" stroke="#000000" points="59.5001,-149.5867 63,-159.5867 66.5001,-149.5868 59.5001,-149.5867"/> +<text text-anchor="middle" x="60.7764" y="-126.5686" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">1</text> +</g> +<!-- Add4->Con_3 --> +<g id="edge4" class="edge"> +<title>Add4->Con_3</title> +<path fill="none" stroke="#000000" d="M78.2693,-121.2693C88.197,-131.197 101.3153,-144.3153 112.4363,-155.4363"/> +<polygon fill="#000000" stroke="#000000" points="110.2067,-158.1564 119.7527,-162.7527 115.1564,-153.2067 110.2067,-158.1564"/> +<text text-anchor="middle" x="80.4929" y="-123.6693" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">2</text> +</g> +<!-- Return5 --> +<g id="node5" class="node"> +<title>Return5</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> +<!-- Return5->Start1 --> +<g id="edge5" class="edge"> +<title>Return5->Start1</title> +<path fill="none" stroke="#ff0000" d="M41.6804,-52.2322C36.0604,-62.3585 29.8706,-75.4211 27,-88 16.3208,-134.7969 13.0904,-150.0596 27,-196 29.9564,-205.7645 35.3252,-215.3771 41.03,-223.7238"/> +<polygon fill="#ff0000" stroke="#ff0000" points="38.2871,-225.9027 47.0095,-231.9169 43.9414,-221.7761 38.2871,-225.9027"/> +<text text-anchor="middle" x="39.4568" y="-54.6322" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#000000">0</text> +</g> +<!-- Return5->Add4 --> +<g id="edge6" class="edge"> +<title>Return5->Add4</title> +<path fill="none" stroke="#000000" d="M55.5234,-52.1686C56.5929,-59.869 57.8647,-69.0257 59.0532,-77.5834"/> +<polygon fill="#000000" stroke="#000000" points="55.6001,-78.1633 60.4426,-87.5867 62.5336,-77.2003 55.6001,-78.1633"/> +<text text-anchor="middle" x="53.2998" 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/chapter02/02-grammar.md b/docs/chapter02/02-grammar.md new file mode 100644 index 00000000..ce875eda --- /dev/null +++ b/docs/chapter02/02-grammar.md @@ -0,0 +1,40 @@ +# Grammar for Chapter 1 + +```antlrv4 +grammar SimpleLanguage; + +program + : statement EOF + ; + +statement + : 'return' expression ';' + ; + +expression + : additiveExpression + ; + +additiveExpression + : multiplicativeExpression (('+' | '-') multiplicativeExpression)* + ; + +multiplicativeExpression + : unaryExpression (('*' | '/') unaryExpression)* + ; + +unaryExpression + : ('-') unaryExpression + | primaryExpression + ; + +primaryExpression + : INTEGER_LITERAL + | '(' expression ')' + ; + +INTEGER_LITERAL + : [1-9][0-9]* + | [0] + ; +``` diff --git a/docs/chapter02/02-lattice.gv b/docs/chapter02/02-lattice.gv new file mode 100644 index 00000000..4faa0e11 --- /dev/null +++ b/docs/chapter02/02-lattice.gv @@ -0,0 +1,35 @@ +digraph Lattice { + // Define nodes + node [shape=circle, style=filled, fillcolor=lightblue]; + Top [label="⊤"]; + Bottom [label="⊥"]; + + A [label="MIN_INT"]; + B [label="..." shape=none style=none]; + C [label="-1"]; + D [label="0"]; + E [label="1"]; + F [label="..." shape=none style=none]; + G [label="MAX_INT"]; + + + // Define edges + Top -> A [arrowhead=none]; + Top -> B [arrowhead=none]; + Top -> C [arrowhead=none]; + Top -> D [arrowhead=none]; + Top -> E [arrowhead=none]; + Top -> F [arrowhead=none]; + Top -> G [arrowhead=none]; + A -> Bottom [arrowhead=none]; + B -> Bottom [arrowhead=none]; + C -> Bottom [arrowhead=none]; + D -> Bottom [arrowhead=none]; + E -> Bottom [arrowhead=none]; + F -> Bottom [arrowhead=none]; + G -> Bottom [arrowhead=none]; + + // Additional customization + {rank=same; A; B; C; D; E; F; G; } + {rank=same; Bottom;} +} diff --git a/docs/chapter02/02-lattice.svg b/docs/chapter02/02-lattice.svg new file mode 100644 index 00000000..218acc6a --- /dev/null +++ b/docs/chapter02/02-lattice.svg @@ -0,0 +1,143 @@ +<?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="548pt" height="266pt" + viewBox="0.00 0.00 547.59 265.88" xmlns="http://www.w3.org/2000/svg"> + <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 261.88)"> + <title>Lattice</title> + <polygon fill="white" stroke="transparent" points="-4,4 -4,-261.88 543.59,-261.88 543.59,4 -4,4"/> + <!-- Top --> + <g id="node1" class="node"> + <title>Top</title> + <ellipse fill="lightblue" stroke="black" cx="269.65" cy="-238.39" rx="19.5" ry="19.5"/> + <text text-anchor="middle" x="269.65" y="-234.69" font-family="Times,serif" font-size="14.00">⊤</text> + </g> + <!-- A --> + <g id="node3" class="node"> + <title>A</title> + <ellipse fill="lightblue" stroke="black" cx="52.65" cy="-128.94" rx="52.79" ry="52.79"/> + <text text-anchor="middle" x="52.65" y="-125.24" font-family="Times,serif" font-size="14.00">MIN_INT</text> + </g> + <!-- Top->A --> + <g id="edge1" class="edge"> + <title>Top->A</title> + <path fill="none" stroke="black" + d="M250.55,-233.09C220.3,-225.77 160.13,-209.08 114.65,-182.89 106.3,-178.08 98.02,-172.1 90.35,-165.89"/> + </g> + <!-- B --> + <g id="node4" class="node"> + <title>B</title> + <text text-anchor="middle" x="150.65" y="-125.24" font-family="Times,serif" font-size="14.00">...</text> + </g> + <!-- Top->B --> + <g id="edge2" class="edge"> + <title>Top->B</title> + <path fill="none" stroke="black" + d="M252.34,-228.66C234.5,-219.09 206.55,-202.47 186.65,-182.89 175.87,-172.28 166.38,-158 159.86,-146.95"/> + </g> + <!-- C --> + <g id="node5" class="node"> + <title>C</title> + <ellipse fill="lightblue" stroke="black" cx="214.65" cy="-128.94" rx="19.5" ry="19.5"/> + <text text-anchor="middle" x="214.65" y="-125.24" font-family="Times,serif" font-size="14.00">-1</text> + </g> + <!-- Top->C --> + <g id="edge3" class="edge"> + <title>Top->C</title> + <path fill="none" stroke="black" d="M261.05,-220.6C250.72,-200.41 233.48,-166.74 223.18,-146.61"/> + </g> + <!-- D --> + <g id="node6" class="node"> + <title>D</title> + <ellipse fill="lightblue" stroke="black" cx="269.65" cy="-128.94" rx="18" ry="18"/> + <text text-anchor="middle" x="269.65" y="-125.24" font-family="Times,serif" font-size="14.00">0</text> + </g> + <!-- Top->D --> + <g id="edge4" class="edge"> + <title>Top->D</title> + <path fill="none" stroke="black" d="M269.65,-218.67C269.65,-198.47 269.65,-166.57 269.65,-147.03"/> + </g> + <!-- E --> + <g id="node7" class="node"> + <title>E</title> + <ellipse fill="lightblue" stroke="black" cx="323.65" cy="-128.94" rx="18" ry="18"/> + <text text-anchor="middle" x="323.65" y="-125.24" font-family="Times,serif" font-size="14.00">1</text> + </g> + <!-- Top->E --> + <g id="edge5" class="edge"> + <title>Top->E</title> + <path fill="none" stroke="black" d="M278.08,-220.6C288.46,-199.94 305.94,-165.16 315.97,-145.22"/> + </g> + <!-- F --> + <g id="node8" class="node"> + <title>F</title> + <text text-anchor="middle" x="386.65" y="-125.24" font-family="Times,serif" font-size="14.00">...</text> + </g> + <!-- Top->F --> + <g id="edge6" class="edge"> + <title>Top->F</title> + <path fill="none" stroke="black" + d="M286.52,-228.57C303.92,-218.93 331.18,-202.25 350.65,-182.89 361.2,-172.38 370.57,-158.37 377.1,-147.4"/> + </g> + <!-- G --> + <g id="node9" class="node"> + <title>G</title> + <ellipse fill="lightblue" stroke="black" cx="485.65" cy="-128.94" rx="53.89" ry="53.89"/> + <text text-anchor="middle" x="485.65" y="-125.24" font-family="Times,serif" font-size="14.00">MAX_INT</text> + </g> + <!-- Top->G --> + <g id="edge7" class="edge"> + <title>Top->G</title> + <path fill="none" stroke="black" + d="M288.46,-233C318.29,-225.56 377.63,-208.69 422.65,-182.89 430.83,-178.2 438.98,-172.41 446.57,-166.39"/> + </g> + <!-- Bottom --> + <g id="node2" class="node"> + <title>Bottom</title> + <ellipse fill="lightblue" stroke="black" cx="269.65" cy="-19.5" rx="19.5" ry="19.5"/> + <text text-anchor="middle" x="269.65" y="-15.8" font-family="Times,serif" font-size="14.00">⊥</text> + </g> + <!-- A->Bottom --> + <g id="edge8" class="edge"> + <title>A->Bottom</title> + <path fill="none" stroke="black" + d="M90.35,-91.99C98.02,-85.79 106.3,-79.8 114.65,-75 160.13,-48.81 220.3,-32.11 250.55,-24.79"/> + </g> + <!-- B->Bottom --> + <g id="edge9" class="edge"> + <title>B->Bottom</title> + <path fill="none" stroke="black" + d="M159.86,-110.94C166.38,-99.88 175.87,-85.6 186.65,-75 206.55,-55.41 234.5,-38.79 252.34,-29.22"/> + </g> + <!-- C->Bottom --> + <g id="edge10" class="edge"> + <title>C->Bottom</title> + <path fill="none" stroke="black" d="M223.24,-111.15C233.57,-90.97 250.81,-57.3 261.11,-37.17"/> + </g> + <!-- D->Bottom --> + <g id="edge11" class="edge"> + <title>D->Bottom</title> + <path fill="none" stroke="black" d="M269.65,-110.68C269.65,-91.1 269.65,-59.3 269.65,-39.16"/> + </g> + <!-- E->Bottom --> + <g id="edge12" class="edge"> + <title>E->Bottom</title> + <path fill="none" stroke="black" d="M315.91,-112.55C305.88,-92.59 288.44,-57.9 278.08,-37.28"/> + </g> + <!-- F->Bottom --> + <g id="edge13" class="edge"> + <title>F->Bottom</title> + <path fill="none" stroke="black" + d="M377.1,-110.49C370.57,-99.52 361.2,-85.5 350.65,-75 331.18,-55.63 303.92,-38.95 286.52,-29.31"/> + </g> + <!-- G->Bottom --> + <g id="edge14" class="edge"> + <title>G->Bottom</title> + <path fill="none" stroke="black" + d="M446.57,-91.49C438.98,-85.48 430.83,-79.69 422.65,-75 377.63,-49.19 318.29,-32.33 288.46,-24.88"/> + </g> + </g> +</svg> diff --git a/docs/chapter02/02-post-peephole-ex1.gv b/docs/chapter02/02-post-peephole-ex1.gv new file mode 100644 index 00000000..6c2b23ae --- /dev/null +++ b/docs/chapter02/02-post-peephole-ex1.gv @@ -0,0 +1,17 @@ +digraph chapter02 { +/* +return 1+2*3+-5; +*/ + rankdir=BT; + ordering="in"; + concentrate="true"; + subgraph cluster_Nodes { + Start1 [ shape=box style=filled fillcolor=yellow label="Start" ]; + Con_13 [ label="#2" ]; + Return14 [ shape=box style=filled fillcolor=yellow label="Return" ]; + } + edge [ fontname=Helvetica, fontsize=8 ]; + Con_13 -> Start1[taillabel=0 style=dotted]; + Return14 -> Start1[taillabel=0 color=red]; + Return14 -> Con_13[taillabel=1]; +} diff --git a/docs/chapter02/02-post-peephole-ex1.svg b/docs/chapter02/02-post-peephole-ex1.svg new file mode 100644 index 00000000..db27e30d --- /dev/null +++ b/docs/chapter02/02-post-peephole-ex1.svg @@ -0,0 +1,56 @@ +<?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: chapter02 Pages: 1 --> +<svg width="114pt" height="220pt" + viewBox="0.00 0.00 114.00 220.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 216)"> +<title>chapter02</title> +<polygon fill="white" stroke="transparent" points="-4,4 -4,-216 110,-216 110,4 -4,4"/> +<g id="clust1" class="cluster"> +<title>cluster_Nodes</title> +<polygon fill="none" stroke="black" points="8,-8 8,-204 98,-204 98,-8 8,-8"/> +</g> +<!-- Start1 --> +<g id="node1" class="node"> +<title>Start1</title> +<polygon fill="yellow" stroke="black" points="80,-196 26,-196 26,-160 80,-160 80,-196"/> +<text text-anchor="middle" x="53" y="-174.3" font-family="Times,serif" font-size="14.00">Start</text> +</g> +<!-- Con_13 --> +<g id="node2" class="node"> +<title>Con_13</title> +<ellipse fill="none" stroke="black" cx="43" cy="-106" rx="27" ry="18"/> +<text text-anchor="middle" x="43" y="-102.3" font-family="Times,serif" font-size="14.00">#2</text> +</g> +<!-- Con_13->Start1 --> +<g id="edge1" class="edge"> +<title>Con_13->Start1</title> +<path fill="none" stroke="black" stroke-dasharray="1,5" d="M45.42,-123.95C46.52,-131.65 47.85,-140.97 49.09,-149.64"/> +<polygon fill="black" stroke="black" points="45.65,-150.32 50.53,-159.72 52.58,-149.33 45.65,-150.32"/> +<text text-anchor="middle" x="42.42" y="-126.55" font-family="Helvetica,sans-Serif" font-size="8.00">0</text> +</g> +<!-- Return14 --> +<g id="node3" class="node"> +<title>Return14</title> +<polygon fill="yellow" stroke="black" points="86,-52 20,-52 20,-16 86,-16 86,-52"/> +<text text-anchor="middle" x="53" y="-30.3" font-family="Times,serif" font-size="14.00">Return</text> +</g> +<!-- Return14->Start1 --> +<g id="edge2" class="edge"> +<title>Return14->Start1</title> +<path fill="none" stroke="red" d="M64.05,-52.39C69.74,-62.38 76.08,-75.42 79,-88 82.62,-103.59 82.62,-108.41 79,-124 76.9,-133.04 73.03,-142.32 68.91,-150.53"/> +<polygon fill="red" stroke="red" points="65.69,-149.14 64.05,-159.61 71.86,-152.45 65.69,-149.14"/> +<text text-anchor="middle" x="61.05" y="-54.99" font-family="Helvetica,sans-Serif" font-size="8.00">0</text> +</g> +<!-- Return14->Con_13 --> +<g id="edge3" class="edge"> +<title>Return14->Con_13</title> +<path fill="none" stroke="black" d="M50.53,-52.3C49.43,-60.02 48.1,-69.29 46.87,-77.89"/> +<polygon fill="black" stroke="black" points="43.39,-77.5 45.44,-87.9 50.32,-78.49 43.39,-77.5"/> +<text text-anchor="middle" x="47.53" y="-54.9" font-family="Helvetica,sans-Serif" font-size="8.00">1</text> +</g> +</g> +</svg> diff --git a/docs/chapter02/02-pre-peephole-ex1.gv b/docs/chapter02/02-pre-peephole-ex1.gv new file mode 100644 index 00000000..411de5ee --- /dev/null +++ b/docs/chapter02/02-pre-peephole-ex1.gv @@ -0,0 +1,34 @@ +digraph chapter02 { +/* +return 1+2*3+-5; +*/ + rankdir=BT; + ordering="in"; + concentrate="true"; + subgraph cluster_Nodes { + Start1 [ shape=box style=filled fillcolor=yellow label="Start" ]; + Con_2 [ label="#1" ]; + Con_3 [ label="#2" ]; + Con_4 [ label="#3" ]; + Mul5 [ label="*" ]; + Con_6 [ label="#5" ]; + Minus7 [ label="-" ]; + Add8 [ label="+" ]; + Add9 [ label="+" ]; + Return10 [ shape=box style=filled fillcolor=yellow label="Return" ]; + } + edge [ fontname=Helvetica, fontsize=8 ]; + Con_2 -> Start1[taillabel=0 style=dotted]; + Con_3 -> Start1[taillabel=0 style=dotted]; + Con_4 -> Start1[taillabel=0 style=dotted]; + Mul5 -> Con_3[taillabel=1]; + Mul5 -> Con_4[taillabel=2]; + Con_6 -> Start1[taillabel=0 style=dotted]; + Minus7 -> Con_6[taillabel=1]; + Add8 -> Mul5[taillabel=1]; + Add8 -> Minus7[taillabel=2]; + Add9 -> Con_2[taillabel=1]; + Add9 -> Add8[taillabel=2]; + Return10 -> Start1[taillabel=0 color=red]; + Return10 -> Add9[taillabel=1]; +} diff --git a/docs/chapter02/02-pre-peephole-ex1.svg b/docs/chapter02/02-pre-peephole-ex1.svg new file mode 100644 index 00000000..29c76bd8 --- /dev/null +++ b/docs/chapter02/02-pre-peephole-ex1.svg @@ -0,0 +1,168 @@ +<?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: chapter02 Pages: 1 --> +<svg width="278pt" height="436pt" + viewBox="0.00 0.00 278.00 436.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 432)"> +<title>chapter02</title> +<polygon fill="white" stroke="transparent" points="-4,4 -4,-432 274,-432 274,4 -4,4"/> +<g id="clust1" class="cluster"> +<title>cluster_Nodes</title> +<polygon fill="none" stroke="black" points="8,-8 8,-420 262,-420 262,-8 8,-8"/> +</g> +<!-- Start1 --> +<g id="node1" class="node"> +<title>Start1</title> +<polygon fill="yellow" stroke="black" points="162,-412 108,-412 108,-376 162,-376 162,-412"/> +<text text-anchor="middle" x="135" y="-390.3" font-family="Times,serif" font-size="14.00">Start</text> +</g> +<!-- Con_2 --> +<g id="node2" class="node"> +<title>Con_2</title> +<ellipse fill="none" stroke="black" cx="222" cy="-178" rx="27" ry="18"/> +<text text-anchor="middle" x="222" y="-174.3" font-family="Times,serif" font-size="14.00">#1</text> +</g> +<!-- Con_2->Start1 --> +<g id="edge1" class="edge"> +<title>Con_2->Start1</title> +<path fill="none" stroke="black" stroke-dasharray="1,5" d="M230.65,-195.54C245.5,-226.47 271.62,-294.07 243,-340 227.56,-364.77 196.8,-378.29 172.04,-385.46"/> +<polygon fill="black" stroke="black" points="170.97,-382.13 162.2,-388.08 172.77,-388.89 170.97,-382.13"/> +<text text-anchor="middle" x="227.65" y="-198.14" font-family="Helvetica,sans-Serif" font-size="8.00">0</text> +</g> +<!-- Con_3 --> +<g id="node3" class="node"> +<title>Con_3</title> +<ellipse fill="none" stroke="black" cx="63" cy="-322" rx="27" ry="18"/> +<text text-anchor="middle" x="63" y="-318.3" font-family="Times,serif" font-size="14.00">#2</text> +</g> +<!-- Con_3->Start1 --> +<g id="edge2" class="edge"> +<title>Con_3->Start1</title> +<path fill="none" stroke="black" stroke-dasharray="1,5" d="M77.57,-337.17C86.92,-346.25 99.3,-358.29 110.17,-368.86"/> +<polygon fill="black" stroke="black" points="107.79,-371.43 117.4,-375.89 112.67,-366.41 107.79,-371.43"/> +<text text-anchor="middle" x="74.57" y="-339.77" font-family="Helvetica,sans-Serif" font-size="8.00">0</text> +</g> +<!-- Con_4 --> +<g id="node4" class="node"> +<title>Con_4</title> +<ellipse fill="none" stroke="black" cx="135" cy="-322" rx="27" ry="18"/> +<text text-anchor="middle" x="135" y="-318.3" font-family="Times,serif" font-size="14.00">#3</text> +</g> +<!-- Con_4->Start1 --> +<g id="edge3" class="edge"> +<title>Con_4->Start1</title> +<path fill="none" stroke="black" stroke-dasharray="1,5" d="M135,-340.3C135,-348.02 135,-357.29 135,-365.89"/> +<polygon fill="black" stroke="black" points="131.5,-365.9 135,-375.9 138.5,-365.9 131.5,-365.9"/> +<text text-anchor="middle" x="132" y="-342.9" font-family="Helvetica,sans-Serif" font-size="8.00">0</text> +</g> +<!-- Mul5 --> +<g id="node5" class="node"> +<title>Mul5</title> +<ellipse fill="none" stroke="black" cx="135" cy="-250" rx="27" ry="18"/> +<text text-anchor="middle" x="135" y="-246.3" font-family="Times,serif" font-size="14.00">*</text> +</g> +<!-- Mul5->Con_3 --> +<g id="edge4" class="edge"> +<title>Mul5->Con_3</title> +<path fill="none" stroke="black" d="M120.43,-265.17C110.25,-275.06 96.48,-288.45 84.97,-299.64"/> +<polygon fill="black" stroke="black" points="82.53,-297.13 77.8,-306.62 87.41,-302.15 82.53,-297.13"/> +<text text-anchor="middle" x="117.43" y="-267.77" font-family="Helvetica,sans-Serif" font-size="8.00">1</text> +</g> +<!-- Mul5->Con_4 --> +<g id="edge5" class="edge"> +<title>Mul5->Con_4</title> +<path fill="none" stroke="black" d="M135,-268.3C135,-276.02 135,-285.29 135,-293.89"/> +<polygon fill="black" stroke="black" points="131.5,-293.9 135,-303.9 138.5,-293.9 131.5,-293.9"/> +<text text-anchor="middle" x="132" y="-270.9" font-family="Helvetica,sans-Serif" font-size="8.00">2</text> +</g> +<!-- Con_6 --> +<g id="node6" class="node"> +<title>Con_6</title> +<ellipse fill="none" stroke="black" cx="207" cy="-322" rx="27" ry="18"/> +<text text-anchor="middle" x="207" y="-318.3" font-family="Times,serif" font-size="14.00">#5</text> +</g> +<!-- Con_6->Start1 --> +<g id="edge6" class="edge"> +<title>Con_6->Start1</title> +<path fill="none" stroke="black" stroke-dasharray="1,5" d="M192.43,-337.17C183.08,-346.25 170.7,-358.29 159.83,-368.86"/> +<polygon fill="black" stroke="black" points="157.33,-366.41 152.6,-375.89 162.21,-371.43 157.33,-366.41"/> +<text text-anchor="middle" x="189.43" y="-339.77" font-family="Helvetica,sans-Serif" font-size="8.00">0</text> +</g> +<!-- Minus7 --> +<g id="node7" class="node"> +<title>Minus7</title> +<ellipse fill="none" stroke="black" cx="207" cy="-250" rx="27" ry="18"/> +<text text-anchor="middle" x="207" y="-246.3" font-family="Times,serif" font-size="14.00">-</text> +</g> +<!-- Minus7->Con_6 --> +<g id="edge7" class="edge"> +<title>Minus7->Con_6</title> +<path fill="none" stroke="black" d="M207,-268.3C207,-276.02 207,-285.29 207,-293.89"/> +<polygon fill="black" stroke="black" points="203.5,-293.9 207,-303.9 210.5,-293.9 203.5,-293.9"/> +<text text-anchor="middle" x="204" y="-270.9" font-family="Helvetica,sans-Serif" font-size="8.00">1</text> +</g> +<!-- Add8 --> +<g id="node8" class="node"> +<title>Add8</title> +<ellipse fill="none" stroke="black" cx="140" cy="-178" rx="27" ry="18"/> +<text text-anchor="middle" x="140" y="-174.3" font-family="Times,serif" font-size="14.00">+</text> +</g> +<!-- Add8->Mul5 --> +<g id="edge8" class="edge"> +<title>Add8->Mul5</title> +<path fill="none" stroke="black" d="M138.76,-196.3C138.21,-204.02 137.55,-213.29 136.94,-221.89"/> +<polygon fill="black" stroke="black" points="133.44,-221.67 136.22,-231.9 140.43,-222.17 133.44,-221.67"/> +<text text-anchor="middle" x="135.76" y="-198.9" font-family="Helvetica,sans-Serif" font-size="8.00">1</text> +</g> +<!-- Add8->Minus7 --> +<g id="edge9" class="edge"> +<title>Add8->Minus7</title> +<path fill="none" stroke="black" d="M153.88,-193.5C163.14,-203.17 175.47,-216.06 185.93,-226.98"/> +<polygon fill="black" stroke="black" points="183.69,-229.71 193.14,-234.51 188.75,-224.87 183.69,-229.71"/> +<text text-anchor="middle" x="150.88" y="-196.1" font-family="Helvetica,sans-Serif" font-size="8.00">2</text> +</g> +<!-- Add9 --> +<g id="node9" class="node"> +<title>Add9</title> +<ellipse fill="none" stroke="black" cx="140" cy="-106" rx="27" ry="18"/> +<text text-anchor="middle" x="140" y="-102.3" font-family="Times,serif" font-size="14.00">+</text> +</g> +<!-- Add9->Con_2 --> +<g id="edge10" class="edge"> +<title>Add9->Con_2</title> +<path fill="none" stroke="black" d="M156.2,-120.83C168.18,-131.06 184.69,-145.15 198.18,-156.66"/> +<polygon fill="black" stroke="black" points="196.23,-159.61 206.11,-163.44 200.78,-154.28 196.23,-159.61"/> +<text text-anchor="middle" x="153.2" y="-123.43" font-family="Helvetica,sans-Serif" font-size="8.00">1</text> +</g> +<!-- Add9->Add8 --> +<g id="edge11" class="edge"> +<title>Add9->Add8</title> +<path fill="none" stroke="black" d="M140,-124.3C140,-132.02 140,-141.29 140,-149.89"/> +<polygon fill="black" stroke="black" points="136.5,-149.9 140,-159.9 143.5,-149.9 136.5,-149.9"/> +<text text-anchor="middle" x="137" y="-126.9" font-family="Helvetica,sans-Serif" font-size="8.00">2</text> +</g> +<!-- Return10 --> +<g id="node10" class="node"> +<title>Return10</title> +<polygon fill="yellow" stroke="black" points="125,-52 59,-52 59,-16 125,-16 125,-52"/> +<text text-anchor="middle" x="92" y="-30.3" font-family="Times,serif" font-size="14.00">Return</text> +</g> +<!-- Return10->Start1 --> +<g id="edge12" class="edge"> +<title>Return10->Start1</title> +<path fill="none" stroke="red" d="M75.73,-52.27C53.67,-77.55 17,-127.09 17,-177 17,-177 17,-177 17,-251 17,-290.8 4.09,-307.45 27,-340 43.69,-363.72 74.05,-377.3 98.34,-384.76"/> +<polygon fill="red" stroke="red" points="97.41,-388.14 107.99,-387.51 99.33,-381.4 97.41,-388.14"/> +<text text-anchor="middle" x="72.73" y="-54.87" font-family="Helvetica,sans-Serif" font-size="8.00">0</text> +</g> +<!-- Return10->Add9 --> +<g id="edge13" class="edge"> +<title>Return10->Add9</title> +<path fill="none" stroke="black" d="M103.87,-52.3C109.82,-60.98 117.12,-71.63 123.6,-81.08"/> +<polygon fill="black" stroke="black" points="120.71,-83.06 129.25,-89.33 126.49,-79.1 120.71,-83.06"/> +<text text-anchor="middle" x="100.87" y="-54.9" font-family="Helvetica,sans-Serif" font-size="8.00">1</text> +</g> +</g> +</svg> diff --git a/docs/chapter02/Makefile b/docs/chapter02/Makefile new file mode 100644 index 00000000..12659b8f --- /dev/null +++ b/docs/chapter02/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/chapter02/README.md b/docs/chapter02/README.md new file mode 100644 index 00000000..2a9b3d06 --- /dev/null +++ b/docs/chapter02/README.md @@ -0,0 +1,255 @@ +# Chapter 2: Arithmetic + +In this chapter we extend the language grammar to include arithmetic operations such as addition, subtraction, +multiplication, division, and unary minus. This allows us to write statements such as: + +``` +return 1 + 2 * 3 + -5; +``` + +Here is the [complete language grammar](02-grammar.md) for this chapter. + +You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter02) 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-chapter01...linear-chapter02) it to the previous chapter. + +## Extensions to Intermediate Representation + +In [Chapter 1](../chapter01/README.md) we introduced following nodes. + +| Node Name | Type | Description | Inputs | Value | +|-----------|---------|------------------------------------|------------------------------------------------------------------|-------------------------------------------------------| +| Start | Control | Start of function | None | None for now as we do not have function arguments yet | +| 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 | + +We extend the set of nodes by adding following additional node types. + +| Node Name | Type | Description | Inputs | Value | +|-----------|------|-------------------------------|------------------------------------------------------------|-----------------------------| +| 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 | + +## Peephole Optimizations + +Nodes in the graph can be peephole-optimized. The graph is viewed through a +"peephole", a small chunk of graph, and if a certain pattern is detected we +locally rewrite the graph. + +During parsing, these peephole optimizations are particularly easy to check and +apply: there are no uses (yet) of a just-created Node from a just-parsed piece +of syntax, so there's no effort to the "rewrite" part of the problem. We just +replace in-place before installing Nodes into the graph. + +This replacement might allow us to *kill* the unused inputs from the replaced +Node, basically doing a modest Dead Code Elimination during parsing. + +E.g. Suppose we already parsed out a constant `1`, and a constant `2`; then when we +parse an `Add(1,2)`, the peephole rule for constant math replaces the Add with a +constant `3`. At this point, we also *kill* the unused `Add`, which recursively +may *kill* the unused constants `1` and `2`. + + + +## Constant Folding and Constant Propagation + +In this chapter and next we focus on a particular peephole optimization: +constant folding and constant propagation. Since we do not have non-constant values +until [Chapter 4](../chapter04/README.md), the main feature we demonstrate now is constant folding. +However, we introduce some additional ideas into the compiler at this stage, to +set the scene for Chapter 4. + +It is useful for the compiler to know at various points of the program whether +a node's value is a constant. The compiler can use this knowledge to perform various +optimizations such as: + +* Evaluate expressions at compile time and replace an expression with a + constant. This idea can be extended in a number of ways and is called + [Partial Evaluation](https://en.wikipedia.org/wiki/Partial_evaluation). +* The compiler may be able to identify regions of code that are dead and no + longer needed, such as when a conditional branch always take one of the + branches ([Chapter 6](../chapter06/README.md)). +* Pointers may be known to be not-null, and a null check can be skipped ([Chapter 10](../chapter10/README.md)). +* Array indices may be known to be in-range, and a range check can be skipped. +* Many additional optimizations are possible when the compiler learns more + about the possible set of Node values. + +In order to achieve above, we need a way to talk about what kinds of values a Node can take +on at runtime - we call these Types, and we annotate Nodes with Types. + +The Type annotation serves two purposes: + +* it defines the set of operations allowed on the Node +* it defines the set of values the Node takes on + +The Type itself is represented as a Java class; the Java classes are used as +convenient to represent the Type's structure; all types are subtypes of the +class `Type`. For now, we only have the following hierarchy of types: + +``` +Type ++-- TypeInteger +``` + +It turns out 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) +. Our lattice has the following structure: + + + +Our lattice elements can be one of three types: + +* The highest element is "top", denoted by T; assigning T means that the Node's value may or may not be a compile time constant. +* All elements in the middle are constants. +* The lowest is "bottom", denoted by ⊥; assigning ⊥ means that we know that the Node's value is **not** a compile time constant. + +An invariant of peephole optimizations is that the type of a Node always moves +*up* the lattice (towards "top"); peepholes are *pessmistic* assuming the worst +until they can prove better. A later *optimistic* optimization will start all +Nodes at *top* and move Types *down* the lattice as eager assumptions are +proven wrong. + +In later chapters we will explore extending this lattice, as it frequently +forms the heart of core optimizations we want our compiler to do. + +We add a `_type` field to every Node, to store its current computed best +`Type`. We need a field to keep the optimizer runtime linear, and later when +doing an optimistic version of constant propagation (called [Sparse Conditional +Constant Propagation](https://en.wikipedia.org/wiki/Sparse_conditional_constant_propagation)). + +We add a `_type` field even to `Start` and `Return`, and later to all control +Nodes - because the Sea of Nodes does not distinguish between control and data. +Both nodes are equally peepholed and optimized, and this will be covered +starting in [Chapter 4](../chapter04/README.md) and [Chapter +5](../chapter05/README.md). + + +There are other important properties of the Lattice that we discuss in [Chapter +4](../chapter04/README.md) and [Chapter 10](../chapter10/README.md), such as the "meet" and "join" operators and their rules. + +## Nodes Pre Peephole Optimization + +The following visual shows how the graph looks like pre-peephole optimization: + + + +* Control nodes appear as square boxes with yellow background +* Control edges are in bold red +* The edges from Constants to Start are shown in dotted lines as these are not true control edges +* We label each edge with its position in the node's list of inputs. + +## Post-peephole + + + +## Demonstration +To demonstrate how the optimisation works, let's consider the following code: +``` +return 1+2; +``` +This is parsed as: +```java + var lhs = parseMultiplication(); + if (match("+")) return new AddNode(lhs, parseAddition()).peephole(); +``` +When the peephole is called on the AddNode node it first calls the compute function: +```java + public final Node peephole( ) { + // Compute initial or improved Type + Type type = _type = compute(); +``` +```java + public Type compute() { + 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 Type.BOTTOM; + } +``` +In our case both of the operands(`1`, `2`) are constants, and instance of +`TypeInteger`. +Since `compute` returns a type, and we know that both of our operands are constants, we can do +constant propagation/folding where we compute the result of the expression so that instead of having `1+2` we can +have `3`. + +The 'TypeInteger' interface inherits from type but also has a field that stores the value observed for that type. +```java + private final long _con +``` + +AddNode has the layout: + + + +Since, we already know the value of both of the operands, we can just add them together and replace the +old node with a constant node that just holds the result of the expression. + +To do this - we have to recognize this happens when the type computed for the node is already a constant, but the node +is still not an instance of constant node. + +This can be captured by: +```java +// Replace constant computations from non-constants with a constant node +if (!(this instanceof ConstantNode) && type.isConstant()) { +} +``` +Note how we return a constant type when doing constant folding +```java +return TypeInteger.constant(i0.value()+i1.value()); +``` +We can create a constant node in the following way: +```java +return new ConstantNode(type).peephole(); +``` +This recursively calls the peephole algorithm. +Now, we end up with this: + + + +Notice, how we added the constant node holding a constant value of `3`. +The remaining add node is unused and can be killed. + +We can call the kill function before this way: +```java +// 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(); +``` +This is responsible for deleting the dangling add node. + +```java +public void kill( ) { + assert isUnused(); // Has no uses, so it is dead + for( int i=0; i<nIns(); i++ ) + setDef(i,null); // Set all inputs to null, recursively killing unused Nodes + + _inputs.clear(); + _type=null; // Flag as dead + assert isDead(); // Really dead now +} +``` + +`isUnused` - This makes sure the current node has no outputs. +Ideally, in our case there would be one output as the return statement is +using the add expression. +However, the optimisation happens before even creating the return node, which means that at this point +there are no users of the add node. + +We can now freely go ahead and delete the node. + +```java +for(int i=0; i<nIns(); i++ ) + setDef(i,null); // Set all inputs to null, recursively killing unused Nodes + +_inputs.clear(); +_type=null; +``` + +We finally end up with this: + + diff --git a/src/main/java/com/seaofnodes/simple/GraphVisualizer.java b/src/main/java/com/seaofnodes/simple/GraphVisualizer.java new file mode 100644 index 00000000..4c625be1 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/GraphVisualizer.java @@ -0,0 +1,117 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.node.*; + +import java.util.*; + +/** + * Simple visualizer that outputs GraphViz dot format. + * The dot output must be saved to a file and run manually via dot to generate the SVG output. + * Currently, this is done manually. + */ +public class GraphVisualizer { + + public String generateDotOutput(Parser parser) { + + // Since the graph has cycles, we need to create a flat list of all the + // nodes in the graph. + Collection<Node> all = findAll(parser); + StringBuilder sb = new StringBuilder(); + sb.append("digraph chapter02 {\n"); + sb.append("/*\n"); + sb.append(parser.src()); + sb.append("\n*/\n"); + + // To keep the Scopes below the graph and pointing up into the graph we + // need to group the Nodes in a subgraph cluster, and the scopes into a + // different subgraph cluster. THEN we can draw edges between the + // scopes and nodes. If we try to cross subgraph cluster borders while + // still making the subgraphs DOT gets confused. + sb.append("\trankdir=BT;\n"); // Force Nodes before Scopes + + // Preserve node input order + sb.append("\tordering=\"in\";\n"); + + // Merge multiple edges hitting the same node. Makes common shared + // nodes much prettier to look at. + sb.append("\tconcentrate=\"true\";\n"); + + // Just the Nodes first, in a cluster no edges + nodes(sb, all); + + // Walk the Node edges + nodeEdges(sb, all); + + sb.append("}\n"); + return sb.toString(); + } + + + private void nodes(StringBuilder sb, Collection<Node> all) { + // 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 ) { + 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("\" "); + sb.append("];\n"); + } + sb.append("\t}\n"); // End Node cluster + } + + + // Walk the node edges + private void nodeEdges(StringBuilder sb, Collection<Node> all) { + // 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; + 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()); + // 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() ) + sb.append(" color=red"); + sb.append("];\n"); + } + i++; + } + } + } + + /** + * Finds all nodes in the graph. + */ + private Collection<Node> findAll(Parser parser) { + final StartNode start = Parser.START; + final HashMap<Integer, Node> all = new HashMap<>(); + for( Node n : start._outputs ) + walk(all, n); + return all.values(); + } + + /** + * Walk a subgraph and populate distinct nodes in the all list. + */ + private void walk(HashMap<Integer, Node> all, Node n) { + if (all.get(n._nid) != null) return; // Been there, done that + all.put(n._nid, n); + for (Node c : n._inputs) + if (c != null) + walk(all, c); + for (Node c : n._outputs) + if (c != null) + walk(all, c); + } +} diff --git a/src/main/java/com/seaofnodes/simple/Parser.java b/src/main/java/com/seaofnodes/simple/Parser.java index 806c985d..72da8eeb 100644 --- a/src/main/java/com/seaofnodes/simple/Parser.java +++ b/src/main/java/com/seaofnodes/simple/Parser.java @@ -1,6 +1,7 @@ package com.seaofnodes.simple; import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; /** * The Parser converts a Simple source program to the Sea of Nodes intermediate @@ -30,6 +31,8 @@ public class Parser { START = new StartNode(); } + String src() { return new String( _lexer._input ); } + public ReturnNode parse() { var ret = (ReturnNode) parseStatement(); if (!_lexer.isEOF()) throw error("Syntax error, unexpected " + _lexer.getAnyNextToken()); @@ -57,20 +60,61 @@ public class Parser { * </pre> * @return an expression {@link Node}, never {@code null} */ - private ReturnNode parseReturn() { + private Node parseReturn() { var expr = require(parseExpression(), ";"); - return new ReturnNode(START, expr); + return new ReturnNode(START, expr).peephole(); } /** * Parse an expression of the form: * * <pre> - * expr : primaryExpr + * expr : additiveExpr * </pre> * @return an expression {@link Node}, never {@code null} */ - private Node parseExpression() { + private Node parseExpression() { return parseAddition(); } + + /** + * Parse an additive expression + * + * <pre> + * additiveExpr : multiplicativeExpr (('+' | '-') multiplicativeExpr)* + * </pre> + * @return an add expression {@link Node}, never {@code null} + */ + private Node parseAddition() { + var lhs = parseMultiplication(); + if (match("+")) return new AddNode(lhs, parseAddition()).peephole(); + if (match("-")) return new SubNode(lhs, parseAddition()).peephole(); + return lhs; + } + + /** + * Parse an multiplicativeExpr expression + * + * <pre> + * multiplicativeExpr : unaryExpr (('*' | '/') unaryExpr)* + * </pre> + * @return a multiply expression {@link Node}, never {@code null} + */ + private Node parseMultiplication() { + var lhs = parseUnary(); + if (match("*")) return new MulNode(lhs, parseMultiplication()).peephole(); + if (match("/")) return new DivNode(lhs, parseMultiplication()).peephole(); + return lhs; + } + + /** + * Parse a unary minus expression. + * + * <pre> + * unaryExpr : ('-') unaryExpr | primaryExpr + * </pre> + * @return a unary expression {@link Node}, never {@code null} + */ + private Node parseUnary() { + if (match("-")) return new MinusNode(parseUnary()).peephole(); return parsePrimary(); } @@ -78,14 +122,14 @@ public class Parser { * Parse a primary expression: * * <pre> - * primaryExpr : integerLiteral + * primaryExpr : integerLiteral | Identifier | '(' expression ')' * </pre> + * @return a primary {@link Node}, never {@code null} */ private Node parsePrimary() { - _lexer.skipWhiteSpace(); - if (_lexer.isNumber()) - return parseIntegerLiteral(); - throw error("Syntax error, expected integer literal"); + if( _lexer.isNumber() ) return parseIntegerLiteral(); + if( match("(") ) return require(parseExpression(), ")"); + throw errorSyntax("integer literal"); } /** @@ -96,7 +140,7 @@ public class Parser { * </pre> */ private ConstantNode parseIntegerLiteral() { - return new ConstantNode(_lexer.parseNumber()); + return (ConstantNode) new ConstantNode(_lexer.parseNumber()).peephole(); } ////////////////////////////////// @@ -146,6 +190,12 @@ public class Parser { _input = buf; } + // Very handy in the debugger, shows the unparsed program + @Override + public String toString() { + return new String(_input, _position, _input.length - _position); + } + // True if at EOF private boolean isEOF() { return _position >= _input.length; @@ -209,11 +259,11 @@ public class Parser { boolean isNumber() {return isNumber(peek());} boolean isNumber(char ch) {return Character.isDigit(ch);} - private long parseNumber() { + private Type parseNumber() { String snum = parseNumberString(); if (snum.length() > 1 && snum.charAt(0) == '0') throw error("Syntax error: integer values cannot start with '0'"); - return Long.parseLong(snum); + return TypeInteger.constant(Long.parseLong(snum)); } private String parseNumberString() { int start = _position; @@ -246,5 +296,4 @@ public class Parser { return new String(_input, start, 1); } } - } diff --git a/src/main/java/com/seaofnodes/simple/Utils.java b/src/main/java/com/seaofnodes/simple/Utils.java new file mode 100644 index 00000000..45f15327 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/Utils.java @@ -0,0 +1,39 @@ +package com.seaofnodes.simple; + +import java.util.ArrayList; + +public class Utils { + public static RuntimeException TODO() { return TODO("Not yet implemented"); } + public static RuntimeException TODO(String msg) { return new RuntimeException(msg); } + + /** + * Fast, constant-time, element removal. Does not preserve order + * + * @param array ArrayList to modify + * @param i element to be removed + * @return element removed + */ + public static <E> E del(ArrayList<E> array, int i) { + if ( i >= 0 && i < array.size() ) { + E tmp = array.get(i); + E last = array.removeLast(); + if (i < array.size()) array.set(i, last); + return tmp; + } + return null; + } + + /** + * Search a list for an element by reference + * + * @param ary List to search in + * @param x Object to be searched + * @return >= 0 on success, -1 on failure + */ + public static <E> int find( ArrayList<E> ary, E x ) { + for( int i=0; i<ary.size(); i++ ) + if( ary.get(i)==x ) + return i; + return -1; + } +} diff --git a/src/main/java/com/seaofnodes/simple/node/AddNode.java b/src/main/java/com/seaofnodes/simple/node/AddNode.java new file mode 100644 index 00000000..7c1968c5 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/AddNode.java @@ -0,0 +1,36 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.*; + +public class AddNode extends Node { + public AddNode(Node lhs, Node rhs) { super(null, lhs, rhs); } + + @Override public String label() { return "Add"; } + + @Override public String glabel() { return "+"; } + + @Override + StringBuilder _print1(StringBuilder sb) { + in(1)._print0(sb.append("(")); + in(2)._print0(sb.append("+")); + 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(i0.value()+i1.value()); + } + return Type.BOTTOM; + } + + @Override + public Node idealize () { + // TODO: add of 0, and many other peepholes added in Chaper04 + return null; + } + +} diff --git a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java index 21f65084..e4d7f412 100644 --- a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java @@ -1,6 +1,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.type.Type; /** * A Constant node represents a constant value. At present, the only constants @@ -15,11 +16,27 @@ import com.seaofnodes.simple.Parser; * The Constant's value is the value stored in it. */ public class ConstantNode extends Node { + Type _con; + public ConstantNode( Type type ) { + super(Parser.START); + _con = type; + } - public final long _value; + @Override + public String label() { return "#"+_con; } - public ConstantNode(long value) { - super(Parser.START); - _value = value; + @Override + public String uniqueName() { return "Con_" + _nid; } + + @Override + StringBuilder _print1(StringBuilder sb) { + return _con._print(sb); } + + @Override + public Type compute() { return _con; } + + @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 new file mode 100644 index 00000000..e3c3616a --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/DivNode.java @@ -0,0 +1,34 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; + +public class DivNode extends Node { + public DivNode(Node lhs, Node rhs) { super(null, lhs, rhs); } + + @Override public String label() { return "Div"; } + + @Override public String glabel() { return "//"; } + + @Override + StringBuilder _print1(StringBuilder sb) { + in(1)._print0(sb.append("(")); + in(2)._print0(sb.append("/")); + 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 i1.value() == 0 + ? TypeInteger.ZERO + : TypeInteger.constant(i0.value()/i1.value()); + } + return Type.BOTTOM; + } + + @Override + public Node idealize() { return null; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/MinusNode.java b/src/main/java/com/seaofnodes/simple/node/MinusNode.java new file mode 100644 index 00000000..0d59ef25 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/MinusNode.java @@ -0,0 +1,28 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; + +public class MinusNode extends Node { + public MinusNode(Node in) { super(null, in); } + + @Override public String label() { return "Minus"; } + + @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()) : i0; + return Type.BOTTOM; + } + + @Override + public Node idealize() { return null; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/MulNode.java b/src/main/java/com/seaofnodes/simple/node/MulNode.java new file mode 100644 index 00000000..26c28281 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/MulNode.java @@ -0,0 +1,32 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; + +public class MulNode extends Node { + public MulNode(Node lhs, Node rhs) { super(null, lhs, rhs); } + + @Override public String label() { return "Mul"; } + + @Override public String glabel() { return "*"; } + + @Override + StringBuilder _print1(StringBuilder sb) { + in(1)._print0(sb.append("(")); + in(2)._print0(sb.append("*")); + 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(i0.value()*i1.value()); + } + return Type.BOTTOM; + } + + @Override + public Node idealize() { return null; } +} diff --git a/src/main/java/com/seaofnodes/simple/node/Node.java b/src/main/java/com/seaofnodes/simple/node/Node.java index 5d74e206..6f47f9eb 100644 --- a/src/main/java/com/seaofnodes/simple/node/Node.java +++ b/src/main/java/com/seaofnodes/simple/node/Node.java @@ -1,5 +1,8 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.type.Type; + import java.util.*; /** @@ -36,6 +39,13 @@ public abstract class Node { */ public final ArrayList<Node> _outputs; + + /** + * Current computed type for this Node. This value changes as the graph + * changes and more knowledge is gained about the program. + */ + public Type _type; + /** * A private Global Static mutable counter, for unique node id generation. * To make the compiler multi-threaded, this field will have to move into a TLS. @@ -50,8 +60,53 @@ public abstract class Node { _outputs = new ArrayList<>(); for( Node n : _inputs ) if( n != null ) - n._outputs.add( this ); + n.addUse( this ); + } + + // Easy reading label for debugger, e.g. "Add" or "Region" or "EQ" + public abstract String label(); + + // Unique label for graph visualization, e.g. "Add12" or "Region30" or "EQ99" + public String uniqueName() { return label() + _nid; } + + // Graphical label, e.g. "+" or "Region" or "==" + public String glabel() { return label(); } + + + // ------------------------------------------------------------------------ + + // Debugger Printing. + + // {@code toString} is what you get in the debugger. It has to print 1 + // 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(); + } + + // 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 + // tik-tok style; the common _print0 calls the per-Node _print1, which + // calls back to _print0; + public final String print() { + return _print0(new StringBuilder()).toString(); } + // This is the common print: check for DEAD and print "DEAD" else call the + // per-Node print1. + final StringBuilder _print0(StringBuilder sb) { + return isDead() + ? sb.append(uniqueName()).append(":DEAD") + : _print1(sb); + } + // Every Node implements this. + abstract StringBuilder _print1(StringBuilder sb); + /** * Gets the ith input node @@ -62,6 +117,8 @@ public abstract class Node { public int nIns() { return _inputs.size(); } + public Node out(int i) { return _outputs.get(i); } + public int nOuts() { return _outputs.size(); } public boolean isUnused() { return nOuts() == 0; } @@ -70,6 +127,119 @@ public abstract class Node { /** + * 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 + * <em>def</em> go dead. This function is co-recursive with {@link #kill}. + * + * @param idx which def to set + * @param new_def the new definition + * @return new_def for flow coding + */ + Node setDef(int idx, Node new_def ) { + Node old_def = in(idx); + if( old_def == new_def ) return this; // No change + // If new def is not null, add the corresponding def->use edge + // This needs to happen before removing the old node's def->use edge as + // the new_def might get killed if the old node kills it recursively. + if( new_def != null ) + new_def.addUse(this); + if( old_def != null && // If the old def exists, remove a def->use edge + old_def.delUse(this) ) // If we removed the last use, the old def is now dead + old_def.kill(); // Kill old def + // Set the new_def over the old (killed) edge + _inputs.set(idx,new_def); + // Return self for easy flow-coding + return new_def; + } + + // Breaks the edge invariants, used temporarily + protected <N extends Node> N addUse(Node n) { _outputs.add(n); return (N)this; } + + // Remove node 'use' from 'def's (i.e. our) output list, by compressing the list in-place. + // Return true if the output list is empty afterward. + // 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; + } + + /** + * Kill a Node with no <em>uses</em>, by setting all of its <em>defs</em> + * to null. This may recursively kill more Nodes and is basically dead + * code elimination. This function is co-recursive with {@link #setDef}. + */ + public void kill( ) { + assert isUnused(); // Has no uses, so it is dead + for( int i=0; i<nIns(); i++ ) + setDef(i,null); // Set all inputs to null, recursively killing unused Nodes + _inputs.clear(); + _type=null; // Flag as dead + assert isDead(); // Really dead now + } + + // Mostly used for asserts and printing. + boolean isDead() { return isUnused() && nIns()==0 && _type==null; } + /** + * We allow disabling peephole opt so that we can observe the + * full graph, vs the optimized graph. + */ + public static boolean _disablePeephole = false; + + /** + * Try to peephole at this node and return a better replacement Node if + * possible. We compute a {@link Type} and then check and replace: + * <ul> + * <li>if the Type {@link Type#isConstant}, we replace with a {@link ConstantNode}</li> + * <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> + * </ul> + */ + public final Node peephole( ) { + // Compute initial or improved Type + Type type = _type = compute(); + + if (_disablePeephole) + 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(); + } + + // Future chapter: Global Value Numbering goes here + + // Ask each node for a better replacement + Node n = idealize(); + if( n != null ) return n; + + return this; // No progress + } + + + /** + * This function needs to be + * <a href="https://en.wikipedia.org/wiki/Monotonic_function">Monotonic</a> + * as it is part of a Monotone Analysis Framework. + * <a href="https://www.cse.psu.edu/~gxt29/teaching/cse597s21/slides/08monotoneFramework.pdf">See for example this set of slides</a>. + * <p> + * For Chapter 2, all our Types are really integer constants, and so all + * the needed properties are trivially true, and we can ignore the high + * theory. Much later on, this will become important and allow us to do + * many fancy complex optimizations trivially... because theory. + * <p> + * compute() needs to be stand-alone, and cannot recursively call compute + * on its inputs programs are cyclic (have loops!) and this will just + * infinitely recurse until stack overflow. Instead, compute typically + * computes a new type from the {@link #_type} field of its inputs. + */ + public abstract Type compute(); + + public abstract Node idealize(); + + /** * 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/ReturnNode.java b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java index 85bbc83f..7fa470f8 100644 --- a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java +++ b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java @@ -1,5 +1,7 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.type.*; + /** * The Return node has two inputs. The first input is a control node and the * second is the data node that supplies the return value. @@ -18,6 +20,21 @@ public class ReturnNode extends Node { public Node ctrl() { return in(0); } public Node expr() { return in(1); } + @Override + public String label() { return "Return"; } + + @Override + StringBuilder _print1(StringBuilder sb) { + return expr()._print0(sb.append("return ")).append(";"); + } + @Override public boolean isCFG() { return true; } + @Override + public Type compute() { + return Type.BOTTOM; + } + + @Override + public Node idealize() { return null; } } diff --git a/src/main/java/com/seaofnodes/simple/node/StartNode.java b/src/main/java/com/seaofnodes/simple/node/StartNode.java index 04bd6bb4..99e240ad 100644 --- a/src/main/java/com/seaofnodes/simple/node/StartNode.java +++ b/src/main/java/com/seaofnodes/simple/node/StartNode.java @@ -1,9 +1,13 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.type.Type; + /** - * The Start node represents the start of the function. For now, we do not have any inputs to Start because our function does not - * yet accept parameters. When we add parameters the value of Start will be a tuple, and will require Projections to extract the values. - * We discuss this in detail in Chapter 9: Functions and Calls. + * The Start node represents the start of the function. For now, we do not + * have any inputs to Start because our function does not yet accept + * parameters. When we add parameters the value of Start will be a tuple, and + * will require Projections to extract the values. We discuss this in detail + * in Chapter 9: Functions and Calls. */ public class StartNode extends Node { @@ -11,6 +15,19 @@ public class StartNode extends Node { super(); } + @Override + public String label() { return "Start"; } + + @Override + StringBuilder _print1(StringBuilder sb) { + return sb.append(label()); + } + @Override public boolean isCFG() { return true; } + @Override + public Type compute() { return Type.BOTTOM; } + + @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 new file mode 100644 index 00000000..d2e32c61 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/node/SubNode.java @@ -0,0 +1,33 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeInteger; + +public class SubNode extends Node { + public SubNode(Node lhs, Node rhs) { super(null, lhs, rhs); } + + @Override public String label() { return "Sub"; } + + @Override public String glabel() { return "-"; } + + @Override + StringBuilder _print1(StringBuilder sb) { + in(1)._print0(sb.append("(")); + in(2)._print0(sb.append("-")); + 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(i0.value()-i1.value()); + } + return Type.BOTTOM; + } + + @Override + 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 new file mode 100644 index 00000000..89f6c715 --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/type/Type.java @@ -0,0 +1,40 @@ +package com.seaofnodes.simple.type; + + +/** + * These types are part of a Monotone Analysis Framework, + * @see <a href="https://www.cse.psu.edu/~gxt29/teaching/cse597s21/slides/08monotoneFramework.pdf">see for example this set of slides</a>. + * <p> + * The types form a lattice; @see <a href="https://en.wikipedia.org/wiki/Lattice_(order)">a symmetric complete bounded (ranked) lattice.</a> + * <p> + * This wild lattice theory will be needed later to allow us to easily beef up + * the analysis and optimization of the Simple compiler... but we don't need it + * now, just know that it is coming along in a later Chapter. + * <p>g + * One of the fun things here is that while the theory is deep and subtle, the + * actual implementation is darn near trivial and is generally really obvious + * what we're doing with it. Right now, it's just simple integer math to do + * simple constant folding e.g. 1+2 == 3 stuff. + */ + +public class Type { + + // ---------------------------------------------------------- + // Simple types are implemented fully here. "Simple" means: the code and + // 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 + public final byte _type; + + public boolean is_simple() { return _type < TSIMPLE; } + private static final String[] STRS = new String[]{"Bot","Top"}; + protected Type(byte type) { _type = type; } + + public static final Type BOTTOM = new Type( TBOT ); // ALL + + public boolean isConstant() { return _type == TTOP; } + + public StringBuilder _print(StringBuilder sb) {return is_simple() ? sb.append(STRS[_type]) : sb;} +} diff --git a/src/main/java/com/seaofnodes/simple/type/TypeInteger.java b/src/main/java/com/seaofnodes/simple/type/TypeInteger.java new file mode 100644 index 00000000..13f816bf --- /dev/null +++ b/src/main/java/com/seaofnodes/simple/type/TypeInteger.java @@ -0,0 +1,41 @@ +package com.seaofnodes.simple.type; + +/** + * Integer Type + */ +public class TypeInteger extends Type { + + public final static TypeInteger ZERO= new TypeInteger(0); + + /** + * The constant value observed for this type + */ + private final long _con; + + public TypeInteger(long con) { + super(TINT); + _con = con; + } + + public static TypeInteger constant(long con) { return new TypeInteger(con); } + + @Override + public String toString() { + return _print(new StringBuilder()).toString(); + } + + @Override + public StringBuilder _print(StringBuilder sb) { return sb.append(_con); } + + @Override + public boolean isConstant() { return true; } + + public long value() { return _con; } + + @Override + public boolean equals( Object o ) { + if( o==this ) return true; + if( !(o instanceof TypeInteger i) ) return false; + return _con==i._con; + } +} diff --git a/src/test/java/com/seaofnodes/simple/Chapter01Test.java b/src/test/java/com/seaofnodes/simple/Chapter01Test.java index a91aabb1..49edfb5b 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter01Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter01Test.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.*; @@ -17,7 +18,7 @@ public class Chapter01Test { Node expr = ret.expr(); if( expr instanceof ConstantNode con ) { assertEquals(start,con.in(0)); - assertEquals(1, con._value); + assertEquals(TypeInteger.constant(1), con._type); } else { fail(); } @@ -30,7 +31,7 @@ public class Chapter01Test { StartNode start = Parser.START; for( Node use : start._outputs ) if( use instanceof ConstantNode con ) - assertEquals(0,con._value); + assertEquals(TypeInteger.constant(0),con._type); } @Test @@ -54,13 +55,9 @@ public class Chapter01Test { } @Test - public void testBad3() { - try { - new Parser("return --12;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Syntax error, expected integer literal",e.getMessage()); - } + public void testNotBad3() { + // this test used to fail in chapter 1 + assertEquals("return 12;", new Parser("return --12;").parse().print()); } @Test @@ -73,15 +70,10 @@ public class Chapter01Test { } } - // Negative numbers require unary operator support that is not in scope @Test - public void testBad5() { - try { - new Parser("return -100;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Syntax error, expected integer literal",e.getMessage()); - } + public void testNotBad5() { + // this test used to fail in chapter 1 + assertEquals("return -100;", new Parser("return -100;").parse().print()); } @Test diff --git a/src/test/java/com/seaofnodes/simple/Chapter02Test.java b/src/test/java/com/seaofnodes/simple/Chapter02Test.java new file mode 100644 index 00000000..36e62f9b --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/Chapter02Test.java @@ -0,0 +1,66 @@ +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 Chapter02Test { + + @Test + public void testParseGrammar() { + Node._disablePeephole = true; // disable peephole so we can observe full graph + Parser parser = new Parser("return 1+2*3+-5;"); + ReturnNode ret = parser.parse(); + assertEquals("return (1+((2*3)+(-5)));", ret.print()); + GraphVisualizer gv = new GraphVisualizer(); + System.out.println(gv.generateDotOutput(parser)); + Node._disablePeephole = false; + } + + @Test + public void testAddPeephole() { + Parser parser = new Parser("return 1+2;"); + ReturnNode ret = parser.parse(); + assertEquals("return 3;", ret.print()); + } + + @Test + public void testSubPeephole() { + Parser parser = new Parser("return 1-2;"); + ReturnNode ret = parser.parse(); + assertEquals("return -1;", ret.print()); + } + + @Test + public void testMulPeephole() { + Parser parser = new Parser("return 2*3;"); + ReturnNode ret = parser.parse(); + assertEquals("return 6;", ret.print()); + } + + @Test + public void testDivPeephole() { + Parser parser = new Parser("return 6/3;"); + ReturnNode ret = parser.parse(); + assertEquals("return 2;", ret.print()); + } + + @Test + public void testMinusPeephole() { + Parser parser = new Parser("return 6/-3;"); + ReturnNode ret = parser.parse(); + assertEquals("return -2;", ret.print()); + } + + @Test + public void testExample() { + Parser parser = new Parser("return 1+2*3+-5;"); + ReturnNode ret = parser.parse(); + assertEquals("return 2;", ret.print()); + GraphVisualizer gv = new GraphVisualizer(); + System.out.println(gv.generateDotOutput(parser)); + } + +} |
